ML-KEM-768 decapsulation costs 791 µs of server CPU, and any client triggers it with 1,088 bytes. In one month of 2023 Cloudflare handled 89 separate attacks above 100 million requests per second; Google mitigated one peaking at 398 million. Absorbing that means owning 79,135 cores that idle the rest of the year. Nobody provisions that at any price, so the endpoint goes down.
This layer refuses a request in 1.5 µs instead of 791. The same flood needs 150 cores. A 24-byte token, 2.2% overhead on the ciphertext it gates, no server state, no session memory, no shared secret — and the cost scales with the flood you survive, not the traffic you serve.
The cost of a post-quantum flood is not the compute bill — you cannot provision spare cores in the time it takes to saturate. Legitimate handshakes queue behind attacker ciphertexts and the endpoint stops answering. Constants are the measured 791 µs decapsulation and 1.5 µs check. Presets use published figures: Google mitigated a 398 million rps HTTP/2 Rapid Reset attack in October 2023, and Cloudflare handled 89 separate attacks above 100 million rps in a single month of 2023. Most HTTP floods end inside ten minutes, which is precisely why headroom must already exist.
| Unprotected | With admission layer | Difference | |
|---|---|---|---|
| Cores provisioned to survive the flood | — | — | — |
| Standby capacity, monthly | — | — | — |
| CPU-hours actually burned | — | — | — |
| Consumed compute, monthly | — | — | — |
| Legitimate handshakes still served, 8-core endpoint | — | — | — |
Every filter faces the same problem: cheap for you, expensive for them. Filters that only reject malformed traffic fail the moment an attacker reads the spec and sends well-formed traffic instead. So the token is bound to itself.
Targets derive from a hash of the token:
targets[z] = SHA-256(token ‖ context)[z] mod 6. Change any element and every target moves,
so a token cannot be repaired into validity — it must be found by search. The server records nothing about
who is solving what. Verification is one hash and a few sums, constant regardless of difficulty.
Hashcash-style puzzles step in powers of two, so each rung doubles what honest clients pay. Because ℤ/6ℤ factors as ℤ/2ℤ × ℤ/3ℤ, difficulty is set by a constraints mod 2 and b mod 3, landing on 2a·3b — 190 settings below 107 against 24 for powers of two, and 1.03× mean overshoot versus 1.19× for binary. Running in production: grind tracks 0.693·2a·3b at ratios 0.96–1.34, verification flat at 2.2 µs, random-token admission measured at 1-in-208 against 1-in-216 by design.
The layer is independent of what it protects. It does not modify, wrap or replace your key exchange — it decides which requests reach it. Confidentiality stays ML-KEM-768's job, unmodified, with its cryptanalysis intact. That separation is deliberate: the layer deciding who gets served should never be the layer keeping secrets.
The server holds no per-client state, so there is no table to exhaust and no session to hijack. Tokens bind to an epoch and a per-request nonce, so a solved token cannot be replayed — without that binding an attacker grinds once and floods, and the advantage inverts after 226 replays. Measured, and the reason the binding is mandatory rather than optional.
Admission is a search problem, so Grover applies and each rung is worth half its classical value — a=4,b=4 gives a quantum attacker what a=2,b=2 gives a classical one. Transpiled for ibm_marrakesh at forced layout, the ℤ/6ℤ constraint check costs 580 two-qubit gates against 12,582,912 for the SHA-256 oracle. The oracle is 99.98% hash, so quantum cost rests on a primitive with published resource estimates rather than anything bespoke. Grover at a=4,b=4 needs 1.28×1010 coherent two-qubit operations.
A ℤ/6ℤ alternating chain encodes both logical states at identical Hamming weight, so collective dephasing cancels. Measured on ibm_marrakesh: a two-qubit singlet holds coherence 22.77 µs against 13.45 µs for the triplet — 1.69×. A four-qubit logical qubit outlives an unencoded GHZ by 1.32×, retaining 0.669 at 12 µs where the control has decohered to noise. Roughly 41% of the dephasing is collective and the encoding removes it, with no active error correction.
At optimization_level=3 Qiskit absorbs permutations
into qubit allocation and reports oracle depth 416 against 717 under forced layout. Any resource
estimate not pinning the layout understates cost by ~1.7×. Our earlier hardware fidelity numbers were
inflated by exactly this; the corrected figure is 82–83% for a single depth-21 circuit.
| a | b | difficulty | verify | vs decapsulation |
|---|---|---|---|---|
| 1 | 1 | 6 | 1.636 µs | 484× cheaper |
| 2 | 1 | 12 | 1.516 µs | 522× cheaper |
| 3 | 2 | 72 | 1.610 µs | 491× cheaper |
| 4 | 3 | 432 | 1.591 µs | 497× cheaper |
| 6 | 4 | 5,184 | 1.608 µs | 492× cheaper |
| 8 | 5 | 62,208 | 1.544 µs | 513× cheaper |
1.516–1.636 µs across a 10,368× difficulty range. Raising the price on attackers costs the defender nothing.
| a | b | difficulty | predicted 0.693·d | measured | ratio | client p50 | p90 |
|---|---|---|---|---|---|---|---|
| 2 | 1 | 12 | 8 | 10 | 1.20 | 0.03 ms | 0.12 ms |
| 3 | 2 | 72 | 50 | 50 | 1.00 | 0.15 ms | 0.59 ms |
| 4 | 2 | 144 | 100 | 90 | 0.90 | 0.29 ms | 1.39 ms |
| 3 | 3 | 216 | 150 | 168 | 1.12 | 0.51 ms | 2.16 ms |
| 4 | 3 | 432 | 299 | 267 | 0.89 | 1.02 ms | 3.82 ms |
| 5 | 3 | 864 | 599 | 578 | 0.97 | 2.09 ms | 6.61 ms |
Ratios 0.89–1.20 across a 72× difficulty range. Search cost is the difficulty, with no structural shortcut appearing as the space grows.
| setting | designed | measured | samples |
|---|---|---|---|
| a=2, b=1 | 1 in 12 | 1 in 12 | 200,000 |
| a=3, b=2 | 1 in 72 | 1 in 72 | 200,000 |
| a=3, b=3 | 1 in 216 | 1 in 223 | 200,000 |
Attacker cost per admitted request against the 793 µs it forces. Because the ladder is 3-smooth, every target below has a rung within 3%.
| asymmetry | difficulty needed | nearest rung | honest client p50 |
|---|---|---|---|
| 1× | 227 | 216 (a=3, b=3) | 1 ms |
| 5× | 1,136 | 1,152 (a=7, b=2) | 4 ms |
| 10× | 2,272 | 2,304 (a=8, b=2) | 8 ms |
| 50× | 11,362 | 11,664 (a=4, b=6) | 41 ms |
| 100× | 22,723 | 23,328 (a=5, b=6) | 81 ms |
| 226× | 51,355 | 52,488 (a=3, b=8) | 183 ms |
| ladder | rungs below 10⁷ | worst gap | mean overshoot |
|---|---|---|---|
| powers of 2 (hashcash) | 24 | 2.00× | 1.190× |
| powers of 6 | 9 | 6.00× | 1.606× |
| 2a·3b (ℤ₂ × ℤ₃) | 190 | 2.00× | 1.031× |
Key establishment is ML-KEM-768 (NIST FIPS 203), used unchanged — a lattice KEM, because that is the construction with standing cryptanalysis behind it. The ℤ₆ layer answers a different question: whether a request deserves a decapsulation at all. Each element is a phase in {0,1,2,3,4,5} — no floating point, no noise sampling, no NTT polynomial multiplication.
All operations are mod-6 integer arithmetic. The verifier takes no branches on token contents and checks every constraint even after the first mismatch, so cost is identical for admitted and rejected tokens.
Every element is an integer in {0,1,2,3,4,5}. Group sums are taken mod 2 or mod 3 against targets derived from the token hash — no rounding, no tolerance band, no ambiguous case.
All ephemeral key material is zeroed immediately after use. Private keys, shared secrets, and intermediate buffers are overwritten in a finally block — guaranteed cleanup.
Run a complete key encapsulation cycle in your browser. Client and server generate ℤ₆ key pairs, negotiate a shared secret, and the admission filter validates every step.
Admission is decided once, at the edge, before any asymmetric work begins: one SHA-256 and a handful of integer sums.
Constraint targets come from SHA-256 of the token itself, so altering any element moves every target at once. No local repair exists.
A token failing any constraint returns 403. Random tokens are admitted at exactly the designed rate — measured 1-in-208 against 1-in-216 at a=3,b=3.
No branches on token contents, and every constraint is checked even after the first mismatch. The same ~1.5 µs whether the ladder is set to 12 or 46,656.
The shield adds no per-request latency, no memory allocation, and no additional network round-trips. It runs inline in the Worker's fetch handler.
A standing challenge, with the attack surface published. The moat is an admission filter over 64 elements of ℤ/6ℤ. Under the hash-bound scheme now running on staging, admission targets are derived from the key itself, so no local repair exists and a valid key must be found by search. Break that and the filter falls.
Produce an admissible key for the current epoch in materially fewer than 0.693·6k attempts. Measured medians across k=2..7 track that figure at ratios 0.84–1.30, consistent with search and nothing better. A method that beats it — by exploiting the ℤ/6ℤ constraint structure, the group sums, or the packing — breaks the admission layer.
targets[z] = SHA-256(packed_key ‖ context)[z] mod 6, and the sum of every element i with i mod k == z must equal targets[z] mod 6. Element i belongs to group i mod k. Keys are 64 elements packed 3 bits each into 24 bytes.
GET https://z6-pqc-staging.fogeboro.workers.dev/api/moat-params?k=4 returns the live context string and difficulty. POST /api/moat-admit with {k, key_b64u, context} answers 200 or 403. Staging only — production runs the constant-target moat, which this scheme replaces.
Five strategies have been run against it — brute force, simulated annealing, coordinate solving, meet-in-the-middle and biased sampling. None beat random search. Annealing, the attack that wins whenever a constraint landscape has a gradient, went from 0.77× at k=3 to 1.74× at k=4: as the space grows it falls further behind, which is the signature of a landscape with nothing to climb. Median attempts track 0.693·6k across k=3..7 at ratios 0.84–1.30. The attack harness is published — beat it.
Quick-craft attack vectors to probe the shield:
Paste a base64url-encoded public key (24 bytes) or leave empty to generate random:
Only attack type, danger score, and which checks triggered are stored locally. No key data is ever saved.
Every component is self-contained. No external dependencies, no hardware assumptions, no trusted setup.
Mod-6 arithmetic (add, sub, mul) and continuous-to-discrete phase mapping. Every real angle is snapped to the nearest π/3 step with a bounded deviation.
Each Z6 element fits in 3 bits (values 0-5). 64 elements pack into 24 bytes. Serialization is a single O(n) bit-shift loop — no alloc, no compression.
Client and server exchange ℤ₆ public keys. Shared secret is wrapped with HKDF-SHA256 + AES-256-GCM — context-bound to the server public key. NIST-standard, constant-time, memory-scrubbed.
In the finally block of every request, all Uint8Array and number[] buffers are filled with zero. No ephemeral key material survives past the response.
Every property of the Z6 PQC gateway is verified — arithmetic, packing, admission filter, key agreement, and memory safety.
Make a real request to the Z6 PQC Gateway, verify the admission filter header, and perform a live KEM handshake.
Use the KEM shared secret to derive an AES-256-GCM key and encrypt/decrypt messages. Your Z6 public key serves as the identity.
Generate shared secrets for multiple public keys in a single request (one per line):
ML-KEM-768 key establishment, ℤ₆ admission control, HKDF-SHA256/AES-256-GCM sealing. Deploy to your own Cloudflare account. No third-party APIs.
Live measurements against this production gateway running v4.0.0. Reproducible harness; no modelled or projected figures.
A stateless public endpoint must decide whether to spend a post-quantum decapsulation on an incoming request. The ℤ6 admission check is a keyless integer test over 64 elements, so it runs before any asymmetric work.
| Path | Cost per request | Junk rejected / second |
|---|---|---|
| Reject at ℤ6 admission filter | 0.36 µs | 2,777,778 |
| Reject after ML-KEM decapsulation | 585.39 µs | 1,708 |
| Advantage | 1,626.1× cheaper — 99.94% of CPU avoided per junk request | |
Unstructured key material is admitted at a rate of 1 in 1296. In fairness, any inexpensive pre-filter yields this shape of saving — a keyed MAC would too. What ℤ6 contributes specifically is that the check is structural and keyless: no shared secret, no server-side state, which is what makes it usable on an anonymous stateless endpoint where a MAC is not available.
| Operation | Median | Role |
|---|---|---|
| ℤ6 moat validation (64 elements) | 0.36 µs | admission screening |
| ℤ6 moat-valid key generation | 5.34 µs | client key construction |
| ℤ6 pack / unpack (24 B) | 0.42 µs | wire encoding |
| ML-KEM-768 encapsulate (JS runtime) | 534.25 µs | client key agreement |
| ML-KEM-768 decapsulate (JS runtime) | 585.39 µs | server key agreement |
| HKDF-SHA256 derive | 34.87 µs | key derivation |
| X25519 keygen / ECDH | 34.21 / 33.89 µs | classical baseline |
The JavaScript ML-KEM used in the Worker runtime is roughly 8× slower than the PQClean C reference (57.4 / 63.1 / 77.9 µs). Both are reported.
| Configuration | Bytes on wire | Within 1500 B MTU |
|---|---|---|
| X25519 (classical, pre-quantum) | 64 | Yes |
| ML-KEM-768 full handshake | 2,272 | No |
| ML-KEM-1024 full handshake | 3,136 | No |
| HQC-128 | 6,682 | No |
| Classic McEliece-348864 | 261,216 | No |
| Z6P v4 sealed message (server key cached) | 1,188 | Yes |
Because the 1,184-byte server public key is fetched once and cached, only the ML-KEM ciphertext travels per message. That keeps a sealed message inside a single MTU where a full ML-KEM-768 handshake would fragment.
| Concurrency | Throughput | p50 | p95 | Success |
|---|---|---|---|---|
| sequential | — | 33 ms | 57 ms | 30/30 |
| 10 | 90 req/s | 64 ms | 329 ms | 60/60 |
| 25 | 236 req/s | 84 ms | 225 ms | 150/150 |
| 50 | 278 req/s | 163 ms | 270 ms | 300/300 |
510 of 510 concurrent requests completed with no failures. Throughput is bounded by the measuring client and its network path, not by a datacentre capacity ceiling.
| Property | v3 | v4 (live) |
|---|---|---|
| Shared secret derivable from transcript | Yes — demonstrated | No |
| Key establishment | custom; public key equalled private key | ML-KEM-768 (FIPS 203) |
| Admission filter | advisory headers only | enforced, HTTP 403 |
| Admission-valid key space | 216 keys (7.75 bits) | ~660 (155.1 bits) |
| Context binding | No | Yes — verified |
| Median latency | 36 ms | 33 ms |
Both paths are live on this host and were timed under identical conditions. The legacy route is marginally faster because it performs no real key establishment; its transcript remains derivable offline, which is why it is deprecated rather than recommended. The 3 ms difference is the entire latency cost of adding genuine post-quantum key establishment to this service.
Every KEM below was benchmarked on the same machine with the same method, using the reference C implementations (PQClean) of the NIST standard and alternate schemes. Nobody builds exactly this, so the comparison is against what the industry actually deploys for post-quantum key agreement.
| Scheme | Status | Public key | Transit | Keygen | Encaps | Decaps | Fits MTU |
|---|---|---|---|---|---|---|---|
| X25519 (classical baseline) | RFC 7748 | 32 B | 64 B | 31.1 µs | 32.9 µs | 32.9 µs | Yes |
| ML-KEM-512 | NIST FIPS 203 | 800 B | 1,568 B | 34.7 µs | 39.9 µs | 50.5 µs | No |
| ML-KEM-768 (the deployed choice) | NIST FIPS 203 | 1,184 B | 2,272 B | 57.4 µs | 63.1 µs | 77.9 µs | No |
| ML-KEM-1024 | NIST FIPS 203 | 1,568 B | 3,136 B | 87.2 µs | 93.3 µs | 114.1 µs | No |
| HQC-128 | NIST alternate | 2,249 B | 6,682 B | 1,437 µs | 2,931 µs | 4,778 µs | No |
| Classic McEliece-348864 | NIST alternate | 261,120 B | 261,216 B | 83,947 µs | 105 µs | 18,881 µs | No |
| Z6 lattice KEM | Research — unvetted | 24 B | 160 B | 10.2 µs | 18.3 µs | 7.3 µs | Yes |
Z6 is the smallest and fastest row in the table. That result is real and reproducible — and on its own it does not make Z6 a better choice than ML-KEM. Three reasons:
| Axis | ML-KEM-768 | Z6 lattice KEM |
|---|---|---|
| Security analysis | Multi-year NIST process, public cryptanalysis, security proofs | None. No external review, no proof, no published attack surface |
| Stated security level | NIST Category 3 | 165-bit key space — a keyspace size, not an assessed security level |
| Interoperability | X25519MLKEM768: one hybrid the whole industry converged on | Proprietary. Interoperates with nothing |
| Regulatory standing | FIPS-validated path for the 2030 federal deadlines | Not FIPS, not eligible |
| Deployment | Majority of browser traffic to major CDNs | This gateway |
Why smaller and faster is not automatically better. ML-KEM's 1,184-byte key and its keygen cost buy something specific: module-lattice operations whose hardness has been studied publicly for years. Z6 keygen samples 64 elements and bit-packs them, so it is cheap precisely because it does less mathematical work. A fair reading is that Z6 occupies a different point on the curve — extremely compact and MTU-resident, with an unquantified security margin — not that it dominates ML-KEM.
The interoperability lesson. The TLS ecosystem converged on a single hybrid (X25519MLKEM768) and deployment followed quickly; by mid-2026 post-quantum key agreement covers the majority of browser traffic to Cloudflare, and Executive Order 14412 set federal deadlines of 2030 for post-quantum encryption. IPsec went the other way — vendors shipped proprietary post-quantum key agreements that could not interoperate, and the migration was delayed by years. Z6 is, by construction, in that second category. It is a research protocol exploring what a compact ℤ₆ construction can do, not a drop-in replacement for a standardised KEM.
Method: PQClean reference implementations via pqcrypto, median of 3–300
iterations per operation depending on scheme cost, single machine, same process. Z6 timings are
a local reimplementation of the deployed construction (sample → pack, HKDF-SHA256,
AES-256-GCM); Z6 transit is measured from a live gateway envelope. Sizes for the NIST schemes
are the implementations' own, matching the published parameters. Harness is reproducible.