Summary
Charon enforces its "Orthrus is strictly read-only" guarantee via two separately maintained Docker API allowlists:
backend/internal/orthrus/muzzle.go — filters requests inside Charon, before they enter the tunnel.
agent/muzzle/muzzle.go — a second, independent filter on the remote agent binary itself, right before it dials the real Docker socket (defense-in-depth).
These two files are hand-copied lists of the same intended policy, in two different Go packages. They have already diverged once in practice: a fix adding /images/{name}/json and /distribution/{name}/json support (for third-party update-checker tools like Dockhand) was implemented in the backend copy only (commits 98a68b67, b71cbd62), and shipped/rebuilt/redeployed twice before anyone noticed the agent's copy still rejected the same endpoints (fixed in eabf358d). The bug was invisible to CI because nothing caught the mismatch between the two lists.
Contributing factor: agent/ has no CI-enforced quality gates
Investigation during the fix above found that all of this repo's lint/staticcheck/coverage tooling (make lint-fast, scripts/go-test-coverage.sh, lefthook hooks, etc.) targets backend/ only. The agent/ module — which is a separate Go module/build target — has no equivalent enforcement. This is part of why the missing allowlist entries in agent/muzzle/muzzle.go weren't flagged automatically.
Suggested follow-up work
- Unify the allowlist source of truth. Options to evaluate: share a single allowlist definition between
backend/internal/orthrus and agent/muzzle (may require restructuring given agent/ is built as a minimal standalone binary and may not want to import backend-side packages/deps), or at minimum add a CI check that diffs the two lists and fails if they drift.
- Wire up CI-enforced tooling for
agent/: lint/staticcheck, coverage gate, and inclusion in the existing pre-commit/lefthook pipeline, matching what backend/ already has.
- Consider a shared test corpus (see companion issue on path-normalization order) that exercises both filters identically.
Related
docs/reports/qa_report.md, docs/plans/current_spec.md, commits 98a68b67, b71cbd62, eabf358d.
Summary
Charon enforces its "Orthrus is strictly read-only" guarantee via two separately maintained Docker API allowlists:
backend/internal/orthrus/muzzle.go— filters requests inside Charon, before they enter the tunnel.agent/muzzle/muzzle.go— a second, independent filter on the remote agent binary itself, right before it dials the real Docker socket (defense-in-depth).These two files are hand-copied lists of the same intended policy, in two different Go packages. They have already diverged once in practice: a fix adding
/images/{name}/jsonand/distribution/{name}/jsonsupport (for third-party update-checker tools like Dockhand) was implemented in the backend copy only (commits98a68b67,b71cbd62), and shipped/rebuilt/redeployed twice before anyone noticed the agent's copy still rejected the same endpoints (fixed ineabf358d). The bug was invisible to CI because nothing caught the mismatch between the two lists.Contributing factor:
agent/has no CI-enforced quality gatesInvestigation during the fix above found that all of this repo's lint/staticcheck/coverage tooling (
make lint-fast,scripts/go-test-coverage.sh,lefthookhooks, etc.) targetsbackend/only. Theagent/module — which is a separate Go module/build target — has no equivalent enforcement. This is part of why the missing allowlist entries inagent/muzzle/muzzle.goweren't flagged automatically.Suggested follow-up work
backend/internal/orthrusandagent/muzzle(may require restructuring givenagent/is built as a minimal standalone binary and may not want to import backend-side packages/deps), or at minimum add a CI check that diffs the two lists and fails if they drift.agent/: lint/staticcheck, coverage gate, and inclusion in the existing pre-commit/lefthook pipeline, matching whatbackend/already has.Related
docs/reports/qa_report.md,docs/plans/current_spec.md, commits98a68b67,b71cbd62,eabf358d.