CI red on main is environmental, not #169: sandbox seccomp tests + golangci-lint install both fail on the runner #170
Labels
No labels
burndown-2026-06
sunday-sprint
coherence-core
consult
headless
interactive
P0
P1
P2
P3
P4
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
coilyco-flight-deck/cli-guard#170
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
TL;DR
cli-guard
mainCI has been structurally red since the canonical gate landed (#165), and it is not caused by #169 (the override engine). #169's code is clean:go build,go vet, andgolangci-lint run ./...(v2.12.2) all pass atcc4f8f7. The red is two environmental failures that also failed on the pre-#169 runs 224 and 226:testjob - 3cli/sandboxsecurity tests fail because the Forgejodockerrunner can't enforce the sandbox's jail + seccomp filter.lintjob - fails in ~14s, before a lint run could finish (and local lint = 0 issues), so it is the golangci-lint install step failing on the runner, not a code violation.Diagnosed from a fresh clone at
cc4f8f7; the 3 sandbox failures reproduce on any box that can't enforce seccomp.Cause 1:
testjob - sandbox security tests can't enforce on the runnergo test -race ./...fails atcli/sandbox:The sandbox (
cli/sandbox/seccomp_linux.go,sandbox_linux.go) jails via unprivileged user namespaces +PR_SET_NO_NEW_PRIVS+ aseccomp(2)filter + CAP_SYS_ADMIN-in-userns bind-mounts. The tests run past their existingt.Skipf("unprivileged namespaces unavailable")guards (so userns is available), but the default Docker seccomp profile on the job container blocks theunshare/mount/seccompsyscalls the jail needs, so the enforcement never bites and the security claims fail.Fix (no dind, no sidecar)
Make the runner capable with one container option, plus a skip-guard safety net:
ci.ymltest job: run its container with--security-opt seccomp=unconfined(viacontainer.options) so the in-container test can create its userns, bind-mount its jail, and install its own seccomp filter. The tests then run for real and guard the boundary - the "capable runner" outcome, with zero nested Docker. (If a cap is also needed,--cap-add=SYS_ADMIN; evidence says the seccomp profile is the blocker, userns already works.)t.Skipfprecedent (sandbox_linux_test.go:102,133) so these three detect missing enforcement capability and skip (not fail) anywhere it is genuinely absent - local dev, a locked-down runner. Green everywhere, actually-tested on CI.Dependency to confirm: the Forgejo Actions runner must permit a workflow to set container
options/security-opt. If yes -> one-lineci.ymlchange. If the runner forbids it -> a small host-side runner-config change to allow it (still not dind/sidecar); until then the skip-guard keeps CI green (degraded: the sandbox claims skip rather than run). Do NOT reach for docker-in-docker or a privileged sidecar - the security-opt on the existing runner is the whole fix (Kai).Cause 2:
lintjob - golangci-lint install fails on the runnerThe
lintjob fails in ~14s (id=1972, run 228), far too fast for a real lint pass, andgolangci-lint run ./...(v2.12.2, the pinned CI version) reports 0 issues locally atcc4f8f7. So the failure is the install step, not the code:The runner reaches
go.dev(thetestjob's Go install succeeds) but the lint install pulls fromraw.githubusercontent.com, which appears unreachable/blocked from the runner.Fix
Install golangci-lint from a source the runner already reaches - e.g.
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION)via the module proxy (works locally, confirmed), or vendor the install via the Go toolchain / a mirror, with a retry. Confirm the exact failure from thelintjob log (run 228, id 1972) before settling the source.Why this matters beyond cli-guard
Every recent headless agent run (#169, ward#338, ward#347) retro-noted that golangci-lint / trufflehog / kdlfmt are absent in the dev-base container, so agents cannot self-run the lint gate and keep landing code the canonical CI must catch. That is an agentic-os dev-base image gap worth a sibling issue - the in-container pre-commit tooling should match the CI gate so an agent can verify before it pushes. (Out of scope for this issue; noting the upstream root cause.)
Scope / dispatch
Two independent fixes (sandbox test job + lint install) - splittable into two runs or one. Not auto-dispatched (Kai dispatches; this touches the security gate + CI infra). A fix run can verify the sandbox change in-container (tests skip/pass) and the lint change on push; the security-opt's real effect needs a push to the actual runner to confirm.
References
cli-guard#169 (the override engine wrongly suspected; it is clean), cli-guard#165 (the canonical CI gate this fixes),
cli/sandbox/seccomp_linux.go+sandbox_linux.go(the jail),.forgejo/workflows/ci.yml(the gate). Diagnosed from a fresh clone atcc4f8f7, golangci-lint v2.12.2, reproduced sandbox failures locally.Ordering: the sandbox-test fix here has a prerequisite in the infra repo - infrastructure#401 (let the forgejo runner honor a per-job --security-opt seccomp=unconfined). That lands FIRST (or confirms workflow options are already honored, in which case this ci.yml change suffices alone). Dispatch infrastructure#401 before this. Note: infrastructure Flux-auto-deploys to the live fleet runner, so that one wants a supervised dispatch, not fire-and-forget.
Upstream root cause of the lint-job red filed: agentic-os#292 - bake golangci-lint/trufflehog/kdlfmt into the dev-base image so agents can self-run the gate (the lint job re-installs golangci-lint from githubusercontent precisely because the agent container never had it). Fixing #292 stops the recurring tax across every repo; this issue (#170) still fixes the cli-guard-side runner/test specifics.