81 Commits
Author SHA1 Message Date
zeekay 7a60a5282f chore: sync working tree
Commits 2 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 10:06:59 -07:00
zeekay 5a4d32092b chore(deps): bump geth v1.20.1 + luxfi deps — stack unification v1.2.6 2026-07-15 11:12:39 -07:00
zeekayandHanzo Dev ea1506e5c2 builder: value-typed ListBuilder + fix latent zap/v1 breakage
StartList returns ListBuilder by value (mirrors the value-typed ObjectBuilder):
Add*/Finish keep pointer receivers, every caller binds to an addressable local so
Go auto-addresses it. Dropped the dead elemSize field (never read — stride is
implicit in which Add* is called). Byte-identical wire (golden+fuzz+all round-trips
green). NOTE: not a measured alloc win — escape analysis already stack-allocated
the &ListBuilder{} at all 9 call sites (composite stays 583ns/5allocs, the 5 being
mandatory writeEnvelopePrefix pool-egress copies). Value is code-cleanliness +
consistency.

Also fixes zap/v1 subpackage: v1.2.4's ObjectBuilder value change left v1/*.go
referencing *zap.ObjectBuilder → the subpackage didn't build (latent; only the
root package is imported downstream so it went unnoticed). Converted to the value
type. Full module (root + v1) builds + tests green.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.2.5
2026-07-14 14:06:56 -07:00
zeekayandHanzo Dev f9909a847a builder: eager-reserve + value ObjectBuilder — zero-defer, zero OB alloc
Decomplect that unlocks a perf win. SetBytes deferred its data into an offsets
[]offsetEntry slice replayed in Finish — machinery that recorded intentions to
replay later, and heap-allocated a slice per object-with-a-bytes-field. Replace
it with directness: StartObject RESERVES the fixed section eagerly, so SetBytes
appends its tail + patches its own pointer on the spot; Finish just returns the
offset. Removing the offsets field makes ObjectBuilder's methods mutate only
ob.b (through the pointer) and never ob itself — so ObjectBuilder becomes a
VALUE type and StartObject stops heap-allocating &ObjectBuilder per call (it was
65% of composite-build allocs).

Byte-identical wire (golden + fuzz + pool byte-equality + nested-proof suites all
green). Real X-tx wire composite: 922ns/11allocs -> 655ns/5allocs (1.4x, <half
the allocs); vs the original byte-blob 2551ns/37allocs that's 3.9x / 7.4x fewer
allocs. Speeds EVERY zap object build node-wide, not just X.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.2.4
2026-07-14 09:34:28 -07:00
zeekayandHanzo Dev 6f9fb0bfef List.ObjectPtr: typed reader for out-of-line object lists (completes AddObjectPtr)
AddObjectPtr (build a repeated-message field as pointer slots) had no matching
typed reader — callers had to hand-deref the 4-byte signed rel. List.ObjectPtr(i)
is that reader: absOffset = slotPos + int32(rel), same rule as Object.Object,
with the HeaderSize/bounds guards. This is the primitive that lets a tx be built
as ONE native-nested ZAP object instead of byte-blob envelope concat.

nested_tx_proof_test.go: reference design for decomplecting utxo/wire — an
X-chain 2in/2out money-move built as a single nested object (SetObject +
AddObjectPtr out/in lists + inline uint16 fx-discriminator preserving fx
polymorphism). Measured vs the byte-blob envelope pattern it replaces:
  build 1345ns/19allocs -> 291ns/0allocs  (4.6x, ZERO alloc via pooled builder)
  parse 4114ns/26allocs -> 104ns/2allocs  (~40x; 2 allocs are result slices)
Round-trip + pooled byte-identical proven.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.2.3
2026-07-13 07:50:41 -07:00
zeekayandHanzo Dev b4ad489eff builder: zero-copy SetBytes + exported Builder pool (write-path hot fix)
SetBytes was heap-copying every payload (append([]byte(nil), v...)) at every
nesting level — the dominant cost of deep wire composition (X-chain tx build
runs ~11 nested builders). It now retains the caller's slice; the caller keeps
it valid until Finish (every in-tree constructor builds+finishes in one frame).
Byte output is unchanged: Finish writes payloads in the same SetBytes order.

GetBuilder/PutBuilder: exported sync.Pool for Builders (write-side counterpart
to the read bufpool). Reuse is unconditionally byte-safe — ensureField zero-
fills every extended span and align zero-fills padding; proven by
TestBuilderPool_ByteIdentical (dirty pooled builder emits bytes identical to a
fresh one). go.sum: re-record luxfi/pq v1.0.3 (upstream re-tag drifted content).

Micro: sample object build 354ns/9allocs -> 257ns/6allocs; the real win
compounds in utxo/wire + xvm envelope composition (next commits).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.2.2
2026-07-13 03:39:31 -07:00
Hanzo Dev 8a09d8eaf3 zap: locality-adaptive Router (P1) — one Send API, in-process Cost-0 fast path
Decouples WHO (Destination) from HOW (Interface) from the message (Payload):
Send picks the cheapest Interface whose CanReach(dst) is true. When the dst
lives in this binary, InProcessInterface (Cost 0) delivers the LIVE Value to a
local handler — zero serialize, zero copy, zero socket; Encode runs only on a
network hop. NodeInterface adapts the existing *Node wire (unchanged) as the
Cost-10 fallback. Named Router to stay orthogonal to the Transport wire enum
and the transport/ GPU subpackage. First consumer: hanzo/cloud o11y telemetry.

