8 min read

Separate staging and production access for coding agents

Separate staging and production access for coding agents with distinct credentials, fixed endpoints, human approvals, audit records, and tested revocation.

Separate staging and production access for coding agents

Coding agents should never move between staging and production by swapping one variable, one URL, or one vague instruction. Separate credentials and separate destinations make an accidental cross-environment call harder. A human decision at the production boundary makes an intentional one accountable.

I have seen teams call their setup "separate environments" because they had two hostnames in a repository. Then a deploy script inherited a production token, a test fixture pointed at a live project, or an agent found a credential in a shell session and used the path that happened to work. The hostname was staging. The authority was production. That is not separation.

The useful rule is plain: staging access should let an agent prove a change works without possessing any authority that can affect production. Production access should exist as a separate, narrow capability that a person grants for a particular run and can withdraw while the run is active.

Environment names do not create a security boundary

A staging label does not protect anything by itself. A security boundary exists when a call aimed at staging cannot authenticate to production, and a call that reaches production cannot do meaningful work without a separately granted production identity.

Teams often blur three different things:

  • Routing decides where a request goes, such as https://api.staging.example.
  • Authentication decides which principal the service sees, such as a service account or SSH key.
  • Authorization decides what that principal can do after authentication succeeds.

Changing only routing leaves the other two untouched. Changing only a credential leaves a dangerous possibility too: a staging credential might authenticate against production because an account administrator reused a client, role, or key across both places. A clean environment boundary changes all three.

Start with separate provider accounts, projects, tenants, namespaces, or subscriptions when the underlying service allows it. They give you a hard identifier to check. Separate databases, object stores, queues, and deployment targets follow naturally. If a provider forces staging and production into one account, create distinct principals and resource scopes, then treat the shared account as an accepted weakness that needs extra review.

Do not use production customer data to make staging feel realistic. Copying it without a deliberate sanitization process changes the problem from access control to privacy exposure. Use synthetic data for ordinary development. If a test needs production-shaped records, generate a minimized, scrubbed dataset and document who approved its use.

A practical boundary has evidence a reviewer can inspect:

Boundary elementStagingProduction
DestinationDedicated staging hostname or accountDedicated production hostname or account
CredentialStaging-only principalProduction-only principal
PermissionsTest operations and test resourcesNarrow live operations only
DataSynthetic or sanitizedLive data only when the task requires it
ApprovalNormal development controlsA deliberate human authorization

The distinction between an environment and an authorization domain matters. You can run many environments inside one authorization domain, but an agent with a broad shared credential can cross among them. That arrangement helps operations, not containment.

A production credential must be a different identity

Production access needs a distinct principal, not a second label attached to the staging credential. If the same API token, cloud role, database user, or SSH key can operate in both places, a routing mistake can become an incident.

Create identities around the action an agent must perform. A coding agent that needs to inspect a deployment should not inherit permission to alter identity settings, delete storage, read every database table, or open an interactive shell on every host. "Administrator, because the agent may need it" is a shortcut that turns unfamiliar code paths into production authority.

Split access by consequence rather than by job title. Typical divisions include:

  • A staging deploy identity that can write only to staging resources.
  • A production observation identity that can read a small health or release surface.
  • A production deploy identity that can update one service or release channel.
  • A separate break-glass identity for humans, outside routine agent workflows.

Do not give an agent a human developer's personal token. Personal tokens collect permissions over time, survive role changes, and often work across more systems than their owner remembers. They also make an audit trail ambiguous: a log shows a person, while an autonomous process made the request.

If your provider supports workload identity, short-lived credentials, or scoped service accounts, use them. The duration helps, but duration does not repair broad scope. A ten-minute administrator credential can still delete a production database in the first minute.

NIST Special Publication 800-53 describes least privilege in control AC-6: organizations should grant only the access necessary for assigned tasks. That sounds obvious until an agent needs a quick fix and somebody reaches for an owner role. The standard does not decide the exact permission for you. It does make the right review question unavoidable: which single action fails if we remove this permission?

For SSH, separate keys are mandatory but insufficient. Give the production key access to named hosts or a restricted account, disable broad forwarding paths where your environment permits it, and avoid putting a general shell behind a credential meant for one deployment command. A key that opens an unrestricted production shell gives the agent a large, difficult-to-review action surface.

Endpoint separation needs an executable check

Configuration should make a cross-environment mixup fail before an API request leaves the machine. Do not ask the agent to remember which environment it is in. Make the selected profile carry both the destination and the identity name, then reject combinations you never intend to allow.

