Skip to content

on-prem: build the runner image from a signed release with no network, test it in CI - #1923

Open
abrichr wants to merge 3 commits into
agent/142-c2from
agent/142-c3
Open

abrichr wants to merge 3 commits into
agent/142-c2from
agent/142-c3

Conversation

@abrichr

@abrichr abrichr commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

Part of the on-prem deployment work. Stacked on #1918 (base agent/142-c2), which adds bin/build-release.sh and the flow-onprem-offline CI job.

What this does

The container option in deploy/on-prem didn't work as shipped. The Dockerfile installed openadapt-flow[privacy] from a ./wheels directory that nothing produced and that wasn't pinned to the lock. Its command, deploy/on-prem/bin/run-queue.sh (and bin/run-queue.sh in docker-compose.yml), was a relative path resolved from the storage volume, so the runner could not start. Nothing built or ran the image.

  • bin/build-image.sh builds the image from a signed release archive (the one build-release.sh makes). It checks the signature against the pinned vendor key with the same code as install.sh --update, refuses a bad archive before calling docker, extracts the archive with the bounded extractor, and runs docker build --network none. pip only sees the release's own lock-pinned wheels, so a missing wheel fails the build instead of reaching an index. It tags openadapt-flow-onprem:<version> and can docker save it.
  • The Dockerfile installs exactly the declared version from those wheels, the same way install.sh does, then runs pip check. It uses an absolute command path. The base image defaults to python:3.12-slim pinned by digest and must already be loaded on the host.
  • docker-compose.yml no longer builds anything. It runs the image named in OPENADAPT_FLOW_IMAGE (required, no default) with pull_policy: never and the absolute run-queue path. The internal network and the no-ports rule are unchanged.
  • bin/test-container.sh runs in the flow-onprem-offline job, after the releases are staged and before the egress-blocked install. It checks that:
    • a tampered archive is refused and no image is built;
    • the same release with the presidio_analyzer wheel removed fails to build, on that package;
    • the signed release builds;
    • inside the image, with no network, the engine and privacy extra import from the image's venv at the release version, and the CLI reports it;
    • the default command resolves, and run-queue.sh files a malformed job under failed/ and records it in the audit log on a bind-mounted storage root;
    • run-queue.sh refuses to start with scrubbing off;
    • compose refuses to start without the image named, never pulls, publishes no ports, and uses an internal network;
    • a TCP connect to 1.1.1.1:443 works from the same image on Docker's default network (the control) and fails from the compose runner;
    • the compose runner stays up watching the queue.
  • README.md and docs/ON_PREM.md describe the new build step.

Testing

  • Locally (macOS, no Docker daemon): shellcheck and actionlint are clean.
  • I ran build-image.sh against a 2.6.0 release built from this lock (89 wheels), signed with a throwaway key, with a stub docker that records its arguments and the build context:
    • The tampered copy was refused by the signature check, and docker was never called.
    • The signed archive passed checksum and signature checks. It produced a context with release/ (VERSION, requirements.lock.txt, 89 wheels) and on-prem/, and called docker build --network none --pull=false.
  • Contract tests: packages/flow test_ci_workflow_contract and test_onprem_service 43 passed. Root test_ci_required_check, test_flow_shards, test_ci_locked_installs and test_nightly 46 passed. check_release_consistency is OK.
  • CI, flow-onprem-offline job on ubuntu-26.04 (run 41941053864, head d6db42e): test-update.sh 51 passed, 0 failed; test-container.sh 18 passed, 0 failed; the egress-blocked install and verify 21 passed, 0 failed. That is one green Linux run of the container checks.
  • The first CI run of test-container.sh (head 977dfd7) failed 2 checks, both test bugs, fixed in d6db42e. With docker build -q, pip's error never reached the log, so the missing-wheel build failed for the right reason but the test couldn't tell; it now uses --progress=plain. The runner is root in the image and audit-log.sh makes the audit directory 0700, so the host could not read the log; the test now reads it from inside the image.
  • Earlier runs on this stack failed "concurrent audit writers preserve one hash chain". The cause was the uutils mkdir on Ubuntu 26.04, which can report success to two racing callers, and that broke the audit and update locks. The fix is on-prem locks: hold on Ubuntu 26.04 (uutils mkdir) and never break a live writer's lock #1922, merged into this branch from on-prem: install, update and roll back the real engine offline in CI #1918.

Not tested

  • A real bundle run inside the container. The test only drives the queue with a malformed job.
  • The VLM appliance profile. It still needs a GPU host and a local model, and it reuses the same image.
  • Arm64 hosts. The release has to be built on the target's architecture and Python minor version, as build-release.sh says.

…, and test it in CI

The Dockerfile installed from an unpinned ./wheels directory that nothing
produced, and its command (bin/run-queue.sh relative to the storage volume)
could not resolve. build-image.sh now verifies a signed release archive
against the pinned vendor key, extracts it, and builds with --network none
from the release's lock-pinned wheels. docker-compose.yml runs a named,
never-pulled image with an absolute command. test-container.sh builds it in
CI, refuses a tampered archive, shows a missing wheel fails the build, and
runs the runner on the compose network with no route out.
@abrichr abrichr added the review-needed Needs an independent review from another vendor's agent label Nov 4, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

review-needed Needs an independent review from another vendor's agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant