Host Smithers workflows over HTTP and WebSocket via a single-run server, a single-workflow serve app, and the multi-run Gateway control plane.
Three host surfaces ship from packages/server, each a tighter fit for a
different deployment:
startServer: self-contained HTTP server with REST routes for run
lifecycle, SSE event streams, and approvals; one server, many workflows by
path. Use it for a plain HTTP API with no client SDK.
createServeApp: Hono app bound to one already-loaded workflow and run;
what bunx smthrs up --serve mounts. Compose it into a
larger Hono app or call app.fetch in tests.
Gateway: headless control plane. Authenticate once, stream events over
WebSocket with resume-on-reconnect, decide approvals, inject signals, run
cron schedules, and serve many registered workflows: the surface the
Gateway client and custom UIs talk to.
import { startServer, createServeApp, Gateway } from "smthrs";
createServeApp needs a SmithersDb adapter and a loaded SmithersWorkflow,
both from your workflow module (new SmithersDb(workflow.db)). Gateway is
constructed with new Gateway(opts), then you register() workflows and
listen().
A listening Node http.Server, with headersTimeout/requestTimeout
applied to bound slow header/body uploads. Full route table (run
start/resume/cancel, SSE events, frames, approvals, signals, /metrics)
lives in Server Mode.
const server = startServer({ port: 7331, authToken: process.env.SMITHERS_API_KEY, rootDir: process.cwd(),});
Builds a Hono app for a single, already-loaded workflow and run: mount it with
Bun.serve, route it into another Hono app, or drive app.fetch directly in
tests. Backs bunx smthrs up --serve.
function createServeApp(opts: ServeOptions): import("hono").Hono;
The multi-run control plane: construct it, register() one or more
workflows, then listen(). Serves RPC over POST /v1/rpc/<method> (and
POST /rpc) and over WebSocket on the same origin, plus /health,
/metrics, /workflows, and per-workflow webhook endpoints. RPC methods
(launchRun, getRun, listRuns, submitApproval, submitSignal, cron,
time-travel, and more) are documented per method; see
launchRun for the request/response and scope shape they
share.
register, extend, and the constructor are chainable. listen defaults to
port 7331; pass path for a Unix socket. close aborts active runs, drains
inflight work, and tears down connections and the scheduler.
Absolute path to the workspace root holding .smithers/ and
smithers.db; disk-backed registry reads (e.g. the listPrompts RPC)
resolve against it. Defaults to process.cwd().
Origin allowlist (defense-in-depth). [] disables the check; when
non-empty, a request or WebSocket upgrade whose browser Origin header
isn’t listed is rejected. Requests with no Origin (server-to-server /
CLI) are always allowed.
Origin allowlist (defense-in-depth); same behavior as token mode above.
Show mode: "trusted-proxy"
Identity is read from request headers; only safe behind a proxy you control
(Cloudflare Access, an internal API gateway) that strips and rewrites them.
Registers a workflow under key: wires up its DB tables, optional cron
schedule, webhook config, and embedded UI bundle. Returns this, so calls
chain. A schedule writes a cron row keyed gateway:<key>; cron-fired runs get
ctx.auth.role = "system", triggeredBy = "cron:gateway", scopes = ["*"].