This shell pattern does not store secrets. It validates the values that select the secret and endpoint before a wrapper or credential broker performs the request:

#!/usr/bin/env sh
set -eu

case "${AGENT_ENV:?set AGENT_ENV}" in
  staging)
    API_BASE="https://api.staging.example.internal"
    CREDENTIAL_REF="agent-staging-deploy"
    ;;
  production)
    API_BASE="https://api.example.com"
    CREDENTIAL_REF="agent-production-deploy"
    ;;
  *)
    printf '%s\n' "AGENT_ENV must be staging or production" >&2
    exit 64
    ;;
esac

printf 'environment=%s\nendpoint=%s\ncredential_ref=%s\n' \
  "$AGENT_ENV" "$API_BASE" "$CREDENTIAL_REF"

A staging invocation produces output shaped like this:

environment=staging
endpoint=https://api.staging.example.internal
credential_ref=agent-staging-deploy

Treat this output as a preflight record, not as proof of authorization. The script only prevents a bad local pairing. Your credential store or action gateway must still refuse to resolve agent-production-deploy unless a human has granted production access.

Avoid configurations that accept arbitrary URLs from the agent. A request interface such as curl "$TARGET" with a bearer token turns every generated string into a possible destination. Even careful agents make mistakes, and untrusted text in a repository can influence their tool calls. Give the agent named actions or fixed endpoint profiles instead.

The same principle applies to SSH. Do not expose a generic host argument alongside a production key. Bind a production deployment action to an expected host group and remote command, or force the human to choose the target at approval time.

A check that compares only a string such as production has one weak spot: names can lie. Check provider identifiers too. A cloud account number, project ID, subscription ID, repository owner, or database cluster ID gives you something less likely to drift when somebody copies a configuration file.

Prompts cannot authorize a live change

An instruction that says "never touch production" is useful context, but it is not access control. Agents follow tool output, repository files, task descriptions, and their own intermediate plans. Any of those can create conflict or confusion. A prompt does not sit in front of the network request and deny it.

A production decision needs an enforcement point outside the agent process. That point should know which process requested the action, which production identity it wants, where the call will go, and what the action is. It should then require an explicit human choice or reject the call.

Per-session approval and per-call approval solve different problems.

Per-session approval works when a human has reviewed a bounded run, such as an agent applying a reviewed deployment plan to one service. It reduces repeated interruptions while still tying authority to a particular process. The approval must expire when that process exits, not linger for the rest of the day because the terminal remains open.

Per-call approval fits actions with irreversible or high-impact effects: deleting data, rotating credentials, changing network exposure, publishing a release, or writing to a production database. Requiring a click or local authentication on every use is intentionally slower. That friction tells you the action deserves attention.

Do not train people to approve opaque cards. An approval screen should make the decision legible with the process identity, the destination, credential identity, method, and request summary. "Agent requests access" gives the reviewer nothing to assess. "Signed coding process requests POST to production deploy endpoint using production deploy identity" is enough to spot a mismatch.

Sallyport uses this split directly: its vault stays locked until local authentication opens it, then a new agent process requires session authorization by default, while individual credentials can require approval on every use. The agent never receives the API or SSH secret itself.

That design choice rejects a popular recommendation: put a production token in a tightly controlled environment variable and rely on a careful prompt. It is popular because it is easy to wire into existing scripts. It fails because the agent process still holds the authority, and any tool call that can read its environment or reuse its process can spend it.

The failure usually starts with a harmless convenience

Approve sensitive calls individually
Set a production key to require one-click or Touch ID approval on every use.

Cross-environment incidents rarely begin with somebody deciding to attack production. They begin with a small convenience that removes one of the checks.

Consider a release repository with two profiles. The team uses DEPLOY_ENV=staging for test runs and DEPLOY_ENV=production for live runs. Both profiles pull DEPLOY_TOKEN from the same developer shell because that made early testing easy. The token has access to both deployment projects.

An agent receives a task to validate a staging rollout. It reads a script that constructs the endpoint from DEPLOY_ENV. A previous command in the same terminal left DEPLOY_ENV=production, while a later helper prints a staging label based on a separate configuration file. The agent sees the label, runs the helper, and the request goes to the production endpoint with a token accepted there.

Nothing in that chain requires malice or an exotic exploit. The team had two labels, one shared credential, two sources of truth, and no approval point. The logs may even show a normal developer account because the shared token belonged to that developer.

