Skip to content

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

@ilmoniemi

Description

@ilmoniemi

User Story

As an operator deploying pyrycode-relay, I want the relay to refuse to start when --insecure-listen is set in production, so that a dev-config promoted to a prod deploy fails loudly at boot rather than serving plaintext traffic.

Context

The relay currently exposes mutually-exclusive --domain (autocert TLS) and --insecure-listen (plaintext) flags. Today nothing distinguishes a dev environment from a production environment, so an operator who copy-pastes a dev manifest into prod can boot a plaintext listener facing the internet.

This ticket introduces a deterministic production-mode signal — the env var PYRYCODE_RELAY_PRODUCTION=1 — and a boot-time check that refuses to start when production-mode is on AND --insecure-listen is set.

The env var contract is also expected to be consumed by sibling startup checks (e.g. refuse-to-run-as-uid-0-in-production, #78). This ticket is the canonical place where the contract is defined.

Related: #9 (ErrCacheDirInsecure boot-time refusal pattern), #39 (multi-instance check). Split from #42.

Acceptance Criteria

  • An exported helper in internal/relay reports whether the process is in production mode by reading PYRYCODE_RELAY_PRODUCTION; "1" means production, anything else (including unset, "0", or other values) means non-production. The PYRYCODE_RELAY_PRODUCTION=1 contract is documented in the helper's Go doc comment.
  • A check function in internal/relay returns the exported sentinel ErrInsecureListenInProduction (branchable via errors.Is) when production-mode is on AND --insecure-listen is set, and returns nil otherwise.
  • The check is wired into cmd/pyrycode-relay/main.go after flag parse, before any listener is started, with fail-fast on error and a structured log line naming the env var and the fix.
  • Unit tests cover the full 2×2 matrix: (a) non-production + insecure-listen → nil; (b) production + insecure-listen → sentinel; (c) production + --domain (autocert) → nil; (d) non-production + --domain → nil. The env var is read through a test seam (injected getter or equivalent), not by mutating process env.

Technical Notes

Size Estimate

S

Metadata

Metadata

Assignees

No one assigned

    Labels

    security-sensitiveTouches auth, crypto, or internet-exposed input pathssize:sSmall ticket: <100 lines production code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions