From a1dd8bbd6543f6dc7e76c3169cb6c2b122ccfc65 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Sun, 12 Jul 2026 15:02:57 -0700 Subject: [PATCH 1/2] fix: emit sbx credential refresh step before agent execution Docker Hub OAuth tokens obtained by `sbx login` during the daemon-setup step can expire or be invalidated by the policy-reset cycle. By the time AWF calls `sbx create`, the sbx daemon no longer has valid Docker Hub credentials, causing 'user is not authenticated to Docker' errors. Add a `generateDockerSbxCredentialRefreshStep()` that runs `sbx login` immediately before the agent execution step, ensuring the daemon has fresh credentials when AWF creates the microVM sandbox. The step is emitted in `compiler_yaml_ai_execution.go` (the common compilation path for all engines) so it applies to Copilot, Claude, Codex, and any future engine using docker-sbx runtime. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- pkg/workflow/compiler_yaml_ai_execution.go | 11 ++++++++ pkg/workflow/docker_sbx_install.go | 19 ++++++++++++++ pkg/workflow/docker_sbx_test.go | 29 ++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/pkg/workflow/compiler_yaml_ai_execution.go b/pkg/workflow/compiler_yaml_ai_execution.go index fe3794bed64..12624d7c924 100644 --- a/pkg/workflow/compiler_yaml_ai_execution.go +++ b/pkg/workflow/compiler_yaml_ai_execution.go @@ -426,6 +426,17 @@ func (c *Compiler) generateAgentRunSteps(yaml *strings.Builder, data *WorkflowDa // connects to via host.docker.internal:18443. c.generateStartCliProxyStep(yaml, data) + // Refresh sbx credentials immediately before AWF execution. Docker Hub OAuth + // tokens obtained during the daemon-setup step can expire between workflow steps, + // causing "user is not authenticated to Docker" errors when AWF calls `sbx create`. + if isDockerSbxRuntime(data) { + refreshStep := generateDockerSbxCredentialRefreshStep() + for _, line := range refreshStep { + yaml.WriteString(line) + yaml.WriteString("\n") + } + } + // Add AI execution step using the agentic engine compilerYamlLog.Printf("Generating engine execution steps for %s", engine.GetID()) c.generateEngineExecutionSteps(yaml, data, engine, logFileFull) diff --git a/pkg/workflow/docker_sbx_install.go b/pkg/workflow/docker_sbx_install.go index b775bc74320..075467ac33c 100644 --- a/pkg/workflow/docker_sbx_install.go +++ b/pkg/workflow/docker_sbx_install.go @@ -131,6 +131,25 @@ func generateDockerSbxAuthAndDaemonStep() GitHubActionStep { }) } +// generateDockerSbxCredentialRefreshStep creates a step that re-authenticates the +// sbx daemon with Docker Hub immediately before AWF runs the agent. OAuth tokens +// obtained by `sbx login` during the daemon-setup step can expire or be invalidated +// by the policy-reset cycle, so a fresh login right before execution prevents +// "user is not authenticated to Docker" errors when AWF calls `sbx create`. +func generateDockerSbxCredentialRefreshStep() GitHubActionStep { + return GitHubActionStep([]string{ + " - name: Refresh sbx credentials", + " env:", + " DOCKER_PAT_VAL: ${{ secrets.DOCKER_PAT }}", + " DOCKER_USERNAME_VAL: ${{ secrets.DOCKER_USERNAME }}", + " run: |", + ` # Re-authenticate sbx immediately before AWF runs.`, + ` # Docker Hub OAuth tokens from sbx login can expire between steps.`, + ` printf '%s' "$DOCKER_PAT_VAL" | sbx login --username "$DOCKER_USERNAME_VAL" --password-stdin`, + ` echo "✅ sbx credentials refreshed"`, + }) +} + // generateDockerSbxPreFlightStep creates a step that verifies the sbx stack works // end-to-end before the MCP gateway and AWF container setup begins. func generateDockerSbxPreFlightStep() GitHubActionStep { diff --git a/pkg/workflow/docker_sbx_test.go b/pkg/workflow/docker_sbx_test.go index 5b0c38bf282..f13f9451f9e 100644 --- a/pkg/workflow/docker_sbx_test.go +++ b/pkg/workflow/docker_sbx_test.go @@ -85,6 +85,25 @@ func TestGenerateDockerSbxInstallSteps(t *testing.T) { assert.Contains(t, content, "sbx rm", "must remove the test sandbox") assert.Contains(t, content, "✅ sbx ready", "must confirm readiness") }) + + t.Run("credential refresh step", func(t *testing.T) { + step := generateDockerSbxCredentialRefreshStep() + require.NotEmpty(t, step, "credential refresh step must not be empty") + content := strings.Join(step, "\n") + assert.Contains(t, content, "Refresh sbx credentials", "must have correct step name") + assert.Contains(t, content, "sbx login", "must re-authenticate with sbx login") + assert.Contains(t, content, "DOCKER_PAT_VAL: ${{ secrets.DOCKER_PAT }}", "must use env for DOCKER_PAT") + assert.Contains(t, content, "DOCKER_USERNAME_VAL: ${{ secrets.DOCKER_USERNAME }}", "must use env for DOCKER_USERNAME") + assert.Contains(t, content, "✅ sbx credentials refreshed", "must confirm refresh") + // The run: section must use env var references, not raw secret expressions. + parts := strings.SplitN(content, "run: |", 2) + require.Len(t, parts, 2, "step must have a run: section") + runBody := parts[1] + assert.NotContains(t, runBody, "${{ secrets.DOCKER_PAT }}", + "raw secrets expression must not appear in shell commands") + assert.NotContains(t, runBody, "${{ secrets.DOCKER_USERNAME }}", + "raw secrets expression must not appear in shell commands") + }) } // TestDockerSbxInstallStepOrderInBuildNpmEngineInstallStepsWithAWF verifies that all @@ -527,4 +546,14 @@ sandbox: // --container-runtime sbx must appear in the AWF invocation. assert.Contains(t, lockStr, "--container-runtime sbx", "AWF invocation must include --container-runtime sbx") + + // Credential refresh step must be present. + assert.Contains(t, lockStr, "Refresh sbx credentials", "compiled workflow must include credential refresh step before execution") + + // Credential refresh step must appear AFTER pre-flight but BEFORE execution. + refreshPos := strings.Index(lockStr, "Refresh sbx credentials") + preflightPos := strings.Index(lockStr, "pre-flight smoke test") + execPos := strings.Index(lockStr, "agentic_execution") + assert.Greater(t, refreshPos, preflightPos, "credential refresh must come after pre-flight step") + assert.Less(t, refreshPos, execPos, "credential refresh must come before agent execution step") } From 7445dc5aae4eb4d29d2f502ca86302b0d7c41d4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 13 Jul 2026 00:46:17 +0000 Subject: [PATCH 2/2] Add changeset --- .changeset/patch-fix-docker-sbx-credential-refresh.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/patch-fix-docker-sbx-credential-refresh.md diff --git a/.changeset/patch-fix-docker-sbx-credential-refresh.md b/.changeset/patch-fix-docker-sbx-credential-refresh.md new file mode 100644 index 00000000000..72e6a305f25 --- /dev/null +++ b/.changeset/patch-fix-docker-sbx-credential-refresh.md @@ -0,0 +1,5 @@ +--- +"gh-aw": patch +--- + +Fixed docker-sbx workflows to refresh sbx credentials immediately before agent execution.