You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The hosted Worker has zero metering or audit. No usage table exists in D1 (migrations 0001–0006 cover accounts, namespaces, growth loops, schedules — none record per-action usage). Authed users are hardcoded quota: { ... unlimited: true } (apps/api/src/worker.ts:1503) and the only counter is the 1/day demo-IP quota (consumeDemoQuota, worker.ts:4296; table contextmem_demo_limits). The roadmap calls this ledger "the shared spine feeding billing, observability, AND on-chain attribution — build it first."
Goal / user story
As the platform owner, I want every billable/auditable action (extract, recall, remember, mcp_read, ai_query, chat) recorded as a durable per-namespace event so I can meter usage, power an observability dashboard, enforce quotas, and later anchor on-chain attribution receipts — built once.
Acceptance criteria
New migration 0007_usage_events.sql creates contextmem_usage_events (id, ts, owner_id, namespace, action with a CHECK on the enum, units INTEGER, request_id, source, metadata_json) + indexes on (namespace, ts) and (owner_id, ts).
A single recordUsage(env, { ownerId, namespace, action, units, requestId, source, metadata }) helper inserts one row, never throws (try/catch + ctx.waitUntil), and is unit-tested.
Events are emitted from the hosted handlers: extraction/build (createNamespaceBuildJob), recall (/api/memwal/recall, worker.ts:631), chat (memwalChat, worker.ts:634/3498), ai-query (aiQueryRun, worker.ts:1349), hosted MCP reads (/mcp, isMcpPathworker.ts:800), and demo extractions.
Each event carries a stable request_id for idempotency so retries don't double-count.
GET /api/usage?namespace=&from=&to= returns aggregated counts/units per action (owner-scoped) using the index, not a full-table scan.
No request bodies or secrets are stored: a test asserts Authorization/delegate keys never leak into metadata_json.
Implementation notes
Reuse the D1 upsert pattern from consumeDemoQuota (worker.ts:4304). Wrap every emit in ctx.waitUntil(recordUsage(...)) so logging never blocks responses. Define the action union in a shared module so packages/mcp tool calls can emit the same events. Keep the table append-only; aggregate with SELECT action, COUNT(*), SUM(units) ... GROUP BY action. A daily rollup table can come later if volume grows — not required now. No wrangler.jsonc change (uses the existing CONTEXTMEM_DB binding).
Sui Overflow angle
This ledger is the precondition for on-chain attribution receipts: each metered extract/recall/remember becomes the exact payload a Talus/Nexus node anchors on Sui (caller, namespace, action, artifactDigest, ts). Building it now turns the later receipt work into a thin write-through over existing rows rather than a from-scratch capture layer during the demo crunch.
Dependencies
Owner-scoping of /api/usage depends on the "hosted accounts + owner-scoped authorization" issue (otherwise ownerId is spoofable). The on-chain attribution-receipt issue consumes this ledger.
Part of the ContextMEM roadmap (#4) • Sui Overflow build.
Context
The hosted Worker has zero metering or audit. No usage table exists in D1 (migrations
0001–0006cover accounts, namespaces, growth loops, schedules — none record per-action usage). Authed users are hardcodedquota: { ... unlimited: true }(apps/api/src/worker.ts:1503) and the only counter is the 1/day demo-IP quota (consumeDemoQuota,worker.ts:4296; tablecontextmem_demo_limits). The roadmap calls this ledger "the shared spine feeding billing, observability, AND on-chain attribution — build it first."Goal / user story
As the platform owner, I want every billable/auditable action (extract, recall, remember, mcp_read, ai_query, chat) recorded as a durable per-namespace event so I can meter usage, power an observability dashboard, enforce quotas, and later anchor on-chain attribution receipts — built once.
Acceptance criteria
0007_usage_events.sqlcreatescontextmem_usage_events(id, ts, owner_id, namespace, action with a CHECK on the enum, units INTEGER, request_id, source, metadata_json) + indexes on(namespace, ts)and(owner_id, ts).recordUsage(env, { ownerId, namespace, action, units, requestId, source, metadata })helper inserts one row, never throws (try/catch +ctx.waitUntil), and is unit-tested.createNamespaceBuildJob), recall (/api/memwal/recall,worker.ts:631), chat (memwalChat,worker.ts:634/3498), ai-query (aiQueryRun,worker.ts:1349), hosted MCP reads (/mcp,isMcpPathworker.ts:800), and demo extractions.request_idfor idempotency so retries don't double-count.GET /api/usage?namespace=&from=&to=returns aggregated counts/units per action (owner-scoped) using the index, not a full-table scan.Authorization/delegate keys never leak intometadata_json.Implementation notes
Reuse the D1 upsert pattern from
consumeDemoQuota(worker.ts:4304). Wrap every emit inctx.waitUntil(recordUsage(...))so logging never blocks responses. Define theactionunion in a shared module sopackages/mcptool calls can emit the same events. Keep the table append-only; aggregate withSELECT action, COUNT(*), SUM(units) ... GROUP BY action. A daily rollup table can come later if volume grows — not required now. Nowrangler.jsoncchange (uses the existingCONTEXTMEM_DBbinding).Sui Overflow angle
This ledger is the precondition for on-chain attribution receipts: each metered
extract/recall/rememberbecomes the exact payload a Talus/Nexus node anchors on Sui (caller, namespace, action, artifactDigest, ts). Building it now turns the later receipt work into a thin write-through over existing rows rather than a from-scratch capture layer during the demo crunch.Dependencies
Owner-scoping of
/api/usagedepends on the "hosted accounts + owner-scoped authorization" issue (otherwiseownerIdis spoofable). The on-chain attribution-receipt issue consumes this ledger.Part of the ContextMEM roadmap (#4) • Sui Overflow build.