Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
4b5da56
Set runtime to docker-sbx in smoke-docker-sbx workflow frontmatter
lpcox Jul 12, 2026
7c2350e
Add sudo: true to sandbox.agent in smoke-docker-sbx workflows
lpcox Jul 12, 2026
a93dacb
compiled workflows
lpcox Jul 12, 2026
335c310
Merge branch 'main' into set-docker-sbx-runtime
lpcox Jul 12, 2026
2b0d69f
Fix sbx create crash: use pipe stdin instead of ignore
lpcox Jul 12, 2026
84831fe
Build AWF from source in smoke-docker-sbx-build-test to test sbx fix
lpcox Jul 12, 2026
46a5b90
Fix sbx auth: preserve XDG_CONFIG_HOME for credential lookup
lpcox Jul 12, 2026
021c52f
compiled workflows
lpcox Jul 12, 2026
fba6408
Merge branch 'set-docker-sbx-runtime' of https://github.com/githubnex…
lpcox Jul 12, 2026
2b4fff1
compiled workflows
lpcox Jul 12, 2026
5f2d9b4
chore: build AWF from source in docker-sbx lock files (stdin pipe fix)
lpcox Jul 12, 2026
0500837
fix: include containers/ dir in source build install
lpcox Jul 12, 2026
4f3e977
fix(sbx): use process.env instead of custom env for sbx create
lpcox Jul 12, 2026
dfafc49
fix(sbx): don't set DOCKER_SANDBOXES_PROXY during sbx create
lpcox Jul 12, 2026
58afd7a
fix(sbx): inherit process.env for sbx create (management command)
lpcox Jul 12, 2026
d4e7e3f
fix(sbx): remove XDG_CONFIG_HOME during sbx create
lpcox Jul 12, 2026
7de0696
chore: update smoke-docker-sbx.lock.yml to build AWF from source
lpcox Jul 12, 2026
591be79
compiled workflows
lpcox Jul 13, 2026
fa3d909
compiled workflows
lpcox Jul 13, 2026
4842502
compiled workflows
lpcox Jul 13, 2026
4d2fb0f
chore: build AWF from source in smoke-docker-sbx.lock.yml
lpcox Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 140 additions & 15 deletions .github/workflows/smoke-docker-sbx-build-test.lock.yml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .github/workflows/smoke-docker-sbx-build-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ safe-outputs:
run-failure: "🐳🏗️ [{workflow_name}]({run_url}) reports {status}. Docker sbx build compatibility issue detected."
timeout-minutes: 30
sandbox:
agent:
id: awf
runtime: docker-sbx
sudo: true
mcp:
version: v0.3.32
strict: false
Expand Down
155 changes: 140 additions & 15 deletions .github/workflows/smoke-docker-sbx-claude.lock.yml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .github/workflows/smoke-docker-sbx-claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ safe-outputs:
run-failure: "🐳🟣 [{workflow_name}]({run_url}) reports {status}. Docker sbx + Claude compatibility issue detected."
timeout-minutes: 15
sandbox:
agent:
id: awf
runtime: docker-sbx
sudo: true
mcp:
version: v0.3.32
strict: false
Expand Down
155 changes: 140 additions & 15 deletions .github/workflows/smoke-docker-sbx-codex.lock.yml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .github/workflows/smoke-docker-sbx-codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ safe-outputs:
run-failure: "🐳🔮 [{workflow_name}]({run_url}) reports {status}. Docker sbx + Codex compatibility issue detected."
timeout-minutes: 15
sandbox:
agent:
id: awf
runtime: docker-sbx
sudo: true
mcp:
version: latest
strict: false
Expand Down
269 changes: 201 additions & 68 deletions .github/workflows/smoke-docker-sbx.lock.yml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions .github/workflows/smoke-docker-sbx.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ safe-outputs:
run-failure: "📰 DEVELOPING STORY: [{workflow_name}]({run_url}) reports {status}. Our correspondents are investigating the incident..."
timeout-minutes: 15
sandbox:
agent:
id: awf
runtime: docker-sbx
sudo: true
mcp:
version: v0.3.32
strict: false
Expand Down
38 changes: 16 additions & 22 deletions src/sbx-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,12 @@ describe('sbx-manager', () => {
process.env.HOME || '/home/runner',
], expect.objectContaining({
input: 'y\n',
env: expect.objectContaining({
DOCKER_SANDBOXES_PROXY: 'http://172.30.0.10:3128',
}),
}));
// XDG_CONFIG_HOME must not reach the sbx CLI
const sbxCreateCallEnv: Record<string, string | undefined> = mockExecaFn.mock.calls[1][2].env;
expect(sbxCreateCallEnv).not.toMatchObject({ XDG_CONFIG_HOME: expect.anything() });
// sbx create must NOT pass a custom env — it inherits process.env so the
// sbx CLI can find daemon credentials. The sandbox interior is isolated
// separately by execInSandbox() which uses sanitizeEnvForSbx().
const sbxCreateCall = mockExecaFn.mock.calls[1][2];
expect(sbxCreateCall.env).toBeUndefined();
});

