Skip to content

feat(native): build Compose images in the guest engine (runtime build-image) - #606

Closed
roodboi wants to merge 13 commits into
claude/native-build-groundworkfrom
claude/native-image-builds
Closed

roodboi wants to merge 13 commits into
claude/native-build-groundworkfrom
claude/native-image-builds

Conversation

@roodboi

@roodboi roodboi commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Stacked on #583 (claude/native-build-groundwork); review only the top two commits. Covers units NB2 (build network) and NB4 (runtime build-image) of native Compose build: support. Native up does not call the new operation yet, so no user-visible up behavior changes. The frontend wiring comes in a later PR of this stack.

Build network (NB2)

  • Egress pools (internet, approved-hosts, host-gateway) now start dockerd with a fixed default bridge, --bip=10.213.255.1/24 --icc=false, instead of --bridge=none. The range is outside Docker's default address pools, so owned project networks keep their allocations. Isolated pools keep --bridge=none, and builds refuse on them.

  • guest-build-network.sh runs after the engine is ready. It isolates docker0:

    • it drops build traffic to the guest itself (relays, sockets, the engine), to the host gateway 100.96.0.1 and to 169.254.0.0/16;
    • it allows DNS to the guest's configured resolvers;
    • it writes a readiness marker bound to the boot ID.

    Docker's own isolation chains already drop default-bridge to project-bridge traffic. A failure here does not fail pool startup, because graph containers never use the bridge; builds then refuse on that pool.

  • The engine client now refuses, before dispatch, any container create whose HostConfig.NetworkMode is unset, default, bridge or host, and any connect to the default bridge. Only BuildKit RUN steps can therefore use that bridge.

