Files
bootnode/hanzo.yml
T
zeekayandHanzo Dev 734997e46c feat(fleet): the Lux fleet API is Go, and it runs where the fleet is
The lux.cloud dashboard was blank because the collector could not reach a
single validator. Two independent breaks, either one fatal:

  1. It called /ext. luxd 1.36 serves /v1 only — /ext/info is 404 — and the
     collector's _rpc() maps any non-200 to None, so every field came back
     null with no error anywhere.
  2. It dialled 15 hardcoded public LoadBalancer IPs. There are now zero
     LoadBalancer Services in lux-mainnet/testnet/devnet; those IPs do not
     answer at all (connect timeout, not 404).

Both are the same root defect: the observer was outside the thing it observes
and had to be told where to look. So the observer moves in. Running in lux-k8s,
a validator's address is its pod IP, read from the pod list on every
collection — there is nothing left to configure and nothing left to go stale.
Chain facts come from the node itself over /v1: info.getNodeVersion,
getNodeID, getNodeIP, peers, isBootstrapped per chain, eth_blockNumber at
/v1/bc/C/rpc (the bc/ segment is load-bearing; /v1/C/rpc is not a route) and
platform.getHeight at /v1/bc/P, which quotes its integer.

That also collapses the MODE A / MODE B split: there is one path, not two.

Two corrections to what the old collector reported:

  version is now what the fleet RUNS, not what the CR declares. The mainnet
  LuxNetwork CR asks for ghcr.io/luxfi/node:v1.34.0, a tag that does not
  exist; the nodes are on luxd/1.36.2. The dashboard must show the running
  truth, so observation wins and image keeps the declared value.

  external_ip becomes ip, matching console/lib/fleet.ts, and carries the
  address the node advertises for itself (info.getNodeIP) rather than an
  address someone wrote down.

Degraded is reported as degraded. A node that does not answer keeps every
field null and counts against nodes_rpc_ok; it is never smoothed over.

Placement: this stays in hanzoai/bootnode rather than folding into
hanzoai/cloud. cloud is not deployed on lux-k8s, and standing the whole
monolith up inside the chain cluster to run a read-only collector is a far
larger change than the collector itself. The surface is registered with zip's
Group; the route set is unchanged.

Deploy: Deployment/Service/RBAC in lux-k8s (lux-fleet.yaml, read-only —
list/get on pods and luxnetworks, no pods/proxy, no exec). The public URL
resolves to bootnode-k8s, so that cluster forwards just /v1/fleets/lux across
(lux-fleet-bridge.yaml); no other path on the host is touched.

Tests: a luxd stub that 404s anything outside /v1 and quotes platform height;
assertions that every probe path is under /v1, that no wire key is ever
omitted (an absent key reads as undefined and blanks a cell), that a down node
stays null, and that /v1/fleets/lux/zzzz is 404 next to a 200 on /overview.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 19:31:06 -07:00

64 lines
2.3 KiB
YAML

# Hanzo CI/CD — one declarative build + test config.
#
# Read by platform.hanzo.ai, the sole orchestrator for this repo. On every
# push / PR the platform GitHub App delivers the event to platform, which reads
# THIS file and builds each image on the shared in-cluster `hanzo-build-*`
# arcbuild pool (bootnode has no self-hosted runners of its own — the reason the
# old GitHub-Actions `.github/workflows/cicd.yml` dead-ended and was removed).
# Registry credentials live in the cluster (kaniko-ghcr), never in GitHub.
#
# Images publish to ghcr.io/bootnode/bootnode as `<sha>-amd64-<tag-suffix>`.
# Rollout is GitOps, not platform: luxfi/universe pins the freshly-built tag and
# Argo applies it. The `deploy:` block below is therefore advisory (platform
# treats the Deployment-style `services:` form as build-only).
images:
- name: api
context: ./api
dockerfile: ./api/Dockerfile
repo: ghcr.io/bootnode/bootnode
tag-suffix: api
- name: web
context: ./console
dockerfile: ./console/Dockerfile
repo: ghcr.io/bootnode/bootnode
tag-suffix: web
# The Lux fleet API: pure Go, built from the repo-root module. Hanzo repos
# publish to ghcr.io/hanzoai — the two entries above are the legacy refs the
# running bootnode Deployments still pull.
- name: fleet
context: .
dockerfile: ./Dockerfile.fleet
repo: ghcr.io/hanzoai/bootnode
tag-suffix: fleet
# Commands run on the arc runner after the image builds. Non-zero fails the run.
test:
- name: api
run: >-
docker compose -f compose.yml up -d postgres redis >/dev/null 2>&1;
cd api &&
DATABASE_URL=postgresql+asyncpg://bootnode:bootnode@localhost:5432/bootnode
JWT_SECRET=ci API_KEY_SALT=ci
uv run pytest -q --tb=short
# Roll the freshly-built images onto the cluster. Only on the listed branches /
# on v* tags. The operator (or kubectl, via the KMS kubeconfig) patches each
# Service to the new image. Omit this block for build-only repos.
deploy:
cluster: bootnode-k8s
namespace: bootnode
on:
- main
services:
- name: web3-platform-api
image: api
- name: web3-platform-web-bootnode
image: web
# KMS source for /deploy secrets (GHCR_TOKEN, KUBECONFIG). workspace + endpoint
# are supplied as repo vars so this file carries no secrets.
kms:
path: /deploy
environment: prod