Skip to content

[awf] ARC/DinD chroot: auto-stage runner binary and critical /etc files for split-filesystem Docker hosts#3852

Open
Copilot wants to merge 3 commits into
mainfrom
copilot/awf-arc-dind-chroot-fix
Open

[awf] ARC/DinD chroot: auto-stage runner binary and critical /etc files for split-filesystem Docker hosts#3852
Copilot wants to merge 3 commits into
mainfrom
copilot/awf-arc-dind-chroot-fix

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 26, 2026

ARC/DinD split-filesystem setups still required manual bootstrap to make chrooted agent runs work (copying binaries and core /etc files into daemon-visible storage). This change removes those manual workarounds by auto-staging required assets when --docker-host-path-prefix targets a shared /tmp path.

  • Problem surface reduced in ARC/DinD mode

    • Added daemon-visible staging for:
      • invoking CLI binary (e.g. copilot, claude, codex)
      • /etc/passwd
      • /etc/group
      • generated chroot /etc/hosts
    • Staging is activated for split-filesystem /tmp host-prefix scenarios.
  • Compose volume generation now supports staged artifacts

    • Introduced src/services/agent-volumes/docker-host-staging.ts for:
      • host-prefix detection
      • staging root/path management
      • file copy + permission handling
      • command binary extraction from agentCommand
    • Updated agent volume builders to mount staged files instead of direct runner paths where applicable.
  • Entrypoint chroot bootstrap now consumes staged runner binary

    • Added AWF_STAGED_RUNNER_BINARY_NAME environment wiring.
    • In chroot mode, entrypoint copies /tmp/awf-runner-bin/<binary> into /tmp/awf-lib/<binary> and prepends /tmp/awf-lib to chroot PATH.
    • This eliminates manual runner→daemon binary copying into /usr/local/bin.
  • Host-path prefix safety improvement

    • translateBindMountHostPath now avoids re-prefixing paths already under the configured docker host prefix.
  • Documentation

    • Added docs/arc-dind.md with ARC/DinD behavior and explicit remaining Node.js requirement for Copilot CLI, including recommended base image guidance.
// tool-specific environment wiring (ARC/DinD staged binary path)
if (stagedBinaryName && shouldUseDockerHostStaging(config.dockerHostPathPrefix)) {
  environment.AWF_STAGED_RUNNER_BINARY_NAME = stagedBinaryName;
}

Copilot AI changed the title [WIP] Fix manual workarounds for ARC/DinD chroot mode [awf] ARC/DinD chroot: auto-stage runner binary and critical /etc files for split-filesystem Docker hosts May 26, 2026
Copilot AI requested a review from lpcox May 26, 2026 14:00
Copilot finished work on behalf of lpcox May 26, 2026 14:00
@lpcox lpcox marked this pull request as ready for review May 26, 2026 14:34
Copilot AI review requested due to automatic review settings May 26, 2026 14:34
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 26, 2026

Documentation Preview

Documentation build failed for this PR. View logs.

Built from commit a9a09ba

@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 96.58% 96.52% 📉 -0.06%
Statements 96.44% 96.39% 📉 -0.05%
Functions 98.19% 98.21% 📈 +0.02%
Branches 90.86% 90.51% 📉 -0.35%
📁 Per-file Coverage Changes (4 files)
File Lines (Before → After) Statements (Before → After)
src/services/agent-volumes/workspace-mounts.ts 100.0% → 97.8% (-2.23%) 100.0% → 97.8% (-2.23%)
src/services/agent-volumes/hosts-file.ts 79.5% → 80.5% (+1.00%) 81.0% → 81.8% (+0.86%)
src/services/host-path-prefix.ts 87.0% → 88.0% (+1.05%) 88.9% → 89.7% (+0.77%)
src/config-writer.ts 89.3% → 90.9% (+1.65%) 89.3% → 90.9% (+1.65%)
✨ New Files (1 files)
  • src/services/agent-volumes/docker-host-staging.ts: 85.4% lines

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

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves ARC + DinD “split runner/daemon filesystem” support for AWF chroot mode by automatically staging a few runner-side artifacts into a daemon-visible shared /tmp location when --docker-host-path-prefix points under /tmp, reducing manual bootstrap steps.

Changes:

  • Adds a Docker-host staging helper to copy /etc/passwd, /etc/group, the invoked CLI binary, and the generated chroot /etc/hosts into a shared staging root under the configured /tmp prefix.
  • Updates agent volume generation and tool-specific environment wiring to use staged artifacts and to pass AWF_STAGED_RUNNER_BINARY_NAME to the container for chroot bootstrap.
  • Updates bind-mount host-path translation to avoid re-prefixing mounts that are already under the configured docker-host prefix; adds docs for ARC/DinD behavior.
