100 Commits
Author SHA1 Message Date
hanzo-dev 1da186ac56 Stop reserving for a validator that cannot start
Co-authored-by: Hanzo Dev <[email protected]>
2026-09-09 12:34:53 -07:00
hanzo-dev 271712fa4e Merge remote-tracking branch 'forge/main'
CI/CD / render (push) Failing after 0s
2026-09-08 18:14:28 -07:00
hanzo-dev bd89ddede3 Merge remote-tracking branch 'origin/main' 2026-09-08 18:14:25 -07:00
hanzo-dev 5f33dc94d4 Give the Zoo wallet the brand that makes it Zoo's
The image is one build serving every label and this file is the only
thing that distinguishes them, so without it the pod would present Lux on
a Zoo surface. It was mounted here and declared nowhere; four days in
ContainerCreating. Content is the wallet repo's own zoo overlay.

Claude-Session: https://claude.ai/code/session_018U2e2U4PPc8UaXYyeT9iNu
2026-09-08 18:14:15 -07:00
Hanzo AI f5200f6e59 Drop the zoo-computer architecture pin
The pin rested on an arm64 pull error, "no match for platform in
manifest". On the amd64 node the same digest reports plainly: not found.
zoo-mainnet has no ghcr-luxfi secret, the one the values file names, so
the private repository answers every request as a miss on both nodes and
the earlier message says nothing about which platforms are published.

