End-to-end encryption
E2E is per channel and opt-in: the human flips it on in the app, and a 32-byte key is
generated on their device — the server never sees it. The agent receives it as
PIDGE_SECRET (base64url), stored next to PIDGE_TOKEN: same storage, same precedence
(env var wins, else the same config file that holds the token — project-, agent- or
machine-scoped; pidge doctor prints the path in use). The secret deliberately does
not ride the setup prompt — chat logs sync to the agent provider’s cloud — so the app
hands the human a terminal one-liner that writes it straight into that config file.
Prefer generating the key yourself, outside Pidge’s code entirely?
Bring your own key.
The {TOKEN, SECRET} pair always travels from one source — mixing a token from one channel with
a secret from another is exactly the mixup the kf key fingerprint catches.
What seals, what stays clear
Section titled “What seals, what stays clear”Sealing is per-field and documented — the manifest’s encrypted_fields/clear_fields lists
are the source of truth. In short:
- Sealed on-device: titles, bodies (
body/body_markdown), subtitles, replies, the human’s messages to you, custom-action labels, tap-to-copy values, and deep links. The server stores onlyv1:AES-256-GCM envelopes for those fields. - Sealed once the media gate is open (
e2e_media_ready— see below): media bytes (photos and files, both directions) and filenames. While the gate is closed they ride clear — the manifest andwhoamistate which applies to your channel right now. - Clear by necessity: action ids, profile/type, urgency,
correlation_id, timestamps, delivery metadata (e.g. file sizes), Live Activity status, and the advisory provenance fields (sent_note, ack summaries, runtime labels — keep secrets out of notes) — the server needs them to route, schedule, attribute and degrade honestly. - Group channels are encrypted in transit, not end-to-end.
What changes in the CLI
Section titled “What changes in the CLI”- Sends seal themselves. With the secret set and the channel E2E, content fields leave your
machine as envelopes with
enc: "v1"+kfalongside. The201echo prints decrypted for display. - Attachments seal only when the media gate is open (
e2e_media_ready— the human’s devices can open sealed blobs). Until then an attachment rides clear even though the text is sealed — don’t attach something you wouldn’t send unsealed.PIDGE_E2E_MEDIA=on/offoverrides per machine, and a channel pinned sealed-media locally refuses to send clear bytes rather than downgrade. listen/wait/askdecrypt the human’s sealed messages and answers before printing. Rows withoutencare clear history and render as before.- Errors are precise, never garbage. A sealed row the CLI can’t open is blanked with an
e2e_errornaming the reason — sealed with another key (kf mismatch), unknown version, missing secret — raw base64 never reaches your terminal. - No secret ⇒ nothing breaks. Sends go clear and the app marks them “⚠️ not encrypted”;
pidge doctorvalidates the secret (32 bytes, shows the kf), warns on an orphan secret, and exits2when an E2E channel’s secret is invalid.
The sealing/opening code is in the open CLI repo (MIT), with committed test vectors any implementation can check against.
Honest scope
Section titled “Honest scope”This protects content against the server — infrastructure, database dumps, the operator.
It does not protect against a compromised agent machine: PIDGE_SECRET sits in your
env/config in the clear. The “ends” are the human’s device and your agent.
