Blog

How to Audit AI Agent Permissions in 30 Minutes

A step-by-step AI agent permissions audit: review .env.local, scope Supabase and GitHub tokens, configure PreToolUse hooks. Complete it in one sitting.

agent-patterns
Single brass key resting on a dark black surface with dramatic side lighting
Photo by Matt Artz on Unsplash

An AI agent permissions audit is a systematic review of every credential, token, and access scope your coding agent can reach. The goal: confirm that each token grants only the permissions the agent needs for its actual work, and that no single credential can cause damage you cannot recover from a backup. Armada Works runs this review on every new codebase before deploying an agent fleet, and the entire process takes about thirty minutes.

This post builds on the guardrails framework we published earlier, which covers the threat model and the PocketOS incident that illustrated it. If you have not read that post, start there. This one assumes you understand why credential scoping matters and walks you through the practical steps of checking what you currently have.

What the Audit Covers

The audit checks five surfaces. Each one answers the same question: if this credential were used at maximum scope, what breaks?

  • .env.local inventory: what credentials exist and what blast radius each carries
  • Supabase token scope: whether your agent holds a Personal Access Token that can delete the entire project
  • GitHub token permissions: whether the token can delete the repository or change its visibility
  • Vercel token and team isolation: whether one token can take down multiple products
  • PreToolUse hook configuration: whether destructive commands are blocked before execution

If all five pass, your agent setup is hardened against the class of unrecoverable failures that hit PocketOS. If any one fails, fix it before handing the agent your codebase.

Step 1: Inventory Your .env.local

Open your .env.local and list every variable. For each credential, classify it into one of three blast-radius tiers.

Tier What It Can Destroy Examples
Account-level Every project under your account Supabase PAT, Vercel team-scope token, GitHub classic PAT with delete_repo
Project-level One project's data, deploys, or history Supabase service-role key, Vercel project-scope token, GitHub fine-grained PAT
Row-level Individual records only Supabase anon key, third-party API keys (PostHog, Resend, etc.)

What passes: zero account-level credentials in .env.local. Every credential is project-level or lower.

What fails: any token that can delete a project, an organization, or a team. The agent does not need that authority to do useful work. If you find one, the remaining steps tell you what to replace it with.

Most vibecoded apps ship with at least one account-level token because the default setup instructions for Supabase and GitHub hand you the broadest credential first. That is not a criticism of the tools. It is the gap this audit exists to close.

Step 2: Scope Your Supabase Credentials

Supabase gives you two credentials that matter here: the service-role key and the Personal Access Token (PAT).

The service-role key is scoped to one project. It can run queries, execute migrations, manage storage buckets, and call Edge Functions. It cannot delete the project itself. This is the credential your agent needs.

The PAT is scoped to your entire Supabase account. It can call the Management API, which includes DELETE /v1/projects/{ref}. That endpoint deletes the project, the database, and all backups associated with it. Your agent does not need this.

How to check:

grep -i 'sbp_\|SUPABASE_ACCESS_TOKEN\|SUPABASE_PAT' .env.local

If that returns anything, you have a PAT in your agent's environment. Remove it. The service-role key (SUPABASE_SERVICE_ROLE_KEY) is sufficient for every operation an AI coding agent performs during normal development work.

What passes: .env.local contains NEXT_PUBLIC_SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY only. No PAT.

What fails: any Supabase PAT present in the file. Even if your agent has never called the Management API, the credential is one curl call away from project deletion.

Step 3: Scope Your GitHub Token

GitHub offers two token types: classic Personal Access Tokens and fine-grained Personal Access Tokens. The classic PAT is the one most developers create first. It is also the dangerous one.

Classic PATs use coarse scopes. The repo scope, which most setup guides tell you to enable, includes delete_repo authority. A fine-grained PAT lets you set individual permissions per repository, including setting the Administration permission to read-only. That blocks repository deletion while still allowing pushes, pulls, and pull request creation.

How to check your token type:

gh auth status

Look for the token prefix: ghp_ means classic PAT, github_pat_ means fine-grained PAT, gho_ means OAuth app token. If you see ghp_, you are running a classic PAT with coarse scopes.

How to check whether your token can delete the repo:

gh api repos/OWNER/REPO --jq '.permissions.admin'

If this returns true, the token has admin-level permissions on the repository. That includes deletion authority.

What passes: fine-grained PAT (github_pat_ prefix) with the Administration permission set to read-only. The gh api check returns false for .permissions.admin.

What fails: classic PAT with repo scope, or any token where the admin permission check returns true. Replace it with a fine-grained PAT. GitHub's token settings page at github.com/settings/tokens?type=beta lets you create one scoped to a single repository with granular permissions.

Step 4: Scope Your Vercel Token

Vercel tokens are scoped to a team. If all your projects live under one team, a single vercel project rm command or API call can delete any of them. Vercel offers no backup recovery at all.

How to check:

