Cybersecurity · Sarja Technologies
Pastry Password Server
A small, self-hosted, encrypted password vault for one person.
Not publicly releasedv1.1.0Unreviewed personal project — not security-audited
What it is
Pastry runs as a local web service on your own machine and is used from a browser. There is no cloud dependency, no analytics and no telemetry. It is written in Python with FastAPI and SQLite; the interface is plain HTML, vanilla JavaScript and CSS, with no external fonts or scripts.
What it is not — yet
- Multi-user accounts — the create-user and disable-user commands exist but refuse.
- Entry sharing between people.
- Single sign-on (SSO) or external identity providers.
- Integrations with other services, a browser extension or a mobile app.
- Independent security certification of any kind.
Verified against the implementation
What it does.
Encrypted vault
Entry names, URLs, categories, timestamps, usernames, passwords, notes and custom fields are sealed in AES-256-GCM blobs. The vault key is random and is wrapped under a key derived from your master password with Argon2id (64 MiB, 3 passes, 4 lanes).
Individual entry protection
An entry can require a separate password. Its contents are then encrypted under a key derived from that second password, so the vault password cannot open them. The entry re-locks on leaving it, on sign-out, on session expiry and after an idle period (5 minutes by default).
Authentication and throttling
One owner account with an Argon2id-hashed master password. Failed logins (5 in 15 minutes, then a 15-minute lockout) and separate-password attempts (5 in 10 minutes, then 10) are rate-limited in SQLite, so lockouts survive a restart. Failure messages are identical for an unknown user and a wrong password.
Session management
256-bit session tokens in HttpOnly, Secure, SameSite=Strict cookies; an inactivity timeout (10 minutes by default) and an absolute lifetime; a list of signed-in browsers with per-session revoke and ‘log out other sessions’. Changing the master password signs everything out.
Web hardening
CSRF token on every state-changing request, same-origin checks, a Host allow-list against DNS rebinding, a restrictive Content-Security-Policy with no inline scripts, no-store caching, and a default bind to 127.0.0.1 (binding elsewhere needs an explicit --host or config change and prints a plain-HTTP warning).
Backup and restore
‘pastry backup’ writes a consistent tar.gz snapshot with a checksum manifest; ‘pastry backup verify’ checks it without touching the live vault; ‘pastry restore’ keeps the previous files. Separately, a passphrase-encrypted export can be imported again.
Password generator
Generated in the browser with crypto.getRandomValues; length 8–64 and selectable character sets. Nothing is sent anywhere until you save the entry.
Security architecture
What is encrypted, and how.
| Data | Protection |
|---|---|
| Entry name, URL, category, timestamps, favourite flag | AES-256-GCM under the vault key |
| Username, password, notes, custom fields | AES-256-GCM under the vault key — or, for a protected entry, under that entry’s separate-password key |
| Vault key | 256-bit random, wrapped with AES-256-GCM under an Argon2id-derived key |
| Master password | Argon2id hash only; no plaintext is stored anywhere |
| Backups and exports | Already-encrypted data; exports additionally use a passphrase-derived key |
Stored in plaintext
Row IDs, random per-entry identifiers, the admin username, the Argon2id hash and KDF parameters, the wrapped vault key, blob sizes and the number of entries.
Server-side decryption
Pastry decrypts on the server, because search and rendering happen there. While a session is open the server holds the vault key in memory. A stolen disk, backup or database file alone does not expose content; an attacker who controls the running machine can read the unlocked vault.
Installation and deployment
Ubuntu 22.04 or newer (or another Linux with Python 3.10+), python3 and python3-venv.
Pastry listens on 127.0.0.1 by default and warns if you bind it elsewhere. For other devices the documentation describes Tailscale (tailscale serve) or a Caddy HTTPS reverse proxy on the local network, each with a Host allow-list entry. A systemd unit is included. These two paths are documented but were not exercised in the project's own testing.
sudo apt install -y python3 python3-venv
./install.sh
.venv/bin/pastry setup # choose a username and a master password (12+ characters)
.venv/bin/pastry start # listens on 127.0.0.1:8787 by default126 automated tests (pytest) cover migration, throttling, sessions, backups, encryption of metadata, anonymous access to every route, and XSS handling.
Known limitations.
From the project's own security review. Read these before relying on it.
- Not independently audited. The review document is the developer’s own; security-sensitive code, especially migration and backup, should be reviewed before depending on it.
- The server holds keys in memory while unlocked; a compromise of the running machine exposes the open vault.
- Python cannot guarantee that key material is erased from memory.
- Argon2id parameters are fixed in code and have not been measured on low-powered hardware such as a Raspberry Pi.
- Copied values stay on the clipboard until overwritten.
- Sessions are memory-only; a restart signs everyone out.
- A lost master password or separate password cannot be recovered. Recovery is the backup plus the master password.
Availability, source and documentation
Pastry has not been published for public download, and there is no public source or documentation URL to link to yet. Its README, security review and deployment guide ship with the project. This page will link to them when a release exists.
Questions about Pastry: contact Sarja.
Questions about Pastry or private security tooling?
Tell us what you need to build, supply, secure or run. You'll hear back from an engineer who can tell you plainly what we'd do — and what we wouldn't.