8 min read

Service accounts for AI agents without shared accountability

Use service accounts for AI agents without shared accountability by assigning workflow identities, named owners, scoped access, audit evidence, and retirement rules.

Service accounts for AI agents without shared accountability

AI agents need service accounts, but a service account should never become the name you use when you do not know who authorized an action. Give every durable workflow its own identity, put a named person behind that identity, and remove it when the workflow stops. Anything weaker creates shared accountability, which is another way of saying nobody can give a complete answer after a bad call.

I have seen this fail in a familiar pattern. A team creates one account called automation, gives it enough access to unblock several coding jobs, and calls the setup temporary. Months later, a deployment job, a dependency updater, and an agent that edits infrastructure all use it. The account remains active because turning it off might break something. When it changes a production setting, the logs identify automation perfectly and explain almost nothing.

The fix is not a grand identity program. It is a few hard boundaries: one workflow identity for one permission purpose, a human owner who can approve or stop it, evidence that ties each use to a specific run, and an exit path designed before the account receives access.

A service account identifies authority, not the actor

A service account says which permission bundle an API or system accepted; it does not prove which agent, prompt, code revision, or human caused the request. Teams routinely blur these two jobs, then discover that their audit trail cannot explain an incident.

Suppose release-publisher can publish build artifacts. A successful request authenticated as that account tells you that the publisher authority was used. It does not tell you whether an approved release workflow made the request, whether a developer ran a local script, or whether an agent retried an old task after its human owner had gone home. The authentication record answers "which authority?" It does not answer "why this call, in this run, now?"

Keep the layers separate:

  • The workflow identity holds narrowly defined authority.
  • The agent session identifies a particular process execution.
  • The human approval or automation trigger explains who started or allowed that execution.
  • The action record captures the target, requested operation, result, and time.

This distinction changes how you investigate. If a deploy call looks wrong, first disable the workflow identity to stop further authority. Then inspect the session record to find the process that used it, the code revision it ran, and the person or system that authorized the session. A single account name cannot carry all of that history without becoming a shared bucket.

RFC 6749 makes a useful but limited statement in its description of the OAuth 2.0 client credentials grant: a client can use its credentials as an authorization grant when it acts on its own behalf. That is correct for a bounded machine workload. It does not mean every process that can present the credential has the same legitimate purpose. Treating the grant as complete accountability is where teams get hurt.

An agent also has a different risk profile from a conventional job. A conventional job usually follows a fixed code path. An agent may choose commands, construct requests, retry with altered arguments, or be steered by material it read in a repository. The service account still needs a stable purpose even when the caller's choices vary. If you cannot write that purpose in one plain sentence, the account has probably accumulated unrelated work.

One workflow needs one bounded identity

A workflow deserves its own identity when it has a distinct purpose, permission boundary, owner, environment, or ending condition. Do not create an account for every prompt or every short task. That produces noise without better control. Create identities at the level where you can revoke a unit of authority without stopping unrelated work.

"Update dependency manifests in approved repositories" and "publish signed artifacts to production" should not use the same identity. The first workflow changes source files and opens review requests. The second changes a release channel. Even if one agent can initiate both, the permissions differ, the person who should approve them may differ, and the response to a suspicious action certainly differs.

The opposite mistake also costs teams time: splitting a single narrow workflow into dozens of account names because each agent run creates one. Run identifiers already describe short lived executions. Service account identities should describe a durable authorization purpose. Use session records for individual runs and accounts for authority that persists across runs.

A workable naming pattern is:

<environment>.<product-or-repository>.<workflow-purpose>

prod.payments.release-publisher
dev.docs.dependency-updater
prod.data.backfill-reader

Names help humans, but names do not enforce scope. Bind each identity only to the actions its workflow needs. Give the dependency updater permission to create a branch and submit a review request if that is all it does. Do not grant it release permissions because it might someday need them. "Might" has created more standing access than any real requirement.

Use separate identities for separate environments. A development agent can run experiments, retry aggressively, and operate on disposable data. That behavior does not belong under a production identity. Copying a production credential into a development configuration reverses the boundary: the least controlled environment now owns the strongest authority.

There is one legitimate exception to strict separation: two invocations can share an identity when they are the same documented workflow, have the same owner, use the same permission set, and end under the same retirement condition. The test is practical. If disabling the account would make you ask "which of these unrelated jobs just broke?", the account covers too much.

An owner must be a person who can stop the work

Every workflow identity needs one named human owner with the authority and obligation to answer for it. A technical contact can help operate the workflow, and a team can provide continuity, but neither replaces a specific owner.

An owner does four concrete things. They confirm that the workflow still has a purpose, approve changes in its access, respond when a suspicious call appears, and retire the identity when the work ends. If the listed owner cannot perform those tasks, the record is decorative.

