Blog

Pairing a phone with no account: a QR code, a pinned key, a relay that reads nothing

Grenade has no accounts because the Mac's screen is the trusted channel: a QR code carries the Mac's public key to the phone, and every connection after that is sealed to it.

Grenade lets a phone type into terminals on your Mac. That is remote code execution by design, so the first question is how the Mac knows the phone, and how the phone knows the Mac. The usual answer is an account and a server that vouches for both. Grenade has neither. The Mac’s screen does the vouching.

The Mac has a key

The first time the daemon runs, it makes a long-term X25519 key pair and keeps it in ~/.grenade, readable only by you. Everything else rests on one fact: only this Mac can use the private half.

The QR code carries the trust

grenade pair shows a QR code. It holds what a phone needs to reach this Mac and to know it is the right one: the Mac’s public key, a one-time secret of 128 random bits, and where to find the Mac (its addresses on the local network, and its relay if it has one).

The code goes from the Mac’s screen to the phone’s camera, so nothing on a network can change it on the way. The phone pins the key it scanned: from then on it talks only to whoever can prove they hold the other half. The secret proves to the Mac that this phone saw its screen. It lasts two minutes and works once.

That is the whole enrollment. No email, no password, and no server that knows who you are.

Every connection starts with a handshake

On each connection, the phone and the Mac each make a fresh, short-lived X25519 key and exchange the public halves. Both then derive the same secret from two Diffie-Hellman results: the phone’s fresh key with the Mac’s long-term key, and the phone’s fresh key with the Mac’s fresh key. HKDF-SHA256 turns that into two keys, one for each direction.

Two things follow:

  • Only the real Mac can finish it. The first result needs the private half of the pinned key. A relay, or another machine on the Wi‑Fi, cannot compute it.
  • A recording cannot be replayed. The Mac’s fresh key is new each time, so yesterday’s bytes mean nothing today.

After the handshake, every frame is sealed with ChaCha20-Poly1305 under a counter that is never sent. A frame that fails to open, or arrives out of order, closes the connection.

The phone’s first sealed frame is either “pair me”, with the secret from the QR code, or “hello”, with the token it got when it paired. The token never travels outside the sealed channel.

When you cannot scan

Sometimes the camera is not an option, so grenade pair also shows a code to type. Six digits would be easy to send to the wrong machine: on a local network, the key a Mac announces is only a claim.

So the typed code is ten digits. The last four are a check that ties the first six to the Mac’s key. The phone computes them from the six digits and the key announced by the Mac in front of it, before it sends anything. If they differ, it stops: the code was mistyped, or something is posing as the Mac. An impostor’s key passes one time in ten thousand, and every failure shows on the phone.

A typed code pairs on the local network only. Six digits are too few for the open internet.

The relay forwards bytes

Away from your Wi‑Fi, the phone and the Mac meet at a relay. The Mac dials out to it, the phone dials in, and the relay joins the two. What it carries is the same handshake and the same sealed frames, so it holds nothing that opens them.

To be let through, the phone shows the relay an access key derived from its token. The relay knows only a hash of that key, which the Mac uploaded. It never sees the token.

What a relay does see: each Mac’s name and addresses, when it is online, each phone’s address, and how much passes and when. You can run your own.

The same on your Wi‑Fi

It would be simpler to trust the local network. Grenade does not: on Wi‑Fi the phone talks to the Mac directly, with the same handshake and the same sealed frames. Someone else on the network sees that the two are talking, and nothing else.

Ending it

A pairing is a token on the Mac. grenade unpair <phone> deletes it, closes that phone’s connections at once and takes it off the relay’s list. A phone that has not been seen for 90 days is unpaired by itself.

What this does not do

It does not protect you from someone with your Mac user account, who can read the key. It does not hide sizes and timing. And a paired, unlocked phone with the app open is a shell on your Mac, which is why the app has a lock of its own.

The full list, for someone on your Wi‑Fi, the relay’s operator and someone holding your phone: Security.

Try it on your own agents.

Two commands on the Mac, one QR code on the phone. No account.