AUTONOMY DIRECTORATE

🏠 Main

🧪 Interactive Apps

📰 News

🛡️ PQ Crypta Proxy

👤 Account

⟨ QUANTUM ERROR PORTAL ⟩

Navigate the Error Dimensions

PQ Crypta Logo

Encrypt & Share — Dual-KEM Zero-Knowledge Architecture

How PQ Crypta delivers quantum-safe encrypted message links: two independent NIST Level 5 algorithms, a key that never leaves your browser, and ciphertext that self-destructs.

ML-KEM-1024 + HQC-256 Zero-Knowledge Dual-KEM Allan Riddel · PQ Crypta · April 2026
Abstract

PQ Crypta’s Encrypt & Share is a zero-knowledge encrypted message sharing system built on a dual-KEM architecture. Every message is independently encapsulated under ML-KEM-1024 (lattice-based, FIPS 203, NIST Level 5) and HQC-256 (code-based, NIST 2025, NIST Level 5). These two algorithms are founded on entirely different mathematical hard problems — Module Learning With Errors (MLWE) for ML-KEM-1024 and the Quasi-Cyclic Moderate Density Parity Check (QC-MDPC) problem for HQC-256. An attacker must break both simultaneously to read any message. The 32-byte AES-256-GCM wrap key that unlocks the private keys exists only in your URL fragment — a browser mechanism that is never transmitted to any server. PQ Crypta stores ciphertext only. This paper describes the architecture, security model, and implementation of the system.

🔐
Try it now — no account required This whitepaper describes the live system at pqcrypta.com/share/. Send a quantum-safe encrypted message in seconds.
Encrypt & Share
The harvest-now-decrypt-later problem: Nation-state actors are already intercepting and archiving today’s TLS-encrypted traffic, waiting for quantum computers capable of breaking RSA and ECDH. Sensitive messages shared today with classical encryption tools can be decrypted in the future. Encrypt & Share was designed from the ground up for exactly this threat model.

1. Origin and Motivation

Most “secure message” tools encrypt messages under a single algorithm — typically AES-256-GCM or ChaCha20-Poly1305 for symmetric encryption, with an RSA or ECDH key exchange. This is secure against classical computers today, but it provides no protection against a capable quantum adversary. Shor’s algorithm breaks RSA and ECDH in polynomial time on a sufficiently large quantum computer. AES-256 survives (Grover’s algorithm halves the key search space to 128-bit equivalent, still infeasible), but the key exchange that delivers the AES key does not.

NIST standardised three post-quantum algorithms in 2024: ML-KEM-1024 (key encapsulation, FIPS 203), ML-DSA-87 (signatures, FIPS 204), and SLH-DSA-SHA2-256s (hash-based signatures, FIPS 205). In 2025, NIST standardised HQC as a fourth algorithm — a code-based KEM specifically chosen as a backup to ML-KEM, designed for precisely the scenario where a structural weakness in lattice-based cryptography is discovered.

Encrypt & Share uses all four. ML-KEM-1024 and HQC-256 provide redundant key encapsulation from different mathematical families. ML-DSA-87 and SLH-DSA-SHA2-256s provide digital signatures. The system was built because no available encrypted message tool used this combination — tools that did use post-quantum cryptography used only one algorithm.

2. Why Dual-KEM?

Single-algorithm post-quantum systems carry a structural risk: if a mathematical breakthrough weakens the underlying hard problem, the entire security stack collapses. The history of cryptography is full of algorithms that were believed secure and later broken — including NIST-standardised ones (DUAL_EC_DRBG in 2013, SIKE in 2022).

Dual-KEM (This System)
  • ML-KEM-1024: MLWE hard problem (lattice)
  • HQC-256: QC-MDPC hard problem (code-based)
  • Different mathematical families — independent security assumptions
  • Both must be broken simultaneously
  • A quantum breakthrough against lattices leaves HQC intact
  • Both are NIST-standardised, NIST Level 5
Single-KEM Systems
  • One algorithm — one security assumption
  • Breaking the underlying problem breaks all messages
  • Even ML-KEM-only systems: if MLWE is weakened, all traffic is exposed
  • No algorithmic diversity or fallback
  • Standard practice today, but unnecessary risk when dual-KEM is feasible

NIST explicitly designed HQC as a backup to ML-KEM for this reason: “to have a KEM that is not based on structured lattices in case ML-KEM is compromised.” Encrypt & Share operationalises NIST’s own recommended dual-KEM strategy.

