Fixes a build break I introduced. vibe-node #2912 (galileo 3.0.3 → 3.0.8, merged as bc4cecf) broke the de-32 build — job ad580587:
#16 [zero-gravity-galileo-geth-node 7/8] RUN chmod +x /0g/bin/0gchaind
#16 0.287 chmod: cannot access '/0g/bin/0gchaind': No such file or directory
Root cause
The Dockerfile hardcoded galileo -> mv <root>/rpc /0g. That held for 3.0.3, where every profile dir carried its own copy of the binaries. 3.0.8 deduplicated them into a shared <root>/bin/ — which is why the tarball went 168 MB → 69 MB — leaving rpc/ as configs-only. So the move produced configs and no binaries, and the build died two layers later on a chmod.
Verified by downloading and listing all three:
galileo 3.0.3 <root>/{rpc,validator,archive,seed}/bin/ + per-profile configs
galileo 3.0.8 <root>/bin/ SHARED + <root>/{rpc,validator,seed,archive}/ configs
aristotle 1.0.6 <root>/bin/ + configs, no profile dirs at all
does <root>/rpc exist is not a sufficient test — my first attempt at this fix used exactly that and still failed, because 3.0.8 does ship rpc/; it just no longer holds bin/. The test run caught it.
The change
Take configs from the profile dir when present, then top the binaries up from the shared root bin/ when the profile dir didn't supply them:
if[ -d "${SRC}/rpc"];then cp -a "${SRC}/rpc/." /0g/;# configs (+ binaries pre-3.0.8)else cp -a "${SRC}/." /0g/;fi# aristotle: everything at rootif[ ! -f /0g/bin/0gchaind ]&&[ -d "${SRC}/bin"];then
cp -a "${SRC}/bin" /0g/ # >= 3.0.8: shared root bin/fi
Then assert both binaries immediately after extraction, naming the chain, the version and the known layouts — so the next upstream reshuffle reports itself at the point of failure instead of as an anonymous chmod error two layers downstream.
Verification — real artifacts, not synthetic
All three now yield an identical/0g, so 3.0.8 reaches parity with 3.0.3:
galileo 3.0.3 profile dir found -> .../rpc
OK | 0gchaind=76M geth=46M | 0g-home bin geth-config.toml
geth-genesis.json kzg-trusted-setup.json rollback_cl.sh
galileo 3.0.8 profile dir found -> .../rpc
binaries not in the profile dir -> topping up from .../bin
OK | 0gchaind=76M geth=46M | (identical set)
aristotle 1.0.6 no profile dir -> using tarball root
OK | 0gchaind=76M geth=46M | (+ archive/validator config variants)
The configs the templates reference — /0g/geth-config.toml and /0g/kzg-trusted-setup.json — are present in every case.
Negative case, a synthetic tarball with binaries in neither location:
0g: FATAL - missing after extraction: bin/0gchaind bin/geth
exit=1
⚠️Not built as a container image — no docker daemon on the controller. The extraction path is exercised verbatim against the real tarballs; the surrounding layers are unchanged. The real build happens on de-32 after deploy, and job ad580587 can simply be re-run.
What I got wrong
I verified the aristotle 1.0.6 tarball layout end-to-end before merging #3356 — downloaded it, confirmed bin/0gchaind and bin/geth — and said so explicitly. I did not apply the same check to galileo 3.0.8 when merging #2912, and that check would have caught this before it reached a host.
#2912's own verification (a clean ./update.sh) only ever proved the compose renders, never that the image builds. Those are different claims, and I let the first stand in for the second.
Impact is contained: 0g has zero dRPC entries and no rpcs: host assignment, so this is the de-32 bench only — no serving node, no revenue. aristotle 1.0.6 built and exported cleanly in the same job, which independently validates #61 and #3356 on a real build.
Related: #61 (merged) made the download fail loudly; this makes the extraction fail loudly. Same lesson, adjacent step.
**Fixes a build break I introduced.** vibe-node #2912 (galileo `3.0.3 → 3.0.8`, merged as `bc4cecf`) broke the de-32 build — job **`ad580587`**:
```
#16 [zero-gravity-galileo-geth-node 7/8] RUN chmod +x /0g/bin/0gchaind
#16 0.287 chmod: cannot access '/0g/bin/0gchaind': No such file or directory
```
## Root cause
The Dockerfile hardcoded `galileo -> mv <root>/rpc /0g`. That held for 3.0.3, where **every profile dir carried its own copy of the binaries**. 3.0.8 **deduplicated them into a shared `<root>/bin/`** — which is why the tarball went **168 MB → 69 MB** — leaving `rpc/` as configs-only. So the move produced configs and no binaries, and the build died two layers later on a `chmod`.
Verified by downloading and listing all three:
```
galileo 3.0.3 <root>/{rpc,validator,archive,seed}/bin/ + per-profile configs
galileo 3.0.8 <root>/bin/ SHARED + <root>/{rpc,validator,seed,archive}/ configs
aristotle 1.0.6 <root>/bin/ + configs, no profile dirs at all
```
**`does <root>/rpc exist` is not a sufficient test** — my first attempt at this fix used exactly that and still failed, because 3.0.8 *does* ship `rpc/`; it just no longer holds `bin/`. The test run caught it.
## The change
Take configs from the profile dir when present, then **top the binaries up** from the shared root `bin/` when the profile dir didn't supply them:
```sh
if [ -d "${SRC}/rpc" ]; then cp -a "${SRC}/rpc/." /0g/; # configs (+ binaries pre-3.0.8)
else cp -a "${SRC}/." /0g/; fi # aristotle: everything at root
if [ ! -f /0g/bin/0gchaind ] && [ -d "${SRC}/bin" ]; then
cp -a "${SRC}/bin" /0g/ # >= 3.0.8: shared root bin/
fi
```
Then **assert both binaries immediately after extraction**, naming the chain, the version and the known layouts — so the next upstream reshuffle reports itself at the point of failure instead of as an anonymous `chmod` error two layers downstream.
## Verification — real artifacts, not synthetic
All three now yield an **identical** `/0g`, so 3.0.8 reaches parity with 3.0.3:
```
galileo 3.0.3 profile dir found -> .../rpc
OK | 0gchaind=76M geth=46M | 0g-home bin geth-config.toml
geth-genesis.json kzg-trusted-setup.json rollback_cl.sh
galileo 3.0.8 profile dir found -> .../rpc
binaries not in the profile dir -> topping up from .../bin
OK | 0gchaind=76M geth=46M | (identical set)
aristotle 1.0.6 no profile dir -> using tarball root
OK | 0gchaind=76M geth=46M | (+ archive/validator config variants)
```
The configs the templates reference — `/0g/geth-config.toml` and `/0g/kzg-trusted-setup.json` — are present in every case.
Negative case, a synthetic tarball with binaries in neither location:
```
0g: FATAL - missing after extraction: bin/0gchaind bin/geth
exit=1
```
⚠️ **Not built as a container image** — no docker daemon on the controller. The extraction path is exercised verbatim against the real tarballs; the surrounding layers are unchanged. The real build happens on de-32 after deploy, and job `ad580587` can simply be re-run.
## What I got wrong
I verified the aristotle 1.0.6 tarball layout end-to-end before merging #3356 — downloaded it, confirmed `bin/0gchaind` and `bin/geth` — and said so explicitly. I did **not** apply the same check to galileo 3.0.8 when merging #2912, and that check would have caught this before it reached a host.
#2912's own verification (a clean `./update.sh`) only ever proved the compose *renders*, never that the image *builds*. Those are different claims, and I let the first stand in for the second.
Impact is contained: 0g has zero dRPC entries and no `rpcs:` host assignment, so this is the de-32 bench only — no serving node, no revenue. **aristotle 1.0.6 built and exported cleanly** in the same job, which independently validates #61 and #3356 on a real build.
Related: **#61** (merged) made the *download* fail loudly; this makes the *extraction* fail loudly. Same lesson, adjacent step.
Fixes the de-32 build break introduced by vibe-node #2912 (galileo 3.0.3 -> 3.0.8,
merged as bc4cecf). Job ad580587:
#16 chmod: cannot access '/0g/bin/0gchaind': No such file or directory
The Dockerfile hardcoded 'galileo -> mv <root>/rpc /0g'. That held for 3.0.3, where
every profile dir carried its own copy of the binaries. 3.0.8 DEDUPLICATED them into
a shared <root>/bin/ (168 MB -> 69 MB), leaving rpc/ as configs-only — so the move
produced configs and no binaries, and the build died two layers later on a chmod.
Note 'does <root>/rpc exist' is not a sufficient test: 3.0.8 still ships rpc/, it just
no longer holds bin/. Verified layouts:
galileo 3.0.3 <root>/{rpc,validator,archive,seed}/bin/ + configs
galileo 3.0.8 <root>/bin/ shared + <root>/{rpc,...}/ configs
aristotle 1.0.6 <root>/bin/ + configs, no profile dirs
So: take configs from the profile dir when present, then top the binaries up from the
shared root bin/ if the profile dir did not supply them.
Also asserts both binaries exist immediately after extraction, naming the chain, the
version and the known layouts — so the next upstream reshuffle reports itself at the
point of failure rather than as an anonymous chmod error.
Verified against the real artifacts — all three now yield an identical /0g
(bin/0gchaind 76M, bin/geth 46M, geth-config.toml, geth-genesis.json,
kzg-trusted-setup.json, 0g-home/, rollback_cl.sh), and a synthetic unknown layout
exits 1 with the diagnostic instead of reaching chmod.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
claude
manually merged commit 6df9f0f17a into main2026-08-12 16:49:39 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes a build break I introduced. vibe-node #2912 (galileo
3.0.3 → 3.0.8, merged asbc4cecf) broke the de-32 build — jobad580587:Root cause
The Dockerfile hardcoded
galileo -> mv <root>/rpc /0g. That held for 3.0.3, where every profile dir carried its own copy of the binaries. 3.0.8 deduplicated them into a shared<root>/bin/— which is why the tarball went 168 MB → 69 MB — leavingrpc/as configs-only. So the move produced configs and no binaries, and the build died two layers later on achmod.Verified by downloading and listing all three:
does <root>/rpc existis not a sufficient test — my first attempt at this fix used exactly that and still failed, because 3.0.8 does shiprpc/; it just no longer holdsbin/. The test run caught it.The change
Take configs from the profile dir when present, then top the binaries up from the shared root
bin/when the profile dir didn't supply them:Then assert both binaries immediately after extraction, naming the chain, the version and the known layouts — so the next upstream reshuffle reports itself at the point of failure instead of as an anonymous
chmoderror two layers downstream.Verification — real artifacts, not synthetic
All three now yield an identical
/0g, so 3.0.8 reaches parity with 3.0.3:The configs the templates reference —
/0g/geth-config.tomland/0g/kzg-trusted-setup.json— are present in every case.Negative case, a synthetic tarball with binaries in neither location:
⚠️ Not built as a container image — no docker daemon on the controller. The extraction path is exercised verbatim against the real tarballs; the surrounding layers are unchanged. The real build happens on de-32 after deploy, and job
ad580587can simply be re-run.What I got wrong
I verified the aristotle 1.0.6 tarball layout end-to-end before merging #3356 — downloaded it, confirmed
bin/0gchaindandbin/geth— and said so explicitly. I did not apply the same check to galileo 3.0.8 when merging #2912, and that check would have caught this before it reached a host.#2912's own verification (a clean
./update.sh) only ever proved the compose renders, never that the image builds. Those are different claims, and I let the first stand in for the second.Impact is contained: 0g has zero dRPC entries and no
rpcs:host assignment, so this is the de-32 bench only — no serving node, no revenue. aristotle 1.0.6 built and exported cleanly in the same job, which independently validates #61 and #3356 on a real build.Related: #61 (merged) made the download fail loudly; this makes the extraction fail loudly. Same lesson, adjacent step.