- router.go: Router, Destination, Payload, Interface, LocalHandler, ErrNoRoute
- interface.go: InProcessInterface (Cost 0) + NodeInterface (Cost 10, ErrNotEncodable)
- router_test.go: 7 tests incl. zero-serialize proof (Encode never called in-process)
v1.2.1
2026-07-09 16:48:57 -07:00
zeekayandHanzo Dev 12508651c6 quic: LP-202 PoC — three-class stream demux over one QUIC-PQ conn
Executable core of LP-202 §5.2 (three-class stream demultiplexing) and
§6.1 (post-quantum channel). One QUIC connection carries the unified
transport's three stream classes — 'R' RPC, 'C' consensus-P2P, 'Z'
ZAP-native — each routed to its own dispatcher by the one-byte class
prefix (the only new byte on the wire), and asserts the connection
negotiated X25519MLKEM768 (IANA 0x11EC) on both sides.

Additive test only; exercises luxfi/zap alone, touches nothing in luxd
consensus or proposer VM. Runs green (CGO_ENABLED=0 in this env; the
default-CGO failure is an unrelated accel/SDK-header issue in the dep
graph, not this test).

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
v1.2.0
2026-06-30 20:30:44 -07:00
z 78a4494c9c docs(brand): add hero banner v1.1.0 2026-06-28 20:38:09 -07:00
z 8fb982599b chore(brand): dynamic hero banner 2026-06-28 20:38:08 -07:00
Hanzo AI 690b4aa95c test: use neutral fixtures in zap roundtrip tests v1.0.1 2026-06-24 08:31:30 -07:00
zeekay 3f54070efc zapgen: -skip-registry flag (private per-service kind namespace)
Sets Schema.SkipRegistry so a per-service schema set (e.g. Hanzo S3's
Get/PutObject kinds) does NOT register into the shared zapv1.DefaultRegistry —
their kind bytes are unique only within the service, not globally. Needed for
the ZAP service-mesh pattern where every service owns a private kind space.
2026-06-23 16:56:02 -07:00
zeekay 1939302660 v1/codegen: emit REPEATED nested objects (repeated message, zero-copy)
The repeated-message wire shape: a list whose elements carry their own
variable-length tails (string/bytes/nested), which the fixed-stride inline
List cannot hold. Completes proto field-shape coverage in the runtime —
map<k,v> reduces to a repeated {key,value} entry, so this is its keystone too.

Composition, not a new wire concept: ListNested = List ∘ NestedAt. The list
slots are a contiguous stride-4 object-pointer array; each pointer is
followed exactly as a singular nested pointer to an out-of-line tailed View.

Runtime:
- builder.go: ListBuilder.AddObjectPtr(targetPos) — appends a signed 4-byte
  relative object pointer (the repeated-message element kind).
- v1/list_nested.go: ListNested[N] (Len/At/All/IsZero), ListNestedAt[S,N]
  (read), WriteListNested[S,N] + NestedElemSetter[N] (write). Same audited
  bounds discipline as Object.Object / ListStride (RED-HIGH-1/2 inherited).

Codegen:
- elemIsTailed: a list element with a string/bytes/nested/list sub-field
  auto-routes to ListNested/WriteListNested/ListNestedAt; scalar-only
  elements stay in the tighter inline List/WriteList/ListAt. One Elem
  declaration, the generator picks the correct encoding.

Proven end-to-end: runtime roundtrip (3 elements each with its OWN string
tail, via At and All, + empty), emit routing test (tailed->ListNested,
scalar-only->List), and a generated-code wire roundtrip (testpkg/repeatwire).
Root zap + full v1+codegen suite green; scalar listwire unchanged (no regression).
2026-06-23 16:38:05 -07:00
zeekay 6d92097dde v1/codegen: emit singular NESTED object fields (zero-copy, typed)
Completes the variable-length codegen (strings/bytes/lists/nested) — the
last shape needed before protoc-gen-zap can map every proto message field
mechanically.

Runtime (v1/nested.go):
- NestedAt[S,N]: read an out-of-line 4-byte object pointer as a typed
  View[N]; null/OOB -> zero View (proto3 unset).
- WriteNested[S,N]: build a flat nested object into the parent tail and
  patch the pointer; the singular peer of WriteList. Composes recursively
  (nested may carry scalars, strings, bytes, lists, further nesting).

Codegen (schema.go, emit.go):
- Field.Nested *NestedMsg (4-byte object pointer, distinct from the 8-byte
  list/tail pointer); IsNested().
- Constructor takes *Value (nil => null), nil-guarded WriteNested build.
- emitNestedSupport: value struct + NestedAt read accessor.
- emitElemWrites: shared scalar/string/bytes element-write body, so list
  elements now also support string/bytes sub-fields (repeated-of-message).
- ListElem/NestedMsg carry Wire (element WireName) for Offset<Wire>_<F>.

Proven end-to-end: runtime roundtrip (nested w/ own string tail + null
case), generator-output tests (go/parser-valid), and a generated-code wire
roundtrip (testpkg/nestwire). Full v1+codegen suite green, no regression.
2026-06-23 16:28:51 -07:00
zeekay 4acd5cf4da v1/codegen: emit variable-length LIST fields (zero-copy, typed)
Completes the variable-length codegen (strings/bytes already shipped; lists now
join them). A list field of a fixed-size element schema generates: a per-element
value-input struct, a typed constructor param []Elem, an inline WriteList build
(bridged onto the hand-rolled ObjectBuilder by the new zapv1.SetterFrom, one
zapv1.Write per element field), and a zero-copy ListAt read accessor returning
zapv1.List[E] (range-over-func ready) — mechanically reproducing examples/
batch_tx.go.

Adds: Schema.Element (flat list-element slots carry no kind byte — fields may
start at offset 0; Wrap skips the kind check); runtime helpers SetterFrom /
WriteString / WriteBytes bridging the hand-rolled fast path to the typed writers.

Proven end-to-end: testpkg/listwire round-trips a BatchTx{scalar + []Item}
through build -> wrap -> ListAt.All() with every element read zero-copy
(TestRoundTrip_List), plus generator-output tests (valid Go via go/parser +
construct assertions). Existing golden tests unchanged.
2026-06-23 15:46:50 -07:00
zeekay e457893743 rename v2 -> v1: the typed-view API + codegen is the canonical 1.0 (no v2, ever)
There is one perfected typed ZAP layer and it lives at github.com/luxfi/zap/v1 —
forwards-only, no backwards-compat baggage, never a breaking major. Mechanical,
behavior-preserving: v2/ -> v1/ (git mv), package zapv2 -> zapv1, import path
.../zap/v2 -> .../zap/v1 (incl. the codegen's emitted banner + import so
generated files reference /v1). Codegen golden tests pass; typed layer vets
clean. (Pre-existing CGO 'resolv' link failures in the root package are
unrelated env noise.)
v1.0.0
2026-06-23 15:05:15 -07:00
zeekay 634ed2379a v2/codegen: emit variable-length string/bytes fields (zero-copy)
Field.Type now accepts "string" and "bytes" (no <N>) for variable-length
tail fields alongside the existing scalar + fixed "bytes<N>" kinds. They take
an 8-byte tail pointer in the fixed payload; the constructor emits
SetText/SetBytes and a standalone zero-copy accessor over Object.Text/Bytes.
Purely additive — existing fixed-scalar/byte-array schemas unchanged.

Tests: new TestEmit_VarLength + all existing green (TestEmit_UnsupportedType
retargeted to 'complex128' since 'string' is now valid). Generated RemoteStorage
Location (3 strings + int32) verified downstream: 44 ns/op, 0 allocs/op reads.

