Files
zeekayandHanzo Dev dada5a3169 health,build: make /v1/health and /v1/metrics report reality
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>
2026-07-25 15:10:46 -07:00

148 lines
4.3 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
print_usage() {
printf "Usage: build [OPTIONS] [BUILD_ARGS]
Build node
Options:
-r Build with race detector
-p Build profile (default: minimal)
Profiles:
minimal - ZAP transport, all VMs, stripped (~30MB)
core - ZAP, P/X/C only, stripped (~25MB) [plugin-ready]
full - gRPC+ZAP, all features, stripped (~36MB)
dev - ZAP, all VMs, debug symbols (~40MB)
tiny - all VMs + UPX compressed (~20MB)
BUILD_ARGS: Additional arguments to pass to go build (e.g., -tags purego)
Examples:
./scripts/build.sh # minimal stripped build
./scripts/build.sh -p full # full build with all VMs and gRPC
./scripts/build.sh -p dev # development build with debug symbols
./scripts/build.sh -p tiny # smallest binary (UPX compressed)
./scripts/build.sh -r # minimal with race detector
"
}
race=''
profile='minimal'
while getopts 'rp:' flag; do
case "${flag}" in
r)
echo "Building with race detection enabled"
race='-race'
;;
p)
profile="${OPTARG}"
;;
*) print_usage
exit 1 ;;
esac
done
# Shift to get remaining arguments (build tags, etc.)
shift $((OPTIND-1))
REPO_ROOT=$( cd "$( dirname "${BASH_SOURCE[0]}" )"; cd .. && pwd )
# Configure the build environment
source "${REPO_ROOT}"/scripts/constants.sh
# Determine the git commit hash to use for the build
source "${REPO_ROOT}"/scripts/git_commit.sh
# Configure build based on profile.
#
# `metrics` is a base tag, not a profile choice: without it luxfi/metric's
# NewRegistry() resolves to the no-op registry (registry_noop.go, //go:build
# !metrics), every metric registers into a black hole, and /v1/metrics answers
# 200 with a zero-byte body — while --api-metrics-enabled=true still reports
# metrics as on. Whether metrics are served is the runtime flag's decision
# alone; the build must not silently overrule it.
base_tags="metrics"
tags="${base_tags}"
ldflags="-X github.com/luxfi/node/version.GitCommit=$git_commit \
-X github.com/luxfi/node/version.VersionMajor=$version_major \
-X github.com/luxfi/node/version.VersionMinor=$version_minor \
-X github.com/luxfi/node/version.VersionPatch=$version_patch \
$static_ld_flags"
strip_flags=""
upx_compress=false
case "${profile}" in
minimal)
echo "Profile: minimal (ZAP, all VMs, NAT, stripped)"
tags="${base_tags},nattraversal"
strip_flags="-s -w"
;;
core)
echo "Profile: core (ZAP, P/X/C chains only, stripped)"
# No optional VMs - future plugin-ready build
strip_flags="-s -w"
;;
full)
echo "Profile: full (gRPC+ZAP, all VMs, all features, stripped)"
tags="${base_tags},grpc,nattraversal,zxcvbn"
strip_flags="-s -w"
;;
dev)
echo "Profile: dev (ZAP, core VMs, debug symbols)"
# No strip flags, keep debug symbols
;;
tiny)
echo "Profile: tiny (all VMs, NAT + UPX compressed)"
tags="${base_tags},nattraversal"
strip_flags="-s -w"
upx_compress=true
;;
*)
echo "Unknown profile: ${profile}"
print_usage
exit 1
;;
esac
# Apply strip flags to ldflags
if [ -n "$strip_flags" ]; then
ldflags="$strip_flags $ldflags"
fi
# Build tags argument
tags_arg=""
if [ -n "$tags" ]; then
tags_arg="-tags=$tags"
fi
# Enable Go 1.26 experimental features if not already set
export GOEXPERIMENT="${GOEXPERIMENT:-runtimesecret}"
echo "Building Lux Node v${version_major}.${version_minor}.${version_patch} with [$(go version)] GOEXPERIMENT=${GOEXPERIMENT}..."
go build -trimpath ${race} ${tags_arg} "$@" -o "${node_path}" \
-ldflags "$ldflags" \
"${REPO_ROOT}"/main
# Apply UPX compression if requested
if [ "$upx_compress" = true ]; then
if command -v upx &> /dev/null; then
echo "Compressing with UPX..."
upx_flags="--best --lzma"
if [[ "$OSTYPE" == "darwin"* ]]; then
upx_flags="$upx_flags --force-macos"
fi
# shellcheck disable=SC2086
upx $upx_flags "${node_path}"
else
echo "Warning: UPX not found, skipping compression"
echo "Install with: brew install upx (macOS) or apt install upx (Linux)"
fi
fi
# Show binary size
echo ""
echo "Binary: ${node_path}"
# shellcheck disable=SC2012
ls -lh "${node_path}" | awk '{print "Size: " $5}'