3. Algorithm Suite

Algorithm Standard Role Level Quantum Threat
ML-KEM-1024 FIPS 203 Key Encapsulation (lattice) Level 5 Resistant ✓
HQC-256 NIST 2025 Key Encapsulation (code-based) Level 5 Resistant ✓
ML-DSA-87 FIPS 204 Digital Signature Level 5 Resistant ✓
SLH-DSA-SHA2-256s FIPS 205 Hash-Based Signature Level 5 Resistant ✓
AES-256-GCM FIPS 197 Private Key Wrap (URL fragment only) 256-bit Halved by Grover*
HMAC-SHA-256 FIPS 198-1 Metadata Integrity Signature 256-bit Resistant ✓
* AES-256-GCM is used solely to wrap the ML-KEM and HQC private keys inside the URL fragment. It is never used to encrypt your message. Grover's algorithm reduces AES-256 to a 128-bit equivalent security level — still computationally infeasible with any foreseeable quantum hardware.

4. Key Properties

🗝️
Zero-Knowledge
The wrap key exists only in the URL fragment. Fragments are never sent to any server by design. PQ Crypta cannot read your message under any circumstances.
🧬
Algorithm Diversity
ML-KEM-1024 (MLWE) and HQC-256 (QC-MDPC) are mathematically independent. Breaking one does not help break the other.
📏
Length-Hiding Padding
All messages are padded to a fixed 4 KB block boundary before encryption. A 5-word note and a 3,000-word letter produce identically-sized ciphertext.
🔏
Tamper-Evident Metadata
Expiry, burn-after-reading flag, and envelope version are HMAC-SHA-256 signed with a key derived from the wrap key. Any server-side tampering is detected.
🔥
Burn After Reading
The server permanently destroys the ciphertext immediately after the first retrieval, making the link invalid for all subsequent access attempts.
📦
Versioned Envelope
Each message is wrapped in a versioned envelope (v2 for dual-KEM). Older link formats can still be decrypted correctly after algorithm upgrades.
🔗
Short Links
A 6-character base-58 short code provides compact sharable links. The wrap key fragment is never included in the short code — only the UUID lookup.
🚫
No Account Required
No registration, no email, no tracking. IP addresses are one-way SHA-256 hashed with a per-service salt before storage — not reversible.

Encrypt Flow

Encryption is entirely client-side. The browser generates keypairs and performs all cryptographic operations via the PQ Crypta API (api.pqcrypta.com). The server only ever receives ciphertext and encrypted key blobs — it never sees plaintext or the wrap key.

Encrypt & Share — Full Encryption Flow
1
Key Generation (parallel). Browser generates ML-KEM-1024 keypair (via max-secure-pure-pq engine) and HQC-256 keypair simultaneously via two parallel API calls to POST /keys/generate.
2
Wrap Key Generation. Browser generates a 32-byte random wrap key using crypto.getRandomValues(). This key will wrap both private keys and will live only in the URL fragment.
3
Plaintext Padding. The message is UTF-8 encoded and padded to a fixed 4 KB (4096-byte) block boundary using random bytes. This ensures ciphertext length reveals nothing about message length.
4
Dual Encryption (parallel). The padded plaintext is encrypted under ML-KEM-1024 and HQC-256 simultaneously via two parallel calls to POST /encrypt. This produces two independent ciphertexts.
5
Private Key Wrapping. Both private keys (ML-KEM and HQC) are individually wrapped with AES-256-GCM using the 32-byte wrap key and unique random IVs. The public keys are also stored in the key blob for self-contained decryption.
6
Metadata HMAC. A 16-byte HMAC-SHA-256 is computed over the serialised metadata (expiry hours, burn flag, envelope version) using a key derived from the wrap key. This signs the metadata without revealing the wrap key.
7
Versioned Envelope Assembly. A v2 JSON envelope is assembled containing both ciphertexts, both encrypted key blobs, envelope version, and HMAC. The complete envelope is sent to POST /share/api.php (store action).
8
URL Construction. The server returns a UUID message ID and short code. The browser constructs the share URL as https://pqcrypta.com/share/?id=<UUID>#<wrapKey>:<metaMac>. The wrap key and HMAC travel only in the fragment — never sent to any server.

URL Structure

The URL is the complete security boundary. Everything before the # can be seen by the server. Everything after it is private to the browser.

