@dawn-ai/postgres-storage

Use this when

Use this package when a deployed Dawn application needs shared durable checkpoints, Agent Protocol threads, and permission grants across instances. Each permissions-store instance caches grants and observes another instance's writes after its next load(). The root accepts a structural pool for Node or edge hosts; /node adds pg connection-string convenience.

Install and import

bash
pnpm add @dawn-ai/postgres-storage pg
ts
import { createPostgresThreadsStore } from "@dawn-ai/postgres-storage"
import { postgresCheckpointer } from "@dawn-ai/postgres-storage/node"

Compatibility and audience

SurfaceRuntimePurityAudienceStability
@dawn-ai/postgres-storageedge-safenot-claimedapplicationsupported
@dawn-ai/postgres-storage/nodenode-onlynot-claimedapplicationsupported

The root's edge-safe classification covers its import graph. Local workerd plus a Neon WebSocket pool is tested; it is not a claim about every edge provider or a live Cloudflare deployment.

Public exports

@dawn-ai/postgres-storage

ExportResponsibility
PostgresCheckpointerOptionsConfigure the checkpointer.
DawnPostgresSaverImplement LangGraph checkpoint persistence.
postgresCheckpointerCreate a Postgres checkpointer.
PostgresStoreOptionsConfigure shared connection and table naming.
PostgresPermissionsStoreDefine the durable permissions store.
PostgresPermissionsStoreOptionsConfigure permissions storage and policy.
createPostgresPermissionsStoreCreate a permissions store.
assertIdentifierReject unsafe SQL identifiers.
DEFAULT_SCHEMAExpose the default schema.
DEFAULT_TABLE_PREFIXExpose the default table prefix.
SqlClientDefine the structural SQL client.
SqlPoolDefine the structural SQL pool.
SqlResultDescribe a structural query result.
CreateThreadInputDescribe thread creation.
PostgresThreadsStoreDefine a durable threads store.
PostgresThreadsStoreOptionsConfigure thread storage.
ThreadDescribe an Agent Protocol thread.
ThreadStatusName thread runtime status.
ThreadsStoreDefine the threads-store contract.
createPostgresThreadsStoreCreate a threads store.

@dawn-ai/postgres-storage/node

The /node entry also re-exports every root export. The repeated rows below establish that subpath's ownership; its three factories use the local connection-string-aware implementations.

ExportResponsibility
NodePostgresStoreOptionsAdd connection-string convenience.
NodePostgresPermissionsStoreOptionsAdd connection-string convenience for permissions.
PostgresCheckpointerOptionsRe-export root checkpointer options.
DawnPostgresSaverRe-export the saver class.
postgresCheckpointerCreate a Node checkpointer.
PostgresStoreOptionsRe-export root store options.
PostgresPermissionsStoreRe-export the permissions store type.
PostgresPermissionsStoreOptionsRe-export permissions options.
createPostgresPermissionsStoreCreate a Node permissions store.
assertIdentifierRe-export identifier validation.
DEFAULT_SCHEMARe-export the default schema.
DEFAULT_TABLE_PREFIXRe-export the default table prefix.
SqlClientRe-export the SQL client contract.
SqlPoolRe-export the SQL pool contract.
SqlResultRe-export the SQL result contract.
CreateThreadInputRe-export thread creation input.
PostgresThreadsStoreRe-export the threads store type.
PostgresThreadsStoreOptionsRe-export thread options.
ThreadRe-export the thread type.
ThreadStatusRe-export thread status.
ThreadsStoreRe-export the threads-store contract.
createPostgresThreadsStoreCreate a Node threads store.

Key contracts

PostgresStoreOptions

ts
export interface PostgresStoreOptions {
  readonly pool?: SqlPool
  readonly ownsPool?: boolean
  readonly assumeMigrated?: boolean
  readonly schema?: string
  readonly tablePrefix?: string
}

Fields: @dawn-ai/postgres-storage#.:PostgresStoreOptions

FieldTypeRequiredDescription
readonly poolSqlPoolnoSupply the pool used by every store call.
readonly ownsPoolbooleannoEnd the supplied pool when this store closes.
readonly assumeMigratedbooleannoSkip this instance's migration pass.
readonly schemastringnoSelect the Postgres schema.
readonly tablePrefixstringnoPrefix this application's tables.

Although pool is optional in the shared type, root factories fail without one. Use /node when connectionString or standard pg environment defaults should create an owned pool.

ts
export interface PostgresPermissionsStoreOptions extends PostgresStoreOptions {
  readonly config?: PermissionsFile
  readonly mode?: PermissionMode
}
ts
export declare function postgresCheckpointer(
  options?: PostgresCheckpointerOptions,
): DawnPostgresSaver
ts
export declare function createPostgresThreadsStore(
  options?: PostgresThreadsStoreOptions,
): PostgresThreadsStore
ts
export declare function createPostgresPermissionsStore(
  options?: PostgresPermissionsStoreOptions,
): PostgresPermissionsStore
ts
export interface NodePostgresStoreOptions extends PostgresStoreOptions {
  readonly connectionString?: string
}
ts
export interface NodePostgresPermissionsStoreOptions extends PostgresPermissionsStoreOptions {
  readonly connectionString?: string
}
ts
export declare function postgresCheckpointer(
  options?: NodePostgresStoreOptions,
): DawnPostgresSaver
ts
export declare function createPostgresThreadsStore(
  options?: NodePostgresStoreOptions,
): PostgresThreadsStore
ts
export declare function createPostgresPermissionsStore(
  options?: NodePostgresPermissionsStoreOptions,
): PostgresPermissionsStore

Behavior contract postgres-storage.migration.instance-scoped

Migration memoization belongs to each store instance; assumeMigrated skips that instance's migration pass, while an unflagged instance begins a locked transaction.

Behavior contract postgres-storage.entry-split

The main entry links without Node built-ins for edge pools, while /node deliberately fails an edge bundle because it imports pg for connectionString convenience.

Migration and pool ownership

Migration state is instance-scoped. In a per-request host, migrate an unflagged cold set of all three components, then set assumeMigrated only for the same database, schema, prefix, component, and current package schema. A wrong assertion fails on the first query. Checkpoint, thread, and permission migrations are separate.

Another process sees a durable permission grant only after that store calls load(). When you construct a custom permissions store, pass the resolved mode and config explicitly; Dawn does not overlay sibling configuration onto an already-created store.

Do not mark every store as owning one shared pool. An injected pool defaults to caller-owned and receives no error listener from Dawn; setting ownsPool: true makes that store end it. A /node factory-created pool is owned, monitored, and closed by that store.

Schemas, table prefixes, and thread IDs organize data; they are not tenant authorization boundaries. Rows are plaintext unless your database and infrastructure provide encryption.

ts
import { Pool } from "pg"
import {
  createPostgresPermissionsStore,
  createPostgresThreadsStore,
  postgresCheckpointer,
} from "@dawn-ai/postgres-storage"
 
const pool = new Pool({ connectionString: process.env.DATABASE_URL })
pool.on("error", (error) => console.warn("Postgres pool error", error))
const checkpointer = postgresCheckpointer({ pool })
const threadsStore = createPostgresThreadsStore({ pool })
const permissionsStore = createPostgresPermissionsStore({ pool })
await Promise.all([checkpointer.ready(), threadsStore.ready(), permissionsStore.ready()])
 
// During application shutdown:
await Promise.all([checkpointer.close(), threadsStore.close(), permissionsStore.close()])
await pool.end()

Continue with Persistence and Tenancy, Production Topology, and Edge and Hono.