DocumentsDosu Decant
Decant Runtime Architecture
Decant Runtime Architecture
Type
Topic
Status
Published
Created
Aug 4, 2026
Updated
Aug 4, 2026
Created by
Dosu Bot
Updated by
Dosu Bot

Decant Runtime Architecture#

Decant runs as a single Bun + TypeScript process that owns the SQLite archive, CLI, local React UI, and API/watch loop simultaneously . There is no background daemon, bearer-token boundary, separate API process, or hosted service — everything executes in the one decant process .

~/.claude + ~/.codex
        |
        v
 Bun + TypeScript Decant process
 parse -> enrich -> ingest + economics vectors -> SQLite WAL + FTS5
        |
        +--> CLI reads / JSON
        +--> local React UI + JSON routes + SSE

This design replaced a three-process Rust/Phoenix/Swift stack in July 2026 . The pre-cutover tree is preserved in the signed pre-typescript tag.


The serve Command#

decant serve is the primary runtime entry point. It binds http://127.0.0.1:3000 by default and runs five subsystems in one process :

  1. Archive owner — the single process that holds the SQLite WAL connection
  2. Source watcher — filesystem + sweep watcher over ~/.claude/projects/ and ~/.codex/sessions/
  3. HTTP API — all /api/* routes per the OpenAPI 3.1 contract
  4. SSE stream — real-time event delivery at GET /api/events
  5. React UI — served as Bun fullstack HTML imports (no separate Vite dev server)

Startup Sequence#

The CLI entry (src/cli.ts serve handler) calls serveApp(), which delegates to serve() in src/server.ts. Startup proceeds in strict order:

  1. Bind the HTTP port first — before touching the archive. A port conflict causes a fast failure rather than leaving an orphaned DB-owning watcher.
  2. Open DB and hydrate economics — archive is opened, derived metadata backfilled, economics cache pre-warmed.
  3. Start source watcher — only if syncing is enabled (skipped under DECANT_NO_SYNC / --no-sync).

Incoming requests while the DB is still initializing return a 503 serviceStartingResponse .

Source Watching and Sync Coordination#

Watch-triggered ingests run in a worker thread via workerSyncRunner(), keeping database writes off the HTTP event loop.

Overlapping sync requests — from the watcher, a periodic sweep, or a manual POST /api/sync — are coalesced by createSyncCoordinator(): only one physical sync worker runs at a time; concurrent callers share the same Promise and receive fanned-out progress updates.

SSE Events#

GET /api/events returns text/event-stream. Events are broadcast to all connected clients via publishServerEvent(). Watch results flow through applyWatchEvent(), which updates shared syncStatus and republishes to SSE exactly once .

Event nameMeaning
helloConnection acknowledgement
pingHeartbeat (~5 s)
readySource watcher initialized
sync_progressBounded in-flight sync snapshot
syncTerminal successful sync report
archive_updatedArchive-derived UI data changed
errorWatcher or sync failure
stoppedSource watcher stopped

Graceful Shutdown#

Shutdown coordinates five subsystems in parallel: economics cache disposal, watcher stop, sync coordinator close, HTTP server stop, and DB close.


Key Invariants#

#InvariantImplication
1One process owns SQLiteNo daemon, token file, or second app may open the DB
2Core modules stay print-freeCLI output/exit-code policy lives only in src/cli.ts
3Local-first onlyNo outbound network calls; loopback bind by default
7Routes are a documented local APIdocs/api/openapi.yaml is the contract; /api/openapi.json is its runtime form

Access Control#

The API has no authentication . The loopback bind (127.0.0.1) is the sole boundary for local-only deployments. For non-loopback binds (e.g., Docker), trusted peers are resolved by replacement precedence: --trusted-peerDECANT_TRUSTED_PEERSDECANT_TRUST_DEFAULT_GATEWAY=1 . Any untrusted remote source receives 403 forbidden_remote.


Primary Source Files#

FilePurpose
src/cli.tsserve command handler; passes watch config to serveApp()
src/server.tsHTTP server, watcher, SSE, sync coordinator, graceful shutdown
docs/api/openapi.yamlOpenAPI 3.1 contract for all /api/* routes
docs/api/routes.mdOperational semantics: access control, SSE events, pagination
AGENTS.mdProject invariants and contributor guidance
Decant Runtime Architecture | Dosu