23 Commits
Author SHA1 Message Date
zeekayandHanzo Dev c72acf6e94 Go 1.27.1, and the code it can say more simply
Modules declare go 1.27.1 and the build images follow. go fix applied the
modernizers, and SHA3-256 now comes from the standard library's crypto/sha3;
omitzero is not applied, since it changes what the JSON says.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 19:13:09 -07:00
zeekayandHanzo Dev e9f14a1c60 Give every package one version
Two thousand and twenty-one packages carried more than one spec across this
estate — eslint alone in a hundred and four forms — and a package resolved twice
is two copies at run time, which is how react-hook-form came to report "two
different types with this name" from files nobody had edited. None do now.

The number lives in the catalog: a manifest names the package, the workspace
names the version. Where a manifest cannot reach a catalog it still carries the
one version, because the table is the source either way.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 02:44:07 -07:00
zeekayandHanzo Dev 376fa636b3 State a version once
A version written into a manifest is written into every manifest that wants it,
which is how eslint came to exist in a hundred and four forms across this estate
and how a package came to resolve twice at run time. The catalog moves the number
out of the manifest: a manifest names the package, the workspace names the
version.

Four are held below their latest because the newer release cannot be used, and
the reason is recorded rather than the version alone: @noble/hashes 2 renamed
every subpath the web3 stack imports, @splinetool/runtime 2 depends on a package
that was never published, @dynamic-labs 5.4.1 the same, and react-resizable-panels
3 points its types condition at the bundle.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-08 02:44:07 -07:00
zeekayandHanzo Dev 7d10eb76e7 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 13:08:11 -07:00
zeekay af44f18473 add native CI; refresh go.sum against the declared requirements
go.mod asked for api v1.1.1 and crypto v1.20.2 while go.sum still carried
v1.0.10 and v1.19.0, so the module did not build.
2026-08-21 00:11:25 -07:00
zeekay 122be786e0 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:59:57 -07:00
zeekay cd6e668717 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:13:58 -07:00
zeekay e1d9581174 track the name agents read, so a clone documents itself
LLM.md is the source and stays the source; CLAUDE.md is a symlink to it, so there is
one file and it cannot drift from itself. What changes is that the link is committed
rather than left for each checkout to recreate.