Share URL Anatomy
https://pqcrypta.com/share/?id= 550e8400-e29b-41d4-a716-446655440000 # Zm9vYmFyYmF6cXV4cXV4cXV4cXV4cQ==:a1b2c3d4e5f6a1b2
?id=<UUID> Server-side lookup key. Sent to server on GET request. Used to retrieve the ciphertext envelope from the database.
?s=<code> Alternate short link form. 6-character base-58 code. Server resolves to UUID and redirects (fragment preserved). Never sent as plaintext.
#wrapKey 32-byte AES-256-GCM wrap key, base64url-encoded. Used to unwrap both ML-KEM and HQC private keys from the key blobs. Never transmitted to any server.
:metaMac 16-byte HMAC-SHA-256 over metadata, hex-encoded. Used to verify expiry/burn metadata has not been tampered with server-side.

Decrypt Flow

When a recipient opens the share URL, all decryption happens in the browser. The server provides only the opaque ciphertext envelope. The wrap key in the fragment unlocks everything — and the server never sees it.

Decrypt Flow — Full Sequence
1
URL Parse. Browser extracts message ID from query string and wrap key + HMAC from URL fragment. Fragment is never sent to the server.
2
Fetch Envelope. Browser calls POST /share/api.php (retrieve action) with the message ID. Server returns the ciphertext envelope (or an error if burned/expired/not found).
3
Metadata HMAC Verification. Browser recomputes HMAC over metadata using wrap-key-derived key and compares against the MAC in the URL. Mismatch means server-side tampering — decryption is aborted.
4
Key Unwrap. Browser decrypts both key blobs with AES-256-GCM using the wrap key. Recovers ML-KEM-1024 private key and HQC-256 private key.
5
Dual Decryption (parallel). Browser calls POST /decrypt twice in parallel — once with the ML-KEM ciphertext and recovered private key, once with the HQC ciphertext and its private key.
6
Cross-Verification. Browser compares the two decrypted plaintexts byte-for-byte. If they differ, decryption is aborted — indicating ciphertext tampering on the server side.
7
Unpad & Display. Padding bytes are stripped from the verified plaintext. The original message is decoded from UTF-8 and displayed.

Versioned Envelope

Every message is stored in a versioned JSON envelope. The version field ensures forward compatibility: as the encryption format evolves, older links continue to decrypt correctly using the appropriate version handler.

v2 Envelope (Dual-KEM, current)
  • ver: 2 — envelope version
  • pq — ML-KEM-1024 ciphertext (base64)
  • hqc — HQC-256 ciphertext (base64)
  • pq_kb — ML-KEM key blob (wrapped keys)
  • hqc_kb — HQC key blob (wrapped keys)
  • mac — metadata HMAC hex
  • exp — expiry hours
  • burn — burn-after-reading flag
v1 Envelope (single-KEM, legacy)
  • ver: 1 — envelope version
  • pq — ML-KEM-1024 ciphertext only
  • pq_kb — ML-KEM key blob only
  • mac — metadata HMAC hex
  • exp — expiry hours
  • burn — burn-after-reading flag
  • HQC fields absent — backward compatible

Database Schema

The share_messages table is the only persistent store. It holds the ciphertext envelope but nothing that can identify the message content or the parties involved.

CREATE TABLE share_messages (
    id              UUID         PRIMARY KEY DEFAULT gen_random_uuid(),
    blob            TEXT         NOT NULL,              -- JSON envelope (ciphertext only)
    key_blob        TEXT         NOT NULL DEFAULT '',   -- AES-wrapped private keys
    algorithm       VARCHAR(64)  NOT NULL DEFAULT 'ml-kem-1024+hqc-256',
    created_at      TIMESTAMPTZ  DEFAULT NOW(),
    expires_at      TIMESTAMPTZ  NOT NULL,
    view_count      INT          DEFAULT 0,
    burn_after_read BOOLEAN      DEFAULT FALSE,
    destroyed       BOOLEAN      DEFAULT FALSE,
    ip_hash         TEXT,                              -- SHA-256 (ip + salt), not reversible
    message_size    INT          DEFAULT 0,
    short_code      VARCHAR(12)  UNIQUE,              -- 6-char base-58 short link
    envelope_ver    SMALLINT     NOT NULL DEFAULT 1
);

-- Partial indexes: only live messages are queried
CREATE INDEX idx_share_expires   ON share_messages(expires_at)  WHERE NOT destroyed;
CREATE UNIQUE INDEX idx_share_short ON share_messages(short_code) WHERE short_code IS NOT NULL;

