Skip to content

feat(router): publish the router image in releases and use it by default - #10104

Merged
rabeckett merged 4 commits into
mainfrom
rabeckett-publish-router-image
Oct 16, 2026
Merged

rabeckett merged 4 commits into
mainfrom
rabeckett-publish-router-image

Conversation

@rabeckett

@rabeckett rabeckett commented Oct 16, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #10102

This is step 2 of 3 in moving the model router into this repository; #10034 has the plan. Step 1
(#10037) moved the source into containers/router. This PR publishes the router image from this
repository's releases and makes it AWF's default router image.

Users see no change yet. Routed runs need a router image pinned by digest, and gh-aw passes its own
pin to the githubnext image. gh-aw switches to the new image in step 3, after the first release
that publishes it has been checked.

Commits

Please review commit by commit.

  1. fix(router): build the router image on Alpine (b7c613a8)

    The router image fails the blocking supply-chain gate on its Debian base,
    python:3.12.15-slim-trixie: 14 High CVEs (55 package matches) in glibc, the gcc runtime
    libraries, perl, ncurses, util-linux, acl and zlib. Debian marks 13 "won't fix" and the other
    has no fix yet. None are in the router's Python dependencies.

    The base becomes python:3.12.15-alpine3.24, pinned by digest, like the other AWF images. It
    has no High or Critical findings, and the image shrinks from 230 MB to 118 MB. Only the
    commands that depend on the base change:

    • apk upgrade instead of apt-get;
    • BusyBox addgroup and adduser, with the same user and group (10001), home directory and
      shell;
    • the short sha256sum options, which BusyBox needs.

    Python 3.12, the hash-pinned requirements.lock, the labels and the build checks for
    VERSION and OPENAPI_SHA256 are unchanged. requirements.lock already has the musllinux
    wheel hashes for pydantic-core, the only compiled dependency.

  2. ci(router): publish and scan the router image (37f7409b)

    • release.yml: a new build-router job, modelled on build-api-proxy:
      • it builds containers/router for linux/amd64 and linux/arm64 and pushes
        ghcr.io/github/gh-aw-firewall/router:<version> and :latest;
      • build arguments: OPENAPI_SHA256, the hash of openapi.yaml at the tag, and VCS_REF,
        the tag commit. github.sha would be the parent of the tag-only version commit. It passes
        no VERSION: the image's version label stays the router's own version, 0.1.3, and the
        Dockerfile fails the build if VERSION differs from it.
      • It checks the pushed image's labels (source, revision, semantic version, OpenAPI hash) and
        both platforms, then signs it with cosign and attests an SPDX SBOM, like the other images.
      • The release job needs build-router and adds the router to containers.txt.
    • supply-chain-scan.yml: the blocking gate builds and scans the router, computing
      OPENAPI_SHA256 itself.
    • action.yml: the setup action reads the router digest from containers.txt into the
      image_tag output, like the other images. Older releases have no router entry, so nothing
      changes for them.
  3. feat(router): default the router to the AWF release image (650fe89f)

    • src/image-resolver.ts: removes the githubnext default. Without a manifest or an explicit
      image, the router now resolves from --image-registry and --image-tag like every other AWF
      image, so --image-tag <version>,router=sha256:<digest> pins it. Routing still fails unless
      the router image is pinned by digest.
    • src/image-resolver.test.ts: covers the new default, a custom registry, and a digest from
      --image-tag.
    • docs/awf-config-spec.md: describes the new default.
    • docs/releasing.md: adds the router to the release checks, the GHCR package list, the local
      build example and the packages to make public.
    • containers/router/README.md: where the image is published and how to pin it, how router
      changes ship in AWF releases, the files that hold the router version, and what to include in
      security reports.
    • containers/api-proxy/fixtures/routing-corpus.js: the provenance comment points at
      containers/router.
  4. feat(routing): support model routing with --build-local (2171912a)

    Before this PR, routing with --build-local was rejected only because the router's githubnext
    default wasn't pinned. Commit 3 removed that default, so --build-local with a
    router=sha256: digest in --image-tag passed the pin check. Compose then built
    containers/router without OPENAPI_SHA256, and the build failed. Now that the router lives
    here, this commit makes --build-local build it from the checkout like the other containers:

    • src/services/router-service.ts: the local build passes OPENAPI_SHA256, the hash of the
      checkout's openapi.yaml. The Dockerfile's check stays as strict as in the release workflow.
    • src/routing/bootstrap.ts: the digest pin is required only for pulled router images. A router
      built with --build-local comes from the same checkout as the CLI, as api-proxy does.
    • Tests: bootstrap accepts a local build without a pin, and compose passes the contract hash.
    • docs/awf-config-spec.md: documents the local build.

Not in this PR

  • gh-aw keeps pinning the githubnext router image until step 3.
  • Router code and routing tables don't change; in containers/router only the Dockerfile's base
    and the README do.
  • tests/integration/model-routing.test.ts keeps its githubnext fallback when ROUTER_IMAGE
    isn't set. CI always sets it, and there's no AWF router digest to pin until the first release.

Validation

Local (Windows on arm64, Docker on linux/arm64):

  • Router image on Alpine:
    • It builds with the release's build arguments, and the labels carry version 0.1.3, this
      repository as the source, the commit as the revision and the hash of openapi.yaml. It runs
      as 10001:10001.
    • The build still fails with a wrong OPENAPI_SHA256, without one, or with a VERSION other
      than 0.1.3.
    • Router pytest (408 passed) and the Docker contract suite (15 passed against the Alpine image).
    • The gate's Grype scan (same version, config and --fail-on high) passes. The Debian image
      fails it with the findings above.
    • I couldn't build linux/amd64 locally (no emulation). test-router.yml and the gate on this
      PR build and test it on amd64.
  • Workflows: actionlint reports nothing new. A script checked the gate's image list and hash,
    the release job's needs, and the build-router steps.
  • Resolver: the image-resolver and related jest suites pass on Linux (96 tests). The full
    src suite has the same failures on main in my environment, so nothing new.
  • Repository checks: npm run lint (no errors) and npm run build.
  • --build-local routing:
    • The bootstrap, compose-generator, image-resolver and src/services suites pass on Linux
      (66 suites, 1,067 tests).
    • In a live routed run with container.buildLocal: true and no image manifest, AWF's compose
      built containers/router from the checkout with the right OPENAPI_SHA256, and the router
      became healthy. The router chose gpt-6-luna at high, the routed call returned 200, and a
      deviating call was admitted. Squid, agent and api-proxy came from v0.28.60, pre-tagged so
      compose skipped building them, because the agent's Go stage and Squid's source build can't
      run in my environment.

After the first release that includes this PR (release.yml has no dry run):

  • router:<version> and router:latest exist with linux/amd64 and linux/arm64, and the
    GHCR package is public and linked to this repository.
  • cosign verify and the SPDX attestation check pass, as in docs/image-verification.md.
  • containers.txt lists router@sha256:….
  • /capabilities matches ghcr.io/githubnext/gh-aw-router:0.1.3, and the Docker contract
    suite passes against the published amd64 image.
  • A routed gh-aw workflow using the new image pinned by digest routes, and its session file
    shows router 0.1.3.

Notes

rabeckett and others added 3 commits October 16, 2026 10:03
The Debian slim base has 14 High CVEs that Debian won't fix or hasn't
fixed, which the blocking supply-chain scan rejects. The other AWF
images use Alpine. Only the base image and the commands that depend on
it change.

Refs #10102

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Build, sign and attest the router image in release.yml and list it in
containers.txt. Scan it in the blocking supply-chain gate, and pin it
from containers.txt in the setup action.

Refs #10102

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Drop the githubnext default so the router resolves from --image-registry
and --image-tag like the other AWF images. Update the docs.

Refs #10102

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@rabeckett
rabeckett requested a review from lpcox as a code owner October 16, 2026 17:05
@rabeckett rabeckett added enhancement New feature or request infrastructure labels Oct 16, 2026
Copilot AI max review requested due to automatic review settings October 16, 2026 17:05
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Documentation Preview

Documentation has been built for this PR.

Download preview artifact

To view locally:

  1. Download the docs-preview-pr-10104 artifact from the workflow run
  2. Unzip and open index.html in your browser

Built from commit 2b0e7d8

@github-actions

Copy link
Copy Markdown
Contributor

🚀 Security Guard has started processing this pull request

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Coverage Check Passed

Overall Coverage

Metric Base PR Delta
Lines 93.26% 93.47% 📈 +0.21%
Statements 91.86% 92.09% 📈 +0.23%
Functions 90.74% 90.91% 📈 +0.17%
Branches 85.71% 86.07% 📈 +0.36%
📁 Per-file Coverage Changes (10 files)
File Lines (Before → After) Statements (Before → After)
src/bounded-execution/strict-json-parser.ts 83.5% → 83.5% (+0.00%) 67.5% → 68.8% (+1.30%)
src/enclave/host-executor-journal.ts 90.4% → 90.5% (+0.03%) 87.4% → 87.5% (+0.06%)
src/artifact-preservation.ts 95.2% → 95.2% (+0.04%) 94.9% → 95.0% (+0.07%)
src/enclave/host-executor-protocol.ts 97.6% → 97.7% (+0.06%) 94.8% → 95.6% (+0.79%)
src/routing/bootstrap.ts 85.3% → 85.3% (+0.08%) 79.8% → 80.0% (+0.20%)
src/enclave/host-executor-server.ts 90.8% → 91.0% (+0.24%) 89.5% → 89.7% (+0.22%)
src/nvx/one-shot-adapter.ts 82.9% → 83.4% (+0.56%) 80.2% → 80.7% (+0.52%)
src/log-directory-setup.ts 96.8% → 100.0% (+3.18%) 96.9% → 100.0% (+3.13%)
src/bounded-execution/schema-hash.ts 92.3% → 100.0% (+7.70%) 80.0% → 93.3% (+13.33%)
src/bounded-execution/finite-schema.ts 59.6% → 81.7% (+22.10%) 57.1% → 78.9% (+21.87%)

Coverage comparison generated by scripts/ci/compare-coverage.ts

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The release-gating multi-platform publication path and Alpine migration require maintainer validation.

1 open finding
What changed in this PR

Makes the router a first-party AWF release image while preserving existing explicit gh-aw image pins.

Changes:

  • Publishes multi-platform router images with signatures, SBOM attestations, and supply-chain scanning.
  • Uses AWF registry/tag defaults and exposes released router digests through the setup action.
  • Migrates the router to Alpine and updates release documentation.
File Description
src/​image-resolver.ts Uses AWF defaults for router images.
src/​image-resolver.test.ts Covers router defaults, registries, and digest pins.
docs/​releasing.md Adds router publication and verification guidance.
docs/​awf-config-spec.md Documents router resolution and pinning.
containers/​router/​README.md Explains publication, versioning, and security reporting.
containers/​router/​Dockerfile Switches to Alpine-compatible build commands.
containers/​api-proxy/​fixtures/​routing-corpus.js Updates the provenance comment only.
action.yml Includes router digests in setup output.
.github/​workflows/​supply-chain-scan.yml Adds router builds to blocking scans.
.github/​workflows/​release.yml Builds, verifies, signs, and publishes router images.

🧠 Review effort: Max


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/image-resolver.ts
With --build-local, compose builds containers/router from the checkout.
Pass the OPENAPI_SHA256 build argument that the Dockerfile verifies, and
require a digest pin only for pulled router images, since a local build
comes from the same checkout as the CLI.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown
Contributor

✅ Copilot review passed with no inline comments.

@rabeckett Add the ready-for-aw label to this PR to trigger agentic CI smoke tests.

@github-actions

Copy link
Copy Markdown
Contributor

🔑 Smoke Copilot BYOK AOAI (api-key) is testing Azure OpenAI BYOK (api-key) mode on this pull request...

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Security Guard completed successfully!

Security Guard

Outcome: reported_no_action

The agent completed and reported no action. A no-op output alone does not verify that all required work was completed or that publication was possible.

Agent messages are omitted from this comment. The category uses structured run status, not interpretation of agent prose.

Generated from Security Guard · haiku45 · 5.24 AIC · ⊞ 6.0K · ◷

Generated by Security Guard for #10104

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

🔌 Smoke Services — All services reachable! ✅

🔌 Service connectivity validated by Smoke Services

@github-actions

Copy link
Copy Markdown
Contributor

🪪 Smoke Copilot BYOK AOAI (Entra) is testing Azure OpenAI BYOK (Entra / GitHub OIDC) mode on this pull request...

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✨ The prophecy is fulfilled... Smoke Codex has completed its mystical journey. The stars align. 🌟

🔮 The oracle has spoken through Smoke Codex

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Chroot tests passed! Smoke Chroot - All security and functionality tests succeeded.

Tested by Smoke Chroot

@github-actions

Copy link
Copy Markdown
Contributor

💎 Smoke Gemini is crystallizing results on this pull request...

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Build Test Suite completed successfully!

Generated by Build Test Suite for #10104

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Smoke Copilot BYOK completed. Copilot BYOK mode operational. 🔓

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

📰 VERDICT: Smoke Copilot has concluded. All systems operational. This is a developing story. 🎤

📰 BREAKING: Report filed by Smoke Copilot

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

Smoke Cloud Hypervisor completed. Cloud Hypervisor + Copilot passed.

Cloud Hypervisor + Copilot smoke test by Smoke Cloud Hypervisor

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

🛡️ Smoke Copilot Network Isolation confirmed the egress allowlist is enforced. ✅

🛡️ Egress verdict from Smoke Copilot Network Isolation

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

✅ Smoke Claude passed

Generated by Smoke Claude for #10104

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

📡 Smoke OTel Tracing completed. All tracing scenarios validated. ✅

📡 OTel tracing validated by Smoke OTel Tracing

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

The Alpine migration and first multi-platform publication and signing path need final maintainer validation.

0 open findings

1 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Avoid ineffective cross-release Docker layer caching

.github/​workflows/​release.yml:347

Optional: VCS_REF changes on every release and is declared before every RUN in containers/router/Dockerfile:8. In-scope build arguments participate in RUN cache keys, so these layers cannot be reused between releases, even when router sources are unchanged. Importing and exporting the GitHub Actions cache therefore adds work without cross-release reuse of those layers. Consider building fresh without this cache, as the agent job does. This trades away cache reuse on reruns of the same release.

🧠 Review effort: Max

@github-actions

Copy link
Copy Markdown
Contributor

Cloud Hypervisor + Copilot smoke test

  1. Merged PR lookup: PASS
  2. github.com connectivity: PASS (HTTP 200)
  3. File write/read: PASS
  4. example.com blocked: PASS (HTTP 000)
    Overall: PASS

Cloud Hypervisor + Copilot smoke test by Smoke Cloud Hypervisor
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test: Claude Engine Validation

Check Status
API ✅ PASS
GitHub CLI ✅ PASS
File ✅ PASS

Overall result: PASS

Generated by Smoke Claude for #10104 · claude · haiku45 · 49.1 AIC · ⊞ 6.1K · ◷
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Copilot: PASS ✅

📰 BREAKING: Report filed by Smoke Copilot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Services smoke test: ✅ Redis PONG, ✅ pg_isready accepting connections, ✅ SELECT 1 = 1. PASS

🔌 Service connectivity validated by Smoke Services
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test Results: Copilot BYOK (Direct Mode)

Test Status
MCP Connectivity ✅
GitHub.com HTTP ✅ (200)
File Write/Read ✅
BYOK Inference ✅

Mode: Direct BYOK (COPILOT_PROVIDER_API_KEY) → api-proxy → api.githubcopilot.com

Overall: PASS

🔑 BYOK report filed by Smoke Copilot BYOK
Add label ready-for-aw to run again

@github-actions github-actions Bot added the smoke-copilot-network-isolation Copilot network-isolation egress smoke test label Oct 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

EGRESS_RESULT allow=pass deny=pass

  • ✅ Allowed domain (api.github.com): 200
  • ✅ Blocked domain (example.com): blocked (curl error)
    Overall: PASS — cc @rabeckett

🛡️ Egress verdict from Smoke Copilot Network Isolation
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

OTEL smoke test results:

  • ✅ 1. Module loading: otel.js loads; exports startRequestSpan, setTokenAttributes, setBudgetAttributes, endSpan, endSpanError, shutdown, isEnabled.
  • ✅ 2. Tests: 3 OTEL suites, 68/68 passed.
  • ✅ 3. Env forwarding: trace context in env-passthrough.ts; OTEL vars in api-proxy-env-config.ts.
  • ✅ 4. Token tracker: onUsage callback is present in token-tracker-http.js.
  • ✅ 5. Diagnostics: /tmp/gh-aw/otel.jsonl has 1 entry.

📡 OTel tracing validated by Smoke OTel Tracing
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

10103 fix: serialize enclave schemas and diagnose host rejections ✅
10099 ci: pin enclave Cloud Hypervisor smoke to AWF v0.28.60 ✅
PR detail read ✅
Playwright title check ❌
File write/read ✅
Build (npm ci && npm run build) ✅
Overall: FAIL

🔮 The oracle has spoken through Smoke Codex
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

Chroot version comparison

Runtime Host Version Chroot Version Match?
Python Python 3.12.15 Python 3.12.15 ✅
Node.js v24.21.0 v22.23.2 ❌
Go go1.22.12 go1.22.12 ✅

Node.js differs between host and chroot, so not all tests passed and the smoke-chroot label was not added.

Tested by Smoke Chroot
Add label ready-for-aw to run again

@github-actions

Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia ❌ not run ❌ FAIL
Bun hono ❌ not run ❌ FAIL
C++ fmt ✅ N/A ✅ PASS
C++ json ✅ N/A ✅ PASS
Deno oak ❌ not run ❌ FAIL
Deno std ❌ not run ❌ FAIL
.NET hello-world ✅ N/A ✅ PASS
.NET json-parse ✅ N/A ✅ PASS
Go color ✅ 1/1 passed ✅ PASS
Go env ✅ 1/1 passed ✅ PASS
Go uuid ✅ 1/1 passed ✅ PASS
Java gson ✅ 1/1 passed ✅ PASS
Java caffeine ✅ 1/1 passed ✅ PASS
Node.js clsx ✅ pass ✅ PASS
Node.js execa ✅ pass ✅ PASS
Node.js p-limit ✅ pass ✅ PASS
Rust fd ✅ 1/1 passed ✅ PASS
Rust zoxide ✅ 1/1 passed ✅ PASS

Overall: 6/8 ecosystems passed — FAIL

Failures:

  • Bun and Deno: the curl ... | bash/sh installer commands were blocked by the sandbox permission policy (no interactive approval). Bun and Deno are not preinstalled, so these tests could not run. I did not try to work around the block.

Environment notes:

  • .NET needed XDG_RUNTIME_DIR and DOTNET_CLI_HOME set to writable paths because /run/user/1001 is missing.
  • Java used -Dmaven.repo.local pointing at a writable directory because ~/.m2 is root-owned and ~/.m2/repository could not be created.

All 8 repositories cloned successfully. The build-test label was not added because of the failures.

Generated by Build Test Suite for #10104 · copilot · auto · 25.8 AIC · ⊞ 12K · ◷
Add label ready-for-aw to run again

@rabeckett
rabeckett merged commit 5f51923 into main Oct 16, 2026
149 of 155 checks passed
@rabeckett
rabeckett deleted the rabeckett-publish-router-image branch October 16, 2026 18:40

This branch had an error being deployed

1 failed deployment
aoai-model — 2171912a Deployed Oct 16, 2026 by rabeckett via conclusion #1982
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Publish the router image in AWF releases and use it by default

2 participants