Provision the pull secret first; the manifest can then be read and the
architecture, if it needs constraining, constrained against evidence.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-06 05:05:46 -07:00
Hanzo AI 2764b10f7e Pin zoo-computer to the architecture its image is built for
ghcr.io/zooai/computer publishes an amd64-only manifest. Scheduled on the
arm64 node the pull ends in "no match for platform in manifest" and the
Deployment never becomes ready.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-06 03:51:08 -07:00
Hanzo AI cfade28c8d deploy: computer, foundation, gym, ui and wallet come home from lux/universe
Every Zoo workload is declared here, where zoo-fleet reads; these five had been
declared only in lux/universe's zoo-mainnet directory. Pins carried as they were
running, digest-pinned where the registry answered.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-04 10:53:48 -07:00
Hanzo AI c1a8831042 iam: zoo.vote signs in with the tenant's issuer
luxfi/vote's zoo tenant is an SPA client, PKCE in the browser like the others.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-04 09:15:42 -07:00
Hanzo AI afeeb9039a zoo-build: pin 0.1.9 by digest
Co-authored-by: Hanzo Dev <[email protected]>
2026-09-04 00:37:40 -07:00
Hanzo AI e27d85362d zoo-build: 0.1.9, the version the lux fleet was already running
zooai/universe is zoo-build's one home; the duplicate declaration in
lux/universe had moved ahead to 0.1.9 while this one stayed at 0.1.7. Carry the
newer pin here before the duplicate is removed there, so nothing regresses.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-04 00:36:50 -07:00
Hanzo AIandClaude Fable 5.1 ace46f80af zoo-vote: declare zoo.vote where zoo-fleet reads
zoo.vote was declared in lux/universe deploy/zoo-mainnet/zoo-vote.yaml. zoo-fleet's
generator reads THIS repository (hanzo/universe infra/k8s/hanzo-cd/project-zoo.yaml,
path deploy/*/*.yaml), so the workload had a declaration in a fleet that does not
serve it and none in the one that does. Declared here, deleted there.

The image is ghcr.io/zooai/vote, built by luxfi/vote's Image workflow: one source
builds lux.vote, zoo.vote and hanzo.vote, each into its own org's registry, so
this pod pulls with Zoo's credential and no other org's access can strand it. The
tenant is a build-arg and the host settles it again at runtime, which is why the
ingress names zoo.vote and www.zoo.vote — the app reads the hostname to know
which DAO it is, so what is routed here is part of its behaviour.

The tag is what the live Deployment runs, with a TODO to pin the first tag the
workflow prints. It could not be verified from a workstation, so it stays a tag
rather than becoming a digest that was guessed.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_015g5a1mCWuuSQTBZZywNnET
2026-09-04 00:23:19 -07:00
Hanzo AI a986b507ec cf-zones: zoo.network apex and www serve from the ingress, not GitHub Pages
One A record at the hanzo-k8s ingress load balancer replaces the four GitHub
Pages records; the site is served from the Zoo prefix in object storage.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-03 21:33:00 -07:00
Hanzo AI 19b7efd0e5 ci: a deploy is a commit, so stop applying to the cluster
Two paths in this workflow wrote to zoo-k8s directly. `deploy-single` ran
kubectl apply -k over one namespace's overlay; `deploy-all` ran a script that
applied the CRDs, applied the whole overlay, and then hand-patched luxd flags
onto a StatefulSet the Rust operator renders -- a patch whose own comment
explains it persists because the operator does not re-render an existing pod
template.

That is a second writer for objects zoo-fleet declares from deploy/*/*.yaml,
and the disagreement is not quiet. Both stamp the pod template
(app.kubernetes.io/managed-by: hanzo-operator vs Helm), and a pod-template
label is not a field two controllers can differ about without a restart, so
each flip rolls the workload. It does not converge; it oscillates.

It also applied the WRONG THING. k8s/ is the hand-applied tree whose currency
deploy/README.md says outright is unknown -- some of it ahead of the cluster,
some behind, some describing objects that were deleted. A pipeline that applies
it on every push re-asserts that drift on a schedule.

What is left writes to git and tests: record the pin, open the PR, run E2E.
zoo-fleet reconciles from the merged file. The kubectl calls still in the E2E
job read pods, events and logs on failure, and write nothing.

The k8s/ tree stays, for what deploy/ deliberately does not cover -- CRDs,
namespaces, the operator's custom resources. `make k8s-apply env=<env>` is that
path, with a human behind it.

deploy-all and e2e-only meant the same thing once neither deployed, so the
modes are now the two acts that remain: pin and e2e.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-03 21:32:59 -07:00
Hanzo AI a9cf29e4c2 zood-mv: the digest names v1.36.58 while the tag says v1.36.68
26e7154 moved the tag from v1.36.58 to v1.36.68 to stop this file declaring a
version behind the fleet, and left the digest on v1.36.58's bytes. A reference
carrying both resolves BY DIGEST, so the declaration it left behind was the one
it set out to remove -- run .58, report .68 -- and now with the tag agreeing
with the running fleet, nothing was left to notice it. That is the failure mode
worth naming: a drifted pin does not fail, it succeeds at doing nothing.

Both halves are now v1.36.68 (ebd16ed5…), read from the registry in one answer.
The setup initContainer moves with them; it ran .58 by the same accident, and a
node whose init and main containers are different builds is a question nobody
should have to ask.

Consequence, on this file specifically: zood-mv is the only luxd StatefulSet on
RollingUpdate with podManagementPolicy Parallel, N=5 and alpha=4. A sync would
have restarted all five Zoo validators together onto ten-releases-old bytes,
and luxd waits for peers before it listens -- every node waiting for a node
that is also waiting.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-03 21:32:58 -07:00
Hanzo AI 4646846e7a deploy: pin every resolvable image to the bytes it runs
A values file named a repository and a tag, and a tag is mutable. Under
pullPolicy: Always that means the fleet pulls whatever the name points at on the
morning it restarts; under IfNotPresent it means a node that pulled `:latest`
once keeps those bytes forever while its neighbour gets a newer build. Both
spellings of the same fault: the file does not say what runs.

41 of 50 image blocks now carry a digest beside the tag, resolved from the
registry, with pullPolicy: IfNotPresent throughout. The chart renders
repo:tag@digest and the reference resolves BY DIGEST -- the tag stays as the
name a human reads. 20 of those were floating (`latest`, `main`, `main-amd64`)
and now name specific bytes.

Four floating tags had a release naming the same digest, so they say it:
consul 1.20 -> 1.20.6, traefik v3.3 -> v3.3.7, zooai/docs latest -> 0.1.4. Each
was verified to be the same manifest, so this renames what is running and moves
nothing. gatus stays on `latest`: no v-tag names its bytes, so a version there
would have been a silent upgrade rather than a rename.

This is also what chart 0.1.13 requires. Its claimed list covers
ghcr.io/hanzoai/cloud:v*, and it refuses to RENDER one of those without a
digest -- so hanzo/cloud-api would have failed the render, which stops the sync
and leaves the running pods alone. Better than the alternative, but the pin is
the point.

Eight files name an image the registry does not have and are left alone rather
than guessed at; deploy/README.md lists them with what the registry does have.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-03 21:32:58 -07:00
Hanzo AI 4436735415 deploy: the generator lives where the CD runs, not here
deploy/applicationset.yaml contradicted the live zoo-fleet on every field that
decides where a sync lands: apiVersion apps.hanzo.ai/v1alpha1 against v1,
namespace cd against hanzo-cd, a github.com/zooai/universe mirror against
git.hanzo.ai/zoo/universe, and chart 0.1.0 against the version the fleet pins.
Applying it would not have adopted anything -- it would have stood up a second
generator over the same deploy/*/*.yaml, racing the first, with neither one
wrong on its own terms.

zoo-fleet is declared in hanzo/universe/infra/k8s/hanzo-cd/project-zoo.yaml,
behind an AppProject that fences Zoo's Applications to the Zoo cluster. One CD,
one standard, three orgs. A repo that also ships a generator is a second
opinion about a question that has one answer, so this copy goes.

The README's chart reference moves to 0.1.13 and says which file actually
decides it, so a helm template on a workstation renders what the cluster would
and a disagreement points at the ApplicationSet rather than at itself.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-03 21:32:57 -07:00
Hanzo AI e387ff4859 Zoo asks luxd for chains where luxd serves them
The zoo manifests, the compose file and the e2e helpers all composed
/v1/bc addresses against luxd, which answers under /v1/chain and no
longer serves the old segment.

A line that records an observation keeps its original spelling.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-03 21:32:56 -07:00
Hanzo AI 65b6fcc78a docs: chain routes are /v1/chain
luxd serves chain APIs under one segment from constants.ChainAliasPrefix
(constants v1.6.4, node v1.36.181):

  /v1/bc/<alias>/...   -> /v1/chain/<alias>/...
  /ext/bc/<alias>/...  -> /v1/chain/<alias>/...

Observations keep their original addresses: a dated note or a recorded status
code is a fact about when it was written, not an instruction to follow.
2026-09-03 21:32:55 -07:00
hanzo-dev 4808eac1ee zoo chain status: run our own status binary, pinned, wearing the Zoo mark
The three chain-environment status pages ran an upstream build on a floating
`latest` tag, so what shipped changed whenever someone else cut a release, and
nothing here pinned it. That build also has no notion of a brand: the `BRAND=zoo`
env beside it was read by no code, which is why these pages carried no Zoo mark
at all.

ghcr.io/hanzoai/status v1.1.17 is the one status binary this estate runs. It
derives every mark from `brand` in status-config and proves each asset is present
at startup. GATUS_CONFIG_PATH is named explicitly because the mount is absolute
while the binary's default path is relative.
2026-09-03 21:32:54 -07:00
hanzo-dev d37876f960 otel-agent: let rbac be the only place the identity is named
Chart 0.1.11 derives the pod's ServiceAccount from the rbac block, so stating
it a second time at the top level says nothing the declaration did not already
say -- and two places to write one name is how they came to disagree in the
first place.

The chart now fails the render if they ever do disagree, so this is safe to
carry as one line instead of two.
2026-09-03 21:32:50 -07:00
hanzo-dev 8ae23fcf21 drop the grafana in ns monitoring
It reads from `prometheus.monitoring.svc:9090`, which does not exist on this
cluster -- so the panel has been a second lens onto nothing. Zoo's telemetry
goes to o11y over OTLZ, and one lens is the point.

It also carried `GF_SECURITY_ADMIN_PASSWORD: admin` beside
`GF_AUTH_ANONYMOUS_ENABLED: true`, in the clear, in a file in the repo. No
ingress pointed at it, so the exposure was in-cluster only, but "no route today"
is not a control.

Its 256Mi is what the otel-agent rollout on zoo-nodes-3mm2zq has been waiting
for: that node is at 99% of allocatable, the agent asks 192Mi, and a DaemonSet
rolling update stalls the whole set on one unschedulable pod.
2026-09-03 21:32:49 -07:00
hanzo-dev fc82233a95 otel-agent: attach the ServiceAccount to the pod
rbac.serviceAccountName CREATES the ServiceAccount and binds nodes/stats and
nodes/proxy to it. The pod's identity is a SEPARATE key that the chart reads
from the top level, so setting only the rbac one produced exactly what it looks
like it would not: the RBAC was correct and unused, and the pods ran as
`default`.

kubeletstats therefore got 403 from every kubelet on this cluster since the
agent was introduced. hostmetrics kept working -- it reads /hostfs and needs no
API identity -- so the agent looked healthy and simply never produced any
k8s.node.* series.
2026-09-03 21:32:49 -07:00
hanzo-dev 45cb841ae9 o11y: zoo joins the plane — four nodes that were producing nothing
Zoo had no observability at all. Not a competing stack to retire, an
absent one: four nodes with no host metrics, no logs, no traces, and
nothing reaching event.* — the cluster was simply dark, and the only
telemetry component in it was a Grafana with nothing of its own to read.

This is the agent hanzo and lux already run, and it differs from lux's by
the cluster identity and nothing else — same receivers, same pipelines,
same 300s intervals, same door. That sameness is the point: a third
cluster should cost a copy, not a design.

Reachability measured before writing it: a zoo pod dials 10.124.0.71:4319
open, so the shared DigitalOcean VPC carries this the same way it carries
lux.

imagePullSecrets is EMPTY on purpose. charts/app defaults it to
ghcr-secret, and on lux that credential answers 403 and turned a public
image into ImagePullBackOff across the whole fleet.
ghcr.io/hanzoai/otel-collector is public; sending a rejected credential
is worse than sending none.

No gateway here. The gateway exists to scrape kube-state-metrics and
chain exporters, and zoo runs neither — object-state metrics need
kube-state-metrics deployed first, which is a separate decision.
2026-09-03 21:32:48 -07:00
hanzo-dev a67fc7784a Serve zoo.cloud from the edge as files, not from a pod 2026-09-03 16:55:18 -07:00
hanzo-dev 215c9c9fea Pin the chart the publisher stands behind; drop the second deploy path
0.1.0 is the version the chart workflow's own comment names as never having
matched its source, so every Application claimed a chart nobody could
reproduce. 0.1.11 is what lux pins and what the registry actually holds.

ci.yml applied manifests with kubectl on every push, beside an ApplicationSet
that reconciles the same namespaces. Two delivery paths let the cluster
disagree with git while both report success; CD is the one that stays.

Claude-Session: https://claude.ai/code/session_01WAriF9qmzQJnBHfumZMxnd
2026-09-03 13:23:44 -07:00
hanzo-dev 84c24ca4c9 Prove the values still render before CD tries to
Same check as the other universes: every values file renders against the
chart CD will use, at the version CD will use, so a file the chart can no
longer template fails on the push rather than at sync time.

CI does not deploy here either. The kustomize apply this repo's ci.yml still
carries is a second delivery path beside the ApplicationSet, and two paths
mean the cluster can disagree with git while both report success.
2026-09-03 11:16:42 -07:00
hanzo-dev 06b69af0e9 Route npm through our own registry
pkg.hanzo.ai is the estate's package host; a repo that resolves against
the public default is the one place a dependency can arrive from somewhere
nobody chose.
2026-09-03 11:04:58 -07:00
hanzo-dev 1bbdbc4f4b retire: name the canonical universe at this tip
Read-only from here. Canonical: https://git.hanzo.ai/zoo/universe
This tip is an ancestor of canonical main, so no commit reachable from this
branch is unique to it.
2026-08-20 17:33:59 -07:00
hanzo-dev 04806b23b5 zoo-devnet: trunk could not be applied without breaking the validator
Two ways origin/main disagreed with the live cluster, both of which only bite
on the next clean `apply -k`:

  NETWORK_HEALTH_MIN_CONN_PEERS was absent. A single-validator devnet has no
  peers, so luxd's default health gate (min 1 connected peer) holds the pod at
  0/1 forever. The LIVE StatefulSet already runs
  --network-health-min-conn-peers=0; trunk just had no way to reproduce it. The
  key went missing when HTTP_ALLOWED_HOSTS was added to this same `config:`
  block — the edit landed beside it and took its sibling with it.

  tag was pinned to 0.2.0 while the live node runs ghcr.io/zooai/node:0.2.2
  (the plugin-dir fix). Applying trunk downgraded the validator.

Neither showed up as an outage because nothing re-applies this overlay on a
schedule — no flux, no argo, no cronjob on zoo-k8s. It would have detonated
under whoever ran the next apply, which is the worst time to find it.

`kubectl kustomize k8s/zoo-devnet` now renders NETWORK_HEALTH_MIN_CONN_PEERS
"0" and tag 0.2.2, matching production.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-28 00:26:21 -07:00
Hanzo Dev 1d3c73fbda fix(zoo-explorer): keep the Zoo EVM path — C-Chain is Lux's, not Zoo's
The previous commit fixed the namespace half of the rpcEndpoint and then
overreached: it also swapped the path to /ext/bc/C/rpc because that is
what answered eth_chainId on the devnet validator. It answers 0x17872
(96370) — the Lux devnet C-Chain. C-Chain is Lux Network's primary EVM
and nothing else uses it, so pointing Zoo's explorer at it would have
made the Zoo devnet explorer index Lux blocks and call them Zoo's.

platform.getBlockchains on zoo-chain-0 lists only the Lux primary
chains (K/G/A/Z/Q/T/B/D/C/X) — there is no Zoo EVM there, and ZooChain
zoo-evm on devnet carries no blockchainId. So the honest state is: the
devnet Zoo EVM was never created, the explorer has nothing to index, and
no RPC path can change that. Keep the Zoo-EVM path and say so.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 22:46:56 -07:00
Hanzo Dev cb6a7d86fa fix(zoo-explorer): the explorer takes -config, and it needs an RPC that resolves
zoo-explorer-explorer had restarted 600+ times in both zoo-system and
zoo-devnet. The operator (<=0.1.3) rendered the ZooExplorer CR as
container args --rpc/--chain/--port, but ghcr.io/luxfi/explorer is a Go
binary whose stdlib `flag` set defines only -config/-data/-http/-mdns/
-version. Every start therefore ended in:

    flag provided but not defined: -rpc
    Usage of explorer: ...

i.e. exit 2 before the listener ever bound. operator 0.1.4
(src/controller.rs::reconcile_explorer) renders the CR into a chains.yaml
ConfigMap and passes --config=/etc/explorer/chains.yaml instead. 62864bd
already pinned 0.1.4 for the readiness fix, so only the rationale is
added here — but that pin is what stops the crash, and the comment should
say so or the next person re-floats the tag.

The crash was hiding two further defects. Both pods now come up Running
and are still useless without these:

* base rpcEndpoint pointed at zoo-chain-rpc.zoo.svc.cluster.local. There
  is no `zoo` namespace on do-sfo3-zoo-k8s (`kubectl -n zoo get svc` ->
  "No resources found"), so the indexer logs `no such host` every 30s and
  has indexed zero blocks since it stopped crashing. `zoo` is not a
  blockchain alias either — the EVM answers on /ext/bc/C/rpc. The CR is
  applied into zoo-<env>, so the address is namespace-relative;
  zoo-testnet/zoo-mainnet keep their own tracked-blockchainID overrides.

* the devnet validator ran without --http-allowed-hosts, so luxd's
  localhost-only default 403s every in-cluster caller — the same
  eth_chainId POST returns 0x17872 with `Host: localhost` and 403
  Forbidden without it. spec.config renders as --kebab-case flags
  (operator 0.1.3+), so HTTP_ALLOWED_HOSTS there is the one source of
  truth; the unreferenced zoo-devnet/statefulset-patch.yaml is not.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 22:42:35 -07:00
Hanzo Dev 62864bdba8 fix(zoo-operator): pin 0.1.4 — readiness is process health, not leadership
zoo-system/zoo-operator sat 0/1 for two days. The probe was not wrong:
path /readyz and port 8081 both match the server main.rs binds. The
endpoint itself was.

Two faults compounded.

1. 0.1.3's /readyz returned 503 for any replica that did not hold the
   lease. Readiness answers "can this pod serve?"; leadership answers
   "who reconciles?". Braiding them means a healthy standby is NotReady
   forever, so the Deployment never goes available. Measured on the live
   pod before the fix — /healthz=200 while /readyz=503, process fine.

2. Something else was holding zoo-system's lease. A stale zoo-testnet
   replica, floating on :main with no OPERATOR_NAMESPACE, had survived
   since the last apply of this file. main.rs falls back to the literal
   "zoo-system" when that env is unset, so it wrote zoo-system's lease
   from zoo-testnet and renewed it indefinitely. git has said replicas: 0
   for that overlay all along (zoo-testnet/network-patch.yaml) — the
   cluster had simply drifted, and the floating tag hid it.

So: pin the immutable semver, and record that the fieldRef and the
scale-to-0 overlays are load-bearing invariants rather than tidiness.
0.1.4 (zooai/operator 98be7d7) carries the /readyz change.

This file still tracked :main because the 0.1.3 pin was only ever made on
a feature branch; trunk kept floating and would have reverted the cluster
on the next clean apply.

Proof, zoo-system after rollout — two replicas, one leader, both Ready,
which 0.1.3 could not do by construction:

  NAME           READY   UP-TO-DATE   AVAILABLE
  zoo-operator   2/2     2            2

  lease holder = zoo-operator-7dd698654-t6v4q   (10.160.16.31)
  10.160.17.253  /readyz=200  /healthz=200      <- NOT the leader
  10.160.16.31   /readyz=200  /healthz=200

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 22:38:13 -07:00
Hanzo DevandGitHub 2bffadc1b1 beluga: wire L3 to run zooai/node (replace luxd stopgap) (#5)
* beluga: wire L3 to run zooai/node (replace luxd stopgap)

Make the Beluga L3 (chainId 420420) a real thinking-chain instead of an
inert luxd subnet stub. The zoo-chain validators tracked beluga under
ghcr.io/luxfi/node (luxd) — a bare subnet runtime with no Zoo EVM and no
AI precompiles, so beluga could never instantiate (blockchainID empty).

Switch the zoo-chain ZooNetwork image to ghcr.io/zooai/node (zood), which
self-installs the Zoo EVM (AI/FHE/PQ/ZK precompiles, incl. the 0x0303
deterministic in-consensus inference precompile and the 0x0304 aivmbridge
that settles Tier-2 Proof-of-Thought to the Lux A-Chain), Zoo DEX and Zoo
FHE VM plugins at startup.

- base/network.yaml + zoo-{devnet,testnet,mainnet}/network-patch.yaml:
  image ghcr.io/luxfi/node -> ghcr.io/zooai/node.
- zoo-{devnet,testnet,mainnet}/statefulset-patch.yaml: command ./luxd ->
  zood (zood wraps luxfi/node, identical config.BuildFlagSet, so all flags
  apply verbatim). Beluga's blockchainID is appended to --track-chains
  once the CreateChainTx mints it on the Zoo primary network.
- base/beluga.yaml: enrich the ZooChain CR with vmId (Zoo EVM
  2n2njofjYvece8gZWCNnc1mqkcqfW6kbrhPRZPVzwxrSQrQ4gE) and
  genesisConfigMap (beluga-genesis), per the operator ZooChainSpec.
- base/kustomization.yaml: configMapGenerator beluga-genesis from
  k8s/base/beluga/{genesis.json,config.json} (stable name, no hash).
- k8s/base/beluga/: deployed copy of the canonical genesis+config
  (evmChainId 420420, BLG gas, precompile-upgrade activations at ts 0).

Image tags are floating `latest` only on devnet; testnet/mainnet pin the
immutable CI semver. The live rollout (restarts the L2 validators) is a
gated prod step performed after the ghcr.io/zooai/node image is verified.

* beluga: pin zood image to immutable sha-371ea17-amd64-node

ghcr.io/zooai/node built CGO-free (hermetic vendored build, digest
sha256:12e8244d…) and published. Pin the operator ZooChain CR image off the
floating 'latest' to the immutable build SHA per prod policy.

* beluga: pin devnet+testnet zood image to sha-371ea17-amd64-node (consistency)

* beluga(devnet): image is operator-derived from the ZooNetwork CR, not the patch

Per 'one and only one way, operator-derived': the zood image comes solely from
k8s/base/network.yaml (the ZooNetwork CR); the operator renders it. The patch
asserts only the luxd CLI flags the operator wrongly passes as ZOO_* env.

* beluga: pin zood image to clean semver 0.2.0 (not the sha hash)

Proper semver for all image pins — ghcr.io/zooai/node:0.2.0 (digest 12e8244d,
same build), not sha-371ea17-amd64-node. The ZooNetwork CR is the single source;
the operator renders it onto the StatefulSet.
2026-06-24 13:44:27 -07:00
6db38c80e1 chain: Zoo is an L2 validated by Lux primary, not sovereign L1 (#6)
- chain.yaml: type l1->l2, parent null->lux, validators 21->0. Zoo's
  blocks are validated by the existing Lux Primary Network validator set
  (platform.validatedBy(zoo bc 4HBM17cz...) -> netID 1111...LpoYY); the
  networkID field is Zoo's OWN EVM chainID (200200), not a sovereign
  primary networkID. Zoo still has its own EVM, distinct from C-Chain.
- configs/genesis/localnet/genesis.json: chainId 200202 -> 200203
  (was a duplicate of devnet; localnet is 200203 per chain.yaml).

Co-authored-by: zeekay <[email protected]>
2026-06-23 21:18:46 -07:00
hanzo-dev 11afcc3033 fix(zoo): pass luxd flags to zoo-chain so validators stay up
The Rust zoo-operator configures each validator by passing the ZooNetwork CR
config map as ZOO_* env vars. luxd (ghcr.io/luxfi/node, CMD ["./luxd"]) does
NOT read http-host / bootstrap / track-chains from ZOO_* env — only from real
CLI flags. So luxd bound its HTTP/health API to the default 127.0.0.1: the
kubelet liveness probe targets the pod IP, never reaches /ext/health, and
SIGTERMs the node ~90s after boot — an endless restart loop that kept testnet
and devnet validators from ever stabilizing (and would have hit mainnet too).

Inject the flags directly onto the operator-rendered StatefulSet via a
per-overlay statefulset-patch.yaml, applied by deploy-all.sh right after the
kustomize apply (the operator owns the StatefulSet, so it is not in the
kustomize set and cannot be patched through the overlay; the operator does not
re-render an existing pod template, so the patch persists).

- --http-host=0.0.0.0 + --http-allowed-hosts=* so the probe can reach health
- --bootstrap-ips/--bootstrap-ids/--network-id/--sybil-protection-enabled as
  real flags (the ZOO_* env equivalents were silently ignored: nodes booted
  with 'Adding bootstrap nodes ... count=0')
- devnet: drop the bogus --track-chains=zoo (and TRACK_CHAINS env) — luxd
  rejects the bare 'zoo' alias; the devnet Zoo EVM chain isn't created yet
2026-06-23 19:21:58 +00:00
hanzo-dev 45ea50e53d fix(zoo-mainnet): lower validator requests to fit zoo-nodes pool
zoo-mainnet zoo-chain-0 was stuck Pending (FailedScheduling: Insufficient
memory on 0/5 nodes). Its validator memoryRequest was an anomalous 8Gi while
the zoo-nodes pool (s-4vcpu-8gb-amd) has only ~6.26Gi allocatable per node, so
a single 8Gi-request pod could never schedule. cpuRequest of 2 also tripped
Insufficient cpu on a packed node.

Lower requests to match zoo-testnet (memoryRequest 4Gi, cpuRequest 1) so all 5
replicas can co-schedule. Limits stay generous (memoryLimit 16Gi, cpuLimit 8)
for mainnet headroom.
2026-06-23 19:01:11 +00:00
hanzo-dev ab71a3dabe fix(zoo): migrate beluga ZooChain to operator ZooChainSpec schema
The ghcr.io/zooai/operator:main binary (crate zoo-operator v0.1.2) watches
ZooChain via Api::all and deserializes each object into ZooChainSpec
(src/crd.rs): chainId (required u64) + optional blockchainId/vmId/
genesisConfigMap/networkRef. The installed CRD uses
x-kubernetes-preserve-unknown-fields, so enforcement is client-side in the
operator.

The old beluga CR carried a rich legacy schema (type/parent/vmType/
evmChainId/coin/coinName/genesis.secretRef/config/precompiles/resources)
with NO chainId. serde failed to decode it → the ZooChain list-watch's
InitialList failed → the whole ZooChain controller stalled, blocking chain
provisioning across all namespaces.

Migrate beluga to the canonical shape, mirroring the working zoo-evm CR:
  evmChainId: 420420  -> chainId: 420420
  parent: zoo-chain   -> networkRef: zoo-chain
Drop fields the operator does not consume (it only stamps status; the L3 is
tracked via the validators' TRACK_CHAINS env, and EVM/precompile/genesis
params live in genesis.json + the VM build, not this tracking CR).
2026-06-23 17:20:51 +00:00
hanzo-dev 1f78a8b9ce fix(zoo-operator): pin to :main (only published tag) — :v0.1.1 is NotFound
The operator Deployment pinned ghcr.io/zooai/operator:v0.1.1, but that tag (and
:main-amd64) return NotFound from GHCR — verified in-cluster: the v0.1.1
ReplicaSet went straight to ImagePullBackOff. The only tag zooai/operator CI
has published is :main. Pin to :main (imagePullPolicy: Always) so a fresh
apply actually starts the operator; switch to an immutable semver once CI cuts
one.
2026-06-23 17:07:45 +00:00
hanzo-dev be9e5ef0d4 fix(zoo): pin validators to ghcr.io/luxfi/node:v1.30.40 (v1.27.4 unpublished)
ghcr.io/luxfi/node:v1.27.4 is NotFound in GHCR (would ImagePullBackOff).
v1.30.40 is the published, proven tag the Lux production cluster
(do-sfo3-lux-k8s, lux-mainnet/testnet) currently runs 5/5 validators on.
Zoo is a Lux L2 and uses the upstream OSS node image directly.
2026-06-23 17:00:57 +00:00
hanzo-dev 8eb8c88bea fix(zoo-operator): bind all per-env ServiceAccounts to the ClusterRole
The zoo-operator readiness probe (GET :8081/readyz) returned 503 for ~9 days
(88k failures) on every replica and the beluga/zoo-chain CRs never reconciled
(empty PHASE/CHAINID).

Root cause — RBAC subject collision:
ClusterRoleBinding/zoo-operator is cluster-scoped, but each env overlay
rendered the SAME binding name and JSON6902-patched its single subject's
namespace to its own env. ClusterRoleBindings are not namespaced, so the four
overlays collided on apply and last-writer-won — only zoo-mainnet's SA ended
up bound. The operator runs with --namespace "" (all namespaces) behind ONE
cluster-global leader-election lease (zoo-operator-leader in zoo-system); the
lease winner was the zoo-devnet pod, whose SA was NOT a subject → 403 on every
zoo.network list/watch → controllers' InitialListFailed → leader never healthy
→ readyz 503 on all pods → 0/1 Deployments.

Fix: define the binding once in base/operator.yaml with ALL four per-env
operator SAs as subjects (one ClusterRole, one binding, every identity —
order-independent), and drop the per-overlay subject-rewrite patches that
caused the collision. The operator's /readyz gates on leadership, so once the
lease winner can list/watch cluster-wide it goes 1/1 and reconciles every
namespace.

Also align zoo-devnet/zoo-system ZooNetwork CRs to the v0.1.1 operator's
ZooNetworkSpec schema (validators:<int> + image{} + resources{} + config{}),
consistent with the mainnet/testnet patches, so the whole tree reconciles
under the pinned ghcr.io/zooai/operator:v0.1.1.
2026-06-23 16:58:28 +00:00
4e35e37b4f dns(zoo.ngo): proxy web3.zoo.ngo surfaces through CloudFlare (#4)
* dns(zoo.ngo): proxy web3.zoo.ngo surfaces through CloudFlare

web3.zoo.ngo, api.web3.zoo.ngo and ws.web3.zoo.ngo are the API and web
hosts behind the zoo.cloud brand surface (zoo.cloud's www CNAME targets
web3.zoo.ngo). They were unproxied A records pointing straight at the
hanzo-k8s LB (134.199.136.205), exposing the origin and skipping
CloudFlare's edge TLS, caching and DDoS protection.

Set proxied: true to match the now-live CloudFlare state. sync.sh skips
proxied records, so this records source-of-truth; the flip was applied
via the CF API. Zone SSL mode is full and the origin serves a valid
Let's Encrypt cert — verified api/v1/chains=200 and api/v1/auth/me=401
through the proxy.

* dns(zoo.ngo): keep api.web3/ws.web3 unproxied — Universal SSL gap

CloudFlare Universal SSL on zoo.ngo covers zoo.ngo + *.zoo.ngo but NOT
the second-level *.web3.zoo.ngo. Proxying api.web3.zoo.ngo and
ws.web3.zoo.ngo breaks the edge TLS handshake because CF has no edge
cert for those names.

Only the apex web3.zoo.ngo (matched by *.zoo.ngo) stays proxied. api.web3
and ws.web3 serve direct from the hanzo-k8s LB with their valid Let's
Encrypt origin cert. Re-proxying requires an Advanced Certificate (ACM)
for *.web3.zoo.ngo, not entitled on this zone (CF error 1450).

---------

Co-authored-by: zeekay <[email protected]>
2026-06-18 21:07:20 -07:00
Hanzo DevandGitHub ea03508ca7 feat(k8s): unify with luxfi/universe — coherent boot + snapshot-native replication (#3)
Brings zoo to parity with lux: the SAME luxd-boot.sh (byte-identical to
luxfi/universe k8s/boot) drives every network, and native ZAP replication
(PQ-encrypted, restore-on-boot) replaces the boot-time admin_importChain (RLP)
path.

- k8s/boot/: shared entrypoint + mainnet/testnet/devnet env (ids 1/2/3,
  zoo EVM chainIds 200200/200201/200202, S3 path zoo/<net>/zaprepl,
  endpoint s3.zoo-system). Script is byte-identical to luxfi/universe.
- k8s/zoo-<net>/snapshot-native/: replicate-s3-creds + replicate-age secrets
  (fill from KMS) + ZooNetwork replication merge-patch. CRD already allows
  unknown fields, so spec.replication is accepted today.
- zoo-mainnet/luxd-rlp-import.yaml: RETIRED banner; kept as the provenance
  record of the one-time chain-200200 seed (now in the snapshot stream).
- SNAPSHOT-NATIVE-ROLLOUT.md: per-network apply order (devnet→testnet→mainnet).

Requires build-zoo rebuilt on database >= v1.20.3 and zooai operator
replication-env parity (mirror of luxfi/operator); until then the boot script
sets REPLICATE_* itself so replication works without operator changes.

Co-authored-by: Hanzo Dev <[email protected]>
2026-06-15 22:35:51 -07:00
Hanzo AI 7bb55af252 chore: update 2026-06-10 18:23:14 -07:00
Hanzo AI cae3edec13 chore: update 2026-06-10 14:14:57 -07:00
Hanzo AI 4b5e307300 chore: update 2026-06-10 13:19:41 -07:00
Hanzo AI 0c9ebfd4a6 k8s: rename test/dev hostnames → zoo-test.network / zoo-dev.network apex TLDs
Mirrors Lux's api.lux-test.network / api.lux-dev.network convention —
hyphenated env in the TLD, not a subdomain of zoo.network. All org
tooling reads the same shape.

- zoo-testnet/ingressroute.yaml: api.test.zoo.network →
  api.zoo-test.network (+ explore + api-explore variants).
- zoo-testnet/network-patch.yaml: ZooGateway.spec.hosts updated.
- zoo-devnet/ingressroute.yaml: api.dev.zoo.network →
  api.zoo-dev.network (+ explore variants).

CF zones for zoo-test.network + zoo-dev.network already exist (DNS
resolves through Cloudflare). DNS A records still need to be created
inside those zones pointing at the Zoo cluster ingress LB — separate
followup.
2026-06-10 09:59:16 -07:00
Hanzo AI 320f061bda k8s: collapse zoo-operator to zoo-system, migrate to CR-driven bring-up
Operator topology fix:
- base/operator.yaml: pin ghcr.io/zooai/operator:v0.1.1 (was :main floating).
  Add OPERATOR_NAMESPACE env via fieldRef: metadata.namespace so the
  leader-election Lease lock is namespace-scoped, not racing in a single
  hardcoded 'zoo-system' literal. imagePullPolicy: Always → IfNotPresent
  (semver tag is immutable).
- zoo-system/patches.yaml: zoo-operator replicas → 2 (HA, leader-election
  picks one). Drop the stale zoo-explorer raw-Deployment patch (operator
  emits it now via ZooExplorer CR).
- zoo-{mainnet,testnet,devnet}/network-patch.yaml: scale per-env
  zoo-operator → 0. Single zoo-system operator watches every namespace
  (--namespace '' default); per-env operators were thrashing the same
  Lease and never becoming leader.

CR migration (away from raw manifests):
- base/network.yaml: full rewrite as ZooNetwork + ZooChain + ZooExplorer
  + ZooGateway base CRs (CRDs already installed: zoonetworks /
  zoochains / zooexplorers / zoogateways).
- base/explorer.yaml: emptied — ZooExplorer CR replaces the raw
  Deployment; operator renders zoo-explorer-explorer Deployment + Svc.
- base/gateway.yaml: Service-only — ZooGateway moved to network.yaml.
- base/ingress.yaml: kind: Ingress → 4 kind: IngressRoute
  (hanzo.ai/v1alpha1). Traefik proxy Deployment kept (it IS the
  hanzoai/ingress L7 proxy on this cluster).
- zoo-{mainnet,testnet}/network-patch.yaml: env-specific patches —
  validators (mainnet=5, testnet=3), bootstrap IPs+IDs from live Lux
  mainnet+testnet svcs, TRACK_CHAINS = Zoo blockchainID per env.
- zoo-{testnet,devnet}/ingressroute.yaml: new — api.test.zoo.network,
  explore.test.zoo.network, api-explore.test.zoo.network (and dev.*
  variants).
- zoo-{system,testnet,devnet}/kustomization.yaml: include new
  ingressroute.yaml resource + strip mainnet-only IngressRoutes via
  $patch: delete pattern.
- zoo-mainnet/zoo-industries.yaml: kind: Ingress → IngressRoute.

Blockers surfaced (separate followups):
1. Operator does NOT translate `spec.config` env keys to lqd CLI
   flags. Rendered StatefulSet sets ZOO_BOOTSTRAP_IPS etc., but
   luxd reads LUXD_*-prefixed env. Operator controller.rs needs to
   render as CLI args. BLOCKS validators joining Lux primary.
2. hanzo.ai/v1alpha1 IngressRoute CRDs not registered on
   do-sfo3-zoo-k8s — current ingress is traefik:v3.3 with
   traefik.io/v1alpha1 native. Either install hanzoai/ingress CRDs
   OR migrate IngressRoutes to traefik.io group.
3. ZooNetwork operator doesn't emit per-pod LoadBalancer Services
   needed for validators to be reachable as external bootstrap peers.
4. Validator staking keys: operator only takes secret_prefix; doesn't
   derive from a KMS mnemonic. Need 5 Secrets per env OR operator
   extension.
2026-06-10 09:13:57 -07:00
Hanzo AI a0d270a651 configs/genesis/testnet: sync to state-repo canonical (zoo testnet h=84 RLP unblock)
Mirrors ~/work/lux/state/chains/zoo-testnet/genesis.json into the
deployment mirror configs/genesis/testnet/genesis.json. Same pattern
as 59f1220 (zoo mainnet sync) — fixes deployment-mirror drift so that
zoo-testnet's 85-block RLP archive imports cleanly.

Source RLP archive at
~/work/lux/state/rlp/zoo-testnet/zoo-testnet-200201.rlp
(sha256 739c96ba8e8173e5b9133802928cb81c7c4b30f9ee8e50bbae734f57eabc4055,
159,648 bytes, blocks 0..84) was exported 2026-06-05 from the preserved
pebbledb at ~/work/lux/state/pebbledb/zoo-testnet via the namespace-
aware scripts/export-rlp.go.

Files now byte-identical (verified by cmp).
2026-06-05 10:24:25 -07:00
Hanzo AI 59f1220481 configs/genesis/mainnet: sync to state-repo canonical (zoo h=799 RLP unblock)
Today's bootstrap-chain CreateChainTx (#168, 2026-06-04) produced a
zoo mainnet chain with block-0 hash `0x88dd3a53…`, which orphans the
Zoo mainnet RLP archive (799 blocks, recorded parent hash
`0x7c548af4…`). admin_importChain returns "block 1 parent mismatch".

Root cause: bootstrap-chain consumed the cc7d3d7 form of zoo-mainnet/
genesis.json (969 bytes), which is a SUBSET of the empirical canonical.
The cc7d3d7 commit was labeled "RLP-matching pristine form" but
omitted the explicit block-0 header fields (parentHash, mixHash,
nonce, number, gasUsed, extraData, coinbase) and the
precompile-defer config fields (quasarTimestamp, fortunaTimestamp,
graniteTimestamp = 253399622400). luxd's defaults for these fields
produce a DIFFERENT block-0 hash than the empirical form.

Fix: sync this deployment-side genesis.json to the empirical canonical
at ~/work/lux/state/chains/zoo-mainnet/genesis.json (1449 bytes, sha256
f8482568e7a10e882c6bd0248549ad75257edec79a83f659f27e1a34e71b59fa).
Now bootstrap-chain will produce a chain with block-0 hash
0x7c548af47de27560779ccc67dda32a540944accc71dac3343da3b9cd18f14933
which matches the RLP archive's recorded parent.

Add LLM.md sibling at this path documenting:
- state-repo as source-of-truth (canonical) vs zoo/universe as
  deployment mirror
- the regression history (cc7d3d7 incomplete form vs empirical)
- the trade-off when re-firing CreateChainTx after the wrong-genesis
  chain has already committed (new chain ID required; aliases must
  re-sync)

This unblocks the zoo mainnet RLP import — a fresh CreateChainTx will
spawn a chain whose block-0 matches the RLP, and the script at
~/work/lux/universe/k8s/lux-mainnet/scripts/zoo-rlp-reimport.sh can
fire cleanly. The orphaned oxDk6jU7… chain stays in P-state but is
effectively unused; the chain-aliases map updates to the new ID.

Followup: testnet+devnet zoo genesis files and hanzo/pars/spc mainnet
genesis files may have the same regression. Audit + sync in a sibling
commit.
2026-06-05 09:45:30 -07:00
Hanzo AI d88c05fce0 configs/genesis: adopt zoo L1 genesis as canonical (Task #109)
Zoo L1 genesis blobs now live in this repo, moved out of
luxfi/genesis. Files are byte-identical to the luxfi/genesis source
at the time of the move (verified via cmp).

  configs/genesis/mainnet/genesis.json — Zoo L1 mainnet (chainId 200200)
  configs/genesis/mainnet/upgrade.json — Zoo L1 mainnet activation
  configs/genesis/testnet/genesis.json — Zoo L1 testnet (chainId 200201)
  configs/genesis/devnet/genesis.json  — Zoo L1 devnet

luxfi/genesis carries MOVED.txt tombstones at the old
configs/zoo-{mainnet,testnet,devnet}/ paths and a no-shadow test
guard against accidental re-introduction. Each brand owns its own L1
genesis blob; luxfi/genesis only owns Lux primary-network genesis.
2026-06-05 09:07:16 -07:00
Hanzo AI 77c2300fd6 k8s/cf-zones: adopt zoo.network, zoo.exchange, zoo.ngo DNS zone YAMLs
Coordination with the Lux universe DX cleanup — Cloudflare zone YAMLs
are pure brand assets and belong with their owning universe. Moved
2026-06-03 from lux/universe/k8s/dns/{zoo-exchange,zoo-network,zoo-ngo}.yaml.

k8s/cf-zones/sync.sh is the per-brand drift-detect-and-PATCH script
(same logic as the legacy lux/universe scripts/dns-sync.sh, scoped to
this directory). Dry-run by default; set CF_API_TOKEN and pass --apply
to push changes.

Single-zone usage:
  ./k8s/cf-zones/sync.sh --zone zoo-ngo
2026-06-03 11:27:14 -07:00
Hanzo AI 436b90b62f k8s/zoo-mainnet: bring zoo-bot + zoo-build under zoo/universe ownership
Coordination with the Lux universe DX cleanup — Zoo brand sites belong
in Zoo universe, not Lux universe. The corresponding files in
~/work/lux/universe/k8s/zoo-mainnet/ will be removed in the matched
Lux universe commit.

zoo-bot.yaml and zoo-build.yaml each declare a Deployment + Service +
hanzo.ai/v1alpha1 IngressRoute. The previous lux/universe/k8s/zoo-mainnet/
directory had no kustomization.yaml, so these were applied imperatively
to lux-k8s; folding them into the zoo/universe kustomization brings them
under the same brand-bounded reconciliation path as zoo-industries.

Live deployments preserved:
  lux-k8s zoo-mainnet/zoo-bot           1/1   33d
  lux-k8s zoo-mainnet/zoo-build         1/1   34d
  lux-k8s zoo-mainnet/zoo-industries    1/1   33d   (already in repo)

zoo-build image still on ghcr.io/luxfi/build-zoo:0.1.7 — follow-up to
migrate the build pipeline to ghcr.io/zooai/* per the canonical
container registry rule.
2026-06-03 11:24:02 -07:00
Hanzo AI f518621f63 universe: full DX collapse — kill dead auth subnet, SQLite + Litestream, fix CI drift
Major collapses across 61 files (-2394 / +642, net -1752):

- Killed Zoo Auth Subnet: parallel auth chain contradicts hanzo.id Casdoor as canonical IAM
  (deleted k8s/zoo-auth/, compose.auth.yml, auth-gateway/, chain-configs/auth-subnet-genesis.json)
- Postgres StatefulSet → SQLite + Litestream + MinIO replication (per memory no_postgres_sqlite_only)
  Bridge becomes single-writer (replicas: 1) with restore init + replicate sidecar
- Plaintext Cloudflare API token in .env wiped; .env added to .gitignore
- 7× <your-domain> CI placeholders → kms.hanzo.ai
- 3 per-env manifests (3 × 130 lines, 90% dup) → 1 canonical manifests/services.yml (89 lines)
- 6 floating tags (:latest / :main) → :main-amd64 immutable per-arch
- Broken org refs ghcr.io/zoolabs/* + @zoolabs npm → ghcr.io/zooai/* + @zooai
- Workflow runner pool zoo-build-amd64 (wrong) → zoo-build-linux-amd64 (canonical)
- ENV var conflation ZOO_NETWORK_ID → LUX_NETWORK_ID + ZOO_EVM_CHAIN_ID
- Stale dev scripts (start/stop/status/setup.sh), convert-to-submodules.sh, Dockerfile.dev,
  .gitmodules, zoo.id/ static page, retired ARC values, k8s/patches/ all removed
- Duplicate deploy.yml workflow (shadows ci.yml) killed
- Makefile extended with remote-ops targets (deploy-{dev,test,main}, pods-{dev,test,main}, verify)

Tier-1 metrics all zero: plaintext secrets, Postgres in source, /api/ in own routes, brand contamination.

Zoo L2 EVM on Lux mainnet remains GREEN (chainId 0x30e08, balance 5e27 wei).
All 4 kustomize overlays render clean (system=35, devnet=36, testnet=36, mainnet=39).
2026-06-03 11:12:00 -07:00
Hanzo AI 09ca9d2781 runners: mark DOKS ARC retired, pointer to canonical arcd fleet
zooai now served by host-level arcd daemons (dbc Mac + evo WSL +
evo Windows) alongside hanzoai, luxfi, parsdao, zenlm. See
hanzoai/universe/infra/runners/README.md for the fleet topology.
2026-06-02 13:46:28 -07:00
Hanzo AI be4c735501 zoo-mainnet/luxd-rlp-import: realized via LuxNetwork.tenantImports[]
Refactor plan in the docstring is done — luxfi/operator v0.7.0
content-hash-addressed tenantImports lives in
lux/universe/k8s/lux-mainnet/network.yaml. This file remains as the
authoritative record of Zoo's RLP source spec (URL/SHA256/blockchainID).
On future spec edits: update both this file and the matching
tenantImports[] entry; the operator's content hash flips and re-fires
the import automatically.

No more inline-shell-block coordination with the lux team. No more
sentinel version-bumping.
2026-06-01 17:00:00 -07:00
Hanzo AI 4697079490 merge: fix/zoo-industries-semver-pin 2026-06-01 16:03:17 -07:00
Hanzo AI b50377b756 merge: chore/canonical-chain-yaml 2026-06-01 16:03:17 -07:00
Hanzo DevandGitHub 316774ed7f feat(chain): canonical Zoo L2 chain.yaml (#2)
Zoo is an L2 on the Lux primary network — rides Lux's validator
set via primary-network staking and does not own a sovereign
networkID. Zoo's identity surfaces through its EVM chainID + the
brand fields.

Model:
- network.parent = lux  → inherits Lux's networkID per env
- chains[*].evmChainIds.{mainnet,testnet,devnet} = {200200, 200201, 200202}
- Each chain gets its own chainID via CreateChainTx on Lux's P-chain

Per-env chainIDs sourced from
~/work/lux/genesis/configs/zoo-{env}/genesis.json — single source
of truth, no duplication in this manifest.

Lux primary L1 declares Zoo as an L2 child in its own chain.yaml.
2026-05-30 01:17:46 -07:00
Hanzo AI 4231be6300 feat(chain): canonical Zoo L2 chain.yaml
Zoo is an L2 on the Lux primary network — rides Lux's validator
set via primary-network staking and does not own a sovereign
networkID. Zoo's identity surfaces through its EVM chainID + the
brand fields.

Model:
- network.parent = lux  → inherits Lux's networkID per env
- chains[*].evmChainIds.{mainnet,testnet,devnet} = {200200, 200201, 200202}
- Each chain gets its own chainID via CreateChainTx on Lux's P-chain

Per-env chainIDs sourced from
~/work/lux/genesis/configs/zoo-{env}/genesis.json — single source
of truth, no duplication in this manifest.

Lux primary L1 declares Zoo as an L2 child in its own chain.yaml.
2026-05-30 01:17:25 -07:00
Hanzo AI f75b580aff feat(zoo-mainnet): record Zoo L2 EVM RLP import spec (source of truth)
Until luxfi/operator grows LuxNetwork.spec.tenantImports[], the actual
RLP import block lives inline in lux/universe luxd-startup.yaml. This
file is the canonical record of Zoo's contribution to that file:
800 blocks from git df8c246 (sha256 0850841a...), imported to chainID
200200 on all 5 lux-mainnet luxd validators 2026-05-25.

Tip: block 799, hash 0xd6f92941bb...; verifiable at
https://api.lux.network/ext/bc/zoo/rpc.
2026-05-25 17:07:25 -07:00
Hanzo AI fa5aa544fd chore: update 2026-05-25 15:15:21 -07:00
Hanzo AI 701b93f048 feat(zoo-mainnet): SPA CR for zoo.industries
Deploys via hanzoai/spa:1.2.0 runtime + zooai/zoo-industries content
image. Operator (kind: SPA, v0.3.2+) reconciles to Deployment +
Service + Ingress on zoo.industries. Replaces the prior Cloudflare
Pages deploy path (cf-pages.yml dropped from zoo.industries repo).

After ghcr.io/zooai/zoo-industries publishes its first image:
  kubectl --context=do-sfo3-zoo-k8s apply -k k8s/zoo-mainnet/
2026-05-19 11:00:20 -07:00
Hanzo AI 34eb7168e4 go.mod: bump go directive to 1.26.3 (security advisory) 2026-05-12 20:55:08 -07:00
Hanzo AI 63fd6b8e20 test(e2e): security swap spec 2026-05-07 10:58:42 -07:00
Hanzo AI b943b526fe docs(arch): desktop stack decomposition + brand-variant build flow
Documents how zoo/app shims hanzo/desktop, the BrandConfig contract,
chain IDs (zoo 200200/200201/200202, hanzo 36900/36901), the sidecar
list (Ollama dropped), and the read-order for new contributors.
2026-05-04 11:28:31 -07:00
Hanzo DevandGitHub 3e51152561 chore: enforce ghcr.io/zooai registry + add service-released auto-bump (#1)
Cleans up registry drift across the live cluster:
- base/operator.yaml: 0.1.0 (non-existent in GHCR) -> :main + TODO semver
- base/bridge.yaml:   0.1.0 (non-existent in GHCR) -> :main + TODO semver
- base/explorer.yaml: blockscout :latest -> :7.0.2.commit.900ef697 (release SHA)
- zoo-system/patches.yaml: remove redundant operator image override

Adds .github/workflows/service-released.yml for auto-bump on
repository_dispatch from zoo service repos. Validates payload pins
semver (rejects :main/:latest) — forcing function for service CIs to
ship semver tags before auto-bump can land.

LLM.md: registry policy, image pin status table, auto-bump flow,
mpc-node orphan documentation, live cluster drift snapshot.
2026-04-30 11:56:18 -07:00
Hanzo AI 2148af97b3 e2e: add IAM (04) + commerce (05) suites with brand purity checks 2026-04-19 12:53:29 -07:00
Hanzo AI 4de005cb60 feat: native amd64+arm64 parallel builds, arch-tagged images 2026-04-12 18:52:33 -07:00
Hanzo AI d1a7dd7daf fix: standardize CI — self-hosted runners, correct registry paths, no ubuntu-latest 2026-04-12 18:40:49 -07:00
Hanzo AI 4fe8ee59b0 feat: add docs and zips k8s manifests 2026-04-12 17:47:35 -07:00
Hanzo AI 718b8208db feat: update universe for new architecture (Vite, wallets, k8s) 2026-04-12 17:47:17 -07:00
Hanzo AI 93faa329ba ci: add self-hosted Renovate (every 15min) 2026-04-09 22:33:56 -07:00
Hanzo AI 25149657f8 ci: group all docker image updates into single rolling PR 2026-04-09 22:33:14 -07:00
Hanzo AI 7cb3092c1e infra: deterministic deploys — pin images to semver, Renovate + dispatch
- All images pinned to semver tags (no :latest/:main anywhere)
- Renovate scans manifests/*.yml + k8s/*.yaml for docker version bumps
- Auto-merge minor+patch for zooai/*, patch-only for hanzoai/*
- Pin digests enabled for reproducible deploys
- update-image.yml: repository_dispatch callback creates PRs on new tags
- Renovate packagePatterns updated: zooai (not zoo-labs)
2026-04-09 22:26:42 -07:00
Hanzo AI 2845aa4718 infra: add ARC runner scale sets for zooai org
- zoo-build-linux-amd64: DOKS (hanzo-k8s), max 10
- zoo-build-linux-arm64: GKE (apps-gke), max 10
- Same clusters as hanzoai runners, separate GitHub App
2026-04-09 21:13:56 -07:00
Hanzo AI e378c8898d infra: migrate all refs from zoo-labs/DOCR to zooai/GHCR
- k8s: registry.digitalocean.com/hanzo/zoo-* → ghcr.io/zooai/*
- k8s: ghcr.io/zoolabs/* → ghcr.io/zooai/*
- manifests: zoo-labs/* repos → zooai/*
- manifests: ghcr.io/zoo-labs/* images → ghcr.io/zooai/*
- compose: ghcr.io/zoolabs/* → ghcr.io/zooai/*
- workflows: zoo-labs/universe → zooai/universe
- docs: zoo-labs org → zooai
2026-04-09 20:03:35 -07:00
Hanzo AI 11069726d9 feat: replicate sidecar patches for SQLite replication to S3
ConfigMap, S3 storage service, bridge deployment patch, apply script.
Same hanzoai/replicate image as <tenant>. DigitalOcean Spaces backend.
2026-04-09 16:33:23 -07:00
Hanzo AI 8251f255f8 feat: Web5 full-chain architecture — 15 chains, M-Chain, BLG token, 3-tier crypto
- Add M-Chain (MpcVM) for CGGMP21 MPC signing via luxfi/mpc
- Update K-Chain with aead+mlkem default scheme, T-Chain threshold reveal
- Update T-Chain with TFHE threshold gating + CKKS confidential compute
- Expand Beluga L3 precompiles: AI inference, FHE CKKS, PQ crypto, ZK proofs
- Add BLG token CRD: gas on Beluga L3, bridgeable to Zoo EVM via B-Chain
- Add /v1/mpc and /v1/beluga routes to gateway
- Fix container images to ghcr.io/zoolabs/* (not DO registry)
- Rewrite README as definitive Zoo Network architecture document
2026-04-07 13:56:43 -07:00
Hanzo AI d2bf8b3c91 feat: Zoo Auth Subnet — decentralized IAM/KMS/MPC on-chain
Private subnet running 3 VMs (I-Chain + K-Chain + T-Chain):
- I-Chain (identityvm): DID, verifiable credentials, OIDC sessions
- K-Chain (keyvm): ML-KEM post-quantum key management, FHE storage
- T-Chain (thresholdvm): threshold signing (CGGMP21/FROST/BLS)

Auth gateway translates standard web2 protocols to on-chain:
- OAuth2/OIDC → I-Chain DID + T-Chain threshold-signed JWTs
- WebAuthn/Passkey → I-Chain credentials + K-Chain key storage
- Key management → K-Chain ML-KEM + T-Chain threshold signing
- OIDC discovery → /.well-known/openid-configuration from chain state

Zero integration cost: same API as Okta/Auth0, decentralized backend.
No single signing key. No centralized identity provider. Just math.

Includes: compose.auth.yml (local 3-node), K8s manifests (production),
genesis configs for all 3 chains.
2026-04-07 12:23:26 -07:00
Hanzo AI 1c8b13260a fix: KMS-only CI secrets, remove GitHub secrets fallback 2026-04-07 10:25:33 -07:00
Hanzo AI 8bf7f4c659 feat: CI/CD pipeline -- build-service, manifests, deploy, renovate (matches liquidity pattern)
- manifests/{main,test,dev}.yml: 13 services across zoo-{mainnet,testnet,devnet}
- build-service.yml: reusable lint+test+build+push to GHCR, notify universe
- ci.yml: layer 2 orchestrator -- deploy-single, deploy-all, E2E
- scripts/ci/update-manifest.sh: atomic manifest field update
- scripts/ci/deploy-all.sh: kustomize-based deploy to DOKS
- renovate.json: auto-merge minor/patch for zoo-labs + hanzoai images

All GHCR (ghcr.io/zoo-labs/*), all DOKS (do-sfo3-zoo-k8s), no GAR/GKE.
2026-04-07 09:57:44 -07:00
Hanzo AI 85320e9d49 fix: remove all gitlink submodules from universe
zoo.exchange, zoo.ngo, zoo.fund, zoo.vote, zoo.foundation,
zoo.lab, zoo.network, zoo.voyage are all standalone repos.
They live at ~/work/zoo/{name} not embedded in universe.

Universe is the deployment control plane (K8s, CI/CD, E2E),
not a monorepo for application code.
2026-04-07 09:50:17 -07:00
Hanzo AI 60fff33c56 fix: complete k8s overlay patches — add GPU/storage to testnet+mainnet, fix testnet image tag
- testnet: tag latest->test, add node resources (GPU+storage), add operator patch, add explorer requests
- mainnet: add node resources (GPU+storage), add operator patch
- All overlays now consistently patch: validators, networkId, chainId, image tag, GPU, storageClass
2026-04-07 02:15:41 -07:00
Hanzo AI 6e314b23a8 fix: sync k8s overlays with chain-native base — remove stale MPC, fix names
All overlays referenced zoo-subnet (renamed to zoo-chain) and mpc-node
(removed — threshold signing is chain-native via T-Chain). Fixes:
- Overlay patches target zoo-chain, use validators field, structured image spec
- Remove all mpc-node StatefulSet patches (devnet/testnet/mainnet/system)
- Remove stale mpc.zoo.network ingress route, add api.zoo.ngo gateway route
- Add zoo-gateway Service for ingress backend
- Route deploy.sh to correct DOKS cluster per environment
2026-03-02 23:59:00 -08:00
Hanzo AI 6665099ded fix: deploy workflow auto-triggers on dev/test/main push
- Branch mapping: dev→devnet, test→testnet, main→mainnet
- Auto-deploy on k8s/ changes (was workflow_dispatch only)
- Apply CRDs before kustomization
- Single DOKS cluster (do-sfo3-zoo-k8s)
- Correct overlay names (zoo-devnet/zoo-testnet/zoo-mainnet)
2026-04-07 00:14:16 -07:00
Hanzo AI 0663a655e4 feat: full 14-chain GPU-accelerated L2 — complete Zoo network
All VMs on every validator, GPU-accelerated, no external dependencies:

Chains: EVM (cevm GPU), DEX, FHE (GPU TFHE), Identity (I),
Keys (K), Threshold (T), Bridge (B), ZK (Z), Quantum (Q),
AI (A), Graph (G), Oracle (O), Relay (R) + Beluga L3

GPU acceleration:
- cevm: C++ GPU EVM at 2.3 Gops/sec
- TFHE: FHE bootstrap/keyswitch 40x speedup
- BLS12-381: consensus batch verify on GPU
- NTT: ML-DSA/ML-KEM/SLH-DSA post-quantum on GPU

Deleted: mpc.yaml (T-Chain replaces MPC StatefulSet)
Updated: gateway routes for all 14 chain RPCs
2026-04-06 12:33:43 -07:00
Hanzo AI ec651af7b1 feat: GPU-native blockchain — full GPU acceleration config
Network config:
  gpu.enabled: true
  gpu.backend: auto (metal/cuda/dawn)
  gpu.evmAccelerator: cevm (C++ GPU EVM, 2.3 Gops/sec)
  gpu.fheAccelerator: auto (TFHE on GPU)
  gpu.consensusAccelerator: true (BLS12-381 batch verify)
  gpu.pqCryptoAccelerator: true (ML-DSA/ML-KEM/SLH-DSA via NTT)
  nvidia.com/gpu: 1 requested per validator pod

All chains: EVM + DEX + FHE + Identity + Keys + Threshold + Bridge + ZK
All GPU-accelerated via unified luxcpp/gpu backend
2026-04-06 12:29:26 -07:00
Hanzo AI 80e6eb1bc7 fix: remove buildTag reference, allvms is default 2026-04-06 12:12:36 -07:00
Hanzo AI 67ab270897 feat: chain-native IAM/KMS/MPC — allvms build with I/K/T/B/Z chains
Replace external services with on-chain VMs running on every validator:

- I-Chain (IdentityVM): DIDs, verifiable credentials, ZK proofs
  Replaces: external Hanzo IAM (id.zoo.network)
- K-Chain (KeyVM): distributed KMS, ML-KEM, ML-DSA, threshold sharing
  Replaces: external Hanzo KMS (kms.zoo.network)
- T-Chain (ThresholdVM): MPC threshold signing, FHE GPU-accelerated
  Replaces: mpc-node StatefulSet (3 separate pods)
- B-Chain (BridgeVM): cross-chain with chain-native MPC signing
- Z-Chain (ZKVM): confidential transfers, ZK proofs

Validators stake ZOO and simultaneously participate in consensus,
key management, threshold signing, and identity verification.

Also adds: DEX, FHE chains to validator set, gateway routes for
all chain RPCs, updated deploy.sh for zoo-k8s context.
2026-04-06 12:09:08 -07:00
Hanzo AI c3655e13c9 feat: make up/down/status for zoo subnet on lux localnet 2026-04-04 13:19:44 -07:00
Hanzo AI 15797d1f56 feat: add ZooGateway CRD for api.zoo.ngo 2026-04-03 17:42:26 -07:00
Hanzo AI 42ddfa5f3b feat: add beluga.yaml to kustomization 2026-04-03 17:30:09 -07:00
Hanzo AI 2e8ecd2709 feat: Beluga L3 ZooChain CR + kustomize integration
ZooChain kind=beluga on existing Zoo validators. No new nodes —
operator reconciles the CR and adds the chain to running zood instances.
2026-04-01 20:30:27 -07:00
Hanzo AI 3b11941458 fix: zoo-node image (single node: EVM+DEX+FHE, not luxfi/node) 2026-04-01 18:06:59 -07:00
Hanzo AI 0236643a24 fix: devnet resources at spec.resources level (200m CPU, 512Mi — operator reads spec.resources not node.resources) 2026-04-01 17:47:28 -07:00
Hanzo AI 2a52d4c2e7 fix: reduce devnet resources (200m CPU, 512Mi, 20Gi storage) 2026-04-01 17:40:44 -07:00
Hanzo AI 1c455a0c10 fix: zoo chain uses luxfi/node (Lux EVM node, not Rust zoo-node) 2026-04-01 17:39:22 -07:00
Hanzo AI f4a7d2ca0e fix: rename zoo-subnet → zoo-chain (it's a chain, not a subnet) 2026-04-01 17:36:50 -07:00
Hanzo AI d4ea92a1e9 fix: validators (not replicas) + staking.secretPrefix per CRD schema 2026-04-01 17:32:18 -07:00
Hanzo AI 4ec0496ef9 fix: staking uses secretPrefix (not secretRef) per CRD schema 2026-04-01 17:30:14 -07:00