fix(agent-toolchain): bake libamt_protocol, give node a writable CARGO_HOME - #1558
Conversation
…O_HOME Agents cannot build multicast's cmd/caddy/sender or cmd/caddy/receiver, so BLO-30049, BLO-30253, BLO-30278, BLO-30722 and BLO-29536 have all sat blocked on the environment rather than on the code. Two concrete gaps, both measured on the running agent image (paperclip-agent:sha-a1d2f61-k8s-vendored): 1. libamt_protocol is absent. `ldconfig -p | grep -c amt_protocol` -> 0, and both packages link against -lamt_protocol, so every CGO_ENABLED=1 build of them dies at link time. Baked here using the same recipe multicast's own CI uses (clone over HTTPS, `make ffi`, `make install`, `ldconfig`). Verified in a pod on that exact image: .so + .a + header install cleanly and ldconfig registers the library. 2. CARGO_HOME is root-owned but agents run as `node`, so any workspace `cargo build` fails with "Permission denied (os error 13)" on the registry cache. Reproduced as uid 1000 on the live image, and fixed by pointing CARGO_HOME at a node-owned path — same shape as the npm prefix already directly above it. This is what lets the fec-raptorq c-bindings, a per-repo submodule that should not be baked, build per run. Deliberately NOT included: libsqlite3-dev. BLO-30359 lists missing SQLite dev metadata as a fault, but mattn/go-sqlite3 v1.14.33 vendors the amalgamation — verified by building and running a CGO_ENABLED=1 binary importing it on the current image with no SQLite headers present. That fault class is a red herring and adding the package would be cargo cult. Refs BLO-30359 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Hey @eyad-hussein! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: dfc58af
Critical Issues (0)
Important Issues (1)
- [native-codex]
Dockerfile.agent-toolchain:29— The image build defaultsAMT_PROTOCOL_REFto the mutablemainbranch, so rebuilding the same toolchain inputs can silently compile and publish a different third-party library (or fail when upstream changes), undermining the images content-addressed/reproducible build contract.- Pin this default to a reviewed immutable commit or release tag, and update it deliberately when upgrading the AMT protocol dependency.
Suggestions (0)
Strengths
- Installs both the shared and static AMT libraries plus the header, and verifies the dynamic linker entry and header after installation.
- Moves Cargos writable cache to a node-owned path while leaving the system Rust toolchain shared and read-only.
Recommended Action
- Fix the Important issue before merge.
- Re-run the toolchain image build and verify the AMT FFI consumer links successfully.
Addresses Ally's Important issue on #1558: defaulting AMT_PROTOCOL_REF to the mutable `main` branch let identical Dockerfile inputs produce a different third-party library, which breaks the reproducibility contract the content-addressed toolchain tag exists to provide. Pinned to ef1bf21a7b82e964dd336b8fe1f7204860c839d5 — this is exactly the commit multicast's own `amt-protocol` submodule points at (`git ls-tree HEAD amt-protocol`), so the baked library matches what the consuming repo expects rather than being an independently drifting third pin. Branch clone replaced with an explicit shallow fetch of that SHA, plus a fail-closed guard: if AMT_PROTOCOL_REF is anything that does not resolve to itself (i.e. a branch or tag), the build stops and says to pin a commit. That makes the reproducibility property enforced rather than conventional. Verified `git fetch --depth 1 origin <sha>` resolves against GitHub. Refs BLO-30359 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Hey @eyad-hussein! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
Both review items addressed — pushed
|
There was a problem hiding this comment.
Ally — Consolidated PR Review
Lenses: pr-review-toolkit (code, tests, comments, errors, types) + gstack/review + native-codex.
Reviewed head: f0188a1
Prior Findings Dispositioned (1)
- prior:dfc58af important 1 — fixed —
Dockerfile.agent-toolchain:122—AMT_PROTOCOL_REFis now pinned to the full immutable commit SHAef1bf21a7b82e964dd336b8fe1f7204860c839d5, and the build guard at lines 129-134 rejects any ref that resolves differently.
Critical Issues (0)
Important Issues (0)
Suggestions (0)
Strengths
- Builds and installs the AMT FFI artifacts after the system Rust toolchain is available, with linker and header self-checks.
- Provides a node-owned Cargo cache without changing the shared system Rust installation.
- Keeps the dependency pin and content-derived image tagging aligned with reproducible builds.
Recommended Action
- No Critical or Important issues found; this review is approved.
- Consider Suggestions opportunistically.
|
Hey @eyad-hussein! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
1 similar comment
|
Hey @eyad-hussein! Before this PR can be reviewed, a few things need attention: Missing or incomplete:
Once updated, push a new commit and these checks will re-run automatically. — commitperclip |
Thinking Path
Linked Issues or Issue Description
Refs BLO-30359 — Agent multicast workspace cannot build cmd/caddy/sender or produce a real PR.
Blocked behind it, and unblocked by this: BLO-30049, BLO-30253, BLO-30278, BLO-30722, BLO-29536.
The bug, in the issue-template shape:
CGO_ENABLED=1 go test ./cmd/caddy/sender ./cmd/caddy/receiverin its workspace and getscannot find -lamt_protocolat link time. A workspacecargo buildseparately fails withPermission denied (os error 13)writing the Cargo registry cache.harbor.blockcast.net/paperclip-agent/paperclip-agent:*-k8s-vendored.critical; one gates an unplayable production live delivery service.What Changed
Single file,
Dockerfile.agent-toolchain:libamt_protocol. New stage after the Rust block (it shells out to cargo) that shallow-fetches a pinnedamt-protocolcommit, runsmake ffi && make installintoINSTALL_PREFIX=/usr/local, thenldconfig. Mirrors the recipemulticast's own CI already uses in.github/workflows/integ-tests.yml→ "Build and install amt-protocol". The repo is public, so an HTTPS fetch works and the submodule's SSH URL is not needed.AMT_PROTOCOL_REFdefaults toef1bf21a7b82e964dd336b8fe1f7204860c839d5— exactly the commitmulticast's ownamt-protocolsubmodule points at, so the baked library matches what the consuming repo expects. A fail-closed guard rejects any ref that does not resolve to itself, so a branch or tag cannot be substituted.ldconfig -p | grep -q amt_protocolandtest -f /usr/local/include/amt_protocol.h.CARGO_HOMEfornodeat/home/node/.cargo, placed beside the existingNPM_CONFIG_PREFIXblock it mirrors.RUSTUP_HOMEdeliberately stays at/usr/local/rustup.Deliberately not changed:
libsqlite3-devis not added — see Risks.Verification
Every claim below was measured in a pod running the current agent image,
harbor.blockcast.net/paperclip-agent/paperclip-agent:sha-a1d2f61-k8s-vendored, not reasoned about from the Dockerfile.Baseline — the gaps are real:
The AMT recipe works on that exact base:
The Cargo fault reproduces, and the fix resolves it (both as uid 1000):
Pinned fetch resolves against GitHub:
Delivery path — the change actually ships:
The tag moves, so
docker-agent.ymlrebuilds the toolchain rather than reusing the cached one, and the existing auto-bump rolls the fleet.On tests: commitperclip flags no test files. There is no unit-test seam for a Dockerfile — the executable assertions are the two build-failing self-checks in the new stage (
ldconfig -p | grep -q amt_protocol,test -f …/amt_protocol.h) plus theAMT_PROTOCOL_REFguard, all of which fail the image build rather than shipping a silent gap. That is the same discipline the file already applies withtinygo version,java -version,protoc --versionand the ffmpeg-versionloop. I have not retitled thisrefactor:— it is a fix, and mislabelling it to satisfy a check would be worse than explaining the gap. Happy to take a maintainer's call.Risks
Low, and contained to image build time.
github.com/Blockcast/amt-protocol. The toolchain image already fetches fromgo.dev,github.com,sh.rustup.rs,download.docker.comand others, so this adds no new class of dependency. A fetch failure fails the build loudly.multicast's submodule pointer must be bumped together. They are identical today; the Dockerfile comment says so explicitly. The fail-closed guard prevents the sloppier failure (silently switching to a branch), not this one.ENV CARGO_HOMEchange is image-wide. Nothing after that line in the build uses cargo, andRUSTUP_HOMEis untouched, so the shared toolchain still resolves — verified by the successfulnode-user build above.libsqlite3-devdeliberately omitted. BLO-30359 lists missing SQLite dev metadata as a fault. It is a red herring:mattn/go-sqlite3v1.14.33 vendors the amalgamation, verified by building and running aCGO_ENABLED=1binary importing it on the current image with no SQLite headers present (GO_SQLITE3_BUILD_OK/ok). Adding the package would be cargo cult. The risk I am accepting is that the ticket text and this PR disagree; I have posted the correction on the ticket rather than silently satisfying a wrong requirement.fec-raptorqis deliberately not baked. It is a submodule versioned with themulticastrepo, so a per-runcargo buildis correct — which the writableCARGO_HOMEis what enables. Baking it would pin a repo-versioned artifact into a shared image.Model Used
Claude Opus 5 (
claude-opus-5[1m]), 1M context, extended thinking, with tool use and code execution — drivingkubectl execprobes against live agent pods,gh/git, and the Paperclip API. Human-directed throughout by @eyad-hussein; all measurements were executed rather than inferred.Checklist
Blockcast/paperclipforagent-toolchain,amt-protocol,amt_protocol,libamt,CARGO_HOMEacross all states; no duplicate or in-flight PR touches this file for this purpose (fix(agent): unblock verified image rollout (PEN-2077) #1058 and ci(policy): fail CI when workflow content escapes its block scalar (BLO-23128) #1183 match the term but are unrelated)Fixes: #/Closes #/Refs #OR (b) described the issue in-PR following the relevant issue template🤖 Generated with Claude Code