Frequently asked questions
Do I have to use the harness?
Do I have to use the harness?
ekanos dev also runs your declared webhooks and schedules on its Triggers surface.If your integration has no UI (tools only), ekanos test with @ekanos/sdk/testing covers your handlers.Can I use TypeScript?
Can I use TypeScript?
checks/typecheck, then checks/typecheck-host-rules under
strict and noUncheckedIndexedAccess). Your code must compile cleanly under
those rules.How do I handle schema evolution?
How do I handle schema evolution?
StorageValidationError when read. Two approaches:- Add a new key — for example
settings/location-v2with the new schema, and migrate old data on read. (Keys allow only[a-z0-9_-], so.v2is not valid.) - Union the schemas — use
z.union([OldSchema, NewSchema])to accept both.
.optional() so rows written before the change still parse.What's the difference between tools and actions?
What's the difference between tools and actions?
parameters, decides when to call them, and explains the result. Return { error: '…' } for expected failures.Actions are for widgets — a user clicks Save, the widget calls useIntegrationAction(), and the declared handler runs deterministically with the values the user typed. input is validated with a zod schema; output optionally.Use tools for anything the assistant should do, actions for deterministic writes from your own UI.Can widgets call the vendor API directly?
Can widgets call the vendor API directly?
useIntegrationFetch() from @ekanos/sdk/hooks instead of bare fetch(). In the harness, live mode checks those requests against your egress list as a convenience. In production, no egress check runs on the client path — only ctx.fetch (server-side handlers) is enforced.useActivationData() gives a widget the activation data where the host provides it (it defaults to null). For credentials that must stay server-side, call the vendor from an action or tool handler through ctx.fetch.How do I handle authentication?
How do I handle authentication?
- Activation form — The user enters credentials in your activation form; handlers read them with
ctx.secrets.get(). - OAuth — Declare
oauthin your definition. The transport runs the flow, and youronTokens(ctx, tokens)stores the tokens withctx.secrets.set().
ctx.storage cannot declare the reserved secret data type, and clientReadable storage is browser-readable.onActivate is declared but not called on the host?
onActivate is declared but not called on the host?
onActivate yet. It runs in the
harness (after a connect on the Activation surface) and in tests via
invokeActivate(). Declare it and test it locally; no change on your side is
expected when host wiring lands.How does a widget write data?
How does a widget write data?
actions, and call it from the
widget with useIntegrationAction() from @ekanos/sdk/hooks. The handler
runs server-side with the full ctx, so it can write ctx.storage or call
your vendor through ctx.fetch. The browser storage route
(fetchIntegrationStorage()) is read-only.How does the harness handle credentials?
How does the harness handle credentials?
- Your activation form — the submitted activation data is kept in memory only and cleared on reload and on Disconnect
.ekanos/local-secrets.json— admin-tier secrets (OAuth client id and secret, webhook signing secrets) saved from the Local OAuth panel. The file is gitignored and never packed byekanos publish
Can I test webhooks locally?
Can I test webhooks locally?
@ekanos/sdk/testing provides invokeWebhook():payloadSchema. The harness’s Triggers surface runs webhooks interactively.What happens if my webhook handler throws?
What happens if my webhook handler throws?
{ status: 'processed' } when you handled the event, or { status: 'ignored' } for a valid but irrelevant event (both are 2xx, and the sender must not retry). Throw only when you could not handle a valid event.Can I emit custom logging?
Can I emit custom logging?
ctx.logger is pino-style, with debug, info, warn, and error:{ integration, accountId, actor } to every line. In tests, the mock context records lines on ctx.logs.How do I release a new version?
How do I release a new version?
version in your definition (and in package.json — ekanos validate
warns when they disagree), then validate, test, and publish. version must be
semver (1.0.0, optionally with a prerelease/build suffix); anything else
fails validation. ekanos status --watch follows the submission for your
current slug and package.json version.Can I have multiple integrations in one package?
Can I have multiple integrations in one package?
ekanos.json (declare one form or the other, never both):ekanos validate cross-checks them for colliding slugs, widget ids, and tool names.What's the minimum viable integration?
What's the minimum viable integration?
slug, name, description, and version are required. A useful minimum is one widget:component must be a component reference (a function component, or a memo/forwardRef/lazy wrapper), not an element.Where do I get help?
Where do I get help?
Can I fork/copy an existing integration?
Can I fork/copy an existing integration?
ekanos.json and the definition — and
re-prefix its widget ids. Slugs and widget ids must be globally unique, so a
copy that keeps them collides.My widget shows the wrong data in the harness but works correctly in my own app. Why?
My widget shows the wrong data in the harness but works correctly in my own app. Why?
- The fixture doesn’t match your widget’s URL — Check the console for
[harness:<slug>] refused GET …. The path and any pinned query params must match exactly. - A module-level promise cache — A widget that starts its promise during render and caches it at module scope keeps serving the first variant’s answer after a variant switch. Create the promise in a non-suspending parent and pass it across the Suspense boundary.
- An omitted variant —
emptyanderrordo not fall back todefault; every request in an undeclared variant is refused.
Where do icons come from?
Where do icons come from?
@ekanos/ui/icon. Ekanos
loads Font Awesome Pro; the generated harness loads Font Awesome Free. A
Pro-only (or mistyped) name renders a circle-question placeholder in the
harness, and ekanos validate flags Pro-only names with an icons/pro-only
warning.Can I publish to more than one source or host?
Can I publish to more than one source or host?
host and source in ekanos.json, written by the first successful publish or by ekanos use. Target another source for a single run with ekanos publish --source <slug>, or with --host for another host. You need a seat on every source you publish to.