Fixing the visible bug by setting DEPLOY_ENV=staging more carefully does not fix the design. The repair is structural:

  1. Replace the shared token with environment-specific identities.
  2. Bind each identity to its allowed account, project, or resource set.
  3. Resolve the identity through a broker or vault rather than the agent's shell environment.
  4. Require human authorization before the production identity can make a call.
  5. Log the process, target, identity reference, action, result, and approval decision.

Keep production and staging settings in one reviewed source of truth when possible, but do not make them interchangeable. A copied block with a changed hostname is where quiet drift begins. Make each profile explicit enough that a reviewer can compare the account ID, credential reference, and allowed operations side by side.

Production approval should describe one bounded run

A human approval has value only when the human can connect it to an intended piece of work. "Allow production for this agent" is too broad. It gives away a capability without an end point that the reviewer can recognize.

Define a production run using concrete boundaries: the agent process, the repository or task, the intended service, the permitted action class, and an expiration condition. The exact mechanics depend on your tools, but the decision should answer these questions before the first call:

  • Which local process is asking, and can you identify its code-signing authority or executable?
  • Which production account or endpoint will receive the request?
  • Which credential identity will the action use?
  • What action can the agent perform during this run?
  • When does the authorization end, and who can revoke it now?

Process identity deserves more attention than it gets. A terminal label or a claimed agent name is easy to mimic. On a managed developer machine, code-signing authority gives a stronger clue about which program opened the request. It does not prove the task is sensible, but it helps stop a different local process from riding on a familiar name.

Approval fatigue is a design failure. If every harmless staging request asks for a click, people will approve without reading. Keep normal staging work available through its own narrow credentials. Reserve production prompts for calls where the destination and consequence justify interruption.

The reverse mistake is worse: one approval that silently covers every future agent process. That is indistinguishable from permanent production access after a few days. Tie approval to the run, expire it when the run ends, and make revocation immediate rather than a ticket somebody handles later.

For a deploy that changes multiple production resources, do not pretend that five independent approvals make review better. Ask for one session authorization only if the plan itself is bounded and visible. Require per-call approval where each call could create a different irreversible effect. The control should match the action, not satisfy a ritual.

Audit records need to answer who spent authority

Keep production secrets outside agents
Sallyport keeps production API and SSH secrets in its encrypted vault and executes actions itself.

A production audit log should let you reconstruct an action without trusting the agent's own account of events. A chat transcript or terminal history can help investigation, but either can be incomplete, edited, or disconnected from the credential that actually made the call.

Capture the request path at the enforcement point, where the credential is used. Record the agent process identity, session identifier, requested credential reference, resolved destination, method or SSH command class, timestamp, approval result, response status, and revocation event. Redact secrets and sensitive request bodies; a useful audit record does not need to become another source of customer data leaks.

NIST SP 800-53 control AU-2 calls for defining the events an organization logs. That detail matters. "We log agent activity" is not a definition. Decide whether a denied request, an approval, a credential use, a destination mismatch, and a session revoke count as events. If you cannot see a denial, you cannot tell whether your boundary stopped a mistake or never received the request.

Tamper evidence matters after an incident because ordinary application logs often sit where an administrator or compromised process can alter them. A hash-chained journal makes changes detectable when you verify the chain against the stored records. It does not turn bad decisions into good ones, and it does not replace backups, access review, or external retention. It gives investigators a way to detect alteration.

Sallyport writes its Sessions and Activity views from one encrypted, hash-chained audit log, and sp audit verify can check that chain offline without a vault key. That is useful when the question is whether the record changed, rather than whether an agent claims it behaved.

Set a review habit that matches risk. Review production authorizations and denied attempts after meaningful agent runs. Periodically compare the production identities in use against the actions they actually performed. Permissions that never appear in the log are candidates for removal. Permissions you cannot explain are already too broad.

Rotation and revocation must work during the incident

Remove tokens from shell access
Sallyport injects bearer, basic, and custom-header credentials without exposing them to the agent.

Separate credentials limit damage only if you can stop using the production one quickly. A rotation plan that requires finding every script, editing every workstation, and waiting for a weekly deployment window is not an incident control.

Give every production agent identity an owner, a location where it is issued, a list of its allowed targets, and a documented revocation path. Keep this information separate from the secret value. During an incident, responders need to know what to disable without opening files that might contain credentials.

