Recommendation: build it, opt-in, passphrase mode only in v1.
Crypto: AES-256-GCM (WebCrypto) with a per-paste random DEK, wrapped by a key derived from the passphrase via PBKDF2-HMAC-SHA-256 (600k iterations, 16-byte salt). Argon2id deferred to v2 (no WebCrypto support; envelope v field reserves the migration path).
API: additive — optional encryption KDF metadata on create/get, has_encryption flag in list views, language: "encrypted"|"null", raw endpoint serves the envelope as an attachment. Server treats content as opaque bytes; 413 applies to envelope bytes.
Feature interplay: burn-after-read still works (charged on fetch, exactly as today — wrong-key viewers burn a read, retries are client-side); search, server-side language detection and highlighting are impossible on encrypted pastes and are skipped/done client-side; expiry/delete/tokens/cans unchanged.
Key sharing UX: passphrase out-of-band is the default; fragment-key sharing is opt-in with a warning; Referrer-Policy: no-referrer recommended.
Explicit threat model: protects against passive server compromise (DB/backup leaks), not against a malicious server swapping its own JS.
Server cost is small (pass-through, skip detection/indexing, list flag); all crypto is client-side WebCrypto with no new dependencies. Closes nothing yet — implementation to follow if the design is accepted.
Design-only PR for issue #39 — no implementation.
**Doc:** `docs/design/e2e-encryption.md`
Recommendation: build it, opt-in, passphrase mode only in v1.
- **Crypto:** AES-256-GCM (WebCrypto) with a per-paste random DEK, wrapped by a key derived from the passphrase via PBKDF2-HMAC-SHA-256 (600k iterations, 16-byte salt). Argon2id deferred to v2 (no WebCrypto support; envelope `v` field reserves the migration path).
- **API:** additive — optional `encryption` KDF metadata on create/get, `has_encryption` flag in list views, `language: "encrypted"|"null"`, raw endpoint serves the envelope as an attachment. Server treats content as opaque bytes; 413 applies to envelope bytes.
- **Feature interplay:** burn-after-read still works (charged on fetch, exactly as today — wrong-key viewers burn a read, retries are client-side); search, server-side language detection and highlighting are impossible on encrypted pastes and are skipped/done client-side; expiry/delete/tokens/cans unchanged.
- **Key sharing UX:** passphrase out-of-band is the default; fragment-key sharing is opt-in with a warning; `Referrer-Policy: no-referrer` recommended.
- **Explicit threat model:** protects against passive server compromise (DB/backup leaks), not against a malicious server swapping its own JS.
Server cost is small (pass-through, skip detection/indexing, list flag); all crypto is client-side WebCrypto with no new dependencies. Closes nothing yet — implementation to follow if the design is accepted.
ListPublic and its COUNT query now filter password_hash IS NULL, so
/api/public (and any page backed by it) no longer leaks metadata
(title, slug, existence) of password-protected pastes. Unlisted
pastes were already excluded. Adds regression test covering both.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Design-only PR for issue #39 — no implementation.
Doc:
docs/design/e2e-encryption.mdRecommendation: build it, opt-in, passphrase mode only in v1.
vfield reserves the migration path).encryptionKDF metadata on create/get,has_encryptionflag in list views,language: "encrypted"|"null", raw endpoint serves the envelope as an attachment. Server treats content as opaque bytes; 413 applies to envelope bytes.Referrer-Policy: no-referrerrecommended.Server cost is small (pass-through, skip detection/indexing, list flag); all crypto is client-side WebCrypto with no new dependencies. Closes nothing yet — implementation to follow if the design is accepted.