Commit Graph
58 Commits
Author SHA1 Message Date
zooqueenandHanzo Dev cf106de1da rip(casdoor): name the identity provider Hanzo IAM, and drop the dead manifest
main still called the provider Casdoor in ten places. hanzo.id is served by
github.com/hanzoai/iam, which its own LICENSE states is a clean-room
implementation carrying no third-party licensed source. The Casdoor-derived
server was hanzoai/iam-v1, retired and no part of this stack.

Every fact the text carried is kept. IAM really does stamp iss claims that
openid-client rejects, which is why the NextAuth provider is type: "oauth" and
why setting `issuer` breaks it; the applications really do live in the
iam-init-data configmap. Only the vendor name changed.

iam.py claimed a "Casdoor-compatible" flow. It is a standard flow — RFC 6749 and
OIDC Core — which is what HIP-0111 requires; naming a vendor for a capability
that has an RFC is what the standard exists to stop.

infra/k8s/cloud-deployment.yaml is deleted rather than edited. Its env block
named casdoorEndpoint / casdoorOrganization / casdoorApplication, and nothing
reads them: hanzoai/cloud configures IAM through IAM_URL, IAM_MINT_CLIENT_ID and
CLOUD_IAM_ISSUER, and ships no conf/. Renaming them to IAM_* would have produced
config that looks live and still does nothing. The file is unreferenced by
kustomization.yaml, duplicates declared state that belongs in universe
(infra/k8s/cloud/), pins registry.digitalocean.com instead of ghcr.io/hanzoai,
and pins a floating :latest tag that must never reach a cluster.

Landed directly on main via an isolated worktree. chore/use-datastore-fork is
139/137 divergent from a history rewrite; merging it would re-import that
lineage, and only two commit subjects there are genuinely absent from main.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-11 13:46:13 -07:00
zeekayandHanzo Dev dee76a9e38 fix(config): list settings arrive comma-separated, as they are written
Settings() raised SettingsError constructing ALLOWED_ORIGINS. The field is
declared list[str] and documented "comma-separated in env", but
pydantic-settings decodes complex types from the environment as JSON before
any validator runs, so the plain `a,b,c` that every Deployment manifest, .env
and compose file writes was fatal — not a bad value, an unconstructable
Settings.

That made every route depending on it 500: require_admin builds an IAMClient,
which calls get_settings(). The lux.cloud fleet overview returned HTTP 500 the
moment the admin gate shipped, and the failure was in the gate, nowhere near
the collector it guarded.

NoDecode hands the raw string to one validator that splits it, for both list
settings. One variable, one format, decided in one place.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 19:35:59 -07:00
zeekayandHanzo Dev 3f54647e51 fix(lux): the reported version is the one that is running
lux-operator is scaled to 0, so the LuxNetwork CR's image tag is only a
declaration and can be arbitrarily stale. Mainnet's CR says v1.34.0 — a tag
that does not exist in the registry — while all five nodes answer
info.getNodeVersion with luxd/1.36.2; testnet's says v1.32.12 against a
running 1.36.24.

Reporting the CR tag as `version` would put a fabricated number on the
dashboard. `version` is now the observed value and `image` stays as declared,
so a drift between intent and reality is visible instead of hidden.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 19:26:09 -07:00
zeekayandHanzo Dev 1f30a4c21c fix(lux): the fleet is observed through lux-k8s, on /v1
luxd serves /v1 only — server/http/server.go pins baseURL="/v1" — so every
/ext call in this repo was a silent no-op. The fleet collector's _rpc()
returns None on non-200, so the lux.cloud dashboard rendered nodes_rpc_ok:0,
version:null and every node rpc_reachable:false while the fleet was in fact
running luxd/1.36.2 (mainnet), 1.36.24 (testnet), 1.36.25 (devnet).

Fixing the path alone would not have helped: the collector addressed each
validator by a hardcoded public LoadBalancer IP, and there are now zero
LoadBalancer Services in lux-mainnet/testnet/devnet. Those IPs time out. So
node addressing is no longer configured — it is derived from the cluster on
every collection, and the one way to reach a validator is the lux-k8s API
server's pod proxy, since luxd RPC is cluster-internal.

That collapses the old MODE A / MODE B split into a single path: read the CR
for the declared shape, the pods for phase/readiness/IP, then each pod's
JSON-RPC through the proxy. One credential authenticates both. Deleting the
endpoint ConfigMap and its 15 baked-in IPs removes the failure mode where
infrastructure moves and a dashboard quietly reports zeros.

Probes move to /v1/health/liveness, not /v1/health: a degraded-but-running
validator answers /v1/health with 503, so probing it would restart nodes that
are merely behind. Any fleet from charts/lux could never have gone Ready.

LuxNode.external_ip becomes .ip — with no public per-pod endpoint, the honest
value is the pod IP.

Avalanche endpoints keep /ext; that is their API, not ours.

Measured, with controls: pod-proxy POST /v1/info -> 200 on all 15 pods;
/ext/health -> 404; /zzz/health -> 404; nonexistent pod -> 404. A test asserts
every probe path is under /v1 and fails if /ext returns.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 19:18:04 -07:00
zeekay 39cb316a15 merge bootnode/bootnode into this history — one repo, both halves
github.com/bootnode/bootnode is the canonical home. These two diverged from
766da81c with real work on BOTH sides — 21 commits there, 26 here — so this is
a merge, not a fork-kill. Deleting either side would have destroyed shipped
work.

