# IP Quality

> Check each agent's egress IP against streaming and AI services, and score it for proxy, VPN, and fraud risk.

URL: https://docs.serverbee.app/en/docs/ip-quality

IP Quality lets each agent assess its VPS egress IP and report the results back to the server. It does two things:

1. **Service unlock detection** — the agent issues HTTP requests from its egress IP to determine the unlock status of popular streaming, AI, and social services.
2. **IP metadata and risk signals** — the server derives country and region from its local GeoIP database, then uses the configured third-party provider for supported network-type flags and, when available, a fraud risk score.

Results appear on a dedicated **IP Quality** sidebar route (global overview), a per-server detail tab, and — when enabled per status page — on the public status page.

## Requirements [#requirements]

* **`CAP_IP_QUALITY`** (bit `1024`) must be enabled on the agent. It is **on by default** (part of `CAP_DEFAULT`).
* A usable GeoIP MMDB is required for local baseline metadata. If no database is loaded, those fields remain unavailable; third-party enrichment can still provide supported risk and network-type fields. See [Configuration](/en/docs/configuration) for `geoip.mmdb_path`.

## Capability (agent-owned) [#capability-agent-owned]

`CAP_IP_QUALITY` is owned by the agent host, like every capability — the server cannot turn it on or off. It is enabled by default, so no action is needed to use IP Quality. To **disable** it on a specific host, add `ip_quality` to the agent's `[capabilities]` deny list (or pass `--deny-cap ip_quality`) and restart the agent:

```toml
[capabilities]
deny = ["ip_quality"]
```

```bash
# Or via the command line
./serverbee-agent --deny-cap ip_quality
```

If the agent does not report `ip_quality`, the agent runs no checks and the UI shows the standard **disabled** placeholder. See [Capabilities](/en/docs/capabilities).

## Built-In Services [#built-in-services]

Nine services are seeded at startup. Each ships with a hardcoded detector that issues an HTTP request from the agent's egress IP and interprets the response to determine unlock status.

| Service            | Category  | Notes                                              |
| ------------------ | --------- | -------------------------------------------------- |
| Netflix            | Streaming | Detects full unlock vs. originals-only vs. blocked |
| Disney+            | Streaming |                                                    |
| YouTube Premium    | Streaming |                                                    |
| Amazon Prime Video | Streaming |                                                    |
| HBO Max            | Streaming |                                                    |
| Spotify            | Social    |                                                    |
| ChatGPT            | AI        |                                                    |
| Gemini             | AI        |                                                    |
| TikTok             | Social    |                                                    |

Each service can be individually enabled or disabled in **Settings → IP Quality → Service Catalog**. Built-in services cannot be deleted; their detection logic is immutable.

## Custom Services [#custom-services]

In addition to the built-in set, administrators can define custom services in **Settings → IP Quality → Service Catalog → Add service**.

Each custom service consists of:

* **URL** — the endpoint to probe. Must use `https://` or `http://` on port 80 or 443. Private / loopback addresses are rejected at creation time and re-validated by the agent on each request and redirect hop (SSRF guard).
* **Method** — HTTP method (`GET`, `HEAD`, etc.) and optional request headers.
* **Match rules** — an ordered list of rules evaluated against the response. The first matching rule wins. Each rule matches on one of:
  * HTTP status code (exact or range)
  * Response body regex
  * Redirect target URL pattern

The possible results are `unlocked`, `restricted`, `blocked`, `failed`, or `unsupported`.

## Check Cadence [#check-cadence]

The agent runs checks:

* **On schedule** — every 12 hours by default, configurable in **Settings → IP Quality → Settings → Check interval**.
* **When the egress IP changes** — the agent automatically reruns on detected IP changes.
* **On demand** — click **Check now** on any server's IP Quality tab.

Check schedules are agent-side. The server only pushes the service catalog and forwards manual triggers.

## IP Metadata and Risk Scoring [#ip-metadata-and-risk-scoring]

After receiving unlock results from an agent, the server:

1. **Derives available baseline metadata** from a loaded local GeoIP MMDB, without an external request. The downloadable DB-IP Lite Country database provides country and region. If the MMDB is missing, unreadable, or does not cover the address, local baseline fields remain empty.
2. **Runs the configured third-party provider unless `risk_provider` is `none`.** Anonymous ipapi.is supplies proxy, VPN, datacenter, Tor, and abuse flags, which ServerBee also uses to derive IP type, but its minimal response does not contain the nested score fields. A numeric 0–100 score and risk level (`low` / `medium` / `high`) require an ipapi.is API key and its full response. Results are cached by IP for 24 hours, so repeated checks of the same IP do not generate repeated API calls.

If no provider returns a numeric score, `risk_score` and `risk_level` are `null` / `unknown`. Local metadata appears only when a usable MMDB is loaded.

### Configuring a Risk Provider [#configuring-a-risk-provider]

Set `ip_quality.risk_provider` in `server.toml` (or `SERVERBEE_IP_QUALITY__RISK_PROVIDER`) to one of:

| Provider              | Value      | Notes                                                                                                                                                                                |
| --------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ipapi.is (default)    | `ipapi_is` | Anonymous: 100 requests per client IP per UTC day with flags but no numeric score. Free account/API key: 1,000 requests per day with the full response required for the 0–100 score. |
| ip-api.com (fallback) | `ip-api`   | **Free tier, non-commercial use only. HTTP-only endpoint.** No API key. Proxy/hosting flags and a derived IP type, but no numeric risk score.                                        |
| Disabled              | `none`     | Local GeoIP metadata only when a usable MMDB is loaded; no risk score.                                                                                                               |

**Default behavior** (no configuration needed): the Server calls ipapi.is anonymously. Anonymous access is limited to 100 requests per client IP per UTC day and returns flags that ServerBee uses for risk signals and a derived IP type, but no numeric risk score. Only a failed primary request, including an HTTP 429 after quota exhaustion, triggers the `ip-api` fallback; a successful response without a score does not. The fallback can supply proxy/hosting flags and a derived IP type, but not a numeric risk score. See the official [ipapi.is developer limits](https://ipapi.is/developers.html) and [pricing](https://ipapi.is/pricing.html).

Create a free ipapi.is account and configure its API key for 1,000 requests per day and the full response required for the 0–100 score:

```toml
[ip_quality]
risk_provider = "ipapi_is"
risk_provider_fallback = "ip-api"  # default; set to "none" to disable

[ip_quality.ipapi_is]
api_key = "your_api_key"
# endpoint = "https://api.ipapi.is"  # optional; override for self-hosted instances
```

To disable risk scoring entirely:

```toml
[ip_quality]
risk_provider = "none"
```

<Callout type="warn">
  `ip-api` (ip-api.com) free tier allows non-commercial use only. Its API endpoint is HTTP (not HTTPS). Review its [terms of service](https://ip-api.com/docs/legal) before using it in production.
</Callout>

<Callout type="info">
  **Migrating from an older config?** Legacy provider names (`scamalytics`, `ipqs`, `proxycheck`, `abuseipdb`) are no longer supported. If your `server.toml` or environment variables reference them, the server will log a startup warning and fall back to no-op scoring. Update `risk_provider` to `ipapi_is` (or `none` to opt out).
</Callout>

Full configuration reference: [Configuration → IP Quality](/en/docs/configuration).

## Viewing Results [#viewing-results]

| Where                                        | What it shows                                                                                                                  |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **IP Quality** sidebar route (`/ip-quality`) | All-servers × services unlock matrix; IP quality card per server (ASN, IP type, risk score and level badge, proxy/VPN markers) |
| Server detail → **IP Quality** tab           | Single server's IP quality card, its unlock matrix, status-change history, and a **Check now** button                          |
| Public status page                           | Unlock matrix section, shown only when `show_ip_quality` is enabled for that page                                              |

Unlock status updates appear in real time over WebSocket — no page refresh required.

## Unlock Status Values [#unlock-status-values]

| Status        | Meaning                                         |
| ------------- | ----------------------------------------------- |
| `unlocked`    | Service is fully accessible from this egress IP |
| `restricted`  | Partial access (e.g. Netflix originals-only)    |
| `blocked`     | Service is not accessible from this egress IP   |
| `failed`      | Network error or timeout during the check       |
| `unsupported` | Service check not applicable for this IP type   |

## Public Status Page Exposure [#public-status-page-exposure]

IP Quality is **off by default** on all public status pages. To enable it for a specific page:

1. Go to **Settings → Status Pages**, edit the page.
2. Toggle on **Show IP Quality**.

For unauthenticated visitors, the egress IP is masked as `*.*.*.*`. Authenticated viewers see the full IP.

## Retention [#retention]

Status-change history events (the `unlock_event` log) are retained for **90 days** by default. Tune with `retention.ip_quality_event_days` (env: `SERVERBEE_RETENTION__IP_QUALITY_EVENT_DAYS`). The hourly cleanup task purges expired rows automatically.

The latest unlock result and IP quality snapshot per server are kept indefinitely (they are replaced on each run, not accumulated).

## Data Flow [#data-flow]

```text
Agent
  │  (CAP_IP_QUALITY effective)
  ├─ runs unlock checks every interval_hours / on IP change / on RunNow
  │   AgentMessage::UnlockResults  (WebSocket)
  ▼
Server
  ├─ upserts unlock_result + appends unlock_event on status change
  ├─ broadcasts BrowserMessage::IpQualityUpdate (unlock results, ip_quality: null)
  ├─ background task: ip_risk scoring (GeoIP + optional third-party provider, cache-first)
  ├─ upserts ip_quality_snapshot
  └─ broadcasts BrowserMessage::IpQualityUpdate (ip_quality: full data)
                    │
                    ▼
             /ip-quality  +  IP Quality tab  +  public status page
```

<Cards>
  <Card title="Capabilities" href="/en/docs/capabilities" />

  <Card title="Configuration" href="/en/docs/configuration" />

  <Card title="Security Events" href="/en/docs/security-events" />

  <Card title="Public Status Page" href="/en/docs/status-page" />
</Cards>
