Skip to content

feat(relay): refuse to boot with --insecure-listen in production (#77)#82

Merged
ilmoniemi merged 3 commits into
mainfrom
feature/77
May 13, 2026
Merged

feat(relay): refuse to boot with --insecure-listen in production (#77)#82
ilmoniemi merged 3 commits into
mainfrom
feature/77

Conversation

@ilmoniemi
Copy link
Copy Markdown
Contributor

What

Adds a boot-time check that refuses to start the relay when PYRYCODE_RELAY_PRODUCTION=1 AND --insecure-listen is set. Defines the PYRYCODE_RELAY_PRODUCTION contract (exact \"1\" means on; anything else means off) as the canonical signal for production mode — sibling startup checks (#78) will reuse this.

  • New file: internal/relay/production.go — exports IsProductionMode, CheckInsecureListenInProduction, and the sentinel ErrInsecureListenInProduction.
  • Wired in cmd/pyrycode-relay/main.go after flag-parse, before any listener is started, with structured logging (env_var, fix) and os.Exit(2) to match the existing flag-validation guard.
  • Env var is read via an injected func(string) string test seam — never mutated process env.

Issue

Closes #77.

Testing

internal/relay/production_test.go covers:

  • IsProductionMode value matrix (8 cases, including the non-\"1\" rows that lock in the exact-match contract against future strconv.ParseBool drift).
  • CheckInsecureListenInProduction 2×2 AC matrix.
  • Sentinel branchability via errors.Is.

All tests pass under go test -race ./...; go vet ./... clean; binary builds.

Architecture compliance

Follows the spec at docs/specs/architecture/77-refuse-insecure-listen-in-production.md exactly: injected-getter seam (no process-env mutation), exact-string match (no truthy parsing), lazy read on every call, exit code 2, structured log fields named in the AC, no Config struct (premature abstraction). Mirrors the ErrCacheDirInsecure (#9) sentinel pattern and the PYRYCODE_RELAY_SINGLE_INSTANCE (#39/#64) env-var shape. Per the security review's SHOULD FIX, the log line names the env var by name only — never logs its value.

ilmoniemi added 2 commits May 13, 2026 08:55
Adds the PYRYCODE_RELAY_PRODUCTION=1 contract and a boot-time check that
returns ErrInsecureListenInProduction when production mode is on AND
--insecure-listen is set. Wired into cmd/pyrycode-relay/main.go after
flag-parse with structured remediation logging and exit 2.

The env var is read via an injected getter so tests cover the matrix
without mutating process env.
@ilmoniemi
Copy link
Copy Markdown
Contributor Author

Code Review: #77

Decision: PASS

Findings

None.

Summary

Clean additive change: new file internal/relay/production.go with IsProductionMode + CheckInsecureListenInProduction + sentinel ErrInsecureListenInProduction, a 7-line wiring block in cmd/pyrycode-relay/main.go:45-51, and table-driven tests covering the AC matrix.

Verified against the criteria:

  • Spec compliance — Symbol names, signatures, exit code (2), structured log fields (env_var, fix), and placement (after the existing flag-validation guard, before NewRegistry) match the architecture doc verbatim. No unscoped refactors.
  • AC coverageIsProductionMode value matrix covers the 8 cases the spec calls out; CheckInsecureListenInProduction table covers the AC's 2×2 verbatim; sentinel branchability is locked in by TestErrInsecureListenInProduction_IsBranchable.
  • Test seamfunc(string) string injected getter, no os.Setenv / t.Setenv anywhere; all tests are t.Parallel()-safe. Passes go test -race ./... and go vet ./... cleanly.
  • Go idiom — Sentinel + Go doc comment mirrors the ErrCacheDirInsecure pattern in internal/relay/tls.go:15-19. errors.Is used for assertion. t.Parallel() + table-driven matches tls_test.go style. No swallowed errors, no goroutines, no shared state.
  • Security goggles (per security-sensitive label) — The architect's spec contains a ## Security review section with PASS verdict and a complete findings list, so step 1 is satisfied. Diff scan: no tokens/secrets in logs (the log line names the env var by name only, honouring the architect's inline SHOULD FIX); no new file or subprocess ops; no crypto; no listener opened before the check runs — the whole point is to refuse to open one. No // #nosec annotations.
  • Blast radius — purely additive exports; no signature changes, no removals; sole call site (main.go:45) is in this diff. codegraph_callers not needed.

Test, vet, and build are green locally. CI test job is green; security and image-scan were still IN_PROGRESS at review time — merge gate should wait for those.

Ready to merge once remaining CI checks complete.

@ilmoniemi ilmoniemi merged commit 15dc00f into main May 13, 2026
3 checks passed
@ilmoniemi ilmoniemi deleted the feature/77 branch May 13, 2026 06:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

relay: refuse to boot with --insecure-listen in production mode

1 participant