8 min read

Temporary production access: grants that end with the task

Temporary production access for AI coding agents needs a purpose, hard expiry, early revocation, and a real denial test after closure.

Temporary production access: grants that end with the task

Production access for an AI coding agent should end because the authorization system says it ends, not because someone meant to come back later. A purpose, a deadline, and a closing check turn a risky exception into a bounded grant that another engineer can inspect.

I have seen access controls fail in the boring way: the incident is resolved, the task is closed, and the credential remains useful. Nothing dramatic happens that day. Months later, the same credential appears in an unrelated script, an old agent process resumes, or a person uses it because it is convenient. The original exception has become permanent production access by neglect.

Temporary production access needs three facts that the enforcement point can evaluate: why the agent may act, what it may touch, and when it must stop. The final part matters as much as the first two. If you cannot demonstrate a denial after the task closes, you have not finished the task.

A deadline must deny requests, not decorate a ticket

A stated end time only has force when the component that performs or authorizes the action checks it on every request. A project tracker, calendar entry, or chat reminder cannot prevent an old process from calling an API at 02:00.

This distinction catches teams off guard because their change process looks disciplined. They require a ticket, a reviewer, and a closure status. Then they hand an agent a credential that remains valid until somebody manually rotates it. The workflow records intent while the credential retains authority.

Put the expiry where the request crosses into production. Depending on your design, that can be an identity provider issuing a short-lived token, an access gateway checking a grant database, an SSH certificate validator, or a broker that holds the credential and performs the action. The protected service must receive a request that it can reject after the deadline.

NIST SP 800-207 makes a useful point in its description of zero trust architecture: access decisions should be evaluated before a session to an enterprise resource is established, and access should be granted on a per-session basis. That does not mean every application must copy a full zero trust product. It does mean that granting a session once and assuming it stays appropriate forever contradicts the model.

For agents, evaluate at least the start of each action. Long-running work needs a separate decision: either let the already-started operation finish under a bounded lease, or cancel it when the lease expires. Decide that behavior before an outage, because an unplanned cutoff during a database repair can be as damaging as unrestricted access.

A deadline should use a timestamp in a single agreed time basis, normally UTC. Do not write "until end of day" and hope every system means the same day. Store 2025-04-18T16:30:00Z, show local time to the approving human, and make the request path compare against the stored timestamp.

Purpose must constrain the grant

A purpose is useful only when it limits what the agent can do. "Help with production" and "investigate the alert" describe a mood, not an authorization boundary.

Write the purpose so an engineer who did not approve the grant can decide whether a request belongs. A workable statement names the triggering work item, the target, the allowed operation, and the expected completion condition. For example:

Investigate elevated checkout failures in incident INC-482. Read logs and deployment status for the checkout service. Restart only the checkout worker if the incident commander approves the restart. End access when INC-482 is resolved or at 16:30 UTC, whichever occurs first.

That statement creates decisions. Reading a billing database does not fit. Pushing an unrelated application change does not fit. Restarting the worker needs a named approval condition. The agent can still be useful without receiving a blank cheque.

Scope needs more than a production label. Bind the grant to concrete targets:

  • named API hosts, repositories, or SSH hosts
  • named methods or commands, such as GET /health or a deployment status query
  • named environments and account identifiers
  • a maximum action count or request rate when the task makes repeated calls plausible
  • a human owner who can stop the work

Avoid the popular recommendation to give an agent broad read access because "read-only is safe." Read access often exposes customer records, configuration, topology, deployment history, or credentials embedded in old logs. It also lets an agent collect far more context than the purpose requires. Give it the specific observability endpoints and log query range needed for the task.

There is another boundary that teams blur: a task purpose is not the same as a prompt. The prompt tells the agent what you asked it to attempt. The authorization purpose tells the enforcement point what it may permit even if the prompt changes, the model makes a poor inference, or a tool output contains hostile instructions. Treat prompts as untrusted input to the access decision.

Credentials and authority expire differently

A credential proves that a caller possesses something. Authorization decides whether that caller may perform this request now. You need both parts to end when the work ends.

A bearer token with an exp claim can be a good time limit if the receiving API validates the signature, audience, and expiry on every request. It is not a universal solution. Some services cache authentication, accept opaque tokens validated elsewhere, or authorize a token against server-side state that remains active. A token that expires in 15 minutes also fails the task boundary if you issued it for a task that ended in four minutes.

The same issue appears with SSH. OpenSSH supports signed user certificates with a validity interval. Its ssh-keygen manual documents -V as the validity interval option. A short-lived certificate is far better than placing a long-lived private key in an agent workspace, but its expiry only answers the time question. You still need to restrict principals, destination hosts, and command behavior.

