Security: what Tapproval can see, and what was fixed
Tapproval answers permission prompts, so a bug in it is a bug in somebody's permission boundary. This page says what it can see, what it promises, what it does not protect against, and every security fix so far. The computer half is open source, so each claim here can be checked in the code.
What Tapproval can see, and where it goes
A small helper on your computer, Tapproval Base, sees what Claude Code asks permission for: the command, or the file and what would be written to it. For the session list it also reads Claude Code's transcripts on that computer. It sends a short card to your watch and prints your answer back to Claude Code. There is no server of ours, no account and no telemetry. Cards take one of three routes:
- Your own network. Watch and computer talk directly.
- Your own iCloud, if you install the Mac bridge. Cards pass through CloudKit's private database, which belongs to your Apple ID. We cannot read it.
- An encrypted tunnel, if you turn on the travel connection. Traffic passes through Cloudflare and is not stored there. The address is secret, but it is only an address: every request must still prove a device key.
The privacy policy has the same routes, with what each part stores.
The rules it will not trade away
- It approves nothing on its own by default. Allowing the harmless tiers automatically is something you switch on. A CRITICAL call is never auto-allowed, whatever the setting.
- It fails closed. Anything it cannot parse, reach or judge ends with the ordinary prompt in your terminal, exactly as if Tapproval were not installed.
- Only a process on your own computer is let in without a key, and even that process can post a card but never answer one, so a command Claude runs cannot approve its own prompt. Being on the same Wi-Fi counts for nothing: on a café network, that is a room full of strangers.
- A current watch never sends its key. Every App Store version from 1.4 signs each request. Each signature can be used once. The helper proves itself in its answer, and the watch keeps an address only from an answer carrying that proof.
- Standard-library Python only. The helper installs no third-party packages, so there is no dependency tree to trust.
What it does not protect against
- A compromised computer. A process running as you can talk to the helper. Letting local processes in is what lets Claude Code hand it a prompt in the first place.
- Your Apple ID, on the iCloud route. With the Mac bridge, the watch learns its first key from your private iCloud. Anyone who controls your Apple ID could answer a prompt, so protect it with two-factor authentication.
- What the audit log holds. The helper logs command text on your computer, with credential-shaped fragments removed. It never leaves the machine, but it is there.
Fixes, disclosed
Newest first. Each entry says what was wrong, when, who could have used it, and the fix. Starting Claude Code once updates the helper.
1.1.45, 12 October 2026: a test copy of the app could answer real prompts
What was wrong. A copy of the watch app running in Apple's Simulator on a Mac, the way the app is tested, could be paired like a real watch and then answer the prompts waiting on that computer. A tap meant for a test card once allowed a real one when the list moved under it.
Since when. From the first public release until 1.1.45.
Who could have used it. Nobody from outside. It needs a paired copy of the app on your own Mac, so the risk was a mistaken tap during testing, not an attack.
The fix. A Simulator copy marks every request, and the helper refuses its answers, its messages to a session and its new sessions unless it was started for testing. Over iCloud, a Simulator copy writes no answers at all. A real watch never sends the mark and is trusted by its key, as before.
1.1.38, 5 October 2026: a program on the computer could pair by using its Wi-Fi address
What was wrong. The relay refuses to give a watch key to a program on the computer itself, because such a key would let a command Claude runs answer its own prompt. It recognised such a program only by the address 127.0.0.1. A program that asked through the computer's own Wi-Fi address instead looked like a watch. --pair, which opens the door without asking, could be run by any program too. So a command running on the computer could open the door and take a key, then answer its own CRITICAL prompt. Separately, any program on the computer, including one run by another user account, could unpair the watch or replace its key.
Since when. From the first public release until 1.1.38.
Who could have used it. Only something already running on your computer, such as a command Claude was talked into running by a web page or file it read. Nobody on the network could use it.
The fix. A program on the computer is now recognised whichever of the computer's addresses it uses, and it is never given a key. Unpairing, replacing the key and the other administration need the key file too, which only your own user account can read. --pair reads that file and signs with it, so the key itself never travels. Without the file, opening pairing still works, but the computer asks "Allow a watch to pair?" first. A new session started from the watch can no longer begin with ! (a shell command) or # (a memory note). Tests show each of these fails without its fix.
1.1.36, 3 October 2026: a computer that had never paired offered a key to its Wi-Fi
What was wrong. So that a first watch could pair with no typing, a computer with no watch paired yet opened its network pairing door by itself for half an hour, again at every Claude Code session start. Anyone on the same network who asked during that time was given a key, and with it could read sessions and answer prompts, CRITICAL ones included. Until 25 September that key was the computer's main key; from then on it was a device key that could be seen and revoked, but still a key.
Since when. From the first public release until 1.1.36. Since 25 September, never on a Mac with the iCloud bridge, which pairs through your Apple ID instead. Never through the travel address.
Who could have used it. Someone on the same local network, such as a café or office Wi-Fi, as a computer that had the helper installed and no watch paired yet. Once a watch had paired, the door stayed shut unless you opened it.
The fix. When a watch asks, the computer now shows "Allow a watch to pair?" with the asking address, and gives a key only after you click Allow. Don't Allow, or no answer within two minutes, shuts the door, and only one question is shown at a time, so a stranger cannot keep putting it on your screen. --pair, which you run yourself, asks nothing more. Tests check that a refusal gives no key and that a late answer cannot shut a door you opened since. Release notes.
1.1.27, 27 September 2026: a request could borrow the key of the one before it
What was wrong. Several requests can share one network connection. The helper kept what it had learned about a request, including whether its key had been proved, on the connection instead of on the request. A request without a key, sent on a connection just after a request that did prove a key, was treated as proved.
Since when. From 1.1.21 (25 September 2026), when signed requests arrived, until 1.1.27.
Who could have used it. Only someone who knew your secret travel address. Cloudflare's tunnel shares connections between callers, so such a person's request could land on a connection your watch had just used, and read sessions or answer a prompt. Without the travel connection turned on, there was no way in.
The fix. Every request now starts from nothing and proves its own key. A test sends a keyless request after a signed one on the same connection and expects a refusal. Release notes.
1.1.21, 25 September 2026: the key stopped crossing the network
What was wrong. Before this, a watch proved itself by sending its key with each request, and it trusted any helper that answered on the right address.
Who could have used it. Someone able to read the traffic, or to answer in the helper's place on your network, could have learned the key.
The fix. Requests are signed and good once, and the helper proves itself in every answer. A Mac with the iCloud bridge never offers a key on the network. The network pairing door hands out a revocable device key rather than the main one. Watches from before signing (the App Store's 1.3 and earlier) still send the key, so the helper still accepts it from them. Every App Store version since 1.4 (2 October 2026) signs; accepting a sent key ends once watches on 1.3 have had time to update. Release notes.
Before the first public release, August–September 2026
- The Wi-Fi was a credential. Anyone on the same network could read transcripts, answer a prompt, or type into a live session. Every caller except a local process now presents a device key.
- The travel address was the key. The tunnel's secret path and the device key became separate secrets, so seeing the address grants nothing.
- Pairing was reachable through the tunnel. On that route the caller looked local. The pairing door is now closed there.
- A local command could answer its own prompt. Local processes may still post cards, but answering one now needs a device key.
Checking a download
From 1.1.27 on, each release carries an archive of its source and a signed statement from GitHub that the release workflow built that archive from the tagged commit. gh attestation verify tapproval-helper-v1.1.27.tar.gz -R marcobelini/tapproval-helper checks it. It proves where a release came from, not that the code in it is safe; for that, the code is there to read.
Reporting a problem
Open a private security advisory, or write to tapproval@thoughtfulsteward.org. You will hear back within a few days. There is no bounty, only credit if you want it, and a fix. What counts, and what is accepted, is in SECURITY.md.