39

One Shared Identity or One Per Tenant?

A design decision at the heart of connecting to many customer-managed databases — one shared identity per environment versus one per customer — why per-tenant separation belongs in *data* rather than in a new identity, where that shared identity should live, and the onboarding handshake that puts it all in motion.

The decision: a shared identity per environment vs one per customer

Once you're authenticating to many customers' databases (Days 37–38 covered a single connection), a fork appears: does your side use one shared identity per environment that every customer trusts, or a separate identity per customer? It sounds like a small implementation detail, but it shapes your entire onboarding pipeline, your security posture, and how much machinery you run — so it deserves an explicit decision rather than a default that happens by accident.

The per-tenant identity approach mints a fresh identity of your own for each customer. It feels safer — every customer is isolated to their own principal — but it means your onboarding must provision an identity for each new customer and garbage-collect it on offboarding, and you now run an identity lifecycle that grows with your customer count. The shared identity approach instead publishes one stable identity per environment that every customer configures their side to trust; onboarding never mints anything on your side. The shared model is simpler by a wide margin, and the next sections show how it keeps isolation without per-tenant identities.

Rule of thumb: default to a shared identity per environment unless a specific isolation requirement forces per-tenant identities — the shared model removes an entire identity lifecycle (provision on onboard, collect on offboard) that otherwise grows with every customer you add.

Per-tenant separation lives in data, not in a new identity

The obvious objection to a shared identity is "doesn't that break isolation between customers?" The answer is no, if you locate the per-tenant separation in the right place. Isolation doesn't have to come from minting a distinct identity per customer — it comes from two other places: the customer's own scoping and your data. Understanding this is what makes the shared model safe rather than reckless.

On the customer side, each customer creates and scopes their own principal (Day 38) that grants access to only their database — so even though your one shared identity is what they trust, that trust only ever reaches the one database they exposed. On your side, what varies per customer is not an identity but a connection record: a plain data row saying which customer principal to link to and where that customer's database is. At runtime you read the row for the current tenant and connect accordingly. Per-tenant state is therefore data, not identity — onboarding a customer adds a row, and nothing about your identity changes.

There is a real trade-off to state honestly: because the one shared identity can reach every customer database that has linked to it, it is a high-value principal — compromise it and the blast radius is large. The shared model accepts this in exchange for a dramatically simpler system, and pays for it by protecting that one identity intensely (tight controls, least privilege, close monitoring). That is a deliberate, defensible trade — concentrate the risk into one well-guarded principal rather than spread a lifecycle-management burden across thousands.

Why the shared identity sits in its own account, away from the compute

If there's one stable identity everything depends on, where it lives becomes a design question with real consequences. The right answer is a dedicated account (or project) of its own, separate from the accounts that host your compute clusters. This looks like extra structure, but it buys a specific and important property: the trusted identity outlives the compute.

The reasoning is lifecycle decoupling, the same instinct as Day 37's "don't trust the cluster issuer." Compute environments get rebuilt, migrated, and occasionally deleted. If the shared identity lived inside a compute account, deleting or rebuilding that account would destroy the identity — and with it every customer's trust relationship that pointed at it, forcing every customer to redo their grant. By placing the identity in a dedicated account that no routine compute operation touches, the identity and all the customer trust that references it survive cluster rebuilds entirely. The compute reaches the shared identity across that boundary only as an internal bootstrap.

Rule of thumb: put the shared identity in a dedicated account separate from compute, so that rebuilding or deleting a cluster never destroys the identity or the customer trust relationships that reference it — the thing many parties depend on must not share a lifecycle with the thing you rebuild routinely.

The onboarding handshake: what you show, what the customer grants

Bringing a new customer online is a handshake that alternates automated steps on your side with one-time manual steps on the customer's — and designing it as a clear, ordered exchange is what makes a genuinely intricate setup feel routine. The guiding metaphor is giving a trusted contractor a key: you tell the customer who you are, the customer adds you to their guest list and tells you the address, and no password ever changes hands.

The sequence has five stages. You show your identity — the stable trust handle the customer will point at, plus any per-customer pinned value (the confused-deputy protection from Day 38). The customer creates a scoped grant: a principal that trusts your shared identity, limited to connect-only, plus a database user for you. The customer submits their details — where their database is and the exact names they created — which you store in the connection record. Finally you run an automated validate: perform the link and bridge, mint a token, and test-connect, so misconfigurations surface immediately with an actionable error rather than at first real use.

The step teams skip at their peril is validate. Because the customer performed manual steps in their own environment, there are many small ways to get it subtly wrong — a mis-scoped grant, a typo'd name, a missing prerequisite — and the difference between a good onboarding and a frustrating one is catching those at setup time with a clear message, not hours later as a mysterious connection failure in production.

Runtime and lifecycle: onboarding is a row, not a provisioning job