This command illustrates a certificate valid for 20 minutes:

ssh-keygen -s ./user_ca -I agent-run-7f3a \
  -n deploy-readonly -V +20m ./agent-run-7f3a.pub

The output normally names the signed public key file, such as:

Signed user key ./agent-run-7f3a-cert.pub: id "agent-run-7f3a" serial 0 for deploy-readonly valid from 2025-04-18T16:00:00 to 2025-04-18T16:21:00

Do not mistake that certificate for a complete task grant. If the remote account can run arbitrary commands, the certificate still authorizes arbitrary commands until 16:21. Use a constrained account, forced command, or a host-side command allowlist where the work demands it.

Revocation and expiry also differ. Expiry is predictable and works even when a central revocation service is unavailable. Revocation ends authority early when the incident resolves, the agent behaves unexpectedly, or an approver withdraws consent. A good design supports both: short maximum lifetime plus immediate revocation.

Task closure needs a machine-readable event

Closing a task must produce an event that an access component can consume. A human changing a ticket status without a connected revocation path creates a false sense of completion.

Use two termination conditions. The first is a hard end time set when the grant is created. The second is an earlier closure signal from the work system or responsible person. The effective end is the earlier of those conditions. A reopened ticket does not silently restore the old grant. It requires a new approval and a new end time.

This protects against a familiar failure. An agent receives access for an incident. The agent reports that it fixed the issue, so the operator closes the incident. A second agent process still holds an authenticated connection and continues gathering diagnostics. The team later reopens the incident, but nobody notices that the old process never stopped. The access decision became tied to a narrative in a ticket, not the lifecycle of the actual process.

Bind the authorization to an immutable run identifier as well as the task identifier. A task can have several runs over several days. Each run needs its own start, end, and owner. When the run exits, revoke the grant. When the task closes, revoke every still-active run attached to it.

The closure event needs an idempotent receiver. Systems retry webhooks and operators press buttons twice. A revocation request should accept repeated delivery and retain the first effective timestamp rather than treating a duplicate as an error that somebody ignores.

Record the source of closure. A status change from a ticketing system, a manual stop by the incident commander, a process exit, and a hard expiry mean different things during an investigation. The result is the same for access, but the audit record should preserve why it ended.

Do not grant an automatic extension just because an agent emits more tool calls near the deadline. That pattern rewards a busy process with more authority. Require a human to make a fresh decision, with a revised purpose if the work has changed.

A grant record prevents fuzzy approvals

Verify the audit chain offline
Run sp audit verify offline to check the ciphertext audit chain without a key.

A structured grant record turns approval into something the system can enforce and an engineer can review. It also exposes missing decisions before the agent touches production.

The following example is intentionally plain. It is a record format, not a policy language. The fields prevent an agent run from inheriting a vague, reusable permission.

grant_id: grant_01JQ7P8V6R
agent_run_id: run_7f3a2c
purpose: "INC-482: inspect checkout worker failures"
owner: "on-call engineer"
created_at: "2025-04-18T16:00:00Z"
ends_at: "2025-04-18T16:30:00Z"
ends_on_task_close: "INC-482"
targets:
  - "https://ops.example.internal/checkout/status"
  - "ssh://checkout-worker-03.internal"
allowed_actions:
  - "GET /checkout/status"
  - "journalctl -u checkout-worker --since 20m"
closure_required: true
verification_action: "GET /checkout/status"

The record does not say "production access: yes." That field would hide the choices that matter. It says who owns the decision, which run gets it, and what request you will use to prove the grant is gone.

Keep the scope readable enough for an approver to reject it. A giant list of wildcard paths makes people rubber-stamp access because they cannot understand it under pressure. If the agent needs twenty unrelated targets, split the work into separate grants or admit that the purpose is too broad.

Do not put a secret in this record. A grant identifier and target description are enough. The executor can resolve a protected credential after it has checked the grant. This design also makes it possible to retain useful audit data without retaining the material that could recreate production access.

The record should survive a restart and be append-only from the perspective of the person running the agent. If the agent can edit ends_at, modify its targets, or erase a closure signal, you have delegated the security decision to the process you intended to constrain.

Make explicit choices for operations already in flight. A log query can finish after expiry without much risk. A deployment, migration, or restart may need a cancellation rule, a transaction boundary, or a human takeover procedure. The worst option is leaving that behavior undefined and discovering it during recovery.

Prove expiry with a negative request

You prove access ended by sending a request that should now fail through the same path the agent used. A closed ticket, an expired badge in an admin console, and a revoked row in a database are supporting evidence. They are not the proof.

