Subtitle: Compartmentalizing Secrets in Multi-Tenant Agent Systems
Track: Security / cryptography · Format: 30 min (compresses to lightning)
Deck: ../talk-2/
When you run many tenants' agents on one platform, the scariest failure is
cross-tenant secret leakage, agent A reading agent B's Slack or AWS token. Most
systems prevent this with careful WHERE tenant_id = ? clauses and code review ,
i.e., by convention. We made it structural: an agent's credential store is bound
to a single (workspace, agent) pair at construction, and each secret is encrypted
with a key derived per-credential via Argon2id from a master key plus a random
per-credential salt, sealed with AES-256-GCM. The result: an agent literally cannot
name, let alone decrypt, another agent's secret, even if its own code is malicious.
This is a deep, copyable walk through the scheme: key-derivation parameters and why,
the salt(32)‖nonce(12)‖ciphertext layout, masked reads for UIs, return-secret-once
webhook semantics, and the honest limits (no key rotation yet).
Engineers building multi-tenant systems (agents or not) who handle per-tenant secrets; security-minded backend engineers.
- "Structural vs conventional" applied to secrets.
- A concrete per-tenant envelope-encryption recipe (Argon2id + AES-256-GCM, per-credential salt).
- API design that makes misuse hard (construction-time binding, masked reads, return-once).
- What it protects against, and what it doesn't (master-key compromise, rotation).
- Where this sits in zero trust: least privilege + agent identity; the maturity path is short-lived, hardware-bound tokens and reference-not-value (Anthropic: "rotating a credential you can grep out of a lockfile doesn't raise the cost"). The credentials-in-the-snapshot exposure makes it concrete.
- The cross-tenant leak nightmare (engineer9 holds AWS + Slack + Linear creds).
- The conventional approach and why it fails under malicious code.
- Thesis: bind access at construction, not at query.
- The
Storeinterface +NewPGStore(pool, masterKey, workspaceID, agentID). - Why this makes cross-agent access syntactically impossible.
- Envelope-encryption overview.
- Argon2id params (time=1, 64MB, 4 threads, 32-byte key) and rationale.
- On-disk layout
salt(32)‖nonce(12)‖AES-256-GCM. - Per-credential salt → why not one key.
- UX:
GetMasked(first-8…last-4), return-secret-once for webhook secrets. - Demo: two agents; one tries and fails to read the other's secret.
- Limits + roadmap (rotation) + takeaways.
Source: internal/credential/store.go, internal/agenthook/handler.go.
"Every multi-tenant tutorial tells you to add WHERE tenant_id = ?. I'll show you why
that's not enough when the code in the tenant is the attacker, and a crypto design
where leaking a neighbor's secret isn't a bug you have to avoid, it's an operation
that doesn't exist."