From CANONICAL (bootnode/bootnode):
  · Base/SQLite storage, Postgres coupling dropped (5650324)
  · Cluster + Node models — models this side never defined
  · native CI: hanzo.yml, zero .github/workflows (a698acc, aff0952)
  · images to ghcr.io/bootnode/bootnode, its own org (9d0957c)
  · luxd fleet dashboard, k8s validator detector

From HERE (hanzoai/bootnode):
  · HIP-0111 IAM: SPA PKCE, JWKS token validation (ed27226),
    JWT audience enforcement (49eb8cb) — security features canonical lacks
  · runtime white-label brand catalog (422f8f1 + today's work)
  · static-export build (852bb7c)
  · today: toolchain pinned, static export via hanzoai/static, one API
    resolver, honest error states

Seven conflicts, each resolved on evidence, not preference:

  web/lib/brand.ts        OURS   — canonical still hardcodes
                                   bootnode → iam.url "https://hanzo.id",
                                   which is the "Sign in to Hanzo ID" leak
                                   fixed today. It also carries a duplicate
                                   `skillsOrg` field.
  web/Dockerfile          OURS   — canonical runs `pnpm config set
                                   dangerously-allow-all-builds true` AND
                                   `--no-frozen-lockfile`: disables the
                                   supply-chain guard and ignores the lockfile.
  db/models.py            THEIRS — has Cluster + Node and no Postgres dialect.
  db/__init__.py          THEIRS — matches those models.
  .github/workflows/*     DELETED— canonical already went native.
  api/bootnode/main.py    BOTH   — took `__version__` from canonical (better
                                   than the hardcoded "2.0.0" here) and kept
                                   the conditional `docs` from b06f09f
                                   (canonical re-adds "docs": "/docs", a URL
                                   production disables, so it 404s).

This drops a386c9b, the Node/MPCWalletModel commit written earlier today. Its
problem did not exist on the canonical side: those models are already there,
and there is no MPC router to need MPCWalletModel. It was written against the
storage layer canonical replaced in June.

Verified on the merged tree, not assumed: `import bootnode.db` succeeds and
exports 14 models, all 27 API routers import, 0 postgresql dialect imports
remain, web tsc reports 0 errors and the production build exits 0 with
localhost:8000 in 0 chunks.
2026-07-26 14:18:12 -07:00
zeekay baddf8096e fix(api): the db package could not import — define the models it declares
`bootnode/db/__init__.py` imported and re-exported three names that models.py
does not define, so `import bootnode.db` raised ImportError on main. Every
consumer died with it: the e2e job's init_db(), the nodes API, the MPC API, the
keys API, the seed. Prod is unaffected only because it runs image 1.0.1, built
before this landed.

It was invisible because test-api ran

    pytest -v --tb=short || echo "No tests yet"

The `|| echo` makes the step exit 0 unconditionally, so CI reported GREEN while
every test module failed to import. That mask is removed; CI can fail honestly.

The consequence was not subtle: docker-release triggers on
`workflow_run: ["CI"] completed`, CI was red, so the image build was SKIPPED
every time — which is why the deployed web image sat at 0.0.1-web from June
while main moved on.

  Cluster        → renamed OrgCluster at some point; call sites updated.
                   The `"kind": "Cluster"` CRD strings are correct and untouched.
  Node           → never existed in ANY revision (checked back to 2026-01-26).
                   Defined here from the contract the code already declares.
  MPCWalletModel → likewise. Distinct from SmartWallet, which is ERC-4337
                   account abstraction, not threshold signing.

Design, in the terms asked for:

DECOMPLECTED — Node stores DESIRED state only. Sync progress, peer count, block
height, cpu/memory, uptime and the backing pod are facts about the cluster, and
fleet_manager already reads them from the Cluster CR. A copy in this table would
be a second source of truth, stale the instant it is written. That split is why
the model is small; it is the spec half of the spec/status pair the CRD layer
already uses, not a new idea.

TYPED — status/node_type/sync_mode are closed Enums at both the Python and the
column level, not free strings. This class of bug was live: the seed wrote
"running" while NodeStatus offers pending/init/starting/bootstrapping/healthy/
unhealthy/terminated — provably DISJOINT sets, so the dashboard's
`filter(n => n.status === "running")` could never match a backend value and
"active nodes" was structurally always 0. One vocabulary now, defined once in
fleet_models and imported everywhere; seed and both frontend call sites moved
onto it.

SMALL INTERFACE — two properties (`is_serving`, `slug`) and a repr. Everything
else is a column read. No cleverness to undo later.

Flagged, not silently reproduced: MPCWalletModel.encrypted_shares holds key
material in the database. It is encrypted, but the standing rule is that secrets
live in KMS. That column is a migration target, documented in the model.

Verified: `import bootnode.db` succeeds, all three enums resolve, every seed
status value is a member of NodeStatus, web tsc 0 errors, web build exits 0.
2026-07-26 13:49:23 -07:00
zeekay 5d1620ab8e fix(api): stop advertising a /docs URL that production disables
The FastAPI app deliberately turns off docs/redoc/openapi in production:

    docs_url="/docs" if not settings.is_production else None

but the root endpoint returned `"docs": "/docs"` unconditionally. So the
public API root promised a URL that 404s — api.bootno.de/ says
{"name":"Bootnode API","version":"2.0.0","docs":"/docs","status":"operational"}
while /docs, /redoc and /openapi.json all return {"detail":"Not Found"}.

Anyone reading the root to find the schema followed a dead link, and the
disabled-in-prod decision was correct, so the answer is to tell the truth
rather than re-enable them.

root() now mirrors app.docs_url and omits the key entirely when docs are off,
so the two cannot drift again — there is no second place to remember when the
setting flips. Verified both ways: with docs_url None the key is absent, with
"/docs" it is present.
2026-07-26 10:03:31 -07:00
zeekayandHanzo Dev 37b6492d08 iam: canonical endpoints; drop the hand-rolled login button
The API resolved JWKS at {iam_url}/.well-known/jwks.json and exchanged
codes at {iam_url}/oauth/token — measured 401 on both; the canonical
/v1/iam/.well-known/jwks and /v1/iam/oauth/token answer.

brand.json pointed at iam.bootno.de, which does not resolve (525). The
brand's canonical issuer is id.bootno.de, already what web/lib/brand.ts
uses.

HanzoLoginButton hand-rolled a /oauth2/authorize URL that exists on no
IAM deployment. It was imported but never rendered — the login page has
used getIam().signinRedirect() (@hanzo/iam PKCE) since HIP-0111 — so it
is deleted rather than repointed. One way in.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-25 13:34:40 -07:00
zeekay 537449038e feat(fleet): server-side admin gate on the luxd fleet overview
Deny-by-default on the ONE cross-tenant infra route, enforced on both the
standalone fleet_app and the mounted api/fleets router:
  unauthenticated -> 401 · invalid token -> 401 · non-admin -> 403 · admin -> 200

Adversarial test exercises the gate end-to-end over real HTTP; only JWKS
verification and the luxd RPC collector are stubbed.
2026-07-25 12:35:14 -07:00
zeekay d5fbe67fad feat(lux): graphical fleet dashboard at lux.cloud
Turn lux.cloud into a live graphical fleet dashboard for the managed luxd
fleet (devnet/testnet/mainnet, 5 validators each on do-sfo3-lux-k8s).

Backend (read-only collector, no mutations, no monolith dependency):
- core/chains/lux_fleet.py: reads the operator LuxNetwork CR (lux.cloud/v1)
  for validators/version/plugin-set/phase, lists luxd-N pods + per-pod LB
  Services, and queries luxd JSON-RPC per node (info.getNodeVersion/getNodeID/
  peers/isBootstrapped, C-Chain eth_blockNumber, P-Chain platform.getHeight),
  plus a native D-Chain DEX summary. Aggregates all 3 nets concurrently.
- fleet_app.py: minimal standalone FastAPI exposing GET /v1/fleets/lux/overview
  (public, read-only). Reuses the bootnode api image; isolated from the
  (currently import-broken) monolith.
- fleets/__init__.py: same overview endpoint also mounted on the main router.
- Uses a READ-ONLY lux-k8s kubeconfig from KMS; only public chain data leaves.

Frontend (Next.js 16, Lux white-label, auto by hostname):
- lib/fleet.ts: typed client + health/status derivations.
- components/lux/fleet-dashboard.tsx: graphical multi-net dashboard — totals,
  world topology map, per-network cards (phase, version, plugin-set, peers,
  C-Chain height, DEX state, per-chain C/P/X/D bootstrap), expandable per-node
  tables (node ID, version, peers, heights). Auto-refresh 15s.
- app/fleet/page.tsx: public /fleet page (the lux.cloud fleet dashboard).
- components/lux/fleet-strip.tsx + page.tsx: live fleet status strip on the
  landing hero.
- navbar + dashboard nav: Fleet links; register existing Lux fleet mgmt page.
- brand.ts: add skillsOrg (fixes pre-existing typecheck/build break).

Deploy: infra/k8s/lux-fleet.yaml — lux-fleet Deployment/Service + ExternalSecret
(lux-k8s RO kubeconfig from KMS) + ingress path /v1/fleets/lux/overview on
api.cloud.lux.network.

Verified: collector live against all 3 nets (real heights/peers/versions/DEX);
api + collector tests pass (8/8); web typecheck + production build green;
/fleet renders live data in browser (screenshot).
2026-07-25 12:35:14 -07:00
zeekayandHanzo Dev 6b0e4399de fix(cors): allow lux.cloud + console.lux.cloud origins
lux.cloud is the canonical Lux web3 console; its RPC & API-Keys section calls
the control plane (api.cloud.lux.network) from the browser. The production CORS
allowlist had cloud.lux.network but not lux.cloud, so key create/list/revoke
preflights 400'd. Add the canonical console origins to production_origins.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-15 23:09:59 -07:00
zeekayandGitHub 67962df1c6 feat(nodes): k8s detector — discover & adopt operator-managed validators (#15)
Bootnode could only see nodes it launched as Docker containers; real Lux/Zoo
validators run as K8s workloads behind operator CRDs and were invisible to
GET /v1/nodes. Add a detector that scans a cluster's kubeconfig and adopts
its running validators into the DB.

- core/k8s_nodes.py: K8sNodeDetector scans LuxNetwork (lux.network/v1alpha1
  luxnetworks) + ZooChain (zoo.network/v1alpha1 zoochains) CRs across all
  namespaces via CustomObjectsApi, enriches each with its luxd/zood pods for
  live status, and upserts Cluster + Node rows. Adoption, not recreation:
  existing rows (keyed on (cluster_id, pod_name)) are updated in place, so
  re-scans are idempotent. New operator chains are one descriptor row.
- db/models.py: add Cluster + Node models (portable JSON/Uuid types, matching
  the existing db_nodes.py / seed_nodes.py consumers that imported them).
- api/nodes: merge DB-adopted k8s nodes into list_nodes() alongside Docker
  discovery; add POST /v1/nodes/detect and POST /v1/nodes/clusters/{id}/scan
  (kubeconfig resolved via the shared fleets path). Reuses existing get_db.
- pyproject: add aiosqlite (config.database_url now defaults to
  sqlite+aiosqlite; the driver was missing).

Detector adopt/idempotency + model mapping verified against in-memory SQLite
with faked LuxNetwork/ZooChain CRs and pods; full app OpenAPI builds with the
new routes; 112 existing tests pass (pre-existing test_lux_fleet /
test_chain_config failures unchanged).
2026-06-23 10:31:23 -07:00
zeekayandGitHub 56503248af feat(db): Base/SQLite storage (VFS→SeaweedFS), drop Postgres coupling (#13)
bootnode stores its catalog in Base/SQLite — the canonical store, durable via
VFS→SeaweedFS — not Postgres. Make the models dialect-portable and the engine
sqlite-aware:
- models.py: postgres JSONB/UUID → portable sqlalchemy JSON/Uuid
- config.py: database_url is str (not PostgresDsn) so sqlite+aiosqlite:/// DSNs
  validate; default = sqlite+aiosqlite:////data/bootnode/bootnode.db
- session.py: normalize sqlite:// → sqlite+aiosqlite://, and skip the sized
  pool (pool_size/max_overflow are invalid on sqlite) + check_same_thread off

Postgres DSNs still accepted (legacy/multi-instance) but no longer required.
2026-06-23 09:19:59 -07:00
zeekay 9d0957c774 ci: bootnode images go to ghcr.io/bootnode/bootnode (own org, not hanzoai)
bootnode is its own GitHub org — its images belong in its own GHCR namespace,
per the never-mix-registries rule. This also removes cross-org friction: the
repo's own GITHUB_TOKEN pushes to ghcr.io/bootnode/* (same org), so once the
arcd App is on the bootnode org, build+push needs no cross-org token.
2026-06-19 21:14:50 +00:00
zeekay 94a58577ba fix(nodes): use multi-arch luxd image (C-Chain EVM on arm64 + amd64)
ghcr.io/luxfi/node:latest is amd64-only; under qemu on arm64 the out-of-process
C-Chain EVM plugin can't complete its go-plugin handshake. The v1.30.14-dchain
tag is multi-arch (amd64+arm64) — verified: arm64 build boots natively, C-Chain
eth_chainId=0x7a69 (31337), no qemu. lux/hanzo/zoo now have working C-Chains on
both local (arm64) and DOKS (amd64).
2026-06-19 19:32:48 +00:00
zeekay 4b23b671a6 feat(api): expose mpc/keys/launch + add contracts & subgraphs
- register previously-dark routers: mpc (threshold ECDSA), keys, launch
  (explorer/indexer/gateway/exchange/faucet ecosystem deploy)
- new /v1/contracts: deploy via signed tx, receipt, code, eth_call
- new /v1/subgraphs: The Graph admin (create/deploy/remove/status)
- align launch CRD luxd ports to canonical 9630/9631
2026-06-19 00:32:57 +00:00
zeekay 9090b70ea2 fix(nodes): correct luxd deploy to canonical Lux
- image luxfi/luxd:latest -> ghcr.io/luxfi/node:latest (the pullable one)
- numeric network-id (localnet=1337/devnet=3/testnet=2/mainnet=1), drop
  Avalanche 'fuji' and 'subnet'/--track-subnets language
- HTTP 9630 / staking 9631 (not 9650); --data-dir=/data
- stage bundled VM plugins into --plugin-dir so C-Chain EVM + zk/quantum/
  threshold/bridge/dex VMs load (mirrors luxd-boot.sh)
- fix port allocator feedback loop that overflowed host ports past 65535
2026-06-19 00:32:57 +00:00
zeekay d6203fd19b feat(api): pin to v1.0.0, sourced DRY from __version__
Never launched, so start at 1.0.0. All app/ZAP-server/OTEL version strings
now read bootnode.__version__ (ZAP protocol version stays its own axis).
2026-06-19 00:32:57 +00:00
zeekayandHanzo Dev 4f0c68df12 fix: get the API booting (stale refactor imports + qdrant healthcheck)
The api crashed on import after a half-finished model refactor, and the qdrant
healthcheck used a binary not in the image.

- compose.yml: qdrant ships no wget/curl, so its healthcheck always failed and
  blocked the api (depends_on service_healthy). Dropped the broken healthcheck and
  gate the api on qdrant service_started (qdrant itself serves fine on :6333).
- db/__init__.py: reconcile to the real models — drop removed Node + Cluster
  (now CRDs) and MPCWalletModel; export the actual set (User, OrgCluster,
  SmartWallet, Subscription, TeamMember, etc.).
- api/mpc + api/keys: MPCWalletModel -> SmartWallet (the renamed model).

API now boots healthy: GET /health 200 {"status":"healthy","version":"2.0.0"},
/docs serving, 42 node/chain/fleet endpoints live (fleets deploy/scale, presets,
pod health/logs). Full local stack up: postgres, redis, clickhouse, qdrant,
meilisearch, api.

Note (follow-up, not blocking boot): api/nodes/db_nodes.py + db/seed_nodes.py
still import removed Node/Cluster models — dead code to remove or port to the
fleet/CRD path; the web frontend build fails on a stale pnpm lockfile.

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-18 22:33:54 +00:00
zeekay a6c2aee6cf chore(repo): merge bootnode-org/main back into the Hanzo-side work
These two repos share root commit 8999f3c3 ("Initial commit") but
diverged long ago — hanzoai/bootnode evolved the recent stack
(api-go, brand package, etc.) while bootnode-org/main retained the
mainline 2026 history (175 commits we don't have).

Brand-leak fix: this work belongs in github.com/bootnode/bootnode,
not github.com/hanzoai/bootnode. Per the no-cross-brand-leak policy,
merge both histories together and push the result back to
bootnode/bootnode as the canonical home.

After this lands:
- bootnode/bootnode is the canonical repo
- hanzoai/bootnode gets archived with a redirect notice
- @bootnode-io/brand npm package's `repository.url` already points at
  bootnode/bootnode (set in the earlier package.json scaffold)

# Conflicts:
#	.github/workflows/ci.yml
#	api/bootnode/api/__init__.py
#	api/bootnode/api/billing/__init__.py
#	api/bootnode/config.py
#	api/bootnode/core/billing/__init__.py
#	api/bootnode/db/models.py
#	api/bootnode/main.py
#	api/tests/test_billing.py
#	web/app/dashboard/infrastructure/page.tsx
#	web/app/dashboard/organization/billing/page.tsx
#	web/app/dashboard/organization/page.tsx
#	web/app/dashboard/page.tsx
#	web/app/layout.tsx
#	web/components/navbar.tsx
#	web/components/unified-navigation.tsx
#	web/lib/brand.ts
#	web/public/logo/zoo-icon.svg
#	web/public/logo/zoo-logo-white.svg
#	web/public/logo/zoo-logo.svg
#	web/tsconfig.tsbuildinfo
2026-05-19 10:23:43 -07:00
zeekayandGitHub 97321f67b0 feat: Go rewrite scaffold — ZAP-first, Mount() per HIP-0106 (#1)
Adds a Go subsystem alongside the existing Python FastAPI service so
bootnode can fold into `superbase` per HIP-0106. The Python service in
`api/bootnode/` stays untouched.

What landed:

- `schema/bootnode.zap` — public typed surface (chains, RPC, tokens,
  NFTs, transfers, smart wallets, ERC-4337, gas, webhooks, fleets,
  nodes, usage). No `.capnp` anywhere — ZAP only, per brand policy.
- `zap/gen/` — hand-rolled Go bindings that match what `zapc generate`
  emits, so `go build` works without zapc installed locally.
- `api/mount.go` — the HIP-0106 contract:
    func Mount(r chi.Router, deps Deps) error
  mounts every FastAPI-parity route group through `api/handlers/`.
- `api/deps.go` — cross-subsystem ZAP client interfaces (IAM, KMS,
  Base, O11y, Commerce). No business logic, no hard imports — keeps
  the Bootnode module independent of sibling subsystems until they
  ship their typed clients.
- `api/handlers/` — 149 chi routes mounted (Python has 150). Each
  handler body is a 501 stub with a `TODO(bootnode-port)` pointing at
  the Python file + symbol to port.
- `api/middleware/` — IAM-backed auth, OTEL-friendly telemetry,
  per-org rate limit (stub).
- `api/store/` — per-tenant SQLite access via `deps.Base.Open(orgID,
  "bootnode")` per HIP-0302.
- `api/web3/` — chain registry + RPC pool surface (production version
  prefers luxfi/* over upstream go-ethereum).
- `cmd/bootnode/main.go` — standalone shim; same `api.Mount` superbase
  calls in production.
- `docs/PYTHON_TO_GO_PORT.md` — endpoint-by-endpoint port map.

Build: `go build ./...` and `go vet ./...` clean. Standalone shim
smokes 200 on /health, /ready, /v1/chains; 401 on auth-required
endpoints with no JWT; 501 on every TODO route.

Out of scope (not in this commit):
- Handler bodies (Phase 2 — porting in RPC → tokens → NFTs → ...
  priority order).
- superbase wiring (lands when the superbase repo opens its main).
- Python service removal (stays until handlers reach prod parity).
2026-05-18 23:36:46 -07:00
zeekay 0aa9c4e346 feat: add Explorer + MPC product pages, fleet management, operator, Helm charts, and CI/CD
- Add Explorer product page with branded block explorer deployment, contract
  verification, Etherscan-compatible API, and multi-chain support
- Add MPC product page with deep Lux MPC integration: CGGMP21 (ECDSA),
  FROST (EdDSA), LSS resharing, 15+ chain support, threshold signing API
- Update landing page with 6-product grid (Node, Data, Wallets, Explorer,
  MPC, Rollups) and 8 API cards including Explorer API and MPC Signing
- Update navbar and command menu with Explorer and MPC products
- Add Lux fleet management dashboard, observability, and services pages
- Add AI chat component and unified navigation with org switcher
- Add Kubernetes operator (Rust) for multi-chain node lifecycle management
- Add Lux Helm charts with genesis configs for devnet/testnet/mainnet
- Add docker-release.yml CI/CD pipeline for automated DOKS deployment
- Add monitoring, network policies, web deployments, and ingress updates
- Refactor API: fleet management, billing tracker, observability endpoints
- Update brand config, API client, and infrastructure manifests
2026-03-12 14:48:40 -07:00
zeekay 72cf6e4c3b fix: update IAM auth integration for bootnode 2026-03-09 23:27:10 -07:00
zeekay 1ce1924c41 fix: convert DATABASE_URL to asyncpg scheme for async SQLAlchemy engine
The K8s DATABASE_URL env var uses postgresql:// which makes
create_async_engine try to load psycopg2 (sync driver). Add
_ensure_async_dsn() to convert postgresql:// and postgres:// to
postgresql+asyncpg:// at engine creation time. asyncpg is already
in dependencies.
2026-03-03 08:13:33 -08:00
zeekay db21b65cb7 fix: run ZAP Cap'n Proto server inside capnp.kj_loop() background task
capnp.AsyncIoStream.create_server() must be called from within an active
KJ event loop context. Wrap the entire serve lifecycle in a background
asyncio Task that runs under `async with capnp.kj_loop()`, fixing the
startup error: "The KJ event-loop is not running on this thread".

The server now signals readiness via `_running=True` after bind and
the caller polls for up to 1 s before raising if startup fails.
2026-03-01 13:50:47 -08:00
zeekay 636e4279bb chore: update domain references from bootnode.dev to bootno.de
Update ZAP endpoints, CORS origins, K8s annotations, and documentation
to use the new bootno.de primary domain.
2026-02-26 15:25:27 -08:00
zeekay 3da4e9740d feat: add 26 chains to registry (10 -> 36 total)
L1: Gnosis, Celo, Fantom, Cronos, Moonbeam, Moonriver, Harmony,
    Rootstock, Fuse, Metis
L2: zkSync Era, Scroll, Linea, Polygon zkEVM, Blast, Mantle, Mode,
    Zora, World Chain, Abstract, Starknet
EVM: Canto, Kaia, Aurora, Boba, Astar

Each chain includes mainnet + testnet networks with chain_id,
explorer_url, native_currency, and public RPC defaults in .env.example.
Adds STARKNET variant to ChainType enum for non-EVM support.
2026-02-26 15:15:42 -08:00
zeekay 9b6b6a1f72 feat: add cloud compute billing tiers and authorization API
Cloud compute quotas per tier (Free: 1 Linux/20hr, PAYG: 5L+2W+1M,
Growth: 20L+5W+3M, Enterprise: custom). Adds billing endpoints for
control-plane authorization, quota checks, and usage reporting.
2026-02-13 15:33:54 -08:00
zeekay 3fe4d9fa43 fix: enforce Hanzo KMS for all secrets, remove plaintext credentials
- Remove hardcoded Secret from bootnode-deployment.yaml (conflicted with ExternalSecret)
- Fix Postgres/Redis pods to read passwords from KMS-managed bootnode-secrets
- Expand ExternalSecret from 7 to 30 secrets with UPPERCASE_UNDERSCORE keys matching Pydantic
- Enable hanzo-kms SDK as optional dependency for direct KMS access outside K8s
- Improve inject_secrets() startup logging to indicate active secrets path
- Rewrite secrets.yaml as documentation-only with flow diagram and setup guide
- Fix compose.yml Bitcoin healthcheck to use env vars instead of hardcoded password
2026-02-12 13:05:04 -08:00
zeekay 8edcaa614f fix: production billing hardening + Stripe→Commerce migration
- Replace eval() with json.loads() in usage tracker (security fix)
- Webhook verification fails-closed in production when secret missing
- Add Commerce env vars to K8s deployment (URL, API key, webhook secret)
- Expose ZAP port 9999 in Dockerfile and K8s service
- Replace Stripe env vars with Square in compose.yml
- Update privacy/terms pages: Stripe→Square via Hanzo Commerce
- Add full billing API client module to web/lib/api.ts (17 endpoints)
- Fix hardcoded localhost URLs in dashboard/analytics/organization pages
- Add production webhook rejection test (57 tests passing)
2026-02-12 11:22:03 -08:00
zeekay fa2bcb0455 fix: unified billing error handling + add billing tests
unified.py: _create_customer now raises CommerceError instead of
returning fake local_ customer IDs that mask failures.
create_subscription raises CommerceError instead of bare Exception.
report_usage raises CommerceError so callers know when usage wasn't
reported.

Added 12 new tests: config AliasChoices regression (P0), commerce
client error paths, unified client error propagation, and webhook
handler edge cases. All 56 billing tests pass.
2026-02-12 10:47:45 -08:00
zeekay 82c099d128 fix: resolve config mismatch + auth security hardening
P0: config.py defined hanzo_commerce_* but billing code accessed
settings.commerce_* — causes AttributeError at runtime. Renamed fields
with AliasChoices for backward compat with HANZO_COMMERCE_* env vars.

P1: deps.py silently swallowed JWT errors (bare pass) — now logs them.
Virtual API key used fake UUID for missing projects — now raises 404.
Updated secrets.yaml to replace stale Stripe references with Commerce.
2026-02-12 10:44:47 -08:00
zeekay e12b34a4cf fix: update ZAP connection URL to bootno.de domain 2026-02-12 10:21:42 -08:00
zeekay 1db85445d4 feat: enhance Lux fleet management with in-cluster + StatefulSet discovery
- Add StatefulSet discovery fallback for kubectl-deployed fleets (non-Helm)
- Add in-cluster mode: auto-detect pod ServiceAccount, skip kubeconfig fetch
- Add list_fleets_incluster() for direct K8s API fleet discovery
- Add get_statefulsets() to HelmDeployer for kubectl-based fleet listing
- Make helm_revision optional (null for non-Helm fleets)
- Add Lux devnet network (chain_id 96370), fix testnet chain_id (96368)
- Add in-cluster self-detection for /v1/infra/clusters endpoint
- Fix kubeconfig fetch: use raw HTTP (pydo can't deserialize YAML), doctl fallback
- Install kubectl in API Dockerfile for fleet management operations
2026-02-12 10:21:01 -08:00
zeekay 249e2984cd feat: integrate Commerce/Square billing (checkout, webhooks, unified API)
- Add checkout/capture endpoints via Commerce → Square payment flow
- Add order.completed/cancelled and payment.paid/failed/refunded webhook handlers
- Update Commerce API paths (/api/v1/customers → /api/v1/user, /subscribe, etc.)
- Migrate unified billing from Stripe references to Square (customer accounts)
- Add customer_id to usage sync worker for proper metered billing
- Export UnifiedBillingClient, UnifiedUser from billing module
- Add Commerce tier-to-plan slug mapping (bootnode-free/payg/growth/enterprise)
2026-02-12 10:20:42 -08:00
zeekay f46dc90a83 refactor: rename domain bootnode.dev → bootno.de
Update K8s manifests (Ingress hosts, CRD API groups, CORS origins),
ZAP schemas, pyproject.toml URLs, SDK docs import paths, e2e test
emails, and K8s annotations to use the canonical bootno.de domain.
2026-02-12 10:20:22 -08:00
zeekay bcc94c2fcb fix: multi-network OAuth login for all cloud portals
- OAuth callback accepts redirect_uri from frontend to derive correct
  IAM client_id per network (all use lux-web3 shared app)
- Token verification accepts tokens from any known IAM app audience
- Frontend sends redirect_uri in callback POST body
- Login page uses proper /login/oauth/authorize URL with client_id
- Brand configs unified to use lux-web3 IAM app for all networks
- Removed dead code (duplicate get_current_user, unreachable methods)
- IAM init data updated with cloud redirect URIs for pars/zoo/hanzo
2026-02-11 18:44:57 -08:00
zeekay 5af2afacea fix: import get_current_user from core.iam not api.deps
The networks module imported get_current_user from bootnode.api.deps
where it doesn't exist — it's in bootnode.core.iam. This broke the
entire API startup (uvicorn can't import bootnode.main:app).
2026-02-11 17:55:17 -08:00
zeekay d9fa1040b2 feat: add 1-click network launcher API + update CI deploy
New /v1/networks endpoint orchestrates deploying a complete
white-label blockchain cloud portal:
- K8s deployment + service for bootnode-web with brand env vars
- Ingress with TLS + CORS for cloud.{domain} and api.cloud.{domain}
- IAM integration for SSO
- Scale and teardown endpoints

CI deploy step now restarts all cloud-web deployments (lux, pars,
zoo, hanzo) when new images are pushed.
2026-02-11 10:58:52 -08:00
zeekay 0247a2ded2 fix: use getattr for allowed_origins backward compat
Older Docker images may not have the allowed_origins field in Settings.
Use getattr with empty list default for graceful fallback.
2026-02-11 10:49:31 -08:00
zeekay 4a8056ca44 fix: CORS allow all cloud domains for multi-network white-label
The CORSMiddleware was initialized with only bootnode.dev origin.
Added all cloud.*.network domains and ALLOWED_ORIGINS env var
for extending origins without code changes.
2026-02-11 10:39:14 -08:00
zeekay 05a14c1f6b Merge remote-tracking branch 'hanzo/main'
# Conflicts:
#	api/bootnode/api/__init__.py
#	api/bootnode/api/billing/__init__.py
#	api/bootnode/core/billing/unified.py
#	infra/cloud/deploy.sh
#	infra/compose.yml
#	infra/k8s/api-deployment.yaml
#	infra/k8s/bootnode-deployment.yaml
#	infra/k8s/bootnode-operator.yaml
#	infra/k8s/hanzo-web3-deployment.yaml
#	infra/k8s/indexer-deployment.yaml
#	web/app/auth/callback/page.tsx
#	web/app/dashboard/infrastructure/nodes/page.tsx
#	web/app/dashboard/layout.tsx
#	web/app/login/page.tsx
#	web/components/unified-navigation.tsx
#	web/lib/auth.tsx
#	web/lib/brand.ts
#	web/public/logo/lux-icon.svg
#	web/tsconfig.tsbuildinfo
2026-02-10 20:51:48 -08:00
zeekay ad6dc4e6eb feat: migrate billing from Stripe to Hanzo Commerce + Square
Remove all direct Stripe integration. Bootnode now uses Hanzo Commerce
API as its payment gateway (Commerce handles Square internally).

- Remove stripe dependency from pyproject.toml
- Rewrite commerce.py client with correct Commerce API paths:
  /api/v1/user (customers), /api/v1/subscribe (subscriptions),
  /api/v1/checkout/* (payments), /api/v1/order (orders/invoices)
- Add unified.py: IAM + Commerce customer linking (hanzo.id SSO)
- Rewrite webhooks.py: Commerce event handler with HMAC verification
- Update sync.py: usage metering via Commerce (not Stripe)
- Update billing API endpoints: checkout, account, webhooks
- Config uses COMMERCE_* prefix (not HANZO_COMMERCE_*)
- Add test_billing.py: 46 tests covering CU, tiers, Commerce client,
  webhooks, and unified billing
2026-02-10 20:50:18 -08:00
zeekay 60cb93aca6 feat: MPC threshold wallets, FHE encrypted balances, real staking integration
- Shamir's Secret Sharing (2-of-3 ECDSA) with AES-GCM encrypted share storage
- FHE module for confidential balance/staking computation (AES-GCM sim, concrete-python optional)
- 8 new MPC API endpoints: create, list, status, sign, shares, decrypt-share
- /v1/keys/status endpoint backing staking page KMS connection
- Staking page wired: real validators from nodes API, MPC wallet connection, Quick Stake signing
- MPCWalletModel with threshold, share holders, encrypted shares columns
- 115 total API routes, all tests passing
2026-02-10 18:57:27 -08:00
zeekay 108e2cada6 feat: DB-backed node infrastructure with globe visualization
- Add Cluster and Node SQLAlchemy models with full metrics columns
- Create DB-backed CRUD router (14 endpoints) replacing in-memory store
- Add SVG world map globe component with pulsing node dots and tooltips
- Seed real DOKS clusters (hanzo-k8s, lux-k8s) + multi-chain nodes on startup
- Wire seed_database into app lifespan for auto-population
- Add network filter and globe visualization to nodes dashboard page
- Keep staking/supported-chains/presets endpoints via lazy imports
2026-02-10 18:43:03 -08:00
zeekay a7b46f543d fix: skip hanzo-kms optional dep in CI, update repo URL to bootnode/bootnode 2026-02-10 15:07:13 -08:00
zeekay 67797d69b4 Update to latest K8S Operator 2026-02-10 14:57:33 -08:00
zeekay fb1e5d96c5 feat: add Lux validator fleet management system
Full-stack fleet lifecycle management for Lux blockchain validators
deployed via Helm to DOKS clusters.

Backend (Python/FastAPI):
- HelmDeployer: async helm/kubectl CLI wrapper with temp file cleanup,
  per-release locking, and command injection protection
- LuxFleetManager: fleet CRUD, scale, rolling restart, node health
  probes via luxd RPC (health + C-chain block height)
- Fleet API: authenticated REST endpoints (ApiKeyDep) with RFC 1123
  input validation, kubeconfig error sanitization
- Pydantic models: 3 networks (mainnet/testnet/devnet), fleet status
  enums, helm values translation
- 110 unit tests (0.30s, all passing)

Frontend (TypeScript/Next.js):
- Fleet dashboard: cluster selector, stats banner, fleet cards with
  scale/restart/destroy operations, node status table, create dialog
- Interactive SVG world map: zero-dep Mercator projection with DOKS
  region markers, pulsing animations, P2P mesh lines, click-to-zoom
- DOKS region data: 11 regions with coordinates and API client
- Dark theme throughout
2026-02-10 13:40:28 -08:00
zeekay 51668b6dd3 fix: update image references from bootnode/ to ghcr.io/hanzoai/bootnode
Updated all container image references across compose, K8s manifests,
cloud deploy scripts, and tests to use the ghcr.io/hanzoai/bootnode
registry format.
2026-02-06 07:55:58 -08:00
zeekay bb3c6906f3 fix: redirect /login to hanzo.id in production
Instead of showing a login page with multiple identity providers,
redirect directly to hanzo.id for a cleaner authentication flow.
2026-02-04 23:09:42 -08:00