@autono/pinbox-core
v0.15.0
Published
Schema, hub logic, and storage adapters. Runs on Bun (local) and workerd / Durable Objects (cloud) from one package.
Readme
@autono/pinbox-core
Schema, hub logic, and storage adapters. Runs on Bun (local) and workerd / Durable Objects (cloud) from one package.
What lives here:
- Schema —
Pin,ThreadMessage,SessionReftypes + published JSON Schema. Exported at./schemaand./schema.json. - Hub — a fetch-style
(Request) => Responsehandler:Bun.serve({ fetch })consumes it locally and the Cloudflare Worker mounts the same handler. REST + WebSocket logic (hello → catch-up → events, cursor-based replay), the append-only event log, session registry, delivery adapters (hooks injection, OpenClaw push, resume-spawn viaBun.spawn, signed webhooks), pluggable auth verifier (none/token/jwt{issuer,jwksUrl,audience}/ custom). Exported at./hub. Streaming routes must callserver.timeout(req, 0)— Bun'sidleTimeoutis a total request deadline. - Storage adapters —
PinStoreis an interface with exactly two implementations:bun:sqlite(local file) and DO SQLite (cloud). Same schema:events,pins(incl.due_at),threads,sessions,links. FTS5 works underbun:sqliteand backspinbox list --search. - Broadcaster (
src/ws.ts, exported at./ws) — two-method interface over the split fanout primitives: Bun has pub/sub topics but no connection enumeration; DO has enumeration + tags but no pub/sub. Topics are fixed at connect asproject:<id>(DO tags are immutable afteracceptWebSocket). Alwaysserver.publish, neverws.publish— the latter excludes the sender, which would stall the originating toolbar's cursor and make it replay its own actions on reconnect. - WS protocol (
src/ws-protocol.ts, exported at./ws-protocol) — the frozen wire vocabulary:hello → catch-up → events, versioned with a min-protocol handshake. Auth happens at upgrade only; no credential ever appears inside a protocol message. Keepalive is transport-level (BunsendPings/ DOsetWebSocketAutoResponse) — protocol 1 has no ping message and exactly one client message, the hello. The local Bun server and the cloud DO server speak this module unchanged. - Realtime host layer (
src/hub-server.ts) —GET /wsis intercepted before the pure handler, which keeps its one-argument invariant and its bearer gate untouched: a browser cannot set headers on an upgrade, so the token ridesSec-WebSocket-Protocol: pinbox.token.<t>. Events reach sockets through exactly one host-registered listener (store.subscribe(...)→Broadcaster.publish); the handler never touches fanout. The host also owns the loopback-origin CORS gate, the/summaryconnectedToolbarsmerge, and an idle timer that never fires while sockets are attached. The upgrade callsserver.timeout(req, 0)— Bun'sidleTimeoutis a total request deadline. - Attachments (
src/attachments.ts) —POST /attachmentscaps bodies at 5 MB and hands the bytes to anAttachmentSink.HubOptionsandPinStoreare pinned final with no media member, so the sink is injected as a store-keyed sidecar: the host callsregisterAttachmentSink(store, sink)—localDirSinkwrites under.pinbox/media/locally, and the cloud host registers an R2 sink. AnAttachmentcarries a path or a URL, never bytes, at any schema version: open pins are re-injected into agents every turn, and inline bytes would be re-paid each time. - DO glue — the importable Durable Object class/handler consumers mount. Exported at
./do. (The deployable template that uses it lives inpackages/cli/templates/worker/.) - Connectors — the
pinbox linkinterface (createItem,postComment,onRemoteComment,onRemoteStatus) and the GitHub implementation, wired into the hub. Environment-specific transports are injected by the host: localghCLI (from the pinbox CLI), GitHub App token (from the worker).
Rules: no imports from sibling packages; minimal runtime deps — Zod v4 (single-source schemas: TS types + trust-boundary validation + JSON Schema via z.toJSONSchema()) and jose (JWT verifier). ./schema and ./do must run on both Bun and workerd; hub pieces that spawn processes (the resume-spawn delivery adapter) are Bun-only by nature. tsdown owns emit (ESM + .d.ts), including the post-build step that emits dist/schema.json for the ./schema.json export. Tests run under bun test.
