paybondpaybond
Sign in

Tenant model

The single severity-zero invariant in Paybond and how it is enforced per service and per surface.

Tenant model

Paybond is tenant-scoped. Tenant isolation is enforced by credentials and verified server-side on every read and write. This document is the platform-facing summary; the normative source of truth is docs/security/tenant-scoping.md, which feature tasks cite directly.

Severity-zero invariant

Cross-tenant data access — reading or writing another tenant's intents, ledger rows, reputation rows, caps, or payment objects — is a severity-zero defect. Treat it as a production incident and block release until remediated.

Derivation rule

Every state-changing path must derive tenant or operator context from authenticated credentials (session, API-key namespace, signed operator proof, or mTLS identity where applicable) before any persistence or outbound payment call.

Unauthenticated client-supplied identifiers (JSON body tenant_id, query string org_id, path guesswork) are not authoritative for authorization. They may be used only as hints that must be validated against the authenticated context.

Enforcement by surface

Sections below are grouped by service (what enforces isolation), not by implementation language. For monorepo layout and language stacks in one place, see Repositories and runtimes on the platform overview.

Harbor

  • Intents, capability issuance, ledger appends, and predicate evaluation agree on the same tenant record for a single request.
  • Evidence binding, settlement, and cap redemption are refused if they would join artifacts across tenants.
  • Idempotency keys are scoped per (tenant_id, operation) — cross-tenant collision is impossible.

Signal (indexer and APIs)

  • Postgres queries and writes include an explicit tenant_id / org_id filter or column set from authenticated context, not from unverified request fields.
  • Background workers receive tenant scope from configuration or message envelopes that were produced by an authenticated upstream.

Ledger

  • Each append carries tenant scope inside the signed payload envelope where multi-tenant deployments share one log; Harbor enforces tenant consistency before the append is accepted.
  • Single-deployment logs still validate tenant consistency so a future multi-tenant layout cannot silently break isolation.

Kit (SDKs)

  • Configured with operator credentials and tenant id; it does not send another tenant's intent_id, capability token, or settlement command to Harbor.
  • Outbound HTTP attaches the same tenant context the host application authenticated, via ServiceAccountHarborSession.

Admin and operator dashboards

  • Sessions bind the admin user to an org/tenant; browser API calls go through server routes that re-validate that binding.
  • Cross-tenant list or search is forbidden without a mandatory tenant filter derived from the session.

Credentials and claims

CredentialAudienceTenant carrier
Session JWT (cookie or Bearer)Browser → Gatewaytid claim. Never reused across tenants.
Harbor access JWTKit / service account → Harbortid claim and sub service-account id. Minted from /v1/auth/harbor-access.
Support session JWTAudited operator → Gatewaytid claim and audited support id. Short-lived.
Audit export download JWTBrowser → bundle streamtid claim, aud = paybond-audit-export-download, and bundle job_id. Not exchangeable for any other call.
Operator x-tenant-id headerHarbor (in-VPC) or Gateway (where accepted)Must match JWT tid; mismatches return 401 before the handler runs.

Forbidden patterns

  • Deriving tenant scope from request bodies, path parameters, or query strings only. These must be validated against the JWT claim.
  • Re-using a Harbor client or Paybond Kit session across tenants.
  • Logging a different tenant's intent_id, operator_did, or capability_token during debugging.
  • Bypassing the Gateway to call Signal or Harbor directly from browser code.