Skip to Content
ArchitectureData Model

Data Model

All control-plane state lives in one DynamoDB table: agent-runner-<env>. It is PAY_PER_REQUEST, KMS-encrypted, with PITR and DynamoDB Streams enabled.

Single-table design

EntityPKSKGSI1 PKGSI1 SK
TenantTENANT#<id>METASLUG#<slug>TENANT
UserUSER#<sub>METATENANT#<tenant_id>USER#<sub>
GrantTENANT#<id>GRANT#<sub>
API KeyTENANT#<id>KEY#<prefix8>
Model CatalogMODELSMODEL#<model_id>
Billing RollupTENANT#<id>BILLING#<YYYY-MM>#<model>#<region>
Free CreditTENANT#<id>CREDIT#FREE
Audit EntryTENANT#<id>AUDIT#<ts>#<uuid>

Access patterns

  • Resolve tenant by slug — the proxy reads the Host header slug and queries GSI1 on SLUG#<slug>. Cached in-memory for 60 seconds.
  • List a tenant’s grants / keys — query PK=TENANT#<id> with the appropriate SK prefix.
  • Fetch the whole model catalog — all catalog entries share PK=MODELS, so one Query (SK begins_with MODEL#) returns everything.
  • Billing rollups — atomic UpdateItem ADD on per-month, per-model, per-region counter rows.

The Stripe customer is stored on the owner’s USER#<sub> META row, not on the tenant. One owner sharing multiple workspaces shares one Stripe customer; each workspace has its own stripe_subscription_id on its TENANT#<id> META row.

For full attribute schemas and additional access patterns, see specs/ARCHITECTURE.md in the repository.

Last updated on