SaaS Connection
When FAIRVISOR_SAAS_URL is set, the edge instance runs a persistent connection loop to Fairvisor SaaS. This page documents the full protocol.
Overview
Edge startup
└─ Register → POST /api/v1/edge/register
└─ Pull config → GET /api/v1/edge/config
└─ ACK → POST /api/v1/edge/config/ack
└─ [request traffic begins]
└─ Heartbeat loop (every HEARTBEAT_INTERVAL seconds)
└─ Config poll loop (every CONFIG_POLL_INTERVAL seconds)
└─ Event flush loop (every EVENT_FLUSH_INTERVAL seconds)
All API calls use:
Authorization: Bearer <FAIRVISOR_EDGE_TOKEN>Content-Type: application/json- Base URL:
FAIRVISOR_SAAS_URL
Step 1 — Registration
On startup, the edge registers itself with SaaS:
POST /api/v1/edge/register
{
"edge_id": "edge-prod-us-east-1",
"version": "0.1.0",
"timestamp": 1736940000
}
On success (2xx): heartbeat, config, and event timers are initialised. On failure: startup fails. The container exits non-zero.
Step 2 — Initial config pull
Immediately after registration, the edge pulls its policy bundle:
GET /api/v1/edge/config
| Response | Meaning |
|---|---|
200 OK |
New bundle in response body; parse, compile, and apply |
304 Not Modified |
Bundle unchanged (no-op) |
401 / 403 |
Credential problem; stop retrying |
The bundle JSON structure is the same as a local policy.json. After successful application, the edge sends an ACK.
Step 3 — Config ACK
POST /api/v1/edge/config/ack
{
"edge_id": "edge-prod-us-east-1",
"version": "0.1.0",
"hash": "sha256:abcdef…",
"status": "applied", // or "rejected"
"error": null, // rejection reason if status="rejected"
"timestamp": 1736940001
}
The ACK communicates whether the edge successfully loaded the bundle or rejected it (e.g., validation failure, schema mismatch).
Heartbeat loop
Every FAIRVISOR_HEARTBEAT_INTERVAL seconds (default 5 s):
POST /api/v1/edge/heartbeat
{
"edge_id": "edge-prod-us-east-1",
"version": "0.1.0",
"policy_hash": "sha256:abcdef…",
"uptime": 3612,
"timestamp": 1736943612
}
Response may include:
| Field | Meaning |
|---|---|
server_time |
SaaS wall clock (used for clock skew detection) |
config_update_available: true |
Trigger an immediate config pull |
If config_update_available is true, the edge pulls the config outside its normal poll interval.
Config poll loop
Every FAIRVISOR_CONFIG_POLL_INTERVAL seconds (default 30 s), the edge polls for config changes using a conditional request:
GET /api/v1/edge/config
If-None-Match: <current_bundle_hash>
This avoids re-parsing an unchanged bundle. When 304 is returned, no action is taken.
Event batching
Decision events (every allow/reject decision, with metadata) are buffered in memory and flushed every FAIRVISOR_EVENT_FLUSH_INTERVAL seconds (default 60 s):
POST /api/v1/edge/events
Idempotency-Key: batch_<edge_id>_<timestamp>_<seq>
{
"edge_id": "edge-prod-us-east-1",
"events": [ ... ], // up to 100 per batch
"clock_skew_suspected": false,
"clock_skew_seconds": 0
}
- Max buffer size: 1 000 events (older events are dropped when full)
- Max batch size: 100 events per flush
- Idempotency key prevents double-counting on retry
Retry and backoff
All SaaS API calls use exponential backoff:
delay = min(2^attempt, 60 seconds) + random_jitter
Non-retriable responses (401, 403, 404) clear the retry state immediately. Retriable responses (5xx, network errors) schedule a retry with backoff.
SaaS circuit breaker
The edge maintains a circuit breaker for SaaS connectivity:
| State | Trigger | Behaviour |
|---|---|---|
CLOSED |
Normal | All SaaS calls proceed |
OPEN |
5 consecutive failures | SaaS calls suppressed; edge continues with cached bundle |
HALF_OPEN |
30 s after opening | One probe call allowed |
CLOSED |
2 successes in HALF_OPEN | Returns to normal |
When the circuit is open, the edge continues enforcing the last known bundle. The fairvisor_saas_reachable metric is set to 0.
Clock skew detection
If |local_time − server_time| > 10 seconds, the edge sets clock_skew_suspected: true in subsequent event flushes. This is informational — enforcement is not affected.
Security considerations
FAIRVISOR_EDGE_TOKENis sent as a Bearer token on every SaaS request. Rotate it from the Fairvisor dashboard.- The config bundle can optionally be signed. If a signing key is configured, the edge rejects bundles that fail signature verification.
- Bundle versioning is monotonically increasing — the edge rejects a bundle with a lower version than the current one.
Graceful shutdown behavior
On worker shutdown, the edge attempts to flush buffered SaaS events before exit.
- Runtime sets
worker_shutdown_timeoutto35s. - Shutdown handler calls SaaS
flush_events()before final worker termination. - If the process is hard-killed before graceful timeout, in-memory buffered events may be lost.
Operational recommendation: use termination grace period of at least 35 seconds for SaaS-connected deployments.