Skip to content

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.sql
CREATE 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:

Terminal window
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.