Skip to main content
The Ekanos harness (@ekanos/harness) is a local development environment that renders every surface of your integration—widgets, the marketplace tile, activation form, and triggers—in actual Ekanos chrome, without Supabase, authentication, or network traffic.

Key features

  • Real chrome, no backend — widgets render in the actual Ekanos dashboard grid with real Widget.* compound components and design tokens
  • Fixtures by default — HTTP exchanges recorded per variant (default, empty, error) answer all requests; the network is refused
  • Live mode opt-in — switch the toolbar to Live API to send real requests to your vendor under your declared egress, with a real localhost OAuth loop for OAuth integrations
  • Fixture variants — switch between happy path, empty state, and error from the toolbar; the widget subtree is rebuilt, so any widget that re-fetches gets the new recording
  • Triggers surface — run webhooks and schedules locally, and (in live mode) the OAuth loop against your own developer app
  • Actions surface — invoke your declared actions handlers from a widget’s Save button; runs in fixtures or live
  • No configuration required — useIntegrationFetch() and useActivationData() from @ekanos/sdk/hooks already work; write fixtures and you’re done

Installation

A project created by ekanos init already lists @ekanos/harness in its package.json. Install dependencies, then run:
ekanos dev checks that the harness and its peers (next, react, react-dom, tailwindcss, @tailwindcss/postcss) are installed, generates the shell under .ekanos/harness/, writes a starter harness.config.ts at your project root the first time (the only file you edit), and starts the dev server. The generated shell is rewritten on upgrade; your harness.config.ts is never touched after it is created.

Quick start

Set up harness.config.ts with one integration:
ekanos dev serves the harness at http://localhost:3100 by default (bound to loopback; --port and --host change that). Select your integration to see tabs for Widgets, Marketplace tile, Activation, and Triggers.

Surfaces

Each integration is rendered at /<slug> with four tabbed surfaces:
  • Widgets — your dashboard grid, rendered with real Widget.* components
  • Marketplace tile — your marketplace card, in the catalogue grid (if declared)
  • Activation — the form users see when connecting your integration
  • Triggers — your declared webhooks, schedules, and OAuth (requires definition)
Each widget can also be opened on its own at /<slug>/widgets/<widgetId>.

How data flows

Fixtures mode (default)

  1. The toolbar’s Data dropdown is on Fixtures (the default)
  2. globalThis.fetch is patched to intercept third-party and /api/… requests
  3. Requests match against your fixtures list for the active variant
  4. A matching fixture is returned; no request leaves the machine
  5. An unmatched request rejects with NoRecordedResponseError, whose message includes the fixture line to paste

Live mode (opt-in)

  1. A live block is declared on the integration entry
  2. The toolbar’s Data dropdown is set to Live API
  3. Widgets’ own requests reach your real API
  4. Requests are checked against your definition’s egress allowlist
  5. The OAuth loop runs on localhost using client credentials from .ekanos/local-secrets.json
  6. One server-held context per integration holds tokens and storage for the life of the dev server

What’s in the bundle

Everything in harness.config.ts is compiled into the browser bundle, including triggerMocks.secrets. Keep mock values fake. The .ekanos/ directory (and its .next build output) is gitignored; never commit the generated build.

Exports

Next steps