← All posts

tsafe's trust model: profiles, namespaces, and the strip list

Two secrets, one exec. You're running a deploy script. You have staging and prod profiles in tsafe. You run `tsafe exec --keys DB_PASSWORD -- deploy.sh` — without specifying the profile. tsafe looks up DB_PASSWORD. It finds it in prod. The deploy script runs with prod credentials. You were testing a staging workflow.

This is the problem a flat namespace creates: without enforced boundaries, a lookup that feels safe based on the key name can resolve to the wrong profile. The intent was staging. The result was prod. The exec succeeded.

Why flat namespaces aren't enough

Most secret managers store secrets as key-value pairs with path prefixes: `prod/DB_PASSWORD`, `staging/DB_PASSWORD`. The enforcement of 'this process only gets staging secrets' is convention. If the caller doesn't specify the right path, or if the lookup falls back to a default profile, they get whatever resolves first. Convention isn't enforcement. It requires every caller to get it right every time.

The same problem with env vars: you can prefix your variables (`PROD_DB_PASSWORD`, `STAGING_DB_PASSWORD`), but a script that reaches for `DB_PASSWORD` will get whatever name it's looking for, regardless of which environment you intended it to run in. Naming discipline is not a security boundary.

Profiles as hard security boundaries

In tsafe, a profile is a security boundary, not a folder. A process executing with the staging profile cannot receive secrets from the prod profile — not by explicit request, not by fallback, not by any lookup. The profile is set at execution time and is not overridable from inside the subprocess.

PortableText [components.type] is missing "code"

The strip list

The strip list handles the ambient case: what happens when prod credentials are already in the environment from a previous command, and you're now running a staging exec? Without cleanup, the staging exec would inject staging credentials into a subprocess that already has prod credentials ambient.

The strip list is what tsafe removes from the environment before injection. It's configured per-profile and runs before the requested keys are injected. By the time the subprocess launches, everything matching the strip list is gone. You don't have to remember to unset it manually. The boundary is enforced by the tool.

The invariant

A secret manager that doesn't enforce profile boundaries at the point of injection is a storage system with labels. Labels are convention. Runtime enforcement is a different thing. Profiles in tsafe are not naming conventions for organising secrets — they're hard boundaries enforced at exec time, with no fallback to other profiles and a strip list that removes ambient cross-profile state before the subprocess sees anything.