Skip to content
fusion-ai-host

The device runtime

runHost drives the whole device side: resolve credentials (enrol on first run), dial OUT to the server, subscribe to this device's command stream, and run dispatched tool calls — reconnecting with capped backoff. Nothing listens on the machine; the binary only ever connects out.

import { ,  } from "@tikab-interactive/fusion-ai-host";
 
await ({
  : "https://pulsn.internal", // the PulsN base URL
  : , // the tools this host exposes
  : ["/Users/me"], // allowed roots (defaults to the home dir)
  : .., // pairing code, first run only
  : "Workstation A", // a human label for the device
});

Every option, straight from the type:

export interface RunHostOptions {
	/** Base URL of the PulsN server, e.g. `https://pulsn.internal`. */
	server: string;
	/** The tools this host exposes (built-ins plus any domain tools). */
	tools: AnyLocalTool[];
	/** Allowed root directories. Defaults to the user's home directory. */
	roots?: string[];
	/** Pairing code for first-run enrollment. Falls back to `FUSION_HOST_CODE`. */
	code?: string;
	/** Human label for this device (defaults to the OS hostname). */
	name?: string;
	/** Log sink (defaults to stdout). */
	log?: (msg: string) => void;
}

Enrollment

The CLI does not log in with a password. A signed-in user mints a one-time pairing code; the device exchanges it (POST /api/host/enroll) for a durable per-device token bound to a server-issued device_id — a copied binary can't claim another machine's id. The credentials are cached locally (keyed by server origin) so subsequent runs reconnect with no code.

Loading diagram...

v1 token storage. The token is cached in a 0600 file under ~/.fusion-host/. The OS secret store (Keychain / DPAPI) is the end state — a native module inside a compiled binary is a packaging step still to be taken.

The dispatch loop

Once connected, each call frame on the stream is matched to a local tool, its input is validated against the tool's zod schema, and it runs — a read/action executes immediately, a mutation returns its plan() proposal unless the call is in apply mode (see Tools → Propose / apply). A throw is caught and returned as an error result; the loop never crashes. A dropped connection reconnects with capped backoff.

Packaging & signing

Compile one self-contained binary per OS — no Bun/Node install on the target:

bun build --compile --minify --target=bun-windows-x64 ./host.ts --outfile fusion-host.exe
bun build --compile --minify --target=bun-darwin-arm64 ./host.ts --outfile fusion-host

What's "locked down" on a modern OS is the privacy / anti-malware trust layer, which gates even the owner's own files when the app is untrusted — a freshly compiled, unsigned binary is the textbook case:

  • macOS — Gatekeeper blocks unsigned/un-notarized binaries; Apple Silicon requires a signature or the kernel kills the process. Sign ad-hoc for local use (codesign -s -, after clearing the com.apple.provenance xattr), or with a Developer ID + notarization to distribute. Bun's JIT needs allow-jit entitlements.
  • Windows (the primary target) — an unsigned .exe runs (with a SmartScreen warning) rather than being hard-killed; managed fleets additionally need an AppLocker/WDAC allowlist entry. Sign on Windows; you can't notarize a cross-compiled .exe from macOS.

This is deployment work, not code — Intune/Jamf pushing the binary, the signing cert, and (for managed fleets) the allowlist and a Full-Disk-Access profile.