From the creators of LazyOwn Redteam Framework comes a free and open-source Zero-knowledge, zero-trust, end-to-end encrypted messaging and file vault, secured with hybrid post-quantum cryptography.
QuantumVault is free and open-source software released under the GNU Affero General Public License v3.0 (AGPLv3).
LazyOwn RedTeam released QuantumVault as free software so that journalists, activists, and ordinary people can communicate with hybrid post-quantum end-to-end encryption, free from surveillance, censorship, or any single point of control over their right to private communication and free expression.
We hope you put it to good use.
LazyOwn RedTeam is a security research and offensive security collective. We spend our time finding the gaps that surveillance, censorship, and authoritarian control exploit, and we know how often ordinary people, journalists, activists, and dissidents are left without tools they can actually trust. Rather than keep this technology behind a paywall or a closed SaaS, we are releasing QuantumVault to the public so that anyone, an independent newsroom, an NGO, a community group, or an individual, can deploy their own instance, inspect every line of code, modify it, and run it under their own infrastructure and their own responsibility.
- Deniable Vault (VeriCrypt): Plausibly deniable encrypted storage con outer/inner volumes.
- Encrypt and store files client-side before they ever leave the user's device, then store them as opaque ciphertext on the server.
- Exchange end-to-end encrypted messages sealed with a hybrid post-quantum key exchange so that only the sender and the intended recipient can ever read them.
- Authenticate without ever sending a password, using SRP-6a, a zero-knowledge password proof.
- Recover a forgotten password without a server-side reset, using a one-time recovery code generated in the browser at registration.
- Resist quantum and classical attacks alike, since breaking the hybrid construction requires breaking both the post-quantum and the classical primitive.
"Zero-knowledge" means the server storing data never has access to plaintext, private keys, or passwords, not as a policy promise, but because the cryptography makes it structurally impossible. "Zero-trust" means operators do not need to be trusted to keep that promise: the source code can be read, the cryptographic primitives verified, and the protections confirmed directly.
The architecture is intentionally hybrid: communications are relayed and encrypted blobs are stored through a central server for convenience and reachability, but that server is reduced to a transport and storage layer. It never holds the keys needed to read what passes through it.
All cryptographic operations that matter, key generation, encryption, and decryption, happen in the browser, using vendored, audit-friendly cryptographic libraries (static/js/vendor/, static/js/qv-crypto.js).
| Purpose | Primitive |
|---|---|
| Post-quantum key encapsulation | ML-KEM-768 (FIPS 203) |
| Classical key exchange | X25519 (Curve25519 ECDH) |
| Hybrid key wrapping | ML-KEM-768 + X25519, combined |
| Key derivation | HKDF-SHA256 |
| Symmetric encryption | AES-256-GCM |
| Authentication | SRP-6a (zero-knowledge password proof, password never leaves the client) |
| Account recovery | QV-RECOVERY-1 (160-bit recovery code, independently re-wraps the existing private key blob) |
The hybrid construction means that an attacker would need to break both ML-KEM-768 and X25519 to recover a user's keys, today or with a future quantum computer.
Because the server never has the password or the private key, a forgotten
password cannot be reset the way a traditional service would. Instead, at
registration the browser generates a 20-byte (160-bit) recovery code,
displayed once as 8 groups of 4 characters (e.g.
ABCD-EFGH-JKMN-...), and uses it to independently encrypt the same
private-key blob that the password protects. The recovery code is never
sent to or stored on the server, only the resulting ciphertext and its
salt.
To recover an account, visit /recover, enter the username, the
recovery code, and a new password. The browser fetches the stored
ciphertext, decrypts it locally with a key derived from the recovery
code, proves to the server that the decryption succeeded (by
reconstructing the account's public key from the recovered blob), and
then re-wraps the same private key under the new password. The
underlying keypair never changes, so previously stored messages and
files remain decryptable.
The recovery code is shown exactly once, in a one-time dialog right after registration. If it is lost and the password is also forgotten, the account's encrypted data cannot be recovered by anyone, including the operator: this is a direct consequence of the zero-knowledge design, not a missing feature.
For a user under surveillance or coercion, the most dangerous signal is the mere presence of an encryption tool. The cover facade hides QuantumVault behind an innocuous public site so an observer who opens the instance URL sees a commonplace page, not a vault.
- Enable per instance with
QV_FACADE_ENABLED=1and a gate phrase hash inQV_FACADE_GATE_HASH(seedocs/FACADE.mdfor the full environment contract). - Five built-in cover templates ship with the application (search portal,
newsletter, technology blog, art gallery, nonprofit), selected with
QV_FACADE_COVER_TEMPLATE. Every cosmetic string is configurable. - Custom templates: an operator may upload a Jinja template into
QV_FACADE_COVER_DIRand select it withQV_FACADE_COVER_TEMPLATE=custom. Templates are validated before rendering: only a closed variable allow-list is permitted, and imports, attribute traversal, scripts, event handlers, external form actions, and embeds are rejected. - Two-step gate: a correct secret phrase reveals the ordinary login; a duress phrase marks the session as duress so the browser preselects the decoy slot. A miss keeps the cover, and every attempt performs the same Argon2id work so the response is not an oracle.
- Untrusted input everywhere: cover variables are bounded, canonicalized, and stripped of control and spoofing characters, and rendered autoescaped.
| Layer | Technology |
|---|---|
| Web framework | Flask 3 (app_factory.py, wsgi.py) |
| Authentication / sessions | Flask-Login, SRP-6a session store backed by Redis |
| Forms / CSRF | Flask-WTF, WTForms |
| Security headers | Flask-Talisman |
| Rate limiting | Flask-Limiter |
| Database | SQLite (instance/users.db) via SQLAlchemy models (models/) |
| Object storage | Garage (self-hosted, S3-compatible) |
| Cache / ephemeral state | Redis |
| Flask-Mail (AWS SES or SMTP) | |
| SMS / MFA | ClickSend |
| Production server | Gunicorn |
| Client-side cryptography | Vendored JS: ML-KEM-768, X25519/Ed25519, HKDF, SHA-2/SHA-3, WebCrypto AES-GCM |
- Python 3.10+
python3-venv- Docker and Docker Compose (recommended), or a manually managed Redis instance and Garage instance
openssl(for local TLS certificates and secret generation)
QuantumVault's server side does not require liboqs or any native post-quantum library: all post-quantum and classical cryptography runs client-side in the browser via the vendored JavaScript modules in static/js/vendor/. No server-side post-quantum library installation is needed to run the application.
git clone https://github.com/grisuno/QuantumVault.git
cd QuantumVault
# One-shot setup: creates the virtualenv, installs Python dependencies,
# generates .env from .env.example, and (if Docker is available) brings
# up Garage + Redis and bootstraps the storage bucket.
make setup
# Start the Flask app in the foreground (dev mode, TLS on the loopback port)
make runBy default the app listens on https://0.0.0.0:4443.
If make run reports missing TLS material, generate a self-signed certificate:
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 365 -subj '/CN=localhost'If Docker is not available, install dependencies and bring up the dependencies natively:
make venv # create the virtualenv
make deps # install Python dependencies
make env # create .env from .env.example and generate Garage tokens
make redis-up # start a local Redis instance
make garage-up # download, run, and provision Garage natively
make run-local # start the Flask appProduction deployments use Gunicorn against the WSGI entry point (wsgi.py). A reverse proxy in front should terminate TLS and forward to the bound port.
make serveConfiguration lives in .env (copy from .env.example, generated automatically by make env). Key settings:
- Object storage (Garage / S3):
S3_ENDPOINT_URL,S3_ACCESS_KEY,S3_SECRET_KEY,S3_BUCKET,S3_REGION - Session store / rate limiting:
STORAGE_URI(Redis URL) - Mail:
MAIL_SERVER,MAIL_PORT,MAIL_USE_TLS,MAIL_USE_SSL,MAIL_USERNAME,MAIL_PASSWORD,MAIL_DEFAULT_SENDER. Supports AWS SES or any SMTP provider, including Gmail with an App Password. If left empty, the app skips sending mail and logs the confirmation link instead, recoverable withpython scripts/email_tool.py link <username>. - SMS / MFA (ClickSend):
CLICKSEND_USERNAME,CLICKSEND_API_KEY. Leave blank to skip SMS in development. - Garage admin / RPC:
GARAGE_ADMIN_TOKEN,GARAGE_RPC_SECRET,GARAGE_KEY_NAME,GARAGE_ADMIN_URL - Flask:
FLASK_DEBUG - Feature flags:
QV_ENABLE_SUBSCRIPTIONS(default1) — set to0to disable the paid-plans blueprint and nav link for operators who only want the free, self-hosted core. - Privacy hardening:
QV_AUDIT_LOG_IP,QV_AUDIT_LOG_UA(both default1),QV_TRUSTED_PROXY(default0). See "Running for high-risk users (Tor)" below.
QuantumVault's zero-knowledge cryptography already keeps message/file content, private keys, and passwords away from the server. The settings below additionally reduce the operational metadata an instance retains, for operators serving journalists, activists, or anyone else whose safety depends on minimizing what a compromised or subpoenaed server can reveal:
- Set
QV_AUDIT_LOG_IP=0andQV_AUDIT_LOG_UA=0so the structured audit log (utils/security.pyaudit_event) recordsipanduaasnullinstead of the requester's address and User-Agent string. The audit log still records timestamps, event names, and correlation IDs, so abuse patterns remain visible without storing identifying details. - Leave
QV_TRUSTED_PROXYunset (or0). This setting is for operators who run their own reverse proxy in front of gunicorn and wantX-Forwarded-Forhonored; behind Tor, the address Tor presents is not a meaningful client identifier, so there is nothing to "trust" and the default (use the directly-connecting socket address) is correct. - Serve the instance as a Tor onion service by pointing a
HiddenServicePortintorrcat the port gunicorn binds to (seemake serve/wsgi.py). No application code changes are needed — Tor handles the.onionaddress and end-to-end transport encryption; QuantumVault's own TLS/HSTS settings (_build_talisman_kwargsinapp_factory.py) can remain as configured for the clearnet listener, if any, or be left at their defaults for an onion-only deployment reached over Tor's own encrypted circuits. - Standard web server access logs (nginx/gunicorn) are outside QuantumVault's control; disable or rotate them aggressively at the reverse-proxy layer if they are not needed, since they can record client addresses independently of the application's own audit log.
- For disposable inbound addresses instead of a static
torrcentry, use the superadmin panel's secure channels (seedocs/SECURE_CHANNELS.md): one click publishes a Cloudflare quick tunnel, a disposable onion service, or both (hybrid), and rotation destroys the old addresses. Audit records keep only fingerprints, never the addresses. First runmake doctoron the server to check thattor,cloudflared, Redis, and Garage are present;make doctor-fixinstalls what is missing. SetQV_CHANNEL_LOCAL_SCHEME=httpswhen the local listener speaks TLS (the dev server does), otherwise the tunnel answers Bad Gateway.
Run make help to list all available targets. The most relevant ones:
| Target | Description |
|---|---|
make setup |
First-time setup: virtualenv, dependencies, .env, and (if Docker is present) Garage + Redis |
make run |
Run the Flask app in the foreground, starting Docker Compose or a local Redis/Garage as needed |
make run-local |
Run Flask without touching Docker (manage Redis/Garage yourself) |
make serve |
Run Gunicorn against the production WSGI module |
make stop |
Stop the dev app, Docker Compose stack, and any Redis/Garage started by make run |
make compose-up / make compose-down |
Bring the Garage + Redis Docker Compose stack up or down |
make garage-up / make garage-down |
Run Garage natively without Docker |
make redis-up / make redis-down / make redis-status |
Manage a native Redis instance |
make db-reset |
Wipe the development SQLite database |
make backupdb |
Snapshot instance/users.db to backups/ |
make doctor |
Check the full operator environment: modules, binaries, redis, garage, channel backends |
make doctor-fix |
Install everything make doctor reports missing (pip deps, apt packages, cloudflared, garage) |
make test |
Run the pytest suite (SRP-6a roundtrip, audit-log redaction, facade, cover, CSRF helper tests) |
make mutate |
Run mutation testing for the facade and cover contracts |
make audit |
Run the security audit stack: pip-audit + bandit + secret scanning |
make pip-audit |
Check the dependency tree for known vulnerabilities |
make bandit |
Static security scan of the Python codebase |
make semgrep |
Run Semgrep rules against crypto anti-patterns in the codebase |
make upgrade-deps |
Refresh the virtualenv to match requirements.txt and regenerate requirements.lock |
make clean |
Remove caches and build artifacts (keeps .env and instance/users.db) |
Because QuantumVault is licensed under AGPLv3, anyone running a modified version of it as a network service must make the corresponding source code available to its users. This keeps every deployment auditable.
Each instance of QuantumVault is operated independently by whoever deploys it. LazyOwn RedTeam provides the software, not a hosted service, and does not control or have visibility into independently operated instances.
QuantumVault is released under the GNU Affero General Public License v3.0 (AGPLv3). See the LICENSE file for the full text.
QuantumVault: free software, post-quantum encryption, for a free press and free expression.
This project has been analyzed by ReadMenator, a zero-token polyglot static analysis tool. Analysis outputs are available:
- KNOWLEDGE_BASE.md -- Full architecture reference with all classes, functions, imports, dependency graphs, UML class diagrams, security audit findings, community analysis, and more.
- readmenator-agent/ -- Agent-friendly, grep-optimized index.
INDEX.md-- Quick reference: what each file doesAPI.md-- Public function contractsGOTCHAS.md-- Change warningsSECURITY.md-- Findings by severity
- readmenator-wiki/ -- Navigable wiki (start here for the big picture).
index.md-- Entry point: overview, reading order, god nodes, connectionscommunity_*.md-- One synthesis page per code communityREPORT.md-- Honest audit: coverage, confidence, limits
AI agents: Read readmenator-wiki/index.md first for the big picture, then readmenator-agent/INDEX.md for grep-friendly lookup.
Developers: Read KNOWLEDGE_BASE.md for full architecture reference.