You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat(router): publish the router image in releases and use it by default - #10104
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.
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.
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.
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.
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
If build-router fails, the release fails, because release needs every build job. The same
Dockerfile and build arguments run on every router PR, in test-router.yml and the gate.
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>
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>
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.
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.
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 thisrepository'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
githubnextimage. gh-aw switches to the new image in step 3, after the first releasethat publishes it has been checked.
Commits
Please review commit by commit.
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 runtimelibraries, 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. Ithas 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 upgradeinstead ofapt-get;addgroupandadduser, with the same user and group (10001), home directory andshell;
sha256sumoptions, which BusyBox needs.Python 3.12, the hash-pinned
requirements.lock, the labels and the build checks forVERSIONandOPENAPI_SHA256are unchanged.requirements.lockalready has the musllinuxwheel hashes for
pydantic-core, the only compiled dependency.ci(router): publish and scan the router image(37f7409b)release.yml: a newbuild-routerjob, modelled onbuild-api-proxy:containers/routerforlinux/amd64andlinux/arm64and pushesghcr.io/github/gh-aw-firewall/router:<version>and:latest;OPENAPI_SHA256, the hash ofopenapi.yamlat the tag, andVCS_REF,the tag commit.
github.shawould be the parent of the tag-only version commit. It passesno
VERSION: the image's version label stays the router's own version, 0.1.3, and theDockerfile fails the build if
VERSIONdiffers from it.both platforms, then signs it with cosign and attests an SPDX SBOM, like the other images.
releasejob needsbuild-routerand adds the router tocontainers.txt.supply-chain-scan.yml: the blocking gate builds and scans the router, computingOPENAPI_SHA256itself.action.yml: the setup action reads the router digest fromcontainers.txtinto theimage_tagoutput, like the other images. Older releases have no router entry, so nothingchanges for them.
feat(router): default the router to the AWF release image(650fe89f)src/image-resolver.ts: removes thegithubnextdefault. Without a manifest or an explicitimage, the router now resolves from
--image-registryand--image-taglike every other AWFimage, so
--image-tag <version>,router=sha256:<digest>pins it. Routing still fails unlessthe 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 localbuild example and the packages to make public.
containers/router/README.md: where the image is published and how to pin it, how routerchanges 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 atcontainers/router.feat(routing): support model routing with --build-local(2171912a)Before this PR, routing with
--build-localwas rejected only because the router'sgithubnextdefault wasn't pinned. Commit 3 removed that default, so
--build-localwith arouter=sha256:digest in--image-tagpassed the pin check. Compose then builtcontainers/routerwithoutOPENAPI_SHA256, and the build failed. Now that the router liveshere, this commit makes
--build-localbuild it from the checkout like the other containers:src/services/router-service.ts: the local build passesOPENAPI_SHA256, the hash of thecheckout'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 routerbuilt with
--build-localcomes from the same checkout as the CLI, as api-proxy does.docs/awf-config-spec.md: documents the local build.Not in this PR
githubnextrouter image until step 3.containers/routeronly the Dockerfile's baseand the README do.
tests/integration/model-routing.test.tskeeps itsgithubnextfallback whenROUTER_IMAGEisn'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):repository as the source, the commit as the revision and the hash of
openapi.yaml. It runsas
10001:10001.OPENAPI_SHA256, without one, or with aVERSIONotherthan 0.1.3.
--fail-on high) passes. The Debian imagefails it with the findings above.
linux/amd64locally (no emulation).test-router.ymland the gate on thisPR build and test it on amd64.
the
releasejob'sneeds, and thebuild-routersteps.image-resolverand related jest suites pass on Linux (96 tests). The fullsrcsuite has the same failures onmainin my environment, so nothing new.npm run lint(no errors) andnpm run build.--build-localrouting:src/servicessuites pass on Linux(66 suites, 1,067 tests).
container.buildLocal: trueand no image manifest, AWF's composebuilt
containers/routerfrom the checkout with the rightOPENAPI_SHA256, and the routerbecame healthy. The router chose
gpt-6-lunaathigh, the routed call returned 200, and adeviating 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.ymlhas no dry run):router:<version>androuter:latestexist withlinux/amd64andlinux/arm64, and theGHCR package is public and linked to this repository.
cosign verifyand the SPDX attestation check pass, as indocs/image-verification.md.containers.txtlistsrouter@sha256:…./capabilitiesmatchesghcr.io/githubnext/gh-aw-router:0.1.3, and the Docker contractsuite passes against the published amd64 image.
shows router 0.1.3.
Notes
build-routerfails, the release fails, becausereleaseneeds every build job. The sameDockerfile and build arguments run on every router PR, in
test-router.ymland the gate.