Two endpoints were answering with the right status code and an empty
truth, so every fleet looked instrumented while reporting nothing.
GET /v1/health encoded apihealth.APIReply through jsonv2, which has no
default representation for time.Duration — and every Result carries one.
The marshal therefore failed on every node, every time, and the handler
fell back to {"healthy":…,"error":"health reply encode failed"}. The
status code is written before the encode, so k8s probes and dashboards
stayed green while the body carried no checks at all. Measured on Zoo,
Hanzo and Pars mainnet, node v1.34.9 and v1.36.2 alike.
The type is defined with encoding/json tags and the POST (jsonrpc) path
already encodes it through that codec, which is why POST returned the
full check set while GET returned nothing. One wire type deserves one
encoder: GET now uses encoding/json too, so the two paths agree by
construction. encoding/json also maps invalid UTF-8 in check Details to
U+FFFD instead of failing, which is the behaviour the old jsontext
AllowInvalidUTF8 option was reaching for. The buffer stays: it keeps a
partial encode from shipping a torn body.
/v1/metrics answered 200 with zero bytes for the same shape of reason.
luxfi/metric resolves NewRegistry() to a no-op registry unless the
binary is built with -tags metrics (registry_noop.go, //go:build
!metrics), and only the `full` profile passed it. Images build with the
default `minimal` profile, so every metric in luxd registered into a
black hole while --api-metrics-enabled=true still advertised metrics as
on. Whether metrics are served is the runtime flag's call; a build tag
must not silently overrule it. `metrics` becomes a base tag on every
profile.
Verified: the three new handler tests fail against the jsonv2 encoder
(checks decode empty, error field nil — the exact production symptom)
and pass after. `go tool nm` shows (*registry).Gather absent from a
default build and present once the tag is set.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
The build installed M-Chain's plugin binary as
tGVBwRxpmD2aFdg3iYjgRvrCe8Jcmq9UNKxyHMus2NZ8WcD8t — the CB58 of the
retired `thresholdvm` identifier — while the genesis builder declares the
chain with constants.MPCVMID (qCURact1n41FcoNBch8iMVBwc9AWie48D118ZNJ5tBdWrvryS).
The plugin registry resolves a CreateChainTx's vmID to an implementation
by filename, so the two never met: M-Chain was declared in genesis and no
node could start it. The Dockerfile comment had already been renamed to
"mpcvm" without the CB58 being recomputed, which is why it read as correct.
Fixes all five sites (plugin build target, the build-verify list, the
runtime COPY, the comment, and publish_plugin_set.sh) and adds
genesis/builder/mchain_test.go, which pins the vmID literally and asserts
M-Chain is present in the height-0 chain set for mainnet, testnet and
local. A vmID is an immutable one-way door once a chain is created with
it, so it is now covered by a test rather than by five copies of a string.
Bumps luxfi/genesis to v1.16.3, where mchain.json states its quorum as
"policy": "7-of-10" instead of a bare mpcThreshold that reads as the
signer count to an operator and as the polynomial degree to a library.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Node has ZERO .proto files and ZERO .pb.go — the wire is hand-written ZAP schemas
(proto/{platformvm,vm,p2p,sync}/*_zap.go). The buf/protoc tooling around it was
orphaned: removed proto/{buf.yaml,buf.gen.yaml,Dockerfile.buf,buf.md,README.md},
scripts/protobuf_codegen.sh, the Taskfile generate-protobuf + check-generate-protobuf
targets, ci.yml buf-lint + check_generated_protobuf jobs (the latter ran the deleted
script → would fail CI), and the buf-lint.yml workflow. defaultPatch 10→11 (dev
version string; image already correct via ldflags). Binary unchanged — tooling/CI only.
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>
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.
Harden the cross-compile target re-export against `set -e`: the prior
`[ -n "$x" ] && export ...` form returns non-zero when the var is empty
(the common native-build case). Empirically bash exempts it from
errexit, but the explicit `if` form is unambiguous across shells. Both
native (Mach-O arm64) and cross (ELF linux/amd64) builds verified via
./scripts/run_task.sh build.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
build-macos-release cross-compiles on a Linux runner with
`CGO_ENABLED=0 GOOS=darwin GOARCH=<arch> ./scripts/run_task.sh build`.
run_task.sh bootstrapped the task runner via `go run task@vX`, which
inherited GOOS/GOARCH and cross-compiled the *task launcher itself* for
darwin — then failed to exec it on the Linux host:
fork/exec /tmp/go-build.../task: exec format error
(reproduced locally on darwin/arm64 by cross-compiling task to linux/amd64.)
task is a build orchestrator that must run on the host. Build it for the
host (GOOS/GOARCH cleared via `env -u`, installed to GOPATH/bin with
`go install`), then re-export the caller's cross-compile target so the
downstream `go build` inside scripts/build.sh still produces the
requested darwin artifact. Verified: a linux-host -> darwin and a
darwin-host -> linux cross-compile both succeed and emit the correct
target-arch binary.
Co-authored-by: Hanzo Dev <dev@hanzo.ai>
Neutralise the promotional surface around legacy upstream upgrade names
(Apricot / Banff / Cortina / Durango / Etna / Fortuna / Granite) while
preserving the on-disk Config schema and the IsXxxActivated() predicate
surface — those field names and methods are load-bearing for every
upstream-derived block parser, codec, and tx executor in the tree, and
Lux activates every gate at chain birth so they are inert in production.
Changes:
- upgrade/upgrade.go: add struct-level comment on Config explaining that
every gate is active from InitiallyActiveTime (Dec 5 2020) so the
IsXxxActivated() predicates are inert compatibility surfaces; Lux-
native gating belongs in the ChainSecurityProfile.
- upgrade/upgradetest/fork.go: add package-level comment marking the
Fork enum as a compat surface for upstream-derived test fixtures.
- vms/platformvm/docs/{block_formation_logic,chain_time_update}.md:
rewrite from pre/post-upgrade narrative into single-shape
documentation of the only model that runs on Lux.
- vms/platformvm/docs/validators_versioning.md, indexer/service.md,
config/config.md, vms/platformvm/warp/README.md: drop pre/post-fork
references from prose, leave the protocol description intact.
- vms/proposervm/proposer/windower.go, pre_fork_block.go,
vms/proposervm/block_test.go, vms/platformvm/warp/validator.go,
vms/platformvm/txs/executor/proposal_tx_executor.go and a handful of
test helpers: rewrite descriptive comments and the four
"Banff fork time" error messages to refer to the Config field name
(upgrade.Config.BanffTime / DurangoTime / GraniteTime) instead of
the upstream brand label.
- tests/granite_integration_test.go -> tests/lp181_integration_test.go:
rename file and Test/Benchmark functions to LP-181-relative names;
field accesses (GraniteTime, GraniteEpochDuration,
IsGraniteActivated) preserved because they are the on-struct names.
- network/network.go, network/peer/peer.go, chains/manager.go,
scripts/tests.upgrade.sh, .github/labels.yml: scrub stray
upstream-brand mentions from comments / labels.
What is intentionally preserved (compat shim policy, same logic as the
geth-config preservation rule for ~/work/lux/coreth /geth /genesis):
- upgrade.Config field names (ApricotPhaseNTime, BanffTime, ..., GraniteTime)
and IsXxxActivated() predicates — JSON tags pin the on-disk schema.
- upgradetest.Fork enum values (NoUpgrades..Granite) — referenced by
hundreds of upstream-derived test fixtures.
- Block-type identifiers (BanffStandardBlock, ApricotProposalBlock, ...)
— wire-format-load-bearing.
- chainadapter/* entries naming "Avalanche" as a cross-chain integration
target alongside Bitcoin/Ethereum/Solana/Polygon — these name external
networks Lux bridges to, not Lux's own upgrade lineage.
- RELEASES.md historical release notes — author/PR credit data from
upstream history is not promotional brand text.
Build verified: GOWORK=off go build ./... -> exit 0.