> ## Documentation Index
> Fetch the complete documentation index at: https://proxy-hopper.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Admin Server

> The Proxy Hopper admin server provides a REST health endpoint, a login endpoint, and the GraphQL management API — all on a separate port from the proxy.

## Overview

Installing `proxy-hopper-webserver` (`pip install proxy-hopper-webserver`) adds a second HTTP server, on its own port (default `8081`), exposing:

| Endpoint             | Auth required | Description                                        |
| -------------------- | ------------- | -------------------------------------------------- |
| `GET /health`        | No            | Liveness check — always returns `{"status": "ok"}` |
| `POST /auth/login`   | No            | Exchange admin credentials for a JWT               |
| `GET /api/v1/status` | Read          | Current target list and server state               |
| `GET /graphql`       | Read          | GraphQL playground (browser UI)                    |
| `POST /graphql`      | Read / Write  | GraphQL API                                        |

The GraphQL API lets you query and mutate **targets**, **IP pools**, and **providers** at runtime — without restarting the server and without editing the config file.

## Embedded vs separate process

There are two ways to run the admin server, and **which one is correct depends entirely on which backend you're using**:

| Backend  | How to run admin                                                         | Why                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| -------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `redis`  | `proxy-hopper admin --config config.yaml` (separate process/pod)         | Both processes connect to the same Redis instance, so the admin API sees real, live pool/quarantine/target state. This is the recommended production shape — scale the proxy independently, run a single admin replica.                                                                                                                                                                                                                                                                                                                                          |
| `memory` | `proxy-hopper run --config config.yaml --admin` (embedded, same process) | The memory backend is plain in-process asyncio queues and dicts — it **cannot be shared across OS processes**. A separately-run `proxy-hopper admin` process with `backend: memory` gets its own private, disconnected backend: it shows only what was seeded from YAML at its own startup, never any live runtime state (pool rotation, quarantine, admin-made edits). `--admin` avoids this entirely by running both the proxy listener and the admin server in one process, sharing one backend object directly — no cross-process boundary to fail to cross. |

Since `backend: memory` only ever supports a single proxy instance anyway (there's no way to share its state across replicas without Redis), embedding admin doesn't cost you anything you didn't already have — there was never going to be more than one node.

```bash theme={}
# Single-container / homelab / Docker Compose: proxy + admin together
proxy-hopper run --config config.yaml --admin

# Redis-backed HA: proxy replicas scale independently, one admin replica
proxy-hopper run --config config.yaml --backend redis --redis-url redis://redis:6379/0
proxy-hopper admin --config config.yaml --backend redis --redis-url redis://redis:6379/0
```

<Note>
  If `proxy-hopper-webserver` isn't installed, `--admin` fails startup immediately with a clear error rather than silently running without it — the same fail-fast behavior as requesting `--backend redis` without `proxy-hopper-redis` installed.
</Note>

## Enabling the admin server

Both the embedded (`run --admin`) and separate-process (`admin` subcommand) topologies share the same `server.admin`/`adminPort`/`adminHost` config fields:

```yaml theme={}
server:
  admin: true
  adminPort: 8081        # default — omit to accept the default
```

Or via environment variable:

```bash theme={}
PROXY_HOPPER_ADMIN=true
PROXY_HOPPER_ADMIN_PORT=8081
```

<Warning>
  The admin server runs on `0.0.0.0` by default. In production, restrict access at the network level — bind it to an internal interface or place it behind a firewall/ingress that only your operations tooling can reach.
</Warning>

## Authentication

If `auth.enabled: true` is set, every request to protected endpoints must include an `Authorization: Bearer <token>` header. The token can be:

* A **JWT** obtained from `POST /auth/login` (requires `auth.admin` to be configured)
* An **API key** declared in `auth.apiKeys`
* An **OIDC token** (when `auth.oidc` is configured)

The `/health` endpoint is always public.

When auth is **disabled**, all endpoints are accessible without credentials. This is acceptable for internal-only deployments where the admin port is not exposed outside your network.

## Obtaining a JWT

```bash theme={}
curl -X POST http://localhost:8081/auth/login \
  -d "username=admin&password=mypassword"
```

Returns:

```json theme={}
{
  "access_token": "eyJ...",
  "token_type": "bearer"
}
```

Pass the token in subsequent requests:

```bash theme={}
curl -H "Authorization: Bearer eyJ..." \
     http://localhost:8081/api/v1/status
```

## GraphQL playground

With the admin server running, open `http://localhost:8081/graphql` in a browser to access the interactive GraphQL playground. You can explore the schema, run queries, and test mutations directly from the browser.

If auth is enabled, add your token via the playground's "Headers" panel:

```json theme={}
{
  "Authorization": "Bearer eyJ..."
}
```

See [GraphQL API](/admin/admin-server/graphql) for the full query and mutation reference.

## Ports summary

| Port   | Purpose                | Config key           | Env var                     |
| ------ | ---------------------- | -------------------- | --------------------------- |
| `8080` | Proxy (client traffic) | `server.port`        | `PROXY_HOPPER_PORT`         |
| `8081` | Admin server           | `server.adminPort`   | `PROXY_HOPPER_ADMIN_PORT`   |
| `9090` | Prometheus metrics     | `server.metricsPort` | `PROXY_HOPPER_METRICS_PORT` |
