One key per environment: why dev must not open prod
Most tools separate dev and prod with permissions the server enforces. Why the boundary is stronger when each environment has its own encryption key.
Crunu teamPublished 7 min read
On this page
Every team agrees that a developer working on a feature should not need production credentials. Fewer teams check how that boundary is actually enforced. Often it is a permission flag on a server: the user has role "developer", the server refuses to return production values. That works until the server, its database or its configuration is not what you think it is.
This post explains the difference between a boundary the server enforces and a boundary built into the encryption, why it matters, and how to lay out environments and roles so that the separation is real.
Two ways to say no
There are two broad ways a secrets system can keep a developer out of production.
Policy. All secrets are encrypted with keys the service controls. When a request comes in, the server checks the user's permissions and decides whether to decrypt and return the value. The boundary is a rule in code and data.
Keys. Each environment is encrypted with its own key, and that key is given only to the people and machines allowed to read it. The server stores ciphertext for everything but cannot decrypt any of it. A developer without the production key can download the production ciphertext and still learn nothing. The boundary is mathematical.
On a normal day, both approaches give the same result. They differ on the bad days:
| Situation | Policy boundary | Key boundary |
|---|---|---|
| A permission is set wrong | Developer can read production | Developer has no production key; nothing to read |
| The database is copied | Values are exposed if the copy includes the keys, or if the service's keys are reached separately | Copy holds ciphertext only |
| The server is compromised | Attacker can decrypt anything the server can | Attacker cannot decrypt; can still deny service |
| An access rule is changed directly in the database | The server applies the changed rule | Clients reject an access list that no admin signed |
The principle behind both is least privilege: every user, process and system gets only the access its task needs. NIST describes it as giving each entity the minimum resources and authorizations it requires (NIST CSRC Glossary). A key boundary makes that minimum hold even when the enforcing system fails.
How Crunu builds the boundary
In Crunu, the environment is the unit of both encryption and access. A project such as payments-api contains environments such as development, staging and production, and each has its own environment key. A new project starts with dev and prod by default.
When someone is given access to an environment, the admin's browser seals that environment's key to the recipient's public key. Members, machines and service tokens each get their own sealed copy. The list of who should have access is itself a document signed by an admin, so the server cannot add a recipient on its own: clients check the signature before trusting the list.
The interface says it plainly: each environment is encrypted with its own key, so access to dev gives no access to prod.
Roles that match real work
A key boundary is only useful if the roles on top of it fit how people work. Crunu has four roles on an environment:
- Editor reads and edits all files in the environment.
- Reader reads all files in the environment.
- Specific files reads only selected files, delivered as a sealed copy that updates on save, and cannot edit.
- Operator has no environment key and cannot see values. An operator adds machines and requests access for them, which a key holder then approves.
Operator is worth a second look. Platform engineers often need to set up servers without needing to read the secrets those servers will use. With a policy boundary, "can set up a production machine" and "can read production" tend to collapse into one permission. With Operator, they stay separate because the operator never holds the key.
Organization roles sit above this. Owners and admins manage members, machines and tokens. Members get only what is granted to them, directly or through access profiles.
Access profiles keep it maintainable
Granting environments one by one does not scale past a few people. An access profile is a named, signed set of "environment and role" pairs that you assign to people. A change to the profile applies to everyone in it.
Here is how that looks at Kiteline, a fictional payments startup used in Crunu's examples:
- The
Developersprofile gives Editor onpayments-api / development,payments-api / staging,web-app / developmentandweb-app / staging. - Jonas is a member with the
Developersprofile. He works on staging every day and has no key for any production environment. - Amara is on call. She holds Reader on
payments-api / production, enough to check configuration during an incident. When she needs to change a value, she sends an access request with a reason, and Priya, the owner, approves it to make her an Editor.
Nobody shares a password, nobody pastes a file, and every change in access is a signed record in the audit log.
Promoting values without opening prod
A frequent reason developers ask for production access is to set up a new environment or keep it in sync with staging. Crunu has two tools that avoid that.
New environment from names only. When you create an environment, you can base it on another one and copy only the variable names, without values. Creating production from development this way gives the person filling in production a complete list of what is needed, without carrying a single development value across.
Compare environments. The compare view shows which files and variables exist in only one of two environments and which differ, with values hidden until you click to show them. You can copy selected values from one environment to another as a new version. Copying into production still requires write access to production, so the person doing it is someone who already holds that key.
The cost of a real boundary
Separation by keys has costs, and it is better to know them in advance.
- Removing someone means rotation. When a key holder is removed, the environment gets a new key, sealed to the remaining recipients, so new versions are unreadable to the person who left. Crunu does this as part of removal. If the person running the removal does not hold a particular environment's key, that rotation waits under Pending actions until someone who does opens the app.
- Admins are still powerful. Owners and admins can reach all shared environments through the organization recovery key, which exists so the organization can recover access when a member loses theirs. Keep that group small, and treat adding an admin as a production access decision.
- Access for people has no expiry date. If you grant production for an incident, remove it afterwards. Time limits exist for invitations, share links, service token leases, machine leases and break-glass access, but not for a person's role on an environment.
- Past reads stay read. A boundary protects what someone has not yet decrypted. Anyone who could read production last month may have copied values; that is what reissuing at providers is for.
Extra layers for production
The key boundary answers "who can decrypt". For production you may also want to limit "from where" and "with what".
- Network rules. Restrict an environment so its contents are served only to allowed networks, such as an office range and the production machines' range. Tightening a rule takes effect immediately; relaxing it waits 15 minutes with an alert, or needs a second admin's signature. This is enforced by the server, so it complements the key boundary rather than replacing it. See network rules.
- CI with OIDC only. A restricted environment can accept CI access only from service tokens bound to a specific repository and branch with OIDC. See giving CI only the secrets it needs.
- Machines instead of people. Production machines run the agent and receive production directly. Fewer humans need production at all. See secrets on servers without files on disk.
A layout that works for most teams
If you are setting this up from scratch, a reasonable starting point:
- One project per deployable service, with
development,stagingandproductionenvironments. - A
Developersaccess profile: Editor on development and staging for every project. - Production: Reader for the few people on call, Editor for one or two maintainers, nothing for everyone else.
- The Operator role on production for people who set up machines but do not need to read values.
- Production machines with only the environments their services use.
- One service token per pipeline, none of them reading production unless bound with OIDC.
- A short list of owners and admins, reviewed whenever someone joins or leaves.
The aim is that when you ask "who can read payments-api / production?", the answer is a short list of names you recognize, and the reason it is short is the encryption, not a setting someone might change.
More in roles, access profiles and access control.
Sources
- NIST CSRC Glossary: least privilege, checked October 27, 2026.