Keyring

Marketplaces and platforms

Many tenants, many keys per tenant, per-tenant budgets that survive rotation, and a node that serves only the tenants it needs.

A marketplace or platform API has many small tenants, each with several keys, and the questions are about the tenant rather than the key: what did this seller do this month, how much may they do per day, and how do I revoke everything they hold.

Per-tenant limits

scope: 'tenant' shares one counter across every key the tenant holds, so a quota is per organisation whatever the customer does with keys, and it survives a rotation. scope: 'key' beside it bounds any single integration:

{
  "rate_limits": [
    {
      "id": "per-key-minute",
      "limit": 600,
      "window_ms": 60000,
      "algorithm": "sliding",
      "scope": "key"
    },
    {
      "id": "per-tenant-day",
      "limit": 100000,
      "window_ms": 86400000,
      "algorithm": "fixed",
      "scope": "tenant"
    }
  ]
}

Test and live traffic never share a counter, so a seller's free test traffic cannot spend their live quota.

Per-tenant usage and billing

A tenant's page shows request counts per project and environment per month, and GET /v1/usage/tenants returns the same. Billing counts active tenants per month from live traffic only, from one definition in the control plane's schema, so the number on the invoice and the number in the dashboard cannot disagree.

Revoking a tenant

Deleting a tenant revokes every key beneath it in the same transaction, so every SDK node learns about all of them through the ordinary feed. A tenant's embed sessions are ended at once by bumping its embed epoch from the tenant's page.

Nodes that serve a slice

A node that serves only some tenants, a regional cell or a dedicated worker, passes tenantIds and caches only their keys, which keeps memory proportional to what it serves. The filter can only be widened at runtime, with admitTenants(), and a narrowed node is incomplete by construction: a key from a tenant outside the filter is in the fail-open table's last row, so pair narrowing with stale-then-closed unless that is acceptable.

Large projects

A node caches the whole project by default and raises its own bound up to 38,000 keys, about 31 MiB. A project larger than that on a node that has never served refuses to start rather than serve an incomplete cache; a node already serving keeps serving and reports the condition on every poll and in stats().policyCache.scopeTooLarge, which is the readiness predicate to drain it on.

On this page