Cubbies
A cubby is a SQLite database your Agent Service declares. Every workflow and code agent the service publishes shares it by alias, and it lives in each vault that connects them. It is where shared working state goes: pipeline status, intermediate results, rows a widget renders.
Shape
| Property | What it means |
|---|---|
| Owned by the service | Every agent the service publishes reads and writes the same cubby by alias. An agent of a different service cannot reach it. |
| One per vault | A service’s cubby is global within a vault: one database per vault, service, and alias, whichever scope triggered the run. A run in vault A never sees vault B’s rows. |
| SQLite | Real tables, indexes, and CHECK constraints, declared by migrations. The sqlite-vec extension is available for vector search. |
| Durable | Survives every run and every redeploy. Disconnecting an agent keeps the data. |
Declare one
A cubby is an alias and a directory of numbered .sql migrations. Keep one
directory per cubby under cubbies/; the directory name is the alias.
ticket-triage/├── cef.config.ts└── cubbies/ └── history/ ├── 001-init.sql └── 002-add-customer.sql-- cubbies/history/001-init.sqlCREATE TABLE IF NOT EXISTS history_messages ( id TEXT PRIMARY KEY, text TEXT NOT NULL, ts INTEGER NOT NULL);Publish the declarations to your Agent Service’s bucket:
cef cubby push --bucket <bucketId>| Option | What it does |
|---|---|
--bucket <id> |
Required. The Agent Service’s bucket, the same one you push agents to. |
--dir <path> |
Directory holding one subdirectory per cubby. Default ./cubbies. |
--alias <name> |
Push only this cubby. |
--cubby-version <semver> |
Version to publish each declaration under. Default 0.1.0. |
Credentials are the same as for cef push: $CEF_DDC_ACCESS_TOKEN or
--access-token. See CLI reference.
You can also declare cubbies in ROC: the Agent Service’s Cubbies page has New cubby and, per cubby, Add migration. Each cubby shows its schema version and published version.
An agent or workflow can also declare a cubby itself, with
cubbies: [{ alias, migrations }] in its config. cef push then declares any
alias the bucket does not have yet, but never changes an existing declaration:
schema changes always go through cef cubby push.
cef cubby push creates no database: a vault’s copy is created, and its
migrations applied, the first time an agent or step touches the cubby (see
When migrations run).
A cubby belongs to
the Agent Service, not to one workflow, so prefix table names with the workflow
or feature that owns them (triage_tickets, triage_escalations), or give each
workflow its own alias. Migrations are forward-only; the rules, reserved
aliases, and when migrations run are on
Cubby schema and migrations.
Use it from a workflow
Two step kinds reach a cubby: cubbyQuery runs a SELECT and puts the rows on
the carried item; cubbyExec runs a write. Values go in args with ?
placeholders, never in the SQL:
{ id: "save", kind: "cubbyExec", params: { alias: "history", sql: "INSERT OR IGNORE INTO history_messages (id, text, ts) VALUES (?, ?, ?)", args: ["={{ $runId }}", "={{ $json.text }}", "={{ $json.ts }}"], },}Parameters and the {{ $runId }} convention are on
Steps: Cubby query and Cubby exec.
Use it from a code agent
ctx.cubby(alias).query(sql, params) returns rows; .exec(sql, params) returns
{ changes, lastInsertRowid }:
await ctx.cubby("history").exec( "INSERT INTO history_messages (id, text, ts) VALUES (?, ?, ?) ON CONFLICT(id) DO NOTHING", [event.eventId, text, Date.now()],);const recent = await ctx.cubby("history").query<{ text: string }>( "SELECT text FROM history_messages ORDER BY ts DESC LIMIT 20",);See Write an agent.
Other readers
| From | How |
|---|---|
| A widget | Named sql queries in the widget declaration. See Widgets. |
| Your app | vault.service(agentServicePubkey).cubby(alias).query(sql, params) with the Vault SDK. |
| ROC | Cubbies → Inspect data: the Tables list with row counts, a SQL runner for queries, and the Migrations applied. In the Workflow Builder, the canvas side rail’s Cubbies button opens the same view beside the workflow. |
Write idempotently
A Task can run more than once and an event can be delivered twice, in a
workflow and in a code agent alike. Key rows with PRIMARY KEY or UNIQUE
(the run id, the event id, a natural key) and write with ON CONFLICT or
INSERT OR IGNORE.
Cubby or Memory Bank
| Cubby | Memory Bank | |
|---|---|---|
| What goes in | Working state: status, intermediate results, rows a widget shows | Durable conclusions the organization should keep |
| Who owns the schema | Your Agent Service | The platform: records and relations |
| Who can read it | Your service’s agents and widgets; the vault’s members through ROC | Anyone the vault’s grants and privacy classes allow |
| Classification | None | Every record and relation carries a privacy class |
Work happens in a cubby; conclusions go to the Memory Bank.
Limits
| Limit | Value | When you hit it |
|---|---|---|
| Size of one cubby | 10 GB | Writes are refused: 413 QUOTA_EXCEEDED, “storage quota exceeded”. Delete or archive old rows; keep large content as objects. |