feat: add dind-box family — Docker-in-Docker for every box variant (#80)#81
Conversation
Adding .gitkeep for PR creation (default mode). This file will be removed when the task is complete. Issue: #80
…n-Box) - Compile issue text, requirements list, and solution plans in docs/case-studies/issue-80/CASE-STUDY.md - Collect upstream evidence (Docker DinD/DooD/Sysbox/Tecnativa/devcontainers/OWASP/Quarkslab) in research.md - Preserve original issue text in issue.md - Recommend nested DinD as the default solution: it satisfies "docker ps only shows my containers" by construction, and document Sysbox as the recommended secure runtime - Rejected DooD with host /var/run/docker.sock as a default for host-safety reasons Removes the .gitkeep marker file used during PR bootstrap (per its own commit message).
Solution summaryDone. The PR is ready for review. Summary of what I did The issue body explicitly asked for a case study (compile data into Three new files under
The bootstrap Want me to /schedule a follow-up agent in ~1 week to open the implementation PR ( This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost estimation:
📊 Context and tokens usage:Claude Opus 4.7:
Total: (178.7K + 2.1M cached) input tokens, 28.3K output tokens, $2.868548 cost Claude Haiku 4.5:
Total: 295.9K input tokens, 9.5K output tokens, $0.406565 cost 🤖 Models used:
📎 Log file uploaded as Gist (1515KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
✅ Ready to mergeThis pull request is now ready to be merged:
Monitored by hive-mind with --auto-restart-until-mergeable flag |
|
Ok, name it Let's fully implement it. |
|
🤖 AI Work Session Started Starting automated work session at 2026-04-28T14:01:09.324Z The PR has been converted to draft mode while work is in progress. This comment marks the beginning of an AI work session. Please wait for the session to finish, and provide your feedback. |
Adds a generic dind recipe at ubuntu/24.04/dind/ that takes any base box image as --build-arg BASE_IMAGE=... and produces a "<base>-dind" sibling. Each running dind-box has its own inner dockerd, so docker ps -a inside lists only that box's children — the host-isolation goal in issue #80. Wires the recipe into the release workflow as a 14×2 (variant × arch) matrix so dind variants are published alongside every existing image (full, essentials, js, and every language box) on Docker Hub and ghcr.io with the standard latest/version/-amd64/-arm64 tag set and per-variant multi-arch manifests. README + case study updated with image tables and the security model (default --privileged, recommended Sysbox runtime, do-not-mount-host-socket warning, per-box docker ps scoping).
Hyphen-prefixed values like '-js' inside YAML flow-style mappings caused GitHub Actions' workflow parser to reject the file (no jobs at all in the last run). Switching to explicit block-style with quoted strings makes the intent unambiguous and matches the style used elsewhere in this workflow.
GitHub Actions accepts object-typed matrix entries, but actionlint flagged
'object should not be evaluated in template with ${{ }}' for steps that
referenced ${{ matrix.variant }} without a sub-key. The previous push to
main with object-typed matrices failed at workflow load (zero jobs, zero
billable time, run name showed file path instead of workflow name) — this
matches GitHub's behaviour when it can't materialize the matrix.
Switch to a flat list of strings and derive base_image_suffix / dind_suffix
inline in each step. Same matrix size (14 entries × 2 archs).
… matrix) Push to feature branch keeps producing 0-second 'failure' runs that record no jobs, no billable time, and report the workflow file path as the run name. Local YAML and actionlint both pass. The pre-existing language matrix uses a flow-style inline list 'language: [a, b, c]' on one line — switch the dind matrix to the same form so it matches the proven pattern.
Just in case GitHub's stricter parser dislikes em-dashes inside new run/comment blocks. Existing em-dashes in the architecture diagram heredoc are left alone since they were already passing on prior runs.
GitHub Actions rejected the workflow with 'Exceeded max expression length 21000' on line 2466 (the create-release step's run block). Adding the dind-box tables to the existing heredoc pushed the step's expression over the per-step limit. Move the dind-box tables to a new 'Append dind-box tables to release notes' step that uses a bash for-loop to generate rows programmatically, keeping the GitHub-side expression interpolation small. Move 'gh release create/edit' to its own step so each step stays well under the limit.
Solution summaryThe polling background task is still waiting. It will notify me when complete. Let me summarize the work done: SummaryI implemented the What's in the PR (#81)New files (
Workflow (
Docs: README image tables for both registries + security banner; case study updated with implementation note; changeset bumping minor version. Issues hit and fixed (visible in commit history)
CI statusThe PR is ready for review ( This summary was automatically extracted from the AI working session output. |
🤖 Solution Draft LogThis log file contains the complete execution trace of the AI solution draft process. 💰 Cost: $31.363773📊 Context and tokens usage:
Total: (334.2K + 52.9M cached) input tokens, 113.1K output tokens, $31.363773 cost 🤖 Models used:
📎 Log file uploaded as Gist (9097KB)Now working session is ended, feel free to review and add any feedback on the solution draft. |
✅ Ready to mergeThis pull request is now ready to be merged:
Monitored by hive-mind with --auto-restart-until-mergeable flag |
Summary
Implements issue #80. Adds a
dind-boxfamily — a Docker-in-Docker sibling for every existing box image, plus the case-study deliverable that the issue explicitly asks for.The user's instruction in pull/81#issuecomment-4335908257 was: "name it
dind-box, and support alternative version for all box types we have (for each language and each arch and so on). Let's fully implement it." — that's exactly what this PR does.What you can pull after this lands
For every existing image, there is now a
<image>-dindsibling on both Docker Hub and ghcr.io, multi-arch (linux/amd64 + linux/arm64):konard/boxkonard/box-dindkonard/box-essentialskonard/box-essentials-dindkonard/box-jskonard/box-js-dindkonard/box-pythonkonard/box-python-dindkonard/box-gokonard/box-go-dindkonard/box-rustkonard/box-rust-dindkonard/box-javakonard/box-java-dindkonard/box-kotlinkonard/box-kotlin-dindkonard/box-rubykonard/box-ruby-dindkonard/box-phpkonard/box-php-dindkonard/box-perlkonard/box-perl-dindkonard/box-swiftkonard/box-swift-dindkonard/box-leankonard/box-lean-dindkonard/box-rocqkonard/box-rocq-dindEach dind variant ships the Docker Engine, Docker CLI, containerd, Buildx, and Compose v2, plus an entrypoint that starts the inner
dockerdand drops to theboxuser.How it's structured
Just one generic recipe under
ubuntu/24.04/dind/rather than 14 duplicated Dockerfiles:ubuntu/24.04/dind/DockerfileFROM ${BASE_IMAGE}then runsinstall.sh. Used by the workflow with--build-arg BASE_IMAGE=...for every variant.ubuntu/24.04/dind/install.shdocker-ce,docker-ce-cli,containerd.io,docker-buildx-plugin,docker-compose-plugin,fuse-overlayfs. Addsboxuser to thedockergroup.ubuntu/24.04/dind/dind-entrypoint.shdockerd(auto-picksoverlay2/fuse-overlayfs/vfs), waits up toDIND_WAIT_SECONDSfor the socket, thenrunusers intoboxand chains to the existing/usr/local/bin/entrypoint.sh(or the bare command if the base image doesn't have one — js box). Has env-var overrides for storage driver, data root, log file, and aDIND_SKIP_DAEMON=1for Sysbox-only / DooD use cases.The release workflow runs this recipe in a 14 × 2 matrix (variant × arch), then assembles per-variant multi-arch manifests, mirroring the existing per-image cadence used for js / essentials / languages / full. Change-detection now also has a
dind-changedoutput so unrelated PRs don't trigger dind rebuilds.Recommended invocation (security model)
Full design and threat model: docs/case-studies/issue-80/CASE-STUDY.md. Original case study from earlier in this PR is preserved verbatim and now has a 2026-04-28 implementation update at the top documenting the final naming and scope decision.
Files changed
Test plan
actionlint(zero issues) and Python YAML parser.bash -nsyntax check.docker-build-test: buildsbox-dind-testfrom the freshly-builtbox-testand verifiesdocker --version,dockerd --version,docker buildx version,docker compose version, and/etc/box/variantare all present and correct.ubuntu/24.04/dind/**will rebuild only the dind matrix (dind-changedoutput).--privilegedor--runtime=sysbox-runc. GitHub-hosted runners disallow--privileged, which is why the PR-time smoke test verifies CLI binaries only and leaves the live-dockerd test for users on hosts that allow it.What I learned writing this
While iterating on the workflow I tripped on GitHub Actions' "Exceeded max expression length 21000" limit — the existing release-notes heredoc was already ~17 KB, and adding the dind-box tables pushed the single
run:step over the per-step expression budget. GitHub's failure was opaque (zero jobs, zero billable time, run name = path string) until I scraped the run page HTML for the annotation. Fixed by splitting the dind tables into a separate appended step that builds them via a bash for-loop, keeping the GitHub-side expression interpolation small.How this addresses each requirement from the case study
konard/boxkonard/box-dindand per-language*-dindvariants are builtFROMtheir non-dind sibling.install.shinstalls all five from Docker's apt repo.dind-entrypoint.shstarts dockerd on container boot and waits for the socket.build-dind-amd64(ubuntu-24.04) andbuild-dind-arm64(ubuntu-24.04-arm).${DOCKERHUB_IMAGE_NAME}-...and${GHCR_REGISTRY}/${GHCR_IMAGE_NAME}-....docker psscopingrunusers intobox.--build-arg BASE_IMAGE=...; the workflow matrix targets every base.dind=true/falseoutput indetect-changes.docs/case-studies/issue-80/; updated with implementation header.Fixes #80