zero-memory
Platform

Usage events

The append-only record of metered work, kept for operational accounting and product insight.

zero-memory records every unit of metered work — an LLM extraction, an embedding batch, an MCP tool call — as a row in an append-only table, public.usage_events. The table is the deployment's own operational accounting ledger: it stays inside your database, and it never contains memory content.

Why it exists

Usage history is unrecoverable: if events are not written from day one, the data simply never existed. A single append-only table, captured from the start, serves two needs at once — operational accounting (how much work ran, for whom, in what units) and product insight (which tools and paths are actually used) — without shipping anything to an external analytics service.

How it works

The table

Each event is one row:

ColumnMeaning
occurred_atWhen the work happened
user_idWhom the work was for
agent_nameThe agent principal, when the caller is an agent
event_typeWhat kind of work (see below)
quantityHow much of it (defaults to 1)
unitcount or tokens
metadataSmall structured context — normally counters and identifiers; recall queries are the bounded exception described below
request_idCorrelation id shared with logs and the audit log

The table is append-only and indexed by time and by (user_id, occurred_at). Row-level security is enabled with no policies — a deliberate deny-all: only the server's service role can access the table directly. End users never query raw rows; owner-scoped dashboard/activity RPCs expose only the caller's derived view.

Event types

Event typeEmitted whenUnit
llm_extractionA metered LLM call completes — ingest extraction, translation, or background judging (hygiene, usefulness, benchmark); metadata carries the input/output token split, the model id, and a purpose tagtokens
embeddingAn embedding batch runs; quantity is the number of textscount
mcp_tool_callAny MCP tool is dispatched; metadata carries the tool name and bounded result counters. Read tools add the scope-usage audit trail: read_mode (explicit, all, hint, session, or open), the session's attached project at call time, and a per-scope count of what was returnedcount
ingest_chunkThe ingest path accepts a chunkcount
session_briefingA briefing build_context call is deliveredtokens
recall_usedA recall outcome is attributed: a later remember supersedes or derives from a recalled memory, the usefulness judge credits it, or a challenge records the negative counterpart (valence: misled)count

Emission is centralized on purpose: MCP tool calls are counted once at the tool dispatcher, not inside every tool, so adding a tool never requires adding instrumentation.

Recording is fire-and-forget

Writes go through a small domain port (UsageRecorder) whose calls are never awaited on the critical path. If the ledger write fails, the failure is logged and the primary operation — the recall, the remember, the ingest — completes normally. Accounting must never break the product.

Attribution

user_id comes from the authenticated request context. Background work (for example, watcher passes) runs outside any request but always knows whose corpus it touched, so it passes the subject explicitly — otherwise the ledger and any check against it would describe different things. agent_name is set when the actor is an agent principal, and request_id ties the event to the same request in the logs — see Observability.

How to use it

Query the table directly with operator (service-role) access:

  • Volume by type — group by event_type and unit over a time window to see extraction token spend, embedding volume, and tool-call counts.
  • Per-user accounting — the (user_id, occurred_at) index makes per-person rollups cheap.
  • Tracing one request — filter by request_id to see every unit of work a single request produced, alongside its log lines and audit entries.
  • Scope-usage audit — group read-tool rows by read_mode, or unnest the per-scope return counts, to answer "are sessions reading pinned to their project, or degrading to everything visible?" The fields are recorded at the source, so the audit is a GROUP BY instead of a fragile reconstruction.

One thing deliberately not in this ledger: refused writes. A refused remember emits no usage event — it is counted by the mcp_write_refused_total Prometheus counter, labelled by refusal reason (see Observability).

Higher-level questions — what the memory actually contributed, not just what it cost — are covered by Value metering, which builds on this ledger's recall_used signal.

Design notes

  • Own table over external analytics. A hosted analytics service would mean a premature dependency and data leaving the deployment. One append-only table covers both accounting and funnel analysis, and keeps a self-hosted installation fully self-contained.
  • Memory content never enters the ledger. Metadata is normally restricted to counters and identifiers — token counts, model ids, tool names, and returned memory ids. The deliberate exception is the user's own recall query, truncated to 200 characters so the activity feed can explain an empty search. It is stored exactly as it was sent, which is also exactly what was searched — nothing rewrites a query, so one string covers both. Treat usage retention as operational metadata retention, not as a content-free guarantee.
  • Deny-all RLS as a pattern. Enabling row-level security while creating no policies is the standard pattern here for operational tables: nothing short of the service role can touch them, and the posture is explicit in the schema rather than implied by omission.

On this page