Authz

How it works.

Two verbs.

ask waits for the person's answer. notify tells them something and never waits. Few verbs on purpose: the fewer an agent chooses between, the better it calls them.

✻ Welcome to Claude Code!
/help for help, /status for your current setup cwd: ~/Developer/api
ship build 412
⏺Bash(authz ask "Deploy the API" --verified -- ./deploy --tag 412)
⎿✓ Allowed · Touch ID on mbp-m5-max
deployed api:412 to eu-west-1
⏺Build 412 is live in eu-west-1. You approved it with Touch ID.
✻ Cooked for 14s
> ▌
? for shortcuts
authz ask waits for the answer.
>_ OpenAI Codex
model: gpt-5-codex directory: ~/Developer/api
› run the migration and tell me when it ends
•Ran authz notify "Migration" --begin --id mig
└ • Migration · mig, begun
•Ran ./migrate
└ 1,204 rows moved
•Ran authz notify "Migration done" --end --id mig
└ • Migration done · mig, ended
─ Worked for 1m 32s
› Ask Codex to do anything
⏎ send ⌃J newline ⌃T transcript ⌃C quit
authz notify tells and moves on.

The exit status is the decision

0
allowed or answered
77
denied
75
nobody answered in time, or withdrawn
79
“no, do this instead”, with the reply
65 · 64
an invalid request · a wrong call

A command Authz ran exits with its own status. The decisions use codes a program rarely returns, so a command never reads as a decision.

A notice that runs

Once (“CI failed”), or --begin, --update and --end on one --id, updating in place on every Mac and the iPhone. It may carry links, never answers.

One document.

Every request is one JSON document, written the same way by agents and programs: what to read, what the person can say, and what their yes lets happen. JSON, not HTML, so text quoted from a page or a file can never become structure on the person's card. Chosen by measurement: 588 agent runs, 14 tasks, three models.

Content
A paragraph in Markdown, or a link. Never an answer.
Fields
text, password, email, url, number, date, checkbox, textarea, select. HTML's attributes, so every model already knows them.
Pages
A form over several pages, shown in order.
Secrets
A 1Password item the request uses. Its value never reaches the caller: it fills the command's environment, never its command line.
Payloads
What the yes lets happen: a command Authz runs, a patch it applies, a step the caller executes, or a payment. {tag} is filled from the form, so the person sees what will run while they type.

Where it asks.

A request presents where the person last touched input, and the phone rings only when it must.

Nobody at a computer
the iPhone rings at once
At a Mac
that Mac asks; the iPhone holds it quietly
Not visibly there
the iPhone rings after 2.5 s
There, not answering
the iPhone rings after 45 s
The Mac's card
takes the keyboard only after 0.8 s without a keystroke, so typing never answers it
The iPhone
a card in the app, a notification with Allow and Deny, one Live Activity per Mac
Voice and a nod
questions that need only intent; never a command, a grant or a secret

What a yes is bound to.

An approval means something only if what runs is, byte for byte, what was shown, and the record of it can be checked afterwards.

A finger, not a click

A yes that matters needs Touch ID or Face ID. It carries a signature by a Secure Enclave key whose use needs the finger or the face, so the app cannot claim a presence the hardware did not see.

Bound to the bytes

A command is pinned whole: its arguments, where it runs, the file that runs and every file its arguments name, by SHA-256. Authz runs it itself, checked again just before; any change refuses. A payment binds its amount and who is paid. This binds the yes, not the agent: what an agent runs without asking was never approved.

Secrets stay in a helper

1Password values and wallet keys live in a process of their own. It checks the answer itself and releases a value, or one signature, only for what was approved. A key never leaves it.

A receipt for every yes

The request's digest, who answered on which device and how, then what ran and how it ended. The asker checks the receipt's signatures before it acts.

The answer, as the asker reads it on stdout

Payments.

Over x402 a resource answers HTTP 402 with its price. Authz asks “Pay 0.005 USDC”, one Touch ID releases the wallet's signature for exactly that transfer, and the resource is fetched.

bash
authz ask --pay https://authz.space/toll/data --wallet <ref>
The toll, step by step →

What is not done.

Authz runs on one person's Macs and iPhone.

Release
undecided
Download, sign-up, pricing
none
Security review
first read 2026-10-17 · open findings before any release (warning)
Payments
Base Sepolia, test money only