@zuno-ai/storage
v0.2.2
Published
Postgres-backed durable storage for Zuno. Exposes the `Storage` interface (`agent_sessions`, `messages`, `events`, `tasks`, `workspaces`, `working_copies`, `org_secrets`, memory entries/drafts, chat-thread map) plus the `applyMigrations()` runner that mat
Readme
@zuno-ai/storage
Postgres-backed durable storage for Zuno. Exposes the Storage interface (agent_sessions, messages, events, tasks, workspaces, working_copies, org_secrets, memory entries/drafts, chat-thread map) plus the applyMigrations() runner that materializes the schema on first boot.
Most embedded callers consume this through @zuno-ai/sdk rather than directly — see @zuno-ai/sdk for the high-level facade. Use this package directly when you need to build your own composition root.
Install
pnpm add @zuno-ai/storage drizzle-orm pgdrizzle-orm and pg are peer dependencies — your consumer app pins the versions.
Usage
import { applyMigrations, createPostgresStorage } from "@zuno-ai/storage";
await applyMigrations({ connectionString: process.env.DATABASE_URL! });
const storage = createPostgresStorage({
connectionString: process.env.DATABASE_URL!,
poolSize: 10,
// Optional — required only for `org_secrets` (encrypted with AES-256-GCM).
secretEncryptionKey: process.env.SECRET_ENCRYPTION_KEY,
});
// `storage` is a `Storage` per `./interface` — pass it to `createAgentLoop`,
// `createBuiltinMemory`, etc.applyMigrations is idempotent. Safe to call on every boot.
Database topology
All Zuno tables live in a configurable Postgres schema. createPostgresStorage takes a schema option (default 'zuno') and applyMigrations runs every migration inside that schema; the __drizzle_migrations tracking table goes there too. Two patterns are supported:
Dedicated Zuno database
Provision a dedicated Postgres database for Zuno; Zuno tables go in the zuno schema (or another name you pick); your host app never touches that DB. Lowest-coordination shape.
ZUNO_DATABASE_URL=postgres://zuno:[email protected]:5432/zunoShared database, distinct schema
Use one Postgres database for both your host app (its tables under public.*) and Zuno (its tables under zuno.*). The two namespaces don't collide; the storage pool sets search_path to "zuno",public on every connection so bare-named queries resolve into Zuno's tables first.
import { createPostgresStorage } from '@zuno-ai/storage';
const storage = createPostgresStorage({
connectionString: SHARED_DB_URL,
schema: 'zuno', // default — explicit for clarity
});Two Zunos, one database
Same database, different schemas. Each instance gets its own __drizzle_migrations tracking table and version-bumps independently:
await applyMigrations({ connectionString: SHARED_DB_URL, schema: 'zuno' });
await applyMigrations({ connectionString: SHARED_DB_URL, schema: 'zuno_tenant_b' });Schema names must match the Postgres unquoted-identifier shape ([a-zA-Z_][a-zA-Z0-9_]{0,62}); anything else throws at construction time.
Schema authoring
Migrations are generated with drizzle-kit against the schema defined in src/impl/schema.ts:
pnpm --filter @zuno-ai/storage db:generate # author a new migration
pnpm --filter @zuno-ai/storage db:migrate # apply locallyCommitted migrations live in migrations/ and ship with the published package (files: ["dist", "migrations", ...]), so applyMigrations finds them at runtime.
Conformance tests
./conformance exports a vitest-based test harness (runStorageConformance(factory)) that any Storage impl can pass. It's a workspace-internal helper — not included in the published dist/. Use it to verify a custom Storage implementation against Zuno's contract.
Reference
Storageinterface — the contract every storage backend implementscreatePostgresStorage— the canonical Postgres backendapplyMigrations— drizzle-kit migration runner- Zuno SDK — high-level facade that wires this up for you
