Skip to main content

ADR-029: Connector Identity Model — Phased (broker interface fixed, provider hardens)

Status

Accepted (decision) — deployment pending validation. 2026-07-21. Supersedes the "defer Key Vault indefinitely" framing of the original tracking issue (docs#322): the Key Vault broker is implemented and merged (platform#1874), so the provider is no longer deferred.

Do not read "accepted" as "operational." The broker code is merged; the production Key Vault, managed identity, per-tenant vaults, RBAC and tenant → vault map are not yet provisioned or applied (platform#1873 and infrastructure#134 are open drafts). The authoritative status table is 16-secrets-and-credential-management.md §0. A material residual risk — IMDS is a VM-level, not process-level, boundary — is open and documented in §3c of that doc.

Fulfils sv0-platform#1273. The architecture-of-record is 16-secrets-and-credential-management.md; this ADR is the ruling behind it.

ADR number note: 028 is reserved by sv0-platform#1255; 029 is this ADR per docs#322.


Context

ADR-027 established that a typed CredentialBroker is the only runtime path that resolves a CredentialsRef. It left open which provider backs the broker. The original plan (docs#322) was speed-first: ship the flat env-broker now, defer Key Vault and federation indefinitely. That plan has been overtaken by events — the per-tenant Key Vault broker landed (sv0-platform#1872 / #1874, ADR-027 Slice 6) as part of the first-production-client (TPX) push, and the platform's own runtime secrets moved to a Key Vault boot-fetch at the same time.

The risk this ADR closes: without a recorded ruling, a connector landing in the interim quietly defaults to per-tenant long-lived secret storage — including cloud keys (AWS) we would later have to migrate off — because that is the path of least resistance. We want the simplest terminal identity model per connector to be the default, not an afterthought.

The industry evidence (Wiz / Orca / Datadog, surveyed in enterprise credential-exchange patterns) is unambiguous: for first-class clouds, exchange a trust relationship, never a long-lived secret. Enterprise buyers expect this.


Decision

The broker interface is fixed; the provider hardens over time. Three phases, one interface.

  1. Env-broker phase — dev / demo ONLY, never a production control. All tenant secrets are materialized flat in the VM environment at boot. A crash dump, debug endpoint, or process compromise exposes every tenant's credentials, not one. This does not satisfy SecurityV0's selected tenant-isolation and least-privilege control design supporting SOC 2 CC6.1/CC6.6 and ISO 27001:2022 A.8.15 / A.5.15, and must not back a real-tenant scan. (Formal audit pass/fail language waits on the control matrix and auditor review.)

  2. Key Vault broker phase — the production store; code merged, deployment pending (ADR-027 Slice 6). Each tenant's credentials live in a SecurityV0-managed vault dedicated to that tenant (in SV0's subscription — not a customer-owned vault). The broker resolves them per scan via the VM's managed identity, with these guards:

    • Server maps tenant → vault; the request never does (a request-supplied vault URL is rejected — SSRF / credential-confusion guard). Enforced.
    • Secret name = server-controlled tenant slug + truncated sha256("<tenant>:<ref>")collision-resistant, not mathematically injective (the digest is truncated to 48 bits); the server-controlled slug prefix carries the tenant separation. Enforced.
    • Bundles cannot set process-control env vars (PATH, LD_*, DYLD_*, NODE_OPTIONS, PYTHONPATH, …) — no subprocess hijack. Enforced.
    • The broker carries no client secret — it authenticates as the VM's managed identity. True, but not a containment boundary: IMDS issues tokens to any code on the VM, so a compromised connector subprocess can currently mint the same token. See doc 16 §3c; isolation is an open pre-production requirement.

    For this provider Mongo holds only a reference. Note the coexisting paste provider stores AES-256-GCM ciphertext in Mongo, so the accurate global claim is "no plaintext customer secret in Mongo."

  3. Federation — the endgame, no long-lived secret exchanged. For each connector, the simplest-terminal identity model:

    ConnectorTerminal modelPer-tenant secret?
    AWSCross-account assume-role + external-IDNo
    Entra / GraphMulti-tenant app + admin consent + SV0 UAMI workload-identity-federation (cert transitional)No
    GCPWorkload Identity FederationNo
    GitHubSV0 GitHub App (one private key)No
    Atlassian CloudForge app + Forge RemoteNo
    ServiceNow / other SaaS (no federation model)OAuth client_credentials, customer-pasted → tenant Key VaultYes (in KV, phase 2)

    The broker interface is unchanged by federation: for a federated cloud the broker mints a token instead of fetching a stored secret. The enabling seam is an injectable Azure credential provider (connectors#274, cert + WIF) — connectors hardwire ClientSecretCredential today, which is the one code blocker to the no-secret endgame.

Default rule for new connectors: target the simplest-terminal model for that source system from the table above. Only fall back to per-tenant KV secret storage (phase 2) when the source system has no federation model. Never default a first-class cloud (AWS/Azure/GCP/GitHub) to stored secrets.


Consequences

Positive

  • The interface is stable across all three phases — a provider upgrade is never a connector rewrite (ADR-027's broker seam absorbs it).
  • Tenant routing for stored secrets is enforced in code (separate vault, server-side map, collision-resistant names) rather than by operator discipline. Note this is a routing/audit/RBAC boundary — containment of a VM compromise additionally requires the IMDS isolation work (doc 16 §3c).
  • Once deployed and validated, the platform can make a defensible customer/auditor statement while federation lands connector-by-connector. What is defensible today vs. what must wait is enumerated in doc 16 §10.

Negative / cost

  • Per-tenant Key Vault is operationally heavier than a flat env: one vault + access grant + tenant → vault map entry per tenant. Accepted — it is the price of a real per-tenant boundary.
  • Federation is not free: each connector needs its injectable-credential seam (connectors#274) before it can mint instead of fetch. Phase 2 (stored secret in KV) is the correct interim for any connector whose seam isn't built yet.

Guardrail

  • The env-broker provider must never be wired to a real-tenant scan. This is a review gate, not just documentation.

Alternatives considered

  • Flat env-broker as the production store (original docs#322 plan). Rejected — no per-tenant boundary; fails SOC 2 / ISO. Retained as dev/demo only.
  • One shared Key Vault, per-tenant secret paths. Rejected — a single vault access grant spans all tenants; a per-tenant vault is the cleaner blast-radius boundary and what the broker's server-side map assumes.
  • Federation first, skip stored-secret phase. Rejected as a blanket rule — ServiceNow and other SaaS have no federation model, so stored-secret-in-KV is permanently the correct terminal for them; and the injectable-credential seam isn't built for every connector yet.

Migration & follow-ups

  • Merged: per-tenant Key Vault broker code (sv0-platform#1872 / #1874).
  • Open drafts, not applied: platform-secret boot-fetch (sv0-platform#1873); prod Key Vault / managed identity / cloud-init (sv0-infrastructure#134).
  • Not started: kv-sv0-tenant-* provisioning + RBAC + tenant → vault map; TPX bundle deposit; Key Vault diagnostics/alerting and access reviews.
  • Open pre-production requirement: connector IMDS isolation plus the negative test proving a connector cannot reach IMDS or an unmapped vault (doc 16 §3c, §7).
  • Open: injectable Azure credential provider — cert + WIF (connectors#274); the multi-tenant Entra app + admin-consent flow; per-connector federation as each seam lands.
  • Doc hygiene: enterprise credential-exchange patterns status → adopted; this ADR + 16-secrets-and-credential-management.md are the canonical pair.