Decision API
Fairvisor Edge exposes a minimal HTTP API on port 8080.
Endpoints
| Endpoint | Method | Purpose |
|---|---|---|
/v1/decision |
POST |
Evaluate a request against the active policy bundle |
/v1/debug/session |
POST |
Start per-user debug session (when debug secret is configured) |
/v1/debug/logout |
POST |
End per-user debug session |
/livez |
GET |
Liveness probe — always returns 200 ok |
/readyz |
GET |
Readiness probe — 200 ok if a bundle is loaded |
/metrics |
GET |
Prometheus 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
| Header | Required | Description |
|---|---|---|
X-Original-Method |
Yes | HTTP method of the original request (GET, POST, …) |
X-Original-URI |
Yes | Full URI of the original request, including query string |
X-Original-Host |
Conditionally | Original request host. Required when using selector hosts. |
Authorization |
No | Bearer JWT; used for jwt:<claim> descriptor keys |
X-Forwarded-For |
No | Client IP chain; used for ip:address and ip:country keys |
X-ASN-Type |
No | Optional fallback input for ip:type when set by gateway/proxy |
X-Tor-Exit |
No | Optional override for ip:tor (1/0, true/false, yes/no) |
| Any custom header | No | Used 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-PolicyX-Fairvisor-Debug-RuleX-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
| Aspect | decision_service |
reverse_proxy |
|---|---|---|
| Host/method/URI source | X-Original-Host, X-Original-Method, X-Original-URI |
nginx $host, $request_method, $uri |
| Response headers | Written directly to the decision response | Stored in ngx.ctx; written by header_filter_by_lua_block |
| Request body handling | Not consumed | Consumed/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.