mroberts b79bf35e70
build / build (push) Failing after 3m38s
Target the self-hosted runner label and harden the workflow
ubuntu-latest matches no runner on this forge, so both the main push and the
v1.0.0 tag queued nothing at all. The runner is labelled linux.

actions/checkout is pinned to a commit rather than a tag, and to v4.4.0 rather
than the current v7.0.1: v7 declares node24, which act_runner does not provide.
regctl moves out of /usr/local/bin, which a self-hosted runner cannot write to
without sudo, and is pinned to a release rather than tracking latest.

Clean under actionlint and zizmor --persona=auditor. Every expansion moves into
env so nothing interpolates into a shell body. secrets-outside-env is suppressed
deliberately: Gitea has no deployment environment protection rules, so a
dedicated environment would add ceremony without a security boundary.
2026-07-31 08:25:20 -05:00

claude-sbx

A Docker Sandboxes template carrying Malcolm's Claude Code configuration — plugins, skills, hooks and memory files — published to the Gitea container registry at git.mroberts.dev.

Consumed by the ai:sbx mise task in mroberts/ai-sandbox via AI_SBX_TEMPLATE.

docs/HANDOFF.md is the original brief. Where the two disagree, this file is current: several of the brief's assumptions turned out to be wrong once tested.

Status: blocked upstream

sbx v0.37.0 and v0.37.1 cannot consume custom templates at all. Every layer stacked on the base image is silently dropped — the sandbox boots with base content only, sbx create exits 0, and sbx inspect reports the correct image. Verified here across all three delivery paths: sbx template load from a tar, sbx template save snapshots, and a registry pull.

This is docker/sbx-releases#366 — the erofs snapshotter stopped building the merged fsmeta.erofs at the top of the chain. Confirmed by others on macOS, Windows, Ubuntu and WSL2, across Homebrew, winget, apt and template load. v0.35.0 is unaffected, and templates saved under 0.37.1 materialize correctly when launched under 0.35.0.

Until it is fixed, this image builds and pushes correctly but produces a sandbox with none of its contents. Use mise run ai:sbx -- setup instead — it installs the same configuration and plugins into a stock sandbox at runtime, and works on 0.37.x today.

What the image contains

Path Source
claude/CLAUDE.md, claude/AGENTS.md ~/.claude, verbatim
claude/hooks, claude/skills ~/.claude, verbatim
plugins.json generated from ~/.claude/plugins/known_marketplaces.json and settings.json
mise https://mise.run, with shims on PATH via .bashrc

Credentials, transcripts, history.jsonl, projects/, file-history/ and session-env/ are never copied. ~/.claude/plugins/ is not copied either — the build reinstalls plugins from the manifest, which keeps the repository small and the image reproducible from source without any access to the host.

agents/ and commands/ are absent because both are empty on the host.

Building

docker build -t git.mroberts.dev/mroberts/claude-sbx:v1 .

Requires no access to ~/.claude. The build installs 10 marketplaces and 17 plugins and fails if the resulting enabled-plugin count does not match the manifest.

Pushing

docker push fails against this registry: git.mroberts.dev is behind Cloudflare, which rejects request bodies over 100 MB, and the base image has a 325 MB compressed layer. Push chunked instead:

mise use -g "github:regclient/regclient[exe=regctl,matching=regctl-linux-amd64]"

regctl registry set git.mroberts.dev --blob-chunk 50000000 --blob-max 50000000
docker save git.mroberts.dev/mroberts/claude-sbx:v1 -o image.tar
regctl image import git.mroberts.dev/mroberts/claude-sbx:v1 image.tar

Plain ubi:regclient/regclient installs regbot rather than regctl; the matching filter above picks the right asset. .gitea/workflows/build.yml does the same on tag pushes and on main, authenticating with the automatic GITEA_TOKEN.

Pulls are unaffected by the Cloudflare limit, which caps uploads only.

Consuming

# ~/.config/mise/config.toml
[env]
AI_SBX_TEMPLATE = "git.mroberts.dev/mroberts/claude-sbx:v1"

The sandbox's Docker daemon pulls from the registry directly and does not share the host image store, so a local build is invisible to it.

Notes from building this

  • Marketplace sources are already portable. The brief called for rewriting SSH remotes to HTTPS. Unnecessary: known_marketplaces.json records marketplaces as {source: github, repo: owner/name} or an HTTPS git URL, never as the SSH remote the checkout happens to use.
  • claude-plugins-official is not built in. A fresh sandbox knows no marketplaces at all, so it is added explicitly like any other, from anthropics/claude-plugins-official.
  • Plugin installs need no authentication, but a marketplace on a non-GitHub host needs a network policy rule — git.mroberts.dev is denied by default.
  • Baked plugin enablement does not survive. sbx recreates ~/.claude/settings.json at sandbox creation, dropping enabledPlugins while leaving the plugin files. Re-enabling against a baked image costs ~1.3s per plugin because the marketplace clones are already present; ai:sbx config does this.
  • ~/.claude/skills is a mount point inside the sandbox, backed by sbx's shared skills store. It cannot be replaced by a copy; seed it with sbx skills import.
S
Description
sbx template for claude code with my plugins pre-configured
Readme
99 KiB
v1.0.0
Latest
2026-07-31 13:19:54 +00:00
Languages
JavaScript 82.9%
Shell 7%
PowerShell 5.5%
Dockerfile 4.6%