Skip to content

symfonic.platform.posture

posture

SCOPE-5 — the production gate, and the dev-mode posture it cannot cover.

The shipped fail-fast is preserved verbatim in behaviour: production detected via SYMFONIC_ENV / APP_ENV / ENVIRONMENT / NODE_ENV ∈ {production, prod} with no verifier registered raises, and ALLOW_INSECURE_PROD=true downgrades it to a CRITICAL log. What moves is where it runs: host assembly, as part of the readiness decision (HOST-5/-6), instead of as a side effect of building a router.

What is new is :class:AuthPosture, and it exists because T4.1.1 recorded a defect and left it open. In dev mode the missing-verifier warning is one-shot: after the first request, a deployment whose environment variable is spelled prd or live — not on the recognised list — trusts X-Tenant-ID in silence forever. A log line that fires once is not a monitoring surface. The posture is a value a readiness endpoint or a diagnostic can read on every poll, and it counts the requests served on trust so the number is not zero when somebody finally looks.

AuthPosture dataclass

AuthPosture(environment: str, production: bool, verifier_registered: bool, insecure_override: bool, requests_served_untrusted: int = 0)

Continuously observable answer to "is this host trusting headers?".

Every field is something an operator would want on a dashboard, and requests_served_untrusted is the one that turns a silent misconfiguration into a rising number.

detect_environment

detect_environment(environ: dict[str, str] | None = None) -> str

The first recognised environment name, or "unknown".

environ is a parameter rather than a read of os.environ at call time so a host can be constructed under a configuration it was given — HOST-1 reads configuration once, and a component that re-reads the process environment on its own is a second configuration source.

Source code in src/symfonic/platform/posture.py
def detect_environment(environ: dict[str, str] | None = None) -> str:
    """The first recognised environment name, or ``"unknown"``.

    ``environ`` is a parameter rather than a read of ``os.environ`` at call
    time so a host can be constructed under a configuration it was *given* —
    HOST-1 reads configuration once, and a component that re-reads the process
    environment on its own is a second configuration source.
    """
    source = os.environ if environ is None else environ
    for var in PRODUCTION_ENV_VARS:
        value = source.get(var, "").strip().lower()
        if value:
            return value
    return "unknown"

production_auth_gate

production_auth_gate(resolver: object) -> None

HOST-5/HOST-6 — fail closed at startup, or log CRITICAL and continue.

Takes the resolver structurally (anything with posture()) so host assembly can gate an adopter's own resolver, not only the shipped one. A host that cannot build its auth path does not start in a degraded "allow everything" mode.

Source code in src/symfonic/platform/posture.py
def production_auth_gate(resolver: object) -> None:
    """HOST-5/HOST-6 — fail closed at startup, or log CRITICAL and continue.

    Takes the resolver structurally (anything with ``posture()``) so host
    assembly can gate an adopter's own resolver, not only the shipped one. A
    host that cannot build its auth path does not start in a degraded
    "allow everything" mode.
    """
    posture_of = getattr(resolver, "posture", None)
    if not callable(posture_of):
        raise RuntimeError(
            "the production auth gate needs a resolver that can report its "
            "posture(); a resolver that cannot describe whether it authenticates "
            "cannot be gated, and an ungateable auth path is not a safe default"
        )
    posture: AuthPosture = posture_of()
    if not posture.production:
        return
    if posture.insecure_override:
        logger.critical(
            "ALLOW_INSECURE_PROD detected — tenant auth disabled in production. "
            "Data WILL leak across tenants. Register a verifier immediately.",
        )
        return
    if not posture.verifier_registered:
        raise RuntimeError(
            "Production environment detected but no tenant auth verifier is "
            "registered. Construct the ScopeResolver with a verifier before "
            "mounting routers, or set ALLOW_INSECURE_PROD=true to bypass "
            "(NOT RECOMMENDED).",
        )