The final elegance of the shared-identity, per-tenant-data design shows up at runtime and in lifecycle, where the whole thing reduces to reading and writing data. At runtime, each unit of work already knows its tenant; the connection driver looks up that tenant's connection record, uses the ambient shared identity to link to the recorded customer principal, runs the bridge, mints the database token, and serves the request from a per-tenant connection pool. Your own identity isn't in the record — it's the shared identity resolved from configuration — so the record holds only the customer's details.

Because onboarding is data, the lifecycle is trivial in the ways that usually hurt. Adding a customer is inserting a row — no code change, no deploy, no restart, because the running service simply starts finding a record for that tenant. Offboarding is deleting the row on your side while the customer revokes their grant on theirs — two independent, reversible actions. Re-running onboarding updates the existing row rather than duplicating anything, so the operation is idempotent. There is no per-tenant identity to create or garbage-collect anywhere, which is precisely the payoff the shared-identity decision was chasing.

Rule of thumb: design onboarding so a new customer is a row of data, not a provisioning job — when adding and removing customers is inserting and deleting connection records (with the customer independently revoking their own grant), you get zero-deploy onboarding, trivial offboarding, and idempotent re-runs for free.

Security questions this design must answer

The shared-identity decision is the one a security reviewer is most likely to challenge, because concentrating reach into a single principal is exactly the kind of trade-off that deserves scrutiny. The questions here are about containment: given that one identity can reach many databases, how is it protected, and what stops one customer's link from becoming another's exposure?

How is the high-value shared principal protected, given it can reach every linked database? The answer is defense proportional to value — tight, least-privilege permissions, close monitoring, and restricted ability to assume customer principals. Can one customer's link reach another customer's database? It must not, and the reason is structural: each customer scopes their own principal to their own database, so the shared identity's reach into any given customer is bounded by what that customer exposed. Is the stored connection metadata (endpoints, identifiers, the per-customer pinned value) encrypted at rest, and does it deliberately contain no database secrets? Yes on both — the record holds references and locations, not credentials, and is encrypted at rest. How do you roll back or disable a single connection, and does offboarding leave anything behind? Per-connection rollback is deleting the record while the customer revokes their grant; because no per-tenant identity was ever minted, there is nothing orphaned to clean up.

Question a reviewer asksWhat a good answer looks like
How is the shared principal protected?Least privilege + monitoring, proportional to its high value
Can one customer's link reach another's DB?No — each customer scopes their own principal to their own database
Is stored metadata encrypted, and secret-free?Yes — references/locations only, no credentials; encrypted at rest
How do you roll back one connection?Delete the record; customer revokes their grant — nothing orphaned

Rule of thumb: when a design concentrates reach into one shared identity, the review's real question is containment, and the strongest answer has two parts — the principal is protected in proportion to its value, and isolation between customers comes from their scoping, not from your identity — so a compromise is bounded and one customer can never reach another through you.

Key takeaways

  • Connecting to many customer databases forces a choice between a shared identity per environment and a per-tenant identity; default to shared unless a hard isolation requirement forces otherwise, because per-tenant identities add a lifecycle that grows with every customer.
  • A shared identity doesn't break isolation, because per-tenant separation lives in the customer's own scoping (their principal reaches only their database) and in your data (a per-tenant connection record), not in a distinct identity per customer.
  • The honest trade-off: the shared identity is a high-value principal that can reach every linked database, accepted in exchange for a far simpler system and paid for by protecting that one identity intensely.
  • Put the shared identity in a dedicated account separate from compute, so rebuilding or deleting a cluster never destroys the identity or the customer trust relationships pointing at it.
  • Onboarding is a handshake: you show a stable identity handle (+ per-customer pinned proof), the customer creates a scoped connect-only grant and a database user and submits their details, then you validate (link → bridge → mint token → test connect) to catch misconfigurations at setup.
  • At runtime the driver maps tenant → connection record → link/bridge → token → per-tenant pool; onboarding is inserting a row and offboarding is deleting a row (customer revokes their side) — no per-tenant identity to provision or garbage-collect, and re-runs are idempotent.
  • A security review focuses on containment: protect the high-value shared principal in proportion to its reach, get customer isolation from the customer's own scoping (not your identity), encrypt secret-free connection metadata at rest, and make per-connection rollback a delete that orphans nothing.

Checklist

  • [ ] I can contrast a shared per-environment identity with per-tenant identities and give the main reason to default to shared.
  • [ ] I can explain where per-tenant isolation actually comes from in the shared model (customer scoping + a data record).
  • [ ] I can state the high-value-principal trade-off honestly and say how it's mitigated.
  • [ ] I can explain why the shared identity belongs in a dedicated account separate from compute lifecycle.
  • [ ] I can lay out the onboarding handshake's stages and explain why the validate step is the one not to skip.
  • [ ] I can describe the runtime lookup path and why onboarding/offboarding being "a row" gives zero-deploy, idempotent lifecycle.
  • [ ] I can answer the containment questions a review asks about a shared identity: how it's protected, why one customer can't reach another, metadata encryption, and clean per-connection rollback.