Payload capture
prxy.monster does not retain plaintext request or response bodies by default. The default mode hash_only writes only the four canonical sha-256 hashes onto the receipt; the bodies themselves never persist.
Three modes
| Mode | What’s stored | Cache eligible | Decryptable? |
|---|---|---|---|
hash_only (default) | sha-256 of canonical request + response | yes | n/a — no plaintext |
encrypted_at_rest (opt-in) | sealed envelope under customer X25519 pubkey | no — cache bypassed | yes, with customer’s private key |
none (compliance opt-out) | nothing beyond receipt hashes | no — cache bypassed | n/a |
Configure per api_key. The mode you pick is recorded on every receipt in the payload_capture field, so any auditor can confirm which mode was active for any specific call.
Why cache is bypassed for encrypted_at_rest and none
Caching across customers with private payloads would defeat the encryption guarantee — the cache key would have to expose the plaintext. We deliberately refuse to make that trade.
The encryption envelope (encrypted_at_rest)
Algorithm: x25519-xchacha20-poly1305-v1.
1. Generate ephemeral X25519 keypair per call.
2. ECDH(ephemeral_priv, recipient_pub) → shared secret.
3. HKDF-SHA256(salt = ephemeral_pub || recipient_pub, info = 'prxy-payload-v1') → 32-byte key.
4. XChaCha20-Poly1305 with random 24-byte nonce → ciphertext + tag.
5. Envelope = { alg, ephemeral_pub, nonce, ciphertext, fingerprint }.
Per-call ephemerals provide forward secrecy. The customer decrypts client-side with their private key; prxy’s server never holds the customer private key.
Customer keypair flow
Operator generates the keypair browser-side at lair /dashboard/admin/api-keys:
- Click “Generate new keypair (browser-side)”.
- Browser runs
x25519.utils.randomSecretKey()→ priv + pub. - Public key auto-fills the form; private key is shown once in a textarea + a download button (
prxy-x25519-<key-prefix>.txt). - Operator submits the form. Gateway computes the SHA-256 fingerprint from the uploaded pubkey bytes server-side (callers can’t lie about the fingerprint).
- The api_key row stores
payload_pubkey,payload_pubkey_fingerprint,payload_pubkey_uploaded_at.
Defense in depth (Sentry)
The Sentry beforeSend and beforeBreadcrumb hooks strip request/response bodies and sensitive headers from outbound events as defense-in-depth:
- Headers stripped:
authorization,cookie,x-provider-key,x-api-key,x-prxy-key. - Fields stripped: any field matching
messages | prompt | body | content | text | system | password.
So even if a module logs an exception with body context, that body is removed before leaving the process.
What about outcome notes?
Outcome notes are sha-256 hashed server-side at write time. Raw text never persists. Reviewers see only the hash. See Outcomes.