Choose where an Interchange workflow or agent runs (runLocal, embedded hub, remote hub plus sidecar), and set up sidecars, credentials, and placement. Load when deciding whether you need a hub.
Install
npx skillscat add corbitsdev/corbits-code/interchange-run-modes Install via the SkillsCat registry.
Interchange run modes
The question is where the control plane lives. Workflow and agent code is the same in every mode.
| Mode | Control plane | Use when |
|---|---|---|
runLocal or createAgent |
none | Scripts, CLI tools, CI, tests, dev loops, one user, run ends with the process |
| Embedded hub | inside your app | You need hub features in one local binary, such as a desktop app |
| Remote hub | hosted, sidecars dial out | Shared team, runs outlive any machine, work placed on other hosts |
Embedded and remote differ only by base URL. Moving up from runLocal is a deploy, not a rewrite. Default to no hub, and move up for one of these reasons:
- A wait (
awaitSignal, approval, timer) must outlive the process. The hub parks, persists, and resumes after a crash. - Credentials are shared or rotated. The hub seals them and freezes a failover chain at deploy.
- Triggers must fire while you are away (mail, cron, webhooks).
- Several principals need grants, audit, approvals, and spend accounting.
- Work must run in a sandbox or on another host.
- People or agents must mail a run by address.
- A deploy must be pinned to a commit.
Hub setup: use_skill interchange-embed-hub, use_skill interchange-hub-setup.
runLocal
Runs the real workflow runtime with in-memory parts. Reference: upstream examples/workflow-quickstart.
const run = runLocal(workflow, {
authorize: allowAll, // required, no default
hasUpstreamSignalResolver: true, // required, true for a top-level run you will signal
actionResolver, // ref -> ActionHandler, throws on unknown
loopFns, // ref -> LoopFn, throws on unknown
invokeStep: createAgentStepInvoker({
source,
material,
contextDir,
authorize: allowAll,
}),
});
await run.signal("go", payload, "sig-1"); // answers an awaitSignal
const result = await run.complete; // { terminalStatus, outputs, events }- The default
invokeStepcallsauthorizeand returns{ output: null }. It runs no model and no tools. A step that needs an agent needs yourinvokeStep(seestep-invoker.tsin the quickstart, which buildscreateAgentand callssend). - Works: signals, loops, actions, inline child workflows, timers, cancel, an in-memory event log.
- Missing: persistence, crash resume,
/approvalsroutes, mail triggers, hub credentials, placement, mail-addressed runs. A crashed run is terminal. - An approval park resolves only if you call
run.signalyourself. - An untimed
awaitSignalinside achildWorkflowis refused, because nothing can answer it. - Unverified: whether
onTriggersections run locally.
For one agent with no workflow, use createAgent with a git-backed contextDir and @intx/tools-posix (use_skill interchange-agents). There is no dedicated desktop harness. Desktop means a local process. Container, VM, Workers, and browser hosts are not shipped.
Hub plus sidecar
- The hub is the control plane. A sidecar is a host process that dials the hub over one WebSocket (
HUB_WS_URL), registers with an allocation token, and runs each deployment as a supervised child. It needs no public address. Usewss://off loopback. - Sidecars exist only through a
SidecarProvisioner(ensure,destroy, idempotent, generation-fenced). None is registered by default, so a fresh hub cannot deploy. - Sidecar env:
HUB_WS_URL,SIDECAR_ID,SIDECAR_TOKEN,SIDECAR_DATA_DIR,SIDECAR_CREDENTIAL_ENCRYPTION_KEY. - A crashed child restarts with backoff (1 s up to 30 s). Three exits in 60 s mark it
crash-loopingand fail the run. - Upstream dev stack:
bin/db-reset && bin/dev --seed, hub on :3000.
Credentials
Agents never discover credentials. The hub resolves them at launch and the harness mediates use. Inference keys travel in the agent.deploy frame and rotation pushes sources.update. Tool credentials are tenant-owned and delivered over the control channel. Proactive OAuth refresh is not built. Docs disagree on whether keys sit in deployment.json, so check DEV.md and the code.
Placement
A workflow declares needs, and a provisioner advertises what it offers:
sidecarPlacement: {
capabilities: [{ capability: "platform:ios", effect: "require" }];
}Selectors are exact, ns:*, or *. Effects are require or block, and block wins. Tenant policy adds constraints that workflows cannot weaken. There is no OS or container sandbox yet, so grants are not a syscall or network sandbox. Untrusted work needs a provisioner declaring an isolation:* capability.
Need more
- Upstream docs:
HARNESS_DESIGN.md,SIDECAR_PLACEMENT.md,CREDENTIALS.md,AUTH.md,unified-execution-host-design.md. - Apps that drive a hub:
use_skill interchange-client-apps. - Workflow authoring:
use_skill interchange-workflows. - Overview:
use_skill interchange.