The blob column holds the JSON envelope. Without the wrap key from the URL fragment, this data is computationally indistinguishable from random bytes. The key_blob column holds the AES-wrapped private keys. Neither field contains recoverable plaintext without the wrap key.

⚛️
See the architecture in action Open DevTools on pqcrypta.com/share/, send a message, and watch the wrap key appear only in the URL fragment — never in any network request.
Open App

Zero-Knowledge Architecture

The defining security property of Encrypt & Share is zero-knowledge: the server holds ciphertext and cannot decrypt it, even with full administrative access and unlimited compute. This is not a policy claim — it is an architectural constraint.

The URL Fragment Guarantee: RFC 3986 defines the fragment identifier as the component of a URL that begins with #. The HTTP specification (RFC 7230) explicitly states that browsers must not include the fragment in requests to the server. This is a fundamental browser mechanism, not a configuration option. The wrap key appended after # is processed only by JavaScript running in the browser. It never reaches any network socket. It never appears in server logs, access logs, or database records.

This means:

  • A subpoena to PQ Crypta produces only ciphertext.
  • A server breach leaks only ciphertext.
  • A database dump leaks only ciphertext.
  • A network intercept of the API call leaks only ciphertext and the UUID lookup.

The only copy of the wrap key exists in the URL as held by the sender and recipient. If the URL is kept private, the message is private — against any adversary that does not have access to the sender’s or recipient’s device.

Algorithm Diversity

ML-KEM-1024 is based on the Module Learning With Errors (MLWE) problem — a variant of LWE with module structure, whose hardness assumption is the difficulty of distinguishing module lattice samples from random. HQC-256 is based on the Quasi-Cyclic Moderate Density Parity Check (QC-MDPC) problem — a code-based problem from information theory unrelated to lattices.

ML-KEM-1024 Security Basis
  • Module Learning With Errors (MLWE)
  • Security reduces to hardness of MLWE over module lattices
  • Dimension: 1024-bit, module rank k=4
  • Best known quantum attack: BKZ lattice sieving
  • Classical: ~256-bit, quantum: ~256-bit (Grover does not apply)
  • Standardised: FIPS 203 (2024)
HQC-256 Security Basis
  • Quasi-Cyclic Moderate Density Parity Check (QC-MDPC)
  • Security reduces to hardness of decoding random quasi-cyclic codes
  • Different mathematical structure: algebraic coding theory
  • Best known quantum attack: ISD (information set decoding)
  • Specifically selected by NIST as ML-KEM fallback for algorithm diversity
  • Standardised: NIST 2025 (HQC)

An attack that breaks ML-KEM-1024 must exploit the MLWE structure — this gives zero information about HQC-256’s QC-MDPC structure, and vice versa. The two security assumptions are mathematically independent. Both must fail simultaneously for any message in the system to be readable.

Length-Hiding Padding

Without padding, ciphertext length leaks message length. An adversary observing traffic can distinguish a “password” from a “3,000-word legal document” by their ciphertext sizes. This is a traffic analysis attack that survives end-to-end encryption.

Encrypt & Share pads all messages to a fixed 4,096-byte block boundary before encryption. The padding consists of cryptographically random bytes appended to the UTF-8 encoded message. A separator byte marks the boundary between content and padding to enable reliable unpadding. A 10-character note and a 3,000-character letter produce identically-sized ciphertexts (subject to the block-size multiple).

4 KB
Fixed block boundary
10,000
Max message chars
≡0
Length information leaked
Independent ciphertexts per message

Tamper-Evident Metadata

Message metadata (expiry hours, burn-after-reading flag, envelope version) is stored in the database alongside the ciphertext. Without protection, a malicious server operator or attacker with database access could modify this metadata — for example, disabling the burn-after-reading flag to read a message multiple times, or extending the expiry of a message that should have been destroyed.

Encrypt & Share protects against this with an HMAC-SHA-256 signature over the metadata, using a 32-byte key derived from the wrap key via a key derivation step. This MAC travels in the URL fragment alongside the wrap key. On decryption, the browser recomputes the MAC and compares it against the metadata in the retrieved envelope before proceeding with decryption. Any mismatch aborts the operation.

Derived key pattern: The metadata MAC key is derived from the wrap key as HMAC-SHA-256(wrapKey, "meta-hmac-v1"), producing a separate 32-byte key. This ensures the MAC key and encryption key are distinct — a standard domain separation practice.

Threat Model

