A hook for Claude Code

Your agent is waiting. You're not at the desk.

Your agent's permission prompt goes to your phone. You tap allow or deny, and it keeps working.

npx tapproval setup
tapproval - Never let your agent wait on you again | Product Hunt
~/src/checkout — claude
> fix the failing webhook test
 
● Bash — npm test -- webhook
⏵ permission required
waiting for phone
✓ allowed from phone
 
● 14 passed in 3.2s
tapproval

Approve Bash?

npm test -- webhook

AllowDeny
01 · How it works

One command. Your phone answers for you.

From install to a prompt on your lock screen in about a minute.

01 — Install

Run setup

npx tapproval setup registers a device, installs the hook and prints a QR to scan. Once per machine — then restart your agent.

02 — Intercept

Your agent asks

Instead of blocking on a terminal nobody is watching, the permission prompt is encrypted and pushed to your phone.

03 — Decide

You tap

Allow or deny, straight from the lock screen. The decision goes back to the hook still holding your agent open, and it carries on.

02 · The handoff

Not a dashboard. A round trip.

The hook is just a subprocess: it reads the tool call on stdin and writes your decision back on stdout. One hop out, one hop back — and the payload is sealed before either of them.

Your machine hook · encrypt
Sealed prompt out
Decision back
Your phone decrypt · tap
AES-256-GCM First tap wins Grace period Per-tool filters
03 · Private by design

The server is a courier, not a reader.

Your command is encrypted on your machine before it is sent, so what the server stores is an opaque blob. Prefer not to trust that? Run the whole thing yourself with setup --self-hosted.

A / What it can see

Metadata, and not much of it

  • Your deviceId — a random uuid
  • The tool name: Bash, Write, Edit
  • Timestamps, and whether you allowed or denied
  • Your phone's user-agent string
B / What it cannot

Everything that matters

  • The command, the diff, or file contents
  • The working directory
  • Your note on a denial
04 · The whole surface area

A handful of commands, no dashboard.

tapproval setupregister, install the hook, print a QR
tapproval pair --iosiPhone: install the app first, then type a code
tapproval doctordiagnose config, hooks, reachability, revoked devices
tapproval mutestop notifications; unmute resumes them
tapproval notify Bash Editbuzz for a shortlist of tools only
tapproval notify --grace=60hold the push a minute, in case you answer at the keyboard
tapproval uninstallremove the hook
05 · See it in action

Setup, pairing, and a real approval.

06 · Connect your phone

Two minutes on your phone, then never again.

07 · Questions, mostly about iPhones

The things people ask before they trust it.

iOS Why is the iPhone setup different?

iOS only allows web push from an app installed to the Home Screen, and a pair code is single-use. Scan the QR in Safari and you pair Safari — not the installed app — and burn the code doing it.

So the order matters. tapproval pair --ios walks you through it:

  1. Open the app URL in Safari (not Chrome — only Safari can install).
  2. Share → Add to Home Screen.
  3. Launch it from the Home Screen icon, then type the code the CLI printed.
  4. Tap Enable notifications and allow the iOS prompt.
iOS My iPhone notifications stopped, but they used to work

This is the common one, and the fix is the same every time: pair the phone again, once.

tapproval doctor      # confirm the Mac side is healthy
tapproval pair --code # then type the code in the installed app

iOS evicts a PWA's push subscription on its own — after long periods unused, when storage is reclaimed, on some iOS updates, or if the Home Screen icon was removed and re-added. Nothing on your machine changed and doctor stays green, because the subscription died silently on Apple's side. Re-pairing mints a fresh one.

If it still doesn't buzz: check iOS Settings → Notifications → tapproval is allowed, and that Focus / Do Not Disturb isn't filtering it. As a last resort, delete the Home Screen icon, re-install from Safari, and pair again.

iOS Why are there no Approve / Deny buttons on my lock screen?

WebKit ignores notification action buttons for web push — unchanged since iOS 16.4. On Android and desktop Chrome you answer with one tap from the lock screen; on iPhone you tap the notification, the app opens, and you answer there. It's one extra tap, and the app recovers the pending request even though iOS drops the notification's target URL.

iOS The notification says "Tap to review" instead of the command

That phone can't decrypt the payload — it either never got the key or lost it (cleared site data, or it was paired before payload encryption existed). Re-pair and it comes back. The request is still answerable meanwhile; only the preview is missing.

If it instead says "Cannot read this request", the phone holds the wrong key — usually because setup was re-run and generated a new one. Same fix: tapproval pair.

Nothing happens when my agent asks for permission

The hook isn't registered, or the session hasn't reloaded it. A session started before install rewrites its settings file from a startup snapshot and deletes the hook — so restart your agent after setup.

tapproval doctor names the exact failure: config, hook registration, timeout ordering, API reachability, Realtime, revoked devices.

It worked for weeks, then quietly stopped

Usually a hook path that no longer exists — the global package was removed without uninstall, or an older version pointed the hook into an npx cache npm has since pruned. npx tapproval setup fixes both: it copies the runtime to ~/.tapproval/runtime/ and repoints the hook there.

The hook fails safe while broken — every prompt falls back to the terminal and nothing is ever auto-approved, which is also why it's easy to miss.

The phone never buzzes, but doctor is all green

Check the grace period. With graceSec above 0, a prompt you answer in the terminal within that window is designed never to reach your phone — no row, no push, nothing to dismiss. tapproval notify --grace=0 to buzz immediately.

Can the server read my commands?

No. summary, detail and cwd are encrypted on your machine with AES-256-GCM before anything is sent. The key is generated by the CLI, handed to each phone once inside the single-use pair code, and deleted from the server the moment that code is claimed. What lands in Postgres is one opaque blob; the push itself carries only a generic "Approve Bash?".

Can I pair more than one phone?

Yes. tapproval pair adds another device and asks which platform; --ios, --android and --code skip the question. Every paired phone gets the same request, and the first answer wins.

What happens if I never answer?

The hook waits timeoutSec (300 by default) and then gets out of the way — the prompt goes back to the terminal exactly as it would without tapproval. Silence is never an approval.

Does it work with agents other than Claude Code?

Today it hooks Claude Code's permission prompts. Codex support is an open issue and a small adapter away — the phone side is agent-agnostic.

Stop babysitting the terminal.

No account, no tunnel, no server to babysit. Works today with Claude Code — Codex support is an open issue and a small adapter away.