Grants and approvals
Every release of a secret needs either a fresh approval or a grant: a time-limited pre-approval that you created by approving. This page covers both.
The approval dialog
Section titled “The approval dialog”When a request is inside the policy and no grant matches, grantpin asks you. The dialog has three buttons:
| Button | What happens |
|---|---|
| Deny | Nothing runs. This is the default button. Esc, closing the dialog, or 120 seconds of silence also mean Deny. |
| Allow once | This one run is released. Nothing is stored. |
| Allow for N | This run is released and a grant is stored, so the same kind of request runs without a prompt for the next N. |
After an Allow button, the macOS authentication sheet (Touch ID, or your account password) repeats the request. Only the button and a successful authentication together release the secret. Cancelling the sheet is a Deny.
N is the policy’s max_grant_ttl, but never more than 24 hours. With no policy it is 1 hour. You cannot pick a different value in the dialog.
The “covers:” line
Section titled “The “covers:” line”The dialog ends with a line like this:
Allow for 1h covers: printenv … in /Users/you/projects/shop and subfolders.It tells you exactly what the third button would permit: the command prefix (the longest matching prefix from the policy, or the whole command when there is no policy), the folder and everything beneath it, for that long, for the same caller and the same secrets. Read the command and this line. They are the control.
What a grant matches
Section titled “What a grant matches”A later request is released without a prompt only if all of these hold:
- the caller name is the same. It is normalised (lowercase, runs of other characters folded to one
-), soClaude Code,claude_codeandclaude-codeare the same caller. The name is supplied by the client and is not authenticated; - the working directory is the grant’s directory or inside it;
- every requested secret is covered. A literal name is covered by an equal entry or a matching glob. A requested glob is covered only by an identical glob entry. The grant stores the secrets as requested, so a glob grant also covers a matching secret you add later;
- the command starts with the grant’s command prefix, argument for argument;
- the grant has not expired and, if it has a use limit, has uses left;
- the stored grant passes its integrity check.
Standing grants
Section titled “Standing grants”A standing grant is one that you create up front for a longer time (up to 90 days), instead of answering a prompt later.
grantpin grants add --caller claude-code --secrets DB_URL,aws/* --ttl 30d -- php artisangrantpin grants add --caller claude-code --secrets API_KEY --ttl 12h --cwd ~/projects/shop --uses 20 -- ./deploy.sh--calleris required. It says who the grant is for.--secretstakes names and globs.- Everything after
--is the command prefix:php artisanalso coversphp artisan migrate, but notphp other. --cwddefaults to the current directory and covers its subfolders.--ttlis required and may be at most 90 days, or less if the policy setsmax_standing_grant_ttl. A longer value is refused, not shortened.--uses Nends the grant after N releases.
You approve it once, in a different dialog:
Create a standing grant?
claude-code may run `php artisan …` with DB_URL, aws/* (aws/access_key) in /Users/you/projects/shop and subfolders for the next 30 days, without further prompts.
Only continue if you just asked for this yourself.
[ Cancel ] [ Create standing grant ]Cancel is the default. Touch ID follows. Nothing is stored until both succeed.
Listing and revoking
Section titled “Listing and revoking”grantpin grantsgrantpin grants listBoth list active grants. The STANDING column says standing for grants made with grants add. EXPIRES is the time left. A row that fails its integrity check is shown as TAMPERED and never matches; revoke it.
grantpin grants revoke IDgrantpin grants revoke --allNeither listing nor revoking needs approval. A revoke is written to the audit log.
Presence tiers
Section titled “Presence tiers”Every secret has a minimum protection level, min_tier: none, dialog or hardware. A run is refused (tier_unmet, exit 78) if either the key store or the approver provides less than the strictest min_tier among the requested secrets. Tiers are never downgraded.
| Backend (macOS) | Tier |
|---|---|
| Secure Enclave key | hardware |
| Keychain fallback key | dialog |
| Touch ID approver (password fallback) | hardware |
New secrets get the key store’s tier by default. You can set one explicitly:
grantpin set API_KEY --min-tier hardwaregrantpin update API_KEY --min-tier dialognone means the vault default. Lowering a secret’s tier with update asks you first (a dialog, then Touch ID). Raising it does not.