Schedule the check immediately after the earlier of task closure or expiry. Use a harmless endpoint that still requires the grant, such as a service status request. Do not test with a public health check, because a public endpoint will succeed whether access ended or not.

A simple test sequence looks like this:

# This call succeeded while the grant was active.
agent-action --grant grant_01JQ7P8V6R \
  GET https://ops.example.internal/checkout/status

# After task closure, repeat the exact protected operation.
agent-action --grant grant_01JQ7P8V6R \
  GET https://ops.example.internal/checkout/status

The second command should produce an output shaped like this:

request_id=req_92a1
status=403
reason=grant_ended
ended_at=2025-04-18T16:12:09Z

Your implementation may return 401, 403, or a connection refusal. Pick one meaning and document it. The audit entry must state that the access layer rejected the request because the grant ended, rather than because DNS failed or the target happened to be unavailable.

Test the paths people forget. If an agent can use both HTTPS and SSH, verify both. If the service issues a cookie after an API exchange, verify that the cookie does not outlive the grant. If a worker queues actions locally, verify that the executor checks authorization when it sends the queued action, not only when it accepts the job.

Run a scheduled expiry test in non-production before you rely on the design in production. Create a grant with a very short lifetime, make one allowed request, wait for expiry, and make the same request again. Then revoke a second active grant early and repeat the request. Those two checks catch different defects: bad clock handling and broken early revocation.

Clock discipline deserves attention. The component that evaluates the deadline should use a trusted system clock and log the time it evaluated. If a laptop can move its clock backward and thereby extend access, the deadline is not enforceable. A central broker reduces this risk because one always-running component makes the decision, but you still need to monitor its time source.

Agent processes need their own boundary

Review every protected call
Individual HTTP and SSH calls are recorded in the Activity journal for review after the run.

A task may continue across retries, terminals, and model turns, but an agent process remains a practical unit for access control. Granting per-run authority gives you a clean place to ask for approval, observe behavior, and revoke immediately.

Do not confuse a conversation with a process. A user can tell an agent, "continue investigating," after a model restart or after a tool launches a child process. If access follows the conversation without a new authorization event, the operator loses track of which executable holds the permission.

Record the process identity that initiated the run. On macOS, code-signing authority gives an approver more useful information than a user-supplied process name. A process called agent tells you almost nothing. A recorded signing identity, executable path, parent process, and launch time make later review possible.

Sallyport uses a session approval for a new agent process by default, and its approval card leads with the process's code-signing authority. That is a sensible process boundary, but session approval should still sit inside a task grant with an end time.

Kill or revoke on normal process exit, but never rely on exit alone. Processes crash, machines sleep, and parent-child relationships get messy. The hard deadline and task closure event remain the backstops. If the process survives past a task closure, the request path must deny it even before the operating system cleans it up.

Separate a human operator's terminal access from the agent's access. It is tempting to let the agent inherit the operator's authenticated shell because that makes setup easy. It also makes attribution useless and can give the agent every privilege the operator accumulated that day. Give the agent a distinct identity and make it ask through its own path.

Approval prompts do not erase the need for scope

A person clicking approve can catch an obviously wrong request, but approvals become weak when they arrive too often or describe too little. A prompt that only says "allow API call" asks the human to guess its consequences.

Put the purpose, target, method or command, grant end time, and agent identity where the approver can see them. If the system cannot explain the request in those terms, it has not collected enough information to ask for approval responsibly.

Per-call approval has a place for operations with irreversible effects: deleting data, rotating a credential, triggering a payment run, or deploying an unreviewed change. It should not become the normal control for every log read. People approve repetitive prompts by reflex, especially during an incident.

For a call marked with its per-call approval flag, Sallyport asks for approval for every use, but that setting should supplement rather than replace an end time. A human may approve a valid request at 16:29, while the access path must still reject any later request after the grant ends.

Keep a fast emergency stop that does not require finding the original approver. The on-call engineer needs a way to terminate a run when the agent loops, misreads output, or starts touching the wrong target. Record who used the stop and why. Emergency control without accountability can become another unreviewed access path.

An audit trail must answer the uncomfortable questions

Ask again for risky keys
A per-key setting can require one-click or Touch ID approval on every single use.

After a production event, people ask when access began, who allowed it, which process used it, what requests succeeded, and whether it really stopped. Logs that only record successes cannot answer the final question.

Record denied calls too, especially denials after expiry or revocation. A denial proves the enforcement point received a request and applied the boundary. It can reveal a stuck agent that keeps trying after task closure, which deserves attention even though the control worked.

