Vault 🇬🇧 🇩🇪 🇫🇷
  • Send
  • Request
  • How it works
  • Security & Trust
  • Pricing
  • Security & Trust

    End-to-end encrypted, even when the recipient requests the secret.

    In the request flow the recipient generates the X25519 keypair locally. The server receives only ciphertext plus public keys, never plaintext or private keys. The known limits of browser-based cryptography are documented openly below.

    In short

    • Your browser encrypts locally.
    • Our server stores your secret only as ciphertext.
    • The private key stays inside your link.
    • After the first successful read, the record is deleted.
    • The code is publicly verifiable.
    • We document our limitations openly.

    Cryptography

    Server behaviour

    • Hard-delete on the first successful read, atomic DELETE...RETURNING. No soft-delete table, no application-level backup of ciphertext rows. Database transaction logs (WAL) are continuously overwritten and not archived; daily server backups are overwritten after 7 days, and the ciphertext in them stays unreadable. SecretService.php:55-79
    • Per-IP rate limit of 10 create and 20 retrieve calls per minute, plus a global ceiling of 200 writes and 500 reads per minute to protect availability. Create.php:82 , Retrieve.php:42
    • TTL is server-enforced between 1 minute and 7 days, or up to 30 days on the Pro and Business plans. The web UI offers 1-hour, 24-hour and 7-day presets; API clients may pick any value in the range of their plan. SecretsValidators.php:43-52
    • 1 MB maximum ciphertext payload, enforced in the validator and the service layer. SecretService.php:28, SecretService.php:300-306

    Transparency

    • Live status page exposes the deployed commit SHA and environment so you can pin the running build. /status
    • RFC 9116 security.txt with contact, PGP key, policy and acknowledgements. /.well-known/security.txt
    • Backend service and browser crypto are published under the AGPL-3.0 license on a public mirror. LICENSE

    What Vault does not protect against

    • Like every web-based crypto tool, the protection only holds while the JavaScript we deliver is the one we intend to ship. A compromise of the server (or a successful TLS man-in-the-middle with a trusted certificate) could swap in a malicious bundle that exfiltrates plaintext before encryption. The main bundle is now delivered with an enforced Subresource Integrity hash, which stops a swapped asset but not a server that publishes a matching hash alongside a tampered bundle. The web component scripts, the third-party bundles and the stylesheets carry no hash at all.
    • No third-party cryptographic audit has been published yet. Until that exists, "trust" rests on the open source mirror plus your own review.

    What metadata may still exist

    The plaintext stays in your browser, but the server still observes a few things by virtue of running the service at all.

    • Optional labels travel base64-encoded in the URL fragment so the sender can preview them. Treat them as URL metadata, not as encrypted content; do not put secret information into the label field.
    • Dashboard notes are stored in plain text beside the ownership record and are readable by us and by everyone in your organisation. Record there what a secret was for, never secret information itself.
    • Ciphertext length is stored as-is, so the byte size of a secret is observable to anyone with database access.
    • Application logs record per-IP request hashes (sha256-truncated, not raw IPs) and route patterns. Token segments are redacted before logging via the SecretLogger redaction filter.
    • A network observer between you and our server can see request size and timing patterns. Padding the ciphertext to a fixed length is on the roadmap; today the size is whatever you typed plus a 16-byte GCM tag.

    How to reduce your risk

    • Pick the shortest TTL you can live with. Default to one hour for ad-hoc secrets and only stretch to seven days when the recipient is offline.
    • Add a password to the secret. The recipient needs both the link and the password, so a leaked link alone is useless. The password derives an extra key via PBKDF2-SHA256 with 1.2M iterations.
    • Leave the label empty if the secret context is itself sensitive. Labels travel in the URL fragment for the sender preview, but they are not encrypted content.
    • Send the link over one channel and the password over a different one. A single-channel compromise then leaks neither half.
    • Before pasting a secret, verify the URL bar reads exactly vault.erseni.com or your self-hosted domain. A look-alike domain hosting a malicious bundle is the strongest practical attack against this design.

    For high-risk users

    If you are a journalist, activist, or otherwise expect a well-resourced adversary, browser-delivered crypto is not the strongest tool you can pick. Treat Vault as a convenience layer with a documented threat model, not as a substitute for the practices below.

    • Run your own instance against your own domain. You then trust your own JavaScript build instead of ours.
    • For long-lived secrets prefer an offline tool: age, GPG, or a hardware-token-backed flow. They are auditable without a running server.
    • Read the secrets.js source before relying on a vault.erseni.com deployment. The bundle is short enough to review on one screen.
    • For seed phrases, key material, or anything that funds an account or revokes access, prefer end-to-end-encrypted email with PGP or an in-person handoff.

    Verify for yourself

    Source mirror: gitlab.erseni.net/open-source/secrets-component

    Deployed build: status page

    security.txt: /.well-known/security.txt

    How to verify the deployed code

    You do not need to take our word for which JavaScript you are running. Here is the concrete chain you can walk.

    1. Open the build manifest. It lists the application commit, component commit, mirror commit and the SHA-256 of every asset delivered to the browser. /build-manifest.json
    2. Click the mirror commit. It opens the exact source on the public mirror that was published with this deploy.
    3. Open DevTools, copy the secrets script URL, fetch and hash it. The result must match the manifest browserJs.sha256. curl -s '<script src from DevTools>' | sha256sum
    4. Read the file on the mirror. The browser crypto fits on one screen and uses native WebCrypto only.

    Why compare hashes by hand when the script tag already carries a Subresource Integrity (SRI) attribute? SRI makes the browser refuse a bundle that does not match the hash in the HTML, but both come out of the same deploy: a compromised server simply serves a new pair. The manifest hash comes from that same server and shares this limit. What makes the check independent is the public mirror: a separate system with a publicly visible history, so compare the manifest values against the mirror commit and read the source there. The web component scripts, the third-party bundles and the stylesheets carry no SRI hash today.

    Send a secret How it works

    Product

    Security

    Legal

    Cancel contracts here © 2026 Erseni Ltd. Zero-knowledge by design.