Skip to content

Configuration

Reeflex is configured entirely by environment variables — nothing is hardcoded, and secrets are passed by reference (Vault / env), never committed. Defaults below are the code defaults; only REEFLEX_CORE_URL (adapters) is effectively required.

Adapter → core (the canonical trio)

Every adapter that talks to reeflex-core over HTTP uses the same three variables:

Variable Default Purpose
REEFLEX_CORE_URL Base URL of the engine, e.g. https://reeflex-core.internal. Required.
REEFLEX_CORE_TOKEN Bearer token, when the engine enforces auth.
REEFLEX_VERIFY_SSL true TLS certificate verification. Every adapter offers a verify-off switch for self-signed/invalid certs, at the user's risk — default is on.

Adapters also expose a mode:

Variable Default Purpose
REEFLEX_MODE enforce observe records the verdict it would have applied and lets the action proceed (fails open); enforce applies it (fails closed). Calibrate in observe, then switch.

Engine server

Variable Default Purpose
REEFLEX_HOST 127.0.0.1 Bind address.
REEFLEX_PORT 8080 Bind port.
REEFLEX_AUTH_TOKEN If set, all routes except GET /healthz require this bearer token.
REEFLEX_MAX_BODY_BYTES 262144 Max request body (256 KiB).

Portal-issued gate credentials (POST /v1/decide only)

Unset by default, and unset means none of this runs — a deployment that takes a new build sees no change. Setting the URL is the operator saying "this portal may issue credentials for my engine", which is a real trust statement.

Variable Default Purpose
REEFLEX_GATE_INTROSPECTION_URL A Reeflex portal's /api/v1/agent/introspect. When set, POST /v1/decide also accepts an rfx_ac_ credential that portal minted, validated by asking it.
REEFLEX_GATE_INTROSPECTION_CACHE_SECONDS 30 How long a POSITIVE answer is reused. 0 = a call per decision. Negative answers are never cached, so a freshly minted credential works on its first use. This is the revocation window: a core that has already seen a credential keeps honouring it for up to this long after the gate is revoked.
REEFLEX_GATE_INTROSPECTION_TIMEOUT 5.0 Seconds. On timeout the credential is not validated and the request is refused (fail closed).
REEFLEX_GATE_INTROSPECTION_CA_BUNDLE (system) CA bundle for the portal's certificate, for a self-hosted portal behind a private CA. There is no switch to disable verification: this call carries a credential.

Where the credential comes from and what it is for. A customer pastes a one-line onboarding command from their portal; the portal mints a credential for that one gate during the token exchange and the client stores it at 0600. The line itself never carries it. Full write-up: reeflex-core/app/gate_credential.py.

The three refusals are three different answers, so a log tells them apart:

portal says no, or is unreachable 401 unauthorized
the declared gate is not the credential's 403 {"reason": "gate_mismatch"}
no X-Reeflex-Gate header at all 403 {"reason": "gate_not_declared"}

A caller using such a credential MUST declare its gate in X-Reeflex-Gate. REEFLEX_AUTH_TOKEN is tried first and needs no header, so an operator's own traffic never makes an introspection call and is unaffected by the portal being down. Hold-resolution routes do not accept these credentials.

