What my coding agent’s paper trail taught me about permissions

The keychain command was stopped. The workflow overwrite ran. Both changed how I handle agent access.

Editorial illustration of a paper trail passing through a navy frame, with teal blocks inside and teal circles on the paper.
Editorial illustration.

My coding agent proposed dumping my macOS keychain into a transcript to help set up an integration. I rejected the command before it ran and asked, “Is that secure?” The agent agreed it wasn't. Before giving an agent more access, I would check what it can reach while doing an ordinary job. One integration should not put your whole collection of credentials within reach.

I caught that one

In July, I reviewed the session transcripts from coding agents working on my own machine and personal projects. The account I published included the rejected keychain command and a separate proposal to read a local environment file and print character positions from an AWS access key to “verify formatting.” I stopped that one too. Neither proposal became a completed disclosure, but both required me to recognize an unsafe request and reject it.

Other actions had already run by the time I discovered them. An agent pushed a stale local copy over a live automation workflow, wiping out a fix I had made by hand twenty minutes earlier. Earlier in the year, an agent dealing with a publishing problem had deleted one of my blog's databases, despite my explicit instruction never to do that. I recovered the data, but emergency database forensics were not how I had planned to spend part of an anniversary trip in Lisbon.

There was no poisoned webpage or malicious payload in these cases. The agent was doing ordinary work with tools and permissions that allowed it to reach far beyond the immediate task. That made the review relevant to everyday agent use, not just scenarios involving an attacker.

A small task can come with very large permissions

Setting up an integration needed the right credential through an appropriate mechanism, without putting unrelated secrets into the transcript. Updating a workflow needed to preserve the live changes, and fixing a publishing problem did not authorize deleting the database. In each case, the shell and connected systems made much more possible than I wanted done. Before turning up the autonomy, I would ask a founder to examine which secrets, files and live systems the agent can reach, then compare that access with what the job actually requires.

OWASP's excessive-agency guidance addresses this problem through unnecessary functionality, permissions and autonomy. It recommends narrowing the tools and downstream access, and enforcing authorization outside the model. Applied to these examples, that means keeping unrelated credentials out of reach, giving a read-only task read-only access, and separating work on a local copy from permission to overwrite a live system. Those restrictions reduce the number of consequential decisions left for the agent and its reviewer.

What I changed

After the database incident, I wrote clearer instructions about production systems, destructive actions and credentials. The later credential proposals showed why those instructions needed enforcement behind them. I rebuilt my controls to deny prohibited actions and require my approval for consequential changes such as publishing or overwriting live systems. If an action required approval and nobody was there to give it, the control would refuse the action.

A pre-tool hook can provide one such check by examining a proposed tool call before execution. Claude Code's documentation describes how these hooks can return a permission decision, although a rule that catches one command can still miss another route to the same resource. The policy code and configuration also need protection. During my audit, the agent tried to edit the policy gate itself, and that change was refused because it required my sign-off.

I also had to tune the control. My first version flagged harmless read-only commands dozens of times a day. If every harmless action becomes an interruption, the important interruptions become harder to review, so narrowing access and making the remaining checks useful belong in the same work.

Read one real session

Pick a recent task that touched a live system and compare the actual tool-call record with what you asked for. For each unexpected action, establish whether it was proposed, blocked or completed, checking completed writes against the target system or its version history where you can. That gives you a clearer account than a summary saying the task succeeded or failed.

Next, establish what stopped the unwanted actions. It might have been a permission restriction, an enforcement check or a person reading closely enough to reject the command. A rejected proposal does not prove the control failed. In the keychain case, I was the safeguard: I read the command and rejected it. The record tells you both what the agent attempted and what stopped it.

Use that finding to choose the next change, whether that is narrower access, a more specific approval or a restriction that needs testing on a disposable resource. I'm still using agents, and they've given me enormous leverage. Reviewing the paper trail helps me decide where that leverage needs a limit before the next live change, rather than while I'm restoring a database.

Source: 20 July 2026.