Kill Switch

The kill switch is an emergency blocking mechanism that runs before any rate-limit rule evaluation. When a request matches a kill switch entry, it is rejected immediately with no further processing.

See Kill Switches configuration for the policy schema. This page covers the algorithm internals.

How it works

Kill switches are the first check in the evaluation pipeline:

request
  └─ bundle loaded?  (503 if not)
  └─ kill switch scan  ← evaluated here, BEFORE route matching and rules
  └─ route matching
  └─ rule evaluation (token bucket, cost budget, …)
  └─ allow

This guarantees that a kill switch fires regardless of which policy or rule would have matched.

For each kill switch entry, the engine:

  1. Extracts the descriptor value from the request using the entry’s scope_key
  2. Compares the extracted value to scope_value (string equality, case-sensitive)
  3. If a route is present, also checks whether the request path is an exact match
  4. Checks the expires_at timestamp, if present — expired entries are skipped
  5. If all conditions match: reject immediately

A request must match all conditions in a single entry. Entries are evaluated in declaration order; the first match wins. The scan is O(n) over all entries — expired entries are skipped after a single timestamp comparison.

Configuration

Kill switch entries are defined in the policy bundle. See Kill Switches configuration for the full schema.

Supported scope keys

Scope key Source Example value
jwt:<claim> JWT claim extracted from Bearer token jwt:org_id"org-abc"
header:<name> HTTP request header header:x-tenant-id"tenant-42"
query:<param> URL query parameter query:api_key"k_abc123"
ip:address Client IP address "203.0.113.42"
ip:country GeoIP country code "TR", "RU"
ip:asn GeoIP ASN number "AS12345"
ua:bot Bot detection from User-Agent "true"

TTL / expiration

The optional expires_at field accepts an ISO 8601 UTC timestamp:

{
  "scope_key": "jwt:org_id",
  "scope_value": "org-abc",
  "expires_at": "2026-03-01T00:00:00Z"
}

The timestamp is parsed into a Unix epoch at bundle load time for zero-cost checking on each request. An entry with an expires_at in the past is silently skipped.

Route scoping

Omitting route makes the entry match any path. Providing route restricts the block to an exact URI path:

{
  "scope_key": "ip:country",
  "scope_value": "TR",
  "route": "/api/v1/completions"
}

This entry only blocks Turkish IPs on /api/v1/completions; other paths are unaffected.

Response headers

When a kill switch fires:

HTTP 429 Too Many Requests
Retry-After: 3600
X-Fairvisor-Reason: kill_switch

The Retry-After value is fixed at 3600 seconds (1 hour). The optional reason field in the kill switch entry is logged but not exposed to the client.

Failure behavior

Kill switches have no per-request mutable state, so there is no shared dict operation on the hot path and no dict-failure code path. If the policy bundle fails to load at startup, Fairvisor returns 503 Service Unavailable for all requests until a valid bundle is loaded.

Shadow mode

In shadow mode, the kill switch check is still performed but the result is recorded as would_reject = true rather than actually rejecting the request. This lets you test a new kill switch entry in production without blocking traffic.

Tuning

The kill switch scan is O(n) where n is the number of entries. For deployments with large entry sets (>100), scope entries with route to reduce the effective scan depth per request path.

Example

{
  "kill_switches": [
    {
      "scope_key": "jwt:org_id",
      "scope_value": "org-abc"
    }
  ]
}

This blocks all requests from org org-abc. Add "expires_at": "2026-04-01T00:00:00Z" for a time-limited block.