Reference
Admin API
scalwsd serves one admin API, HTTP/1.1 with JSON or plain-text answers, on up to four
transports. scalwsctl and the console use it; this page lists every endpoint for tools
that call it directly. Design records: ADR-0017
(socket and views), ADR-0023 (remote admin) and
ADR-0049 (upstream pools).
Transports
| Transport | Configured by | Access | Serves |
|---|---|---|---|
| Admin socket | server.admin.socket, default /run/scalws/admin.sock | Unix socket, mode 0660: the server’s user and group. Each change is audited with the peer’s uid, gid and pid. | Every endpoint. |
| Remote admin | server.admin.remote | TCP with mutual TLS: only clients with a certificate from client_ca. Each change is audited with the certificate subject and the client address. | Every endpoint, like the socket. |
| Metrics listener | server.metrics.address | Loopback TCP, no authentication. | GET of the operational views marked “metrics” below. Changes answer 403; other views answer 403 too. |
| Probe listener | server.health.address | TCP, no authentication. | GET /healthz, GET /readyz, and GET /metrics with server.health.metrics: true; everything else is 404. |
Paths may carry a /v1 prefix (/v1/status and /status are the same endpoint); tools
should use it. Changes use POST (or PUT for upstream members); other methods answer
405, unknown paths 404. Error answers are one line of plain text.
Over the socket with curl:
curl --unix-socket /run/scalws/admin.sock http://localhost/v1/statusViews
All views are GET. “Metrics” marks the views the metrics listener serves as well.
| Path | Metrics | Answer |
|---|---|---|
/v1/metrics | yes | Prometheus text format, with the per-domain counters. See logging-metrics.md. |
/v1/healthz | yes | 200 ok, or 503 draining while the process shuts down. |
/v1/readyz | yes | {"state": "ready" | "active" | "draining", "generation", "instance", "pid"}; 503 while draining. |
/v1/status | yes | Version, generation, uptime, listeners, metrics address, admin socket, counts of tenants and applications, enabled subsystems, draining. |
/v1/apps | yes | One entry per application: domains, runtime type, state, workers, requests in flight, recent requests and p95, diagnostic findings. |
/v1/diagnose?app=tenant/app | yes | Diagnostics of every application, or of one; 404 for an unknown application or before the first window. |
/v1/optimizer | yes | The optimizer journal. |
/v1/explain?app=tenant/app | yes | The last advice of the local advisor; 400 without app, 404 without advice, 503 when server.ai is off. |
/v1/dashboard, / | yes | The read-only HTML dashboard. |
/v1/tenants | no | Tenants with their user, applications, resources and, with cgroups, memory and CPU usage. |
/v1/tenants/usage?interval_ms=1000 | no | CPU, memory, I/O and pressure of each tenant over the interval (200 to 10,000 ms), with its limits; 409 without cgroup v2. |
/v1/config/history | no | The kept configuration generations with time and source. |
/v1/config/diff?from=N&to=M | no | Line diff of two kept generations (plain text); 404 for a generation that is not kept. |
/v1/certs | no | ACME certificate status per application. |
/v1/upstreams | no | Every upstream pool with its members (below). |
/v1/upstreams/{name} | no | One pool; 404 unknown pool. |
Changes
Available on the admin socket and the remote admin listener only.
| Method and path | Effect | Answers |
|---|---|---|
POST /v1/reload | Reads the configuration file and applies it. | 200 {"generation": N}; 422 with the reason when it is refused. |
POST /v1/rollback?generation=N | Applies a kept generation again (default: the previous one). | 200 {"generation": N}; 400 for a non-numeric generation; 409 with the reason. |
POST /v1/apps/{tenant}/{app}/restart | Starts a fresh instance of the application’s runtime, then drains the old one. | 200 {"generation": N}; 409 with the reason. |
POST /v1/apps/{tenant}/{app}/scale?workers=N | Sets the worker target of a managed Node.js pool within its bounds. | 200 {"from", "to", "min", "max"}; 400 without workers; 404 unknown application; 409 for a runtime that cannot be scaled. |
POST /v1/cache/purge?host=H&path=P (or host and prefix, or tag, or all=1) | Removes cached responses. | 200 purged; 400 for another combination; 404 cache disabled. |
POST /v1/explain?app=tenant/app | Queues a request to the local advisor. | 202 queued; 400 without app; 404 unknown application; 429 when the queue is full; 503 when server.ai is off. |
POST /v1/tenants/{tenant}/limits?cpu=&memory=&memory_percent=&io_weight=&io_bandwidth=&clear= | Sets (or with clear=cpu,memory,io_weight,io_bandwidth or all removes) run-time cgroup limits of a tenant, kept until scalwsd restarts. | 200 with the override and the effective limits; 400 invalid value; 404 unknown tenant; 409 without cgroup v2; 500 when the kernel refused it. |
PUT /v1/upstreams/{name} | Replaces the members of a members_from: api pool (below). | 200, 400, 404, 409, 413, 501. |
Upstream pools
GET /v1/upstreams
Every pool of the active configuration, sorted by name:
{
"pools": [
{
"name": "api",
"members_from": "api",
"balance": "round_robin",
"members": [
{
"address": "192.0.2.5:8080",
"weight": 2,
"active_requests": 1,
"consecutive_failures": 0,
"ejected_until_unix_ms": null
},
{
"address": "192.0.2.6:8080",
"weight": 1,
"active_requests": 0,
"consecutive_failures": 0,
"ejected_until_unix_ms": 1792000000000
}
]
}
]
}| Field | Meaning |
|---|---|
name | Pool name (upstreams.<name>). |
members_from | file or api. |
balance | round_robin, least_conn or hash. |
members[].address | Canonical member address: host:port, [ipv6]:port or unix:/path. |
members[].weight | Weight, 1 to 1000. |
members[].active_requests | Requests in flight on the member (until their response body is finished). |
members[].consecutive_failures | Failures since the last success or ejection. |
members[].ejected_until_unix_ms | End of the current ejection in Unix milliseconds; null while the member takes traffic. |
With no pools the answer is {"pools": []}.
GET /v1/upstreams/{name}
One pool, as one element of pools above. 404 unknown pool for a name that is not
configured.
PUT /v1/upstreams/{name}
Replaces the whole member list of a pool with members_from: api, without a reload:
curl --unix-socket /run/scalws/admin.sock -X PUT \
--data '{"members": [{"address": "192.0.2.5:8080", "weight": 2}, {"address": "192.0.2.6:8080"}]}' \
http://localhost/v1/upstreams/apiThe body is a JSON object with exactly one key, members: a list of
{"address": "...", "weight": N}, where weight is optional (default 1). Addresses follow
the configuration’s rules: ip:port, [ipv6]:port, host:port, or unix:/path as the
only member of a plain-HTTP pool. An empty list empties the pool (its requests then get
503 with Retry-After: 1).
| Status | Body | When |
|---|---|---|
200 | The pool, as in GET /v1/upstreams/{name} | The list was installed. |
400 | invalid body: ... or the first problem, for example members[1].weight: must be between 1 and 1000 | Not a valid body; an invalid address or weight; a duplicate member; more than 10,000 members. |
404 | unknown pool | No pool of that name. |
409 | the pool takes its members from the configuration file (members_from: file) | A file pool. |
413 | body larger than 2 MiB | The body is too large. |
501 | run-time pool members are not supported under scalws-controller yet | The process is an instance of scalws-controller (listener_mode: inherited or reuseport). |
403 | Sent to the metrics listener. |
A refused request changes nothing. The change is serialised with reloads; members that stay (same canonical address) keep their connections, load and health, requests in flight to a removed member finish, and the series of removed members leave the metrics. Members set this way survive reloads but not a restart. See load-balancing.md.
scalwsctl upstreams, scalwsctl upstreams show <pool> and
scalwsctl upstreams set <pool> <addr[=weight]>... call these endpoints
(scalwsctl).
Under scalws-controller
The controller’s local control API (controller.socket, default
/run/scalws-controller/control.sock, mode 0600; see
adaptive-runtime.md) answers the same three calls without the
/v1 prefix:
| Method and path | Answer |
|---|---|
GET /upstreams, GET /upstreams/{name} | Forwarded to the admin socket of one serving instance: 200 {"ok": true, "result": <the instance's answer>}, or 502 {"ok": false, ...} with the instance’s status and answer, the connection error, or no serving instance. |
PUT /upstreams/{name} | 501 {"error": "run-time pool members are not supported under scalws-controller yet; use members_from: file"}. |
A pool name that does not follow the name rule (lower-case letter first, then a-z, 0-9,
-, _, at most 63 characters) is 404 there.