Both secret-bearing downloads can now be sealed with a password. Unticked, the
forms produce exactly the files they did before.
- internal/secretfile: envelope format — magic/type/scrypt params/salt/nonce
prefix header, then 64 KiB AES-256-GCM chunks each authenticated with the
header, its counter and an end-of-stream flag, so truncation, reordering and
tampering fail to open instead of restoring a plausible prefix. Streams both
ways, so a full backup never sits in memory.
- Panel: "Encrypt with a password" checkbox on the full-backup and
domain-export forms (shared partial, toggled from panel.js — no inline
script); domain import detects an encrypted export by magic bytes, not by
extension, and asks for the password.
- selfpost-backup: writes .spbk when given a password and converts one back
with -decrypt, which a restore needs. The password comes from
SELFPOST_BACKUP_PASSWORD or -password-file, never argv.
- Docs: README, security.md (+ accepted risk: encryption stays opt-in),
architecture.md, progress.md, CHANGELOG.
Verified locally: panel-encrypted archive decrypts through the CLI and unpacks;
wrong password and password mismatch are refused; UI checked in a browser.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>