Keep the grant decision and individual actions connected by identifiers. A reviewer should be able to move from grant_01JQ7P8V6R to its approving owner, associated task, run identity, allowed scope, closure source, and every request. If those records live in unrelated systems with no shared identifier, reconstruction becomes guesswork.

Tamper evidence matters because access records often become important after someone has an incentive to edit them. Sallyport projects its session and activity journals from an encrypted, hash-chained audit log, and sp audit verify can verify that chain offline over ciphertext. That property does not decide whether a request was appropriate, but it makes silent alteration of the recorded sequence harder to hide.

Do not collect every prompt and model thought as a substitute for action logs. Those records create privacy and retention problems and still may not identify the actual network request. Start with the authorization decision and action result. Add prompt context only when your operational and privacy rules justify it.

Give reviewers a fixed closure record. It should state the grant identifier, effective end timestamp, termination source, count of successful actions, count of denied post-end actions, and result of the verification request. That record turns a vague claim of "access removed" into something a change reviewer can challenge.

Build the control in a small, testable sequence

You do not need a general policy engine to enforce temporary access. You need a narrow mechanism that every agent action must use and a refusal path you can test.

Start by listing the actions agents currently take in production. Separate read operations, reversible writes, and actions that can cause irreversible or widespread effects. For each action, identify the actual credential holder and the enforcement point. This exercise often reveals credentials living in environment variables, shell profiles, CI logs, or copied configuration files.

Then build the minimum lifecycle:

  1. Create a distinct run identity and structured grant with a purpose and hard end time.
  2. Route each protected HTTP or SSH action through an executor that checks the grant before use.
  3. Accept an early closure or revoke event and record its effective timestamp.
  4. Send a harmless protected request after termination and retain the denial result.
  5. Review expired and revoked grants regularly for attempts that continued after access ended.

Do not start with elaborate rules that classify every possible command. Teams spend weeks debating syntax while their agents still hold long-lived credentials. Start with named targets, short lifetimes, per-run identity, and proof of denial. Add finer restrictions where your action history shows that broad scopes are actually being requested.

The quality bar is simple and demanding: when the task closes, an old agent process must fail at the production boundary, and you must be able to show why it failed. Until you can run that check, the access is temporary only on paper.

FAQ

Is a ticket reminder enough to remove an AI agent's production access?

A ticket that says "remove access when done" is an instruction, not a control. The authorization path must reject the agent after a recorded deadline or a closure event. Test that rejection with a real request before you mark the task complete.

How long should temporary production access last?

Use the shortest period that fits the work plus an explicit review point if the task runs long. An agent diagnosing a failed deployment may need an hour; an agent making a narrowly scoped migration may need less. Do not issue a week-long grant because nobody wants to revisit the decision.

Does an expiring token guarantee access ends?

No. Token expiry stops use only if every relevant service validates the expiry and rejects the token after that point. A credential with a long lifetime, a cached session, or a separate SSH path can remain usable after the agent's task has ended.

What should a purpose statement for agent production access include?

A purpose statement should name the incident, change, or maintenance task; the exact systems involved; permitted operations; an accountable owner; and a stop condition. "Production debugging" is too broad because it gives no reviewer a clear boundary to enforce.

Can task closure automatically revoke an AI agent's access?

Treat a closed task as a revocation trigger, then verify the action rather than trusting a workflow status. The system issuing or brokering access should deny new calls, active sessions should end where possible, and the audit record should show both the closure and the denial.

Should AI coding agents get per-session or per-task access?

For an autonomous coding agent, per-run access is usually the safer default. A process can continue after a human has left the terminal, fork into another task, or be reused with a different instruction. Bind the grant to the specific agent run and terminate it when that run exits.

Can SSH certificates provide temporary production access?

SSH certificates can carry a validity interval, and OpenSSH documents the ssh-keygen -V option for that purpose. They help with time limits, but they do not by themselves restrict the remote commands, repository, or host set unless you add those controls too.

Are approval prompts a substitute for an expiration time?

No. Approval proves that a person allowed an action at a moment in time; it does not define how long the permission lasts. Repeated prompts also train people to approve mechanically, which defeats the point of putting a human in the path.

How do I prove an agent can no longer reach production?

Verify from the same agent identity and the same route used during the task. Record the command, timestamp, expected denial, actual response, and the audit event that explains it. A dashboard status alone does not prove the protected service denied the request.

What should an audit record show for temporary agent access?

Keep the smallest record that can reconstruct the decision: purpose, owner, identity, scope, start and end time, closure signal, revocation result, and verification result. Protect it from silent editing, because a mutable spreadsheet cannot settle a disputed production action.

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