Skip to content
Like what we’re building? Star on GitHub

Data model — credentials, metering and routing policy ​

Seventeen Mongo collections hold what funds a run: a credential sealed at rest, the connector alias a workflow addresses it by, the bundle that carries it to a runner pod, the four meters that bill it, and the policy that decides where it may route.

Drawn from the code at commit 6c136a77d — main at v3.281.3, with #2652, #2005 PR-A and the PR-B'1 carriage; how this page is kept true: README.

Block 1 — a credential at rest, and who may see it ​

  • api_keys, generic_secrets and bot_secret_bindings are scoped by the team and fail closed. Two api_keys reads and writes are the stated exceptions: GetOwned carries no tenant term at all and is bounded by scope_user instead — only a key's own owner is served that way — and MarkFingerprintUsed fails open on a tenant-less context. Every api_keys write is a $set of the fields it names (Patch); the store has no whole-document writer, so a row keeps the columns a newer server build wrote on it and a stale read never carries back an is_default or a last_used_at another writer just moved.
  • oauth_credentials has no tenant_id field and no tenant filter at all. Its whole tenancy model lives in the user_id owner namespace, and the spellings do not mean what they look like: org: carries a TEAM id, while the genuinely org-level one is orgtier:.
  • A bot id is a slug, never a key: bot_secret_bindings.bot_id, api_keys.bots and cred_pledges.bots are compared by exact string equality — only api_keys.bots is canonicalised; bindings and pledges keep the raw spelling — and the launch resolves a slug through team bot_sources, then platform bot_sources, then the baked catalog on the server's own filesystem. An FK to bot_sources._id (a UUID) would be wrong.
  • (scope_team, name) on generic_secrets is held by NOTHING: two team-wide secrets may share a name.
  • teams, users and orgs are anchors — the identity model is its own page. Every edge to orgs on this page is zero-or-one except cred_pools.org_id, so no collection here but a pool may be read as proving a parent org.

Block 2 — a credential in flight: the sealed bundle and the pool lease ​

  • A sealed bundle carries the credential itself, not a pointer to it. The ref maps ride beside it, and only the OAuth ref is drawn, dotted, because no query on run_secrets resolves them — they live inside sealed_bundle, not in an indexed field. The api_keys edge carries no id at all.
  • The link runs the other way from what a reader expects: the run document holds no secrets_ref. run_secrets points back at the run.
  • Deleting a pledge removes its own document and nothing else: the cred_pool_usage rows keyed on its id survive until their own 400-day TTL, which is why that edge is optional at both ends.
  • No DOCUMENT id links a lease and a bundle — which is why no edge joins them. The lease's inlined source / ref / key_id name the pledged credential and are copied from the pledge, never resolved independently — which is why the lease draws no second edge to api_keys.
  • The zero audience admits only teams of the owning org — the opposite polarity from orgs.credential_audience, whose zero value admits nobody.

Block 3 — metering and policy: four meters on four identities ​

  • Four meters, four identities, and none substitutes for another. Their tier vocabularies differ too: credential_usage.tier has four values because it merges a tenant's key and its subscription into team, where runs.credential_tiers keeps the publisher's five and holds byok and oauth-forfait apart.
  • Five dotted edges in this block carry the parent id inside a composed string: usage_windows.key, org_usage._id, llm_routing_policies._id. No store joins on orgs._id or teams._id, so none of them is a key lookup. The solid edges are the ones whose field is the reference: credential_usage.tenant_id and model_prefs.tenant_id, both written verbatim, and platform_credentials.teams, an allow-list of team ids. The sixth dotted edge, runner_pool, is a pool name resolved in the runner_pools family.
  • org_usage's subject is a team id whenever the launch gate read the zero Org — an org-less team, or an org the gate could not read — so an org| subject is not proof of an org, and a meter row's owner is zero-or-one.
  • usage_windows.key carries five scope spellings — platform, local, tenant:<id>, org:<id> and the literal account. Three of its windows (frequency, spend, auth) are not provider windows at all.
  • platform_settings is one collection, one document per family. This page draws the four a run's funding and routing read — platform_credentials, runner_pools, usage_caps and budget_floor; the bot-facing families are drawn on data-model-forge-bots.md. Sovereign runner pools are the runner_pools family, not a collection of their own.
  • llm_routing_policies is not a platform_settings family: its documents are tenant data, one per org or team, while platform_credentials.routing holds the platform level of the same policy. A document is a team's or an org's, never both — its _id carries exactly one of the two prefixes.

Block 4 — connector connections: a team's alias for one connector instance ​

  • The unique (tenant_id, connector, alias_norm) index is what keeps the alias rule true between replicas: the memory and file twins check alias-free under a lock they hold, and a cloud deployment has no such lock. Two concurrent Creates of one alias both passing an in-code check would answer one name twice — the index turns the second into a duplicate-key refusal the store reports as ErrExists.
  • The tenant is in every filter, not merely on the record: a read, update or delete that exists but belongs elsewhere is not found, never forbidden. updated_at is the record's own version — the update writes against the stamp it read, so a successful read followed by a zero-match replace is a conflict, not a missing record.
  • auth_placement records what the scheme said at creation, so a workspace-tier package update that keeps the connector's id but moves the credential into a query string cannot move a stored credential with it: the stored placement is compared at resolution and a mismatch is refused by name.

