feat: ship a container image on ghcr.io - #106
Merged
Merged
Conversation
Publish a multi-architecture (amd64/arm64) image built from a digest-pinned rust:1.97.0-alpine stage onto a digest-pinned gcr.io/distroless/static-debian13:nonroot, for HTTP-transport deployments that would otherwise need a Rust toolchain. The release workflow builds each architecture on a native runner and joins the digests into one manifest only after both push, so a tag never resolves to a half-populated multi-arch image; the container jobs are siblings of publish, not upstream of it, so a broken image cannot hold back a crates.io release whose binaries already shipped. CI gains a build-only amd64 leg, gated on paths-filter because a full image build would otherwise be the slowest job on every PR. A docker dependabot ecosystem keeps the pinned base digests moving. The image presets BUGWARDEN_POLICY, which turns the bare binary's allow-all fallback into a startup error when the operator mounts no policy: the one deployment shape that fails closed by construction (tightening only, I9). MCP_HOST defaults to 0.0.0.0 because a namespace-local bind is unreachable; the bearer gate, not the bind address, is the access control, and the image refuses to start without a token unless --insecure-no-auth is passed. docs/DESIGN.md's TLS note carried a standing trigger — re-evaluate the OS-trust-store decision if this project ever ships its own container image. RE-EVALUATED 2026-08-18: the decision stands. The distroless base carries the ca-certificates bundle, so the image works unconfigured while an operator behind an internal CA can still mount over the bundle's path, which roots baked into the binary would take away.
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Multi-arch (amd64/arm64) container image at
ghcr.io/plusky/bugwarden, modeled on ruoqa-mcp's container commit and adapted: workspace build, digest-pinned bases (both FROM lines, with a docker dependabot ecosystem to move them),BUGWARDEN_POLICYpreset so a missing policy mount fails closed instead of inheriting allow-all (I9, recorded on DESIGN.md's CLI table row), native-runner release matrix with digest-join manifest, build-only CI leg behind a paths filter, README Container section, compose.yaml with hardening (read_only,cap_drop: ALL,no-new-privileges,init).Verified on the built image (podman, both arches)
--insecure-no-auth→ refuses before binding, exit 1 (nothing listening on the published port).tools/list11 vs 20; the 9 removed are exactlyWRITE_TOOLS; calling one returns the router's own "tool not found".--initturns 13s SIGKILL stops into 1s exit 143.Adversarial review before opening
Two reviewers, both initially NOT MERGE-SAFE; all findings fixed: the docs previously described the bearer gate before it existed (resolved by landing #105 first and re-verifying against the real gate); a false custody claim reproduced against the binary; a fabricated cross-reference (twice);
.dockerignore/Dockerfile comments describing mechanisms that don't exist; tag-pinned bases vs the repo's SHA discipline (now digest-pinned; the fix pass caught its own first attempt producing two wrong digests); compose's audit-volume example that couldn't start; the DESIGN.md re-evaluation now dated per the file's DECIDED precedent. All four new action SHAs verified against upstream tags.Verification
All five AGENTS.md commands independently re-run on the rebased tree: fmt/clippy×2 clean, 471 passed / 0 failed, deny fully green. actionlint clean; all YAML parses; zero broken README anchors.
Known follow-up (issue to come): bugwarden handles only SIGINT, so as PID 1 without
--initadocker stoptakes the 13s SIGKILL path — k8s has no--init, so the proper fix is a SIGTERM handler in main.rs.