Merge remote-tracking branch 'origin/main' into capy/gsta-64-bun-1.4.4

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
garrytan committed 2026-10-23 04:39:20 +00:00
commit 5bdf88a710
15 files changed
+409 -40

No files matched your search

+1 -1
View File
@@ -67,7 +67,7 @@ Invoke them by name (e.g., `/office-hours`).
| `/benchmark` | Performance regression detection (page load, Core Web Vitals). |
| `/benchmark-models` | Cross-model benchmark for skills (Claude, GPT, Gemini side-by-side). |
| `/eval-plan` | Preregister a paid eval (bars, arms, decoys, stop rules), dry-run it for $0, pilot three priced items, then approve the spend. |
| `/cso` | Supported security findings with explicit coverage. Static assessment remains available without catalog profiles; contained runtime/scanner execution requires matching qualified profiles, and no catalog has one yet (scanner build inputs are reviewed and await their qualification run; runtime qualification needs a private evaluator that is still being built), so `/cso` runs static assessment only. Runtime-tested bundles authenticate separate external assertions. Project-test completion remains `self_reported` because target code controls the test process; `tested` is reserved for a future target-independent completion witness. |
| `/cso` | Supported security findings with explicit coverage. Static assessment remains available without catalog profiles. Contained runtime and scanner execution needs Docker and a matching qualified profile. The runtime catalog qualifies ten images: Node 24.4.0, Bun 1.3.10, Python 3.13.4 with uv 0.8.0, Rails on Ruby 3.4.4, and PostgreSQL 17.2, each for linux/amd64 and linux/arm64. The scanner catalog qualifies twelve: gitleaks, OSV-Scanner, Schemathesis, Semgrep, Trivy and zizmor on both platforms. Without Docker, or where no profile matches (native Windows Docker, for example), `/cso` runs static assessment only. Runtime-tested bundles authenticate separate external assertions. Project-test completion remains `self_reported` because target code controls the test process; `tested` is reserved for a future target-independent completion witness. |
| `/setup-gbrain` | Set up gbrain for cross-machine session memory sync. |
| `/sync-gbrain` | Keep gbrain current with this repo's code; refresh agent search guidance in CLAUDE.md. |
+18
View File
@@ -8,6 +8,24 @@
- **`bin/gstack-bun-version.sh`**: `GSTACK_BUN_VERIFIED` is now 1.4.2 1.4.3 1.4.4, with 1.4.4's checksums for linux-x64, linux-x64-baseline and linux-aarch64, the targets 1.4.3 covers. They come from the release's `SHASUMS256.txt.asc`, whose clearsigned signature verifies against Bun's release key F3DCC08A8572C0749B3E18888EAB4D40A7B22B59 (Robobun, the key oven-sh/bun's own Dockerfiles assert), and each archive was downloaded and hashed to the same value. The 1.4.2 and 1.4.3 entries are unchanged and verify the same way. gstack's own minimum (1.4.2), install default (1.4.3) and CI pins do not move.
- **`test/gstack-capy-install.test.ts`**: a gbrain-like repo whose CI pins only 1.4.4 refuses `~/.bun` 1.4.3 and installs the 1.4.4 archive. **`test/bun-version-drift.test.ts`**: every verified version must carry a checksum for each installer target. The existing "verified archives" text assertions list 1.4.4.
## [1.91.181.0] - 2026-10-23
**/cso can run its contained runtime checks.** Before this release the runtime catalog qualified nothing, so /cso always fell back to static assessment. It now qualifies ten pinned images: Node 24.4.0, Bun 1.3.10, Python 3.13.4 with uv 0.8.0, Rails on Ruby 3.4.4, and PostgreSQL 17.2, each for linux/amd64 and linux/arm64. With Docker installed, comprehensive /cso boots the target app inside the matching image and tests findings and repairs against it. The images passed the private evaluator's release qualification (run-5 statements, re-delivered under Garry's approval). The ingress then checked them in qualify run 39674065531 at 514fbfd.
### Added
- **`lib/cso/runtime-catalog.json`** is now catalog `cso-v3-39674065531-133cb360842d`, which replaces `cso-v3-build-reviewed-2026-09-10`. Promote run 39841502986 generated it from qualification run 39674065531's authenticated statements and attested it. GitHub Actions committed the attested bytes (`sha256:078e0bf6…`). Each runtime's image is pinned to a public `ghcr.io/garrytan/gstack/cso-staging/…@sha256` digest, and each entry records its source commit (133cb36), SBOM and provenance digests, and requalification triggers (`preparationSha256` `c721b585…`).
### For contributors
- **The /cso free tests no longer assume the committed runtime catalog is empty.** Six tests pinned `runtimes: []` and broke as soon as a promotion landed. Tests that need an unqualified catalog now build one with `unqualifiedRuntimeCatalog()`. The replay tests expect whichever fail-closed rejection the committed catalog produces, so the suite passes with the catalog both before and after a promotion.
## [1.91.180.0] - 2026-10-22
**The /cso docs say what the qualified catalogs now allow.** AGENTS.md, the README and `docs/cso-release-qualification.md` still said that no qualified runtime or scanner image existed, so /cso ran static assessment only. The scanner catalog has been qualified since #3042. The runtime catalog becomes qualified with the runtime promotion PR that ships alongside this one.
### Changed
- **AGENTS.md**, **README.md** and **`docs/cso-release-qualification.md`**: the /cso entries name the ten qualified runtime images and the twelve scanner images, and say when /cso still falls back to static assessment: without Docker, or where no profile matches.
## [1.91.179.0] - 2026-10-22
**docsync-fault-interface fits its budgets under load again.** On 2026-10-18 it timed out under load on a Ubicloud full run. Its nested 36-case suite spent most of its time starting processes: each fault actor spawn loaded about 36 ms of imports (an empty bun takes 5.5 ms), inspect repeated Git reads, and the whole nested run sat under one 120 s outer test. With real Git on 4 cores, inner cases now take a 0.70 s median (max 1.25 s) unloaded, against 1.07 s before. Under 8 busy loops the max is 3.44 s, inside bun's default 5 s; before, cases reached 4.45 s. The worst outer test went from about 100 s of its 120 s cap to 33 s. No timeout, budget, retry, assertion or case changed.
+1 -1
View File
@@ -299,7 +299,7 @@ Each skill feeds into the next. `/office-hours` writes a design doc that `/plan-
| `/qa` | **QA Lead** | Explore browser, API, CLI, job and webhook behavior. Reproduce bugs, write failing regressions, fix the cause and re-verify before committing. |
| `/qa-only` | **QA Reporter** | Explore and report with replayable evidence. Suggest regression cases without changing product code or tests. |
| `/pair-agent` | **Multi-Agent Coordinator** | Share gstack's own browser with any AI agent. One command, one paste, connected. Works with OpenClaw, Hermes, Codex, Cursor, or anything that can curl. Each agent gets its own tab. Auto-launches headed mode so you watch everything. Auto-starts ngrok tunnel for remote agents. Scoped tokens, tab isolation, rate limiting, activity attribution. (Runs on the bundled browser — the fallback engine; agents driving Aside just open their own tabs.) |
| `/cso` | **Chief Security Officer** | Security audit with an application model, supported findings, independent challenge, and explicit coverage. Static assessment remains available without catalog profiles. With matching qualified profiles, comprehensive mode adds contained runtime/scanner execution and reviewable repair candidates for Node/Bun, Python, and Rails; no qualified runtime or scanner profile is published yet, so audits run static assessment only. Runtime-tested bundles authenticate separate external assertions. Project-test completion remains `self_reported` because target code controls the test process; `tested` is reserved for a future target-independent completion witness. |
| `/cso` | **Chief Security Officer** | Security audit with an application model, supported findings, independent challenge, and explicit coverage. Static assessment remains available without catalog profiles. With matching qualified profiles, comprehensive mode adds contained runtime/scanner execution and reviewable repair candidates for Node/Bun, Python, and Rails. Qualified runtime and scanner images are published for linux/amd64 and linux/arm64. Without Docker, audits run static assessment only. Runtime-tested bundles authenticate separate external assertions. Project-test completion remains `self_reported` because target code controls the test process; `tested` is reserved for a future target-independent completion witness. |
| `/ship` | **Release Engineer** | Sync main, run tests, explore changed behavior, audit coverage, gate integrity and docs, then verify, push and open a PR. |
| `/land-and-deploy` | **Release Engineer** | Merge the PR, wait for CI and deploy, verify production health. One command from "approved" to "verified in production." |
| `/canary` | **SRE** | Post-deploy monitoring loop. Watches for console errors, performance regressions, and page failures. |
+1 -1
View File
@@ -1 +1 @@
1.91.179.0
1.91.181.0
+1 -1
View File
@@ -1,4 +1,4 @@
# gstack digest v1.91.179.0 — regenerate/re-copy after upgrading gstack
# gstack digest v1.91.181.0 — regenerate/re-copy after upgrading gstack
Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed
for agent hosts without a full skill install. The full skills add workflows,
+6 -5
View File
@@ -26,11 +26,12 @@ 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.
No qualified runtime or scanner image exists yet, so contained runtime and
scanner execution is unavailable and setup preloads nothing: the scanner
catalog is empty while its build inputs await review, and the runtime catalog's
profiles stay unqualified until a private evaluator, still being built, can
qualify them.
Both catalogs are qualified. The scanner catalog
(`lib/cso/scanner-images/catalog.json`) lists twelve scanner images: six
scanners, each on linux/amd64 and linux/arm64. The runtime catalog
(`lib/cso/runtime-catalog.json`) lists ten runtime images: Node, Bun, Python,
Rails and PostgreSQL, each on both platforms. The private evaluator qualified
them, and `cso-runtime-promote.yml` promoted them (described below).
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
+334 -13
View File
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"revision": "cso-v3-build-reviewed-2026-09-10",
"previousRevision": null,
"revision": "cso-v3-39674065531-133cb360842d",
"previousRevision": "cso-v3-build-reviewed-2026-09-10",
"helperAbi": 3,
"buildRevision": "cso-runtime-inputs-2026-09-10",
"profiles": [
@@ -10,7 +10,11 @@
"stack": "node",
"platform": "linux/amd64",
"state": "build_reviewed",
"versions": { "node": "24.4.0", "npm": "11.4.2", "cso-preparation": "1.0.0" },
"versions": {
"node": "24.4.0",
"npm": "11.4.2",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -18,7 +22,11 @@
"stack": "node",
"platform": "linux/arm64",
"state": "build_reviewed",
"versions": { "node": "24.4.0", "npm": "11.4.2", "cso-preparation": "1.0.0" },
"versions": {
"node": "24.4.0",
"npm": "11.4.2",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -26,7 +34,10 @@
"stack": "bun",
"platform": "linux/amd64",
"state": "build_reviewed",
"versions": { "bun": "1.3.10", "cso-preparation": "1.0.0" },
"versions": {
"bun": "1.3.10",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -34,7 +45,10 @@
"stack": "bun",
"platform": "linux/arm64",
"state": "build_reviewed",
"versions": { "bun": "1.3.10", "cso-preparation": "1.0.0" },
"versions": {
"bun": "1.3.10",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -42,7 +56,11 @@
"stack": "python",
"platform": "linux/amd64",
"state": "build_reviewed",
"versions": { "python": "3.13.4", "uv": "0.8.0", "cso-preparation": "1.0.0" },
"versions": {
"python": "3.13.4",
"uv": "0.8.0",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -50,7 +68,11 @@
"stack": "python",
"platform": "linux/arm64",
"state": "build_reviewed",
"versions": { "python": "3.13.4", "uv": "0.8.0", "cso-preparation": "1.0.0" },
"versions": {
"python": "3.13.4",
"uv": "0.8.0",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -58,7 +80,11 @@
"stack": "rails",
"platform": "linux/amd64",
"state": "build_reviewed",
"versions": { "ruby": "3.4.4", "bundler": "2.6.7", "cso-preparation": "1.0.0" },
"versions": {
"ruby": "3.4.4",
"bundler": "2.6.7",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -66,7 +92,11 @@
"stack": "rails",
"platform": "linux/arm64",
"state": "build_reviewed",
"versions": { "ruby": "3.4.4", "bundler": "2.6.7", "cso-preparation": "1.0.0" },
"versions": {
"ruby": "3.4.4",
"bundler": "2.6.7",
"cso-preparation": "1.0.0"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -74,7 +104,9 @@
"stack": "postgresql",
"platform": "linux/amd64",
"state": "build_reviewed",
"versions": { "postgresql": "17.2" },
"versions": {
"postgresql": "17.2"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
},
{
@@ -82,9 +114,298 @@
"stack": "postgresql",
"platform": "linux/arm64",
"state": "build_reviewed",
"versions": { "postgresql": "17.2" },
"versions": {
"postgresql": "17.2"
},
"reviewedAt": "2026-09-10T00:00:00.000Z"
}
],
"runtimes": []
"promotion": {
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"evidenceDigest": "sha256:3e7b150d2baa737c49743a5ca04c126a1a03e40e3f761c3820076949d8d886df",
"qualificationEvidenceDigest": "sha256:0ddc6d6876ae8b6c630faf576e8fbbb95323bd193998e37c4a83e42dafe1bc9c",
"requalification": {
"helperAbi": 3,
"isolationPolicyHash": "2ae25762ae556094cff083d1e9f0eb4884f22f5918920609c7b05a57c8e208fc",
"preparationSha256": "c721b5851c661dda35218994f714043472ad09bc339c4c3b5c7cb6961f69b898",
"buildInputsRevision": "cso-runtime-inputs-2026-09-10"
}
},
"runtimes": [
{
"id": "node-24.4.0-amd64",
"stack": "node",
"platform": "linux/amd64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/node-amd64@sha256:f7a52b8916cc78a846650c52b7847839b475fa6e81c5f3a7fd9b57e6b2915444",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"cso-preparation": "1.0.0",
"node": "24.4.0",
"npm": "11.4.2"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:1f33ea574738bf16ae2433f5a03a731654c2a808404e1547a6a598f3e955a973",
"provenanceDigest": "sha256:9e7d120e174eba06610ba853c8f89837f3e9d29547e9b63fa961531edee43239",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "node-24.4.0-arm64",
"stack": "node",
"platform": "linux/arm64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/node-arm64@sha256:a21c1aae3279793f546c09106528e3e05189e90f46c74a1dfd4bd294cd37f7ba",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"cso-preparation": "1.0.0",
"node": "24.4.0",
"npm": "11.4.2"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:6e13ed8415018576ab6cca8d962f6f3ea547c7c26ec01d645aee2910f9a2fa28",
"provenanceDigest": "sha256:39e5fa6d84ca0b22206d3c2c37827df490e2e21122f6a25dc03d117ce1e6e238",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "bun-1.3.10-amd64",
"stack": "bun",
"platform": "linux/amd64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/bun-amd64@sha256:49809f7c933954c2468c6dda731d1cef0d38ad1eb6b29d341f715fe09073aa89",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"bun": "1.3.10",
"cso-preparation": "1.0.0"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:b20227dfa4d520ebc7c0a448535e1bf0defcfc8aa5ffaca01157db246f5573a5",
"provenanceDigest": "sha256:bb111e4982d61b72d6e564d7a915440d6c61c0cb8e081810bfef33cefdc40323",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "bun-1.3.10-arm64",
"stack": "bun",
"platform": "linux/arm64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/bun-arm64@sha256:3194bbf094cdd0ee8a92b43f782f3ab1cad195dafc3eb9dcea66e4c2d2795733",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"bun": "1.3.10",
"cso-preparation": "1.0.0"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:f4e3a814c9121d98016bf19c590b4821741aec3904917e14bbf7f40d26a03253",
"provenanceDigest": "sha256:003a5e1a85412907b9c7ce9198ebe6a09bda8d457ecff60ae6ccc91d78ad0775",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "python-3.13.4-uv-0.8.0-amd64",
"stack": "python",
"platform": "linux/amd64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/python-amd64@sha256:93d17316a0bd53721904ef1d4b3259421248f1ee73b8ace14f4e323090f6e107",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"cso-preparation": "1.0.0",
"python": "3.13.4",
"uv": "0.8.0"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:0ae2eef050f0d4e68b4702fe29e2e8e325f4fa83500d9accc86f10a4825de947",
"provenanceDigest": "sha256:47b91e8706c764b4b1ca82dc96a90b131cf15882fca41b9fa2e45824bf2a5699",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "python-3.13.4-uv-0.8.0-arm64",
"stack": "python",
"platform": "linux/arm64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/python-arm64@sha256:69fbc13c7395c0f10fd02ebb648a41d6bcd20b801a8369a4073433a64720eafb",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"cso-preparation": "1.0.0",
"python": "3.13.4",
"uv": "0.8.0"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:c7da1793dda06b71b7705dbb1b4ca7f44d994a843aacea7e7b9df3210df4f29c",
"provenanceDigest": "sha256:5cb118f89b3422d1e4e009f0e004ea27e39f7b19d5a5d7090513da5b2c1f7abe",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "rails-ruby-3.4.4-amd64",
"stack": "rails",
"platform": "linux/amd64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/rails-amd64@sha256:11e35903d9d53a9965fd5cb03d898275db41512b152443a12c433a36e6e1ee69",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"bundler": "2.6.7",
"cso-preparation": "1.0.0",
"ruby": "3.4.4"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:74da76f47df2ba2f2bee0b829fb4e1c220dc859c211e5c51d2e17e2b6d3c9cb2",
"provenanceDigest": "sha256:183ec989bc6a22f36348177a46cb81aea7156cb6625af82b2f6d2672eade768c",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "rails-ruby-3.4.4-arm64",
"stack": "rails",
"platform": "linux/arm64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/rails-arm64@sha256:2210c1aec0a8226cd2aafa9a6238f95fc07a2c840129e85aff6d70d6cec6ddce",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"bundler": "2.6.7",
"cso-preparation": "1.0.0",
"ruby": "3.4.4"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "application",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:3a115e5a520e0baf0828600f03bcd068709e8a48fa6dd1b11e677c9ef9a1c9d9",
"provenanceDigest": "sha256:6e64dd8ccec1dc673baff97364bc4d7264580d351cee0513671904d870ce0525",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"positiveNegativeAssertionsPassed": true,
"heldOutRepairPassed": true
}
},
{
"id": "postgresql-17.2-amd64",
"stack": "postgresql",
"platform": "linux/amd64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/postgresql-amd64@sha256:006442609a277af34656366320866e3d6eb26e4c9241e2313c4856519628eab4",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"postgresql": "17.2"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "postgresql",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:5b7699536fdeb0f473eee0952e4e9f1b37050c099b9b5076eb1781d854977d6f",
"provenanceDigest": "sha256:337bb19b9aa7c6be3f6658f8f0ec4b84238ba69f7be006184f129b44e76b9b71",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"multiDatabasePassed": true,
"readinessPassed": true
}
},
{
"id": "postgresql-17.2-arm64",
"stack": "postgresql",
"platform": "linux/arm64",
"state": "qualified",
"image": "ghcr.io/garrytan/gstack/cso-staging/postgresql-arm64@sha256:9df877dabc0ea9d05beb7947c10dc3000617c1e5162e95b83dd9618ad3d16f7a",
"entrypoint": "/opt/cso/entrypoint",
"helperAbi": 3,
"versions": {
"postgresql": "17.2"
},
"policyVersion": "cso-isolation-v1",
"qualifiedAt": "2026-10-21T20:57:28.000Z",
"qualification": {
"kind": "postgresql",
"sourceCommit": "133cb360842d0a7cf88fc1d10dcb624f2b7b841f",
"workflow": "https://github.com/garrytan/gstack/actions/runs/39674065531",
"sbomDigest": "sha256:2b319e83d0b74def00dea625e24c886a71f7fea828327eb6cb4dfd8f7e777aa8",
"provenanceDigest": "sha256:cf27a88b3b1351aec9182e2b3509bd7e83b6046d1e9f1ea8d46054d30acbeeaf",
"verifiedProvenance": true,
"containmentPassed": true,
"coldStartPassed": true,
"multiDatabasePassed": true,
"readinessPassed": true
}
}
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "gstack",
"version": "1.91.179",
"version": "1.91.181",
"description": "Garry's Stack — Claude Code skills + fast headless browser. One repo, one install, entire AI engineering workflow.",
"license": "MIT",
"type": "module",
+2 -1
View File
@@ -11,6 +11,7 @@ import { newRun, readJson, saveReport } from '../lib/cso/state';
import { MAX_OUTPUT, canonical, sha256, type VerificationObservation } from '../lib/cso/contracts';
import { ISOLATION_POLICY_HASH } from '../lib/cso/docker';
import { canonicalStartPlan, canonicalTestPlan, patchHash, verifyRepair } from '../lib/cso/verification';
import { replayRuntimeRejection } from './helpers/cso-runtime-catalog';
const ROOT = path.resolve(import.meta.dir, '..');
const launcher = path.join(ROOT, 'bin', process.platform === 'win32' ? 'gstack-cso-launcher.exe' : 'gstack-cso-launcher');
@@ -342,6 +343,6 @@ describe('CSO --paths audit scope', () => {
const supplied = cso(repo, ['replay', verified.bundle.id, '--source', repo]);
expect(supplied.status).not.toBe(0);
expect(supplied.stderr).not.toContain('does not match the bundle input hashes');
expect(supplied.stderr).toContain('MISSING_QUALIFIED_RUNTIME');
expect(supplied.stderr).toContain(replayRuntimeRejection(request.runtimeProfile, `linux/${arch}`).message);
}, 60_000);
});
+7 -6
View File
@@ -6,6 +6,7 @@ import { spawnSync } from 'node:child_process';
import { canonical, sha256, type VerificationObservation } from '../lib/cso/contracts';
import { ISOLATION_POLICY_HASH } from '../lib/cso/docker';
import { saveReport, withLock } from '../lib/cso/state';
import { replayRuntimeRejection } from './helpers/cso-runtime-catalog';
import { canonicalStartPlan, canonicalTestPlan, patchHash, verifyRepair } from '../lib/cso/verification';
const ROOT=path.resolve(import.meta.dir,'..'),launcher=path.join(ROOT,'bin',process.platform==='win32'?'gstack-cso-launcher.exe':'gstack-cso-launcher');
@@ -64,23 +65,23 @@ describe('CSO interrupted-run and replay commands',()=>{
const finding={title:'Unprotected record read',rootCause:'Record lookup omits the caller authorization predicate',location:{path:'app.js',line:1,symbol:'recordLookup'},advisoryIds:[],severity:'high',confidence:'high',confidenceRationale:'The caller-to-record trace directly establishes the missing authorization predicate',evidence:'supported',attackerControl:'Authenticated caller chooses the record identifier',impact:'Another tenant record is returned',scenario:'A tenant supplies another tenant record identifier and receives the record',trace:['GET /records/:id','recordLookup','record response'],references:['app.js:1','OWASP API1:2023'],recommendation:'Bind the lookup to the authenticated caller before returning the record',challenge:{reviewer:'independent-reviewer',independent:true,mode:'independent_agent',callers:'Authenticated route forwards the record identifier',controls:'Authentication exists without record authorization',counterevidence:'Opaque identifiers reduce guessing but do not authorize known identifiers',conclusion:'The record lookup lacks a caller authorization predicate'}};
expect(command(['submit',run.runId,writeInput('replay-finding.json',{findings:[finding]})]).status).toBe(0);const reportPath=path.join(dir,'report.json'),report=JSON.parse(fs.readFileSync(reportPath,'utf8')),findingId=report.findings[0].id,start=canonicalStartPlan(snapshot,'node',3456),tests=canonicalTestPlan(snapshot,'node');
const request:any={findingId,runtimeProfile:`node-24.4.0-${process.arch==='arm64'?'arm64':'amd64'}`,port:3456,start:start.command,legitimate:[{name:'legitimate control',path:'/control',method:'GET',expected:{status:200,includes:'CONTROL_OK'}}],security:{name:'unauthorized record is denied',path:'/records/other',method:'GET',expected:{status:403,includes:'DENIED'},vulnerable:{status:200,includes:'RECORD'}},existingTests:tests.commands,fixtures:{},boundaryFiles:['app.js'],testFiles:tests.files,changes:[{path:'app.js',beforeSha256:sha256(fs.readFileSync(path.join(snapshot,'app.js'))),after:'module.exports = "fixed"\n',effect:'source'}],review:{reviewer:'independent-reviewer',independent:true,rootCauseRepaired:true,featurePreserved:true,boundaryMocks:false,rationale:'The caller predicate is added while the legitimate control and existing test remain unchanged.',reviewedPatchHash:''}};request.review.reviewedPatchHash=patchHash(request);
const platform=process.arch==='arm64'?'linux/arm64':'linux/amd64',runtime:any={id:request.runtimeProfile,stack:'node',platform,image:`ghcr.io/garrytan/gstack/cso-staging/node-${process.arch==='arm64'?'arm64':'amd64'}@sha256:${'b'.repeat(64)}`},executor={observe:async(_source:string,phase:'before'|'after',received:any,_runtime:any,_verifier:any,_work:string,_control:string,_execution:any,evidence:any,witness:any)=>{const observation:VerificationObservation={booted:true,legitimate:true,security:phase==='before'?'intended_failure':'pass',existingTests:false,output:`external ${phase} assertions passed`,inputHash:''},receipt=await witness.attest(observation,[{command:received.existingTests[0],code:0,output:tap,minimumPassingTests:evidence.minimumPassingTests[0]}]);return{observation:{...observation,existingTests:receipt.diagnosticTestsPassed,inputHash:witness.binding.harnessHash},witness:receipt};}};
const platform=process.arch==='arm64'?'linux/arm64':'linux/amd64',rejection=replayRuntimeRejection(request.runtimeProfile,platform),runtime:any={id:request.runtimeProfile,stack:'node',platform,image:`ghcr.io/garrytan/gstack/cso-staging/node-${process.arch==='arm64'?'arm64':'amd64'}@sha256:${'b'.repeat(64)}`},executor={observe:async(_source:string,phase:'before'|'after',received:any,_runtime:any,_verifier:any,_work:string,_control:string,_execution:any,evidence:any,witness:any)=>{const observation:VerificationObservation={booted:true,legitimate:true,security:phase==='before'?'intended_failure':'pass',existingTests:false,output:`external ${phase} assertions passed`,inputHash:''},receipt=await witness.attest(observation,[{command:received.existingTests[0],code:0,output:tap,minimumPassingTests:evidence.minimumPassingTests[0]}]);return{observation:{...observation,existingTests:receipt.diagnosticTestsPassed,inputHash:witness.binding.harnessHash},witness:receipt};}};
const verified=await verifyRepair({runId:run.runId,runDir:dir,manifest,rawRequest:request,runtime,verifier:runtime,policyHash:ISOLATION_POLICY_HASH,auditPolicyHash:sha256(canonical(report.policy)),archives:[],executor});
report.findings[0].reproduction='reproduced';report.findings[0].repair='runtime_tested';report.findings[0].verificationId=verified.bundle.id;report.findings[0].verificationAssurance={assertions:'authenticated_out_of_process',testCompletion:'self_reported',review:'self_attested'};saveReport(dir,report);
const leased=withLock(dir,()=>command(['replay',verified.bundle.id])) as ReturnType<typeof command>;expect(leased.status).not.toBe(0);expect(leased.stderr).toContain('INSUFFICIENT_CAPACITY');expect(leased.stderr).toContain('Another helper is updating this run');
const retained=command(['replay',verified.bundle.id]);expect(retained.status).not.toBe(0);expect(retained.stderr).toContain('PREREQUISITE');expect(retained.stderr).toContain('MISSING_QUALIFIED_RUNTIME');
const retained=command(['replay',verified.bundle.id]);expect(retained.status).not.toBe(0);expect(retained.stderr).toContain(rejection.code);expect(retained.stderr).toContain(rejection.message);
const manifestPath=path.join(dir,'snapshot.json'),expiredManifest={...manifest,expiresAt:new Date(Date.now()-1_000).toISOString()};fs.writeFileSync(manifestPath,`${JSON.stringify(expiredManifest,null,2)}\n`);
const expiredPresent=command(['replay',verified.bundle.id]);expect(expiredPresent.status).not.toBe(0);expect(expiredPresent.stderr).toContain('MISSING_INPUT');expect(expiredPresent.stderr).toContain('Retained source expired');
const suppliedExpired=command(['replay',verified.bundle.id,'--source',repo]);expect(suppliedExpired.status).not.toBe(0);expect(suppliedExpired.stderr).toContain('PREREQUISITE');expect(suppliedExpired.stderr).toContain('MISSING_QUALIFIED_RUNTIME');expect(suppliedExpired.stderr).not.toContain('Retained source expired');
const suppliedExpired=command(['replay',verified.bundle.id,'--source',repo]);expect(suppliedExpired.status).not.toBe(0);expect(suppliedExpired.stderr).toContain(rejection.code);expect(suppliedExpired.stderr).toContain(rejection.message);expect(suppliedExpired.stderr).not.toContain('Retained source expired');
const noncanonicalExpiry=new Date(Date.now()+86400_000).toISOString().replace(/\.\d{3}Z$/,'Z');fs.writeFileSync(manifestPath,`${JSON.stringify({...manifest,expiresAt:noncanonicalExpiry},null,2)}\n`);
const invalidExpiry=command(['replay',verified.bundle.id,'--source',repo]);expect(invalidExpiry.status).not.toBe(0);expect(invalidExpiry.stderr).toContain('INCOMPATIBLE_INPUT');expect(invalidExpiry.stderr).toContain('Retained snapshot expiry is invalid');expect(invalidExpiry.stderr).not.toContain('MISSING_QUALIFIED_RUNTIME');
const invalidExpiry=command(['replay',verified.bundle.id,'--source',repo]);expect(invalidExpiry.status).not.toBe(0);expect(invalidExpiry.stderr).toContain('INCOMPATIBLE_INPUT');expect(invalidExpiry.stderr).toContain('Retained snapshot expiry is invalid');expect(invalidExpiry.stderr).not.toContain(rejection.message);
fs.writeFileSync(manifestPath,`${JSON.stringify({...manifest,expiresAt:new Date(Date.now()+86400_000).toISOString()},null,2)}\n`);fs.writeFileSync(path.join(snapshot,'app.js'),'module.exports = "tampered"\n');
const corruptRetained=command(['replay',verified.bundle.id,'--source',repo]);expect(corruptRetained.status).not.toBe(0);expect(corruptRetained.stderr).toContain('INCOMPATIBLE_INPUT');expect(corruptRetained.stderr).toContain('Retained snapshot changed');expect(corruptRetained.stderr).not.toContain('MISSING_QUALIFIED_RUNTIME');
const corruptRetained=command(['replay',verified.bundle.id,'--source',repo]);expect(corruptRetained.status).not.toBe(0);expect(corruptRetained.stderr).toContain('INCOMPATIBLE_INPUT');expect(corruptRetained.stderr).toContain('Retained snapshot changed');expect(corruptRetained.stderr).not.toContain(rejection.message);
fs.rmSync(snapshot,{recursive:true,force:true});fs.rmSync(path.join(dir,'readable'),{recursive:true,force:true});
const expired=command(['replay',verified.bundle.id]);expect(expired.status).not.toBe(0);expect(expired.stderr).toContain('MISSING_INPUT');expect(expired.stderr).toContain('Retained source expired');
fs.writeFileSync(path.join(repo,'app.js'),'module.exports = "changed"\n');const changed=command(['replay',verified.bundle.id,'--source',repo]);expect(changed.status).not.toBe(0);expect(changed.stderr).toContain('INCOMPATIBLE_INPUT');expect(changed.stderr).toContain('does not match the bundle input hashes');
fs.writeFileSync(path.join(repo,'app.js'),'module.exports = "vulnerable"\n');const supplied=command(['replay',verified.bundle.id,'--source',repo]);expect(supplied.status).not.toBe(0);expect(supplied.stderr).toContain('PREREQUISITE');expect(supplied.stderr).toContain('MISSING_QUALIFIED_RUNTIME');expect(supplied.stderr).not.toContain('does not match the bundle input hashes');
fs.writeFileSync(path.join(repo,'app.js'),'module.exports = "vulnerable"\n');const supplied=command(['replay',verified.bundle.id,'--source',repo]);expect(supplied.status).not.toBe(0);expect(supplied.stderr).toContain(rejection.code);expect(supplied.stderr).toContain(rejection.message);expect(supplied.stderr).not.toContain('does not match the bundle input hashes');
},30_000);
});
+3 -3
View File
@@ -13,7 +13,7 @@ import {
type PreparationSandboxRunner, type OfflinePreparationRequest, preparedIdentityEntry } from '../lib/cso/preparation-executor';
import { CSO_HELPER_ABI } from '../lib/cso/runtime-catalog';
import { DockerPreparationSandboxRunner, isBlockedRegistryAddress, RegistryEgressBroker } from '../lib/cso/preparation-docker';
import { completeRuntimeCatalogFixture, qualifiedRuntimeFixture } from './helpers/cso-runtime-catalog';
import { completeRuntimeCatalogFixture, qualifiedRuntimeFixture, unqualifiedRuntimeCatalog } from './helpers/cso-runtime-catalog';
const roots: string[] = [];
const digest = 'a'.repeat(64);
@@ -138,9 +138,9 @@ function errorCode(error: unknown): string | undefined { return error instanceof
afterEach(() => { for (const value of roots.splice(0)) fs.rmSync(value, { recursive: true, force: true }); });
describe('CSO constrained dependency preparation executor', () => {
test('the unqualified default catalog and forged admissions fail closed', async () => {
test('an unqualified catalog and forged admissions fail closed', async () => {
const source = snapshot('node'), plan = inspectPreparation(source, 'node'), runner = new FakeRunner(), cache = cacheFixture();
expect(() => admitPreparationRuntime({ plan, platform: 'linux/amd64' })).toThrow('MISSING_QUALIFIED_RUNTIME');
expect(() => admitPreparationRuntime({ plan, platform: 'linux/amd64', catalog: unqualifiedRuntimeCatalog() })).toThrow('MISSING_QUALIFIED_RUNTIME');
const executor = new PreparationExecutor({ cache, runner });
try {
await executor.acquire({ plan, admission: { schemaVersion: 1, catalogRevision: 'forged', runtime: runtime('node') },
+7 -4
View File
@@ -4,7 +4,7 @@ import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { inspectPreparation, railsTestConfiguration } from '../lib/cso/preparation';
import { assertRuntimeCompatible, RUNTIME_CATALOG, rollbackCatalog, selectRuntime, validateRuntimeCatalog } from '../lib/cso/runtime-catalog';
import { completeRuntimeCatalogFixture, qualifiedRuntimeFixture } from './helpers/cso-runtime-catalog';
import { completeRuntimeCatalogFixture, qualifiedRuntimeFixture, unqualifiedRuntimeCatalog } from './helpers/cso-runtime-catalog';
const roots: string[] = [];
const hash = 'a'.repeat(64);
@@ -231,10 +231,13 @@ describe('CSO inert Rails preparation', () => {
describe('CSO runtime catalog admission', () => {
const runtime = () => qualifiedRuntimeFixture('node');
const catalog = () => completeRuntimeCatalogFixture('test-1');
test('default catalog cannot execute unqualified images', () => {
test('default catalog admits only digest-pinned reviewed profiles and unqualified profiles cannot execute', () => {
expect(RUNTIME_CATALOG.profiles).toHaveLength(10);
expect(RUNTIME_CATALOG.runtimes).toEqual([]);
expect(() => selectRuntime('node', 'linux/amd64')).toThrow('Reviewed build profile node-24.4.0-amd64 is awaiting a qualified image promotion');
for (const runtime of RUNTIME_CATALOG.runtimes) {
expect(RUNTIME_CATALOG.profiles.filter(profile => profile.id === runtime.id && profile.stack === runtime.stack && profile.platform === runtime.platform)).toHaveLength(1);
expect(runtime.image).toMatch(/@sha256:[a-f0-9]{64}$/);
}
expect(() => selectRuntime('node', 'linux/amd64', unqualifiedRuntimeCatalog())).toThrow('Reviewed build profile node-24.4.0-amd64 is awaiting a qualified image promotion');
});
test('selects exact qualified digest and platform', () => {
expect(selectRuntime('node', 'linux/amd64', catalog()).image).toContain('@sha256:');
+4 -1
View File
@@ -69,7 +69,10 @@ describe('CSO runtime catalog promotion', () => {
const matrix = imageBuildMatrix(buildInputs);
expect(matrix.include).toHaveLength(10);
expect(committedCatalog.profiles).toHaveLength(10);
expect(committedCatalog.runtimes).toEqual([]);
for (const runtime of committedCatalog.runtimes) {
expect(committedCatalog.profiles.filter(profile => profile.id === runtime.id && profile.stack === runtime.stack && profile.platform === runtime.platform)).toHaveLength(1);
expect(runtime.image).toMatch(/@sha256:[a-f0-9]{64}$/);
}
expect(committedCatalog.buildRevision).toBe(buildInputs.revision);
expect(matrix.include.find(row => row.stack === 'bun')!.versions).not.toHaveProperty('node');
validateRuntimeCatalog(committedCatalog);
+2 -2
View File
@@ -10,7 +10,7 @@ import { total } from '../lib/cso/admission';
import { SCANNER_IDS, ScannerExecution, ScannerId, scannerPlans } from '../lib/cso/scanners';
import { type PreparationSandboxRunner } from '../lib/cso/preparation-executor';
import { canonicalStartPlan } from '../lib/cso/verification';
import { completeRuntimeCatalogFixture } from './helpers/cso-runtime-catalog';
import { completeRuntimeCatalogFixture, unqualifiedRuntimeCatalog } from './helpers/cso-runtime-catalog';
const roots: string[] = [];
afterEach(() => { for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true }); });
@@ -186,7 +186,7 @@ describe('Schemathesis application admission', () => {
expect(calls).toBe(0); expect(out.outcome.gaps[0].message).toContain('boundary is missing or transformed');
});
test('no runtime profile means a prerequisite, not a scanner success', async () => {
const out = await executeScanner(input('schemathesis'), { catalog: catalog('schemathesis') });
const out = await executeScanner(input('schemathesis'), { catalog: catalog('schemathesis'), runtimes: unqualifiedRuntimeCatalog() });
expect(out.outcome.status).toBe('not_assessed'); expect(out.outcome.gaps[0].message).toContain('runtime is unavailable');
});
test.each(['remote-ref', 'server', 'hook', 'callback', 'operation', 'unbounded', 'source-command'])('rejects hostile or ambiguous %s harness input', kind => {
+21
View File
@@ -5,6 +5,8 @@ import { canonical, sha256 } from '../../lib/cso/contracts';
import { ISOLATION_POLICY_HASH } from '../../lib/cso/docker';
import {
CSO_HELPER_ABI,
RUNTIME_CATALOG,
selectRuntime,
type QualifiedRuntime,
type RuntimeCatalog,
type RuntimePlatform,
@@ -125,3 +127,22 @@ export function installStagedRuntime(catalog: RuntimeCatalog, runtime: Qualified
catalog.promotion.evidenceDigest = `sha256:${sha256(canonical(catalog.runtimes))}`;
return installed;
}
/** The given catalog with its promotion removed: every reviewed profile is still awaiting a qualified image. */
export function unqualifiedRuntimeCatalog(base: RuntimeCatalog = RUNTIME_CATALOG): RuntimeCatalog {
const { promotion: _promotion, ...reviewed } = base;
return { ...reviewed, profiles: reviewed.profiles.map(profile => ({ ...profile, versions: { ...profile.versions } })), runtimes: [] };
}
/**
* The fail-closed rejection replay reaches at runtime admission for a fixture
* bundle whose image digest no qualified catalog entry carries.
*/
export function replayRuntimeRejection(profile: string, platform: RuntimePlatform): { code: string; message: string } {
try {
selectRuntime(profile, platform);
} catch (error) {
return { code: 'PREREQUISITE', message: String((error as Error).message).split(':')[0] };
}
return { code: 'INCOMPATIBLE_INPUT', message: 'Qualified runtime digest does not match the bundle' };
}