Security
Chime carries messages from the AI agents on your machines to your phone, and routes your tap-replies back — including replies that approve or steer a process actually running on your computer. So the relay in the middle has to be unable to read those messages just as much as it has to be unable to forge or inject one. Both properties come from keeping every key off the relay (a zero-knowledge relay) and authenticating every message end-to-end.
The single reason Chime is end-to-end encrypted is so you can trust that the
managed relay at api.chime.sh — or anyone who ever compromises it — cannot
read or write your messages. It is not primarily about hiding traffic from
the network; it’s about making the relay’s operator a non-participant in
the conversation.
The encryption stack
Section titled “The encryption stack”Pairing runs a post-quantum handshake (PQXDH — X25519 Diffie-Hellman combined with an ML-KEM-1024 key encapsulation, so the session survives even a future quantum-capable adversary), then every message after that flows through a Double Ratchet — the same session-ratchet design used by Signal. Each message gets its own derived key, and that key is destroyed once the message is decrypted, so compromising one message’s key doesn’t expose the rest of the conversation (forward secrecy).
| Primitive | Algorithm | Reference |
|---|---|---|
| Diffie-Hellman | X25519 | RFC 7748 |
| Signatures | Ed25519 | RFC 8032 |
| Post-quantum KEM | ML-KEM-1024 | FIPS 203 |
| Key derivation | HKDF-SHA256 | RFC 5869 |
| Symmetric ratchet | HMAC-SHA256 | RFC 2104 |
| Message encryption | AES-256-GCM | NIST SP 800-38D |
The Go reference implementation uses Cloudflare CIRCL for ML-KEM-1024. The phone app doesn’t reimplement any of this in a second language — it runs the exact same Go crypto code, compiled to WebAssembly, so there is only ever one crypto stack to audit and keep in lockstep between machine and phone.
Every envelope is authenticated (AEAD), not just encrypted: a message the relay didn’t legitimately receive from an endpoint fails to decrypt. That’s what makes the relay unable to forge or inject a message, on top of being unable to read one.
What the relay can and cannot do
Section titled “What the relay can and cannot do”The relay is treated as untrusted — an honest-but-curious-to-actively-malicious store-and-forward queue. Here’s the honest accounting:
| Capability | Possible? | Why |
|---|---|---|
| Read message plaintext | No | Payloads are AES-256-GCM under Double-Ratchet message keys the relay never sees. |
| Forge or inject a message | No | Every envelope is authenticated with a key only the two endpoints derive; a relay-authored ciphertext fails to decrypt. |
| Tamper with a message in flight | No | Same authentication; the message header is bound into it. |
| Desync an endpoint by replaying a message | No | Decryption is atomic — a replayed or tampered envelope is a no-op, not a crash. |
| Drop, delay, reorder, or duplicate messages | Yes | An availability property, not a confidentiality one — the relay is at-least-once; endpoints dedupe and acknowledge. |
| See metadata — routing IDs, message sizes, timing, online/offline status | Yes | Chime does not currently hide traffic-analysis metadata. Routing IDs are opaque (derived from a public key hash) but stable. This is a known, accepted limitation, not a claim we hide. |
The takeaway: a fully compromised relay can stop messages from flowing, but it cannot read a single payload and cannot put words into your agent’s mouth or your reply.
Pairing: the codename MITM check
Section titled “Pairing: the codename MITM check”Pairing is the one moment a human has to actually verify something. When you
run chime pair, both your machine and your phone derive the same
codename — a short, human-readable string like wry-otter-42 — from a
hash of the pairing party’s public identity key. You compare the codename
printed in your terminal against the one shown on your phone before tapping
Accept.
That comparison is the man-in-the-middle check: the relay brokers the pairing exchange, but it cannot substitute its own keys for either side’s without changing the codename on at least one of them. The prekey bundle each side publishes is itself signed by that same identity key, so a relay can’t quietly swap in a different key mid-handshake either — the signature check fails first. See Quickstart for what this looks like in practice.
Where keys live
Section titled “Where keys live”Keeping keys off the relay only matters if the endpoints hold them safely.
On your machine, the daemon (chimed) stores its identity keys
(Ed25519 + X25519 + ML-KEM private keys) at ~/.chime/identity and its
ratchet state + relay token at ~/.chime/state.json, both written with
file mode 0600. The real local trust boundary is the 0600 Unix socket at
~/.chime/sock: only processes running as your Unix user can talk to
chimed at all.
On your phone, the session (identity keys, ratchet state, relay token)
is stored through hardware-backed secure storage rather than plain app
preferences: on Android it’s encrypted with an Android Keystore-backed key
(TEE/StrongBox-backed where the device supports it); on iOS it’s a Keychain
item set to whenUnlockedThisDeviceOnly with iCloud Keychain sync disabled,
so it’s readable only while the device is unlocked and never leaves the
device via backup. The app also disables Android’s cloud backup for app data
entirely.
No inbound port — the architecture that matters
Section titled “No inbound port — the architecture that matters”Your machine never listens for inbound connections on Chime’s behalf.
chimed dials out to the relay over an outbound connection and holds it
open; nothing on your machine binds a port that the internet can reach. The
only thing reachable at all is that 0600 Unix socket — local-user only, not
network-exposed.
This is a meaningfully different shape from an exposed-gateway pattern, where a bridge process listens for inbound webhook/API traffic and has to be reachable from the outside world to work at all. That pattern has had a bad few months industry-wide: security researchers reported 900+ publicly exposed OpenClaw gateway instances initially, with later estimates in the 30,000–40,000 range, and CVE-2026-32025 (“ClawJacked”, CVSS 8.8) was reported against exposed OpenClaw deployments, per Bitsight and The Hacker News reporting. That isn’t a knock on OpenClaw’s code specifically — it’s the structural risk of any always-listening gateway you have to expose to reach it remotely: exposure is now part of the attack surface, on top of whatever the code itself gets right. Chime’s daemon was designed to not need that shape at all — the relay connection is outbound-only, so there is no gateway to accidentally leave open.
Read the primary sources
Section titled “Read the primary sources”This page is a user-facing summary. The authoritative specs, kept in the repo and updated as the protocol evolves:
docs/PROTOCOL.md— the wire format and every crypto derivation, byte for byte.docs/15-threat-model.md— the full threat model, trust boundaries, and reliability-vs-forward-secrecy tradeoffs.
- Self-host — run your own zero-knowledge relay instead of the managed one.
- Quickstart — pairing, end to end.