vercel team ls

Count the number of teams and the projects per team. The risk surface is the number of projects accessible through the token in your .env.local.

What passes: production applications live in separate Vercel teams. The token used by your agent can only reach the project it is actively working on. Vercel's free tier allows multiple teams at no additional cost.

What fails: all projects under a single team with a single token in .env.local. This means the agent can delete any sibling project, and a mistake in one project's context could take down a completely unrelated product.

Step 5: Configure PreToolUse Hooks

A PreToolUse hook is a shell script that Claude Code runs before every tool invocation. If the script exits with code 2, the tool call is blocked and the agent sees a refusal message. This is the runtime layer that catches destructive commands even when your token scoping has a gap.

How to check whether you have one:

Look for a hooks key in your project's .claude/settings.json, then check the same key in your user-scope settings at ~/.claude/settings.json. The hook should exist in both locations. A hook that lives only in the project directory can be removed by the agent itself (by editing the project settings file). The user-scope copy is the backstop.

What the hook should block (minimum set):

  • Project and account deletion commands for Supabase, Vercel, and GitHub
  • Force pushes to main, master, or production branches
  • Repository visibility changes (gh repo edit --visibility public)
  • Recursive deletion of home or root directories
  • Database-level drops (DROP DATABASE, DROP SCHEMA public CASCADE)
  • Any shell command that modifies the hook script itself

What the hook should allow:

  • Table-level SQL (DROP TABLE, TRUNCATE) that is recoverable from a daily backup
  • Normal git operations: push, pull, commit, rebase
  • Deployments, migrations, builds, and tests
  • All non-destructive CLI commands for Supabase, Vercel, and GitHub

Robert Cowherd, founder of Armada Works, calls this the sustainability principle: block only what you cannot recover from, so the hook stays on permanently. A hook that blocks too aggressively gets disabled within a week, which is worse than no hook at all.

What passes: hooks configured in both project scope and user scope, blocking all unrecoverable operations listed above. Test the hook by running it against a few blocked patterns and confirming exit code 2.

What fails: no hook configured, or a hook only in the project directory without a user-scope mirror.

Ship-to-Prod Checklist

Run through this list after completing the five steps. Every item should pass before you hand an AI coding agent your codebase.

  • No account-level credentials in .env.local
  • Supabase PAT removed (service-role key only)
  • GitHub fine-grained PAT with Administration set to read-only
  • gh api admin permission check returns false
  • Vercel production projects in separate teams
  • PreToolUse hook configured in project scope (.claude/settings.json)
  • PreToolUse hook mirrored to user scope (~/.claude/settings.json)
  • Hook tested against blocked patterns (exits with code 2)
  • Hook tested against allowed patterns (exits with code 0)
  • Off-vendor database backup running (nightly pg_dump to R2, S3, or GCS)

If you are running a fleet of agents rather than a single coding assistant, the audit surface multiplies. Each agent should have its own scoped token rather than sharing a single credential. For a deeper look at fleet-level oversight, see how to monitor your AI agent fleet in production.

Frequently Asked Questions

How long does an AI agent permissions audit take?

About thirty minutes for a single-project setup. The slowest step is creating a GitHub fine-grained Personal Access Token, which requires navigating GitHub's token settings and selecting permissions per repository. If you run multiple projects, add about ten minutes per additional project for Vercel team separation and token rotation.

Do I need to run this audit every time I add a new environment variable?

Not the full audit. But any time you add a new credential to .env.local, classify it by blast radius before giving your agent access. If the new token is account-level, that is a red flag worth investigating. The full five-step audit is worth running quarterly or after any significant infrastructure change: new hosting provider, new database, new CI pipeline.

Does this audit apply to AI coding tools other than Claude Code?

The token-scoping steps (Steps 1 through 4) apply to every AI coding tool with shell access: Cursor, Windsurf, Cline, Aider, and others. The PreToolUse hook mechanism (Step 5) is specific to Claude Code. Other tools have their own extension points (Cursor rules, Windsurf configurations) that can implement similar runtime blocks using the same principle.

What if my Supabase service-role key is compromised?

The service-role key can read and write all data in one project, but it cannot delete the project or its backups. If compromised, rotate the key immediately in the Supabase dashboard under Settings, then API, then Service Role Key. Update .env.local and any deployment environment variables. The project and its data remain intact. The risk is unauthorized data access, not infrastructure destruction.

Should I use separate credentials for each agent in a fleet?

Yes. Each agent should hold only the credentials its job requires. A Content agent that writes markdown files does not need a Supabase service-role key. An Outbound agent that drafts emails does not need a Vercel deployment token. Scoping credentials per agent limits the blast radius if any single agent behaves unexpectedly.


If you want help running this audit against your specific stack, or scoping credentials for a multi-agent fleet, book a free 30-minute call. Armada Works includes this review in the scope of every Pilot engagement.

Written by
Robert Cowherd
Book a call