Topic 05

Least Privilege for Agents

Security

The agent acts with whatever credentials your session holds: the environment of your shell, your cloud login, your database URL, your repository token. Every command it runs, including one it was talked into by text it read, runs as you. Least privilege for an agent means giving the session its own, smaller identity, so that the worst thing it could do is something you could live with.

The previous four pages each ended in the same place. A secret read by accident, an injected build step, a squatted package's install script, a shortcut that skips a check: how bad each one gets depends on what the session is allowed to do. This page is about setting that limit deliberately, before any of them happens.

What It Inherits

A terminal agent starts with your environment. That means the variables your shell profile exports, the cloud command-line tool's cached login, the keys loaded in your SSH agent, the repository host's token, the package registry's publish token, a cluster configuration file. Nothing about starting an agent removes any of these. It inherits the union of everything you set up over years for every task you have ever done.

The variables an agent inherits from your everyday shell, names only
$ env | cut -d= -f1 | grep -E "URL|TOKEN|KEY|SECRET|PASS|SOCK" | sort
DATABASE_URL
PACKAGE_PUBLISH_TOKEN
PAYMENTS_LIVE_KEY
REPO_TOKEN
SSH_AUTH_SOCK

At Cragmoor your profile exports the production database URL "for the monthly report", with a user that can write. One afternoon you ask the agent why a member's booking is missing. It offers to query the database directly, and the database in its environment is production. The approval prompt caught it: you read the command, saw the host, and said no. Nothing should have depended on that. The command could as easily have been a cleanup the agent decided was helpful, approved at the end of a long day.

The environment is half the picture. Credentials also live in files the session can read: the cloud tool's cached login in your home directory, private keys next to it, a package registry configuration holding a publish token. A listing of variables will not show any of those. A sandbox that hides your home directory from the session, as Chapter 3 set up, removes them; a clean environment on its own does not.

A Session Identity

Give the agent credentials of its own: scoped tokens issued for the agent's work, limited to one repository, with only the permissions the task needs, and an expiry measured in days. The major repository hosts offer exactly this kind of token, restricted to particular repositories and to specific permissions, with an expiration date you choose. The same idea applies to the cloud project, the database and the package registry.

A separate identity buys two things beyond the limit itself. The audit log can now tell what the agent did from what you did, which matters on the day something goes wrong and someone asks. And revoking the agent's token does not lock you out, so you can revoke it the moment you are unsure, without hesitating over what else it would break.

Read-Only by Default

Most of what an agent does in a session is reading: code, logs, issues, query results. So the default identity reads. A read-only database user on a replica. A viewer role in the cloud project. A repository token that can read issues and not close them. The task that needs a write gets the write, for that task, and the default session cannot change anything outside the working tree.

Read-only limits damage, not leaks. An agent that can read member data and reach the network can still send that data somewhere, which is why the previous pages kept untrusted text and outbound network away from sessions that can see private data. Read-only is one control of several, not a reason to relax the others.

No Production in the Session

Production credentials are never present where an agent runs. Not in the environment, not in a configuration file, not in the shell history. Staging should hold synthetic data, and Cragmoor's does not: it holds the weekly copy of real members from Chapter 3, so staging credentials stay out of the session too, and its side effects go to sinks. Cragmoor's staging email sender points at a test inbox, so an integration test cannot send a 15-minute waitlist offer to a real member waiting for a real class. The payment provider's live mode is the same: staging talks to the sandbox, and the live key exists only in production's secret store.

Your everyday shell, and the shell an agent session should start from
Your shellyears of setup
Production database URL with write rights. Live payment key. Repository token for every repository you can see. Package publish token. Cached cloud login with an owner role.
The agent's sessionone task's worth
A read-only user on a test database of synthetic members. The payment sandbox key. A token for one repository, expiring in a week. No publish token. A cloud viewer role, if any.

Irreversible Means Impossible First

Some actions cannot be taken back: deleting or migrating data, sending messages to people, spending money, pushing to shared branches, publishing packages, changing permissions. For these, the order of defences matters. First make them impossible in the session, by withholding the credential that would allow them. Only then rely on an approval prompt from Chapter 3 for whatever must remain possible. A missing permission behaves the same way every time. An approval prompt depends on someone's attention, many times an hour, and attention is the resource an agent session spends fastest.

