Skip to content

Bring your own key

By default, turning on end-to-end encryption mints the 32-byte channel key on your iPhone or Mac. That’s the low-friction path — but it means the app generated the key for you. If you’d rather not take our word for the generator, bring your own key: create it yourself on the machine where your agent runs, and the Pidge app only ever receives it.

What this buys you:

  • The key is born outside Pidge’s code. No claim about our random-number generator to trust — you ran the generator.
  • The secret never transits anything. With a minted key, the app hands PIDGE_SECRET to the agent’s machine via an AirDropped terminal command. With your own key, the secret is already on that machine the moment it exists — the only thing that travels is the key you paste into the app, from your terminal to your Keychain.
  • You can verify, end to end. You know the key, so you can check every link in the chain (below).

Run this on the machine where your agent runs, as the user the agent runs as (and with the same PIDGE_AGENT set, if you use one). It needs openssl, which macOS and virtually every Linux ship with:

Terminal window
sh -c 'umask 077; k=$(openssl rand -base64 32 | tr "+/" "-_" | tr -d "="); [ ${#k} -eq 43 ] || { echo "key generation FAILED — is openssl installed?" >&2; exit 1; }; a=$(printf %s "$PIDGE_AGENT" | sed "s/^[[:space:]]*//;s/[[:space:]]*$//" | tr -c "A-Za-z0-9_.-" "_" | cut -c1-64); d="${XDG_CONFIG_HOME:-$HOME/.config}/pidge${a:+/agents/$a}"; mkdir -p "$d"; chmod 700 "$d"; f="$d/env"; touch "$f"; chmod 600 "$f"; grep -v "^PIDGE_SECRET=" "$f" > "$f.tmp"; printf "PIDGE_SECRET=%s\n" "$k" >> "$f.tmp"; chmod 600 "$f.tmp"; mv "$f.tmp" "$f"; echo "PIDGE_SECRET saved to $f"; echo; echo "Your key — paste it into the Pidge app:"; echo "$k"'

One command, three effects:

  1. Generates 32 random bytes with your machine’s CSPRNG (openssl rand) and encodes them as 43 base64url characters — the exact wire form Pidge uses everywhere.
  2. Installs the key as PIDGE_SECRET in the CLI’s config file (~/.config/pidge[/agents/<id>]/env, permissions 600), exactly where setup --claim keeps the channel token. It’s idempotent: re-running replaces the old PIDGE_SECRET line and touches nothing else. If your setup stored the token in a project scope (pidge-cli ≥ 0.43, inside a git repo), run npx pidge-cli doctor afterwards — it prints the config path in use and validates the secret; if it doesn’t see PIDGE_SECRET, move that one line into the file doctor names.
  3. Prints the key, once, for the one manual step left: entering it in the app.

In the Pidge app: channel → End-to-end encryption → turn the toggle on → Use My Own Key… → paste the printed key. The app validates it (43 base64url characters, exactly 32 bytes — the same string must work verbatim as PIDGE_SECRET) and shows its fingerprint.

The option only appears while the channel has no key yet. A channel that already has one (minted earlier, or synced in from another device via iCloud Keychain) won’t offer it — swapping keys under an agent and an encrypted history is exactly the foot-gun this guards.

  • Same key on both ends: run npx pidge-cli doctor on the agent’s machine. It validates the secret and prints its fingerprint — it must match the one the app showed when you pasted the key. The channel’s connect screen keeps showing that fingerprint.
  • The server only has ciphertext: fetch any sealed notification straight from the API (GET /api/v1/notifications/<cid> with the channel key) — the content fields are v1: AES-256-GCM envelopes. You hold the key and the format is documented, with committed test vectors in the open CLI — nothing stops you from decrypting an envelope yourself, outside any Pidge code.
  • One key per channel. Don’t reuse it across channels or anywhere else.
  • Keep it out of chats. The agent’s chat log syncs to the provider’s cloud — that’s why the setup prompt never carries the secret, and yours shouldn’t either. Terminal and app only. Storing it in a password manager is fine.
  • It doesn’t expire and there’s no server recovery. Lose the key (and the iCloud Keychain copy) and the encrypted history is unreadable, permanently.
  • The generated key lands in your terminal scrollback once — clear it if the machine is shared. (It deliberately never enters shell history: the command stores it in a variable, not in your prompt line.)

Bring-your-own-key removes one trust assumption — that Pidge’s key generation is honest. The remaining one is the same as for any end-to-end encrypted app: the client you run must not leak the key it holds. See the security model for the full threat model.