Skip to content

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.

Chime architecture: laptop, relay, phone Your laptop's chimed daemon dials out to the relay over an encrypted connection; the relay only ever sees ciphertext and forwards it on to your phone. Private keys stay on the laptop and the phone — they never touch the relay. Your machine chimed holds machine keys Relay untrusted ciphertext only no keys, ever Your phone holds phone keys dials out push / WS

Keys are generated on, and never leave, the endpoints — the relay only ever moves ciphertext. Your machine has no inbound port to expose: it dials out to the relay, never the other way around.

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.

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 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.

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.

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.