Show a summary per file
File Description
src/services/host-path-prefix.ts Prevents double-prefixing bind-mount sources already under the configured prefix.
src/services/agent-volumes/workspace-mounts.ts Stages the invoked CLI binary into a docker-host-visible location and mounts it into the agent.
src/services/agent-volumes/volume-builder.ts Passes config into /etc mounts to enable staging behavior.
src/services/agent-volumes/hosts-file.ts Writes generated chroot /etc/hosts under the staging root when enabled.
src/services/agent-volumes/etc-mounts.ts Stages /etc/passwd and /etc/group under the shared /tmp prefix when enabled.
src/services/agent-volumes/docker-host-staging.ts New staging utility module (prefix detection, stage root management, file copy, command binary extraction).
src/services/agent-volumes-mounts.test.ts Adds coverage asserting staged mounts under a /tmp docker-host-path-prefix.
src/services/agent-environment/tool-specific-environment.ts Wires AWF_STAGED_RUNNER_BINARY_NAME for chroot to consume staged runner binary.
src/services/agent-environment/excluded-vars.ts Excludes AWF_STAGED_RUNNER_BINARY_NAME from host env passthrough.
src/services/agent-environment-runtime.test.ts Adds coverage for AWF_STAGED_RUNNER_BINARY_NAME env wiring in /tmp prefix mode.
docs/arc-dind.md Documents ARC + DinD staging behavior and remaining Node.js requirement.
containers/agent/entrypoint.sh Consumes AWF_STAGED_RUNNER_BINARY_NAME to copy staged binary into chroot PATH under /tmp/awf-lib.

Copilot's findings

Tip

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

  • Files reviewed: 12/12 changed files
  • Comments generated: 5

