Repository navigation
feat(native): merge train batch 6, native image builds (#583, #606, #607, #612, #628, #629, #630) - #639
Conversation
A 14-service retaining cleanup sent every container stop at once. Each stop holds one engine connection for up to its grace period, and the batch failed at the connect stage while the provider host held most of its descriptor limit, leaving the graph in cleanup-intent with containers running. The stop batch now runs through at most four workers that share one queue and are all joined before it returns. A connect that fails or times out sent no request, so it is retried with capped backoff inside the batch deadline and a 30-second window from that stop's first connect failure. Once one stop exhausts the window, later connect failures return at once. Response, timeout and transport failures are still never retried. A stop whose worker panicked or never ran is reported with the worker stage. The existing stop budget already covers fully serialized grace periods, so bounded dispatch fits it. A partial failure keeps the reached stops, and the next cleanup stops only the unreached instances. Refs HACK-1302 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native Compose review no longer aborts on an unsupported port. Each port or expose entry outside the modeled subset becomes an invalid_port error diagnostic at services.<name>.ports[<i>] (or .expose[<i>]), collected with the rest of the review; the entry is omitted from the plan so authored text is never echoed, and enrollment stays incompatible. Import preview reviews each environment entry on its own: only an interpolated or ambiguous entry is refused at its pointer, and literal siblings stay mapped. Doctor's compose backend check names the isolatedNetworks adaptation for an external network, warns (with nativeUpRefusals: ["basic_build"]) for a basic build that native image-project up refuses, and the config validate/plan input-family refusal now names the next step. Refs HACK-1907 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`hack doctor --migrate-env-config` deleted the committed hack.env.json and edited the tracked Compose file in non-interactive runs, because every step accepted its default. A run that can't prompt now refuses with E_INTERACTIVE_REQUIRED before any fix step writes, naming each git-tracked file it would delete or edit, unless --accept-tracked-env-changes is passed. Interactive runs list those files and ask per step; tracked legacy files get their own removal prompt. Native doctor now runs the same project-file migration instead of pointing to v4. The implicit migration (env exec, sessions, lifecycle env) reports each moved key value-free and removes a fully delivered, untracked legacy dotenv file that Compose no longer names, so no plaintext copy stays behind. Refusals no longer claim .hack/.env files exist. Refs HACK-1904 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bels and nested defaults
Native Compose review and execution now accept four Compose forms that
previously refused the whole project:
- depends_on.<svc>.required: false. An optional edge to a service the
selected profiles disable is skipped with optional_dependency_inactive,
as in Compose. An active optional target is enforced like a required one
(fail closed) and reported as optional_dependency_enforced. restart: true
and missing targets keep their refusals.
- stdin_open and tty. stdin_open keeps an open, unattached stdin
(OpenStdin true, AttachStdin/StdinOnce/Tty false). tty is accepted, but
no pseudo-terminal is allocated (terminal_not_allocated warning), so logs
stay multiplexed.
- caddy hostnames with caddy.tls internal and no caddy.reverse_proxy. The
Compose router proxies nothing for this form, so native records it as
inert: no route, hostname claim or relay, an inert_route_declaration
warning, and graph admission accepts exactly those two labels. Runtime
host metadata still advertises the hostnames, as on Compose, but the TS
route map and additionalHostnames adaptation exclude inert services.
Every other partial or extra caddy label set is still refused.
- Nested interpolation defaults such as ${A:-${B:-}}. Inner :- and :+
expressions resolve recursively, up to four levels, within the shared
1 MiB budget. Unselected words are validated without requiring inputs.
?, unbraced $NAME and $(...) inside a word stay refused. A reference to
a managed value of the same service now refuses with
execution_environment_owner instead of resolving to a default or empty.
New plan fields are omitted when unused, so existing plan IDs do not
change. Older bundles refuse plans that use them (deny_unknown_fields).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native down always retained data, and a Compose edit that changes graph resources left retained data that restore cannot adopt, with no frontend way to start fresh. `hack down --remove-data` (Compose parity: `down --volumes`) now discards it explicitly: - Native Compose-file projects only. Authored native-config projects and the Compose backend refuse the option as a usage error. - The project must be stopped; a running graph refuses and names `hack down` first. Removal runs no lifecycle hooks and never stops compute itself. - It asks once after a read-only check of the exact saved run; `--yes` skips the question and `--json` requires `--yes` (E_INTERACTIVE_REQUIRED before any runtime request otherwise). - Under the restart lock (a pending restart refuses), after the original frontend finalization, it requests `graph cleanup --remove-data`, which deletes only receipt-owned volumes behind a durable cleanup intent and releases shared dependency caches without deleting them. - The run mapping is retired only after the receipt is `removed`, every owned volume is recorded and observed absent and caches are only released. A replaced mapping is never removed. - Any failure keeps the mapping; a rerun resumes a durable cleanup intent (compute absent) or an already removed receipt without replaying deletion. Refusals carry at most a closed native code. Restore refusals that say retained data was not adopted now name the two remedies: revert the change, or run `hack down --remove-data`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native image-project up and restart now resolve every HACK_NATIVE_*
selection from three places, once per command:
- the committed .hack/native/adaptation.json, plus optional
adaptation.<env>.json, holding the existing closed adaptation schema;
- a per-machine settings file outside every project share,
${XDG_CONFIG_HOME:-$HOME/.config}/hack/native/<project>/settings.json,
for the share grant, AWS profile, host dependency files, HTTPS,
prepared base and approved hosts (closed v1 schema, 0700 directory,
0600 owner-only regular file, refused inside the project root);
- the existing variables, which still win per setting, whole-value.
Per-env files are picked by the run's effective env inside native start
and restart preflight, so fresh, retained and restarted runs select the
same files. Without --env the Compose-backend pre-check counts a network
as isolated only when every candidate adaptation isolates it.
Native doctor adds a value-free "native settings" check: the origin of
each setting (env, machine, project, unset) and closed warnings for
configuration native does not apply (internal DNS/TLS, internal
extra_hosts, routes without probes, ambient COMPOSE_PROFILES, an AWS
region without a profile, unread .hack/native files, missing share
grant).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ations
The private graph environment envelope gains version 2 with a `files` map:
per service, one to four `{name, target, mode: "0400", content_b64}`
members. Version 1 with files, version 2 without them, and malformed,
duplicate or unknown member fields refuse, so an older runtime refuses
file delivery instead of dropping it.
Members are decoded into zeroized memory, bounded to 16 KiB each and to
the existing 32 KiB per-service payload together with values, and staged
in the same per-container tmpfs allocation as values.json: a root-only
0700 `files` directory with each member owned by the container's numeric
UID at mode 0400. They are bound read-only at their declared absolute
targets. Targets under /proc, /sys, /dev, /run and /var/run, nested or
duplicate targets, and any compiled mount or tmpfs that equals, contains
or sits under a target refuse before effects. Members live as long as the
allocation and are retired with it by the existing cleanup.
A service may receive files without values. It then keeps its own process
(no launcher wrap) and needs only a numeric image or Compose user. Live
owner restore and normalized serve-restore deliver fresh members; graph
exec and one-off runs refuse envelopes carrying files. Errors and receipts
stay value-free.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A host dependency selection may now be version 2, where one entry names services: [...] instead of a single service. Each listed service expands to its own version 1 entry with the same binding, ports, aliases and host authority, in order, and the expanded entries are then validated exactly as version 1: the 128-binding limit counts expanded entries, duplicates refuse, and an entry sets exactly one of service and services. Version 1 files are unchanged and both forms give the same dependency plan. Startup, restart preflight and the stopped-restart intent reader all expand before validation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Compose-file native graphs refused every image that declares a VOLUME, which blocked stock database images even when a named volume covered the path. The graph now admits an image VOLUME when exactly one explicit, whole (no subpath) named volume owned by the same receipt is mounted at each declared target. Missing, duplicate, bind, subpath, tmpfs and dependency-cache covers refuse with graph_image_volume_uncovered before any container is created; foreign OS, architecture or image identity still refuse with graph_image. Start, restart, restore and one-off share the predicate through verify_images. It reads only reviewed config, adds no capability and does not change plan or receipt identities. The per-graph volume budget rises from 8 to 16 (dependency caches still count, since each volume costs per-graph inspection and cleanup work; caches keep their separate 64-volume owner limit). Native doctor adds a warning-only "native data services" check: native_data_owner_handoff_unqualified for a service that runs a published image's own entrypoint (no build, no entrypoint override) with a named data volume and no explicit user:, and native_retained_data_mutable_image for one on a latest or untagged image without a digest. Reports carry codes and service names only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Groundwork for native image builds; no command builds images yet and native up still refuses build services. - provider-pins.json pins buildx 0.34.1 (darwin-arm64, Developer ID signed) by SHA-256. `runtime prepare-builder --archive <file>` checks the pin before creating any state, copies the file into private candidate state, rehashes the owned copy, verifies its code signature and records a tree digest. An existing tree is re-verified and reused; an interrupted preparation is retained and refused. `runtime probe` reports `builder_prepared` and refuses a changed tree. - provider/build_scan.rs is a pure scanner that reports the images a build of the selected target can pull (FROM, COPY --from, RUN --mount from=, ONBUILD triggers of internal stages). It follows the Dockerfile frontend embedded in the pinned engine: directives, escape token, continuations, heredoc bodies, global ARG substitution with literal build-argument overrides, stage names and indexes, target reachability. It refuses what it cannot resolve exactly (syntax directives, variable modifiers, quotes, uncertain platform arguments, non-arm64 platforms, stage/image name collisions, unknown instructions and flags, non-Docker-Hub references, size and count bounds) and never echoes Dockerfile text or values. Refs HACK-1270 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ependencies Native up and restart now declare the effective internal.extra_hosts and checkout-local extra-host aliases on every service, as the Compose override does, and require a reviewed host dependency binding for every profile-active (service, alias) pair. An alias without a binding, a non-host-gateway target, an inexact name or a conflicting service mapping refuses value-free instead of being silently ignored. Lifecycle hooks that change the aliases refuse after hook cleanup. Host dependency selections gain a declarative version 2 form: one entry per binding applies to every active service that declares its aliases (or to listed services) and expands to version 1 bindings before discovery, review and the existing limits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ctrl-C or SIGTERM on a ready native Compose-import graph whose up.after completed now runs down.before, graph cleanup, lifecycle-process retirement and down.after, matching hack down on the Compose backend and the authored native path. A second interrupt cancels a running down hook and forces cleanup. A failed down.before still stops the graph and is reported as E_LIFECYCLE_FAILED. Ctrl-C during up.after runs no down hook. A private value-free journal beside the run mapping (run, writer generation, phase; compare-and-swap under a lock) records intent before each hook and known completion after it exits. hack down skips completed phases, runs owed ones once after the frontend's finalization, and refuses unknown completion without replay; the next up refuses it too. Projects without down hooks keep the attached graph owner and write no journal. Adds the native-foreground-down-hooks TLA+ model (1,246 states) with a journal-guard negative control, unknown-generation and owe-withdrawal controls, and stop and crash-recovery reachability controls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The privacy check flags Linux home paths; the fixture only needs a nested absolute target with a space. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HACK_NATIVE_GCLOUD_ADC=file selects a gcloud Application Default
Credentials adapter on the native Compose foreground. It runs after the
login hooks, beside the AWS adapter. Services that bind
~/.config/gcloud/application_default_credentials.json read-only (short or
long syntax; ~, ${HOME}, $HOME or the absolute home) lose that mount from
public Compose. They receive the same bytes as a private mode-0400 file
member at the authored target through the version 2 private envelope.
The adapter reads only the fixed home ADC path: no symlink, owned by the
user, not group- or world-writable, 1 to 16 KiB of UTF-8 JSON, unchanged
during the read. It admits authorized_user and impersonated user
credentials and refuses service_account keys, external accounts and any
credential_source. GOOGLE_APPLICATION_CREDENTIALS must equal the target;
CLOUDSDK_CONFIG, metadata-host and token-file selectors refuse. One
`gcloud auth application-default print-access-token` proves the login
(output discarded); an ADC change during startup refuses. The run mapping
records only gcloud {mode: "file"}. Restart and retained restore keep it,
and exec rechecks selectors without delivering anything.
The AWS and gcloud adapters now share one private CLI helper: clean
environment, stdout only with a 64 KiB cap, own process group, SIGKILL on
a 30 s timeout, zero-filled buffers, no retry.
AWS credentials stay a startup snapshot. The foreground now prints one
value-free stderr line ten minutes before expiry and one at expiry, naming
only the service count and the local time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… adopt The native Compose review refused Hack's own `hack.service.one-shot: "true"` label as an ownership label, while `hack config import --dry-run` accepts it as a native job. The review now consumes that exact key and literal value as a completion role: the plan marks the service `one_shot`, the frontend gives it a `completed` readiness goal, and the graph driver admits the marker only with completion readiness and adds no container label. Ownership namespaces and every other `hack.*` key stay refused; an invalid value, an unconditional restart policy or routing labels on a one-shot refuse with their own codes. `hack config adopt`, including `--dry-run`, still requires the Compose backend because it reads that backend's existing containers and volumes. Its refusal on another backend now names the rerun, for example `HACK_RUNTIME_BACKEND=compose hack config adopt --dry-run`. Refs HACK-1906 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
prepareNativeProjectServices refused every service labeled hack.service.one-shot before the native review could classify it, so the reviewed completion goal was unreachable through hack up. The frontend label allowlist now admits only the exact key with the literal value "true" (map form) or the exact entry hack.service.one-shot=true (list form), matching the native review. Other values, bare keys, sibling keys and ownership labels still refuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Nested default and alternative words were built in full before the top-level resolve charged them, so repeated or nested references could allocate far past the 1 MiB budget (multiplying per nesting level) before execution_input_budget refused. Each literal, escape and nested value is now checked against the remaining budget before it is appended; the budget is still charged once, when the finished value is appended at top level. Tests: a peak-heap contract proves single-level and nested repetition refuse without building past the budget, and an exact-fit case proves nested output is charged exactly once. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A one-off job keeps the parent service's mounts, but its image check saw only the new job container, so an image VOLUME covered by the parent's owned named volume was always refused with graph_image_volume_uncovered. Verify against the parent receipt's volume resources plus the job container; parent containers are still not re-inspected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native Compose review now parses port entries that use one interpolation
per component with a literal default, such as
"${BIND:-127.0.0.1}:${PORT:-8108}:8108", instead of reporting them as
invalid. Review stays value-free: native publication uses the literal
default, warns with interpolated_port_default at services.<s>.ports[<i>],
and records only the variable names (published_reference,
host_ip_reference; omitted when unused, so literal plans keep their
identity).
A reference without a literal default, a nested default, an interpolated
target or protocol, or a reference spanning components is a located
unresolved_port_binding error, so the rest of the review still runs and a
profile-gated service no longer blocks the default plan.
Short-form splitting follows Docker's port-spec rule after interpolation,
so bracketed and bare IPv6 hosts parse. ::1 is kept, :: is narrowed to ::1,
and a specific non-loopback host stays an unsupported_host_binding error,
now located at its entry with a clearer message.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A contract key with `services: [...]` migrates only to a service scope,
and service values never reach Compose interpolation; Hack passes Compose
only global values under their own names. Compose reads `.hack/.env` for
interpolation on its own, so deleting it after the migration dropped a
`${KEY}` value that launches still needed.
The migration now keeps `.hack/.env` when a service-only key in it is
named in the Compose file or another env_file it lists (or is a COMPOSE_
setting), and says why in both the implicit and doctor reports, without
values. Compose input this check can't fully read keeps the file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`hack down --remove-data` took the shared native operation lock as a bare directory, so a removal killed while waiting for finalization or cleanup left a lock every rerun refused, and SIGINT/SIGTERM bypassed its release. Removal now links a complete owner record (operation, pid, nonce) into the same lock directory. A rerun reclaims the lock only when that recorded owner is gone (ESRCH), under a separate recovery guard that re-reads the owner, following the projects-registry lock protocol. Live, reused or inaccessible owners, owner-less locks (restart, filesystem recovery) and a busy guard still refuse. The command turns SIGINT/SIGTERM into an abort signal that kills and awaits the owned runtime request and ends the finalization wait, so the lock is released before the command fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
x-hack-disposable-cache names regenerable directories inside a volume, and an empty list is valid; everything outside those directories is retained data. Doctor no longer treats such a volume as a cache, so a database volume with a declared cache directory keeps both advisories. Only hack.dependencies.cache-volume volumes are excluded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An optional dependency on a profile-disabled service is dropped from the execution graph, but review still applied its condition requirements: a service_healthy edge without a target healthcheck raised missing_healthcheck, and a service_completed_successfully edge to an always-restarting target raised nonterminating_completion_dependency. Either made the whole plan enrollment-incompatible although the target is never started or awaited. Review now stops after the optional_dependency_inactive warning for such edges; active edges and required edges to disabled targets are validated as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Native Compose-file graph containers ran with every capability dropped, so stock root entrypoints that chown their data directory and then switch users (database images) failed on first start. Compose-file services now get exactly moby's default capability set for the pinned engine (docker-29.5.2), lowered explicitly as CapDrop ALL plus the effective CapAdd set, with no-new-privileges kept. cap_add and cap_drop follow Docker's normalization and precedence. cap_add accepts the default names plus NET_ADMIN, NET_BROADCAST, SYS_NICE, IPC_LOCK and IPC_OWNER; review refuses ALL and capabilities that act on the shared VM kernel or on Hack's in-container helpers. privileged stays refused. Services without capability fields keep their existing plan identity. Authored native services, the persistent-data process policy and Hack's helper containers keep every capability dropped. The guide documents the security model: the development VM is the isolation boundary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A global `ARG BUILDPLATFORM=...` or `ARG TARGETPLATFORM=...` default replaces the frontend's platform value, and the frontend expands `--platform` against the global arguments. The scanner accepted `--platform=$BUILDPLATFORM` by spelling alone, so such a stage could select another platform. The spelling is now accepted only while no global default overrides the argument; an overridden one must expand to linux/arm64 like any other value. A bare global ARG still keeps the frontend's value. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-slim The native file and storage helper image was the last place next still shipped Bun 1.4.2. Catalog the registry-derived oven/bun:1.4.4-slim index, platform manifests, configs and Bun/libc byte pins for linux/arm64 and linux/amd64, and use the 1.4.4-slim tag for new VM file preparation. Selection refuses any catalog entry whose Bun version is not current. Saved storage records and VM file manifests written under the earlier 1.4.2 helper still parse, so saved stop and recovery stay available. That artifact is no longer cataloged: storage enrolled under it refuses verification as artifact drift and is never relabeled or re-seeded. Refs HACK-1758 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ble maps The native post-hook alias check passed the configuration snapshot read before `up.before`, so a hook that edited `internal.extra_hosts` in hack.config.json compared stale static aliases and startup proceeded. The check now re-reads and validates project configuration itself (`requireNativeProjectInternalExtraHostsUnchanged` takes no snapshot) and refuses after hook cleanup when the effective aliases changed. `readLocalExtraHosts` collapsed every read error into an empty map, so an existing but unreadable checkout-local `.internal/extra-hosts.json` silently dropped configured aliases. It now uses `readOptionalTextFile`: an absent map is empty, an unreadable one refuses with `E_CONFIG_INVALID`. This also covers Compose `up` and the `hack internal extra-hosts` commands, which no longer overwrite a map they could not read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A down hook run by a foreground Ctrl-C now leads its own process group with stdin closed. Canceling it (a second interrupt) sends SIGTERM to the whole group, then SIGKILL once the shell exits or after a two-second grace, and output draining after cancellation is bounded by the same grace. A hook such as `sleep 600; echo finished` no longer leaves its child running with the output pipes open, which blocked the forced stop and the journal settlement. Hooks run by `hack down` keep the caller's process group and terminal stdin. The frontend's stop listeners now stay installed until down.after and the finalization acknowledgement settle (disposed in an enclosing finally), so a further interrupt cancels a running down.after through the force signal instead of terminating the frontend with an after-started journal record. Real-process regressions cover shell plus sleep child cancellation, SIGTERM-ignoring descendants, an escaped process holding the pipes, the non-owned path, and a second Ctrl-C during a real down.after through startNativeProject. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Engine copy-up seeds an empty named volume from the image's VOLUME directory before a read-only mount applies, so a read-only cover still lets the image write into that volume, which another service may share. Image VOLUME targets now need a writable owned named-volume cover; a read-only or malformed ReadOnly cover refuses with graph_image_volume_uncovered for services and one-off jobs alike. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Go's encoding/json folds some non-ASCII runes onto ASCII field names (for example U+0131 dotless i, U+017F long s and U+212A Kelvin sign), with rules that have changed between Go versions. So "OnBuıld", "archıtecture" or "diff_ıds" could name a field for the engine while a check here missed it. Every object Hack reads from image metadata now refuses, fail closed and value-free, when any of its member names contains a non-ASCII character: - the image config top level, and its config and container_config scopes; - platform fields; - the archive config and its rootfs; - manifest, index, descriptor and platform objects. ASCII case-fold matching and the duplicate and ambiguity refusals are kept. The special U+017F and U+212A folds are removed, since those keys now refuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
f88495e closes the parser differential reported against Every image-metadata object Hack reads now refuses, fail closed and value-free, when any of its member names contains a non-ASCII character:
ASCII case-fold matching and the duplicate and ambiguity refusals are unchanged. The special U+017F and U+212A folds are removed, because those keys now refuse. Tests:
Checks: @codex review 🤖 Addressed by Claude Code |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Here are some automated security review suggestions for this pull request.
Reviewed commit: f88495e0f6
ℹ️ 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.
resolve_build_base fetched only the config and handed BuildKit a pinned docker-image reference, so acquisition's archive budget never covered base layers. The selected manifest's config and layer sizes are now summed (with overflow checked) and must fit the per-image compressed archive budget (ImagePolicy archive_limit, the budget ensure-image applies). The manifest must also have 1 to MAX_LAYERS layers. Either check fails before any build, with a coded refusal naming the pinned base reference and the declared total. Descriptor sizes stay required unsigned integers, so a missing, negative or fractional size refuses. Bounded unpacked acquisition of build bases is tracked in HACK-1945. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0d13545e9a
ℹ️ 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".
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
Stage names are stored lowercase, and BuildKit lowercases a FROM base name before its stage lookup. The FROM lookup compared the expanded name unchanged, so `FROM alpine AS Base` followed by `FROM Base` was refused. It now lowercases the name the way stage_by_name already does for COPY --from, RUN --mount from= and the build target. A name that matches no earlier stage still falls through as an image reference, where uppercase is invalid, and a stage named Scratch is still refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Nice work! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
…ts input Docker runs credential helpers through Go's os/exec, which ignores a broken pipe when the helper exits without reading the server address it was given. Hack refused that case, so a helper that answered and exited quickly could fail a registry pull depending on timing, and the registry identity-token test failed intermittently on loaded CI runners. A broken pipe on that write is no longer an error; the helper's exit status and reply decide the result. Other write errors still refuse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🛡️ Codex Security Review · Automatically triggered
Here are some automated security review suggestions for this pull request.
Reviewed commit: 6c0dd7d8e2
ℹ️ 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.
…ition BuildKit fetches a build base's layers itself, so the preflight only summed their sizes. A malicious base could carry foreign-layer urls or non-distributable layer types that direct acquisition refuses, and BuildKit would then fetch them from the guest's own namespace, outside the build network's isolation and the source policy. Before any build, the base's config descriptor and every layer descriptor now go through the checks direct acquisition applies: - the config: an image-config media type, then Fetch::checked with the metadata bound; - every layer: a distributable gzip, zstd or uncompressed media type (layer_extension), then Fetch::checked with the archive ceiling, which covers a valid digest, a size within limits and no urls. An empty urls array is treated as absent, matching acquisition. Refusals are coded native_build_base_image, name the base reference and the reason, and never echo URLs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@codex review |
|
Codex Review: Didn't find any major issues. Hooray! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
Codex Review: Didn't find any major issues. Chef's kiss. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
🛡️ Codex Security Review · Automatically triggeredSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
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 withHACK_NATIVE_BUILDS=1, which nativeup, restart and doctor all check. Each component's reviewed head is merged withgit 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.f21d5a04460ddff29f33a1d07f42e3288ec7460e;7f42e328adds #628's squash (re-review requested)f07ea583a0e3e797a93db842009989c7;a93db842only merges #629 into it (re-review requested)Security properties in the merged code
BUILDKIT_*,BUILDX_*andSOURCE_DATE_EPOCHare refused. A build argument equal to a managed secret value is refused without echoing the value. Build arguments are not secrets, matching Docker.build_policy::document, set throughEXPERIMENTAL_BUILDKIT_SOURCE_POLICYin 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,gitand blob sources. Pinned bases whose config hasOnBuildtriggers are refused.ADDsources are refused, including the JSON form, multiple sources and variables;<<;scratchis refused;ARGnames must be plain identifiers;linux/arm64;Integration changes
resolve_build_baseruns on feat(native): acquire images from any OCI registry with bounded budgets #605's guarded registry client, so the resolver and address policy, origin-bound authentication and the realm rules apply. It uses feat(native): opt-in linux/amd64 services under Rosetta 2 in the VM #609's native platform selection, so builds stay Docker Hub and linux/arm64 only. The older Docker Hub-only fetch helpers are dropped.build-imageandreclaim-build-imagescommands sit beside the image-policy and emulation options. The build error helpers takeimpl Into<ErrorMessage>.9f33a1d0).classifyAdapted, the Compose-backend refusal and doctor.uprefuses a build with a message that namesHACK_NATIVE_BUILDS, so doctor's basic-build advice names it too; one test expectation changed to match.prefer-nativepasses, and a test covers it.a0e3e797): each test gets its own fixture root, and EINVAL fromset_read_timeoutafter the peer closes is accepted.nextmerge: the last merge brings in Kit's CI shard and SIGKILL harness changes (test: raise SIGKILL route-retirement harness budget (HACK-1901) #624, fix(ci): treat cancelled-only shard results as non-failures #627). The commit hook formattedtests/native-compose-generation.test.tswith Biome, a whitespace-only change to that Kit file.Validation on this branch (
6337e0ed)RUSTUP_TOOLCHAIN=1.97.1 bun run check:local: pass. All-features clippy-D warnings(Zig 0.15.2): pass.--lib: 1346 pass.--features native-config-planprovider::graph: 673 pass. Contracts:project_contract66,compose_fields_contract10,interpolation_budget_contract1,local_contract14.bun run test:models(TLA+ 1.7.4): exit 0, 172 controls verified.docs:prepare+docs:check,privacy:check, gitleaks overorigin/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
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.