mirror of
https://github.com/zap-proto/dev.git
synced 2026-10-30 15:05:18 +00:00
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.
142 lines
7.0 KiB
Plaintext
142 lines
7.0 KiB
Plaintext
---
|
||
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
|