Threat Status Mechanism
Classical computer attacks (RSA/ECDH break) Mitigated ✓ No classical key exchange used. ML-KEM + HQC only.
Harvest-now-decrypt-later (HNDL) Mitigated ✓ Dual NIST Level 5 PQC. Both must be broken simultaneously.
Server-side breach / database dump Mitigated ✓ Zero-knowledge: wrap key never transmitted. DB holds ciphertext only.
Server-side metadata tampering Mitigated ✓ HMAC-SHA-256 over metadata verified client-side before decryption.
Ciphertext tampering (one algorithm) Mitigated ✓ Cross-verification: both plaintexts must match. Mismatch aborts.
Traffic analysis (message length) Mitigated ✓ 4 KB block padding: all ciphertexts are same size modulo block.
Re-use of one-time link Mitigated ✓ Burn-after-reading: ciphertext wiped immediately after first retrieve.
Man-in-the-middle (TLS layer) Mitigated ✓ Fragment never sent over any transport. TLS intercept gets ciphertext only.
URL shared with wrong recipient User responsibility The URL is the credential. Handle it like a password.
Compromised recipient device Out of scope Endpoint security is outside any cryptographic system’s scope.

Cross-Verification

Cross-verification is the final layer of integrity checking. After both ciphertexts are decrypted independently by their respective algorithms, the resulting plaintexts are compared byte-for-byte. Correct dual encryption of the same plaintext under two independent algorithms must produce identical plaintexts on decryption.

If the plaintexts differ, one of the following has occurred: (1) the server has replaced or corrupted one ciphertext; (2) the private keys have been tampered with such that only one decryption path succeeds; or (3) an implementation bug exists. In all cases, the mismatch is surfaced to the user and decryption is aborted. This provides an active tamper detection mechanism beyond what AES-GCM authentication tags alone provide.

Client-Side Cryptography

All cryptographic operations are performed client-side in the browser via the PQ Crypta API. The JavaScript client (/share/js/share.js) is a self-contained IIFE with no external dependencies beyond the platform’s Web Crypto API and the PQ Crypta REST API.

Cryptographic Operations (Browser)
  • crypto.getRandomValues() — wrap key generation
  • crypto.subtle.importKey() — wrap key import
  • crypto.subtle.encrypt() — AES-256-GCM private key wrap
  • crypto.subtle.decrypt() — AES-256-GCM private key unwrap
  • crypto.subtle.sign() — HMAC-SHA-256 metadata MAC
  • crypto.subtle.verify() — HMAC verification
  • crypto.subtle.deriveBits() — HMAC key derivation
Delegated to PQ Crypta API
  • POST /keys/generate — ML-KEM-1024 + HQC-256 keypairs
  • POST /encrypt — post-quantum encryption of padded plaintext
  • POST /decrypt — post-quantum decryption of ciphertext
  • All heavy-duty PQC operations (multi-hundred KB public keys)
  • Algorithm-specific key serialisation and formatting

The wrap key and all decrypted key material exist only in JavaScript variables for the duration of the operation. They are not stored in localStorage, sessionStorage, cookies, or any persistent browser storage.

API Layer

The PQ Crypta API runs at api.pqcrypta.com on port 3003. It is a Rust-based HTTP server providing key generation, encryption, and decryption endpoints for all 31 supported post-quantum algorithms. The max-secure-pure-pq engine handles ML-KEM-1024 (combined with ML-DSA-87 and SLH-DSA-SHA2-256s for the signature layer), and hqc-256 handles the code-based KEM.

The share page uses API key authentication via the X-API-Key header, loaded from /api_config.php at runtime. The API enforces granular permissions per endpoint per key.

Server-Side Storage

/share/api.php handles four actions over a PostgreSQL database:

store action
  • Validates base64 blob, key_blob, algorithm, envelope_ver
  • Rejects blobs over 256 KB (prevents abuse)
  • Generates short code (6-char base-58, 10 collision-avoidance attempts)
  • SHA-256 hashes the IP address with a per-service salt
  • Inserts row; returns UUID id + short_code
retrieve action
  • Accepts UUID id or short code s
  • Verifies message exists, is not expired, is not destroyed
  • Increments view_count
  • If burn_after_read: immediately sets destroyed=true, blob=''
  • Returns blob + key_blob + envelope metadata
destroy action
  • Accepts UUID id
  • Sets destroyed=true, blob='', key_blob=''
  • Prevents any further retrieval of the message
  • Used by the “Destroy this message now” button
