Skip to content

Mobile security and encryption

The link between your phone and your desktop is end-to-end encrypted. The relay that carries it is a byte forwarder that cannot decrypt anything. This page explains how that holds up, and is equally clear about what it does not cover.

Your desktop usually sits behind a router that will not accept inbound connections, and a phone moves between networks constantly. So neither device listens for the other. Both dial outward to a relay, which pairs the two connections and forwards bytes between them.

The relay is a rendezvous point, not a participant. The encryption is negotiated directly between your phone and your desktop, inside the tunnel the relay forwards.

StageProtocol
PairingNoise IKpsk0 over Curve25519, ChaCha20-Poly1305, BLAKE2s, using the single-use pairing token as a pre-shared key
Ongoing sessionsNoise KK over Curve25519, with the desktop as initiator
Message framingA secretstream construction that detects truncation, reordering, and replay
Push payloadsXChaCha20-Poly1305, sealed to your device’s key

Session keys are re-negotiated roughly every two minutes, following WireGuard’s rekey interval. That bounds how much traffic a single compromised key could ever expose, and it means recovering from a key compromise does not require re-pairing.

The desktop keeps sealing frames under the old keys until the phone’s half of a new handshake reaches it, so the phone keeps the receive keys a rekey replaced, briefly. Each lives at most 180 seconds after it was superseded, at most eight are kept, and every older one is dropped the moment a frame opens under a newer key. Each keeps its own strictly sequential counter, so replay protection is unchanged, and the phone never sends under an old key. That window has two costs, and both end with it. A relay can hold back a desktop-to-phone frame sealed under an old key and still have it open up to 180 seconds later, which is a delay, not a forgery or a replay. And a phone compromised inside the window also yields those retired keys, which open only desktop-to-phone frames not yet read under them.

Pairing ends with a six-digit code derived from the handshake transcript, shown on both devices. Comparing it is what makes a machine-in-the-middle on the relay detectable: an attacker who intercepted the exchange would produce different digits on each side. See Pairing a phone.

  • On the phone, pairing keys and trust anchors live in hardware-backed secure storage: the Keychain on iOS, the Android Keystore on Android.
  • On the desktop, the device identity is encrypted with your operating system’s secure storage. If that is unavailable, Kangentic refuses to persist the keys rather than writing them unprotected, which is why pairing is disabled on a system without it.

Content is encrypted, but carrying it still involves infrastructure that observes something. Whoever operates the relay you use, including Kangentic when you use the hosted one, can still see the IP addresses of both peers, the timing of every connect and reconnect, frame sizes and frequency, and which two connections were paired together.

The hosted relay states that disclosure in full for the instance Kangentic runs, including the two parts specific to how it is deployed. This is inherent to operating any relay rather than a property of this one.

The practical mitigations are: run your own relay, which removes Kangentic from the picture entirely; single-use pairing tokens; and the relay’s own per-IP and per-slot rate limits and connection caps, so only paired devices can consume relay capacity at all.

The guarantee is structural rather than a promise. The relay routes purely on an opaque slot identifier in the connection URL and forwards every binary message byte for byte, and its source has no runtime dependency on the end-to-end crypto package at all, which a test enforces. The blind-relay guarantee covers the mechanism, and Relay security model states each claim with the test that pins it.

A paired phone is granted every capability the protocol defines (thirteen as of v0.46.0), and shell access, file reads, and arbitrary command execution are not among them because those verbs do not exist in the protocol at all. This matters more than a permission toggle would: there is no code path to abuse, so a compromised phone or a flaw in the pairing flow still cannot turn into command execution on your desktop.

The command-terminal verb added in v0.46.0 does not change that. A start names only the project, and the desktop runs that project’s own agent CLI at its root, the way its New terminal button does, dropping any branch, model, or effort the request carries. Stop ends only a Command Terminal and refuses a task’s session. What keeps it from being shell access is that a Command Terminal ends the moment its agent exits, so interactive-terminal never has a bare shell to type into. The one moment left, between the spawn and the agent starting, the phone closes on its side by holding every keystroke and message to a terminal until the desktop has seen the agent start.

Use GitHub’s private vulnerability reporting on the relevant repository, or email security@kangentic.com.

Kangentic is free and open source. A star helps other people find it.

Star on GitHub