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 → vaultmap are not yet provisioned or applied (platform#1873 and infrastructure#134 are open drafts). The authoritative status table is16-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.
-
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.)
-
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
pasteprovider stores AES-256-GCM ciphertext in Mongo, so the accurate global claim is "no plaintext customer secret in Mongo." - Server maps
-
Federation — the endgame, no long-lived secret exchanged. For each connector, the simplest-terminal identity model:
Connector Terminal model Per-tenant secret? AWS Cross-account assume-role + external-ID No Entra / Graph Multi-tenant app + admin consent + SV0 UAMI workload-identity-federation (cert transitional) No GCP Workload Identity Federation No GitHub SV0 GitHub App (one private key) No Atlassian Cloud Forge app + Forge Remote No ServiceNow / other SaaS (no federation model) OAuth client_credentials, customer-pasted → tenant Key Vault Yes (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 hardwireClientSecretCredentialtoday, 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 → vaultmap 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 → vaultmap; 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.mdare the canonical pair.