health action
  • Returns row count and recent message counts
  • Used by internal health check monitoring
  • Triggers cleanup of expired messages on every call

Short Links

Every encrypted message receives a 6-character base-58 short code in addition to its full UUID URL. The alphabet excludes visually ambiguous characters (0, O, I, l) to minimise transcription errors: 23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz.

The short link form is pqcrypta.com/share/?s=Ab3Xk9#wrapKey:metaMac. The server resolves the short code to a UUID internally. The wrap key fragment is never included in the short code and never sent to the server as part of the short-code lookup. It is always appended to the full resolved URL by the browser.

Expiry, Burn-After-Reading, and Client-Side Timers

Server-Side Expiry

Eight expiry durations are available at message creation:

Label Value (hours) Use case Server action at expiry Link state
1 minute 0.0167 h Highly sensitive, immediate use blob wiped ✓ Invalid
5 minutes 0.0833 h Quick handoff blob wiped ✓ Invalid
15 minutes 0.25 h Short session blob wiped ✓ Invalid
30 minutes 0.5 h Meeting / call duration blob wiped ✓ Invalid
1 hour 1 h Standard short-lived share blob wiped ✓ Invalid
24 hours 24 h Default — next-day access blob wiped ✓ Invalid
3 days 72 h Cross-timezone coordination blob wiped ✓ Invalid
7 days 168 h Extended access window blob wiped ✓ Invalid

Expiry is enforced at two independent levels: the expires_at column is checked on every retrieve attempt and returns an error immediately on any expired message, and a cleanup sweep runs on every API call to set destroyed=true and clear blob='' on all rows where expires_at < NOW(). Expiry hours are stored in the envelope and signed by the metadata HMAC — the server cannot silently extend a link’s lifetime without breaking the MAC.

Burn-After-Reading

Burn-after-reading is implemented atomically within the retrieve operation: the destroyed flag is set and blob is cleared in the same database update that returns the row to the client. A race condition between two simultaneous retrieve requests for the same burn-message is resolved by the PostgreSQL row-level update: only the first succeeds; the second receives an empty blob and an error response.

Client-Side Security Timers

Two independent JavaScript timers run in the browser to minimise the window during which sensitive content is visible on screen. Neither timer is negotiable or bypassable through the URL — they are hardcoded security constants.

Result View — Hard 60s Countdown
  • Starts immediately when the share link is displayed after encryption
  • Counts down unconditionally — no reset on activity
  • At 10 seconds remaining: badge turns urgent (red)
  • At 0: link view is cleared from the DOM and compose view is restored
  • Purpose: limits the window a share URL is visible on an unattended screen
  • Constant: RESULT_TIMEOUT_SEC = 60
Decrypt View — 120s Idle Countdown
  • Starts when the decrypted message is displayed
  • Resets on any user activity: mousemove, mousedown, keydown, scroll, touchstart, click
  • Badge appears when ≤30 seconds of idle remain
  • At 10 seconds remaining: badge turns urgent (red)
  • At 0: decrypted message is cleared from the DOM
  • Purpose: protects against shoulder-surfing on an unattended device
  • Constants: DECRYPT_IDLE_SEC = 120, DECRYPT_SHOW_AT_SEC = 30
Two separate timeout layers: The server-side expiry controls how long the ciphertext exists in the database (1 minute to 7 days). The client-side timers control how long plaintext is visible on screen after decryption (max 120s idle, or 60s hard on the link view). They are independent — a 7-day link still clears the decrypted message from the screen after 120 seconds of inactivity.
8
Server expiry options
60s
Hard link-view timeout
120s
Idle decrypt timeout
1
Max reads (burn mode)

CSP and No-Leak Design

The share page enforces a strict Content Security Policy that blocks inline scripts, inline styles, and event handler attributes. All scripts are loaded as external files with nonces. The connect-src directive limits API calls to api.pqcrypta.com. The frame-ancestors 'none' directive prevents the page from being loaded inside an iframe, protecting against clickjacking. A Referrer-Policy: no-referrer header ensures the share URL (including any cached URL that might contain the fragment) is not sent as a Referer to any external resource.

The decrypted message content is inserted into the DOM using textContent, not innerHTML — eliminating any XSS vector from the message payload. No user-supplied content ever reaches innerHTML, eval(), or any equivalent execution sink.

🔒
Live system — free, no account Create an encrypted message at pqcrypta.com/share/. Open DevTools → Network tab. Observe that the wrap key fragment is never present in any request.
Encrypt & Share Now