Files
dev/content/docs/post-quantum.mdx
zeekay 4738ffea2f docs: hide Z-Wing, kill fabricated perf, banner spec-only protocols
Red-team review (this session) caught half the protocol pages were AI
slop — describing Go APIs that don't exist in repos that contain only
schemas, plus performance tables contradicted by the only real bench
harness in tree. This is the surgery:

post-quantum:
- Drop the Z-Wing brand from public docs (user feedback: confusing).
- Reframe PQ as ZAP's stack: IETF X-Wing KEM (transport) + hybrid
  Ed25519+ML-DSA-65 (identity) + PQ-RNS (name service). One canonical
  story; layers are independent.
- Per-language status matrix shows realistic state (X-Wing KAT shipped
  in 4 SDKs, sigs in all, resolver in progress).

protocols/{a2a,acp,fix,ws}:
- Marked "Status — spec-only, January 2026". Schema exists; runtime
  doesn't. Removed every Go code snippet that called a non-existent
  function. Replaced with a roadmap section.

protocols/rns:
- Rewrote against the actual schema at zap-proto/rns/schema/zap_rns.zap
  (Record / Query / Response / signed-by-registry).
- Added the PQ-RNS end-to-end flow diagram and the DID definition
  (base32(SHA3-256(kemPk || sigPk))).
- Documents the cross-language KAT fixture published at
  zap-proto/rns/testdata/pqrns_kat.json.

protocols/mcp:
- Rewrote against the actual bridge.go API: NewBridge(*zap.Node),
  AddServer, GetTools, CallTool, CallToolByName. Bridges existing
  stdio MCP servers (it's a bridge, not a from-scratch impl).
- Dropped the invented zmcp.NewServer / Connect / ListTools API.

protocols/http:
- Killed the 6× req/s / 19× p99 / 6.2× RSS table — contradicted by
  bench-results.txt which shows ZAP-HTTP currently ~10% slower.
- Replaced with honest current numbers + the value-prop statement.

protocols/native:
- Killed the 18× / 38× / 6.7× / 9.2× gRPC table — the bench harness
  has no gRPC comparator, the numbers were fabricated.
- Replaced with the actual concurrency=32 numbers from bench-results
  + an explanation that the win is semantic, not currently latency.

transports:
- Killed the @zap-proto/wasm and Cloudflare-Worker code snippets that
  referenced a non-existent npm package.
- Replaced with the honest pattern: browser→TLS→Worker→ZAP-over-X-Wing
  to origin. Points readers at the SDK status table.

app/page.tsx:
- Dropped Z-Wing from the home page cards.
2026-05-27 04:49:24 -07:00

142 lines
7.0 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Post-Quantum
description: ZAP's post-quantum options — IETF X-Wing KEM at the transport, hybrid Ed25519+ML-DSA-65 identities, PQ-bound names via RNS.
---
# Post-Quantum
ZAP is PQ-ready at the wire, the identity, and the name-service layer. This page lays out what's standardized, what's optional, and where to look in the SDKs.
## Layers
```
┌──────────────────────────────────────────────────────────────┐
│ Name service PQ-RNS identity bound to KEM pk │
│ Identity / sig hybrid sigs Ed25519 + ML-DSA-65 │
│ Transport (KEM) IETF X-Wing X25519 + ML-KEM-768 │
│ AEAD ChaCha20-Poly1305 with seq-numbered nonces │
└──────────────────────────────────────────────────────────────┘
```
Each layer is independent. You can pick PQ at the transport without PQ at the name service, or vice versa, or both. Default for the reference SDKs is **all three**.
## KEM — IETF X-Wing
ZAP's wire-level key agreement is **[IETF X-Wing](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/)**: X25519 ⊕ ML-KEM-768, combined via SHA3-256 with a fixed label. The combined shared secret is safe as long as *either* X25519 *or* ML-KEM-768 is. Bit-for-bit interop with any X-Wing peer (same construction Cloudflare, Anthropic, iMessage PQ3 have adopted).
```
shared_secret = SHA3-256( "\./" || "X-Wing" || ss_M || ss_X || ct_X || pk_X )
```
| Input | Bytes | Source |
|---|---|---|
| `"\./"` | 3 | label prefix |
| `"X-Wing"` | 6 | ASCII protocol name (IETF spec) |
| `ss_M` | 32 | ML-KEM-768 shared secret |
| `ss_X` | 32 | X25519 shared secret |
| `ct_X` | 32 | X25519 ephemeral pk from encapsulator |
| `pk_X` | 32 | X25519 static pk of recipient |
### Cross-language conformance
Every SDK ships a KAT (known-answer test) pinning the combiner output for fixed inputs:
```
ss_M = 32 × 0x01
ss_X = 32 × 0x02
ct_X = 32 × 0x03
pk_X = 32 × 0x04
expected SHA3-256 = 72df2088314a73de80c21d9593f13fcd5629c800c70b1507f0dd918fde5fe4ed
```
A Python or TypeScript SDK that disagrees on the label, hash, or input order fails this test immediately — that's how we prevent silent divergence.
## Identity — hybrid signatures
Long-term identities are **hybrid keypairs**: Ed25519 (classical) + ML-DSA-65 (FIPS 204). Both signatures must verify; either-or compromise still fails forgery. This protects identity claims even under a quantum-capable adversary.
```
IdentityPublic (3200 bytes):
[Ed25519 pk: 32][ML-DSA-65 pk: 1952][X-Wing pk: 1216]
```
The X-Wing pk in the identity is the **static** key — used as the recipient pk in the combiner above. Forward secrecy comes from the X25519 ephemeral inside X-Wing.
## Name service — PQ-RNS
RNS (Resource Name Service) resolves names to **identity records** signed by the owning identity itself. The record holds the PQ-bound identity + current endpoints, signed under the identity's hybrid key.
```
record {
name : "acme.payments"
identity : IdentityPublic ← hybrid keypair
endpoints : [zap://...]
signed_at : UnixSeconds
signature : hybrid signature over the canonical encoding
}
```
The resolver doesn't need to be trusted — anyone can verify a returned record. There is no CA, no DNS provider, no registrar in the trust path.
A ZAP client that resolves `acme.payments`:
1. Pulls the signed record from any RNS resolver
2. Verifies the hybrid signature (Ed25519 + ML-DSA-65)
3. Picks an endpoint, dials it
4. The handshake re-uses the identity's X-Wing static key as `pk_X` — the connection is bound to the same identity the record resolved to
Bearer tokens, API keys, and CA-signed certificates never enter the picture.
Full guide: [RNS over ZAP](/docs/protocols/rns).
## Migration paths
| Where you are | What to enable |
|---|---|
| Classical-only legacy | Keep TLS in front of ZAP; opt into PQ-RNS for name service (no transport change) |
| Want PQ transport, keep DNS | Enable X-Wing in your SDK config; keep existing name service |
| Greenfield deployment | All three: X-Wing KEM, hybrid sigs, PQ-RNS — the reference SDK default |
## What's standardized vs. ZAP-specific
| | Standardized | ZAP-specific |
|---|---|---|
| X-Wing KEM | IETF draft-connolly-cfrg-xwing-kem | bit-for-bit |
| ML-KEM | NIST FIPS 203 | bit-for-bit |
| ML-DSA | NIST FIPS 204 | bit-for-bit |
| Composite signatures (Ed25519 + ML-DSA) | Composite-ML-DSA (IETF draft) | bit-for-bit |
| RNS record format | — | canonical CBOR-tagged ZAP schema |
| RNS resolution protocol | — | spec-only today, see [zap-proto/rns](https://github.com/zap-proto/rns) |
ZAP doesn't invent crypto. It composes IETF / NIST primitives in a published, auditable way.
## In the browser & at the edge
| Where | Path |
|---|---|
| Browser | The browser hop is **TLS 1.3** (Chrome / Safari handle the KEM negotiation; recent versions ship X25519MLKEM768). Inside that TLS hop, the application can do PQ-RNS resolution against an HTTP-bridge RNS resolver and pass through to a ZAP origin. End-to-end PQ requires a server-side terminator. |
| Cloudflare Worker / Deno / Bun | V8 isolates can run the WASM build of the SDK once published. Today, point the browser at a server-side terminator and proxy. |
| Lux network (validator ↔ validator) | Native PQ ZAP on TCP — production mandatory, all three layers on. |
| Service ↔ service inside a cluster | Native PQ ZAP — same as above, no TLS in front. |
## Per-language status
| SDK | X-Wing KAT | Hybrid sigs | PQ-RNS resolver |
|---|---|---|---|
| Go ([zap-proto/go](https://github.com/zap-proto/go)) | ✅ | ✅ | reference impl |
| TypeScript ([zap-proto/ts](https://github.com/zap-proto/ts)) | ✅ | ✅ | spec-only |
| Python ([zap-proto/py](https://github.com/zap-proto/py)) | ✅ | ✅ | spec-only |
| Rust ([zap-proto/rust](https://github.com/zap-proto/rust)) | ✅ | ✅ | spec-only |
| C++ ([zap-proto/cpp](https://github.com/zap-proto/cpp)) | ⏳ | ✅ | spec-only |
| Java ([zap-proto/java](https://github.com/zap-proto/java)) | ⏳ | ✅ | spec-only |
| C# ([zap-proto/cs](https://github.com/zap-proto/cs)) | ⏳ | ✅ | spec-only |
"Spec-only" means the schema is published but the resolver / publisher is not yet implemented in that SDK. The KAT vectors and the canonical record encoding are language-neutral — you can implement against the spec without waiting for a per-language client.
## Further reading
- IETF: [draft-connolly-cfrg-xwing-kem](https://datatracker.ietf.org/doc/draft-connolly-cfrg-xwing-kem/)
- NIST: [FIPS 203 (ML-KEM)](https://csrc.nist.gov/pubs/fips/203/final), [FIPS 204 (ML-DSA)](https://csrc.nist.gov/pubs/fips/204/final)
- [Wire spec](/docs/protocol) — handshake byte layout
- [RNS over ZAP](/docs/protocols/rns) — PQ name service detail
- [Architecture](/docs/architecture) — why hybrid by default