Comment thread containers/agent/entrypoint.sh Outdated
Comment on lines +569 to +573
STAGED_RUNNER_BINARY_CHROOT=""
if [ -n "${AWF_STAGED_RUNNER_BINARY_NAME:-}" ] && [ -f "/tmp/awf-runner-bin/${AWF_STAGED_RUNNER_BINARY_NAME}" ]; then
if mkdir -p /host/tmp/awf-lib 2>/dev/null; then
if cp "/tmp/awf-runner-bin/${AWF_STAGED_RUNNER_BINARY_NAME}" "/host/tmp/awf-lib/${AWF_STAGED_RUNNER_BINARY_NAME}" 2>/dev/null && \
chmod +x "/host/tmp/awf-lib/${AWF_STAGED_RUNNER_BINARY_NAME}" 2>/dev/null; then
Comment on lines +63 to +66
const hostsRootDir = shouldUseDockerHostStaging(config.dockerHostPathPrefix)
? getDockerHostStageRoot(config)
: config.workDir;
const chrootHostsDir = fs.mkdtempSync(path.join(hostsRootDir, 'chroot-'));
Comment on lines +34 to +48
export function stageHostFile(config: WrapperConfig, sourcePath: string, relativeTargetPath: string, mode = 0o644): string | undefined {
try {
if (!fs.statSync(sourcePath).isFile()) {
return undefined;
}
} catch {
return undefined;
}

try {
const stageRoot = getDockerHostStageRoot(config);
const targetPath = path.join(stageRoot, relativeTargetPath.replace(/^\/+/, ''));
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.copyFileSync(sourcePath, targetPath);
fs.chmodSync(targetPath, mode);
Comment on lines +20 to +23
const stagedGroupPath = stageHostFile(config, '/etc/group', 'etc/group');
mounts.push(`${stagedPasswdPath || '/etc/passwd'}:/host/etc/passwd:ro`);
mounts.push(`${stagedGroupPath || '/etc/group'}:/host/etc/group:ro`);

Comment on lines +57 to +70
function resolveBinaryPath(binaryName: string): string | undefined {
if (!binaryName) {
return undefined;
}
const pathEntries = (process.env.PATH || '').split(path.delimiter).filter(Boolean);
for (const entry of pathEntries) {
const candidate = path.join(entry, binaryName);
try {
const stat = fs.statSync(candidate);
if (stat.isFile()) {
return candidate;
}
} catch {
// Keep scanning PATH entries.
@github-actions

This comment has been minimized.

@lpcox
Copy link
Copy Markdown
Collaborator

lpcox commented May 26, 2026

@copilot address review feedback and make sure that all docs, specs, and schema are consistent with the implementation

Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 26, 2026

@copilot address review feedback and make sure that all docs, specs, and schema are consistent with the implementation

Addressed in a929b96. I fixed the review feedback around staged-path validation, staged runner binary safety, stale chroot-host staging cleanup, executable binary resolution, and the /etc/passwd//etc/group fallback behavior, and I updated the related docs/spec/schema text to match the implementation.

Copilot finished work on behalf of lpcox May 26, 2026 14:59
@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Coverage Regression Detected

This PR decreases test coverage. Please add tests to maintain coverage levels.

Overall Coverage

Metric Base PR Delta
Lines 96.58% 96.48% 📉 -0.10%
Statements 96.44% 96.35% 📉 -0.09%
Functions 98.19% 98.22% 📈 +0.03%
Branches 90.86% 90.54% 📉 -0.32%
📁 Per-file Coverage Changes (4 files)
File Lines (Before → After) Statements (Before → After)
src/services/agent-volumes/workspace-mounts.ts 100.0% → 96.3% (-3.71%) 100.0% → 96.3% (-3.71%)
src/config-writer.ts 89.3% → 90.9% (+1.65%) 89.3% → 90.9% (+1.65%)
src/services/host-path-prefix.ts 87.0% → 89.3% (+2.33%) 88.9% → 90.6% (+1.74%)
src/services/agent-volumes/hosts-file.ts 79.5% → 82.1% (+2.66%) 81.0% → 83.0% (+2.10%)
✨ New Files (1 files)
  • src/services/agent-volumes/docker-host-staging.ts: 87.2% lines

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

const stageRoot = '/tmp/gh-aw/awf-docker-host-stage';
const staleDir = path.join(stageRoot, 'chroot-stale');
fs.mkdirSync(staleDir, { recursive: true });
fs.writeFileSync(path.join(staleDir, 'hosts'), '127.0.0.1 localhost\n');
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Smoke Test: Copilot BYOK (Offline) Mode

Test Result
GitHub MCP (list PRs) ✅ PR #3827 returned
GitHub.com connectivity ⚠️ N/A (pre-step output not expanded)
File write/read ⚠️ N/A (pre-step output not expanded)
BYOK inference (this response)

Running in BYOK offline mode (COPILOT_OFFLINE=true) via api-proxy → api.githubcopilot.com

Author: @Copilot | Assignees: @lpcox, @Copilot

Overall: PARTIAL PASS (BYOK inference + MCP confirmed ✅; pre-step variables not expanded)

🔑 BYOK report filed by Smoke Copilot BYOK

@github-actions
Copy link
Copy Markdown
Contributor

🔥 Smoke Test Results

Test Status
GitHub MCP (list PRs)
GitHub.com connectivity
File write/read

Overall: PASS

PR: [awf] ARC/DinD chroot: auto-stage runner binary and critical /etc files for split-filesystem Docker hosts
Author: @Copilot · Assignees: @lpcox, @Copilot

📰 BREAKING: Report filed by Smoke Copilot

@github-actions
Copy link
Copy Markdown
Contributor

[awf] ARC/DinD chroot: auto-stage runner binary and critical /etc files for split-filesystem Docker hosts ✅
refactor: split server.proxy.test.js into 6 focused test modules ✅
Disambiguate internal test-helper exports across six modules ✅
PASS

Warning

Firewall blocked 1 domain

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

  • registry.npmjs.org

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

network:
  allowed:
    - defaults
    - "registry.npmjs.org"

See Network Configuration for more information.

🔮 The oracle has spoken through Smoke Codex

@github-actions
Copy link
Copy Markdown
Contributor

Chroot Runtime Version Comparison

Runtime Host Version Chroot Version Match?
Python Python 3.12.13 Python 3.12.3
Node.js v24.15.0 v22.22.3
Go go1.22.12 go1.22.12

Result: FAILED — Python and Node.js versions differ between host and chroot.

Tested by Smoke Chroot

@github-actions
Copy link
Copy Markdown
Contributor

Gemini Smoke Test Results

  • PR Titles: Could not retrieve (MCP tools missing)
  • GitHub MCP Testing: ❌
  • GitHub.com Connectivity: ❌
  • File Writing Testing: ✅
  • Bash Tool Testing: ✅

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.

💎 Faceted by Smoke Gemini

@github-actions
Copy link
Copy Markdown
Contributor

🏗️ Build Test Suite Results

Ecosystem Project Build/Install Tests Status
Bun elysia 1/1 passed ✅ PASS
Bun hono 1/1 passed ✅ PASS
C++ fmt N/A ✅ PASS
C++ json N/A ✅ PASS
Deno oak N/A 1/1 passed ✅ PASS
Deno std N/A 1/1 passed ✅ PASS
.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 All passed ✅ PASS
Node.js execa All passed ✅ PASS
Node.js p-limit All passed ✅ PASS
Rust fd 1/1 passed ✅ PASS
Rust zoxide 1/1 passed ✅ PASS

Overall: 8/8 ecosystems passed — ✅ PASS

Generated by Build Test Suite for issue #3852 · sonnet46 1.1M ·

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test Results

Check Result
Redis PING ❌ No response
PostgreSQL pg_isready ❌ No response
PostgreSQL SELECT 1 ❌ Failed

Overall: FAILhost.docker.internal is not reachable from this environment. Service containers are unavailable.

🔌 Service connectivity validated by Smoke Services

@github-actions
Copy link
Copy Markdown
Contributor

Smoke Test: Claude Engine — ✅ PASS

Total: PASS

💥 [THE END] — Illustrated by Smoke Claude

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.

[awf] ARC/DinD: chroot mode still requires 6 manual workarounds on v0.75.4

4 participants