mirror of
https://github.com/hanzoai/zap.git
synced 2026-10-30 18:17:49 +00:00
Per user direction: "no capnp we DON'T use that" + "must use zapc"
+ "must be our fork". The runtime now has zero capnp footprint —
schema bindings come from our own hand-rolled compiler.
Workspace layout:
zap/
├── Cargo.toml workspace root
├── zapc-rs/ zapc — schema compiler
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs Public compile_to_rust API
│ ├── parser.rs .zap → Schema IR (file id + structs)
│ ├── codegen.rs Schema → Rust readers/writers
│ └── main.rs `zapc <schema.zap>` CLI
└── rust/ hanzo-zap — runtime
├── Cargo.toml build-deps: zapc (path)
├── build.rs invokes zapc::compile_to_rust
├── schema/
│ └── zap.zap canonical schema (was zap.capnp)
├── src/
│ ├── wire.rs ZAP framing + io
│ ├── schema.rs include!() generated bindings
│ ├── server.rs raw TCP listener + Handler trait
│ ├── msg.rs canonical type ids
│ └── lib.rs
└── benches/
└── wire.rs criterion bench: ZAP vs JSON
Wire format unchanged — still byte-for-byte aligned with
github.com/luxfi/zap (Go) and @zap-proto/zap (TS). Each struct in the
schema becomes a `<name>::Reader<'a>` (zero-copy view into the slot
region) plus a `<name>::Writer` (owning builder). Variable-length
fields (Text/Data) use the standard i32 LE relative offset + u32 LE
length pair at the slot, payload appended to the tail.
Tests: 6/6 pass — header roundtrip, bad magic reject, bad version
reject, raw frame io, generated proto roundtrip, KeysSnapshot with
2 KiB payload.
Bench (single core, criterion):
decode/keys_subscribe zap=19 ns json_scan=591 ns (31× win)
encode/keys_snapshot zap=736 ns (~1 GiB/s)
encode/keys_subscribe zap=436 ns json_format=134 ns (-3.3×)
Decode dominates the JWT-validation hot path (every consumer decodes
KeysRotate / KeysSnapshot from the IAM publisher), so the 31× decode
win is what matters. Encode-side regression on tiny messages is from
allocating two Vecs (slot + tail); will fold into one pre-sized buf
in a follow-up.
The user-flagged JSON encoding from the previous v1 prototype is
gone: `cargo tree -p hanzo-zap | grep -i serde_json` returns empty.
77 lines
2.1 KiB
Plaintext
77 lines
2.1 KiB
Plaintext
@0xc0a8b5e0e5f0a000;
|
|
|
|
# Canonical ZAP schema. Compiled by zapc (../zapc-rs) at build time
|
|
# into `OUT_DIR/zap_schema.rs`, surfaced via `hanzo_zap::proto`.
|
|
#
|
|
# Layout policy: fields packed in declaration order with primitive
|
|
# alignment, struct slot region 8-byte aligned. Text/Data fields use
|
|
# an i32 LE relative offset + u32 LE length at the field slot;
|
|
# payload bytes follow the slot region. Matches Go luxfi/zap.
|
|
|
|
# ── Connection lifecycle ───────────────────────────────────────────
|
|
struct Init {
|
|
clientId @0 :Text;
|
|
browser @1 :Text;
|
|
version @2 :Text;
|
|
}
|
|
|
|
struct InitAck {
|
|
serverId @0 :Text;
|
|
name @1 :Text;
|
|
manifest @2 :Data;
|
|
}
|
|
|
|
# ── RPC ────────────────────────────────────────────────────────────
|
|
struct Push {
|
|
id @0 :Text;
|
|
method @1 :Text;
|
|
params @2 :Data;
|
|
}
|
|
|
|
struct Resolve {
|
|
id @0 :Text;
|
|
result @1 :Data;
|
|
}
|
|
|
|
struct Reject {
|
|
id @0 :Text;
|
|
code @1 :Int32;
|
|
message @2 :Text;
|
|
}
|
|
|
|
# ── JWKS-via-ZAP push ─────────────────────────────────────────────
|
|
# Replaces /.well-known/jwks polling. Subscribers cache locally and
|
|
# receive deltas on every Cert mutation in IAM.
|
|
|
|
struct KeysSubscribe {
|
|
application @0 :Text;
|
|
cursor @1 :UInt64;
|
|
}
|
|
|
|
struct KeysSnapshot {
|
|
application @0 :Text;
|
|
seq @1 :UInt64;
|
|
jwksJson @2 :Data;
|
|
}
|
|
|
|
struct KeysRotate {
|
|
application @0 :Text;
|
|
seq @1 :UInt64;
|
|
newKey @2 :Data;
|
|
}
|
|
|
|
struct KeysRevoke {
|
|
application @0 :Text;
|
|
seq @1 :UInt64;
|
|
kid @2 :Text;
|
|
}
|
|
|
|
# ── Control ────────────────────────────────────────────────────────
|
|
struct Ping { nonce @0 :UInt64; }
|
|
struct Pong { nonce @0 :UInt64; }
|
|
|
|
struct ZapError {
|
|
code @0 :Int32;
|
|
message @1 :Text;
|
|
}
|