API Reference
Operator HTTP API The /v1 surface served on operator_http_listen. Loopback by default, session-or-bearer authenticated, RBAC-gated.
portunus-server exposes its operator surface as an HTTP API on
operator_http_listen (loopback by default). Most /v1/* requests require
either a Web session cookie or an API bearer token.
Web UI users authenticate through local password login:
POST /v1/auth/login
Host : 127.0.0.1:7080
Content-Type : application/json
{ "user_id" : "admin" , "password" : "correct horse battery staple" }
Successful login returns {"password_change_required": false} and sets an
HttpOnly portunus_session cookie. API clients and CLI automation continue
to use bearer credentials:
GET /v1/clients
Host : 127.0.0.1:7080
Authorization : Bearer T006-quickstart-token
Cookie-authenticated mutating requests (POST, PUT, PATCH, DELETE) must
include an exact same-origin Origin header, X-Portunus-CSRF: 1, and JSON
content type when a body is present. Bearer-token requests do not need the CSRF
header.
Responses on missing / invalid authentication:
Status Body Cause 401 unauthenticated{"error":{"code":"unauthenticated","message":"..."}}Missing / invalid bearer or Web session 403 not_owner{"error":{"code":"not_owner","message":"..."}}Authenticated, but caller does not own the resource 403 password_change_required{"error":{"code":"password_change_required","message":"..."}}Web session must change a temporary password first 403 client_not_granted / port_outside_grant / protocol_not_grantedRBAC envelope rejection (see RBAC ) 403 csrf_*Cookie-authenticated write failed CSRF checks 429 rate_limitedToo many login, onboarding, or password-reset attempts 503 bootstrap_requiredServer has no superadmin yet — complete Web onboarding
Method Path Notes GET/v1/auth/statusPublic status: { onboarding_required } POST/v1/auth/onboardingPublic first-admin creation; requires setup token while no active superadmin exists POST/v1/auth/loginPublic local password login; sets portunus_session POST/v1/auth/logoutSession logout; requires session cookie and CSRF headers
Method Path Notes GET/v1/users/me{ user_id, role, display_name } for the caller (used by SPA AuthGate)POST/v1/users/me/passwordChange own password; body includes current_password, new_password, new_password_confirm
Method Path Notes POST/v1/client-enrollmentsCreate a one-time enrollment command for a new client POST/v1/clients/{name}/enrollmentCreate a one-time re-enrollment command for an existing client GET/v1/clientsList clients; superadmin sees all PUT/v1/clients/{name}Update the client's advertised address; body {address} POST/v1/clients/{name}/revokeRevoke + disconnect DELETE/v1/clients/{name}Delete a revoked client row
Method Path Notes POST/v1/rulesPush a rule (single-target, multi-target, port-range, with caps, with SNI) GET/v1/rulesList rules; non-superadmin sees only owned rules. Optional ?client=<name> filter GET/v1/rules?owner=<user_id>Superadmin filter by owner PUT/v1/rules/{rule_id}Hot-update rate_limit only; client, listen, protocol, and targets must stay unchanged DELETE/v1/rules/{rule_id}Drain + remove GET/v1/rules/{rule_id}/statsAggregate stats GET/v1/rules/{rule_id}/stats?per_port=truePer-port detail (range rules) GET/v1/rules/{rule_id}/stats?per_target=truePer-target detail (multi-target rules) GET/v1/rules/{rule_id}/stats/streamSSE stream, 5-second cadence; accepts ?per_target=true (v0.6+)
Method Path Notes POST/v1/usersAdd user (superadmin); accepts optional initial_password and password_change_required GET/v1/usersList users (superadmin) GET/v1/users/{id}User detail DELETE/v1/users/{id}Cascading remove (superadmin) POST/v1/users/{id}/passwordReset user password (superadmin); revokes sessions and API tokens by default
Method Path Notes POST/v1/users/{id}/credentialsIssue a credential GET/v1/users/{id}/credentialsList DELETE/v1/users/{id}/credentials/{cred_id}Revoke POST/v1/users/{id}/credentials/{cred_id}/rotateRotate (self or superadmin)
Method Path Notes POST/v1/grantsAdd grant (superadmin) GET/v1/grantsList grants (superadmin) DELETE/v1/grants/{id}Cascading revoke
Per-rule caps travel on the POST / PUT /v1/rules body (see rate_limit
above). The endpoints below set a per-(owner, client) envelope.
Method Path Notes GET/v1/clients/{client_id}/ownersList owners with rules under this client GET/v1/clients/{client_id}/owners/{owner_id}/rate-limitRead the per-owner envelope PUT/v1/clients/{client_id}/owners/{owner_id}/rate-limitSet the per-owner envelope DELETE/v1/clients/{client_id}/owners/{owner_id}/rate-limitRemove the per-owner envelope
Per-(user, client) monthly traffic quotas and historical traffic queries.
Traffic queries take from and to (Unix seconds, required) plus an optional
bucket (sample granularity).
Method Path Notes GET/v1/users/{user_id}/quotasList a user's quotas PUT/v1/users/{user_id}/quotas/{client_name}Set the monthly quota PATCH/v1/users/{user_id}/quotas/{client_name}Update monthly_bytes and/or clear period usage DELETE/v1/users/{user_id}/quotas/{client_name}Remove the quota GET/v1/users/{user_id}/quotas/{client_name}/statusCurrent period usage vs. cap GET/v1/users/{user_id}/traffic?from=<unix>&to=<unix>&bucket=<size>Historical traffic for a user GET/v1/clients/{client_name}/quotasList a client's quotas GET/v1/clients/{client_name}/traffic?from=<unix>&to=<unix>&bucket=<size>Historical traffic for a client GET/v1/traffic/global?from=<unix>&to=<unix>&bucket=<size>Historical traffic across all rules
Method Path Notes GET/v1/settings/advertised-endpointRead the advertised-endpoint override + effective value (superadmin) PUT/v1/settings/advertised-endpointSet/clear the override; host must be covered by the server cert SAN (superadmin)
Method Path Notes GET/v1/audit?limit=N&outcome=allow|denyv0.7-shape JSON array (back-compat) GET/v1/audit?since=<RFC3339>&until=<RFC3339>&cursor=<base64>&limit=Nv0.8 envelope {entries, next_cursor?, count}
Method Path Notes GET/v1/metricsRBAC-gated mirror of the standalone /metrics; superadmin only
The standalone scraper-facing endpoint at metrics_listen:7081/metrics
is unchanged (Prometheus continues scraping without bearer).
Code Meaning 200Success (read) 201Resource created 204Success, no body 400Validation error 401unauthenticated403RBAC denial, CSRF denial, or password_change_required 404Resource not found 409Conflict (e.g. conflict.legacy_to_sni_unsupported) 422Capability gate (e.g. multi_target_unsupported_by_client, sni_unsupported_by_client, rate_limit_unsupported_by_client) 429Authentication throttle 503bootstrap_required
The full code → exit-code → HTTP-status mapping is frozen at v1; see
the per-spec contract docs in
specs/*/contracts/operator-api.md .
curl -sS -X POST http://127.0.0.1:7080/v1/rules \
-H "Authorization: Bearer $PORTUNUS_OPERATOR_TOKEN " \
-H 'Content-Type: application/json' \
-d '{
"client": "edge-01",
"listen_port": 8443,
"targets": [
{"host": "primary.local", "port": 443, "priority": 1},
{"host": "backup.local", "port": 443, "priority": 2}
],
"protocol": "tcp",
"sni_pattern": "*.example.com",
"rate_limit": {
"bandwidth_in_bps": 1048576,
"concurrent_connections": 1000
}
}'