The credential tier walk ​

walkCredentialPlan enumerates seven tiers in a fixed order. These seven are the plan's own; what a run is stamped with is the five of runs.credential_tiers.

  • The two gates are not symmetric: HAS closes the Pool tier and nothing else, so a team holding its own unpinned key still walks Platform. Of the two, only PG closes Platform.
  • SOV is a closure, not the skip of one tier: a team mapped to a sovereign runner pool, or holding llm_fallback: none, loses Org, Pool and Platform. The two conditions diverge at the refusal: under llm_fallback: none the launch is refused as RF draws it, while a pool-mapped team on the default fallback is, by default, at most warned.
  • envFunded removes every tier except Generic. It holds only for a run whose every model route names an openai_compatible/… model, and not for every such run — not, for example, when a route's model or provider text reads the environment.
  • RF's funding check reads the providers the walk attributes to the run's routes (spendableProviders), so a route the walk cannot read — an unpinned agent, a human gate's or a tool recovery's model, a forfait rung of the routing ladder, for example — can turn a refusal into a start, unless the policy is llm_fallback: none and the team holds no credential of its own.
  • After the walk, the fallback door (acquireDoorGrant) can lease a pool credential; SOV shuts it.
  • Tier 7 is not a fallback credential but a retry posture: a credential a tier passed over comes back only where the restore's own vetoes allow it; the run then parks on its refusal with a durable retry.
  • Outside the sovereign posture nothing here refuses a credential-less run by default.

Collections ​

collectionpackagedocument structtenant field (values)unique / TTL indexestwin
api_keyspkg/secretsApiKeytenant_id: team | orgtier:<org> | platform:none unique; no TTLnone (cloud-only)
connectionspkg/connectionmongoDoc (Connection + derived alias_norm)tenant_id: teamtenant_connector_alias unique on (tenant_id, connector, alias_norm); no TTLfile connections.json — the memory twin checks aliases under a lock the Mongo twin replaces with the unique index
oauth_credentialspkg/secretsOAuthRecordnone — user_id namespace: user | org:<team> | orgtier:<org> | platform:user_id+kind+rank unique; no TTLnone (cloud-only)
oauth_pendingpkg/secretsOAuthPendingnone — owner_key namespaceowner_key+kind unique; TTL expires_at (10 min)none (cloud-only)
generic_secretspkg/secretsGenericSecrettenant_id: team (empty in the file store, which pins scope_team: local)none unique; no TTLfile secrets.json — the file and memory twins keep no order among duplicate names
bot_secret_bindingspkg/secretsBotSecretBindingtenant_id: teamtenant_id+bot_id+secret_name_for_workflow uniquenone (cloud-only)
run_secretspkg/secretsRunSecretsRecordtenant_id: team (fails open with no ctx)none unique; TTL expires_at (24 h)none (cloud-only)
cred_poolspkg/credpoolPoolnone — org_id names the owning org (GetByOrg resolves on it)none unique (pool_org only); no TTLnone (cloud-only)
cred_pledgespkg/credpoolPledgenone — _id is the donor'snone unique; no TTLnone (cloud-only)
cred_leasespkg/credpoolLeasetenant_id: team (audit + the cross-team takeover guard)none unique; TTL acquired_at (90 d)none (cloud-only)
cred_pool_usagepkg/credpoolledgerDocnone — _id is pledge + periodnone unique; TTL period_start (400 d)none (cloud-only)
credential_usagepkg/credusageusageDoctenant_id: team | empty (an _id segment)none unique; TTL month_start (400 d)none (cloud-only)
cred_slotspkg/storecredSlotDocnone — _id is the credential fingerprint, one cap shared by every tenant a platform key servesnone unique; no TTL (the reconcile pulls a holder whose run document is gone)memory-only twin credslot_fs.go (one process, one mutex)
usage_windowspkg/usagecapreadingDocnone — scope inside key: platform | local | tenant:<id> | org:<id> | accountnone unique; TTL observed_at (14 d)memory-only
org_usagepkg/orgusageusageDocnone — _id subject is an ORG (a team for an org-less team, or an org the gate could not read)none unique; TTL month_start (400 d)none (cloud-only)
model_prefspkg/modelprefsPreftenant_id: team | empty in local mode (an argument, not ctx)tenant+user+key unique, tenant+user+slot unique partialfile model-prefs.json
llm_routing_policiespkg/platformcfgRoutingPolicyRecordnone — _id is team:<id> | org:<id>none (no EnsureSchema)none (cloud-only)
platform_settingspkg/platformcfg, pkg/usagecap (the budget_floor type is pkg/budgetfloor's)PlatformCredentials, RunnerPools, settingsDoc, budgetfloor.Policynone — platform-scoped, one document per familynonenone (cloud-only)
runspkg/store/mongostore.Runtenant_id: teamdrawn on data-model-runs.mdfile runs/<id>/run.json
teamspkg/identityTeam_id IS the tenantslug unique, runner_pool unique partialnone (cloud-only)
userspkg/identityUsernoneemail uniquenone (cloud-only)
orgspkg/identityOrg_id IS the orgslug unique; migrated_from_team_id unique partialnone (cloud-only)
FS: baked catalogbots/———FS, not Mongo