User Story
As an operator deploying pyrycode-relay in a Linux container, I want the relay to refuse to start when its effective Linux capabilities exceed an explicit allowlist, so that a container runtime that grants stray capabilities (CAP_SYS_ADMIN, CAP_NET_ADMIN, etc.) fails loudly at boot rather than silently accepting elevated privileges.
Context
Container runtimes can grant capabilities via --cap-add, capability bounding sets, or default Docker profiles. A misconfigured deploy that grants the relay extra capabilities escapes CI build scans and runs with more privilege than intended.
This ticket adds a Linux-only boot-time check that parses the effective capability set from /proc/self/status (the CapEff: line) and aborts if any capability outside an explicit allowlist is present. On non-Linux platforms (darwin dev runs), the check is a no-op with a single startup log line.
Related: #9 (ErrCacheDirInsecure boot-time refusal pattern).
Acceptance Criteria
Technical Notes
/proc/self/status's CapEff: line is a 64-bit hex mask. Linux capability bit positions are stable kernel ABI.
- Whether to also check
CapPrm / CapBnd / CapInh is architect's call — CapEff is the minimum.
- No existing
_linux.go / _other.go files in the repo; this ticket establishes the convention.
Size Estimate
S
Split from #42.
User Story
As an operator deploying pyrycode-relay in a Linux container, I want the relay to refuse to start when its effective Linux capabilities exceed an explicit allowlist, so that a container runtime that grants stray capabilities (CAP_SYS_ADMIN, CAP_NET_ADMIN, etc.) fails loudly at boot rather than silently accepting elevated privileges.
Context
Container runtimes can grant capabilities via
--cap-add, capability bounding sets, or default Docker profiles. A misconfigured deploy that grants the relay extra capabilities escapes CI build scans and runs with more privilege than intended.This ticket adds a Linux-only boot-time check that parses the effective capability set from
/proc/self/status(theCapEff:line) and aborts if any capability outside an explicit allowlist is present. On non-Linux platforms (darwin dev runs), the check is a no-op with a single startup log line.Related: #9 (
ErrCacheDirInsecureboot-time refusal pattern).Acceptance Criteria
ErrUnexpectedCapabilityexists ininternal/relay, branchable viaerrors.Is, alongside an exported allowlist of capabilities the relay legitimately needs. The error message identifies the unexpected capability (bit position and symbolic name where known) and the allowlist contents. Architect determines the allowlist contents by reading current code — at minimumCAP_NET_BIND_SERVICEif the relay binds privileged ports without setcap, otherwise the empty set./proc/self/status'sCapEff:hex mask and returnsErrUnexpectedCapabilityif any bit outside the allowlist is set, nil otherwise. On non-Linux platforms the check is a no-op returning nil and logsskipping linux-only capability check on <GOOS>exactly once at startup. The Linux/non-Linux split is handled at compile time (build tags); this ticket is the first introduction of that pattern in the repo, so architect chooses the file naming.cmd/pyrycode-relay/main.goafter flag parse, before any listener is started, with fail-fast on error and a structured log line that names the unexpected capability and the operator fix (--cap-dropor equivalent)./proc/self/statuscontent → wrapped error (not a panic). The CapEff string is injected via a test seam rather than touching real/proc.PYRYCODE_RELAY_PRODUCTION=1contract; this ticket does not consume it.Technical Notes
/proc/self/status'sCapEff:line is a 64-bit hex mask. Linux capability bit positions are stable kernel ABI.CapPrm/CapBnd/CapInhis architect's call —CapEffis the minimum._linux.go/_other.gofiles in the repo; this ticket establishes the convention.Size Estimate
S
Split from #42.