Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/qualify-released-linux-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ on:
- tools/tests/test_released_sdk_inventory.py
- tools/tests/test_released_sdk_filesystem.py
- tools/tests/test_released_sdk_diagnostics.py
- tools/tests/test_released_sdk_scanner_diagnostics.py
- tools/RELEASED_SDK_INVENTORY.md
workflow_dispatch:

Expand Down
42 changes: 41 additions & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@ name: Validate build images

on:
pull_request:
# GHA caches from PR merge refs and release tags cannot seed another PR.
# A trusted default-branch producer makes this shared stage reusable there.
push:
branches:
- main
paths:
- .dockerignore
- .github/workflows/validate.yml
- linux/Dockerfile
- windows/Dockerfile
- portable/Dockerfile
- llvm-toolchain.json
- llvm-bootstrap-packages.lock
- tools/install_llvm_toolchain.py
- tools/validate_llvm_toolchain.py

permissions:
contents: read
Expand All @@ -13,6 +28,7 @@ concurrency:
jobs:
changes:
name: Select changed images
if: github.event_name == 'pull_request'
runs-on: ubuntu-26.04
outputs:
portable: ${{ steps.changes.outputs.portable }}
Expand Down Expand Up @@ -144,6 +160,30 @@ jobs:
- name: Test required-check aggregation
run: tools/test-require-image-checks.sh

llvm-cache:
name: Prime shared LLVM cache on main
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-26.04
timeout-minutes: 360
steps:
- name: Check out image sources
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4

- name: Build shared LLVM source toolchain
uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7
with:
context: .
file: linux/Dockerfile
target: llvm-toolchain
build-args: LLVM_BUILD_JOBS=2
platforms: linux/amd64
outputs: type=cacheonly
cache-from: type=gha,scope=llvm-toolchain-23.1.3
cache-to: type=gha,mode=max,scope=llvm-toolchain-23.1.3,ignore-error=true

llvm:
name: Shared LLVM source toolchain
needs: changes
Expand Down Expand Up @@ -587,7 +627,7 @@ jobs:
- linux
- windows
- windows-native
if: always()
if: always() && github.event_name == 'pull_request'
permissions:
contents: read
runs-on: ubuntu-26.04
Expand Down
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@
compiler limits separate from model-worker concurrency. Build the shared
`llvm-toolchain` stage once before parallel PR target validation; selected
image jobs and the required aggregate retain that dependency. Cold source builds
may use the supported six-hour hosted-job timeout.
may use the supported six-hour hosted-job timeout. Input-filtered trusted
`main` pushes seed the same shared LLVM Actions cache for later PRs and
measurements; PR merge-ref and release-tag caches cannot seed another PR.
Keep that cache-only job separate from required PR image/native checks and
private-package access. Cache eviction or changed inputs can require rebuilding.
- Every semantic release publishes the broad Linux, slim and portable Linux Classic,
general Windows, and task-focused Windows Classic images with their
supported tags. The Linux publisher owns `linux-build`, `classic-build`, and
Expand Down Expand Up @@ -137,6 +141,15 @@
root fields and bounded UID/mode ownership headers before and after protected
reads. Keep full stat equality for every nonroot path; see
`tools/RELEASED_SDK_INVENTORY.md` for the root fence contract.
Fence the scanner's one-file bounded local log with compression disabled;
Moby rejects compression with a single retained file. The schema-2 filesystem
receipt records execution as true after an accepted start or complete fenced
started-state proof, false before any attempt, and null for an unresolved
attempt. Reconcile after cleanup; a created snapshot cannot exclude a pending
start. Acquisition remains schema 1 and never starts its container. Recovery
observations cannot grant successful qualification. Retain only fixed failure stages,
codes, exit status and errno names; keep arbitrary logs, paths and exception
text out of published diagnostics.
- Validate Dockerfiles with `docker build --check`. Build and smoke-test each
affected image, including `classic-validation` before `classic-final` and the
Windows `classic-check` target plus native bundle; note that a cold
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,11 @@ Producer CI gives cold source builds up to the supported six-hour hosted-job
limit. It keeps LLVM and Windows build concurrency at two jobs each on hosted
runners. A shared stage cache compiles the source once before parallel PR image
validation; each target also retains its own LLVM-versioned cache scope.
Trusted `main` pushes affecting the LLVM stage seed its shared Actions cache,
which later PRs and measurements can restore. GitHub isolates caches created
on PR merge refs and release tags, so those caches alone cannot seed a new PR.
The first build, changed inputs, or cache eviction may still require a cold
build; required image and native checks run independently of the cache seed.
Increase build parallelism only for runners with corresponding CPU and memory
capacity; model-worker concurrency does not set compiler concurrency.

Expand Down Expand Up @@ -484,6 +489,14 @@ It binds the installed executables used by the later consumer; earlier library
builds remain part of trusted SDK production and are not historical execution
attestations.