Test this sequence before you need it:

  1. Start an approved production agent run that can perform a harmless, reversible operation.
  2. Revoke the session or disable its production credential.
  3. Make the agent repeat the operation.
  4. Confirm that the enforcement point denies it and that the denial appears in the audit record.
  5. Issue a replacement credential and confirm that staging access still works independently.

This test catches a common operational flaw: a UI says "revoked," but a cached token, persistent SSH connection, or long-lived process keeps working. For APIs, inspect token lifetime and refresh behavior. For SSH, inspect existing connections and multiplexing. A revoke button is only credible when the next attempted action fails.

Rotate staging and production identities separately. If a staging secret leaks, you should not need to interrupt production. If production authority becomes uncertain, revoke and replace it even when the suspected exposure looks small. People routinely underestimate how far a token traveled through shell history, debug output, copied logs, or agent tool context.

Build the boundary before granting live access

Teams should earn production access through evidence, not through confidence in a prompt or an agent model. A coding task usually has a staging route, a dry run, a read-only query, or a human-operated release path that can prove most of the work first.

Use this readiness test before allowing a new production action:

  • Staging and production use different credentials that cannot authenticate across environments.
  • The agent cannot read plaintext secrets from files, environment variables, prompts, or tool output.
  • The request path checks a fixed destination and a provider account or project identifier.
  • A human sees the production action and grants authority outside the agent process.
  • Revocation, denial logging, and audit verification have been exercised rather than assumed.

If one item fails, keep the action in staging or have a human perform production work directly. That is not a defeat for agent automation. It is an accurate statement that the control path is unfinished.

The best early production permission is often a narrow observation action against a non-sensitive status endpoint. It tests destination selection, identity separation, approval, logging, and revocation without letting an agent change customer-facing state. Once that path survives real use, add one write action at a time and remove permissions that the agent never needs.

Do not merge staging and production access later because approvals feel inconvenient. Friction at the production boundary is the price of knowing who authorized a live action, which process performed it, and how to stop it. Keep that boundary deliberate.

FAQ

Are separate environment variables enough to isolate staging and production?

No. An environment variable is only a routing hint unless the credential, target account, data, and permission boundary also differ. If a staging process can use a production credential after one variable changes, you have not separated access.

Should coding agents use the same production credentials as developers?

Give the agent a dedicated production identity, not the same identity used by a human developer or staging automation. Limit it to the exact API operations, repositories, hosts, or deployment actions it needs. A short-lived credential is preferable when your provider supports it.

What production actions should require human approval?

A human should approve the specific production run after reviewing its intended target and action. One approval for a defined agent process may fit low-risk work, while destructive or irreversible calls deserve approval on every use. Approval must happen outside the agent's own text channel.

Is production read-only access safe for an AI coding agent?

No. Read access can expose customer data, internal configuration, source code, and credentials embedded in records. Give production read access only when the task needs it, and use redacted exports or a purpose-built read model where possible.

Can a local mock replace staging for agent testing?

Use a real staging environment with distinct credentials, a distinct account or project where practical, and test data that cannot affect customers. A local mock is useful for fast feedback, but it cannot prove that endpoint routing, authorization, or deployment wiring works safely.

Do separate credentials stop an agent from damaging production?

Separate identities limit the blast radius, but they do not prevent an agent from making a bad request within its granted scope. You still need narrow permissions, human authorization for production, useful logs, and a way to revoke an active run.

How can I verify that an agent is really targeting staging?

Check both the destination and the identity. Record the resolved hostname, cloud account or project identifier, credential principal, and requested operation before allowing a production action. Do not accept a label such as ENV=prod as proof.

How should teams rotate credentials used by coding agents?

Keep staging and production secrets in different stores or namespaces, with different owners and rotation records. Rotate the production credential when an agent run goes wrong or an authorization boundary becomes uncertain. Rotating only the staging secret does not repair production exposure.

Can an agent prompt forbid production changes safely?

No. A prompt can be ignored, rewritten, or confused by tool output and repository instructions. Production control needs an enforcement point that receives the attempted action and asks a human or rejects it.

When does a coding agent actually need production access?

Production access is justified when the task cannot be completed with staging, a human can explain the exact action and target, and the credential has a narrow scope. For many coding tasks, production access is unnecessary and should remain unavailable.

Sallyport

Sallyport runs API calls and SSH commands for your AI agent. The keys stay in a local vault on your Mac; you approve each run and every action lands in a sealed journal.

© 2026 Sallyport · Open source under Apache-2.0 · Oleg Sotnikov