The One-Line Question

Before a long or unattended run, write down the worst thing this session could do with what it holds. One line. If the answer is "drop the production members table" or "email every member", the setup is wrong, whatever the task is, and no amount of care in the prompt fixes it. If the answer is "make a mess of a branch I can delete", the setup is right. The cloud agents of Chapter 13 are the same question with a smaller answer, and that smaller answer is most of their appeal.

Two minutes of checking makes the question honest. Start the session from a clean shell and list its environment once, names only, to see what it actually holds. In a shell started from scratch, the same listing shows a handful of variables and none of the credentials above.

The same listing in a shell started with only the basics
$ env | cut -d= -f1 | sort
_
HOME
LANG
PATH
PWD
SHLVL
TERM
Least Privilege vs a Sandbox

A sandbox, as Chapter 3 set it up, limits where the process can reach: which files, which network destinations. Use it to contain what the session can touch.

Least privilege limits what the identities the session holds may do at the places it can reach. A sandbox with production credentials and an open route to the database protects nothing that matters; a narrow token on an unsandboxed laptop still exposes your files. Use both; neither stands in for the other.

Misconceptions
  • "Approval prompts are least privilege." A prompt is a person deciding quickly, many times an hour. A withheld credential decides the same way every time.
  • "The agent runs on my laptop, so it has my rights anyway." That is the problem being solved. Your rights are the union of everything you ever do, and the session needs a fraction of them.
  • "Read-only is safe." Read-only limits damage, not leaks. An agent that can read member data and reach the network can still send it somewhere.
  • "Staging is harmless." A staging copy of production members and a staging email sender wired to the real mail service make staging production under a different name.
  • "Least privilege slows the agent down." It costs a few minutes once per project and removes a whole class of failures. The agent works the same within the narrower identity.
Why It Matters
  • Start agent sessions from a shell without production credentials, and list its environment once to prove it. Inherited variables are the easiest grant to miss.
  • Give the agent its own scoped, expiring token per repository and service, so the audit log can tell its actions from yours.
  • Make irreversible actions impossible before making them approvable. Withheld credentials do not get tired.
  • Point staging's outbound side effects at sinks. A test that cannot reach a real member cannot email one.

Knowledge Check

You start a terminal agent from your everyday shell, whose profile exports the production database URL. What can the agent reach?

  • Only the files in the working tree, since agents are confined to the repository they start in
  • Only what the project instructions file lists, since that file defines the whole session's access
  • The production database, with whatever rights that URL's user has, through any command it runs
  • Nothing outside staging, since agents refuse to connect to any host with production in its name

A team runs its agent in a container with no network except to the database, using the production credentials. Which statement is right?

  • It is safe, since the sandbox prevents any data from leaving the container
  • The sandbox limits where it can reach; the credentials still allow anything at the database
  • It is unsafe only if the container also has access to the developer's home directory
  • It is equivalent to least privilege, since the container can reach only one service

What does giving the agent a read-only database user prevent, and what does it not?

  • It prevents both damage and data leaks, because a read-only user cannot export rows from tables
  • It prevents nothing useful, since an agent can grant itself write rights through the database
  • It prevents data leaks but not damage, since a read-only user can still run destructive migrations
  • It prevents changes to the data, but not reading it and sending it somewhere over the network

Why should irreversible actions be made impossible before they are made approvable?

  • A withheld credential refuses every time; an approval prompt relies on attention, many times an hour
  • Approval prompts are only shown in the editor, so a terminal agent would never display any of them at all
  • Agents are built to retry refused prompts until they are approved, so the prompts eventually give way
  • Withholding credentials is faster to set up than configuring any approval prompts in a new tool

A team's staging uses synthetic members, but its email sender is wired to the real mail service. What is the risk?

  • None, since synthetic members have made-up addresses that no real mail service will deliver to
  • An integration test can send real messages, so staging behaves like production for email
  • The test suite slows down, since every integration test waits for real deliveries to complete
  • The mail provider may suspend the account, since staging traffic looks like spam to it

You got correct