What it costs, since it is a network call on the decision path. One outbound request per cache miss; a new fail-closed mode (portal unreachable ⇒ that caller's decisions are denied) that applies only to portal-credential callers; and revocation bounded by the cache TTL above. Measure it in your deployment.

Policy engine (OPA)

Variable Default Purpose
REEFLEX_OPA_BIN opa Path to the OPA binary.
REEFLEX_POLICY_DIR (bundled) Directory of Rego policy packs; empty uses the bundled base policy.
REEFLEX_OPA_TIMEOUT 10 OPA evaluation timeout (seconds).
REEFLEX_WINDOW_SECONDS 3600 Rolling window for cumulative session state (e.g. R5 delete budget).

Audit & holds

Variable Default Purpose
REEFLEX_AUDIT_LOG (off) Path to the append-only JSONL audit log. Empty disables file audit.
REEFLEX_HOLDS_PATH (in-memory) Path to the holds store.
REEFLEX_HOLD_TTL_SECONDS 14400 Default hold TTL (4 hours) before expires_ts.
REEFLEX_RESOLUTION_POLICY Which principal type may resolve a hold (HIL / AIL policy). See Why Reeflex.
REEFLEX_RESOLVER_TOKENS Binds a bearer token to the principal it is: {"tok": {"type":"human","id":"alice"}}. Without it the approving principal is only asserted by the caller — and since 0.2.0 an unverifiable approver is refused, not recorded.
REEFLEX_REQUIRE_VERIFIED_APPROVER true (since 0.2.0) Refuse to resolve a hold whose approver cannot be verified (403 principal_not_verified). false/0/no/off opts out; anything unrecognised reads as the default.

REEFLEX_RESOLUTION_POLICY checks the principal type the caller claims; REEFLEX_RESOLVER_TOKENS is what establishes who the caller is. See reeflex-core README → Approver verification.

Verified approvers

Since reeflex-core 0.2.0 this is on by default, and it is a breaking change for anyone who was resolving holds with a self-asserted approver. The image sets REEFLEX_REQUIRE_VERIFIED_APPROVER=true; upgrading a deployment that never configured REEFLEX_RESOLVER_TOKENS turns every hold resolution into 403 principal_not_verified.

Two ways forward, and the refusal itself names both:

# THE FIX — bind each approver's bearer token to the principal it IS.
# Inline JSON, or a path to a JSON file. Re-read per request: no restart.
REEFLEX_RESOLVER_TOKENS='{"tok_live_alice": {"type": "human", "id": "alice@example.com"}}'

# THE ESCAPE HATCH — pre-0.2.0 behaviour while you wire the above up.
# Holds resolve on the caller's word, and every record says so
# (decided_by_verified: false), so the deployment cannot claim four-eyes.
REEFLEX_REQUIRE_VERIFIED_APPROVER=false

Why the default moved: with it off, one bearer token could raise an irreversible production hold and approve it as human:totally-invented-auditor, and core minted and persisted the Art. 14 record saying a human had overseen it (RFX-84, reproduced live). Shipping an artefact whose default accepts an invented approver, in a product whose claim is evidence of human oversight, is not defensible.

The 403 carries a machine-readable remedy alongside error/reason:

{
  "error": "principal_not_verified",
  "reason": "the approver human:alice@example.com is asserted by the caller and this core cannot check it: ...",
  "hold_id": "…",
  "remedy": {
    "principal": "human:alice@example.com",
    "why": "verification_not_configured",   // or "unbound_credential"
    "actions": ["set REEFLEX_RESOLVER_TOKENS to …", "or … REEFLEX_REQUIRE_VERIFIED_APPROVER=false"],
    "docs": "https://github.com/Reeflex-io/reeflex/blob/main/docs/reference/configuration.md#verified-approvers"
  }
}

Freeze (operator kill-switch)

Variable Default Purpose
REEFLEX_FREEZE false When true, every non-read action is denied under reeflex.policy/frozen; a state change fires a freeze.flipped webhook + audit entry and a SIEM kill_switch event.

SIEM export & webhooks

Variable Default Purpose
REEFLEX_SYSLOG_ENABLED false Emit decision/lifecycle events over syslog to a SIEM.
REEFLEX_SYSLOG_ADDRESS host:port of the syslog collector.
REEFLEX_SYSLOG_PROTOCOL udp udp, tcp, or tls.
REEFLEX_SYSLOG_FORMAT json Wire format.
REEFLEX_SYSLOG_FACILITY local0 Syslog facility.
REEFLEX_SYSLOG_TLS_VERIFY true Verify the collector's TLS cert (when protocol is tls).
REEFLEX_WEBHOOK_URL (off) Endpoint for hold/freeze webhooks.
REEFLEX_WEBHOOK_QUEUE_SIZE 1000 Bounded outbound webhook queue.

See SIEM export for the event shapes and the syslog wiring.

Adapter-specific settings

Each adapter documents its own additional variables (e.g. the MCP gateway's REEFLEX_MCP_CONFIG, upstream mappings, and timeouts) in its own README — reeflex-mcp/, reeflex-wordpress/, reeflex-claude/.