Skip to content

feat: add sandbox.agent.runtime: gvisor for gVisor container runtime support#44796

Merged
lpcox merged 10 commits into
mainfrom
copilot/feat-add-gvisor-install-script
Jul 10, 2026
Merged

feat: add sandbox.agent.runtime: gvisor for gVisor container runtime support#44796
lpcox merged 10 commits into
mainfrom
copilot/feat-add-gvisor-install-script

Conversation

Copilot AI commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

AWF already supports container.containerRuntime: "gvisor" in its stdin config (gh-aw-firewall#6093). This wires up the compiler side: a new sandbox.agent.runtime frontmatter field, a pre-agent gVisor install step, and the AWF config JSON plumbing.

New frontmatter field

sandbox:
  agent:
    id: awf
    runtime: gvisor
    sudo: true   # required — install step uses sudo

Compiler changes

  • sandbox.go — New AgentRuntime type + Runtime AgentRuntime field on AgentSandboxConfig
  • frontmatter_extraction_security.go — Extracts sandbox.agent.runtime from YAML
  • awf_config.go — New ContainerRuntime string on AWFContainerConfig; emitted as "containerRuntime":"gvisor" in the AWF stdin JSON when set
  • firewall.gogetAgentContainerRuntime() / isGVisorRuntime() helpers
  • copilot_engine_installation.gogenerateGVisorInstallStep(): downloads runsc + containerd-shim-runsc-v1 from storage.googleapis.com/gvisor, installs to /usr/local/bin, runs sudo runsc install, restarts Docker with systemctl restart (not reload — the reload path skips setHostGatewayIP()), verifies with docker run --rm --runtime=runsc
  • nodejs.go — Injects gVisor step before the AWF install step in BuildNpmEngineInstallStepsWithAWF
  • main_workflow_schema.json — Adds runtime enum field to sandbox.agent schema

Compile-time validation

Two incompatible combinations are rejected at compile time:

Combination Error
runtime: gvisor + runner.topology: arc-dind ARC DinD has no access to the sidecar daemon's systemd
runtime: gvisor + sudo: false (default) Install step requires root; must set sudo: true

sudo: true's deprecation warning/error is suppressed when runtime: gvisor is set — gVisor fundamentally requires root so the warning would be misleading.



✨ PR Review Safe Output Test - Run 29116481336

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

💥 [THE END] — Illustrated by Smoke Claude · 78.6 AIC · ⌖ 31.5 AIC · ⊞ 8.4K ·
Comment /smoke-claude to run again

Copilot AI and others added 2 commits July 10, 2026 17:02
… AWF config emission

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
… log message

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copilot AI changed the title [WIP] Add gVisor installation script and sandbox.agent.runtime field feat: add sandbox.agent.runtime: gvisor for gVisor container runtime support Jul 10, 2026
Copilot AI requested a review from lpcox July 10, 2026 17:07
Copilot finished work on behalf of lpcox July 10, 2026 17:07
@lpcox lpcox marked this pull request as ready for review July 10, 2026 17:08
Copilot AI review requested due to automatic review settings July 10, 2026 17:08

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.

Pull request overview

Adds compiler support for running AWF agent containers with gVisor.

Changes:

  • Adds sandbox.agent.runtime: gvisor parsing, schema, and validation.
  • Installs and registers runsc before AWF execution.
  • Passes the selected runtime through AWF configuration.
Show a summary per file
File Description
.changeset/minor-gvisor-runtime-support.md Documents the feature.
pkg/parser/schemas/main_workflow_schema.json Defines the runtime field.
pkg/workflow/awf_config.go Emits the AWF runtime configuration.
pkg/workflow/copilot_engine_installation.go Generates the gVisor installation step.
pkg/workflow/firewall.go Adds runtime helpers.
pkg/workflow/frontmatter_extraction_security.go Extracts runtime frontmatter.
pkg/workflow/gvisor_test.go Tests compilation and validation behavior.
pkg/workflow/nodejs.go Inserts gVisor setup into npm-engine workflows.
pkg/workflow/sandbox.go Models agent runtime configuration.
pkg/workflow/sandbox_validation.go Validates runtime combinations.
pkg/workflow/strict_mode_sandbox_validation.go Adjusts strict-mode sudo handling.

Review details

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 11/11 changed files
  • Comments generated: 5
  • Review effort level: Medium

Comment thread pkg/workflow/sandbox_validation.go Outdated
Comment on lines +125 to +127
// gVisor installation requires root access (sudo install, sudo runsc install,
// sudo systemctl restart docker). It cannot be combined with sudo: false.
if agentConfig.NetworkIsolation {
if agent.SudoExplicitlyEnabled {
// Exception: sandbox.agent.runtime: gvisor requires root access for installation,
// so sudo: true is implicitly required and the deprecation warning is suppressed.
if agent.SudoExplicitlyEnabled && agent.Runtime != AgentRuntimeGVisor {
Comment thread pkg/workflow/nodejs.go
Comment on lines +157 to +160
// gVisor must be installed and registered BEFORE AWF starts the agent container.
if isGVisorRuntime(workflowData) {
steps = append(steps, generateGVisorInstallStep())
}
Comment on lines +352 to +356
` URL="https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}"`,
` echo "Downloading runsc for ${ARCH}..."`,
` curl -fsSL "${URL}/runsc" -o /tmp/runsc`,
` curl -fsSL "${URL}/containerd-shim-runsc-v1" -o /tmp/containerd-shim-runsc-v1`,
` sudo install -m 755 /tmp/runsc /usr/local/bin/runsc`,
Comment on lines +552 to +556
agentRuntime := getAgentContainerRuntime(config.WorkflowData)
if awfImageTag != "" || isArcDindTopology(config.WorkflowData) || agentRuntime != "" {
container := &AWFContainerConfig{
ImageTag: awfImageTag,
ImageTag: awfImageTag,
ContainerRuntime: agentRuntime,
@lpcox

lpcox commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

@copilot run pr-finisher skill

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

@github-actions github-actions Bot 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.

Review: sandbox.agent.runtime: gvisor

The overall structure is solid — the compilation flow, validation, and test coverage are well thought out. Three issues need attention before merging:

Blocking:

  1. No binary integrity verification (line 355) — runsc and containerd-shim-runsc-v1 are installed as root without SHA-512 checksum verification. The official GCS bucket publishes .sha512 files alongside the binaries. This is a supply-chain risk.

Non-blocking but important:
2. Unpinned latest URL (line 352) — non-reproducible; a new gVisor release could silently break the workflow or introduce incompatibilities.
3. docker pull hello-world in verification (line 373) — makes an outbound Docker Hub call that may fail in environments with restricted network policies. Use a pre-cached image instead.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 40.8 AIC · ⌖ 4.84 AIC · ⊞ 4.8K

` URL="https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}"`,
` echo "Downloading runsc for ${ARCH}..."`,
` curl -fsSL "${URL}/runsc" -o /tmp/runsc`,
` curl -fsSL "${URL}/containerd-shim-runsc-v1" -o /tmp/containerd-shim-runsc-v1`,

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.

Security: No integrity verification of downloaded binaries

The runsc and containerd-shim-runsc-v1 binaries are downloaded from storage.googleapis.com and immediately installed as root (sudo install -m 755) without verifying their authenticity. The official gVisor release page publishes SHA-512 checksums (runsc.sha512, containerd-shim-runsc-v1.sha512) at the same URL prefix.

This is a supply-chain risk: a MITM or compromised GCS bucket could deliver a malicious binary that runs as root inside the agent container.

Suggested fix:

curl -fsSL "${URL}/runsc" -o /tmp/runsc
curl -fsSL "${URL}/runsc.sha512" -o /tmp/runsc.sha512
sha512sum -c /tmp/runsc.sha512
curl -fsSL "${URL}/containerd-shim-runsc-v1" -o /tmp/containerd-shim-runsc-v1
curl -fsSL "${URL}/containerd-shim-runsc-v1.sha512" -o /tmp/containerd-shim-runsc-v1.sha512
sha512sum -c /tmp/containerd-shim-runsc-v1.sha512

@copilot please address this.

"",
` echo "::group::Install gVisor (runsc)"`,
` ARCH=$(uname -m)`,
` URL="https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}"`,

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.

Reliability: Unpinned latest release URL causes non-reproducible builds

releases/release/latest resolves to whatever is current at workflow run time. A new gVisor release could break the workflow silently (e.g., behavioural changes, removed flags, incompatible runtime version with the installed Docker/containerd).

Consider pinning to a specific release (e.g., release/20240101.0) or, at minimum, documenting the implicit policy here and testing against it in CI.

@copilot please address this.

` echo "::endgroup::"`,
"",
` echo "::group::Verify gVisor works"`,
` docker pull hello-world`,

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.

Reliability: Verification step pulls from Docker Hub, which may be blocked by sandbox network policy

When sandbox.agent.runtime: gvisor is used with a restricted network (network.allowed not including registry-1.docker.io), docker pull hello-world will fail during the install step — before AWF even starts, so the AWF network isolation hasn't taken effect yet. But if the runner is already restricted at the host firewall level, this pulls from an unconfigured external endpoint.

Consider using docker run --rm --runtime=runsc busybox true (or any image guaranteed to be already present on GitHub-hosted runners) instead of pulling hello-world at verification time.

@copilot please address this.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

⚠️ Test Quality Score: 75/100 — Acceptable

Analyzed 10 test(s): 10 design, 0 implementation, 0 violation(s).

📊 Metrics (10 tests)
Metric Value
Analyzed 10 (Go: 10, JS: 0)
✅ Design 10 (100%)
⚠️ Implementation 0 (0%)
Edge/error coverage 5 (50%)
Duplicate clusters 0
Inflation Yes (6.5:1, justified)
🚨 Violations 0

Test Classification

Test File Classification Quality
TestGenerateGVisorInstallStep gvisor_test.go:17 Design — install script content HIGH — 14 assertions verify architecture detection, binary sources, paths, systemctl restart, pre-pull behavior
TestGVisorInstallStepOrderInBuildNpmEngineInstallStepsWithAWF gvisor_test.go:55 Design — step ordering HIGH — verifies gVisor step precedes AWF install step in emitted actions
TestGVisorAWFConfigJSON gvisor_test.go:93 Design — config serialization HIGH — includes error check; verifies containerRuntime: gvisor in output
TestGVisorAWFConfigJSONAbsentByDefault gvisor_test.go:122 Design — negative contract HIGH — includes error check; verifies containerRuntime absent when unconfigured
TestGVisorValidation_ArcDindIncompatible gvisor_test.go:148 Design — error path HIGH — error path asserted; verifies compile-time error for invalid combination
TestGVisorValidation_SudoFalseIncompatible gvisor_test.go:173 Design — error path HIGH — error path asserted; verifies sudo:false + gvisor produces error
TestGVisorValidation_ValidCombination gvisor_test.go:196 Design — positive path HIGH — verifies valid gvisor + sudo:true passes validation
TestGVisorStrictModeSudoTrueSuppressed gvisor_test.go:218 Design — suppression HIGH — verifies sudo:true doesn't trigger warning when runtime:gvisor
TestGVisorFrontmatterExtraction gvisor_test.go:239 Design — E2E compilation VERY HIGH — end-to-end: verifies markdown → compiled YAML includes gVisor install, AWF install, correct config, and step ordering
TestIsGVisorRuntime gvisor_test.go:290 Design — unit + edge cases HIGH — 5 subtests cover nil input, missing config, non-gVisor, disabled agent, valid runtime
✅ Strengths
  • 100% design tests: All 10 tests verify behavioral contracts (install script requirements, step ordering, config serialization, validation rules, E2E compilation).
  • Comprehensive error path coverage: Tests 3–6 and 8 explicitly assert error cases (incompatible configurations, strict mode suppression).
  • Edge case coverage: TestIsGVisorRuntime covers nil, missing config, wrong type, disabled flag.
  • E2E integration test: TestGVisorFrontmatterExtraction validates full compiler pipeline with real markdown → YAML.
  • No coding violations: No mock libraries (testify/assert and require used appropriately), build tag present, all assertions have descriptive messages.
  • Table-driven test: TestIsGVisorRuntime uses t.Run for clean edge case organization.

Verdict

PASSED. 0% implementation tests (threshold: ≤30%). No coding violations.

Note on inflation ratio (6.5:1): The high line ratio is justified for this new feature because (1) comprehensive E2E compilation test with setup/teardown, (2) multiple validation scenarios (arc-dind, sudo constraints), and (3) all production lines are behavioral, not boilerplate.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 14.2 AIC · ⌖ 17.6 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actions github-actions Bot 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.

REQUEST_CHANGES — 3 blocking issues in addition to those already flagged

The existing review comments already called out: unpinned latest download URL (supply chain risk), gVisor-only step injection via the npm path (CodexEngine uncovered), the open/blocked upstream dependency, and the sudo-scope confusion.

This pass adds three more that must be fixed before merge:

Critical / High:

  1. systemctl restart docker on GH-hosted runners — GitHub-hosted ubuntu-* runners do not use systemd; this command will fail with a hard error, making the feature completely broken on the primary target environment. (line 367)
  2. DockerHub pull in install stepdocker pull hello-world is a live network call that will hit the 100-pull/6h anonymous quota on shared runners. Replace with a local runsc --version check. (line 374)
  3. Unvalidated runtime string forwarded downstream — the YAML value is cast to AgentRuntime and emitted as-is into AWF config JSON with no allowlist check. In frontmatter_extraction_security.go of all places, this should be a switch against known constants. (line 272)
Lower-priority observations (non-blocking)
  • Ignored sudo parameter: generateGVisorInstallStep no longer accepts a parameter (correctly), but the test in gvisor_test.go was written when it did — worth confirming tests still compile cleanly.
  • No docker-ready wait after restart: even with a portable restart command, a until docker info; do sleep 1; done loop is needed before docker info --format or any docker run to avoid a race.

🔎 Code quality review by PR Code Quality Reviewer · 49.4 AIC · ⌖ 4.76 AIC · ⊞ 5.4K
Comment /review to run again

` # Docker's SIGHUP reload does NOT call setHostGatewayIP(), so`,
` # --add-host host.docker.internal:host-gateway breaks for any`,
` # container started after a reload-only config change.`,
` sudo systemctl restart docker`,

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.

systemctl restart docker will fail on GitHub-hosted runners: these runners do not use systemd, so this command errors with System has not been booted with systemd — making this feature non-functional on its primary target environment.

💡 Suggested fix

Use a portable restart + readiness wait:

# Portable full restart (not SIGHUP/reload) on both systemd and non-systemd runners
sudo service docker restart 2>/dev/null || sudo systemctl restart docker

# Wait for daemon to be ready
timeout 30 bash -c 'until docker info &>/dev/null; do sleep 1; done'

service docker restart triggers a full daemon restart (not a SIGHUP reload), so the setHostGatewayIP() concern from the PR description still applies correctly here. The readiness loop also addresses the race condition where docker run immediately follows a restart.

"",
` echo "::group::Verify gVisor works"`,
` docker pull hello-world`,
` docker run --rm --runtime=runsc hello-world`,

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.

docker pull hello-world / docker run hello-world in the install step will hit DockerHub rate limits or fail in air-gapped environments, breaking every gVisor-enabled workflow on shared runners that exhaust the anonymous pull quota (100 pulls/6 h per IP).

💡 Suggested fix

Verify gVisor using only local tools — no image pull needed:

# Confirm runsc binary is functional — no network required
runsc --version
runsc --help &>/dev/null && echo "runsc OK"

If a container smoke-test is truly needed, use the Docker daemon's built-in hello-world-equivalent with a scratch image that can be baked into the runner, or skip it and rely on runsc --version + docker info --format '{{.Runtimes}}' to confirm registration.

// Extract runtime (container runtime for the agent container)
if runtimeVal, hasRuntime := agentObj["runtime"]; hasRuntime {
if runtimeStr, ok := runtimeVal.(string); ok {
agentConfig.Runtime = AgentRuntime(runtimeStr)

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.

Unvalidated runtime string is passed downstream without allowlist check: any arbitrary string from the workflow YAML is cast directly to AgentRuntime and forwarded as containerRuntime in the AWF config JSON. If AWF passes this value to Docker as --runtime=<value>, this is an injection vector.

💡 Suggested fix

Validate against the allowlist of known AgentRuntime constants before accepting the value:

if runtimeStr, ok := runtimeVal.(string); ok {
    rt := AgentRuntime(runtimeStr)
    switch rt {
    case AgentRuntimeGVisor:
        agentConfig.Runtime = rt
        frontmatterExtractionSecurityLog.Printf("Extracted sandbox.agent.runtime: %s", runtimeStr)
    default:
        // Return a parse/validation error — unknown runtime
        return nil, fmt.Errorf("sandbox.agent.runtime: unsupported value %q (supported: gvisor)", runtimeStr)
    }
}

This is particularly important in a file named frontmatter_extraction_security.go.

@github-actions

Copy link
Copy Markdown
Contributor

Design Decision Gate — ADR Required

This PR makes significant changes to core business logic (470 new lines in pkg/workflow/) but does not have a linked Architecture Decision Record (ADR).

Draft ADR committed: docs/adr/44796-gvisor-runtime-for-agent-containers.md — review and complete it before merging.

This PR cannot merge until an ADR is linked in the PR body.

What to do next
  1. Review the draft ADR committed to your branch — it was generated from the PR diff

  2. Complete the missing sections — add context the AI could not infer, refine the decision rationale, and list real alternatives you considered

  3. Commit the finalized ADR to docs/adr/ on your branch

  4. Reference the ADR in this PR body by adding a line such as:

    ADR: ADR-44796: Use gVisor as an Optional Container Runtime for Agent Containers

Once an ADR is linked in the PR body, this gate will re-run and verify the implementation matches the decision.

Why ADRs Matter

ADRs create a searchable, permanent record of why the codebase looks the way it does. Future contributors (and your future self) will thank you.

Michael Nygard ADR Format Reference

An ADR must contain these four sections to be considered complete:

  • Context — What is the problem? What forces are at play?
  • Decision — What did you decide? Why?
  • Alternatives Considered — What else could have been done?
  • Consequences — What are the trade-offs (positive and negative)?

All ADRs are stored in docs/adr/ as Markdown files numbered by PR number (e.g., 0042-use-postgresql.md for PR #42).

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · 63.4 AIC · ⌖ 13.3 AIC · ⊞ 8.5K ·
Comment /review to run again

@github-actions github-actions Bot 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.

Skills-Based Review 🧠

Applied /tdd and /codebase-design — requesting changes on security and correctness issues.

📋 Key Themes & Highlights

Key Issues

  • Supply-chain gap: The gVisor binaries are downloaded with curl -fsSL but no checksum verification, unlike the AWF install step which explicitly cites SHA256 verification as a supply-chain defence. This is the highest-priority issue since runsc runs under sudo.
  • Unpinned latest version: Using release/latest/ makes compiled workflows non-reproducible and fragile across gVisor releases. Should pin like AWF does.
  • Silent unknown runtime: An invalid runtime: sometypo is silently ignored (schema catches it, but the Go extractor doesn't). A defensive check at extraction time would make the system more robust.
  • systemctl assumption: No validation that the runner has systemd. The arc-dind guard is good, but generic self-hosted runners without systemd would hit an opaque mid-job failure.

Positive Highlights

  • ✅ Excellent test coverage (329 lines) — install step content, step ordering, AWF config JSON emission, validation errors, end-to-end compilation all covered.
  • ✅ The systemctl restart vs reload distinction is well-documented with an explicit comment explaining the setHostGatewayIP() breakage.
  • ✅ Clean separation: isGVisorRuntime() helper keeps all callers from duplicating nil/disabled checks.
  • ✅ Suppressing the sudo: true deprecation warning for gVisor is the right UX call — documented clearly in the PR body and code.
  • ✅ Compile-time validation for arc-dind and sudo:false is exactly the right approach for safety.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 73 AIC · ⌖ 5.05 AIC · ⊞ 6.6K
Comment /matt to run again

` ARCH=$(uname -m)`,
` URL="https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}"`,
` echo "Downloading runsc for ${ARCH}..."`,
` curl -fsSL "${URL}/runsc" -o /tmp/runsc`,

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.

[/tdd] No checksum verification for downloaded binaries — a compromised or MITM-replaced runsc would execute with sudo.

The AWF install step explicitly calls out SHA256 verification as a supply-chain protection. The gVisor curl downloads have no equivalent check.

💡 Suggested fix

gVisor publishes signed checksums at ${URL}/runsc.sha512. Add verification before the sudo install:

curl -fsSL "${URL}/runsc" -o /tmp/runsc
curl -fsSL "${URL}/runsc.sha512" -o /tmp/runsc.sha512
sha512sum -c /tmp/runsc.sha512
curl -fsSL "${URL}/containerd-shim-runsc-v1" -o /tmp/containerd-shim-runsc-v1
curl -fsSL "${URL}/containerd-shim-runsc-v1.sha512" -o /tmp/containerd-shim-runsc-v1.sha512
sha512sum -c /tmp/containerd-shim-runsc-v1.sha512

This mirrors the supply-chain protection described in generateAWFInstallationStep.

@copilot please address this.

// Extract runtime (container runtime for the agent container)
if runtimeVal, hasRuntime := agentObj["runtime"]; hasRuntime {
if runtimeStr, ok := runtimeVal.(string); ok {
agentConfig.Runtime = AgentRuntime(runtimeStr)

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.

[/codebase-design] Unknown runtime values are silently accepted — an invalid runtime: sometypo will produce no error and silently have no effect at runtime.

The JSON schema rejects unknown values via the enum, but the extraction layer performs no parallel check, so workflows bypassing schema validation (e.g., tests) could pass an invalid runtime undetected.

💡 Suggested fix

Add a check after extracting the value:

switch agentConfig.Runtime {
case AgentRuntimeGVisor, "":
    // valid
default:
    return nil, fmt.Errorf("sandbox.agent.runtime: unknown value %q; valid values: gvisor", runtimeStr)
}

This makes the Go layer the authoritative validator rather than relying solely on the JSON schema.

@copilot please address this.

` # Docker's SIGHUP reload does NOT call setHostGatewayIP(), so`,
` # --add-host host.docker.internal:host-gateway breaks for any`,
` # container started after a reload-only config change.`,
` sudo systemctl restart docker`,

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.

[/tdd] systemctl restart docker will fail in workflows that don't run on systemd-based GitHub-hosted runners (e.g., container jobs, self-hosted runners without systemd). There's no test or validation guard for this runner requirement.

A failed Docker restart leaves the runner in a broken state mid-job rather than surfacing a clear compile-time or early-runtime error.

💡 Options
  1. Compile-time check: Add a runner.os: linux (or similar) guard in sandbox_validation.go, parallel to the arc-dind check.
  2. Runtime fallback: Wrap the restart in a check:
    if command -v systemctl &>/dev/null && systemctl is-system-running &>/dev/null; then
      sudo systemctl restart docker
    else
      echo "::error::gVisor requires systemd to restart Docker. Use a GitHub-hosted Linux runner."
      exit 1
    fi

Option 1 is preferred — fail fast at compile time with a clear message.

@copilot please address this.

"",
` echo "::group::Install gVisor (runsc)"`,
` ARCH=$(uname -m)`,
` URL="https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}"`,

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.

[/tdd] Downloading from latest rather than a pinned version makes this step non-reproducible and prone to silent breakage when gVisor releases incompatible changes.

Pinning to a specific release version (stored as a constant next to DefaultFirewallVersion) makes builds reproducible and avoids unexpected failures after upstream releases.

💡 Suggested approach
const defaultGVisorVersion = "20240520" // update as needed

// in sandbox.go or constants
const DefaultGVisorVersion = defaultGVisorVersion

Then in the install step:

URL="(storage.googleapis.com/redacted)

with GVISOR_VERSION injected at compile time from the constant, similar to how AWF version is handled.

@copilot please address this.

engine: copilot
strict: false
network:
allowed:

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.

[/tdd] TestGVisorStrictModeSudoTrueSuppressed asserts no new warnings are added but uses GetWarningCount() without verifying the method is not a stub returning 0. If GetWarningCount always returns 0, this test passes vacuously.

Consider also asserting that sudo:true WITHOUT runtime:gvisor does increment the warning count — this negative case makes the suppression logic meaningful rather than just checking the happy path.

💡 Suggested addition
// Baseline: sudo:true without gvisor should produce a warning
sandboxConfigWithoutGVisor := &SandboxConfig{
    Agent: &AgentSandboxConfig{
        ID: "awf",
        SudoExplicitlyEnabled: true,
    },
}
compilerBaseline := NewCompiler()
compilerBaseline.strictMode = false
_ = compilerBaseline.validateStrictSandboxCustomization(sandboxConfigWithoutGVisor)
assert.Greater(t, compilerBaseline.GetWarningCount(), 0, "sudo:true without gvisor must emit a warning")

@copilot please address this.

Copilot AI and others added 3 commits July 10, 2026 17:42
…or path, gate containerRuntime by version

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
…false, no deprecation suppression

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>

Copilot AI commented Jul 10, 2026

Copy link
Copy Markdown
Contributor Author

@copilot run pr-finisher skill

Done. Four review threads addressed in commit fix: address review threads:

  1. sandbox_validation.go — Removed the sudo: true requirement for gVisor. The install step uses shell-level sudo commands independently of sandbox.agent.sudo, so gVisor now works with sudo: false (network-isolation mode).

  2. strict_mode_sandbox_validation.go — Removed the gVisor exception from the sudo: true deprecation check. The existing strict-mode error/warning applies to sudo: true regardless of runtime.

  3. codex_engine.go — Added the gVisor install step before the AWF install step in CodexEngine.GetInstallationSteps, matching the existing npm/Copilot path in nodejs.go.

  4. awf_config.go — Gated containerRuntime emission behind AWFContainerRuntimeMinVersion (v0.28.0 placeholder, pending gh-aw-firewall#6093). With the default AWF version (v0.27.29) the field is not emitted; it will be emitted once the constant is updated to the actual release version.

ADR also updated to reflect all of the above.

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🚀 Smoke Antigravity MISSION COMPLETE! Antigravity has spoken. ✨

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

🚀 Smoke Pi MISSION COMPLETE! Pi delivered. 🥧

@github-actions

github-actions Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Smoke Copilot Small failed. Please review the logs for details.

@github-actions

Copy link
Copy Markdown
Contributor

📰 BREAKING: Smoke Copilot - AOAI (Entra) is now investigating this pull request. Sources say the story is developing...

@github-actions

Copy link
Copy Markdown
Contributor

📰 BREAKING: Smoke Copilot is now investigating this pull request. Sources say the story is developing...

@github-actions

Copy link
Copy Markdown
Contributor

📰 BREAKING: Smoke Copilot - AOAI (apikey) is now investigating this pull request. Sources say the story is developing...

@github-actions

Copy link
Copy Markdown
Contributor

Agent Container Tool Check

Tool Status Version
bash 5.2.21
sh available
git 2.54.0
jq 1.7
yq 4.53.3
curl 8.5.0
gh 2.96.0
node 22.23.1
python3 3.11.15 (PyPy 7.3.23)
go 1.24.13
java 10.0.301
dotnet 10.0.301

Result: 12/12 tools available ✅

Overall Status: PASS

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "awmgmcpg"

See Network Configuration for more information.

🔧 Tool validation by Agent Container Smoke Test · 12.4 AIC · ⌖ 7.82 AIC · ⊞ 4.6K ·
Comment /smoke-test-tools to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke test

  • ✅ GitHub CLI
  • ❌ Web fetch
  • ✅ File write
  • ✅ Bash verify
  • ❌ Build
  • Overall: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • proxy.golang.org

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "proxy.golang.org"

See Network Configuration for more information.

🥧 Smoke Pi — Powered by Pi · 1.92 AIC · ⌖ 3.88 AIC · ⊞ 4.4K ·
Comment /smoke-pi to run again

@github-actions

Copy link
Copy Markdown
Contributor

Smoke Test Results (Gemini):

  1. GitHub MCP: ✅
  2. Web Fetch: ✅
  3. File Writing: ✅
  4. Bash Tool: ✅
  5. Build gh-aw: ❌
    Overall status: FAIL

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • localhost

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "localhost"

See Network Configuration for more information.

Smoke Gemini — Powered by Gemini · 11.5 AIC · ⌖ 6.88 AIC · ⊞ 9.3K ·
Comment /smoke-gemini to run again

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

Quiet checks hum once
GitHub lights the terminal
Smoke drifts into green

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex · ⌖ 4.3 AIC ·
Comment /smoke-codex to run again

@github-actions

Copy link
Copy Markdown
Contributor

🧪 Smoke Test: Claude — Run 29116481336

Core #1-12: 1️⃣✅ 2️⃣✅ 3️⃣✅ 4️⃣✅ 5️⃣✅ 6️⃣✅ 7️⃣✅ 8️⃣✅ 9️⃣✅ 🔟✅ 1️⃣1️⃣✅ 1️⃣2️⃣✅

PR Review #13-19: 13✅ 14✅ 15✅ 16✅ 17✅ 18⚠️(allowed-files config blocks push) 19⚠️(no safe PR)

Overall: PARTIAL — all executed tests passed; #18 & #19 skipped.

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

💥 [THE END] — Illustrated by Smoke Claude · 78.6 AIC · ⌖ 31.5 AIC · ⊞ 8.4K ·
Comment /smoke-claude to run again

@github-actions github-actions Bot 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.

💥 Automated smoke test review - all systems nominal!

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • android.clients.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "android.clients.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

💥 [THE END] — Illustrated by Smoke Claude · 78.6 AIC · ⌖ 31.5 AIC · ⊞ 8.4K
Comment /smoke-claude to run again

@@ -0,0 +1,52 @@
# ADR-44796: Use gVisor (runsc) as an Optional Container Runtime for Agent Containers

**Date**: 2026-07-10

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.

Smoke-test review: consider linking the referenced issue gh-aw-firewall#6093 for traceability once it lands.


### Context

Agent containers in gh-aw run under a standard Docker runtime backed by Linux cgroups and namespaces. This provides process-level isolation but leaves the host kernel's system call surface fully accessible to agent code. Any vulnerability in a system call handler can be exploited to escape the container. For higher-assurance workloads, operators need a runtime that interposes on all syscalls and runs them through a user-space kernel, substantially reducing the kernel attack surface exposed to untrusted agent code. The AWF daemon already supports `container.containerRuntime: "gvisor"` in its stdin config (gh-aw-firewall#6093), but the compiler had no way to emit that setting or install the runtime on the runner. This PR wires up the missing compiler side: a new `sandbox.agent.runtime` frontmatter field, an auto-generated pre-agent gVisor install step, and the AWF config JSON plumbing that passes `containerRuntime: "gvisor"` to AWF.

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.

Smoke-test review: the AWFContainerRuntimeMinVersion placeholder (v0.28.0) should be tracked so it is updated when the firewall release ships.

@github-actions

Copy link
Copy Markdown
Contributor

PR: feat: add sandbox.agent.runtime: gvisor for gVisor container runtime support
1 ❌ 2 ✅ 3 ✅ 4 ✅
5 ❌ 6 ✅ 7 ✅ 8 ✅
9 ✅ 10 ✅ 11 ✅ 12 ✅
13 ✅ 14 ❌ 15 ✅ 16 ✅
Overall: FAIL
Author: @Copilot
Assignees: @lpcox, @Copilot

Warning

Firewall blocked 5 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

📰 BREAKING: Report filed by Smoke Copilot · 91.6 AIC · ⌖ 4.67 AIC · ⊞ 19.8K ·
Comment /smoke-copilot to run again
Add label smoke to run again

@github-actions

Copy link
Copy Markdown
Contributor

Comment Memory

Circuits hum at dawn,
Bots rehearse their tiny proofs,
Merge winds test the pines.

Note

This comment is managed by comment memory.

It stores persistent context for this thread in the code block at the top of this comment.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

Warning

Firewall blocked 5 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

📰 BREAKING: Report filed by Smoke Copilot · 91.6 AIC · ⌖ 4.67 AIC · ⊞ 19.8K ·
Comment /smoke-copilot to run again
Add label smoke to run again

@github-actions github-actions Bot 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.

Smoke validation ran successfully; inline notes are informational only.

Warning

Firewall blocked 5 domains

The following domains were blocked by the firewall during workflow execution:

  • accounts.google.com
  • clients2.google.com
  • contentautofill.googleapis.com
  • safebrowsingohttpgateway.googleapis.com
  • www.google.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "accounts.google.com"
    - "clients2.google.com"
    - "contentautofill.googleapis.com"
    - "safebrowsingohttpgateway.googleapis.com"
    - "www.google.com"

See Network Configuration for more information.

📰 BREAKING: Report filed by Smoke Copilot · 91.6 AIC · ⌖ 4.67 AIC · ⊞ 19.8K
Comment /smoke-copilot to run again
Add label smoke to run again

@lpcox lpcox merged commit 6f0c741 into main Jul 10, 2026
230 of 233 checks passed
@lpcox lpcox deleted the copilot/feat-add-gvisor-install-script branch July 10, 2026 19:33
@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review comments if any, and rerun checks once the branch is up to date.

Generated by 👨‍🍳 PR Sous Chef · 7.7 AIC · ⌖ 4.78 AIC · ⊞ 7.5K ·
Comment /souschef to run again

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.

feat: add gVisor (runsc) installation script and sandbox.agent.runtime frontmatter field

4 participants