Released SDK inventory adoption also requires the separate hosted filesystem
qualification in [`tools/RELEASED_SDK_INVENTORY.md`](tools/RELEASED_SDK_INVENTORY.md).
Its schema-2 receipt reconciles lost Docker start responses during fenced cleanup:
execution is `true` when proven, `false` before an attempt, and `null` when the
attempt remains unresolved. Successful qualification still requires the accepted
start response, complete filesystem equality and owned cleanup. The separate
schema-1 acquisition container remains unstarted.

Shared application libraries live under `/usr/local`; Debian packages provide
the baseline transitive libraries. Compiler flags use
`-march=x86-64 -mtune=generic`. Verification checks actual ELF dependency
Expand Down
53 changes: 50 additions & 3 deletions tools/RELEASED_SDK_INVENTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,17 +162,64 @@ privileges or credentials, fixed `/usr/bin/python3 -I -B` entrypoint and bounded
wait/log output. Host-side image, command, source, mount and ownership fences
surround execution and cleanup. Only this owned executed scanner may be stopped
on failure; the acquisition container remains unstarted.
The local log driver uses exactly one 128 MiB file with `compress=false`, and
that configuration is checked with the other container fences. Moby's
[local driver configuration](https://github.com/moby/moby/blob/docker-v29.4.2/daemon/logger/local/config.go)
rejects its default compression setting when `max-file=1`; explicit disabling
allows startup while preserving the log bound.

Its separate `filesystem-qualification.json` receipt explicitly records
`image_code_executed: true`, the exact index/platform/config/producer/consumer,
scanner/helper hashes, run identity, its own container and cleanup outcome.
Its separate schema-2 `filesystem-qualification.json` receipt records
`image_code_executed` as `true`, `false` or `null`, along with the exact
index/platform/config/producer/consumer, scanner/helper hashes, run identity,
its own container and cleanup outcome. `true` requires an accepted Docker start
response or a complete owned-container fence with coherent running/exited state,
true-integer PID, nonzero valid UTC RFC3339Nano `StartedAt`, and false paused,
restarting and dead flags. This records Docker's execution boundary, not proof
of particular CPU instructions. Moby's [start implementation](https://github.com/moby/moby/blob/docker-v29.4.2/daemon/start.go)
starts the task before setting the running state; its
[state transitions](https://github.com/moby/moby/blob/docker-v29.4.2/daemon/container/state.go)
preserve `StartedAt` after exit, and its
[inspection](https://github.com/moby/moby/blob/docker-v29.4.2/daemon/inspect.go)
formats that timestamp as RFC3339Nano.

A start response can be lost after execution begins. Cleanup's complete fences
before mutation and after stop also reconcile this evidence; proven execution
survives later cleanup failure. `false` means no start was attempted and no
fenced started state was observed. After any attempted start, missing inspection,
fence drift, malformed state or only a created/zero-start snapshot leaves `null`
unless a prior accepted response or fenced observation proved `true`. A created
snapshot cannot exclude a pending start request, even when removal succeeds.
Only fixed booleans and null enter this evidence; raw state/timestamps are not
published. Acquisition receipts and scanner reports retain their separate
schema 1. Reconciliation never grants qualification: success still requires the
accepted start response, completed scan, full comparison and successful cleanup.
Success requires exact equality of all three maps, every metadata/tool field and
canonical digests. An omitted unrelated producer file, missing notice, changed
resolved path, malformed report, source drift or cleanup failure stops the final
collection. This is trusted-producer filesystem qualification; image Python and
OS remain trusted. It does not establish runtime, export, gameplay or audible
acceptance, and does not claim independence from a malicious image OS.

Failed filesystem qualification retains a fixed host phase and, after a valid
Docker wait response, an integer exit status in `0..255`. Unexpected Docker HTTP
statuses retain the fixed `docker-request-denied` code without response bodies
or request paths. The original fixed qualification error also remains in
`failure.reason` if cleanup fails independently. The top-level reason then
reports `owned-cleanup-failed` with `owned_container_removed: false`; a failed
lookup, including 404 after a rejected or ambiguous create, never certifies
removal. Resolving the generated name still requires the full ownership fence
before any mutation. The collector fences the exited container before reading logs
even when the process failed. Nonzero
exit output is limited to 4 KiB and must use the same strict stdout-only Docker
framing as successful reports. The only retained scanner output is an exact
stopped-report schema containing a fixed stage and allowlisted failure code,
plus an allowlisted errno name for OS errors. Unknown codes, extra fields,
stderr, raw tracebacks, paths, exception strings and malformed output never
enter the receipt. A nonzero exit always fails qualification, including when
its output resembles a successful inventory. Diagnostics preserve owned
cleanup and every source, image, mount, isolation and inventory comparison gate;
they provide failure evidence rather than SDK acceptance.

The fixed `portable-final` row admits only `atrinik/classic-portable-build:1.22.0`
with this authenticated release identity:

Expand Down
Loading
Loading