NIST SP 800-53 Revision 5, control AC-2 on account management, requires organizations to define account types, establish conditions for group and role membership, and disable accounts when they are no longer associated with a user or no longer needed. The control applies cleanly to nonhuman accounts even though people often read it only as employee account guidance. A machine identity without an accountable custodian has no one to establish those conditions or decide that the account is no longer needed.

Put the account record in a repository or registry that the access review process uses. Do not bury it in a wiki page that drifts from the actual binding. This minimal record has enough fields to support a serious review:

identity: prod.payments.release-publisher
owner: "Morgan Lee"
technical_contact: "Release engineering on-call"
purpose: "Publish approved signed payment service artifacts to production"
allowed_actions:
  - "upload signed artifact to production release repository"
  - "read release metadata for the payment service"
environments:
  - production
permission_bindings:
  - "artifact-repository/publish-payments-prod"
credential_method: "short lived workload token"
source_repository: "payments-service"
review_after: "2026-06-30"
retire_when: "The payment service stops using this release workflow"
incident_contact: "Release engineering on-call"

The record makes ambiguity visible. If allowed_actions becomes a paragraph containing several systems and vague verbs such as "manage" or "administer", split the workflow. If retire_when says "never" or has no condition, the account has entered the permanent-access pile. If the owner field names a distribution list, assign a person before granting access.

Ownership changes need their own control. A departing employee should not remain the nominal owner because transferring the record feels tedious. Require the incoming owner to accept the account, read its purpose and bindings, and set the next review date. If nobody accepts it, disable it. Systems do not get an exemption from ownership merely because they keep running.

Shared accounts turn a small incident into a guessing exercise

A shared account fails most clearly during a routine-looking incident, not a dramatic breach. Consider a team that uses prod.agent-ops for three workflows: a release agent, an incident summary agent, and an infrastructure repair agent. The account can read deployment state, alter an environment variable, and trigger a rollback.

At 16:20, someone notices that an environment variable changed to an invalid value. The API log says prod.agent-ops made the request. The release team says its run had completed earlier. The incident team says its agent was reading status but does not believe it writes settings. The infrastructure owner says a repair prompt was tested that afternoon, but no one retained the exact session. All three statements can be true, and the account log cannot resolve the conflict.

The usual response is to search chat messages, repository history, shell histories, and model transcripts. That may find an answer, but it is slow and incomplete. Worse, the same account remains active because disabling it could stop release recovery. The incident has now tied together detection, containment, and unrelated production operations.

Separate identities change the sequence. If prod.payments.release-publisher makes an unexpected call, disable that identity. The incident summary account and repair account keep their own authority. The action record should include a workflow identity and run reference, so investigators can locate the exact session without arguing from memory. This is why "one account per team" is not a compromise. It is a decision to merge failure domains.

Some teams defend shared accounts by saying that centralization makes rotation easier. That feels true because there is only one credential to replace. The operational saving is small, while the cost appears whenever you need targeted revocation, a permission review, or an explanation. Automate credential issuance and binding management instead of making the account boundary broad enough to be convenient.

Do not confuse a shared account with a shared permission role. Several identities can receive the same narrowly defined role if they perform the same allowed operation. The identities remain distinct, so logs and revocation still work. Reusing a role preserves manageability; reusing an identity destroys attribution.

Permission scope must follow actions, not agent ambitions

Verify action evidence offline
Verify the encrypted hash-chained audit log offline with sp audit verify, without a vault key.

Grant access for the exact action path you intend to permit, then make the agent prove that it needs anything else. An agent's general capability does not justify general authority.

Start with the workflow's verbs and objects. "Read the open issues in repository A and create a branch in repository A" is an action description. "Maintain repository A" is not. The first statement lets an administrator find a read scope and a branch creation scope. The second usually ends with broad write access because nobody can map it to a precise permission.

The same discipline applies to API permissions. If a workflow reads a report and posts a comment, do not give it account management access because the API groups those endpoints in an attractive broad role. Build a smaller permission set if the provider permits it. If the provider does not, place an action-specific intermediary in front of the broad credential or reconsider whether the agent should perform that action unattended.

This is where many agent deployments make a subtle error. They grant broad access because the agent needs to inspect context before it acts. Reading context and changing a target are different authorities. Give read access where possible, then require a separate identity or approval path for state changes. A model that can read a production configuration does not automatically need permission to edit it.

Test the boundary with deliberately wrong requests before putting the workflow into regular use. For a release publisher, try to publish an artifact for a different product, delete a release, and modify repository settings. Each request should fail at the authorization layer. A successful happy-path test only proves that you granted enough access. Rejected adjacent actions prove that you did not grant too much.

Keep the test result with the ownership record. It can be as plain as a small table:

AttemptExpected resultReview result
Publish approved payment artifactAllowedConfirmed
Publish artifact for another serviceDeniedConfirmed
Delete a production releaseDeniedConfirmed
Change repository membershipDeniedConfirmed

Do not let an agent choose an identity from a menu of powerful accounts. The workflow runner should attach the identity that belongs to the job. An agent that can select between unrelated authorities can often route around the boundary you designed.

Credentials should expire before forgotten workflows do

Long lived credentials make retirement hard because a forgotten copy can keep working after you disable a visible job. Prefer short lived workload credentials issued to a verified runtime, or have a controlled component perform the authenticated action while the agent receives only the result.

This is a separate issue from identity design. You can have a beautifully named, owned service account and still lose control if its credential sits in a repository, a local environment file, an agent transcript, or a build log. The account record says who may use authority. Credential handling decides who can actually present it.

The preferred sequence is simple:

  1. Verify the calling runtime or agent session.
  2. Issue a credential with a short expiry and the workflow's limited scope, or execute the requested action on its behalf.
  3. Record the requested action and the authorization decision.
  4. End the session and invalidate authority that belongs only to that session.

Do not hand a plaintext secret to the agent merely because the agent needs to call an API. That turns every prompt, tool output, trace, and accidental log into a possible credential distribution path. Masking secret values in console output helps after exposure; it does not prevent the agent from receiving and reusing them.

Sallyport takes the second route for its supported HTTP and SSH channels: the agent asks for an action through its MCP connection, while the app keeps API and SSH credentials in its encrypted vault and executes the action itself. That arrangement can keep credentials out of an agent context, but it does not remove the need to design separate workflow identities and owners.

For systems that must use a credential directly, record where it is issued, how it is delivered, its maximum lifetime, and who can revoke it. Rotation should not depend on someone remembering a calendar entry. Make issuance and replacement part of the workflow deployment process, then test a rotation before the account becomes important.

A credential with a short expiry is not permission to ignore logs. An agent can do real damage during a short session. Expiry limits persistence after misuse; scope, approval, and action recording limit what happens during the session.

Audit records need to join authority to a specific run

Broker HTTP authority safely
Sallyport injects bearer, basic, or custom-header credentials for HTTP calls without handing them to the agent.

A useful audit trail lets you reconstruct an action without treating the service account as the whole story. Store the workflow identity, the agent run identifier, the initiating trigger, the authorization decision, the target, the operation, the result, and the timestamp in records that investigators can correlate.

Use a consistent event shape. The following JSON is not tied to a provider, but it captures the fields people usually wish they had after the fact:

{
  "event_id": "act_01J8Q7M6K4",
  "time": "2026-04-14T16:20:31Z",
  "workflow_identity": "prod.payments.release-publisher",
  "run_id": "run_8f3c1d",
  "trigger": {
    "type": "approved_ci_job",
    "initiator": "morgan.lee",
    "source_revision": "a1b2c3d4"
  },
  "authorization": {
    "decision": "approved",
    "approved_by": "morgan.lee",
    "approval_ref": "apr_31fa"
  },
  "action": {
    "target": "production artifact repository",
    "operation": "publish",
    "resource": "payments-service/2.4.1"
  },
  "result": "success"
}

Do not put credentials, full prompts containing sensitive material, or unrestricted payloads into an audit record just because you want forensic detail. Record enough stable context to establish causality, then apply the same data handling rules you would apply to any operational log. An audit system that becomes a second secret store creates its own incident path.

Integrity matters too. Logs that an agent or a compromised workflow can alter do not settle disputes. Use append-only storage, separate write permissions from read and administration permissions, and verify integrity on a schedule. Keep the evidence available after an account is retired. Retirement removes future authority; it must not erase the history needed to explain past actions.

Sallyport's Sessions and Activity journals are projected from a write-blind encrypted, hash-chained audit log, and its sp audit verify command checks that chain offline without needing a vault credential. That is useful evidence for actions it brokers, but your surrounding systems still need to preserve the workflow owner, trigger, and business approval context.

Design the review around questions someone can answer in minutes: Which workflow had this authority? Who owned it at the time? Which run used it? What approved that run? Which exact operation succeeded or failed? If any answer requires reconstructing a story from chat history, your records are incomplete.

Retirement is a workflow, not an annual cleanup task

Separate keys from agent runs
Sallyport keeps API and SSH keys in its encrypted vault, outside the agent process.

Retire a service account when its workflow ends, when its owner cannot be replaced, or when a material change makes its original purpose false. Annual reviews find stale accounts, but they are too slow for events you already know about.

Build retirement conditions into the original account record. A migration workflow can retire when the migration completes. A repository automation can retire when the repository archives. A vendor integration can retire when the contract ends. These conditions make the decision less political because the account already has an agreed ending.

Use this sequence when you retire an identity:

  1. Disable new use of the account and revoke active credentials or bindings.
  2. Monitor expected failures for a defined observation window and identify any undocumented dependency.
  3. Restore only the minimum access needed for a verified dependency, with a new owner and record if the workflow is still legitimate.
  4. Preserve the ownership record, access history, and action logs under your retention rules.
  5. Remove the identity and its remaining credentials when the observation window closes.

Do not start by deleting the account. Deletion can remove useful configuration and make failures harder to diagnose. Disabling gives you containment and lets ordinary monitoring reveal hidden callers. It also forces a useful conversation: if a workflow breaks, who claims it and why was it absent from the registry?

The account owner's departure deserves immediate attention. Before their final day, transfer ownership only after the replacement accepts responsibility. If no replacement exists, disable the account. A team may decide to re-enable it later for a documented operational need, but an orphaned account should not keep authority because someone might need it.

Retirement also applies when a workflow expands. If a dependency updater begins deploying code, do not edit its purpose until it describes two unrelated jobs. Retire or reduce the old identity, then create a deployment identity with its own owner, scope, tests, and review record. That preserves the audit meaning of both accounts.

Accountability survives only if reviews can revoke access

A service account review has value only when reviewers can see the actual bindings, identify the current owner, and disable access without a week of negotiation. Build that authority into the operating process before you need it.

Review the highest-risk workflows more often, but do not turn every review into a paperwork ritual. Ask whether the stated purpose still exists, whether the listed owner still has authority, whether recent actions fit the purpose, and whether the scope still matches observed use. If the answer is unclear, reduce or disable the account while the owner clarifies it.

Measure health with facts that reveal neglected authority: accounts with no owner, past-due review dates, no action history for a meaningful period, credentials near or beyond their intended lifetime, and workflows whose recent operations fall outside their documented purpose. These are review queues, not vanity metrics.

The first practical action is to export your existing agent identities and write one sentence beside each: "This workflow may do X for Y, owned by Z, until condition W." Accounts that resist that sentence are the ones carrying hidden shared accountability. Disable or split those before adding more agent capability.

FAQ

When should an AI workflow get its own service account?

Use a separate identity when the workflow has a different owner, purpose, permission set, environment, or retirement date. A minor revision of the same controlled job can retain its identity if the owner reviews its permissions and records the change. Treat a change in authority as a new identity, not as a routine edit.

Can a service account identify an individual AI agent run?

A service account identifies the workload that received authority. It does not identify the agent process, model, repository, prompt, or human who authorized a run. Keep those facts in separate session and action records, then join them through a run identifier.

Are shared service accounts ever acceptable for AI agents?

No. A shared account erases the distinction between workflows at the point where permissions matter most. Separate accounts may add a little administration, but they make revocation, review, and incident investigation practical.

What should a service account ownership record contain?

Each record needs a named human owner, a technical contact if different, a purpose, an allowed action description, its permission location, its environments, a review date, and a retirement condition. A team name alone is not enough because teams do not answer an approval request or leave a company. Store the record where access reviewers can find it without reading source code.

Should autonomous coding agents receive long lived API credentials?

Service account credentials are often longer lived and easier to copy than a human login session. Prefer short lived workload credentials or a broker that performs the authenticated action without disclosing the credential to the agent. If a long lived credential is unavoidable, restrict its scope, store it outside the agent context, and rotate it on a schedule.

How do I retire a service account safely?

Disable the account first, then observe failed calls and restore only if an owner documents a real dependency. Revoke credentials and active bindings after the observation period, preserve the account record and audit history, then delete the identity according to your retention rules. Deletion before evidence preservation turns a cleanup task into an investigation problem.

What logs are needed for accountable agent actions?

For each action, retain the workflow identity, the agent process or session identifier, the initiating human or approved automation, the target, the requested operation, the result, and the timestamp. Also retain the authorization decision that allowed the action. An API access log that contains only a service account name cannot answer who approved a risky run.

Is OAuth client credentials flow safe for AI agents?

The OAuth 2.0 client credentials grant authenticates a client acting on its own behalf. That can fit a tightly bounded noninteractive workflow, but it does not solve ownership, agent session attribution, or retirement. You still need a registry, short credential lifetime, and action records.

Should development and production AI agents share an account?

Separate development, test, and production identities even if the code path is identical. Development agents routinely execute experiments and retries that production identities must never inherit. A production account should have a production owner and a retirement condition tied to the production workflow.

What events should trigger immediate service account retirement?

Disable it immediately when its owner leaves, its repository is archived, its approval path disappears, or its workflow changes beyond the documented purpose. Do not wait for a scheduled review when you already know the account no longer has a legitimate operator. Scheduled reviews catch omissions; they should not delay obvious revocation.

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