- LAUNCH_CHECKLIST.md: pre-launch plan for v1.24.11, 154/154 boxes
unchecked, zero references; superseded by LLM.md/CHANGELOG.md/RELEASE.md
and NETWORKS.yaml + genesis/configs for the chain-ID/port tables.
- rename_app.sh, replace_imports.sh: spent one-off sed migrations. Both
are now actively harmful — replace_imports.sh would rewrite the live
github.com/luxfi/vm/manager import in vms/manager.go.
- gen_zoo_addr: stray 3.4MB darwin/arm64 build artifact committed at root;
source preserved at cmd/gen_zoo_addr/gen_zoo_addr.go (.gitignore already
covers the intended output path).
- .ci-status-check.md, .ci-trigger: dated CI-poke stamps, no workflow reads
them.
Also removed (untracked): .claude/worktrees/ agent scratch — a 73MB
whole-tree duplicate that polluted every grep. Detached via git worktree
remove; node HEAD cf8e4c6f51 was an ancestor of main, and the nested
lux/evm worktree HEAD 7156c44f6 is release tag v1.104.12, so no work lost.
Deleted the two fully-merged worktree-agent-* branches it left behind.
No Go source, config, manifest, or .github/ file was touched.
Captures the devnet-canary findings so the gated rollout is resumable: the
v1.36.11 EVM-plugin api skew (fixed in v1.36.12), the v1.36.2->v1.36.x P-Chain
codec migration (one-time wipe + proven cross-version re-bootstrap), the durable
rejoin mechanism, per-net RewardManager addresses/config shape, and the
one-at-a-time verify-tip roll protocol. Also folds in the pre-existing
RewardManager->DAO-Safe C-Chain design note.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Drop the Avalanche-heritage /ext prefix; /v1 is the single canonical route
surface (one way, no backward compat). The node's baseURL is the source of
truth; clients, SDKs, CLI, indexer, maker, genesis, netrunner, and the
k8s/compose/gateway/explorer configs are updated to match.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Genericize the external-consumer example to a white-label tenant's
network-bootstrap tooling; the the tenant repo path belongs only in that
tenant's own repo, never in a lux repo.
ONE declarative flow for both lux release artifacts, on platform.hanzo.ai +
self-hosted arcd — NO GitHub Actions:
- hanzo.yml: node image build (matrix linux/{amd64,arm64}, native long-poll
dispatch onto lux-build-* pools, tag-pattern {{git.branch}} -> :vX.Y.Z).
Validated against the platform parsePlatformConfig.
- scripts/publish_plugin_set.sh: extracts the 12 baked VM plugins from the
node image + SHA256SUMS, uploads to s3://lux-plugins-<env>/<pluginset>/
(operator pluginSource), round-trip sha-verified. DRY — no second compile.
- RELEASE.md: the ONE canonical build+publish runbook; lists the .github
workflows to retire (docker.yml, release.yml, build*.yml, ci.yml, ...).
- LLM.md: points at RELEASE.md as the canonical release path.
Proof (no GitHub): node:v1.30.40 provenance == 44b67b99a0 (reused, not
rebuilt); evm@v1.99.37 + dexvm@v1.5.15 rebuilt natively on the spark fleet
(go1.26.4, CGO=0) and the full 12-plugin set published to
s3://lux-plugins-staging/ (round-trip verified). neo's lux-plugins-devnet
prefix untouched.
External (HTTP / JSON-RPC) is the only place JSON is legitimate. Every
existing encoding/json import in node/ moves to github.com/go-json-experiment/json
(v2 root, not the v1 sub-package). NewEncoder/NewDecoder rewrite to
MarshalWrite/UnmarshalRead. MarshalIndent rewrites to Marshal with
jsontext.WithIndent. json.RawMessage rewrites to jsontext.Value.
*json.SyntaxError rewrites to *jsontext.SyntacticError.
81 files migrated. LLM.md captures the rule + v1->v2 delta table.
Known v2 semantic deltas surfaced by existing tests (followups, not regressions):
- [N]byte fields with no MarshalJSON now marshal as base64 string (v1 marshalled
as JSON array of byte numbers). Affects vms/platformvm/txs/*_test.go fixtures
with embedded BLS proofOfPossession.
- time.Duration has no v2 default representation; configs that wire-format
Duration as nanoseconds (vms/{xvm,platformvm}/config, config/spec) need to
switch to string-form Duration or carry an explicit option. v2 root does not
re-export FormatDurationAsNano.
- v2 enforces strict UTF-8 (vms/chainadapter/messaging fixture has non-UTF-8).
- json.MarshalWrite does not append a trailing '\n' (v1 NewEncoder.Encode did);
service/auth/auth_test.go expectation updated.
- nil []byte round-trips to empty (not nil); config_test deep-equal fixtures
surface this.
All affected sites are at the API boundary; ZAP wire envelope already covers
the internal data paths (state, P2P, consensus, MPC, threshold). Internal
JSON sites that should move to ZAP next (separate work):
- vms/da/store.go (DA blob/cert storage as JSON)
- vms/platformvm/airdrop (airdrop claims as JSON in db)
- vms/chainadapter/appchain (SQLite materializer schema/data blobs)
- vms/chainadapter/messaging (conversation codec)
- staking/kms.go (KMS HTTP client — external technically, leave)
- utils/{bimap,ips} (small marshaler shims — low priority)
Add the per-VM policy table (user-tx vs service-only) and the wiring
contract (Init -> fee.Validate -> ValidateFee at the user-tx entry,
internal paths bypass). The actual gates live in the per-VM repos
(luxfi/chains/<vm>/feegate.go, luxfi/oracle/vm/feegate.go,
luxfi/relay/vm/feegate.go) — this is the index page.
The companion commit `7496606282` retired the gRPC fallback inside
vms/rpcchainvm/. This commit closes the loop by deleting every
remaining `//go:build grpc` file under node/, removing the dual-build
plumbing entirely. ZAP is the only wire protocol; there is no
`-tags=grpc` opt-in.
Per the "one and only one way to do everything" mandate, the
backwards-compat scaffolding (gRPC adapters, protoc stubs, connect-go
example handlers, OTLP gRPC exporter, x/sync gRPC sync engine,
keystore-over-gRPC client/server, rpcwarp gRPC signer, gRPC alias
reader) is removed forward-only — no aliases, no deprecation period.
Deletions (84 files):
- proto/pb/{aliasreader,http,io,keystore,message,messenger,net,p2p,
platformvm,rpcdb,sdk,sender,sharedmemory,signer,sync,
validatorstate,vm,warp}/ — protoc stubs (entire tree)
- proto/{p2p,platformvm,sync,vm}/*_grpc.go — gRPC type re-exports
- db/rpcdb/{grpc_server,grpc_client,grpc_test}.go — rpcdb gRPC adapter
- service/keystore/rpckeystore/ — keystore-over-gRPC (dead consumer)
- x/sync/ — entire merkledb sync engine (100% gRPC-tagged)
- internal/ids/rpcaliasreader/ — gRPC alias reader (dead consumer)
- connectproto/ — connect-go XSVM ping handler scaffolding
- vms/platformvm/warp/rpcwarp/{client,server}.go — gRPC warp signer
- vms/platformvm/network/warp.go — protobuf-based warp justification
handler (warp_zap.go retains the no-op verifier consumers expect)
- vms/components/message/message_grpc.go + message_test.go
- vms/example/xsvm/api/ping.go + vm_http_grpc.go + cmd/{run,xsvm}/
- wallet/network/primary/examples/sign-l1-validator-* (5 dead
example main packages)
- trace/{exporter_grpc,exporter_type,exporter_type_test,noop,tracer}.go
(OTLP gRPC exporter + duplicate Tracer types — trace_zap.go has
the canonical Tracer interface + no-op tracer)
- message/bft_grpc.go (Simplex BFT wrapper)
Edits (9 files): every surviving `_zap.go` drops its `//go:build !grpc`
constraint (the files are unconditional now) and drops stale
"ZAP version" / "ZAP mode" inline comments.
Doc updates:
- LLM.md ZAP Transport section: remove the `-tags=grpc` opt-in
language. Replace with "ZAP is the only wire protocol... there is
one and only one way". Update Latest Tag to v1.26.31. Update the
rpcdb topology section to reflect single-adapter state.
go.mod: connectrpc.com/connect, connectrpc.com/grpcreflect,
otlptracegrpc moved out of direct deps. google.golang.org/grpc +
protobuf demoted to indirect (still pulled transitively via luxfi/dex).
Verified:
- `go build ./...` (default, no tags) clean
- `go test ./db/rpcdb/... ./trace/... ./message/... ./vms/rpcchainvm/...
./vms/components/message/... ./vms/platformvm/network/...
./vms/example/xsvm/... ./service/keystore/... ./proto/...` clean
- `grep -rln '//go:build grpc' --include='*.go' node/` returns zero
- `grep -rln '//go:build !grpc' --include='*.go' node/` returns zero
- `grep -rln 'google.golang.org/grpc' --include='*.go' node/` returns
zero (only transitive deps remain in go.sum)
Pre-existing TestGraniteNetworkIDConfiguration failure in tests/ is
unrelated — fails on the parent commit too (verified via stash).
Layer A — wire framing: github.com/luxfi/api/zap (unchanged)
Layer B — service spec: github.com/luxfi/proto/rpcdb (transport-agnostic
data carriers, was node/proto/zap/rpcdb)
Layer C — service impl + transports: node/db/rpcdb/
- service.go transport-neutral Service wrapping database.Database
- zap_server.go ZAP transport adapter (default; used by cevm)
- grpc_server.go gRPC transport adapter (-tags=grpc)
One Service. Many transport adapters. Adding a transport is a new
adapter file wrapping *Service; storage logic stays in service.go.
Removed (-1390 LOC of duplicate/dead code):
- node/proto/zap/rpcdb/rpcdb.go (moved to luxfi/proto/rpcdb)
- node/proto/rpcdb/rpcdb_zap.go (dead HandlerRegistry path, no callers)
- node/proto/rpcdb/rpcdb_grpc.go (duplicated gRPC server, replaced by
node/db/rpcdb/grpc_server.go)
- node/db/rpcdb/db.go (re-export shim, replaced by Service)
Consumers updated to import the canonical adapter at node/db/rpcdb:
- vms/rpcchainvm/zap/client.go
- vms/rpcchainvm/zap/client_dbserver_test.go
- vms/rpcchainvm/zap/cevm_e2e_test.go
go.mod: add `replace github.com/luxfi/proto => ../proto` for in-tree
development of the central wire-types module.
Tests: db/rpcdb (3/3) + rpcchainvm/zap (4/4 incl. cevm cross-process
e2e — KP_META written via ZAP db channel, no fallback to local zapdb).
- Config bridge at node/config/config.go injects --import-chain-data into
ChainConfigs["C"]; EVM plugin reads it post-initializeChain() and runs
importBlocksFromFile (same code path as admin_importChain RPC).
- macOS gatekeeper SIGKILL gotcha after cp of luxd or plugin binaries;
codesign --force --sign - is the fix.
- Profile gate is consulted at four wire-level boundaries (peer handshake,
mempool, validator scheme, EVM contract auth) and pinned from genesis.
NodeIDScheme enum (MLDSA65=0x42 canonical, Secp256k1=0x90 classical-
compat-unsafe only). NodeID derivation now domain-separated via
SHAKE256-384("LUX_NODE_ID_V1" || chain_id || scheme || pubkey).
Wire encoding includes a leading scheme byte so the receiver can
verify without trusting the profile alone.
Strict-PQ profile rejects secp256k1 NodeIDs.
Classical-compat profile accepts both (transition path).
Migration: hardfork activation switches strict-PQ chains from
mixed-scheme to ML-DSA-only at a configured activation block.
network/peer/scheme_gate.go is the single primitive consumers funnel
inbound NodeIDs through: SchemeGate.Classify(nodeID, scheme, height,
site) returns a TypedNodeID once the chain policy admits the pair.
The ActivationHeight field implements the hardfork transition window;
ClassicalCompatUnsafe mirrors the operator opt-in flag (refused under
strict-PQ regardless, honoured on permissive).
Existing 20-byte ids.NodeID array stays byte-identical for storage /
map keys / codec; the scheme byte travels alongside it on the wire
via ids.TypedNodeID. Consumers of the 20-byte form (peer/upgrader,
proposervm block proposer, platformvm validator registry, mempool
sender) continue to work unchanged at the storage layer; the
SchemeGate boundary is what stamps the scheme byte and runs the
cross-axis check.
Bumps luxfi/ids v1.2.9 -> v1.2.10 (NodeIDScheme, TypedNodeID,
DeriveMLDSA, FullDigest, error surface) and luxfi/consensus
v1.23.2 -> v1.23.3 (ValidatorSchemeID accessor, AcceptsValidatorScheme,
ErrValidatorSchemeMismatch).
Tests:
- TestSchemeGate_NewSchemeGate_RejectsNilProfile
- TestSchemeGate_StrictPQ_AcceptsMatchedScheme
- TestSchemeGate_StrictPQ_PostActivation_RejectsClassical
- TestSchemeGate_StrictPQ_PreActivation_AcceptsBothSchemes
- TestSchemeGate_StrictPQ_PreActivation_RejectsClassicalAfterCutover
- TestSchemeGate_Permissive_AcceptsClassicalUnderUnsafeFlag
- TestSchemeGate_RejectsUnknownSchemeByte
- TestSchemeGate_PinsProfileScheme
- TestSchemeGate_SiteTagIncludedInError
Patch-bump: v1.26.9 -> v1.26.10.