connected posture with the same pairing flow;
they differ only in how often the machine is expected to be online. The choice
your UI offers stays exactly two-valued — managed by Checkfu or
connected (yours) — never a menu of hosting geographies.
The copyable client’s protected pairing-recovery store currently supports
macOS and Linux. Windows is not supported: the client returns a typed failure
before filesystem access because POSIX mode bits cannot prove a Windows ACL,
owner SID, or reparse-point boundary. Windows support requires those native
checks and a real Windows proof lane; a simulated platform test is not that
evidence.
The runtime dials out to Checkfu, so there is no inbound port, tunnel, or
public URL to configure, and your app never sees the runtime’s credential: the
one-shot pairing code is the only secret that crosses your product, and the
user’s machine redeems it directly against the platform.
CHECKFU_API_KEY and CHECKFU_WORKSPACE_ID come from
Get access. The examples use the
TypeScript SDK.
1. Mint a pairing code in your app
A pairing binds an end-user Principal to an AgentDefinition and produces a single-use code. The code is returned exactly once, by this authenticated create; the public pairing read exposes state, never the secret.code and the copyable client command. The
ConnectLocalAgent component in @checkfu/ui
renders this state (code display, presence badge, offboard control) from
injected data, holding no credential and making no network call itself.
2. Run the client on the user’s machine
Any supported macOS or Linux machine, same command:redeem_id so a crashed
redemption recovers instead of double-pairing, announces outbound presence
under a fresh generation, and supervises the local agent behind the
ACP boundary.
Redemption is exactly-once: an idempotent retry recovers the same runtime, a
second redeemer conflicts.
The <your-acp-agent> argument is any command speaking ACP on stdio — the
same contract every harness in the catalog uses.
3. Watch the pairing complete
4. Drive the Session — online or offline
Address a turn to the runtime by naming it in theuser.message. Everything
else is the ordinary public Session surface:
provisioning (with
run.created in the log and no run.started), and the turn executes when the
runtime next announces. Tell the user exactly that — “queued; it runs when
your machine reconnects” — rather than surfacing a failure. Under standard
retention an Automation targeting an offline runtime queues the same way;
under ZDR it fails closed instead of retaining execution content.
5. Offboard
state: "active"
in the window before that next call. If you need the row to read revoked at a
definite moment, revoke it explicitly rather than relying on the cascade.
What connected execution is — and is not
A connected runtime shares the durable Session log, governance, and audit path, but makes no Runner, sandbox, conformance, or model-control claim; its narration is attested against its registered key and honestly classed as narration, never as platform-witnessed evidence. See what a connected runtime realizes for the capability ceiling, and note that a ConnectedRuntime is not a customer-VPC Runner — that is a separatecustomer_runner topology.
Connecting an existing third-party gateway (for example an operator-owned
OpenClaw Gateway) is the sibling story — a control-plane federation rather
than an end-user pairing. It rides the same connected-ACP boundary; see
managed OpenClaw versus an existing Gateway.
A worked example
examples/personal-assistant wires this whole journey into a chat assistant:
/connect local-agent mints the pairing and relays the code,
/local <message> drives the paired runtime and relays the reply when it is
online — or answers honestly that the turn queued durably when it is not —
and /disconnect local-agent revokes it.