it('uses SBX_DEFAULT_NAME when no name provided', async () => {
Expand All @@ -126,36 +125,31 @@ describe('sbx-manager', () => {
expect(name).toBe(SBX_DEFAULT_NAME);
});

it('uses default squidPort 3128 when not specified', async () => {
it('does not pass custom env during create (inherits process.env)', async () => {
mockExecaFn
.mockResolvedValueOnce({ exitCode: 0, stdout: '', stderr: '' })
.mockResolvedValueOnce({ exitCode: 0, stdout: 'Created sandbox', stderr: '' });

await createSandbox({ workspaceDir: '/ws', squidIp: '172.30.0.10' });

expect(mockExecaFn).toHaveBeenCalledWith(
'sbx',
expect.arrayContaining(['create']),
expect.objectContaining({
env: expect.objectContaining({ DOCKER_SANDBOXES_PROXY: 'http://172.30.0.10:3128' }),
}),
);
const sbxCreateCall = mockExecaFn.mock.calls[1][2];
expect(sbxCreateCall.env).toBeUndefined();
});

it('uses custom squidPort when specified', async () => {
it('temporarily removes DOCKER_SANDBOXES_PROXY and XDG_CONFIG_HOME during create', async () => {
process.env.DOCKER_SANDBOXES_PROXY = 'http://old-proxy:3128';
process.env.XDG_CONFIG_HOME = '/home/runner';
mockExecaFn
.mockResolvedValueOnce({ exitCode: 0, stdout: '', stderr: '' })
.mockResolvedValueOnce({ exitCode: 0, stdout: 'Created sandbox', stderr: '' });

await createSandbox({ workspaceDir: '/ws', squidIp: '172.30.0.10', squidPort: 8080 });

expect(mockExecaFn).toHaveBeenCalledWith(
'sbx',
expect.arrayContaining(['create']),
expect.objectContaining({
env: expect.objectContaining({ DOCKER_SANDBOXES_PROXY: 'http://172.30.0.10:8080' }),
}),
);
// Both should be restored after create
expect(process.env.DOCKER_SANDBOXES_PROXY).toBe('http://old-proxy:3128');
expect(process.env.XDG_CONFIG_HOME).toBe('/home/runner');
delete process.env.DOCKER_SANDBOXES_PROXY;
delete process.env.XDG_CONFIG_HOME;
});

it('throws when auth check fails (non-zero exit)', async () => {
Expand Down
31 changes: 26 additions & 5 deletions src/sbx-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -157,18 +157,39 @@ export async function createSandbox(config: SbxConfig): Promise<string> {

logger.info(`[sbx] Running: sbx ${args.join(' ')}`);

const env = sanitizeEnvForSbx();
delete env.XDG_CONFIG_HOME;
env.DOCKER_SANDBOXES_PROXY = proxyUrl;
// Do NOT pass a custom `env` to sbx create. The sanitized env (which strips
// vars matching TOKEN, SECRET, KEY, etc.) also strips variables the sbx CLI
// needs internally for credential lookup against the daemon's auth store.
// Since sbx create is a management command that talks to the local daemon
// (not user code running inside the sandbox), inheriting process.env is safe —
// these env vars never enter the sandbox interior. The sandbox interior's env
// is controlled separately by execInSandbox() which uses sanitizeEnvForSbx().
//
// DOCKER_SANDBOXES_PROXY must NOT be set during create — it forces the daemon
// to route Docker Hub registry auth through Squid, which isn't ready yet.
// XDG_CONFIG_HOME must also be removed — the Copilot harness sets it to $HOME,
// which makes the sbx CLI look for credentials in $HOME/ instead of the
// default $HOME/.config/ where `sbx login` stored them.
const savedProxy = process.env.DOCKER_SANDBOXES_PROXY;
const savedXdg = process.env.XDG_CONFIG_HOME;
delete process.env.DOCKER_SANDBOXES_PROXY;
delete process.env.XDG_CONFIG_HOME;

const createResult = await execa('sbx', args, {
env,
input: 'y\n',
stdio: ['ignore', 'pipe', 'pipe'],
stdio: ['pipe', 'pipe', 'pipe'],
reject: false,
timeout: 120_000, // 2 minute timeout for sandbox creation
});

// Restore env vars
if (savedProxy !== undefined) {
process.env.DOCKER_SANDBOXES_PROXY = savedProxy;
}
if (savedXdg !== undefined) {
process.env.XDG_CONFIG_HOME = savedXdg;
}

const stdout = (createResult.stdout || '').trim();
const stderr = (createResult.stderr || '').trim();
const sbxSucceeded = stdout.includes('Created sandbox');
Expand Down
Loading