Skip to content

fix(runtime): Preserve sandbox metadata - #65

Merged
moshloop merged 9 commits into
mainfrom
fix/sandbox-metadata-descriptors
Aug 11, 2026
Merged

fix(runtime): Preserve sandbox metadata#65
moshloop merged 9 commits into
mainfrom
fix/sandbox-metadata-descriptors

Conversation

@moshloop

@moshloop moshloop commented Aug 11, 2026

Copy link
Copy Markdown
Member

What

  • Preserve execution metadata when overriding the sandbox backend.
  • Close inherited agent descriptors to prevent detached agents from keeping Git pushes open.

Notes

  • Added regression coverage for descriptor inheritance.

Summary by CodeRabbit

  • New Features

    • Added sandbox selection and override handling for AI runs.
    • Added per-tool allow and deny policies where supported.
    • Added a manually triggered workflow for publishing multi-platform development images.
    • Expanded the development image with Go, browser automation, AI tools, and utilities.
  • Bug Fixes

    • Improved permission handling to prevent unintended approvals.
    • Added validation when requested tool policies are unsupported.
    • Prevented unrelated file descriptors from being inherited by launched agents.
  • Documentation

    • Updated setup and container documentation for the new image and publishing process.

…roval

Avoids rejecting approvals when suspension becomes resumable before transcript persistence completes.
Polls briefly for the suspended assistant seed and fails on timeout or cancellation.
…secrets

Provide a complete sandbox for multi-backend agent development, including Go, Flanksource tools, browser automation, and shell utilities.
Embed the dependency manifest and pass GitHub credentials through BuildKit secrets to keep release resolution reliable without leaking tokens into image layers.

