Obol has three separate identities that are never interchangeable, and one scoping key that appears on every stored row. Getting these two ideas right explains most of the system’s shape.

The tenancy hierarchy

Hierarchy
The organization is a logical level, not a table: workspaces.org_id is a plain column, and org-level commercial state lives on billing_accounts, anchored to a designated billing workspace. A workspace carries an environment: dev, stage, or prod. The environment is part of both visibility and authorization, and the rule is absolute — a dev key can never reach a prod connection.

workspace_id is the isolation boundary

Every Postgres row and every Redis key carries workspace_id. That is tenant isolation; there is no second mechanism (ADR-0006).
Scoping
Two consequences follow:
  • Snapshots are per-workspace. Control compiles one bundle of keys, connections, tools, and the Cedar policy set for each workspace. The gateway and control never read another workspace’s activation, even for a shared publisher artifact: under ADR-0020 enabling one materializes an immutable, content-addressed bundle row and snapshot entry for that workspace. That materialization is unbuilt — there is no connector_bundles table, and control reads a reviewed pack off disk instead.
  • Control’s API is not an oracle for workspace ids. On /api/v1/workspaces/{workspace_id}/…, membership resolves before the action, so a non-member receives the same 404 that a non-existent workspace returns. A 403 is only ever seen by a member whose role lacks the action.

Three authentications

A Clerk session identifies an operator in the browser (ADR-0033). Account creation is invitation-gated by the Clerk instance.After authentication, control still owns workspaces, membership, and every workspace_id: each /api/v1 route verifies the session against the issuer’s published JWKS, resolves membership, and applies one named action — never a role, org, or claim taken from the browser.A dashboard session never becomes a virtual key or an MCP JWT, never reaches the gateway, and never carries an authorization decision. Clerk holds no vendor secret and no key-encryption material.With no issuer configured, control’s operator routes answer 503 and the dashboard refuses to serve. Fail-closed is the specified behavior.
These three never convert into one another. A browser session cannot be exchanged for a virtual key; a virtual key cannot be exchanged for a vendor token; a vendor token never travels back toward the caller.

Inbound versus outbound, concretely

Credential flow
ADR-0024 requires that a worker never see a vendor credential and never call a vendor: it returns a bounded, bundle-declared request plan, and the gateway validates it, injects the credential, and performs the HTTP itself. That protocol is not implemented — the shipped WorkerExecutor still runs ADR-0014’s hand-off and would send the credential to a worker in x-obol-upstream-authorization. No worker exists to receive it, so no credential crosses that hop today. The tier is parked (ADR-0058). See Trusted workers.

Custody modes

A connection records how the vendor credential is held. Even under federated custody, invariant 8 holds on the vendor-facing identity: every broker account and broker OAuth client is workspace-scoped. Pooled platform-wide broker keys and a shared broker OAuth app are forbidden.
Hybrid custody — Obol vaulting a read-only credential alongside a broker-held write credential, which is the only path by which a federated write reaches verified — is explicitly deferred to phase 2 and requires amending the connection contract. It does not ship today.

What a virtual key carries

A key is not merely an authenticator; it is the scope object. Keys can also run in progressive mode, exposing only search_tools and call_tool instead of the full catalog, which keeps large tool schemas out of the model’s context window. See Virtual keys for minting, rotation, and revocation.

Human roles are control-owned, not Cedar

Cedar authorizes agent traffic. Operator permissions in the dashboard are a separate, control-owned mechanism over a closed role vocabulary (ADR-0037). An unknown stored role fails closed. Every workspace-scoped operation applies exactly one of eleven named actions — workspace.read, key.manage, policy.draft, policy.publish, connection.manage, routing.publish, approval.decide, approval.policy.manage, audit.read, usage.read, membership.manage — and the test inventory is derived from the generated OpenAPI paths rather than hand-listed, so a new route fails the suite until someone assigns it an action. A matching approval policy can further restrict whether an admin or owner may decide a particular held call. Approval permission never substitutes for Cedar tool permission.

Virtual keys

Minting, scoping, and revocation.

Vault

Envelope encryption and KEK providers.

Credential brokering

Native custody, federated custody, and what each one can prove.

Invariants

Rules 1, 5, and 8 in full.