55 Commits
Author SHA1 Message Date
zeekayandHanzo Dev e1b1e27069 Build on Go 1.27
Bumped the go directive to 1.27.1. go build, go vet and the full test
suite all pass on the 1.27.1 toolchain.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-17 13:00:53 -07:00
zeekay 2210675e6e The info, admin and health replies cross the plane carrying their values
A ZAP field is an offset. Eight of these replies held a map, which has no
order to give one, so the reply could not cross between two processes at
all. Two more held values whose fields are every one unexported —
netip.AddrPort, time.Time — and those are worse: they are not refused, they
derive an EMPTY layout, so the reply crossed, reported no error and carried
nothing. GetNodeIPReply has one field, so its whole answer was blank. And a
health check says what it likes, so its details were an `any`, which has no
shape until it holds something.

Each map is now a list of a stated element type, ordered by the key it was
an object of. An address is the text netip already wrote for it, an instant
is three numbers, and a check details are the bytes of their own JSON —
the one shape an object, a string and a list all share.

The JSON does not move. Every list marshals to the object it replaced and
reads one back, and wire_test.go holds that against the old shape spelled
out as a literal rather than against itself. The order is new where there
was none: a map answered the same question two ways.

APIReply also states the status it carries, so the 503 a probe reads and
the healthy a person reads are one fact computed once.
2026-08-29 22:53:03 -07:00
zeekay 1de772ad2d The shapes every chain answers in state their own ZAP wire
A JSONTxID, a GetTxArgs, a GetBlockArgs and the ids inside them are what
P and X reply with, and each holds an ids.ID — [32]byte, bytes_fixed[32]
on the wire, which the derived codec refuses to carry. So none of those
calls could cross the op-call plane. zap_gen.go is generated by
zip.Codecs from the same layout the plane encodes against, and the JSON
edge does not move: encoding/json reads the tags and knows nothing about
a ZAP codec.
2026-08-29 18:58:36 -07:00
zeekay 9098761124 The numerics state the shape they marshal into
Each of these writes itself as a quoted decimal — a JSON number is a float64, so
a uint64 above 2^53 does not survive one — which means a schema built from the
Go fields would describe the integer and not the wire. zip published `{}` for
every one of them: true, since the fields are not the wire form, and useless to
anything generating a client, which then types the field as an untyped value.

The shape is declared beside MarshalJSON because the bytes and the schema are
the same fact about the same type. zip reads it through SchemaDescriber.

This is the JSON EDGE only. On the ZAP plane a u64 is a u64 and none of this
applies — the quoting exists because browsers speak JSON, and it stops at the
boundary where they do.
2026-08-29 13:38:46 -07:00
zeekay 084841011e feat(types): the JSON-RPC boundary carries 16- and 256-bit numbers too
The set was Uint32, Uint64, Float64. Two widths the chain actually uses were
missing, so callers reached past this package for both.

Uint16 is what platformvm's API already asks for today.

Uint256 is the width the EVM carries. A wei value, a token balance or a
supply does not fit in 64 bits — an 18-decimal balance passes 2^64 at about
18.4 tokens — so every such field arriving here had to become a bare string
or a hand-rolled big.Int. It is uint256.Int rather than big.Int because a
fixed-size value cannot be nil and does not allocate; the module was already
in the graph.

Every numeric here is a QUOTED decimal, which is the point of the package: a
JSON number is a float64, and a uint64 above 2^53 does not survive one. The
test asserts Uint64 REFUSES a value it cannot hold, which is the failure
Uint256 exists to prevent.

Unquoting was written out once per type and is now read in one place. Adding
two more types would have made five copies of it.
2026-08-28 21:22:30 -07:00
zeekayandHanzo Dev 6c08e7f422 the corporate name is Lux Industries Inc
Lux Partners Limited is not the entity behind this work. Every place the
old name stood — copyright lines, site footers, brand config, legal and
compliance text — now names the company that actually holds it.

lux.fund is the exception and is not renamed to match: that site belongs
to Lux Assets Management LLC, a different company, and it says so.

