Beyond the database, an application plugs into runtime substrate — an event bus, a cache, an observability pipeline. The manifest wires each through a plugin slot: name the plugin and its connection, and the generator emits the client wiring and, where needed, the extra processes to run. You declare the slot; the framework builds the plumbing.
events kafka { bootstrap_servers "${KAFKA_BOOTSTRAP_SERVERS}" }
cache valkey { url "${VALKEY_URL}" }
observe otel { service_name "chowk" endpoint "${OTEL_EXPORTER_OTLP_ENDPOINT}" }events — the asynchronous spine
The events slot wires the event bus that carries domain events from emitter to subscriber. This is the slot that makes the transactional outbox real: it emits a relay process that drains the outbox table into the broker, and the app's subscriptions compile into a worker process that consumes from the broker and dispatches them.
Without this slot, an event has nowhere to flow — the framework falls back to an in-memory consumer and every asynchronous flow silently no-ops. Declaring events kafka { … } is what turns emits and subscription from declarations into a running pipeline. bootstrap_servers is env-expanded, so the broker address is deploy-specific.
cache — opt-in read acceleration
The cache valkey { … } slot wires a Valkey/Redis-compatible cache. The slot alone only emits the client and connection; an entity opts into it per-read with a cache spec (cache_read / cache_populate / cache_invalidate). So the cache is available everywhere but used only where an aggregate asks for it — no implicit caching to reason around.
observe — instrumentation for free
The observe otel { … } slot wires OpenTelemetry tracing and metrics. Every request, command, query, and subscription is instrumented automatically; spans and metrics export over OTLP to the configured endpoint. Point it at a collector in a deploy, or leave the endpoint env unset for a no-op exporter in local dev. You write no instrumentation code — it comes from the slot.
security — the HTTP security posture
The manifest also declares the project-wide HTTP security surface — CSRF protection and CORS:
security {
csrf {
bootstrap "issue_on_reject"
client { auto_retry true }
}
cors {
allowed_origins ["http://localhost:5173"]
}
}This lowers to the security stanza of the emitted config: double-submit CSRF (with a bootstrap that mints a token on the rejecting response so a fresh client can recover), and a CORS allow-list for your SPA origins. Declaring it makes the surface explicit and teachable rather than relying on defaults.
The process tree
Put the slots together and a running deployment is more than one process. The events slot in particular fans the app out into three:
flowchart LR
C[client] -->|HTTP| M[monolith<br/>·the API·]
M -->|writes| DB[(database)]
M -->|writes event| OB[[outbox table]]
OB -.drained by.-> RL[relay<br/>·singleton·]
RL -->|publishes| BR{{event bus}}
BR -->|consumed by| WK[worker<br/>·runs subscriptions·]
WK -->|dispatches| M| Process | Emitted from | Role |
|---|---|---|
monolith | the app itself | serves the HTTP API — every route |
relay | the events slot | drains the transactional outbox into the broker (one leader-elected instance) |
worker | the subscriptions | consumes broker events and dispatches subscription handlers |
An end-to-end asynchronous flow needs all three up. If the postmaster/log shows no event effect after an action, the usual cause is the relay or worker not running — the write committed, but nothing drained the outbox.
Client realms — the generated SDKs
Finally, the manifest declares client realms — one per client SDK the generator emits:
realm web {
kind client
language typescript
root "gen/clients/web"
auth { transmission cookie }
// … slots: types, validators, tanstack queries, api-client, … …
}Each realm emits a full typed client stack — wire models, Zod validators, TanStack query/mutation hooks, an API-fetch client with auth and CSRF baked in — rooted at root. Chowk declares a web realm (cookie auth, for the SPA) and a cli realm (header/bearer auth, for the command line). You never hand-write that stack; you build a thin product shell on top of it. The realm is the seam between the generated backend and the hand-built frontends — the last piece of infrastructure the manifest wires.