mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-16 09:55:29 +02:00
* feat(cso): add verified audits and replayable repair bundles * fix(cso): harden qualification and setup boundaries * fix(cso): assemble security canaries at runtime * fix(cso): bound release proof and maintenance work Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): require complete evaluation reports Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): replay expired snapshots from supplied source Co-Authored-By: OpenAI Codex <noreply@openai.com> * test(cso): synchronize DNS cancellation assertion Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore(ship): exempt repository owner from liveness proof Co-Authored-By: OpenAI Codex <noreply@openai.com> * test(cso): make recheck retention overlap deterministic Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: bump version and changelog (v1.85.0.0) Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): pass native release gates Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: move release to v1.86.0.0 Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): resolve rechecks by finding Co-Authored-By: OpenAI Codex <noreply@openai.com> * chore: move release to v1.87.0.0 Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): pass macOS and Windows release gates Normalize BSD wc output, compare Windows paths by filesystem identity, preserve portable snapshot race coverage, and narrow POSIX-only Windows fixtures. Co-Authored-By: OpenAI Codex <noreply@openai.com> * fix(cso): harden native verification gates * fix(cso): refine Windows native diagnostics * test(cso): isolate Windows Git startup failure * test(cso): stabilize Windows native diagnostics * fix(cso): support hardened Git on Windows * fix(cso): close final verification gaps * test(cso): bound cold Docker fixture setup * fix(cso): restore cross-platform free-suite gates --------- Co-authored-by: OpenAI Codex <noreply@openai.com>
195 lines
13 KiB
Markdown
195 lines
13 KiB
Markdown
# Building and qualifying CSO runtimes
|
||
|
||
`bun run build:cso` compiles the trusted launcher, helper, and independent
|
||
watchdog. Bun must support all four `--no-compile-autoload-*` switches. macOS and
|
||
Linux also require a C compiler (`cc`, Clang, or GCC). Linux links the launcher
|
||
statically; macOS signs it with the hardened runtime. `bun run build` includes
|
||
this step and remains a strict release/developer build. Setup feature-probes the
|
||
four Bun switches and the host native toolchain before building. If either is
|
||
unavailable, it builds the rest of gstack, removes any stale CSO helpers, and
|
||
reports `/cso` unavailable; the skill then reports `not assessed` and never
|
||
substitutes repository tooling. Re-run setup after installing the prerequisite.
|
||
When the probe succeeds, setup keeps the strict failure behavior and checks the
|
||
artifacts for freshness. The generated
|
||
`bin/gstack-cso-launcher` is the setup-built direct command; the compiled core
|
||
is not the entrypoint. The core's direct-invocation marker catches accidental
|
||
misuse; it does not authenticate a process against the trusted same-user host.
|
||
The native launcher is the pre-runtime boundary that strips Bun, Node, and
|
||
loader injection variables. CSO is not exposed as an npm package bin: its native
|
||
launcher, compiled core, watchdog, and hidden `.gstack-cso-generation` manifest
|
||
must be built and installed together for the current host. Windows also requires
|
||
the empty `.gstack-cso-generation.lock` file from that same publication. Windows
|
||
builds the native launcher with Visual Studio 2022 Build Tools (Desktop
|
||
development with C++), discovered from Git Bash through the installed Developer
|
||
PowerShell script; only the core is compiled with Bun.
|
||
The dedicated Windows CI job checks startup, argument forwarding, and runtime
|
||
injection separately from static audit and private-state support. POSIX
|
||
watchdog execution remains unavailable there.
|
||
|
||
Every successful fresh install or upgrade asks the trusted launcher to preload
|
||
the current host platform's qualified runtime and scanner images. This is the
|
||
only automatic image acquisition path. It first validates the committed
|
||
catalogs, pins a local Unix-socket Docker endpoint, rejects remote TCP/SSH
|
||
contexts, and uses a new empty Docker configuration so pulls are anonymous.
|
||
Only fully qualified `registry/repository@sha256:...` catalog entries are
|
||
requested. Missing Docker or public registry access is a visible, nonfatal
|
||
prerequisite: static audits remain available and a later `./setup` retries.
|
||
The preload stage reserves 30 seconds for endpoint admission and gives each
|
||
declared image a separate 30-second inspection/download window. Set
|
||
`GSTACK_CSO_IMAGE_PULL_TIMEOUT_SECONDS=120` when registry speed requires a
|
||
longer per-image window (accepted range: 5–300 seconds). The helper derives an aggregate deadline from the
|
||
catalog size and caps the complete stage at one hour. A per-image timeout leaves
|
||
that digest unavailable and continues with the next entry; reaching the hard
|
||
aggregate deadline stops all remaining work. When a registry prerequisite stops
|
||
further downloads, setup still checks later catalog entries locally and reports
|
||
every already-present digest. A later setup resumes from exact local digests.
|
||
Application, verifier, and scanner execution still use `--pull=never` with no
|
||
egress. `gstack-cso doctor --repo PATH` never downloads; it reports a qualified
|
||
profile unavailable until that exact digest and platform are already present
|
||
in the local daemon.
|
||
|
||
The ordinary free suite exercises contracts without requiring Docker. The
|
||
`cso-docker-integration` CI job sets `GSTACK_CSO_DOCKER_TESTS=1`; unavailable Docker
|
||
or missing containment prerequisites fail that job. Run it locally with a
|
||
working local daemon:
|
||
|
||
```bash
|
||
bun run build:cso
|
||
GSTACK_CSO_DOCKER_TESTS=1 bun run test:cso:docker
|
||
```
|
||
|
||
When the staged-image workflow supplies `GSTACK_CSO_TEST_IMAGE`, platform,
|
||
stack, and exact tool versions, the same command also runs stack-specific
|
||
qualification journeys. A second native qualification job waits for every
|
||
same-architecture staging job, so Rails receives both its application digest
|
||
and the staged PostgreSQL digest; a missing matching artifact fails the gate.
|
||
The Node journey starts with an empty public archive
|
||
cache, acquires an integrity-pinned dependency through the registry-restricted
|
||
broker, installs and executes it offline, reproduces a producer-visible fixture
|
||
defect, and verifies a patch bound to its recorded closure and preparation
|
||
proofs. The helper issues a `runtime_tested` `RepairBundle` only when its
|
||
out-of-process witness authenticates the external assertions. Canonical project
|
||
tests run in a target-controlled process, so their completion remains
|
||
`self_reported`: target code can forge reporter output or terminate the runner.
|
||
Recorded command, count, exit, and output hashes are diagnostic evidence; they
|
||
do not establish target-independent test completion. The `tested` state remains
|
||
reserved for a future target-independent witness and is not emitted today.
|
||
Until the runtime-tested bundle, replay, and current-source recheck journey
|
||
passes, the held-out repair gate remains pending and catalog
|
||
promotion is prohibited. The candidate journey also asserts that no run-owned
|
||
containers, preparation call directories, archive staging directories, or
|
||
verification work directories remain. A skipped staged-image journey is not a
|
||
passing cold-start or private held-out-repair result. Bun and Python have cold acquisition, offline install,
|
||
boot, control, and project-test journeys. Rails runs those checks twice against
|
||
the same cold public-gem inputs: once with two disposable SQLite connections
|
||
and once with two databases in a fresh staged PostgreSQL sidecar. The Rails
|
||
fixture builds the source-platform `sqlite3` and `pg` native gems only in the
|
||
offline execution phase.
|
||
|
||
For hashed Python requirements resolved by pip, the broker records the requested
|
||
index URL, allowed contacted hosts, and verified wheel hash. Pip's internal
|
||
`files.pythonhosted.org` response URL is opaque to that TLS boundary, so
|
||
`resolvedUrl` remains `null`; `registryResponseSha256` identifies the acquired
|
||
archive bytes, not a verified response URL.
|
||
|
||
Image publication starts with the committed
|
||
[`build-inputs.json`](../lib/cso/images/build-inputs.json). Its reviewed revision
|
||
pins distinct native manifests for every stack and the SBOM generator. Each
|
||
profile records the inspected source tag and immutable index for provenance,
|
||
then `baseImages` keyed by `linux/amd64` and `linux/arm64`; Python additionally
|
||
records native uv manifests. CI fails if a source tag no longer resolves to the
|
||
reviewed index, a platform manifest is absent from that index, its image
|
||
configuration names another architecture, or an executable reports a different
|
||
runtime or package-manager version. Bun does not declare a Node version because
|
||
its container's `node` path is a Bun-backed fallback rather than a Node release.
|
||
A Bun project with a Node engine constraint therefore receives an explicit
|
||
prerequisite instead of a fabricated compatibility result.
|
||
|
||
Configure the repository's `cso-runtime-release` GitHub environment with required
|
||
maintainer review and deployments restricted to protected `main`. The
|
||
[`cso-runtime-images.yml`](../.github/workflows/cso-runtime-images.yml) workflow
|
||
runs a build-only native matrix on pull requests. Its manual publication jobs
|
||
only accept `main`. They build on native amd64 and arm64 runners, publish to
|
||
the `cso-staging` namespace, signs provenance and SBOM attestations, and verifies
|
||
the workflow identity and source commit. It then pulls that exact digest into
|
||
the local daemon for tests that prohibit implicit pulls. The per-image smoke test executes
|
||
the embedded verifier against a loopback service with both a passing control
|
||
and an intentionally failing control, alongside the containment suite. After
|
||
all staging rows finish, native qualification jobs run the matching cold-start
|
||
journey described above. The Node row additionally runs the complete lifecycle
|
||
journey.
|
||
Registry publication is staging, not runtime qualification.
|
||
|
||
GitHub Container Registry creates new packages private. Before a staging digest
|
||
can enter native or private qualification, a package administrator must make
|
||
its package public in GitHub's package settings. This is an explicit bootstrap
|
||
step because GitHub documents the visibility change as irreversible and does
|
||
not provide a supported workflow API for it. The release jobs use their scoped
|
||
workflow token to require `visibility: public` from GitHub's package REST
|
||
endpoint, then pull the exact platform digest with `docker --config` pointing
|
||
at a new directory whose `config.json` contains only an empty `auths` object.
|
||
The GitHub token, Docker credentials, contexts, certificate paths, and
|
||
credential helpers are absent from that Docker client. A private package,
|
||
metadata mismatch, failed anonymous pull, or different returned digest stops
|
||
staging, qualification, and promotion. Both
|
||
runtime and scanner promotion repeat this anonymous pull check on fresh hosted
|
||
runners before opening a catalog PR.
|
||
|
||
Review the retained staging artifacts together with all checks in
|
||
[`qualification.json`](../lib/cso/images/qualification.json). Cold-start fixtures,
|
||
held-out repair evaluations, precision/recall thresholds, crash cleanup, and all
|
||
secret-canary checks must pass before a reviewed catalog change can make a
|
||
digest executable. A successful protected-main qualification run packages one
|
||
`qualified-runtime.json` statement for each stack and architecture under the
|
||
`cso-qualified-runtime-statements` artifact. All ten statements bind the same
|
||
source commit and workflow run. The protected
|
||
[`cso-runtime-promote.yml`](../.github/workflows/cso-runtime-promote.yml) workflow
|
||
authenticates that run through the Actions API, checks every statement against
|
||
the committed build profiles, and emits an attested
|
||
`runtime-catalog.candidate.json`. The protected job immediately verifies the
|
||
candidate attestation against its workflow identity, protected-main source ref,
|
||
source commit, and exact file digest. It then compares `previousRevision` with
|
||
the source catalog, commits those same bytes to a fresh branch, and opens a
|
||
normal review PR. A concurrent catalog promotion fails the compare-and-swap or
|
||
produces a merge conflict instead of silently replacing the newer matrix.
|
||
The catalog stores a separately recomputable digest of the retained runtime
|
||
matrix and the digest of the complete external qualification statements. The
|
||
helper rejects a post-generation image or qualification mutation even if the
|
||
statement digest field was left unchanged.
|
||
Rollback retains the prior compatible catalog revision.
|
||
|
||
The public qualification ingress is
|
||
[`cso-runtime-qualification.yml`](../.github/workflows/cso-runtime-qualification.yml).
|
||
Set the protected environment variable `CSO_QUALIFICATION_ACTOR` to the GitHub
|
||
service account used by the private evaluator. After its held-out assertions and
|
||
accuracy run pass, that account sends a `repository_dispatch` event of type
|
||
`cso-runtime-qualified`. The payload has one key, `statements`, containing ten
|
||
objects with `schemaVersion`, `helperAbi`, `state`, `buildRevision`,
|
||
`runtimeId`, `stack`, `platform`, immutable `image`, exact `versions`,
|
||
`sourceCommit`, and the true-valued `checks` accepted by
|
||
`cso-runtime-promotion.ts`. The ingress checks the actor and matrix size,
|
||
re-verifies both OCI attestations against the protected staging workflow and
|
||
source commit, replaces the workflow, time, SBOM digest, and provenance digest
|
||
with values it observed, then runs the catalog generator as its final schema
|
||
gate. It uploads the statements only after that gate passes. The payload carries
|
||
no held-out assertion, application source, finding, or repair bundle.
|
||
|
||
The current reviewed profiles and unpromoted catalog establish this release
|
||
process; they do not claim published gstack images, passed containment, or
|
||
measured security accuracy.
|
||
Private held-out find-to-repair evaluations remain mandatory release gates for
|
||
Node, Bun, Python, and Rails. The producer-visible Node lifecycle fixture and the
|
||
other cold-start journeys do not satisfy those gates. Qualification artifacts
|
||
therefore record private checks as `"pending"` and `qualified: false` until all
|
||
four private evaluations and the accuracy thresholds pass. The remaining
|
||
external prerequisite is one successful protected-main run that uploads all ten
|
||
authenticated statements. This sandbox cannot create that evidence because it
|
||
has neither a native arm64 runner nor the private held-out assertions.
|
||
|
||
Attestation behavior follows [Docker's SBOM documentation](https://docs.docker.com/build/metadata/attestations/sbom/)
|
||
and [GitHub CLI's verification policy](https://cli.github.com/manual/gh_attestation_verify).
|
||
Public-package behavior follows [GitHub's package permissions documentation](https://docs.github.com/en/packages/learn-github-packages/about-permissions-for-github-packages),
|
||
[GitHub's package REST endpoint](https://docs.github.com/en/rest/packages/packages#get-a-package-for-a-user),
|
||
and [Docker's client configuration documentation](https://docs.docker.com/reference/cli/docker/),
|
||
inspected on September 11, 2026.
|