Two kinds of line were left for a person rather than rewritten. A dated
news item recording that Lux Partners Limited was established in the Isle
of Man is a historical fact, and renaming it would falsify the record. And
a sentence asserting that the company is domiciled in the Isle of Man is a
claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle
of Man company, so swapping the name there would assert something untrue.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-28 12:53:27 -07:00
zeekay fc3395fcd9 deps: take consensus v1.36.63
v1.36.2 no longer hashes to what go.sum records, so a clean build cannot
verify it. Move to a current version rather than rewrite the recorded hash.
Build and full tests pass.
2026-08-21 00:19:32 -07:00
zeekay 5e80b1f245 ci: build and test on the git-runner fleet 2026-08-21 00:11:47 -07:00
zeekay 39486f7e19 carry the github copy onto the forge
github.com held commits this host lacked, which refuses the mirror push
("fetch first") and stops github receiving anything at all. Merging rather
than overwriting, so the mirror is a fast-forward again and neither side's
work is dropped.
2026-08-20 21:51:01 -07:00
zeekay 05e7482ac4 run on the native fleet
Adds .hanzo/workflows/cicd.yml calling the canonical pipeline and drops the
GitHub-hosted workflows it replaces. Lands here because this is where the repo
is written — git.hanzo.ai is upstream and github.com is its push mirror, so the
same change made on github.com never reaches the runner fleet.
2026-08-20 20:20:09 -07:00
zeekayandHanzo Dev dc7d3dbe61 zap: one message type for validators.State, and a guard on the flag bits
Message types must stay under 0x40 because MsgErrorFlag IS 0x40: a type with bit
6 set arrives indistinguishable from an error response, so every reply to it
decodes as a remote error whose text is the raw response bytes. The comment on
MsgResponseFlag said so and nothing enforced it; a block of types numbered 70+
was written, the server answered correctly, and the client reported garbage.

validators.State has eight methods and no eight-wide run remains below the limit,
so a type per method could not be spelled correctly even once. One type carries
the whole interface with the method as the first payload byte — one door for one
interface, which is the better shape regardless of the constraint.

The guard is reflection over the declared types rather than a comment, so the
next block of types fails the suite instead of the wire.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 18:24:33 -07:00
zeekayandHanzo Dev 40a666fb62 zap: message types for validator state (70-79)
Validator state is an interface over live node-owned state, so a plugin-hosted
VM cannot receive it by value and must ask across the boundary — the same reason
the atomic message types exist. M-Chain forms its threshold committee from these
answers.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 18:21:03 -07:00
zeekayandHanzo Dev 3dca6e6a74 zap: carry a validator-state server address to plugins
A plugin-hosted VM rebuilds its Runtime from the initialize request, and an
interface over live node-owned state cannot be copied through a struct literal.
SharedMemory already had this problem and was solved by binding a ZAP server
node-side and naming its address here. Validator state has the identical shape
and was simply never plumbed, so every plugin VM saw a nil handle.

For most VMs that is invisible. For M-Chain it is fatal: its entire job is a
threshold ceremony among the validator set, so with no way to learn who the
validators are it reports "no validator committee available" and can never
generate a custody key, on any network, at any configuration.

Appended after the existing trailing fields, never inserted, so both skew
directions read the capability as ABSENT rather than as present-with-garbage. A
plugin that believed it had validator state but dialed nothing would form a
committee of nobody, and a quorum of nobody signs anything. The mutation that
inserts the field instead of appending it fails the skew test.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-20 18:18:31 -07:00
zeekayandHanzo Dev 324f613c3d zap: a VM that cannot answer is not a VM that said no
The six state-sync messages were reserved with payloads defined and no capability
bit, so a node could only learn whether a VM syncs by asking and reading the
reply. That conflates two different answers: a VM with no state-sync surface
cannot respond at all, and a VM that has one may still choose to replay. A node
repairing a damaged chain has to tell those apart — the first means look
elsewhere, the second means this VM decided.

