Blog · 2026-09-14

"Per-client isolation: separate secrets, domain, OAuth and logs"

Every client deploy gets its own env, its own domain, its own OAuth policy and its own logs, so one client's incident stays that client's incident.

When you run one MCP connector per client, the question that matters is not "does it work", it is "what happens when one client's deploy has a bad day". On DenkOps the answer is: nothing happens to the other nineteen. Each client's connector is a separate project, and a project's secrets, domain, OAuth policy, and logs are all scoped to that project alone. Isolation is not a convention you have to maintain, it is the unit you deployed into.

Separate secrets

Every project has its own environment variables, encrypted and write-only after saving: nobody, including you, can read a saved value back through the dashboard, the CLI, or an agent that deployed the code. Client A's API key lives only in client A's project, injected into client A's process at run. There is no shared secrets store where a typo in one project's config could expose another client's credential, because there is no shared store at all.

Separate domain

A client connector can be served on the client's own domain, mcp.clientco.com instead of a shared platform hostname, once you add the domain and verify it with DNS records. Verification gates routing, so nobody can claim a domain they do not control, and TLS is issued and renewed automatically once it is verified. The client sees a URL that is theirs; you never mix client branding into a shared app.

Separate OAuth

With connector: true, each project gets its own hosted OAuth stack: discovery, dynamic client registration, a consent screen, token issuance. That means each client's connector also gets its own access policy, public, or an allowed-email list you set per project. Client A's user list has no bearing on who can reach client B's connector, because access is decided per project, not per platform account.

{ "name": "clientco-support", "slug": "clientco-support", "runtime": "bun", "connector": true }

Revoking a person's access, or tightening a client from public to an allowlist, touches exactly one project's policy.

Separate logs

Logs are scoped to the project that produced them, readable in the dashboard or over MCP so you or an agent can query them directly. Debugging client B's error means reading client B's logs; client A's traffic never appears in the same stream, and there is nothing to filter out.

Why this adds up to zero blast radius

Put the four together and a leaked key, a misconfigured access rule, or a bug that fills the logs in one client's connector stays inside that project's env, domain, OAuth policy and logs. Nothing about the isolation depends on your code remembering to namespace something correctly, it is the shape of the deploy. That is what makes one template, many clients safe to run at twenty clients instead of just at one: adding client twenty-one does not raise the risk to the first twenty, because there was never a shared surface between them to begin with.

FAQ

If one client's connector is compromised, can an attacker reach another client's data?

No, not through the platform. Each client's connector is a separate project with its own environment variables, its own OAuth access policy, and its own domain. There is no shared secrets store or shared access list between projects for a compromise to cross through.

Can each client have their own login screen and access rules?

Yes. Each connector's OAuth consent flow and access policy, public or an allowed-email list, are set per project. Changing who can reach one client's connector never affects any other client's policy.

Where do I look when a specific client reports an error?

That client's own project logs, viewable in the dashboard or queried directly over MCP. Logs are scoped per project, so you are reading only that client's traffic, with no need to filter a shared stream.

Ship it yourself: bunx denkops deploy or say "deploy on DenkOps" from your coding agent.

Start on DenkOps →

← "What a slot is, and why per-slot beats per-request when you have 20 clients" · Build and ship your own MCP connector from the chat →