Skip to content

feat(devcontainer): autonomous-agent sandbox with egress lockdown + scoped tokens#107

Open
karlkfi wants to merge 3 commits into
mainfrom
claude/upbeat-bohr-cf46b0
Open

feat(devcontainer): autonomous-agent sandbox with egress lockdown + scoped tokens#107
karlkfi wants to merge 3 commits into
mainfrom
claude/upbeat-bohr-cf46b0

Conversation

@karlkfi

@karlkfi karlkfi commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

What

Adds a containerized sandbox so Claude Code agents can run autonomously (--dangerously-skip-permissions) without host risk or credential exfiltration. New files only — no existing behavior changes.

  • .devcontainer/init-firewall.sh — default-drop container egress, allowlisting only the Anthropic API and GitHub's published CIDR ranges (fetched from api.github.com/meta). Self-verifies at the end (GitHub reachable, example.com blocked) and exits non-zero if the lockdown didn't take.
  • .devcontainer/claude-settings.jsondeny rules (secret-file reads, sudo, Keychain) baked into the container user's ~/.claude/settings.json so repo-level settings can't relax them.
  • scripts/mint-installation-token.sh — host-side; reads the App PEM from the macOS Keychain via process substitution (never written to disk), mints a ≤1h, single-repo-scoped installation token with minimal permissions. Prints only the token to stdout.
  • .devcontainer/{Dockerfile,devcontainer.json,README.md} — the image (golang:1.26, matching the repo builder), wiring (NET_ADMIN, firewall as postStartCommand), and the security model + one-time agent-App setup steps.

Why

Enables running many parallel worktree agents with far fewer approval prompts. Full-bypass mode consults no permission rules, so the container must be the boundary — but the container still holds push credentials, so "the container is the sandbox" only protects the host. Three layers cover the gaps:

Layer Stops
Egress allowlist Exfiltration / C2 to any host except Anthropic + GitHub
Credential-read deny A leaked key escaping via an allowed channel (e.g. a PR body on GitHub)
Scoped credentials Catastrophic git ops surviving a token leak

Reviewer notes

  • Vendoring keeps the allowlist tiny. Because the repo vendors Go deps, go build/go test run offline, so egress is just Anthropic + GitHub — no fragile CDN/module-proxy allowlisting.
  • The agent commits as a separate least-privilege identity. Smoke-testing the mint script against the existing actions-gateway-test App returned HTTP 422: that App is the runner control plane (actions:write, administration:write, organization_self_hosted_runners:write, metadata:read) — it has no contents/pull_requests and is too powerful to hand an agent. The README documents creating a dedicated App with only contents:write+pull_requests:write; the mint script targets it via env overrides, no code change.
  • deny globs are defense-in-depth, not the primary control. They can't reliably gate a git push by target branch and can't hide an env-var token from printenv. The real guards are the short-lived scoped token + server-side branch protection on main (called out in the README).
  • Validated locally: both scripts pass bash -n; the JWT RS256 signing path was verified against a generated keypair (signature Verified OK); the Keychain hex store/read round-trips byte-for-byte; the mint script minted a real ghs_… token scoped to exactly this repo.
  • Status: not yet wired into any automated flow — these are the building blocks. Creating the dedicated App and enabling branch protection are manual follow-ups noted in the README.

Tracking

This PR also adds the initiative's plan doc and backlog entries so the work is pickable cold:

  • docs/plan/agent-workflow-automation.md — goal, the four levers, done/in-flight/open, decisions made.
  • docs/STATUS.md — Progress row + Q62 (go-live: dedicated agent App + branch protection) and Q63 (auto-merge + CI auto-fix, blocked by Q62).

karlkfi added 3 commits June 1, 2026 21:38
… + scoped tokens

Add a containerized sandbox so Claude Code agents can run autonomously
(--dangerously-skip-permissions) without host risk or credential
exfiltration. The container is the boundary; three layers cover its gaps:

- .devcontainer/init-firewall.sh — default-drop egress, allowlisting only
  the Anthropic API and GitHub's published CIDRs (from api.github.com/meta).
  Go builds need no egress because the repo vendors its deps. Self-verifies
  (GitHub reachable, example.com blocked) and exits non-zero otherwise.
- .devcontainer/claude-settings.json — deny rules (secret-file reads, sudo,
  Keychain) baked into the container user's settings so repo settings can't
  relax them. Closes the GitHub exfil channel the firewall can't (GitHub is
  an allowed host).
- scripts/mint-installation-token.sh — host-side; reads the App PEM from the
  macOS Keychain via process substitution (never on disk), mints a <=1h
  repo-scoped installation token with minimal permissions. Verified
  end-to-end.
- .devcontainer/{Dockerfile,devcontainer.json,README.md} — image, wiring,
  and the security model + one-time agent-App setup steps.

The agent commits as a separate least-privilege identity, not the
actions-gateway-test runner App (which lacks contents/pull_requests and
carries administration:write).
Capture the autonomous-agent workflow initiative kicked off by the
.devcontainer sandbox: goal, the four levers (approval prompts, parallelism,
PR/CI/merge automation, orchestration glue), what's done vs in-flight vs open,
and the decisions made (dedicated least-privilege agent App; minimal
Anthropic+GitHub egress allowlist). Remaining work tracked as Q62/Q63.
@karlkfi karlkfi force-pushed the claude/upbeat-bohr-cf46b0 branch from bddf140 to 0d77cb2 Compare June 2, 2026 04:40
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