Claude-Session-Id: b5841c80-b074-4324-9370-4f1d38d793ea
The root Dockerfile/entrypoint.sh were byte-identical copies of
pkg/container/base/*, but only the latter pair is //go:embed-ed and
actually built as claude-env:base. Nothing referenced the root copies —
.goreleaser.yaml has no dockers: block and no Makefile/Taskfile/workflow
target used them — so they were pure drift surface.

Completes the single-source consolidation started in e6ed4cdd, whose
README already points at pkg/container/base/Dockerfile.

Claude-Session-Id: b5841c80-b074-4324-9370-4f1d38d793ea
Adds a docker job to the release workflow that builds
pkg/container/base/Dockerfile for linux/amd64 and linux/arm64 and pushes
it to Docker Hub and GHCR, reusing
flanksource/action-workflows publish-multi-platform-docker-image (pinned
to v1.2.1). Each platform builds on a native runner and is published by
digest, so consumers never see a partially assembled index.

The job runs after goreleaser rather than in parallel: the image installs
flanksource/captain via deps at 'latest', so the tag's release assets
must exist first or the image would ship the previous release's binary.

The reusable workflow already forwards GITHUB_TOKEN as a BuildKit secret,
which is exactly what the Dockerfile's deps layer consumes to avoid
GitHub's unauthenticated API rate limit.

Claude-Session-Id: b5841c80-b074-4324-9370-4f1d38d793ea
Moves the docker job out of the release workflow into its own
workflow_dispatch-only 'Publish Image' workflow. The image is ~6.3GB and
takes two native-runner builds to assemble, which is a lot to spend on
every patch release of a Go binary — and Release's own workflow_dispatch
is for cutting a tag, so gating the job inside it would conflate the two
triggers.

Dispatching from a tag ref builds that tag, since the reusable workflow
checks out the calling ref. A resolve job derives the image tags,
defaulting the version to the most recent reachable tag and rejecting
values containing commas or whitespace before they reach the reusable
workflow's tag parser. Publishing :latest and the platform list are
inputs so an older tag can be republished without moving :latest.

Claude-Session-Id: b5841c80-b074-4324-9370-4f1d38d793ea
Exercise commit hooks through the real Runner so committing/committed notices remain visible in streamed and buffered run output.
Allow the generated webapp entrypoint to be tracked for commit validation.
Prevent AI backends from silently granting broader access than requested. Propagate Claude deny-lists, emit Codex approval policies, reject unsupported per-tool policies, and resolve sandbox selections for HTTP/spec runs.

BREAKING CHANGE: Backends without per-tool policy support now reject requests specifying permissions.tools; unbrokered Claude runs use the restricted default instead of bypassing permissions.
…criptors

Retain execution metadata when overriding the sandbox backend and prevent detached agents from keeping Git pushes open through inherited descriptors.
Add regression coverage for descriptor inheritance.
@moshloop
moshloop enabled auto-merge (rebase) August 11, 2026 08:07
@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1d949db2-4122-4dab-b1bd-fc098004eb2f

📥 Commits

Reviewing files that changed from the base of the PR and between 1d9f383 and acf3cfe.

📒 Files selected for processing (14)
  • .github/workflows/publish-image.yml
  • pkg/ai/provider/claude_cli.go
  • pkg/ai/provider/claude_cli_test.go
  • pkg/ai/provider/claudeagent/permissions_test.go
  • pkg/ai/provider/claudeagent/provider.go
  • pkg/ai/provider/cmux/provider.go
  • pkg/ai/provider/codex_appserver.go
  • pkg/ai/provider/codex_appserver_params_test.go
  • pkg/api/permissions.go
  • pkg/api/tool_policy_support_test.go
  • pkg/cli/prompt_render_test.go
  • pkg/container/base/Dockerfile
  • pkg/container/base/deps.yaml
  • pkg/gitagent/snapshot_audit_ginkgo_test.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/ai/provider/claude_cli_test.go
  • pkg/ai/provider/cmux/provider.go
  • pkg/ai/provider/codex_appserver.go
  • pkg/container/base/deps.yaml
  • .github/workflows/publish-image.yml

Walkthrough

The PR adds backend permission-policy validation, posture-aware approvals, sandbox resolution, an expanded container image and publishing workflow, descriptor inheritance controls, commit notice coverage, and repository integration updates.

Changes

Permission policy and approval handling

Layer / File(s) Summary
Tool-policy capability contract
pkg/api/permissions.go, pkg/api/registry/*, pkg/api/tool_policy_support_test.go
Backends now declare tool-policy support. Unsupported per-tool policies return validation errors.
Provider permission enforcement
pkg/ai/provider/claude_cli.go, pkg/ai/provider/cmux/provider.go, pkg/ai/provider/codex_*, pkg/ai/provider/gemini_cli.go, pkg/ai/provider/genkit/*
Provider execution paths validate permissions before starting and pass normalized tool and Codex approval policies.
Claude Agent permission initialization
pkg/ai/provider/claudeagent/*
Claude Agent defaults to standard permissions, forwards denied tools, and applies edit allowlists conditionally.
Codex approval posture handling
pkg/ai/provider/codex_appserver*
Codex approval responses now depend on the active run posture and default to decline before initialization.
Suspended-turn polling
pkg/aichat/approval_execution*
Approval execution waits for the persisted suspended assistant message and handles cancellation and timeout.

Run sandbox resolution

Layer / File(s) Summary
Sandbox selection and recording
pkg/cli/ai_sandbox.go, pkg/cli/ai_prompt_file.go
Sandbox precedence is resolved from request, prompt, and global settings. The selected configuration is recorded on the run.
Prompt rendering integration
pkg/cli/prompt_render.go, pkg/cli/prompt_render_test.go
HTTP and ephemeral rendering apply sandbox settings before prompt defaults. Tests cover precedence and metadata preservation.

Base image and publishing

Layer / File(s) Summary
Base image toolchain
pkg/container/base/Dockerfile
The image adds configurable versions, Go, browser tooling, development utilities, aliases, and multiple agent CLIs.
Dependency manifest and build context
pkg/container/base/deps.yaml, pkg/container/base_image.go, pkg/container/base_image_test.go
The build context embeds deps.yaml and installs the registered tools.
BuildKit token and environment wiring
pkg/container/base_image.go, pkg/container/build.go
Builds force BuildKit and optionally pass GitHub tokens as secrets.
Manual multi-platform publishing
.github/workflows/publish-image.yml, README.md
The workflow resolves tags and invokes reusable multi-platform publishing. The README documents the image and workflow.

Agent descriptor inheritance

Layer / File(s) Summary
Platform-specific descriptor closure
pkg/gitagent/workspace.go, pkg/gitagent/workspace_descriptors*
Inherited descriptors are marked close-on-exec through Linux and Darwin-specific implementations.
Descriptor inheritance test
pkg/gitagent/workspace_ginkgo_test.go
The integration test verifies that an unrelated descriptor is not inherited by a launched agent.

Commit notice coverage

Layer / File(s) Summary
Runner commit notice integration
pkg/ai/agent/commit/notice_test.go
The test verifies one commit and matching committing and committed notices in streamed and buffered results.

Repository integration updates

Layer / File(s) Summary
Generated artifact allowlisting
.gitignore, .gavel.yaml
The generated web application index is no longer ignored and is allowed by commit validation.
Direct module updates
go.mod
The clicky, aichat, commons, and commons-db versions are updated.
Container documentation
README.md
The project layout and container image documentation now describe pkg/container/base and its publishing workflow.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 63.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the sandbox metadata preservation changes, although the pull request also includes broader tool-policy, container, and descriptor updates.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/sandbox-metadata-descriptors
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/sandbox-metadata-descriptors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Gavel summary

Source Pass Fail Skip Duration

Totals: 0 passed · 0 failed · 0 skipped · -

View full results

@socket-security

socket-security Bot commented Aug 11, 2026

Copy link
Copy Markdown

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Gavel summary

Source Pass Fail Skip Duration
ai 87 0 0 27ms
aichat 113 0 2 12.2s
anthropicmock 1 0 0 3ms
api 80 0 0 24ms
attachments 5 0 0 3ms
bash 4 0 0 183.319µs
callertools 10 0 0 84ms
captain 6 0 0 3ms
claude 22 0 0 23ms
claudeagent 13 0 0 202ms
cli 209 0 0 6.4s
cmux 4 0 0 273.778µs
collections 12 0 0 616.837µs
credentials 5 0 0 37ms
database 15 0 0 3.8s
genkit 25 0 0 121ms
gitagent 66 0 0 8.2s
github.com/flanksource/captain/migrations 11 0 0 4.3s
github.com/flanksource/captain/pkg/ai 232 0 0 410ms
github.com/flanksource/captain/pkg/ai/agent 23 0 0 -
github.com/flanksource/captain/pkg/ai/agent/commit 52 0 0 4.0s
github.com/flanksource/captain/pkg/ai/agent/setup 16 0 0 60ms
github.com/flanksource/captain/pkg/ai/agent/verify 21 0 0 390ms
github.com/flanksource/captain/pkg/ai/agent/worktree 6 0 0 -
github.com/flanksource/captain/pkg/ai/assistanttags 15 0 0 -
github.com/flanksource/captain/pkg/ai/fixture 42 0 0 150ms
github.com/flanksource/captain/pkg/ai/fixture/kubeproxy 2 0 0 30ms
github.com/flanksource/captain/pkg/ai/fixture/mcpproxy 6 0 0 -
github.com/flanksource/captain/pkg/ai/history 54 0 0 -
github.com/flanksource/captain/pkg/ai/internal/gen-model-registry 12 0 0 -
github.com/flanksource/captain/pkg/ai/middleware 25 0 0 -
github.com/flanksource/captain/pkg/ai/pricing 9 0 0 -
github.com/flanksource/captain/pkg/ai/prompt 16 0 0 30ms
github.com/flanksource/captain/pkg/ai/provider 144 0 0 10ms
github.com/flanksource/captain/pkg/ai/provider/claudeagent 44 0 0 1.4s
github.com/flanksource/captain/pkg/ai/provider/cmux 119 0 0 860ms
github.com/flanksource/captain/pkg/ai/provider/genkit 36 0 0 10ms
github.com/flanksource/captain/pkg/ai/provider/jsonrpc 6 0 0 50ms
github.com/flanksource/captain/pkg/aichat 5 0 0 130ms
github.com/flanksource/captain/pkg/aimock 50 0 7 680ms
github.com/flanksource/captain/pkg/aimock/anthropicmock 14 0 0 20ms
github.com/flanksource/captain/pkg/aimock/openaimock 18 0 0 -
github.com/flanksource/captain/pkg/api 136 0 0 470ms
github.com/flanksource/captain/pkg/api/registry 96 0 0 -
github.com/flanksource/captain/pkg/bash 348 0 0 30ms
github.com/flanksource/captain/pkg/captainconfig 25 0 0 -
github.com/flanksource/captain/pkg/claude 138 0 0 -
github.com/flanksource/captain/pkg/claude/tools 18 0 0 -
github.com/flanksource/captain/pkg/cli 532 0 0 41.9s
github.com/flanksource/captain/pkg/cmux 1 0 0 -
github.com/flanksource/captain/pkg/codexconfig 10 0 0 10ms
github.com/flanksource/captain/pkg/container 72 0 1 -
github.com/flanksource/captain/pkg/database 99 0 0 12.7s
github.com/flanksource/captain/pkg/dod 11 0 0 1m0s
github.com/flanksource/captain/pkg/gitagent 31 0 0 380ms
github.com/flanksource/captain/pkg/gitagent/proxy 12 0 0 10ms
github.com/flanksource/captain/pkg/monitor 54 0 0 1.9s
github.com/flanksource/captain/pkg/sandbox 1 0 0 -
github.com/flanksource/captain/pkg/sandbox/adapter 26 0 0 -
github.com/flanksource/captain/pkg/sandbox/presets 13 0 0 -
github.com/flanksource/captain/pkg/session 70 0 0 10ms
history 69 0 0 16ms
migrations 6 0 0 4.3s
openaimock 2 0 0 156.039µs
provider 10 0 0 14ms
registry 38 0 0 1ms
session 28 0 0 12ms
tools 38 0 0 1ms

Totals: 3539 passed · 0 failed · 10 skipped · 2m45s

View full results

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/ai/provider/codex_appserver.go`:
- Around line 134-138: Move the c.setPosture(postureFor(req)) call in
ExecuteStream to immediately after c.turnMu.Lock(), ensuring each turn’s posture
is set only after acquiring the turn lock. Add a concurrent-turn regression test
where a restricted first request and bypass second request verify the first
turn’s approval cannot use the second posture.

In `@pkg/api/registry/providers.go`:
- Line 24: The Claude Agent adapter must enforce every tool policy it accepts
before advertising support. In pkg/ai/provider/claudeagent/provider.go:456,
normalize req.Permissions.Tools.Policies(), forward all resulting deny policies
to DisallowedTools, and reject unsupported ask/auto policies (or implement their
Claude Agent equivalents); then update pkg/api/registry/providers.go:24 so
ModeAgent.ToolPolicy is not advertised unless this complete enforcement is
available.

In `@pkg/cli/prompt_render_test.go`:
- Around line 201-280: Extend TestRenderPromptResolvesSandbox with a case whose
override SandboxRef includes both Agent and Policy, then assert
rendered.Input.Sandbox preserves those values and
rendered.Config.ResolvedSandbox() retains them after overlayRuntimeSpec and
applyRunSandbox. Keep the existing sandbox-kind assertions intact.

In `@pkg/container/base/Dockerfile`:
- Around line 85-86: Update the Go installation RUN step to download the
architecture-specific archive selected by GO_VERSION and dpkg
--print-architecture into a temporary file, verify it against the pinned
official SHA-256 checksum for that version and architecture, and only then
extract it into /usr/local as root. Remove the direct curl-to-tar pipeline while
preserving support for both GO_VERSION and the detected architecture.
- Line 2: Make all base-image build inputs immutable: in
pkg/container/base/Dockerfile lines 2, 14-16, 90, and 130-138, pin the base
image digest, set explicit versions for the three agent CLI arguments and
Ginkgo, pin the Playwright package, and lock global npm packages with versions
and integrity or a lockfile; in pkg/container/base/deps.yaml lines 57-62, pin
task, golangci-lint, gavel, repomap, and captain. Update
.github/workflows/publish-image.yml to pass the selected build arguments through
to the image build.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8775d626-11b0-4996-b3ca-dc7a9e23d4ce

📥 Commits

Reviewing files that changed from the base of the PR and between 5db1179 and 1d9f383.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (43)
  • .gavel.yaml
  • .github/workflows/publish-image.yml
  • .gitignore
  • Dockerfile
  • README.md
  • entrypoint.sh
  • go.mod
  • pkg/ai/agent/commit/notice_test.go
  • pkg/ai/provider/claude_cli_test.go
  • pkg/ai/provider/claudeagent/agent.ts
  • pkg/ai/provider/claudeagent/bridge_params.go
  • pkg/ai/provider/claudeagent/permissions_test.go
  • pkg/ai/provider/claudeagent/provider.go
  • pkg/ai/provider/cmux/provider.go
  • pkg/ai/provider/codex_appserver.go
  • pkg/ai/provider/codex_appserver_approval.go
  • pkg/ai/provider/codex_appserver_params_test.go
  • pkg/ai/provider/codex_appserver_protocol.go
  • pkg/ai/provider/codex_cli.go
  • pkg/ai/provider/codex_cli_test.go
  • pkg/ai/provider/gemini_cli.go
  • pkg/ai/provider/genkit/genkit.go
  • pkg/aichat/approval_execution.go
  • pkg/aichat/approval_execution_test.go
  • pkg/api/permissions.go
  • pkg/api/registry/backend.go
  • pkg/api/registry/provider.go
  • pkg/api/registry/providers.go
  • pkg/api/tool_policy_support_test.go
  • pkg/cli/ai_prompt_file.go
  • pkg/cli/ai_sandbox.go
  • pkg/cli/prompt_render.go
  • pkg/cli/prompt_render_test.go
  • pkg/container/base/Dockerfile
  • pkg/container/base/deps.yaml
  • pkg/container/base_image.go
  • pkg/container/base_image_test.go
  • pkg/container/build.go
  • pkg/gitagent/workspace.go
  • pkg/gitagent/workspace_descriptors.go
  • pkg/gitagent/workspace_descriptors_darwin.go
  • pkg/gitagent/workspace_descriptors_linux.go
  • pkg/gitagent/workspace_ginkgo_test.go
💤 Files with no reviewable changes (3)
  • entrypoint.sh
  • Dockerfile
  • pkg/gitagent/workspace.go

Comment thread pkg/ai/provider/codex_appserver.go Outdated
Comment thread pkg/api/registry/providers.go
Comment thread pkg/cli/prompt_render_test.go
Comment thread pkg/container/base/Dockerfile Outdated
Comment thread pkg/container/base/Dockerfile Outdated
Prevent AI transports from silently dropping tool restrictions or accepting unenforceable per-tool prompts, and serialize Codex approval posture updates per turn. Preserve sandbox agent and policy metadata during prompt rendering. Pin container dependencies, base images, tool versions, and Go checksums while allowing deliberate workflow overrides.

BREAKING CHANGE: Per-tool ask policies are now rejected, and unsupported backends fail instead of running without requested restrictions.

Claude-Session-Id: 00f6645c-67f0-4e1e-a840-a600b23e945c
@moshloop
moshloop merged commit ff7eed3 into main Aug 11, 2026
12 checks passed
@moshloop
moshloop deleted the fix/sandbox-metadata-descriptors branch August 11, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant