Decision API

Fairvisor Edge exposes a minimal HTTP API on port 8080.

Endpoints

EndpointMethodPurpose
/v1/decisionPOSTEvaluate a request against the active policy bundle
/v1/debug/sessionPOSTStart per-user debug session (when debug secret is configured)
/v1/debug/logoutPOSTEnd per-user debug session
/livezGETLiveness probe — always returns 200 ok
/readyzGETReadiness probe — 200 ok if a bundle is loaded
/metricsGETPrometheus metrics

POST /v1/decision

The primary endpoint. Evaluates the incoming request headers against the active policy bundle and returns 200 (allow) or 429 (reject).

Request

HeaderRequiredDescription
X-Original-MethodYesHTTP method of the original request (GET, POST, …)
X-Original-URIYesFull URI of the original request, including query string
X-Original-HostConditionallyOriginal request host. Required when using selector hosts.
AuthorizationNoBearer JWT; used for jwt:<claim> descriptor keys
X-Forwarded-ForNoClient IP chain; used for ip:address and ip:country keys
X-ASN-TypeNoOptional fallback input for ip:type when set by gateway/proxy
X-Tor-ExitNoOptional override for ip:tor (1/0, true/false, yes/no)
Any custom headerNoUsed when a policy references header:<name> limit keys

The request body is not consumed.

Response: 200 Allow

HTTP/1.1 200 OK
RateLimit-Limit: 1000
RateLimit-Remaining: 847
RateLimit-Reset: 1
RateLimit: "per-org-rps";r=847;t=1

Body is not part of the public contract (current implementation returns empty body on allow).

Response: 429 Reject

HTTP/1.1 429 Too Many Requests
Retry-After: 15
X-Fairvisor-Reason: token_bucket_exceeded
RateLimit-Limit: 1000
RateLimit-Remaining: 0
RateLimit-Reset: 15
RateLimit: "api-limits";r=0;t=15

Body: implementation-dependent (do not rely on it).

Debug session headers (optional)

When debug session is active, Fairvisor also returns debug headers such as:

  • X-Fairvisor-Debug-Policy
  • X-Fairvisor-Debug-Rule
  • X-Fairvisor-Debug-Reason

See Debug Session.

Response: 503 No bundle

If no policy bundle is loaded (e.g., immediately after startup before SaaS delivers config):

HTTP/1.1 503 Service Unavailable
X-Fairvisor-Reason: no_bundle_loaded

GET /livez

Always returns 200 while the nginx process is running. Use as a Kubernetes livenessProbe.

HTTP/1.1 200 OK

ok

GET /readyz

Returns 200 when a policy bundle is loaded and the engine is ready to process requests. Returns 503 during startup before the first bundle is applied.

HTTP/1.1 200 OK

{"status":"ready","policy_version":"...","policy_hash":"...","last_config_update":1736940000}
HTTP/1.1 503 Service Unavailable

{"status":"not_ready","reason":"no_policy_loaded"}

Use as a Kubernetes readinessProbe to hold traffic until the edge is ready.


GET /metrics

Returns Prometheus-format metrics.

HTTP/1.1 200 OK
Content-Type: text/plain; version=0.0.4

# HELP fairvisor_decisions_total Total decisions
# TYPE fairvisor_decisions_total counter
fairvisor_decisions_total{action="allow",reason="all_rules_passed",policy="policy-a",route="/api/v1"} 1542
fairvisor_decisions_total{action="reject",reason="token_bucket_exceeded",policy="policy-a",route="/api/v1"} 23
...

See Metrics for the full metric catalogue.


Mode differences

Aspectdecision_servicereverse_proxy
Host/method/URI sourceX-Original-Host, X-Original-Method, X-Original-URInginx $host, $request_method, $uri
Response headersWritten directly to the decision responseStored in ngx.ctx; written by header_filter_by_lua_block
Request body handlingNot consumedConsumed/forwarded to backend

In reverse_proxy mode, callers do not call /v1/decision directly — Fairvisor Edge acts as a transparent proxy that enforces policy on every proxied request.


Authentication

The decision endpoint itself has no authentication. It is an internal service; expose it only to trusted callers (nginx internal location, Envoy cluster, VPC-internal network). Do not expose port 8080 to the public internet.