description: "Domain data form (ctx.storageDomain) for hosts and maintainers choosing, mounting, or debugging schema-validated, change-emitting KV domains over storage backends."
English | 中文
Use this package to declare schema-validated key-value domains and open them through ctx.storageDomain over a configured storage backend. Reads return synchronously from validated in-memory state, while each write becomes durable before it resolves and emits domain/changed in order. Product packages use domain handles instead of accessing storage backends directly. This host-side state does not add tools, prompts, or session events, so it remains invisible to the model and agent loop.
Use this package when a host package keeps durable, schema-validated records — workspace records, session sidecar metadata. The owning package declares the domain once; consumers open it and get synchronous reads and durable, change-emitting writes without ever touching a backend.
Choose it for any host-side data that must survive restarts and stay valid against a schema: the domain form validates every stored record at open, and every write is durable before it resolves. Avoid it when the data belongs in a session event log — the session persistence seam owns that surface.
The owning package declares the domain once with defineDomain — name, version, and zod record schemas — and exports it. defineDomain fails loudly at module load on a bad name, a version that is not a non-negative integer, or a global schema that accepts null.
// Owning package, once:
const workspaceSpec = defineDomain({
name: 'workspace',
version: 1,
tables: { workspaces: domainTable(workspaceRecordSchema) },
})
A consumer opens the declared domain through ctx.storageDomain and keeps the returned handle; reads are synchronous, writes are durable:
const domain = await ctx.storageDomain.open(workspaceSpec)
await domain.table('workspaces').put(id, { path: '/work/demo' })
const record = domain.table('workspaces').get(id) // synchronous, from memory
domain.table('workspaces').update(id, (r) => ({ ...r, path: newPath }))
The caller owns the handle's lifecycle and releases it with domain.close() when the feature shuts down (typically its own ctx.effect disposer); domains still open when the plugin unmounts are closed by the facility.
The domain plugin's configuration decides which backend serves which domain — never the hub. backend names the default route; routes overrides it per domain name. A route naming an unregistered backend fails loudly at open with backend-not-found.
| Field | Default | Meaning |
|---|---|---|
backend |
required | Default backend name for every domain without an explicit route |
routes |
{} |
Per-domain overrides: domain name → backend name |
The generated configuration catalog is the exhaustive source for every accepted field and its JSDoc.
Every write resolves only after the backend acknowledges durability, and each emits one domain/changed event in write order. Failures carry stable DomainError codes: already-open (the name is open or still closing), facet-unsupported (the routed backend serves no kv facet), invalid-record (a stored record or global fails its schema, naming the table and key), missing-key (an update on an absent record), and closed (any use after close). Backend failures such as version-mismatch pass through unchanged.
Read these pages when the domain layer's view is not enough: the subsystem reference is the authoritative contract, and the Agent Note records the design and deferred work.
Nothing. The package registers no tools, injects no prompts, and appends no session events; it stores non-session data behind ctx.storageDomain and emits only the in-process domain/changed event, which reaches a model only if a consumer renders it through its own documented surface.
Zero: no text from this package enters any model request.
Independent: domain reads and writes never touch request prefixes, so nothing here can invalidate provider cache reuse.
These limits define when the domain layer is a poor fit or needs special operational care. They are current package constraints, not a task backlog.
domain/changed is an in-process event; a second host process or a reconnecting GUI observes no changes until the cross-process revision pattern lands (Agent Note).version-mismatch); changing a schema requires migrating the stored data by hand.