CapStateSync is set at the handshake, where every other optional surface is
already discovered, so the distinction costs no round trip.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-16 08:19:06 -07:00
zeekayandHanzo Dev 42ce072b1f zap: the state-sync surface has payload types
The six message numbers were reserved and nothing was ever built on them, so
neither end of the boundary could carry a state summary and the type assertion
that gates state sync failed on every chain in every release. The VM implements
sync; nothing could reach it.

Five of the calls are ordinary request/response. Accept is not: a summary is an
object with behaviour on the VM's side and only its identity can cross, so it
travels as an id and the server answers against the summary it produced under
that id. Rebuilding one from bytes the caller supplies would accept a summary
nobody ratified.

Absence has three answers rather than one zero: no summary is ErrorNotFound, a
VM that does not sync at all is ErrorStateSyncNotImplemented, and neither is
ErrorUnspecified, which means the fields can be trusted.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-16 08:17:26 -07:00
zeekayandHanzo Dev ecb87b5eb5 zap: an error the wire has no word for must not read as success
ErrorUnspecified is the zero value and carries "the call succeeded" — a reply
holding it is read as a good answer and its fields are trusted. The only other
codes name a closed database and a missing key, so every other failure had
nowhere to go and was reported as the zero. A caller then took a zero-valued
block as a real one.

ErrorInternal gives those failures a word. Appending is safe in both
directions: errorFromZAP's default arm turns an unknown code into an error, so
a reader that predates this still sees a failure rather than a success.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-15 22:40:11 -07:00
zeekayandHanzo Dev 31058e5e41 zap: add Indexed to the atomic surface, and stop Serve panicking after Close
Indexed looked superfluous from the C-Chain side — no settlement path calls
it — so the first cut of this surface omitted it. That was wrong. The
D-Chain's autonomous seam drive is built on it: inside BuildBlock it
enumerates pending C->D intents by a fixed discovery trait, recovering each
key from the LastKey cursor, because a shared-memory object does not carry
its own key in its value. Without Indexed the drive finds nothing and no
intent is ever imported, which would have left the seam as dark as it is
today while looking fixed.

Also fixes a shutdown race that a round-trip test surfaced. Close() nils
the conns map under s.mu and Serve's accept loop then assigned into it,
panicking — fatally, since Serve runs on a detached goroutine. Any server
whose stop path is "cancel then Close" with a connection in flight could
hit it, which already includes the node's per-chain rpcdb server. The
regression test forces the interleaving rather than racing for it, and
fails with the exact panic when the guard is removed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 14:21:00 -07:00
zeekayandHanzo Dev 8743bbe098 zap: carry the cross-chain atomic seam across the plugin boundary
One atomic.Memory exists per node and every chain gets a handle carved out
of it. A VM linked into the node reaches its handle off runtime.Runtime; a
VM hosted as a ZAP plugin — the C-Chain EVM and the dexvm both are — does
not, because the node rebuilds the plugin's Runtime from InitializeRequest
fields and neither an interface over a live database (SharedMemory) nor the
chain-alias lookup (BCLookup) survives a struct literal. The plugin's
SharedMemory was therefore nil and every DEX 0x9999 settlement reverted
"requires the cross-chain atomic capability".

Adds the wire for it: MsgAtomicGet/MsgAtomicApply, and AtomicServerAddr +
DChainID appended to InitializeRequest. The node binds a ZAP server over
its own handle and the plugin dials it, so the node is the server and the
existing strictly request/response transport carries this with no duplex
framing and no gRPC — the same shape already used for DBServerAddr.

Indexed is deliberately not proxied: no settlement path calls it.