Left untracked, the doc was conditional on a step outside the repo: a fresh clone had
no CLAUDE.md, nothing loaded LLM.md, and an agent worked here having never read what
this repo already explains. Most of the estate already tracks theirs; this is the rest
agreeing with them.
2026-08-20 16:40:14 -07:00
zeekay 184d148990 chore(deps): bump geth v1.20.1 + luxfi deps — stack unification 2026-07-15 11:12:38 -07:00
zeekay 24e6401b0c go: 1.26.3 → 1.26.4 (security: crypto/x509, mime, net/textproto) 2026-06-06 22:07:50 -07:00
zeekay 614b7a597e deps: bump p2p/node/sdk/vm to current floors (Wave 2G-Cascade)
Drops codec/jsonrpc transitive (p2p v1.19.2 -> v1.21.1).
2026-06-06 06:16:18 -07:00
zeekay 253f054173 WIP: session checkpoint 2026-06-05 (codec rip in flight) 2026-06-05 15:34:05 -07:00
zeekayandGitHub a3ca2a4138 docs: add LICENSING.md pointer to canonical IP/licensing strategy (#1) 2026-05-15 17:41:09 -07:00
zeekay 90e525598c deps: luxfi/crypto v1.19.0 2026-05-13 11:57:58 -07:00
zeekay 7984f28512 go.mod: bump go directive to 1.26.3 (security advisory) 2026-05-12 21:32:11 -07:00
zeekay 5220f95271 zwing: real cross-language interop — Python↔Go and TypeScript↔Go
The previous "full PQ" claims for Python and TS were tested only within
each language. This commit proves the wire bytes actually match Go.

What was wrong:
  Both Python and TS were calling ml_dsa65.sign(secretKey, message)
  WITHOUT the FIPS 204 ctx parameter, while Go was calling
  DSAPriv.SignCtx(rand, digest, []byte("lux.zwing.v1")). The signed
  messages diverged → handshake verify failed across language pairs.

Fix:
  Python: ML_DSA_65.sign(sk, digest, ctx=ZWING_DOMAIN) and matching
          verify with ctx=ZWING_DOMAIN.
  TS:     ml_dsa65.sign(digest, sk, { context: ZWING_DOMAIN }) and
          matching verify { context: ZWING_DOMAIN }.

Real interop tests added:
  cmd/zwing-test-server/main.go  — minimal Go server: prints listen
                                    addr + identity hex on stdout,
                                    accepts ONE handshake, echoes one
                                    payload, exits.
  py/test_interop_go.py          — Python client connects via TCP,
                                    runs run_initiator + Channel echo
                                    against the live Go server.
  js/interop_go.test.ts          — TS client over node:net.Socket
                                    runs runInitiator + Channel echo
                                    against the live Go server.

Both interop tests pass:
  Python ↔ Go:       Ran 1 test (and 43 within-Python tests)
  TypeScript ↔ Go:   1 interop test passes alongside 32 within-TS tests

Wire-byte compatibility now PROVEN end-to-end for X-Wing KEM, hybrid
identity (Ed25519 + ML-DSA-65 with lux.zwing.v1 ctx), HKDF channel
keys, and ChaCha20-Poly1305 records across Go ↔ Python ↔ TypeScript.
2026-05-06 15:04:18 -07:00
zeekay e90f664a63 zwing: full PQ stack in Python and TypeScript with 100% coverage
Port the complete Z-Wing protocol to Python and TypeScript, no longer
just the X-Wing combiner. Each language now ships:

  identity   Hybrid Ed25519 + ML-DSA-65 + X-Wing keypair, marshal/parse,
             sign/verify, constant-time equality
  kem        Full IETF X-Wing encapsulate/decapsulate (ML-KEM-768 +
             X25519 + SHA3-256 combiner) with wire-byte compat
  handshake  1-RTT mutual-auth handshake mirroring Go state machine —
             same labels (lux.zwing.v1/handshake-init etc), same wire
             format (HandshakeInit / HandshakeResponse / transcript hash
             / AEAD-sealed responder identity)
  channel    Post-handshake ChaCha20-Poly1305 with sequence-numbered
             nonces (high 4 zero, low 8 BE counter), MAX 1 MiB frames

Python implementation:
  - cryptography (Ed25519/X25519/ChaCha20-Poly1305) + kyber-py (ML-KEM)
    + dilithium-py (ML-DSA). Pure Python, zero native deps.
  - test_full.py: 39 tests covering happy paths + every error branch
  - py/__init__.py modules at 100% line coverage:
      channel.py 51 stmts 100%
      handshake.py 159 stmts 100%
      identity.py 91 stmts 100%
      kem.py 26 stmts 100%
      zwing.py 27 stmts 100%

TypeScript implementation:
  - @noble/post-quantum (ml-kem768, ml-dsa65) + @noble/curves (ed25519,
    x25519) + @noble/ciphers (chacha20poly1305) + @noble/hashes
  - full.test.ts: 32 tests covering full e2e + every wire-decode error
  - vitest --coverage v8: 100% statements / 100% branches / 100%
    functions / 100% lines across every source file (channel.ts,
    handshake.ts, identity.ts, kem.ts, zwing.ts)

Cross-language KAT (72df...fe4ed) still pins on the X-Wing combiner.
Full handshake wire-byte interop is now possible Go↔Python (both use
FIPS 204 ML-DSA-65 with identical 3309-byte signatures); Rust uses
PQClean Dilithium3 which has the same byte sizes as Round 3 — full
Rust↔Go interop test pending a unified ML-DSA-65 source.
2026-05-06 14:18:16 -07:00
zeekay 65dacb6e06 zwing: 100% statement coverage on PQ + crypto paths
Drive every reachable error branch in identity, KEM, handshake, wire,
channel, dial, listen, zap integration. Coverage went 77.4% → 100.0%.

Refactors that came out of the audit:

* Convert chacha20poly1305.New into mustChaCha20: the only failure
  mode is wrong-size key, which never happens with our 32-byte HKDF
  output. The construction is now infallible by precondition.
* Drop the Sign() error return: ML-DSA-65 SignCtx with a valid
  identity never errors. Sign is now (id) -> []byte. callers in
  runInitiator / runResponder simplified.
* derive() drops the impossible io.ReadFull error branch — HKDF over
  32-byte output is infallible.
* parseHandshakeResponse precheck guarantees the ct/encLen reads
  cannot short-fail, so those error returns are elided.
* XWingEncapsulate / GenerateXWingKey drop curve25519.X25519 error
  branches that only fire on wrong-length scalars (we always pass 32
  bytes).

Defensive seams:

* parseTestableDSAPub (identity.go) wraps mldsa.PublicKeyFromBytes so
  tests can drive the defensive parse-error path even though current
  upstream cannot produce one.
* decapsulateMLKEM (kem.go) wraps ML-KEM Decapsulate similarly. Both
  seams default to the real upstream call in production.

Coverage tests added (coverage_test.go, ~36 tests):

  identity:  GenerateIdentityFrom short-reader, Sign/Verify error paths,
             ParseIdentityPublic size + ml-dsa + roundtrip, Equal
             nil + mismatched DSA + mismatched X-Wing.
  kem:       ParseXWingPublicKey size, GenerateXWingKey nil + short reader,
             XWingEncapsulate short reader, XWingDecapsulate size +
             low-order ephemeral.
  wire:      writeFrame oversize + writer error + partial-fail,
             readFrame header EOF + zero-length + max-length + body EOF.
  handshake: parseHandshakeInit short reads + trailing garbage,
             parseHandshakeResponse all 4 error paths,
             splitRespIDPayload all 5 error paths,
             runInitiator missing-config + write/read failures + bad
             response (4 separate adversarial responses),
             runResponder missing-config + read failure + write failure +
             bad signature + low-order X-Wing peer pubkey.
  channel:   newConn happy path, mustChaCha20 panic on bad key,
             Read/Write sequence-exhausted, multi-frame split,
             corrupted-ciphertext, double close, deadline getters,
             nonceFor edge cases.
  dial/listen: missing-config, bogus addr, dial unreachable, accept
             handshake fail + post-close, HandshakeTimeout > 0 path.
  zap:       DialZAP / ListenZAP happy path + missing-config + dial fail.

Wire format and KEM combiner output are unchanged. Cross-language
KAT (72df...fe4ed) still pins.
2026-05-06 13:37:48 -07:00
zeekay 0967911206 zwing: Python (zwing/py) + TS (zwing/js) ports with cross-language KAT
Add canonical Python and TypeScript ports of the IETF X-Wing combiner
under py/ and js/ inside this repo. Both implement

  shared_secret = SHA3-256("\./X-Wing" || ss_M || ss_X || ct_X || pk_X)

byte-for-byte and pin the same KAT as the Go (kat_test.go) and Rust
(zap-protocol/zap zap::zwing) ports:

  inputs:  ssM=0x01..x32, ssX=0x02..x32, ctX=0x03..x32, pkX=0x04..x32
  output:  72df2088314a73de80c21d9593f13fcd5629c800c70b1507f0dd918fde5fe4ed

Layout:
  py/    pyproject.toml + zwing.py + test_zwing.py (stdlib hashlib)
  js/    package.json + zwing.ts + zwing.test.ts (@noble/hashes ^2.2.0)

Z-Wing now has 4 byte-compatible implementations under one canonical
roof (luxfi/zwing). The Rust port lives at zap-protocol/zap as
zap::zwing because that's the canonical Rust home for the schema +
runtime. All four pin the same hex.
2026-05-06 13:08:54 -07:00
zeekay 0c10ffee9d zwing: cross-language KAT pinning IETF X-Wing combiner
Pin SHA3-256("\./X-Wing" || ssM || ssX || ctX || pkX) for the fixed
inputs ssM=0x01...x32, ssX=0x02...x32, ctX=0x03...x32, pkX=0x04...x32.

Expected hex:
  72df2088314a73de80c21d9593f13fcd5629c800c70b1507f0dd918fde5fe4ed

Same KAT is duplicated in:
  - ~/work/zap/zap/src/zwing.rs   (Rust crate)
  - ~/work/zap/zap-py/tests/test_zwing.py
  - ~/work/zap/zap-js/tests/zwing.test.ts

If any language port diverges on label, hash, or input ordering, that
test fails first. Cross-implementation interop drift caught at compile
time, not in production.
2026-05-06 13:00:49 -07:00
zeekay 3358b98053 zwing: add DialZAP / ListenZAP one-call ZAP-over-Z-Wing helpers
Composing PQ transport with ZAP RPC was already a 2-line pattern:

    raw, _ := zwing.Dial(ctx, addr, cfg)
    conn   := zap.NewConn(raw, nil)

Two lines means two ways to mess it up (forget to wrap, leak the
raw conn). Add the canonical one-call wrappers so there is exactly
one way to do it:

    conn, _ := zwing.DialZAP(ctx, addr, cfg)
    ln,   _ := zwing.ListenZAP(":9999", cfg)
    srv     := zap.NewServer(ln, handler)

Pairs with luxfi/api v1.0.10 zap.NewListener. No new wire format,
no new dep — these are sugar over the existing primitives.
2026-05-06 12:50:56 -07:00
zeekay 66f1cb5d18 zwing: keep IETF X-Wing labels, frame Z-Wing as composition
Z-Wing v0.1.0 briefly relabelled the X-Wing KDF combiner with a
"Z-Wing" domain separator. That was wrong: the only reason to use
IETF X-Wing is exact interop with anything that speaks the IETF
draft, and rebranding the combiner breaks that interop while adding
zero security. The Lux contribution lives ONE layer up.

This commit restores the IETF labels everywhere:

* kem.go: combineXWing writes "\./" || "X-Wing" || ss_M || ss_X ||
  ct_X || pk_X. The label slice names go back to xwingLabelPrefix /
  xwingLabelName.
* LLM.md: rewritten to position Z-Wing as a composition (real X-Wing
  KEM + Ed25519+ML-DSA-65 hybrid identity + ChaCha20-Poly1305 +
  RNS-or-TCP transport), not a separate KEM. Z-Wing's domain
  separation lives in the HKDF info string "lux.zwing.v1/{i2r,r2i}"
  and the ML-DSA context "lux.zwing.v1", not in the KEM.

Tests pass with the IETF labels (TestHandshakeRoundTrip,
TestZAPOverZWing, TestForwardSecrecy, TestSequencedNonces,
TestIdentityMismatch, TestDialListen).

The package is still called zwing and the construction is still
called Z-Wing. The KEM at the bottom of the stack is and stays IETF
X-Wing.
2026-05-06 12:48:27 -07:00
zeekay f21120ce2f zwing v0.1.0: Z-Wing PQ secure channel for Lux
Z-Wing is the canonical Lux post-quantum secure channel. One construction,
one wire format, no negotiation. Layered design exposes a net.Conn so
luxfi/api/zap (and any other stream protocol) runs unchanged on top.

  KEM:       X-Wing  (X25519 + ML-KEM-768, IETF draft-connolly-cfrg-xwing-kem
                     SHA3-256 combiner: "\./" || "X-Wing" || ssM || ssX || ctX || pkX)
  Identity:  Ed25519 + ML-DSA-65 hybrid signatures
  AEAD:      ChaCha20-Poly1305 with sequence-numbered nonces (per direction)
  Transport: TCP (RNS routing is a follow-up package)

Public API: GenerateIdentity / Dial / Listen / Client / Server.

Six tests cover the spec end-to-end: handshake round-trip, TCP dial+listen,
ExpectedRemote pinning, key-zero-after-handshake forward secrecy, sequenced
nonces, and ZAP-over-Z-Wing application round-trip.
2026-05-06 12:18:21 -07:00