# 🩺 Admin API The Admin API is an HTTP endpoint on the server process for reading and replacing the running configuration, checking readiness, and scraping metrics. `pingclair reload` and `pingclair stop` use it. This page describes **v0.2.2**. ## 🔌 Enable the Admin API The API exists only when the global `admin` option is present, or when `pingclair run` starts without any configuration file. The default address is `127.0.0.1:2019`. ```caddyfile { admin 127.0.0.1:2019 } http://:8080 { respond "ok" } ``` An occupied admin port prevents startup with `failed to bind admin API on ADDR`. `admin off` disables the Admin API listener. ## 🔐 Who may call it - **With a token**, written as `admin
`, every request must send `Authorization: Bearer `. A missing or incorrect token receives `401` with `WWW-Authenticate: Bearer`. - **Without a token**, the server logs a warning and admits loopback clients only. Other clients receive `403`. - **`origins` and `enforce_origin`** in an `admin { … }` block restrict which browser origins may call the API. A request without an `Origin` header is admitted unless `enforce_origin` is set. Every endpoint below, `/live` and `/ready` included, is behind these checks. ## 🧭 Endpoints | Method and path | What it does | | --- | --- | | `GET /live` | `200` while the process runs, draining included. | | `GET /ready` | `200` once every listener is bound; `503` before that and from the moment a stop begins. | | `GET /metrics` | Prometheus text exposition. Empty when metrics collection is off. | | `GET /config/[path]` | Read the running configuration, or one value inside it. | | `POST`, `PUT`, `PATCH`, `DELETE /config/[path]` | Change one value and apply the result. | | `GET` … `DELETE /id/` | The same, addressed by an `@id` field in the document. | | `POST /load` | Replace the whole configuration. | | `POST /adapt` | Convert a Pingclairfile to the JSON document, without applying it. | | `POST /stop` | Stop the process gracefully. | | `GET /reverse_proxy/upstreams` | The upstream addresses the configuration names, including those inside `handle` blocks. | | `GET /cache` | Response-cache size against its ceiling. | | `POST /cache/purge` | Purge one cached URL. The body is `{"host": "…", "path": "…"}`. | The configuration document is Pingclair's own JSON schema, the one `pingclair adapt` prints. A Caddy document (`{"apps": …}`) is refused with a message that names the schema this endpoint takes, and that refusal is a deliberate boundary rather than a missing adapter: the two documents share no top-level key and no handler name, so accepting Caddy's JSON would mean a second configuration surface kept in step with Caddy's module tree. What `POST /load` does accept besides its own JSON is a **Caddyfile**, sent with `Content-Type: text/caddyfile` — the format operators keep in git. ## 📄 Reading and writing configuration - **A missing path reads as `null`.** `GET /config/` for a key or index that does not exist answers `200` with the JSON value `null`. Writes to a missing path still fail, and the error names the nearest parent. - **Reads carry a path-qualified `Etag`.** A config write that sends `If-Match` with the value read from the same path is applied only if the document has not changed since; otherwise it receives `412` and the running document stays unchanged. A write without `If-Match` is unconditional. This does not extend conditional writes to `/load` or `/adapt`. - **Secrets are masked.** Reads show `[redacted]` in place of the admin token, DNS provider credentials, basic-auth hashes, FastCGI `env` entries with credential-like names, and header values named `Authorization`, `Proxy-Authorization`, `Cookie`, `Set-Cookie`, or containing `api-key`, `token`, `secret`, or `password`. The stored configuration keeps the real values, and a traversal write (`PATCH /config/…`) edits them in place. - **A document carrying `[redacted]` as a secret is refused** by `/load` and `POST /config`. Restore the original secret values before loading an exported document. - **Reads keep working during a reload.** Each request is answered from one published generation of the document. A write concurrent with another reload may receive `409`, or `412` for a conditional write; read again and retry. ## 🔁 What a load can change A load swaps the configuration atomically: a request sees either the old one or the new one, and a configuration that fails to compile leaves the old one serving. Changes that need new sockets or a new process-wide policy are refused with `409` and `restart_required`, and nothing is applied: - adding or removing a listen address; - adding a TLS hostname; - changing a startup-fixed global policy, `metrics` and `trusted_proxies` included; - enabling mutual TLS on a listener that allows session resumption. A process started without a configuration file is the exception: on Unix, its first `/load` may add plaintext HTTP listeners. TLS and HTTP/3 listeners need a file at startup. Reloadable process-log settings are not subject to those startup-policy restrictions. ## 📊 Metrics Nothing is collected unless the global `metrics` option is set; without it, `/metrics` and a site's `metrics` route answer `200` with an empty body. `metrics { per_host }` adds a `host` label for the hosts the configuration serves, and folds every other `Host` into `other`. The standard request families use Caddy's names. The families renamed in 0.2.0: | Before 0.2.0 | From 0.2.0 | | --- | --- | | `pingclair_requests_total` | `caddy_http_requests_total` | | `pingclair_request_duration_seconds` | `caddy_http_request_duration_seconds` | | `pingclair_request_size_bytes` | `caddy_http_request_size_bytes` | | `pingclair_response_size_bytes` | `caddy_http_response_size_bytes` | | `pingclair_response_duration_seconds` | `caddy_http_response_duration_seconds` | | `pingclair_request_errors_total` | `caddy_http_request_errors_total` | | `pingclair_admin_http_requests_total` | `caddy_admin_http_requests_total` | | `pingclair_reverse_proxy_upstreams_healthy` | `caddy_reverse_proxy_upstreams_healthy` | Histogram `_bucket`, `_sum`, and `_count` series follow their family. The old names are no longer exported. Metrics without a Caddy equivalent keep their `pingclair_` names: connections, overload, cache, access-log drops, upstream timing, errors and retries, TLS and HTTP/3 counters, readiness, configuration version, queue occupancy, circuit state, and process resources. The labels are Pingclair's; the names do not imply Caddy's full label schema. ## 🧭 Related pages - [Command line](/reference/command-line/): `reload` and `stop`, which call this API. - [Configuration model](/concepts/configuration/#-a-reload-swaps-the-configuration-without-a-restart): what a reload can and cannot apply. - [Directives: global options](/reference/directives/#global-options): `admin` and `metrics`. 📚 The [CHANGELOG](https://github.com/dorianverlaine/pingclair/blob/main/CHANGELOG.md) records these changes and their upgrade consequences.