This is what Hanzo S3 needs to define its messages as ZAP schemas (no protobuf,
no _pb, no marshal).
2026-06-23 14:35:58 -07:00
zeekay 0a1626d91d dexsession: scope fee-on-transfer id divergence to the IntentSubmitted topic + cross-repo id golden (swap-seam r2)
FEE-ON-TRANSFER id divergence (LOW/INFO #2c). On-chain DeriveIntentID binds the OBSERVED-DELTA
locked amount (the value the 0x9999 vault actually received), not the requested amount. For
native LUX + standard ERC-20s, locked == amountIn so this off-chain pre-derivation equals the
on-chain id exactly. For a FEE-ON-TRANSFER token, locked < amountIn so the pre-derived id
DIVERGES — but that is only a missed pre-registration optimization, not a safety or correlation
break: the on-chain IntentSubmitted event carries the REAL id as its INDEXED topic (and the real
locked amount), so a keeper correlates via that topic — the authoritative binding — and builds
the D order from the event, never a guessed id. Scoped the DeriveIntentID doc to state this
precisely (pre-registration valid iff locked == amountIn; otherwise correlate via the indexed
topic).

CROSS-REPO id golden (LOW/INFO #3b). Added TestIntentID_OffChainEqualsOnChain: the off-chain
DeriveIntentID over fixed inputs must hash to a shared golden the precompile side ALSO pins
(its TestIntentID_CrossRepoGolden, native_intentid_parity_test). dexsession cannot import
precompile/dex (EVM+geth+cgo + a forbidden compile edge to C Verify), so the two id
implementations are anchored to ONE golden — either drifting fails its half. Closes the missing
cross-repo equivalence test.

CGO_ENABLED=0 pure-Go (GOWORK=off); full dexsession suite green.
v0.8.11 v0.8.10 v0.8.12
2026-06-21 22:43:29 -07:00
zeekay 9b0c85bc80 dexsession: settlement byte-identity (intentID word) + scope route off the on-chain hookData (swap-seam r2)
THE SETTLE CALLDATA REVERTED (byte-identity). EncodeSettlementHookData emitted a 64-byte
Phase-B body (outputID|amount), but the precompile's decodeSettlementBody requires 96
(outputID|amount|intentID) — the intentID word binds the credit to the taker's intent record
(the per-taker cap + deadline gate). So every settle tx built off this calldata REVERTED
on-chain (ErrSettleBodyMalformed). Add the intentID word so the body is byte-identical with
precompile/dex/settle_hookdata.go; settlementBodyLen is now 96. The DExportRef already carries
IntentID, so the production importSettlement path and every ResolveExport supplies it.
calldata_parity_test pins the full 96-byte body against a hardcoded vector.

THE MULTI-HOP ROUTE REVERTED (RT01 scoping). prepareRouteLocal signed an on-chain hookData of
DI01|RT01|hopCount|path. The precompile has NO route-marker awareness: it classifies the DI01
tag as a Phase-A intent, then decodeIntentBody rejects the RT01 body width (not in {0,32,64})
-> every multi-hop swap REVERTED on-chain. Fix by scoping the route OFF the on-chain calldata:
the on-chain leg is now a PLAIN DI01 intent on the entry market (nonce=CallIndex, matching the
id this session derives — DeriveIntentID binds the entry market + nonce, NOT the path, so the
id is unchanged). The PATH travels to the D router over the ZAP control plane (the RouteRequest
envelope, buildRouteRequest/NotifyRoute), where it belongs — it is D-matcher orchestration that
moves no C-side value (one input object, one final output object regardless of hop count).
Removed EncodeRouteIntentHookData/DecodeRouteIntentHookData (the on-chain RT01 wire the
precompile never supported — a bug generator); the path's one canonical wire is the
RouteRequest envelope. Reworked the route parity test to pin the new property (on-chain route
leg is a precompile-accepted DI01 body carrying no RT01/DS01 tag) and the multi-hop e2e test to
read the path from the session (control plane), not the signed calldata.

CGO_ENABLED=0 pure-Go (GOWORK=off); full dexsession suite green.
2026-06-21 22:39:02 -07:00
zeekay 1c4e7947f1 dexsession: chain-observable intent id (off-chain == on-chain) for watch correlation
The off-chain side derived the intent id with a ZERO txID (pre-landing) while the chain
used the REAL txID, so the ids never matched and the keeper's watch could not correlate
against a live chain (the swap-seam watch-correlation break). FIX, byte-identical to
precompile/dex: DeriveIntentID drops txID+callIndex and binds a NONCE instead
(networkID,cChainID,dChainID,account,assetIn,amountIn,marketID,nonce). The nonce is the
taker's disambiguator carried in the swap's DI01 hookData; EncodeIntentHookData now emits
deadline[32]|nonce[32] (minimal-width, matching the precompile decoder), so off-chain and
on-chain derive the IDENTICAL id from the IDENTICAL calldata.

- SwapIntentRequest.CallIndex -> Nonce (uint64); wire reflowed (sirNonce uint64).
- session.go/server.go/v4session.go derive the id without txID; the swap prepare carries
  (deadline, nonce) in hookData. v4 LP/route paths pass their CallIndex as the nonce.
- TestParity_DeriveIntentID / TestParity_VectorPin updated to the new canonical algorithm
  (NO txID; nonce trails marketID) — the cross-repo byte-identity contract holds.

Full dexsession suite green (GOWORK=off, CGO_ENABLED=0). Pairs with precompile
fix/swap-rail-intent-record (DeriveIntentID nonce) — keep the two byte-identical.
2026-06-21 19:51:56 -07:00
zeekay ca9d679795 zap/dexsession: set retag route via FinishWithFlags, drop [6:8] byte patch
The retag path hand-patched the ZAP frame flag word at fixed header offset
[6:8] — the sole deviation from the FinishWithFlags idiom used everywhere
else, brittle against any wire-layout change. buildPreparedIntent now takes
the msgType and finalizes under it directly, so NotifyIntent builds an
MsgNotify frame in one step instead of building MsgPrepare then re-tagging
finished bytes. retag() is deleted (no other callers).

TestWire_PreparedIntent_RouteFlags asserts the route flag (Flags()>>8) round-
trips for both MsgPrepare and MsgNotify and the body is untouched by the route
choice. Full dexsession suite green.
2026-06-21 19:03:16 -07:00
zeekay d99447b95a merge: ZAP DexSession V4 bidirectional control plane (v0.8.9) v0.8.9 2026-06-21 07:27:08 -07:00
zeekay ef4f89000f dexsession: V4 bidirectional session model (the uncommitted extension)
Commits the V4 session files that were built + red-verified in the working tree
but never git-added (only v4route.go landed in 9719fcc, so that commit did not
compile). Adds V4Swap/Route/Liquidity/Collect/State sessions + the bidirectional
V4_* message set + per-action scoped caps, wired into server/wire. Orchestration
only — no compile edge to the value path; money-plane invariant intact.
2026-06-21 07:26:30 -07:00
zeekay 9719fcc65d dexsession: harden route reader (Red-ZAP L1+L2)
L1: bound hopCount via division (len/32) not hopCount*32 — overflow-safe even on
a 32-bit int build (latent; deploy targets are 64-bit).
L2: zero RouteStatus.Ref unless phase is terminal (Committed/Refunded) in the
direct Poll() reader, mirroring streamRoute's gate — orchestration-surface
hygiene (the chain re-binds on import regardless). Non-money-plane hardening.
2026-06-21 02:21:14 -07:00
zeekay 6b96ea6691 dexsession: ZAP/Cap'n-Proto orchestration for the DEX (capability transport + promise pipelining)
Orchestration ONLY — ZAP is NEVER settlement authority. A ZAP response is never
sufficient to move money; money moves only when C consumes a real D->C atomic
export object or D consumes a real C->D object. The layer holds no StateDB/
AtomicState/shared-memory and has no compile edge to precompile/dex or the EVM:
it cannot be imported into the C Verify path, and Verify does not import it.

- DexSession capabilities (Cap'n-Proto): bootstrap -> restricted session;
  unforgeable, grant-scoped QuoteCap/IntentCap/WatchCap/SettlementCap/AdminCap.
  No cap exposes a value-moving method (proof by exhaustion).
- prepareSwapIntent returns 0x9999 CALLDATA the user signs (no funds reserved,
  no enforceable amountOut). notifyIntent triggers a D scan (cannot validate a
  match without a D block). importSettlement points at a DExportRef pointer; the
  chain binds recipient/asset/amount to the real object.
- Wire format + DeriveIntentID/DeriveUTXOID + V4 swap selector + hookData phase
  tags reproduced as pure values (no EVM/cgo dep) and pinned byte-identical to
  precompile/dex + chains/dexvm by parity tests (three-homes pattern).
- Promise pipelining (thenAsync + SwapFlow) overlaps network latency end-to-end.
- 26 tests incl. TestRED_ZAP_ResponseAloneCannotMoveMoney. CGO_ENABLED=0, pure Go.
2026-06-21 00:56:09 -07:00
hanzo-dev 32407342ec reconcile: carry MCP<->ZAP bridge onto refactored main
WIP main was an orphaned old base; remote main is the canonical refactor and
already contains the WIP intent, advanced further: PQ handshake (bigger
handshake/), the full transport/ suite WITH C kernels (dpdk_pmd/gpudirect_rdma/
uma_cuda) and QUIC (quic-go in go.mod), plus larger node.go/zap.go. All 15
modified source files + go.mod/go.sum in the WIP were the older base and are
superseded (accept remote). LLM.md/MIGRATION.md: remote canonical, no unique
local notes to fold. The 416 mdns/rust/target/* build artifacts the WIP had
committed are dropped (compiled output, never committed).

Genuinely-new, ported: mcp/ package (MCP<->ZAP bridge for fast tool calling),
absent from remote. Builds clean against the refactored zap API (go build
./... OK).

Full WIP preserved on branch wip/local + tag backup/pre-reconcile-main.
2026-06-20 18:37:21 +00:00
zeekay 2ba428c21b fix(forward+node): canonical HTTP-over-ZAP security hardening (v0.8.8)
V4 CRITICAL: forward.handle no longer panics on hostile method/path
  (validForwardLine→400 + http.NewRequestWithContext); node.safeHandle adds a
  recover() boundary across all 5 dispatch sites (TCP dispatchLoop x2,
  getOrConnect, QUIC handleCallStream + invokeHandlerOneWay) — a handler panic
  drops one conn, never crashes the node. Proven on TCP+QUIC.
V2 HIGH: forward/identity.go single-source strip set (superset of base's
  StripIdentityHeaders + Cookie drop + X-Gateway-* sweep), applied on BOTH legs
  (serve.go backend + client.go gateway); Authorization preserved.
V6: SSE responses → 501 (buffered-stream guard).
+ consolidation: zap_crosswire_test.go proves byte-identical wire vs zap-proto/go;
  MIGRATION.md roadmap.
Red-reviewed SHIP (0 crit/high/med). Zero exported-API change — source-compatible.
v0.8.8
2026-06-18 13:39:56 -07:00
zeekay 6dd965f755 feat(forward): canonical HTTP-over-ZAP contract (Forward/Response/Push + Serve/Relay/Forwarder)
HIP-0110 single source of truth for the ingress→gateway→service ZAP path.
- Forward (identity+req), Response (status/body/headers), Push (SSE stream)
- Serve: backend terminal (ZAP→http.Handler, identity injected as X-* headers)
- Relay: gateway ZAP→ZAP (envelope-only auth/billing Gate, body never deserialized)
- Forwarder: ingress HTTP→ZAP client (http.RoundTripper)
- Fixes the 8-byte Object slot encoding (zap_backend.go's 4-byte offsets overlapped)
14 tests, race-clean, CGO_ENABLED=0.
v0.8.7
2026-06-18 11:16:25 -07:00
Antje WorringandHanzo Dev 1707a8e99a docs: tidy LLM.md indexes; CLAUDE.md -> LLM.md symlink convention
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-17 09:54:23 -07:00
zeekay c8e36d0448 zapclient: fix native-TCP Client→Server path (opcode routing + static dial)
Two defects made the canonical zapclient native-TCP path non-functional
(undetected — no test did a real Client→Server TCP round-trip; zapweb
works via Dispatch, inter-shard uses raw zap.Node):

1. Server.Register bound the node handler under the full opcode, but the
   Node routes inbound by Flags()>>8 (the high byte). Mismatch → handler
   never found → call hung. Register now uses op>>8; dispatch still reads
   the full Flags() to select the procedure.
2. Connect with a static Discovery set the node NoDiscovery, so it never
   learned peer addresses and could not dial. Added WithStaticPeers + a
   static Discovery, ConnectDirectID (returns the handshake-learned
   NodeID), and client-side dial-by-address (memoized) so static peers
   (placeholder NodeID + real Address — the cloud/K8s Service-DNS path)
   are reachable. mDNS path unchanged.

Adds TestNativeTCPRoundTrip as the regression guard.
v0.8.6
2026-06-15 23:36:04 -07:00
zeekay 3f0c0973e0 node: make mDNS discovery best-effort, not fatal to serving
Start() closed the listener and failed the whole node when mDNS
discovery couldn't start. But mDNS is zero-config LAN convenience, not a
serving requirement — on networks without multicast (most Kubernetes pod
overlays, restricted hosts) the node must keep accepting on its listener;
peers reach it by address (Service DNS at the fixed port). Now a
discovery-start failure logs a warning and continues. This makes 'mDNS on
by default' safe everywhere: LAN gets zero-config discovery, cloud serves
on the fixed port regardless.
v0.8.5
2026-06-15 17:41:49 -07:00
zeekay dc10cb6582 zapweb/ts: faithful binary ZAP codec + browser client (@zap-proto/web)
The browser half of one protocol. A TypeScript port of the ZAP wire
codec (zap.go + builder.go) byte-identical to Go — proven by golden
vectors emitted from golden_test.go (12/12 encode+decode) — plus a
zapweb WebSocket client mirroring client.go (opcode stamp, [reqID][flag]
correlation), validated by a live TS-client to Go-echoserver round-trip
(cmd/echoserver). procedureOpcode matches Go FNV-1a. Zero runtime deps:
browser-global WebSocket, Node native type-stripping for tests.
v0.8.4
2026-06-15 13:32:13 -07:00
zeekay 4ad4ffba06 zapweb: ZAP-over-WebSocket transport for browser clients
Browsers cannot open raw TCP/QUIC, so the external surface could not be
native ZAP. zapweb bridges a WebSocket to zapclient.Server.Dispatch — the
exact same procedure registry + PeerVerifier the native node uses. One
dispatch path, two transports (native ZAP service↔service, zapweb
browser↔service). No REST. Adds the dependency-free coder/websocket.
Server.Dispatch is the new exported, transport-agnostic entry point.
v0.8.3
2026-06-15 12:07:37 -07:00
zeekay 685224b8f6 zapclient: add WithServerPort to complete the latent ServerOptions.Port
ServerOptions.Port existed but had no functional-option setter, so every
zapclient.Server bound an ephemeral port reachable only via mDNS — which
is not forwarded across most Kubernetes pod overlays. WithServerPort lets
a server bind a fixed port reachable by Service DNS (the production
discovery model). Pairs with WithNoDiscovery.
v0.8.2
2026-06-15 01:23:02 -07:00
Hanzo AIandzeekay 9ef9e81959 go.mod: bump go directive 1.26.3 → 1.26.4
Patch fix: crypto/x509, mime, net/textproto security fixes per Go 1.26.4 release notes (2026-06-02). Also compiler, runtime, go fix, crypto/fips140 bug fixes.
2026-06-10 22:33:38 -07:00
Antje WorringandHanzo Dev ac14d09067 chore(zap): remove unused mcp bridge package
package mcp (an MCP-over-ZAP tool-calling bridge from the initial commit) has zero importers anywhere in the tree and its Bridge is never instantiated, so the handlers never register. Dead code in a core wire library; remove it rather than carry/harden an unreachable path.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-06 19:03:04 -07:00
Antje WorringandHanzo Dev 9b69731086 fix(handshake): enforce HandshakeTimeoutSec read deadline (pre-auth slowloris)
HandshakeTimeoutSec was defined but referenced nowhere and no SetReadDeadline existed, so a peer could stall the pre-auth handshake indefinitely (goroutine/FD exhaustion). Initiator.Run and Responder.Run now set a read deadline of HandshakeTimeoutSec when conn is a net.Conn, cleared on return; bare io.ReadWriter callers remain responsible for their own timeout.

Co-authored-by: Hanzo Dev <dev@hanzo.ai>
2026-06-06 18:05:41 -07:00
Hanzo AI 048b6c91f5 zap: revert codec subpackage — ZAP is pure transport, no codec
ZAP is the wire protocol (FIX, HTTPS, WS, QUIC, X-Wing adaptors) — pure
bytes in / bytes out. Internal serialization is not its concern.

The codec subpackage was added in v0.8.0 by a misguided relocation of
luxfi/codec; revert. Application-level struct serialization belongs in
each VM (hand-rolled like luxfi/net's binio.go pattern), not in ZAP.
2026-06-06 15:49:32 -07:00
Hanzo AI 3fa1fd2dd6 zap: absorb luxfi/codec → zap/codec subpackage (canonical ZAP wire codec home)
Move the entire luxfi/codec module content into luxfi/zap/codec as the
canonical home for the ZAP wire codec. luxfi/codec module is now
redundant and queued for archival; consumers should import
github.com/luxfi/zap/codec directly.

This decomplects the alias chain proto/zap_codec → codec/zapcodec into
one canonical home. The 'codec' name lives because the package IS the
wire codec; 'zap' is its namespace because ZAP is the wire it speaks.

Contents:
  - codec.go, errors.go, packing.go (root codec.Manager + helpers)
  - codecmock/  (go.uber.org/mock generated Manager mock)
  - linearcodec/ (big-endian linear codec, legacy compat)
  - reflectcodec/ (struct fielder + reflection type codec)
  - wrappers/ (packer/closer wrappers)
  - zapcodec/ (little-endian ZAP-native codec, sourced from the
    cleaned luxfi/zapcodec@v1.0.1 extraction)

All import paths rewritten github.com/luxfi/codec/* and
github.com/luxfi/zapcodec → github.com/luxfi/zap/codec/*.

Build: GOWORK=off go build ./codec/...       — PASS
Tests: GOWORK=off go test ./codec/...        — PASS (wrappers, zapcodec)
2026-06-06 15:42:29 -07:00
Hanzo AI 62ec03f8a3 WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:34:05 -07:00
Hanzo AI f42cdb6618 feat(zap): v2 generic API, bufpool, transport allocator + UMA/RDMA cgo
Three orthogonal additions, one wire format.

v2 package — generic ad-hoc schema dispatch (~/work/lux/zap/v2):
- README + IMPOSSIBILITY + BENCH_RESULTS document the v1↔v2 split.
- builder/field/iter/list/pool/registry/schema/view + codegen package
  emit v1-equivalent fast paths from declarative .zap sources.
- _compile_fail_test/ + examples/ cover negative and positive cases.
- v2_test.go exercises the generic dispatch path.
- doc.go in v1 marks v1 deprecated for *new* schema authoring; existing
  v1 buffers stay parseable by both v1 and v2.

bufpool — pooled read slabs (bufpool.go + bufpool_test.go):
- Message gains optional *bufRef; Release() returns the slab to its
  pool when sourced from the pooled read path, GC-managed otherwise.
- ParseHeader returns (data, rootOffset) without allocating *Message,
  used by zapv2 to avoid two allocations per parse.
- bench_read_test.go + node_quic_stream_test.go cover both paths.

Transport allocator + UMA/RDMA cgo (transport/*):
- Transport.Caps()/Buffer interface: capability + slab-typed buffer
  contract. Bytes() vs DevicePtr() decoupled so UMA and GPUDirect
  can share the same call surface.
- DPDK PMD + GPUDirect RDMA + CUDA UMA real cgo backends gated behind
  build tags; stub paths remain default on darwin / non-Linux.
- transport_test.go covers Caps reporting + Buffer lifecycle on the
  default transport.

LLM.md + go.mod follow the additions.
2026-06-04 17:06:59 -07:00
Hanzo DevandGitHub 91e197dc53 Merge pull request #5 from abhicris/test/2026-06-01-composite-fuzz
test: add composite + schema + opcode fuzz targets
2026-06-03 13:23:10 -07:00
Hanzo AI 3fac1c82bf feat(zap): v0.7.2 — Object.ListStride per-stride clamp + List.Len SAFETY doc + FuzzParse
LP-023 Red round 3 follow-ups:

NEW-V1 follow-up: tighter per-element stride clamp via Object.ListStride.
The bare Object.List uses the permissive `length <= len(data)` baseline
(wire layer cannot know per-accessor stride). Callers that know the
element width can opt into the tighter `length * minStride <= bufRem`
clamp via ListStride(off, stride), rejecting poisoned length values up
front instead of relying on per-element bounds checks.

NEW-V1 docstring: List.Len() now carries the SAFETY caveat — callers MUST
NOT pre-allocate via make([]T, l.Len()) without an independent bound.

FuzzParse (Red follow-up #3): round-trip property fuzzer pinning the
Parse↔Bytes contract: msg.Bytes() == data[:msg.Size()], Parse is
idempotent on its own output, Version() always in {Version1, Version2}.
1M+ execs clean on M1 Max with 20s fuzztime; seed corpus includes
adversarial buffers that exercised RED-HIGH-1/2/3.

Tests:
- TestNewV1_ListStrideTighterClamp — poisoned length=100 passes bare
  baseline but ListStride(0, 4) rejects (400 > bufRem)
- TestNewV1_ListStrideAcceptsHonestLength — honest length=5 stride=4
  buffer must pass

All existing tests still pass (RedRound2 HIGH-1/2/3, MEDIUM-1, V18 + base).

Wire format unchanged — purely a tightened acceptance test; backward
compatible at the wire level. Callers update obj.List(off) →
obj.ListStride(off, minStride) per element type.
2026-06-02 15:31:41 -07:00
Hanzo AI 9d86b95754 fix(zap): close LP-023 v3.1 Red round 2 wire gaps (HIGH-1, HIGH-2, MEDIUM-1)
RED-HIGH-1: Object.List length is now clamped against len(data) at parse.
Attacker-set length=0xFFFFFFFF used to flow through to consumers, where
`for i := 0; i < l.Len()` would iterate 4G times.

RED-HIGH-2: Object.Object, Object.List, and Object.Bytes now reject any
absOffset < HeaderSize. A negative relOffset can no longer alias the wire
header (Magic/Version/Flags/RootOffset/Size). The signed-cast remains so
honest builders can finalize nested items before their parent.

RED-MEDIUM-1: bump wire Version from 1 to 2 (Version1 still accepted at
Parse for backward compat). NewBuilder emits Version2 by default;
NewBuilderV1 is kept for explicit-legacy paths. Message.Version() exposes
the parsed version so v3 platformvm Wrap*Tx accessors can gate on
Version2 to reject v1-vs-v2 schema confusion (e.g. NetworkID=11 byte 0
collision with TxKindBaseFull).

RED-V18: Parse now rejects size < HeaderSize. A buffer with size=0
previously slipped through and panicked on subsequent Root()/Flags().

Regression tests pinned in-tree at zap_test.go:
  TestRedRound2_HIGH1_UncappedListLength
  TestRedRound2_HIGH2_BackwardListPointer
  TestRedRound2_HIGH2_BackwardObjectPointer
  TestRedRound2_HIGH2_BackwardBytesPointer
  TestRedRound2_MEDIUM1_VersionParse
  TestRedRound2_MEDIUM1_NewBuilderEmitsV2
  TestRedRound2_V18_SizeZeroRejected
  TestRedRound2_F1_NegativeBitPatternSweep

Originals at /tmp/red_zap_attacks/*_test.go.
2026-06-02 15:11:11 -07:00
Hanzo AI 98340e220d fix(zap): reject negative relOffset in Object.Bytes (F1)
Object.Bytes was casting the relative offset to int32 before bounds
checking. A bit-pattern with the high bit set (e.g. 0xFFFFFFE0)
sign-extended to -32, slipped past the absPos+length > size guard, and
let a Bytes accessor alias bytes BACK inside the same object's fixed
section. Once a TxID = hash(buffer) lands, two distinct buffers (legit
vs aliased-Memo) hash differently while decoding to identical logical
content — classic transaction malleability.

Fix: drop the int32 cast in Object.Bytes. SetBytes always defers
payload writing to ObjectBuilder.Finish, which writes after the fixed
section — Bytes relOffsets are always forward-pointing. Switching to
uint32→int conversion routes negative bit-patterns through the size
bounds check as ~4 GiB positions, which the guard now rejects.

Object.Object and Object.List keep the signed-int32 decoding: builders
may finalize a nested object/list BEFORE the outer object (TestList,
TestNestedObject exercise exactly this), so legitimate negative
relOffsets exist for those types.

Regression tests:
  TestBytesNegativeRelOffsetRejected (0xFFFFFFE0)
  TestBytesMaxUintRelOffsetRejected (0xFFFFFFFF)

Coordinates with luxfi/node LP-023 Phase 1 batch 2 Red review (F1).
2026-06-02 13:56:51 -07:00
Hanzo AI 3530d8a4ec Merge github.com:luxfi/zap 2026-06-02 11:39:20 -07:00
Hanzo AI 7148d32d20 fix: gofmt -s across repo (CI format check) 2026-06-02 11:38:38 -07:00
Hanzo AI 60596c1474 chore(deps): go get -u ./...
Updated dependencies to latest minor versions via go get -u + go mod tidy.
2026-06-02 11:09:25 -07:00
Hanzo DevandGitHub c1af0458cc feat(quic): ZAP transport with X25519MLKEM768 PQ-TLS (#6)
New package github.com/luxfi/zap/quic provides the canonical QUIC
transport for the ZAP messaging substrate.

Wire format is identical to the TCP transport (length-prefixed
Cap'n Proto frames), so every consumer's wire-level expectations
are preserved.

What QUIC adds beyond TCP+TLS:

  - Multiplexed bidi/uni streams (one ZAP RPC per QUIC stream,
    no head-of-line blocking between RPCs)
  - Connection migration on local-IP changes (Wi-Fi to LTE,
    NAT rebind, validator re-homing)
  - 0-RTT resumption via TLS 1.3 session tickets
  - TLS 1.3 hybrid post-quantum key exchange X25519MLKEM768
    (IANA NamedGroup 0x11ec) as the default
  - ALPN allowlist 'zap/1'

Cryptography:

  - We use the IANA-registered hybrid X25519MLKEM768 verbatim;
    no custom combiner, no z-wing fork. Go stdlib's crypto/tls
    performs HKDF-Extract(X25519_ss || MLKEM_ss) internally.
  - Server cert sig stays classical (ECDSA / RSA) — Go stdlib
    TLS does not yet ship ML-DSA. The hybrid KEM defends against
    harvest-now/decrypt-later; the cert is the orthogonal
    auth-time concern, gated by today's trusted CA.
  - 0-RTT replay is documented; non-idempotent handlers must
    set RejectEarlyData=true.

Back-compat is sacred: TransportTCP is the default for
NodeConfig.Transport, so every existing consumer keeps working
untouched. TransportQUIC is opt-in and requires:

  import _ "github.com/luxfi/zap/quic"

so the QUIC TransportFactory registers itself at init.

Tests prove X25519MLKEM768 actually negotiates end-to-end:

  $ go test ./quic/... -run TestHandshake -v
  TestHandshake_NegotiatesX25519MLKEM768
      client negotiated CurveID = 0x11ec, ALPN = "zap/1"
  --- PASS

Also covered: classical-fallback negotiation when server excludes
the hybrid, ALPN allowlist rejection, multiplex of concurrent
streams, 0-RTT round-trip, unidirectional broadcast streams,
frame-size enforcement, QUICConfig propagation through Listen.

Benchmarks on Apple M1 Max (1-RPC ping-pong, single connection):

  RTT_QUIC/256B            0.40 MB/s   634 us/op
  RTT_QUIC/1MiB           44.94 MB/s    23 ms/op
  RTT_TCP_Baseline/256B    3.16 MB/s    81 us/op  (no TLS, no PQ)
  RTT_TCP_Baseline/1MiB  838.88 MB/s   1.2 ms/op
  Multistream concurrent (16 parallel): 5.02 MB/s @ 50us/op

QUIC pays for TLS 1.3 + PQ KEM relative to the plaintext-TCP
baseline; small-message latency is 8x, large-message throughput
is 18x slower. The multistream concurrency case is where QUIC
wins — independent RPCs do not block each other.

Files:

  quic/conn.go         transport-level Conn (Send/Recv/streams)
  quic/server.go       Listener wrapping quic-go Listener/EarlyListener
  quic/client.go       Dialer with 0-RTT-attempting DialEarly
  quic/tls.go          TLS defaults + self-signed test cert helper
  quic/factory.go      registers QUIC TransportFactory with parent zap pkg
  quic/config.go       Config wrapper exposed via NodeConfig.QUICConfig
  quic/doc.go          package documentation
  quic/README.md       deployment guide + threat model
  quic/handshake_test.go  X25519MLKEM768 negotiation + multiplex + 0-RTT
  quic/benchmark_test.go  QUIC vs TCP baseline RTT + throughput

  transport.go         new Transport enum + TransportFactory hook
  node_quic.go         QUIC-path Send/Call/ConnectDirect dispatch
  node.go              additive: Transport field on NodeConfig, branch
                       in Start/Send/Call/ConnectDirect
  LLM.md               QUIC transport section under PQ-TLS Support
  go.mod / go.sum      add github.com/quic-go/quic-go v0.59.1
2026-06-02 03:53:13 -07:00
Hanzo AI 18a951b477 docs: ZAP-native storage migration plan + ancient-store status
Document the canonical storage backends in luxd today and the
migration path per surface. ZAP-native ancient store (in luxfi/geth)
is the first surface shipped; P-Chain/X-Chain/chainData migrations are
tracked separately (#186, #187).

The EVM ancient store migration is one-shot via
`luxfi/geth/cmd/migrate-ancient`. The luxfi/operator NodeFleet CRD
defaults archive pods to --ancient-store-backend=zap.
2026-06-02 03:37:54 -07:00