37 KiB
Administration API
The API is served on the separate administration listener, normally
http://127.0.0.1:9090. It is never mounted on reverse-proxy HTTP/TLS listeners.
Loopback HTTP is the default. Settings can enable administration HTTPS, choose a
certificate and persist a non-loopback bind; plain remote HTTP requires the explicit
startup --allow-remote-admin gate. Responses carry no-store, CSP and other browser
protection headers.
Browser documentation and anonymous surface
Open /docs on the administration listener after signing in. The OpenAPI JSON,
JavaScript, styles and Swagger vendor assets all require the same session. Writes
are opt-in and CSRF-protected. See API_ACCESS.md.
Only GET/HEAD /, the panel routes /overview, /vhosts, /streams,
/acl-profiles, /configuration, /tls, /cache, /logging, /settings,
/assets/login.js, /assets/login-langs.json, /assets/style.css, /favicon.svg,
/healthz, /api/auth/status, and POST /api/auth/setup, /api/auth/login are
anonymous. Panel routes serve the same guarded shell as /, so a browser refresh
preserves the selected subpage instead of falling back to the landing page. Root
optionally authenticates and selects login or operational HTML. /healthz is exactly {"status":"ok"}.
Unknown/private paths and method mismatches return a generic 401 without a session.
Only top-level /docs and /docs/ navigations redirect to /?next=docs; API/schema
fetches never redirect to login HTML. Other invalid Host/Origin checks can return 403.
Authentication
| Method / path | Input or result |
|---|---|
GET /api/auth/status |
Public setup_required boolean only; no version or operational state |
POST /api/auth/setup |
One-time {token, username, password}; creates no session |
POST /api/auth/login |
{username, password}; sets session cookie and returns session data |
GET /api/auth/session |
Username, CSRF token, absolute expiry and idle timeout |
POST /api/auth/logout |
Revoke the current session |
POST /api/auth/logout-all |
Revoke all administrator sessions |
POST /api/auth/password |
{current_password, new_password}; changes password and revokes sessions |
Mutations, including setup/login, require Origin equal to the active administration scheme plus {Host} and
X-RustProxy-Request: 1. Authenticated mutations also need X-CSRF-Token from
session data. Send JSON as application/json. A permanent bearer access token
is not supported; the old setup capability does not authorize API reads/writes.
The rustproxy_session cookie is HttpOnly, SameSite=Strict and Path=/ with the configured
7–30 day absolute lifetime (default 30 days); server-side revocation still applies. HTTPS
administration adds the Secure attribute; behind a trusted HTTPS-terminating reverse proxy
the external forwarded scheme controls that flag. A protected SSH/VPN tunnel remains valid,
while direct remote administration should use HTTPS plus firewall/private-network policy.
Direct IP/localhost Host values must use the actual administration port. A DNS name explicitly
listed in administration.allowed_hosts represents the browser-facing authority and may use
the trusted reverse proxy's external port. Foreign Origin/cross-site requests are rejected.
There is no permissive CORS mode.
Auth request bodies are limited to 4096 bytes.
Typical errors include 401 for missing/invalid credentials, 403 for origin/CSRF
rejection, 409 for already-completed setup, 422 for password policy and 429 for
throttling/busy workers. Error objects usually contain an error code/string;
framework extraction failures are not guaranteed to have that JSON shape.
Configuration and observations
| Method / path | Result |
|---|---|
GET /healthz |
Public process health/version; not a backend health probe |
GET /api/config |
{revision, config} |
PUT /api/config |
Input {expected_revision, config}; returns the applied envelope; retained for atomic multi-section changes |
PUT /api/config/{section} |
Input {expected_revision, value}; replaces one validated section and returns the applied envelope |
GET /api/backends |
Authenticated per-vhost backend observations and active/desired frontend plans |
GET /api/status |
Uptime, active revision, configured counts, listener addresses, feature flags, aggregate metrics, active/desired TCP/UDP streams with per-stream L4 counters, active/desired administration state, TLS policy/cipher inventory and structured-log sink counters |
status.streams contains active, desired, restart_required and a metrics map keyed by startup-active stream ID. Stream counters include active/total/completed sessions, rejects, errors, RX/TX bytes and UDP packet counts; the dashboard derives rates from byte-counter deltas.
| GET /api/logs | Recent bounded access entries, ring capacity and contention-drop count |
| GET /api/audit | Last 100 audit entries; database retention is 10,000 |
| GET /api/events | SSE snapshot events containing status and logs |
| GET /metrics | Prometheus text exposition |
| GET /api/metrics | Alias of /metrics |
Except for health and authentication/bootstrap routes, these endpoints require a session. These administrative previews are not anonymous. The separate configurable exporter uses its own CIDRs and optional bearer token; see the scrape section below.
SSE has eight viewer slots, generates a snapshot approximately every two seconds and rechecks session validity. It does not queue an unlimited per-viewer event log or guarantee replay. The browser falls back to polling. An active dashboard renews idle activity, not the absolute session deadline.
Configuration updates return 409 for a stale revision, 422 for validation failure
and 429 when the eight-operation update admission bound is reached. Persistence and
snapshot publication finish before a successful response. Disconnecting the caller
after the detached writer starts does not cancel an accepted write.
The document limit is 256 KiB, with an additional 1024-byte request-envelope allowance.
For ordinary Settings saves prefer PUT /api/config/{section}. Supported sections are
listeners, response, cache, logging, forwarding, headers, prometheus,
administration, tls, vhosts, redirects, not_found_hosts, streams, acl-profiles and host-policies.
Each section write still uses revision CAS, full validation and atomic persistence.
Only a change to the derived cache-compatibility identity purges hot/cold cache;
unrelated section saves do not.
Configuration fields
The canonical document uses vhosts, redirects, listeners, administration, tls, logging,
prometheus, cache, compression, server_header, headers, host_policies and other defaulted
settings. Unknown fields are rejected. backends/routes at the top level are
input-only legacy migration fields, omitted from saved canonical documents.
{
"vhosts": [{
"id": "application",
"frontend": {
"hosts": ["app.localhost", "alias.localhost"],
"listeners": [{"address":"127.0.0.1:8080", "tls":false, "http2":false}]
},
"backends": [{"id":"application", "url":"http://127.0.0.1:9000", "allow_private":true}],
"routes": [{"id":"root", "path_prefix":"/"}]
}]
}
The backend service must already exist. Routes without backend_id select from
the vhost pool; an explicit ID pins that path to a member of the same pool.
vhosts[].force_ssl=true redirects matching cleartext requests with HTTP 308 to the
same hostname/path/query. Force SSL requires both a selected plain HTTP listener and a
selected TLS listener. Port 443 is preferred when several TLS ports are selected;
otherwise multiple non-standard TLS ports are rejected as an ambiguous redirect target.
vhosts[].forward_client_ip defaults to true. Set it to false only when an application
must see RustProxy's transport peer instead of the visitor: backend requests then omit
Forwarded, X-Forwarded-For and X-Real-IP, while X-Forwarded-Host/Proto/Port/URI
remain and RustProxy/Tinyauth still use the real client address. See examples and
VHOSTS.md. HTTPS upstream certificate verification is disabled when tls_verify is omitted. Set tls_verify: true to validate the origin certificate chain and identity. For verified IP-based origins, use a DNS tls_server_name present in the
certificate and provide tls_ca_pem when the issuer is private. Disabling verification
retains TLS encryption and all backend address/DNS policy checks. Certificate metadata references
files under <data-dir>/certs; it does not create a vhost or expose a listener.
First-class redirect hosts live under redirects; see REDIRECTS.md for matching, target, status, path/query and cache behavior.
ACL rules have network CIDR and action (allow/deny). Omitted ACL means no
restriction in that scope; an explicit empty ACL defaults to deny. Matching deny
wins. Host and route decisions are ANDed. Identity is the socket peer, not an
untrusted forwarded header. There are up to 128 rules per ACL.
Headers run global, then vhost (where applicable), then route rules. set replaces, append adds a
separate value and remove accepts no value. Transport, WebSocket handshake and
client-identity headers are protected. Up to 32 rules per direction/scope, 128-byte
names and 8192-byte values are allowed. Response rules and matching vhost security policy also cover locally generated
proxy errors/denials. Multiple Set-Cookie fields remain separate.
Routing matches exact normalized hostnames, then Nginx-style location precedence: exact path,
longest priority prefix (^~), first matching regex in declaration order, then longest ordinary
prefix at a segment boundary (/api matches /api/x, not /apix). Case-insensitive regex is
also supported. Repeated literal slashes are merged for route/ACL selection, while an unrevised
backend request retains the original path/query. Locations can explicitly strip/replace/regex-
rewrite the upstream path or return a local response. Ambiguous encoded separators and traversal
forms remain rejected. Vhost pools support load balancing as described in VHOSTS.md; locations
may pin traffic to one member. Wildcard hostname matching is supported only in the documented
leading-label form; hostname regex remains unsupported.
Metrics and logs
Metric names use rust_proxy_ and bounded aggregate labels. Transport counters cover
HTTP/2 requests/connections, TLS handshakes/errors and WebSocket tunnel totals,
active tunnels, errors, frame bytes and closed-tunnel duration sum/count. Ordinary
HTTP body bytes and WebSocket frame bytes are separate measurements.
Access entries are emitted at exchange completion or tunnel closure, not only when
headers arrive. WebSocket exchanges retain status 101 and a websocket_closed
outcome when cleanly released. Errors/cancellation affect the outcome. No query
values, request bodies, cookies, password material or WebSocket payloads are logged
by the access logger. Error diagnostics use stderr tracing. The in-memory access ring retains up to 4096
completed entries; live SSE/UI snapshots use the newest 256 by default. The dashboard can
temporarily expand an open Live Logs window to the retained 4096-entry buffer and keep
merging new live snapshots until that window closes. This is not a durable lossless log archive.
0.1.0 cache, listener and diagnostics additions
| Method / path | Contract |
|---|---|
| GET /assets/langs.json | Public compiled language bundle: default ID, discovered language metadata/translations |
| GET /api/cache | Authenticated cache settings, metrics and bounded object metadata; never returns cached bodies |
| POST /api/cache/purge | Full/global or vhost-scoped purge with the normal mutation protections and audit |
| POST /api/cache/invalidate | Selective authenticated PURGE/BAN by exact URL, prefix, tag, safe regex or cache key; optional host/vhost scope |
| GET /api/cache/bans | List active in-memory BAN rules |
| DELETE /api/cache/bans/{id} | Remove one BAN rule with audit |
| POST /api/cache/cold/cleanup | Start an authenticated/CSRF-protected asynchronous selective Cold Cache cleanup job |
| GET /api/cache/cold/cleanup/{id} | Read status/result of an authenticated selective cleanup job |
Configuration includes defaulted listeners, cache, transform, compression, server_header
objects and optional per-route cache (false bypasses, null/omitted inherits).
The complete documented field contracts are in SETTINGS.md and CACHE.md. Listener
changes persist immediately but require restart; all other settings here are hot
published. Settings forms use revisioned section writes where one settings area can
be changed independently; full-document PUT remains for changes that must publish
multiple sections atomically.
Status and SSE snapshots include cache, system, and listeners with active,
desired, startup-default addresses and restart_required. Metrics include four
compression counters; log entries include cache (HIT/MISS/BYPASS, or - before
cache classification). OS measurements are optional/null and sampled off the
request path. Unsupported is not represented as zero. Prometheus adds fixed-name
cache/resource series; absent OS measurements are omitted. No arbitrary URL/cache-key
labels are introduced and the metrics endpoint remains authenticated.
The language bundle is generated by build.rs from langs/*.json and embedded into the binary; uploading a new file requires recompilation/restart. It exposes public translations only, not filesystem paths, administrator data or credentials.
0.2.0 vhosts and backend state
vhosts defaults to an empty array. All models, enum values, defaults and bounds
are documented in VHOSTS.md, with complete examples under examples/vhosts-*.json.
Use the same revisioned PUT endpoint; there is no unauthenticated health-control
endpoint or separate unsynchronized frontend/backend write API.
GET /api/backends returns {pools, active_frontends, desired_frontends}. Pool state
includes id, algorithm, ready, health_mode, sticky_mode, keepalive,
members. Members expose ID/origin/weight, status, healthy flag, active exchanges,
request/error/byte totals, EWMA byte rate/header latency, last check/result, failure
and success counters, transition count and last upstream status. It does not expose
affinity tokens. Member status is up/down/checking/draining/disabled. In health-off
mode, eligibility is not proof that the origin is live. Draining describes operator
intent; inspect healthy as well. ready reflects new-assignment availability;
a pool containing only draining members may still serve existing cookie sessions.
Status contains backend_pools and vhosts; listener state additionally includes
active_endpoints, desired_endpoints. Each endpoint reports its address, TLS and
H2/H3 transport capabilities. restart_required covers socket and listener-capability
changes. Listener capabilities are configured only under listeners.endpoints. A vhost
then narrows them with http2_enabled and http3_enabled; both default true. For TLS,
SNI selects either H2+HTTP/1.1 or HTTP/1.1-only ALPN for the matched vhost, so disabling
H2 on one hostname does not disable it for another hostname sharing the listener.
Metrics add rust_proxy_backend_* per bounded vhost/member labels. Access entries
add nullable backend (vhost/member for a forwarded pool exchange). Cache hits
have no actual upstream member request and do not increase its request counters.
The /assets/vhosts.js frontend uses the same API/CSRF/revision
boundaries; the static script itself, like other frontend assets, is publicly readable.
0.3.0 certificate upload and scoped operations
POST /api/certificates/upload accepts JSON
{"cert_pem":"PEM chain","key_pem":"PEM private key","hosts":["app.example"]}.
It requires the same session, Origin and CSRF guards as configuration updates.
The request limit is 300 KiB; each PEM value is at most 128 KiB. At most two
blocking upload workers run, and the directory holds at most 128 upload-prefixed
pairs. This endpoint is staging only, not a configuration mutation.
Success: {"certificate":{"id":"upload-...","covered_hosts":["app.example"], "hosts":[],"cert_file":"upload-....pem","key_file":"upload-....key"},"staged":true}.
covered_hosts contains the normalized DNS names extracted from the leaf certificate
SubjectAltName extension; hosts remains the configured active SNI bindings. No key or
certificate PEM is returned. Add the metadata to config.tls.certificates and its ID
to a vhost's certificate_id, then save tls/vhosts as needed. The panel keeps a
combined full-document Save only when staged certificate metadata and vhost assignment
must be published atomically. Unknown certificate references, mismatched keys,
uncovered DNS names and conflicting same-name selections fail validation.
There is no ACME enrollment, expiry/chain-trust guarantee or remote PEM retrieval.
GET /api/cache adds vhosts: [{id, usage: {entries, resident_bytes}, settings}].
POST /api/cache/purge?vhost=app purges only that configured vhost and invalidates
its pending fills. Missing/unknown IDs return 404, not an accidental global purge.
Omitting the query purges all entries. Both actions are audited and require CSRF;
an empty body is accepted. Configuration updates purge only when cache compatibility
changes; unrelated section saves retain both hot and cold objects.
POST /api/cache/cold/cleanup accepts a JSON selection with optional
unused_for_seconds, optional oldest (maximum count) and hits_zero. At least one
criterion is required. It returns HTTP 202 with a ColdCleanupJob; poll
GET /api/cache/cold/cleanup/{id} until status is done. The result contains
removed_entries and freed_bytes. This is intentionally separate from full/scope purge
but reuses the same internal Cold Cache candidate/removal machinery as automatic GC.
0.22.1 selective cache invalidation
POST /api/cache/invalidate accepts an InvalidationRequest:
{
"operation": "purge",
"selector": "exact",
"value": "/assets/app.js?v=42",
"host": "app.example",
"vhost": "application",
"ttl_seconds": null,
"confirm_wide": false
}
operation is purge or ban. selector is exact, prefix, tag, regex or
cache_key. host and vhost are optional independent scope restrictions. BAN accepts
ttl_seconds (1..604800, default 3600) and the runtime keeps at most 1024 active rules.
Every regex operation and prefix / requires confirm_wide=true; the browser panel asks
for a danger confirmation before sending it. Regex input is length-bounded and compiled
with Rust's non-backtracking regex engine plus a compiled-size limit.
The response includes result with operation/selector/value/host/vhost,
affected_entries, optional ban_id, elapsed_ms and status, plus current cache metrics
and active BAN rules. Selective PURGE removes RAM and Cold Cache matches, removes the full
Vary family under a matched primary key and prevents fills that started before the PURGE
from publishing afterward. BAN creation does not synchronously scan the store: matching
objects created before the rule are rejected on lookup and removed incrementally by cache
maintenance; fresh objects created after the rule may be stored. BAN rules are runtime
state and are not persisted across process restart.
GET /api/cache/bans returns the active rule views. DELETE /api/cache/bans/{id} removes
one rule. All invalidation mutations use the standard administrator session, same-origin
request marker and CSRF protections; requested operator/scope information is written to the
existing audit log.
The transform configuration section is replaceable through the normal revision-CAS
PUT /api/config/transform endpoint. Vhost objects carry a transform override and
Docker assignments carry transform_enabled; both default to disabled per vhost.
Global transform settings also expose resource controls: memory_budget_bytes (1 MiB..2 GiB,
default 128 MiB), max_body_bytes (1 KiB..32 MiB, default 2 MiB), and max_inflight
(1..1024, default 32). preset is optional UI intent metadata with values
conservative, recommended, performance, or uber; numeric limits remain authoritative.
Resource pressure is fail-open: work that cannot reserve the configured memory/concurrency
capacity is left unchanged rather than queued behind full-body transformation work.
GET /api/transform returns in-memory transformation observability for the administration
Transform workspace: totals and per-vhost counters for examined/applied/skipped/error
responses, transformed cache HITs, original/transformed/saved bytes, accumulated transform
time, applied counts by content kind and bounded skip/fail-open reasons. These counters are
runtime diagnostics and reset when the RustProxy process restarts.
GET /api/logs accepts optional vhost, q, exact numeric status, cache and
limit parameters. Limit clamps to 1..4096 and defaults to 256; search length is at most
256 bytes. Filters run over the retained ring before the limit is applied. cache accepts
HIT, MISS, BYPASS or -. Entries now include nullable vhost independently of backend:
cached and static responses can be attributed without claiming that an origin was contacted.
SSE still sends the newest 256 entries per global snapshot. The Live Logs UI can seed a
temporary 4096-entry buffer on demand and then merge subsequent live snapshots into it until
the window closes. This is not a durable log stream or a per-tenant authorization boundary.
The status endpoint keeps existing field names, adds feature flags for new
capabilities, and assembles small values rather than one deeply expanded macro.
The management script /assets/management.js is a public static asset; APIs remain
protected. Pages and per-vhost cache fields use the standard configuration endpoint.
0.4.0 certificate inventory, forwarding, auth and listeners
GET /api/certificates requires the existing admin session and returns revision,
metadata-only certificates including covered_hosts, active hosts, assigned_vhosts,
file names and the per-entry default flag, plus acme:false. Inventory reads only the
public certificate PEM on a blocking worker and derives current DNS SubjectAltName values,
so older/manual entries and in-place renewals do not depend on stale persisted coverage
metadata. Persisted covered_hosts is only a fallback when the public PEM cannot be read.
It never reads a private key for this enrichment and never returns PEM contents or private-key bytes. Existing upload remains a
CSRF-protected staging operation. The inventory UI saves its returned metadata in
config.tls.certificates; vhost assignment is a later independent configuration
save. Every configuration mutation retains revision CAS and validation.
Vhost additions: tinyauth, server_header, diagnostic_headers,
backend_header, backend_timing_header, and
frontend.listener_ids. Global additions: forwarding,
diagnostic_headers, and listeners.endpoints. Each effective
TLS binding can carry http3 with the five fields described in HTTP3.md. These
are full-config fields, not independent unaudited mutation endpoints. Unknown or
invalid references, trust ranges, URLs, limits and protected header names fail
validation before publication.
Status contains named_endpoints and active/desired endpoint transport metadata,
H3 connection counters and feature flags. Feature flags describe included source
capabilities, not evidence of successful build/conformance testing. Existing
Prometheus metrics add rust_proxy_http3_connections_total,
rust_proxy_http3_active_connections and rust_proxy_http3_rejected_connections_total
and rust_proxy_http3_requests_total. See source metrics.rs for exact HELP/type
contracts and INTEGRATIONS_040.md for configuration examples.
0.22.3 certificate hot reload and external deployment
POST /api/certificates/reload is an authenticated, Origin/CSRF-protected control-plane
operation. It re-reads every certificate/key file referenced by the current desired
configuration, validates a fresh immutable routing/TLS snapshot and publishes it at the
same configuration revision. Invalid or mismatched material returns an error and the
previous snapshot stays active. The response contains reloaded, revision, public_tls,
http3_new_handshakes and administration_restart_required. New public H1/H2 TLS and HTTP/3
handshakes use the refreshed certificates; established connections are not renegotiated.
Administration TLS is startup-fixed and therefore reports when a process restart is needed.
POST /api/certificates/{certificate_id}/reload is the targeted variant for a manual/file-backed certificate (issuer == null). It validates the requested configured ID and atomically republishes the immutable TLS snapshot at the same configuration revision. It is intended for external renewers that replace the files already referenced by RustProxy. Application-managed ACME certificate IDs are rejected with 409 because their renewal pipeline owns those files. The endpoint accepts either the normal administrator session + CSRF flow or a scoped Certificate API token.
POST /api/certificates/deploy accepts
{"certificate_id":"shared-cert","cert_pem":"...","key_pem":"..."} under the same
authenticated Origin/CSRF boundary. It is a convergent upsert for externally owned certificate
ids: a missing id creates a manual certificate entry, an existing manual id is rotated in place,
and an identical RustProxy-owned generation is a no-op. The pair is validated inside the running
RustProxy process and changed material is staged under immutable generation-specific files before
a revision-CAS configuration publish. This is important for root-run Certbot hooks when RustProxy
itself runs as an unprivileged service account: final generations are created with the service's
filesystem identity rather than root:root. Managed ACME ids are rejected, and failed
configuration publication removes any newly staged generation.
For a certificate owned by a local external issuer such as system Certbot, prefer the local CLI deploy hook, which calls that loopback API after bounded local file checks:
rust-proxy tls deploy --id shared-cert \
--cert /etc/letsencrypt/live/example/fullchain.pem \
--key /etc/letsencrypt/live/example/privkey.pem
The command resolves Certbot's local live/ symlinks, checks bounded regular files/private-key permissions, then sends the pair to POST /api/certificates/deploy. The running service validates the certificate/key and DNS SANs, stages generation-specific files inside RustProxy's private certificate directory, and creates or switches the manual certificate id through the normal revision-CAS configuration path. A single id may already be selected by multiple vhosts, so those vhosts switch together
without individual edits. RustProxy-managed ACME entries (issuer != null) are rejected to
avoid two renewal owners for the same lineage. The existing /api/certificates/upload remains
the generic PEM staging endpoint; private certificate paths are never accepted from a remote
request.
0.23.0 scoped Certificate API credentials
External automation no longer needs a full administrator browser session. From Settings -> Certificate API, an administrator can create multiple dedicated credentials (up to 32), each consisting of a Key ID, Bearer token and optional non-secret comment. Each Key ID can be rotated independently. The clear token is returned only by the create/rotation response; SQLite stores only its SHA-256 hash plus metadata/comment. Every credential has exactly two scopes: certificate deploy and certificate reload.
API clients send both headers:
Authorization: Bearer <token>
X-RustProxy-Certificate-Key: <key-id>
A Certificate API credential is accepted only by POST /api/certificates/deploy, POST /api/certificates/reload, and POST /api/certificates/{certificate_id}/reload; it cannot read configuration, logs, metrics or any other administration endpoint. Non-loopback use requires the configured HTTPS administration surface. The normal administrator session + Origin/request-marker + CSRF path remains valid for the same operations. Credential management remains administrator-only: GET /api/certificate-api lists metadata, PUT /api/certificate-api creates/rotates one Key ID, DELETE /api/certificate-api/{key_id} revokes one token, and DELETE /api/certificate-api revokes all tokens.
Example external deploy:
curl --fail --show-error \
-H 'Authorization: Bearer TOKEN' \
-H 'X-RustProxy-Certificate-Key: certbot-main' \
-H 'Content-Type: application/json' \
--data-binary @deploy.json \
https://proxy-admin.example/api/certificates/deploy
For an on-host Certbot hook, a token is optional: invoke the installed binary directly instead:
rust-proxy tls deploy --id shared-cert \
--cert /etc/letsencrypt/live/example/fullchain.pem \
--key /etc/letsencrypt/live/example/privkey.pem
dev.sh is development tooling and is not required for production certificate rotation.
0.5.0 cache inspector and certificate jobs
GET /api/cache additionally returns dashboard: {domains, objects, matching_objects, offset, limit, statistics_revision}. Domain rows flatten
hits/misses/bypasses/stored/saved_body_bytes/reasons beside vhost/domain and
resident counts. Object rows contain vhost/domain/path/body_bytes/resident_bytes/
hits/age_seconds/remaining_ttl_seconds, never bodies, request headers or queries.
vhost=ID filters, vhost= selects legacy only; omitted selects all. Limit 1..200
(default 50), offset <=16384. Unknown vhost ->404. The same scope semantics apply
to POST /api/cache/purge. Existing global metrics remain process-lifetime values.
The historical 0.5 GET /api/acme response did not support renewal; see the
current 0.7 response below.
POST /api/acme/issue accepts at most 16 KiB of JSON with certificate_id, email,
domains, environment, agree_tos, confirm_production, dns_server, dns_port, key_name,
key_secret, key_algorithm, sign_query and propagation_seconds. See
ACME_RFC2136.md for exact constraints. It returns 202 {job} only after admission;
one process-wide job, 429 when busy. Job fields: id, certificate_id, status,
environment, message, certificate. The ready certificate is immutable PEM metadata,
not private bytes. Import is a separate normal config CAS write. Both endpoints
are behind the administrator session; POST retains Origin/CSRF and audit.
LogEntry adds cache_reason, a fixed string token. Debug response headers add
X-Cache-Reason when X-Cache is enabled, and standardized RFC 9211 Cache-Status detail
when enabled. Transformed cached representations add the transformed Cache-Status
extension parameter. X-Cache-Status is stripped if supplied by an origin but is not
emitted by RustProxy.
diagnostic_headers.hide_mock_origin is removed and old documents containing it are rejected on
export; request_id is the only diagnostic-header switch. No revision response-header behavior remains. TLS listener
http3 omission is default-on; explicit null is persisted off. Legacy listeners use
https_http3 with identical default/null behavior.
0.6 provider and scrape additions
POST /api/acme/issue accepts provider: http01, cloudflare, ovh or
rfc2136 (omitted retains RFC2136 compatibility). Common fields include
certificate_id, domains, email, environment, agree_tos and
confirm_production. DNS providers also accept propagation_seconds.
Cross-provider/unknown fields are rejected. Provider-specific fields, secret
handling and request examples are in ACME_PROVIDERS.md. GET /api/acme returns
only public job status and validated certificate metadata, not input credentials.
Issuer metadata on certificates is optional { "provider": "cloudflare", "environment": "staging" }. Do not confuse metadata with CA trust validation.
config.prometheus controls the new dedicated HTTP scrape endpoint, which uses
actual-peer CIDRs and optional bearer authentication, not administrator cookies.
The exact configured GET path only is accepted (query strings rejected). Disabled
or changed/restart-pending settings return 404; denied peers 403; invalid tokens
401; non-GET 405; exhausted scrape capacity 429. No administrative actions are
available there. Admin /api/metrics and /metrics remain session-protected
previews on the admin listener, even when the dedicated endpoint is disabled.
See PROMETHEUS.md for the full settings and metric contract. Docker discovery is an independent collector group (include_docker_vhosts) and exports only dedicated rust_proxy_docker_vhost_* / rust_proxy_docker_backend_* families; ordinary vhost/backend families exclude dynamic Docker routes.
0.7 renewal and host access
GET /api/acme now returns {job, providers, automatic_renewal:true, interval_seconds:43200, renewals}. renewals is keyed by certificate ID and
contains last_checked, next_check, last_renewed, not_after (Unix seconds), failures,
status and a public message token. No credential/account data is returned.
GET /api/certificates also includes renewal observations. Statuses are observations,
not a promise of a successful future CA operation.
POST /api/acme/renew accepts only {certificate_id, dry_run:false}. It uses an
existing managed lineage and returns 202 {job} after admission, or 404 unknown ID,
409 unmanaged/invalid profile or disabled prerequisites, 429 worker busy. Actual
worker/CA failures arrive in job state. No force-renew flag is offered. Dry runs
never deploy their test certificate and never postpone a due real renewal.
The usual session, Origin/CSRF protections and audit apply.
A certificate's issuer supports auto_renew (default true for saved managed
metadata, explicit false opts out). Toggle through ordinary revisioned config;
no provider credentials need resubmitting. Old immutable generations are retained.
POST /api/basic-auth/hash accepts {username, password} with at most 4096 bytes
of request JSON. It requires an administrator session and the same mutation/CSRF
guards, returns {username, password_hash}, and never saves the plaintext password.
Passwords are 12..128 UTF-8 bytes with no control characters. Usernames are 1..128
bytes, no colon/control characters. Hashing has two shared blocking-worker slots;
no available slot returns 429. Invalid input returns 400. This is NOT a public
login endpoint and does not create a browser session for a vhost.
A vhost accepts websockets and basic_auth with enabled, realm, require_tls,
forward_credentials and users[{username,password_hash}]. Basic and Tinyauth cannot
be enabled simultaneously. Hash import accepts only the bounded Argon2id policy
created by the hash endpoint, not arbitrary-cost PHC strings or htpasswd formats.
Missing websockets retains the previous true behavior; the new-host UI selects
false. Basic TLS defaults true and forwarding defaults false.
Backend pool members have pool_eligible (default true). False excludes that
member from default load balancing and sticky selection without blocking an
explicit location backend_id. Visual custom locations edit existing vhost
routes: ID, path_prefix, backend_id, enabled, cache, ACL and advanced headers.
This is prefix routing, not regex, rewrite or nginx configuration evaluation.
See host access for cumulative ACL and authentication semantics.
0.18 Docker discovery additions
| Method / path | Contract |
|---|---|
GET /api/docker |
Authenticated cached in-memory Docker-agent observations; no agent poll and no configuration write |
POST /api/docker/refresh |
Authenticated + Origin/CSRF immediate poll of enabled agents followed by ephemeral route reconciliation; no configuration revision write |
PUT /api/config/docker-agents |
Revision-CAS replacement of desired Docker-agent settings, per-agent domains and service hostname/alias/IP assignments |
config.docker_agents stores desired agent endpoint/token/base-domain/IP override/listener references, a per-agent 5..3600 second inventory poll interval, and assignments. Inventory data returned by agents is not persisted in the configuration document. The normal configuration API remains authoritative for desired state; the Docker observation API is operational state only. Docker assignments may also carry an ACL profile, inline ACL, health-check settings and per-vhost proxy timeouts. Publishing/unpublishing is therefore an ordinary CAS configuration update and does not require polling the agent.
HTTP/2 lifecycle settings
As of 0.24.3 the preferred configuration is per listener: listeners.endpoints[].binding.http2_settings contains idle_timeout_seconds, max_connection_lifetime_seconds and drain_timeout_seconds for that TLS H2 or cleartext H2C endpoint. Update these values by revision-CAS replacement of the listeners section (PUT /api/config/listeners), the same ownership model used by per-listener HTTP/3 limits. max_connection_lifetime_seconds: 0 disables the absolute lifetime limit. Idle and lifetime expiry both use graceful GOAWAY/drain shutdown.
The top-level http2 object introduced in 0.24.0 and PUT /api/config/http2 remain accepted for backward compatibility. They are a fallback only for listeners that do not yet have explicit binding.http2_settings; the structured 0.24.3 UI writes listener-scoped values.
The runtime system diagnostics object includes platform, supplied by the shared system-statistics backend (Linux, FreeBSD, OpenBSD, NetBSD, or the generic/unsupported fallback). The browser only renders that value; it does not detect the operating system itself.