Both skew directions are pinned by test rather than reasoned about, since a
wire change that forks a mixed fleet is the failure this design exists to
avoid. New node -> old plugin: the appended bytes are inert, the plugin
keeps its nil handle and its existing fail-closed revert. Old node -> new
plugin: Remaining() is 0, so the capability reads as ABSENT instead of
raising the ErrUnexpectedEOF that the Capabilities append once caused.
Neither direction changes how any block executes.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-06 14:08:52 -07:00
zeekayandHanzo Dev 7a86664099 deps: drop go.sum lines that disagree with the checksum log
These hashes were recorded from direct VCS fetches made while GOPRIVATE covered
github.com/luxfi/*, which switches off the module proxy and checksum
verification together. Tags moved afterwards, so what is written down here no
longer matches what sum.golang.org holds and the build refuses to verify.

proxy.golang.org still serves the originally-published bytes and those verify
against the log, so dropping only the disagreeing lines and re-tidying restores
a verified module graph with no version change. Lines that agree are untouched;
this is not a regenerated go.sum.

The failure followed the machine rather than the repository — the same commit
built wherever the module cache was clean. Any machine or CI runner that
fetched luxfi modules under the old GOPRIVATE holds the same poisoned entries.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 09:37:03 -07:00
zeekay 6fef0fd562 chore: sync working tree
Commits 1 outstanding change(s) that were sitting uncommitted.
No build artifacts and no secrets in the changeset (both checked).
2026-07-26 10:01:48 -07:00
zeekayandHanzo Dev 400b37b91d fix(zap): length-tolerant InitializeResponse decode (evolvable VM wire)
InitializeResponse.Decode now reads the trailing Capabilities field only when >=8 bytes remain, so a
newer node decoding a pre-v1.0.16 plugin's short payload yields Capabilities=0 instead of the
unexpected-EOF that broke every EVM Initialize in the v1.36.11 skew (Capabilities appended at protocol
42 without a bump). Appending a future trailing field can no longer EOF-break an older peer. New test
locks the tolerance.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-16 12:31:15 -07:00
zeekay ac5b78b61f chore(deps): bump geth v1.20.1 + luxfi deps — stack unification 2026-07-15 11:12:38 -07:00
zeekayandHanzo Dev eb596a6fb1 zap: infer transport network from addr (unix socket / in-proc fast path)
Dial + Listen now infer the network from the address instead of hardcoding
tcp: a socket path (/tmp/vm.sock, @abstract, or any '/' path) → unix-domain
socket (same-host IPC, no TCP/UDP stack, no ephemeral-port exhaustion); a
host:port → tcp (genuinely remote). Strict backward-compat: existing
127.0.0.1:port addrs still dial tcp. Enables the rpcchainvm same-host fast
path — a co-located plugin VM connects back over a unix socket with zero
plugin-side code change once rebuilt against this api.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-11 23:29:19 -07:00
zeekayandHanzo Dev 8bdf9cbf1b zap: carry the Quasar EXPORT capability across the rpcchainvm boundary (wire)
Two-tier consensus v1.36 splits finality into Nova (local, reorgable accept tip)
and Quasar (⅔-by-stake EXPORT-final). A VM's `finalized`/`safe` tags and its
cross-chain (warp) export gate must resolve to the Quasar height. But the
C-Chain EVM runs as a SEPARATE rpcchainvm plugin process, and nothing carried
the export height across the ZAP boundary — so the whole export tier was dormant
in the real deploy (finalized/safe stuck at genesis).

Wire additions (OPTIONAL, capability-gated — generic VMs are unaffected):
- MsgSetQuasarFinalized (60) / MsgQuasarHeight (61): the two export RPCs. Kept
  < 0x40 so the response/error flags OR in cleanly, grouped after Warp (50-59).
- InitializeResponse.Capabilities (uint64) + CapQuasarExport bit: the plugin
  advertises the export capability in the handshake so the node wires the
  observer only for an export-capable VM. Appended to the struct codec (all four
  encode/decode sites go through the struct methods, so it stays consistent).
- SetQuasarFinalizedRequest / QuasarHeightResponse message structs.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-10 10:14:31 -07:00
zeekayandHanzo Dev 1fcd6b64f0 fix(deps): repair vanished pins / go.sum drift ( gosum-refresh) + build green
Co-authored-by: Hanzo Dev <[email protected]>
2026-06-30 13:56:13 -07:00
zeekay a88a0f457b go: 1.26.3 → 1.26.4 (security: crypto/x509, mime, net/textproto) 2026-06-06 22:03:54 -07:00
zeekay 57c5d20765 deps: bump luxfi/vm v1.2.0, sampler v1.1.0, staking v1.5.0 (relicense)
Picks up the 2026-06-06 relicensing:
- luxfi/vm v1.2.0 → Lux Ecosystem License v1.2 (was Lux Research v1.0).
  License-only retag of the extracted runtime; patent reservation
  preserved for runtime optimization surfaces, royalty-free for
  Descending Chains.
- luxfi/sampler v1.1.0 → BSD-3-Clause (was Lux Research v1.0).
  License restored to match the luxfi/node provenance (originally
  extracted from node/utils/sampler).
- luxfi/staking v1.5.0 → BSD-3-Clause (was Lux Research v1.0).
  License restored to match the luxfi/node provenance.

No code changes in this commit — go.mod/go.sum only.
2026-06-06 19:02:00 -07:00
zeekay a35ea22dbb deps: bump p2p v1.21.1 (Wave 2G-Cascade) 2026-06-06 06:14:38 -07:00
zeekay a47abce458 WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:33:51 -07:00
zeekay 5b4a544244 api: drop codec indirect — bump ids v1.2.15 + warp v1.19.2
ids v1.2.15 + warp v1.19.2 both shed their direct codec/wrappers
imports (encoding/binary inlined in ids.ID.Prefix/Append; firstErr
helper inlined in warp packages). With both consumers gone from api's
build closure, codec drops out of go.mod and go.sum entirely.

Also drops luxfi/node, ProjectZKM/Ziren, golang/mock, luxfi/atomic,
google/renameio — transitive cleanups that fall out of the same module
graph pruning.
2026-06-04 21:32:20 -07:00
zeekay 83f31044c5 fix: gofmt -s across repo (CI format check) 2026-06-02 11:39:38 -07:00
zeekay c286bab46f chore(deps): go get -u ./...
Updated dependencies to latest minor versions via go get -u + go mod tidy.
2026-06-02 11:18:43 -07:00
zeekay f256a553ae decomplect: rip NetworkUpgrades interface + wire struct (16 fork-time fields) from zap/runtime 2026-05-18 23:16:14 -07:00
zeekayandGitHub 03714502f2 docs: add LICENSING.md pointer to canonical IP/licensing strategy (#1) 2026-05-15 17:30:13 -07:00
zeekay f4b781ff7d deps: luxfi/crypto v1.19.0 2026-05-13 12:00:07 -07:00
zeekay f249e1883b go.mod: bump go directive to 1.26.3 (security advisory) 2026-05-12 21:28:12 -07:00
zeekay a42c840107 zap: add NewListener so any net.Listener can serve ZAP
zap.Listen("tcp", ...) is the common path. NewListener is the
escape hatch that lets a Z-Wing listener (or any other net.Listener
that yields post-handshake net.Conns) feed zap.Server unchanged.

This is the seam zwing.ListenZAP uses to compose a one-call
PQ-on-the-wire ZAP server.
2026-05-06 12:50:29 -07:00
zeekay 6993f40700 docs(zap): canonical TCP port 9999 documented 2026-05-06 10:37:28 -07:00
zeekay d89d3a16e5 docs(zap): call out X-Wing — IETF-standardized X25519+ML-KEM hybrid
X25519 + ML-KEM-768 is the IETF X-Wing construction
(draft-connolly-cfrg-xwing-kem). Document where Lux uses X-Wing
(zap-protocol Rust core, AI-agent ZAP) vs the RNS hybrid (Lux node
sessions with identity-bound handshake). Comparison table added so
new code picks the right one.
2026-05-06 10:23:33 -07:00
zeekay 8ad962dc42 docs(zap): comprehensive LLM.md — wire format, PQ scope, two-implementation reality 2026-05-06 10:21:13 -07:00
zeekay 99631b3b4e fix(zap): correctness — distinct MsgErrorFlag, response flag on success, drop debug stderr
Three real bugs in luxfi/api/zap:

1. Debug fmt.Fprintf(os.Stderr, ...) prints on every wire read/write/
   handler invocation. Visible in production logs as [ZAP-WIRE-READ],
   [ZAP-CLIENT-READ], [ZAP-SERVER-WRITE], [ZAP-HANDLER]. Stripped.

2. Server's success path sent the handler's returned msgType directly
   without OR'ing MsgResponseFlag. Echo-style handlers (return same
   msgType) produced responses that looked like fresh inbound requests
   to the client demultiplexer, which dropped them. Tests caught this
   as 'Expected response type 137, got 9'.

3. Client treated every response as an error because the protocol used
   a single MsgResponseFlag for both success and error paths. Added
   MsgErrorFlag (0x40) — error responses now carry MsgResponseFlag
   AND MsgErrorFlag; success responses carry only MsgResponseFlag.
   All msg types must stay < 0x40 (the existing range 1-52 is fine).
2026-05-06 10:19:43 -07:00
zeekay 06b7887c87 feat: GetNodeVersionReply.Consensus + ConsensusInfo (info api); drop dead agentic_network.go
agentic_capnp sub-package was never generated; build-tag dead code referencing it
broke 'go mod tidy' (could not resolve unused phantom import). No-fake policy.

deps: tls v1.0.3 (hkdf.New); go mod tidy
2026-05-05 16:57:04 -07:00
zeekay 55e851bacb deps: update go.mod/go.sum 2026-04-19 17:04:02 -07:00
zeekay f4791bbf26 chore: symlink AGENTS.md and CLAUDE.md to LLM.md
Canonical project context lives in LLM.md. Symlinks ensure
agentic coding tools (agent, Cursor, etc.) find context
automatically regardless of which filename they look for.
2026-04-01 14:08:02 -07:00
zeekay b009e78f4d chore: bump Go 1.26.0 → 1.26.1
Fixes 5 stdlib CVEs (html/template, os, net/url, crypto/x509 x2).
2026-03-12 01:07:37 -07:00
zeekay d24119bc75 feat: add typed State enum to ZAP VM protocol 2026-03-11 12:09:33 -07:00
zeekay c03b59c868 docs: add LLM.md project guide 2026-03-11 10:34:33 -07:00
zeekay f4325cd5d3 chore: update Go module dependencies 2026-03-03 14:41:48 -08:00
zeekay d5f3eb1b77 chore: add BSD-3-Clause LICENSE 2026-02-14 05:26:18 -08:00
zeekay f51e8ffb83 debug(zap): add wire-level debug output for Initialize diagnosis
Add temporary stderr debug prints to ReadMessage, readLoop, handleConn,
and ServerConn.Write to capture exact msgType bytes on the wire. This
will reveal why the client sees MsgResponseFlag set on Initialize
responses that the server sends as MsgInitialize (0x01).
2026-02-10 22:07:02 -08:00
zeekay d38045dba5 feat(zap): add error response protocol and concurrent request handling
Add MsgResponseFlag for error responses over the wire, enable concurrent
request dispatch with per-goroutine panic recovery, and simplify State
type to use canonical vm.State. Update consensus dependency to v1.22.67.
2026-02-10 18:54:45 -08:00
zeekay cc9ec49d75 feat: add ZAP-format agentic network schema
Convert agentic.capnp to proper whitespace-based ZAP syntax:
- Remove file IDs, ordinals (@N), braces, semicolons
- Use indentation-based structure
- Add AgenticNetwork service interface
- Supports ZMQ, Cap'n Proto, gRPC wire protocols
- Includes post-quantum cryptography primitives
2026-02-04 17:56:47 -08:00
zeekay 57d45de11a chore: sync dependencies and format code 2026-02-04 15:46:05 -08:00
zeekay dd2f2c716e Add ZAP wire protocol transport for VM communication
- Add zap/ package with zero-copy binary wire protocol
- Support for VM client/server communication over ZAP
- Wire format: [4-byte length][1-byte message type][payload]
- Includes sender, transport, and VM implementations
2026-01-24 11:39:40 -08:00
zeekay b1db41a415 api: initial v1.0.0 2026-01-16 11:52:14 -08:00