runtime build-image --request <private file> --json (NB4)

  • Request and inputs. The request is a singly linked, mode-0600 JSON file owned by the user. The context and Dockerfile resolve inside the project without symlinks (the existing source::resolve). The Dockerfile and .dockerignore files must be bounded, singly linked regular files.
  • Base images. The Dockerfile is scanned with the feat(native): pin a BuildKit client and scan Dockerfile image references #583 scanner. Every image it can pull resolves to a digest-pinned manifest through the registry code (registry_image::resolve_build_base, digest refs must match their pin). Each one is handed to BuildKit as a named context <name>=docker-image://docker.io/<repo>@<digest> in the private bake definition. A base image whose stored configuration has ONBUILD triggers refuses. That closes the gap feat(native): pin a BuildKit client and scan Dockerfile image references #583 listed.
  • Build key. The key hashes the namespace (exact project root plus branch), context and Dockerfile paths, Dockerfile and .dockerignore bytes, target, salted argument hashes, platform, and the buildx and engine versions. Other files in the context are not hashed, matching Compose: an existing image is reused until an explicit rebuild. Base-image tags resolve only when a build runs. Each project root and worktree gets its own namespace, so one shared pool can host many of them.
  • Build client and arguments. The client runs buildx bake with a private mode-0600 definition file. The file carries the context, a private copy of the scanned Dockerfile and ignore rules, the pinned base contexts, labels, tag and argument values, and it is removed after the run on every path. Argv carries only paths and fixed flags. The client environment is a fixed allowlist: an empty private DOCKER_CONFIG and BUILDX_CONFIG (no contexts, credential helpers or proxies), the verified forwarded engine socket and the source policy path. Every string in the definition is escaped so bake never interpolates project text. Names must be plain identifiers; BUILDKIT_*, BUILDX_* and SOURCE_DATE_EPOCH refuse. Image labels carry a digest of the pool incarnation, never the token itself.
  • Build args are not secrets (Docker parity): an image records them in its history, and resolved base references an argument selects appear in base_pins in the receipt and the result. The guide says to use BuildKit build secrets for secrets (not yet supported natively). Forwarded client output still redacts argument values, including their Go-quoted, JSON-escaped and bake-encoded forms, and strips terminal controls within fixed bounds (16 KiB lines, 4 MiB total), as defense in depth. Native up (feat(native): build Compose build services in native up behind HACK_NATIVE_BUILDS #612) refuses a build arg equal to a managed hack env secret.
  • Source policy. Every build runs with EXPERIMENTAL_BUILDKIT_SOURCE_POLICY naming a private mode-0600 file. It holds build_policy::document (feat(native): pin a BuildKit client and scan Dockerfile image references #583) for exactly the pinned base references, sits beside the definition and is removed with it. The policy denies docker-image://* and oci-layout://* except the exact pinned references, and denies http, https, git and docker-image+blob sources. It is applied here, and it covers sources a base image's stored ONBUILD triggers add (such bases already refuse during resolution).
  • Time budget. One operation deadline covers base resolution and the client; resolution runs on its own thread so Ctrl-C, SIGTERM and the deadline interrupt it.
  • Leases and the down fence. The provider lease is held only for admission and adoption, never while BuildKit runs. A shared flock fence is taken under the lease and held for the whole operation. runtime down checks that fence under its own lease and refuses with provider_busy instead of stopping the engine under a build. A per-key lock serializes builds of one key.
  • Receipts and recovery. Receipts live at run/image-builds/<namespace>/<key>.json. A building receipt found under the key lock belongs to a dead attempt. It is recorded indeterminate and the image is built again; no operator action is needed.
  • Adoption. The builder must report the same incarnation, boot and engine ID as at admission. It must report the metadata's containerimage.config.digest, and the image must be linux/arm64. It must carry exactly the owned labels, no other reserved ownership labels (com.docker.compose.*, io.hack-local.*, io.hack.native-config.*, io.hack.native-file.*, io.hack.storage-witness.*), and no image VOLUMEs.
  • Reuse. A previous image is reused only with a verified receipt from the same checkout, incarnation and engine whose image still inspects that way.
  • Cancellation and timeout. SIGINT, SIGTERM and SIGHUP, and the time budget (default 30 minutes, at most 2 hours), stop the client's whole process group: SIGTERM, then SIGKILL after 10 seconds. The group is signaled only while its leader is unreaped.
  • No deletion. Nothing in this PR removes images or prunes caches. Storage admission and reclamation are the next PR in the stack.

Validation

  • RUSTUP_TOOLCHAIN=1.97.1 bun run check:local: pass. All-features Clippy with -D warnings (Zig 0.15.2): pass.

  • Focused tests: cargo test --lib -- image_build engine::tests registry_image build_scan: 65 passed, after rebasing onto the current feat(native): pin a BuildKit client and scan Dockerfile image references #583 head. They cover:

    • a fake pool and a fake client (/bin/sh reading a script from the client's private directory), with argv and environment inspected: values never reach argv or receipts;
    • warm reuse with no client spawn, rebuild, incarnation change, a missing image and relabeled images;
    • reserved labels, volumes and platform;
    • client failure and missing metadata;
    • timeout and cancellation killing the whole group;
    • recovery from a dead attempt, and a foreign receipt;
    • a pool change between admission and adoption;
    • key-lock and down-fence refusals, and base-resolution failure;
    • request refusals (paths, symlinks, hard links, arguments, bounds);
    • key binding;
    • the guest-daemon.sh argument matrix per network mode, and the network script syntax and modes;
    • the engine create/connect guard and ONBUILD detection.

    The image-build tests were repeated 5 times under load.

  • Full cargo test from a short-path checkout of this diff before the rebase: 21 binaries, 1300 passed, 1 failed, 109 ignored. The one failure was this PR's own cancellation test reading the child PID before it was written. It is fixed in the second commit: the PID is now published atomically. The rebase brought in only the two scanner commits from feat(native): pin a BuildKit client and scan Dockerfile image references #583. In an earlier run from a /tmp symlinked path, resources::tests::explicit_root_binding_requires_exact_executable also failed. That is a symlinked-path artifact of where the run started; it passes from the canonical path.

  • bun run --cwd packages/cli typecheck: pass. bun run docs:prepare && bun run docs:check: pass. tests/prerelease-plan.test.ts and tests/native-candidate-authored-artifact.test.ts, which read the native candidate guide: 64 pass. This PR changes no TypeScript.

  • I did not run the full Bun suite locally because the host is loaded; hosted CI runs it.

  • gitleaks (staged and origin/next..HEAD): no leaks.

Remaining gaps and host evidence still needed

None of these were faked; each needs a prepared private candidate home on Apple Silicon:

  • H1. The host buildx (docker driver) must work over the smolvm-forwarded engine socket, including BuildKit's /session and /grpc upgrade. If it does not, the client moves into the guest, as the feat(native): pin a BuildKit client and scan Dockerfile image references #583 notes describe.

  • H2. BuildKit RUN with network must work on the new docker0 under --internet and --allow-host. The negative controls must hold: a build RUN cannot reach a running project container's IP, the guest relays, or 100.96.0.1. DNS must work through the guest resolvers. Existing graphs must be unaffected after a pool restart.

  • H3. Named build contexts must match the scanner's short names for FROM, ARG-substituted FROM and COPY --from=<image>.

  • H5 (required for the stack): source policy on a real build. With the pinned buildx and engine:

    • a pinned build succeeds under the policy;
    • a base whose ONBUILD trigger pulls an unpinned image, or runs ADD https://…, fails with "denied by policy";
    • named-context image-config resolution uses the same docker-image://docker.io/<repo>@<digest> identifier the policy allows.

    Not run here.

  • H6. buildx bake specifics on the pinned client (0.34.1). --allow fs.read=<context> and --allow fs.read=<private Dockerfile dir> must satisfy bake's filesystem entitlements. The metadata file must be keyed by the target ({"build": {"containerimage.config.digest": …}}). HCL escaping of definition strings was checked with bake --print on buildx 0.37.2 only.

  • H4. The built image Id must equal the metadata's containerimage.config.digest with the overlay2 store, and the labels must be present.

  • Behaviors on a real VM. /v1.53/info must expose a stable ID. --build-arg NAME must read the value from the environment. A cold and a warm build of a small public-image fixture, cancellation mid-RUN, and a VM stop mid-build (then indeterminate, then success) must all behave as described.

  • Base images are anonymous Docker Hub only. Any-registry base images (Nov 5 decision) depend on the registry generalization in flight elsewhere. resolve_build_base and the scanner's reference check are the seams to widen, and private registries would also need the client's DOCKER_CONFIG to read the host's credential helpers read-only. Not done here.

  • amd64 and Rosetta. platform: linux/amd64 builds (Rosetta, opt-in) are not supported; builds are linux/arm64 only.

  • Orphaned client. If hack-native itself is killed (SIGKILL), the client can outlive it. The client inherits the build fence and the per-key lock, so both stay held until it exits. runtime down keeps refusing and the same key refuses native_build_busy; a test covers this with a SIGKILLed coordinator process. The receipt stays building, and the next run after the client exits recovers.

Refs HACK-1270

🤖 Generated with Claude Code


Get evidence
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.

roodboi and others added 2 commits November 5, 2026 17:56
Adds the runtime operation that builds one Compose-style image inside the
native VM's engine with the pinned host BuildKit client, and the isolated
build network it needs. Native up does not call it yet.

- Egress pools give dockerd a fixed default bridge (10.213.255.0/24,
  icc=false) instead of --bridge=none; isolated pools keep none.
  guest-build-network.sh isolates it after the engine starts (drops build
  traffic to the guest, the host gateway and link-local metadata; allows DNS
  to the guest resolvers) and writes a boot-bound readiness marker. The
  engine client now refuses any container create that does not name an
  owned network, none or a container namespace, and any connect to the
  default bridge, so only BuildKit RUN steps can use it.
- runtime build-image --request <private 0600 file> --json: paths resolve
  inside the project without symlinks; the Dockerfile is read (never run)
  and scanned; every base image resolves to a digest-pinned manifest and is
  handed to BuildKit as a named build context; base images with stored
  ONBUILD triggers refuse. The build key covers namespace (project root +
  branch), paths, Dockerfile and .dockerignore bytes, target, salted
  argument hashes and builder/engine versions; context contents are not
  hashed (Compose parity).
- Argument values reach the client only through its environment
  (--build-arg NAME); names that would change the client's own environment
  refuse. Labels carry a digest of the pool incarnation, never the token.
- The provider lease is held only for admission and adoption, never while
  BuildKit runs. A shared build fence makes runtime down refuse instead of
  stopping the engine under a build. A per-key lock serializes builds; a
  building receipt found under it is a dead attempt and is recorded
  indeterminate before building again.
- Adoption requires the same incarnation, boot and engine as admission, the
  metadata's config digest, linux/arm64, exactly the owned labels, no other
  reserved ownership labels and no image VOLUMEs. Reuse requires a verified
  receipt from the same checkout, incarnation and engine whose image still
  inspects that way.
- SIGINT/SIGTERM/SIGHUP and the time budget (default 30 min, max 2 h) stop
  the client's whole process group (SIGTERM, then SIGKILL after 10 s).

Refs HACK-1270

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fake client now publishes its child PID atomically, cancellation waits
for that published PID, and the timeout case checks the child only when the
client forked it within the one-second budget.

Refs HACK-1270

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@linear-code

linear-code Bot commented Nov 5, 2026

Copy link
Copy Markdown

HACK-1270

@roodboi

roodboi commented Nov 5, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 5, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-06T17:37:40.534638Z 460ddff Manual request
🔒 Security Review ✅ Completed 2026-11-06T17:42:42.977341Z 460ddff Manual request

Security findings

Advisory findings (1)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5eb57778f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: e5eb57778f

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

roodboi and others added 3 commits November 5, 2026 22:56
…builds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The default-bridge guard refuses a container create that names no network
before admission is consulted, so the dispatch-level admission controls
(built only with environment-launcher) now send NetworkMode none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The build client now reads a private copy of the Dockerfile bytes that were
scanned and hashed, with the effective ignore rules written beside it, instead
of reopening the project paths while bases resolve. Its stderr is piped through
a filter that redacts every build-argument value, strips terminal control
characters, cuts long lines and bounds total output before it reaches the
caller, and stops writing before the operation returns.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0c58c89b5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: a0c58c89b5

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…esolution

Closing the output forwarder no longer waits on the sink lock: it marks the
sink closed, waits at most one second for a write in progress, and returns, so
a caller stream that stops draining cannot hold the operation or its build
fence. The time budget now starts before base-image resolution: each
resolution gets the operation deadline, runs on its own thread so Ctrl-C,
SIGTERM and the deadline interrupt it, and the client gets only what remains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1ea5f339e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 1ea5f339e4

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…den redaction

The build client now runs buildx bake with a private mode-0600 definition
file that carries the argument values, the context, the pinned base contexts,
labels and tag. It is removed after the run, so values never enter the
client's command line or environment, which stays a fixed allowlist. Every
string in the definition is escaped so bake never interpolates project text.
Argument names must be plain identifiers; only names BuildKit or buildx
interpret themselves (BUILDKIT_*, BUILDX_*, SOURCE_DATE_EPOCH) refuse.

The output filter also redacts each value's Go-quoted, JSON-escaped and bake
encoded forms. On a cut line it trims a trailing partial value from the
sanitized bytes that are forwarded, not the raw ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f26acd9d2e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Build arguments follow Docker: an image records their values in its history,
and resolved base references an argument selects are reported in receipts and
results. The guide now says never to pass secrets as build arguments and
points to BuildKit build secrets, which native builds do not support yet.
Output redaction stays as defense in depth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: f26acd9d2e

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

roodboi and others added 2 commits November 5, 2026 23:53
…builds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bake client now receives EXPERIMENTAL_BUILDKIT_SOURCE_POLICY naming a
private mode-0600 policy file written beside the definition and removed with
it on every path. The policy comes from build_policy::document for exactly
the pinned base references the definition names as contexts. It allows those
images and denies every other image and network source, including sources a
base image's stored ONBUILD triggers add.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

The receipt read bound is now 64 KiB, which covers a receipt with the most
base pins the scanner accepts at their longest, so a receipt a build writes
can always be read again. A new end-to-end test also shows that remote ADD
sources (URLs, git refs, variables, ONBUILD forms) refuse at prepare, before
any build effect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8c39b609e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

… lock

state::write publishes a receipt through <key>.pending and a rename. A process
that died between the two left that file, and every later write for the key
failed with AlreadyExists. Under the per-key lock, which proves no writer is
live, the build now discards the leftover before writing; the committed
receipt, if any, is unaffected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3b42fdd93c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 5a40b44f45

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 5a40b44f45

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…lled

The build client now inherits the build fence and the per-key lock. A flock
belongs to the open file description, so if hack-native is SIGKILLed while the
client runs, both stay held until the client and anything it left running
exit. runtime down keeps refusing, and a later attempt of the same key
refuses with native_build_busy instead of sharing the surviving client's
attempt files. An orderly return still releases both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🎉

Reviewed commit: 460ddff233

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 460ddff233

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@roodboi

roodboi commented Nov 6, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: 460ddff233

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review · Automatically triggered

Security review completed. No security issues were found in this pull request.

Reviewed commit: 460ddff233

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

roodboi added a commit that referenced this pull request Nov 7, 2026
…overage

The native-build-retention coverage row and model README said the fence is
released with its holder when the coordinator dies. Since #606 the build
client inherits the fence and per-key lock, so both stay held until an
orphaned client exits; the model still does not cover that case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
roodboi added a commit that referenced this pull request Nov 7, 2026
, #612, #628, #629, #630) (#639)

## What this is

Merge train batch 6 into `next`: the native image builds stack and its
fixes, as one batch. Builds stay off by default: they run only with
`HACK_NATIVE_BUILDS=1`, which native `up`, restart and doctor all check.
Each component's reviewed head is merged with `git merge --no-ff`, in
stack order. Integration conflicts are resolved in the batch's merge
commits, described below. Hosted CI on this branch is the full-suite
gate for the combined result.

| PR | Title | Linear | Head included | Review |
| --- | --- | --- | --- | --- |
| #583 | feat(native): pin a BuildKit client and scan Dockerfile image
references | HACK-1270 | `f21d5a04` | Codex code + security: no
findings; 0 open threads |
| #606 | feat(native): build Compose images in the guest engine (runtime
build-image) | HACK-1270 | `460ddff2` | Codex code + security: no
findings; 0 open threads |
| #607 | feat(native): admit build storage, budget build cache and
reclaim superseded build images | HACK-1270 | `9f33a1d0` | Codex code +
security: no findings; 0 open threads |
| #612 | feat(native): build Compose build services in native up behind
HACK_NATIVE_BUILDS | HACK-1270 | `7f42e328` | Codex clean at `8ec7460e`;
`7f42e328` adds #628's squash (re-review requested) |
| #628 | fix(native): admit native build fields and name services in
build refusals | HACK-1925 | `f07ea583` | Codex code + security clean;
already squashed into #612's branch |
| #629 | fix(native): keep the VM responsive during image builds and
stop a silent guest | HACK-1924 | `a0e3e797` | Codex code + security: no
findings; 0 open threads |
| #630 | fix(native): restore a sourceless graph after its project files
were touched | HACK-1914 | `a93db842` | Codex clean at `009989c7`;
`a93db842` only merges #629 into it (re-review requested) |

## Security properties in the merged code

- **Build arguments:** they reach the BuildKit client only through a
private 0600 bake definition, never its environment. The client
environment is cleared to an allowlist. Names must be plain identifiers,
and BuildKit-interpreted names such as `BUILDKIT_*`, `BUILDX_*` and
`SOURCE_DATE_EPOCH` are refused. A build argument equal to a managed
secret value is refused without echoing the value. Build arguments are
not secrets, matching Docker.
- **Source policy:** a source policy from `build_policy::document`, set
through `EXPERIMENTAL_BUILDKIT_SOURCE_POLICY` in a private file, applies
to every build. It allows only the scanner's digest-pinned base images
and denies other images, `oci-layout`, `http`, `https`, `git` and blob
sources. Pinned bases whose config has `OnBuild` triggers are refused.
- **Dockerfile scanner:**
- remote `ADD` sources are refused, including the JSON form, multiple
sources and variables;
- JSON-form arguments are read as BuildKit reads them, and a
lenient-decode differential fails closed;
- heredocs are recognized as BuildKit's lexer does, including whitespace
after `<<`;
  - a stage named `scratch` is refused;
  - `ARG` names must be plain identifiers;
  - platform arguments must resolve to `linux/arm64`;
  - expanded values are size-bounded.
- **Build output:** forwarded output is sanitized and then redacted,
including Go-quoted and JSON-escaped forms and values split across a
line cut. It is bounded by size.

## Integration changes

- **#606 with #605 / #609:** `resolve_build_base` runs on #605's guarded
registry client, so the resolver and address policy, origin-bound
authentication and the realm rules apply. It uses #609's native platform
selection, so builds stay Docker Hub and linux/arm64 only. The older
Docker Hub-only fetch helpers are dropped.
- **#606 / #607 with #605 / #609 / #615:** the `build-image` and
`reclaim-build-images` commands sit beside the image-policy and
emulation options. The build error helpers take `impl
Into<ErrorMessage>`.
- **#607:** the native-build-retention TLA+ contract is registered
beside the restart-policy, pool-emulation and multi-root contracts. Its
model-coverage row now says an orphaned build client keeps the fence and
per-key lock until it exits (`9f33a1d0`).
- **#612 with batch 5:**
- The builds-enabled classification flows through `classifyAdapted`, the
Compose-backend refusal and doctor.
- With the gate off, native `up` refuses a build with a message that
names `HACK_NATIVE_BUILDS`, so doctor's basic-build advice names it too;
one test expectation changed to match.
- Startup and restart keep the dependency-file selection, host aliases,
emulation policy and the inactive-profile skip alongside the build
services.
- **New (#609 x #612):** a build service whose adaptation selects amd64
emulation is refused before any effect, because native builds are
linux/arm64 only. `prefer-native` passes, and a test covers it.
- **#629:** the socket cleanup after a clean stop is kept inside #629's
new guest stop. #629's two flaky relay tests are fixed on its branch
(`a0e3e797`): each test gets its own fixture root, and EINVAL from
`set_read_timeout` after the peer closes is accepted.
- **`next` merge:** the last merge brings in Kit's CI shard and SIGKILL
harness changes (#624, #627). The commit hook formatted
`tests/native-compose-generation.test.ts` with Biome, a whitespace-only
change to that Kit file.

## Validation on this branch (`6337e0ed`)

- Clean CLI typecheck: pass. Biome on the 20 changed files: no errors.
- `RUSTUP_TOOLCHAIN=1.97.1 bun run check:local`: pass. All-features
clippy `-D warnings` (Zig 0.15.2): pass.
- Cargo `--lib`: 1346 pass. `--features native-config-plan`
`provider::graph`: 673 pass. Contracts: `project_contract` 66,
`compose_fields_contract` 10, `interpolation_budget_contract` 1,
`local_contract` 14.
- `bun run test:models` (TLA+ 1.7.4): exit 0, 172 controls verified.
- Focused bun tests, bounded runner, the changed test files: 242 pass, 0
fail.
- `docs:prepare` + `docs:check`, `privacy:check`, gitleaks over
`origin/next..HEAD`: pass.

Host evidence: 11 of 11 builds passed on an M5 host with the relay
(#629). Still open: the source-policy host check (a pinned build
succeeds, and a base trigger that pulls an unpinned image or fetches
https fails with "denied by policy") and the bake `--allow fs.read` /
metadata shape on the pinned buildx 0.34.1. Both are listed in #606.

After this merges, each component PR is closed with a link to the merge
commit.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- codesmith:footer -->
---
<a
href="https://backend.blacksmith.sh/track/run-proof?expires=1796625833&installation_model_id=17053&pr_number=639&ref=codesmith_pr_footer&repository=hack-dance%2Fhack&return_to=https%3A%2F%2Fgithub.com%2Fhack-dance%2Fhack%2Fpull%2F639&variant=see_it_in_action&signature=c3ba624f6f34813ba26773c5b4c3caf941bd59515e8c3016c92df8f0e2a6048f"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-see-it-in-action-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://pr-comments-assets.blacksmith.sh/codesmith/proof-see-it-in-action-light.svg"><img
alt="See it in action"
src="https://pr-comments-assets.blacksmith.sh/codesmith/proof-see-it-in-action-dark-3x.png"
width="132" height="28"></picture></a>
<sup><a
href="https://app.blacksmith.sh/hack-dance/codesmith/hack/pr/639?autoLogin=true&ref=codesmith_pr_footer">Open
in [code]smith</a> · Need help on this PR? Tag
<code>@codesmith-bot</code> with what you need.</sup>

<!-- codesmith:autofix:disabled -->
<!-- /codesmith:footer -->

---------

Co-authored-by: Dimitri Kennedy <344842+roodboi@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@roodboi

roodboi commented Nov 7, 2026

Copy link
Copy Markdown
Contributor Author

Merged into next through the train batch PR #639 (squash commit e59fcc2). This PR's reviewed head 460ddff233bd5ea7f4f94635cecb8bc746620450 was included. #639 describes the integration resolutions and the batch security fixes: Go-style image config key reading, non-ASCII key refusal, the build base size bound (unpacked size tracked in HACK-1945), build-base descriptor validation and case-insensitive stage names. Closing as merged by batch; the branch is kept.

🤖 Addressed by Claude Code

@roodboi roodboi closed this Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant