Persistence and Tenancy

Dawn's Node runtime uses local SQLite stores by default. That is a good one-process default, but it is not a tenancy model: production tenant ownership is an application boundary and is never inferred from a thread id.

What Dawn persists

The runtime writes several independent kinds of state. They do not share one lifecycle merely because they belong to the same application.

DataDefault locationShared optionNamespace or tenant keyDeletion behaviorOwner
LangGraph checkpoints and pending writes.dawn/checkpoints.sqlitepostgresCheckpointer through checkpointerthread_id, checkpoint namespace, and checkpoint id; no authenticated tenant key is addedAfter metadata deletion, the runtime awaits deleteThread when the saver supports it; a failure leaves metadata goneThe app selects the saver; the runtime writes route state
Agent Protocol thread metadata.dawn/threads.sqlitecreatePostgresThreadsStore through threadsStorethread_id; metadata can record application fields, but the id itself proves no ownerThe thread row is removed firstThe app owns thread authorization; the runtime maintains status and route metadata
Runtime permission decisions.dawn/permissions.jsoncreatePostgresPermissionsStore through permissions.storeTool/gate key and pattern within the configured store; not thread-scoped or tenant-scoped by defaultThread deletion does not remove permission decisionsThe application owns policy and store boundaries
Typed long-term memory.dawn/memory.sqliteA separate MemoryStore, such as pgvectorMemoryStore, through memory.storeThe route-declared memory scope: workspace, route, and any application-supplied tenant, user, or agent dimensionsManaged by memory APIs and retention policy, not by thread deletionThe application owns namespace derivation and lifecycle
Local workspace files<appRoot>/workspace/ through the local filesystem backendAn application-supplied backends.filesystemApp/workspace path; there is no automatic thread or tenant partitionThread deletion does not remove arbitrary workspace filesThe application and its tools
Per-thread sandbox volumesNone until sandbox is configured; location is provider-specificA durable provider volume, such as a Docker named volume or Kubernetes PVCthreadId is the provider lookup keySandbox destruction on thread deletion removes the thread volume; idle reap and shutdown release compute but keep itThe configured sandbox provider

Choose local or shared stores

Local SQLite and file stores keep setup small and make sense for development or one long-lived Node process with durable local disk. Move the three durable runtime stores independently when compute becomes ephemeral or several processes must see the same records:

@dawn-ai/postgres-storage implements those three stores over Postgres. Typed long-term memory remains a separate memory.store with its own schema, retrieval behavior, and retention needs; @dawn-ai/memory-pgvector is one shared option.

Generated Hono database boundary

The generated Hono request stores select the default public schema and default dawn table prefix for checkpoints, threads, and permissions. Their public.dawn_* tables contain no application namespace, so each generated Hono app requires an app-dedicated database. A tenant field in application input does not partition those tables.

Several applications may share one database only through hand-composed store wiring that passes a unique schema or tablePrefix consistently to all three Postgres stores. Preserve the generated edge lifecycle and bundling constraints when doing so: per-request pools and disposal, migration coordination, original-Request environment binding, static provider imports, serialized config, and explicit permission policy/hydration. The generated stores.mjs is replaced on rebuild and does not expose an app naming option; see Edge and Hono.

A running process caches loaded permission decisions because PermissionsStore.match() is synchronous. On the configuration-resolved Node path, serveRuntime resolves and loads its configured permissions store once at Node boot. The Postgres implementation hydrates its in-memory map with load(), but that cache does not auto-refresh across replicas. A shared permissions table therefore does not automatically provide instant invalidation: the application owns the refresh or reload strategy when externally added grants or revocations must propagate, whether that means an explicit reload schedule, rebuilding the handler, or another application-controlled mechanism.

Tenant ownership

Derive tenant and user scope from identity that your service has already verified. Keep a server-side ownership record that relates that identity to each thread and other namespace-bearing resource, then check it before accepting a caller-supplied id.

Do not treat route parameters, request bodies, thread ids, or memory scope strings as authentication. They are addressing inputs. A useful design keeps the verified principal, tenant ownership record, storage namespace, and authorization decision distinct.

memory.resolveScope receives only { routePath, appRoot }. It does not receive a verified request identity or middleware context automatically. If a memory namespace needs per-request identity, provide application-owned wiring that genuinely has access to that identity and test the isolation boundary; do not assume Dawn inferred it.

What deleting a thread removes

DELETE /threads/:thread_id is ordered and not transactional across stores:

  1. The runtime deletes thread metadata first.
  2. If the configured checkpoint saver exposes deleteThread, the runtime awaits that call.
  3. Only after the checkpoint step succeeds—or is unsupported—does it destroy the thread's sandbox state and volume when a sandbox manager is configured.

Optional checkpoint deletion support is the limited best-effort boundary: a saver without deleteThread is skipped. Once a supported saver is called, its errors are not swallowed. A saver error propagates after metadata is already gone and can prevent sandbox cleanup. An HTTP 204 means every step that was attempted completed; it does not mean a cross-store transaction committed.

That operation does not remove global permission decisions, typed long-term memory, arbitrary workspace files, application database rows, or data held by another service. Account deletion therefore requires an application-level inventory and workflow across every relevant store.

Operators need reconciliation for partial deletions, an idempotent retry path for checkpoint and sandbox cleanup, and audit records that distinguish a missing metadata row from fully completed cleanup.

Backup, restore, encryption, and retention

Choose backup and restore procedures for each backend, and rehearse restoring a consistent set of thread metadata and checkpoints. A restored thread row without its corresponding checkpoint history may still exist but cannot reproduce the prior route state. Workspace files, sandbox volumes, and long-term memory need their own backup decisions.

Dawn does not add application-level encryption to stored values and does not provide an account-erasure transaction across these stores. Postgres rows are plaintext application data unless the application or infrastructure adds encryption. Protect database credentials, storage volumes, backup copies, and access logs accordingly.

Define retention by data class: checkpoint history, thread records, permission grants, long-term memory, workspace output, sandbox volumes, and backups rarely need identical windows. Record which component performs expiry or deletion and how failed cleanup is retried and audited.

Migration checklist

  1. Inventory every row in the matrix, including local workspace and sandbox data.
  2. Establish tenant ownership and namespace rules before copying data.
  3. Provision the destination stores and exercise their native schema initialization and backup path.
  4. Quiesce or otherwise account for writes while copying local data with backend-native tooling or an application-specific migration. Dawn does not ship a local-to-Postgres data migration command.
  5. Configure checkpointer, threadsStore, permissions.store, and memory.store explicitly, then verify representative reads and writes.
  6. Decide how workspace files and sandbox volumes survive replacement compute.
  7. Rehearse rollback and restoration before removing the local copies.
  8. Add routing and coordination separately: shared durable stores do not distribute the active-run gate or cancellation registry.