Aller au contenu

API & OpenAPI docs

Everything the admin UI does, it does over Taranac’s own REST API — the web interface is just a client. That API is fully described by an OpenAPI schema generated from the code itself, and the platform can serve that schema as interactive documentation: Swagger UI, Redoc and the raw openapi.json.

That documentation surface is off by default. It is a single environment flag away, and this page explains what you get when you turn it on, why you probably should not leave it on in production, and where to browse it without installing anything.

One flag gates all three routes. Off (the default) — /docs, /redoc and /openapi.json return 404. On — they are served, and served without authentication.

The quickest way to see the API is the public demo instance, which runs with the docs surface enabled on purpose:

The demo runs the same images as your install, so what you read there is what your instance exposes.

RouteWhat it is
/docsSwagger UI — the flat, ordered tag list, with an Authorize button and try-it-out request forms
/redocRedoc — the same schema rendered as a reference document, with tags nested into domain groups (Network, AAA · TACACS+, NAC, Configuration Tracker, …)
/openapi.jsonThe raw OpenAPI schema, for client generators, Postman/Insomnia imports and API diffing

Note that all three are mounted at the application root, not under /api/. The bundled edge (the edge container) already proxies them; a third-party reverse proxy in front of Taranac that only forwards /api/* will not reach them.

DOCS_ENABLED lives in the .env file in your deployment bundle directory and defaults to false:

.env
DOCS_ENABLED=false # Swagger/Redoc/openapi.json — keep off in production

Set it to true and recreate the api container so it picks the new value up:

Terminal window
./taranac up -d # recreates containers whose env changed

To turn it back off, set it to false and run ./taranac up -d again. When the flag is off, FastAPI is built with no documentation URLs at all: the three routes 404, and the schema is never even generated.

When the flag is on, admins get an API item in the sidebar’s Advanced section that opens /docs in a new tab. The UI reads the flag from the backend (it is carried on /auth/me as docs_enabled), so the link can never point at a disabled endpoint — no flag, no link.

The schema is not raw FastAPI output — the tag taxonomy, the per-tag descriptions and the Markdown overview at the top are maintained in the product as a first-class surface, and CI fails if a route carries a tag that is not described. The conventions it documents are the same ones the whole API follows:

ConventionBehaviour
Base pathManagement endpoints live under /api/v1. What sits outside it: the health probes (/healthz, /ready), the three documentation routes above, and EST enrollment at /.well-known/est/ (see EST). The deliberately public surfaces (captive-portal flows, CRL and certificate downloads) keep the /api/v1 prefix — they are public by their guard, not by their path.
AuthPOST /api/v1/auth/login returns a token; send it as Authorization: Bearer <token>. In Swagger UI, paste it into Authorize. A login that runs into a second factor answers mfa_required with a short-lived mfa_token instead — the session token then comes from POST /api/v1/mfa/verify.
SessionsA login returns a short-lived access token (15 minutes) and a refresh token. POST /api/v1/auth/refresh spends the refresh token and returns a new pair. The server refuses a refresh once the session has been idle longer than Idle Timeout allows, or has passed Maximum Session Length (see How a web session ends). POST /api/v1/auth/logout with the refresh_token in the body revokes it, so the session cannot be renewed. An access token already issued stays valid until it expires.
Token purposeEvery JWT states what it is for. The Authorization header accepts purpose: session and nothing else unless an endpoint widens that explicitly — the forced-password-change token (password_change) opens POST /api/v1/auth/change-password, and the token redeemed from an e-mailed setup link (mfa_enrollment, 15 minutes) opens the enrollment endpoints. Anything else is a 401. The half-authenticated tokens are never bearer tokens at all: mfa and the portal’s byod_mfa are spent in the body of their own verify call, and the two are not interchangeable.
AuthorizationEndpoints are gated by RBAC permissions — GET /api/v1/rbac/schema lists them. Admin accounts bypass the gate.
ErrorsA consistent body { "detail", "code", "field" }, localized from the Accept-Language header (en / ru).
ValidationRequest bodies are strict: unknown fields are rejected with 422, lengths and ranges are bounded, blank optional text normalizes to null.
TimestampsStored and returned in UTC (ISO-8601); clients localize.
PaginationList endpoints accept page, per_page, search, sort_by, sort_order and return { items, total, total_by_filter, page, per_page, pages }.
Rate limitsNot global. A handful of endpoints an attacker would hammer — login, token refresh, password reset, POST /api/v1/mfa/challenge and /mfa/verify, the portal’s guest flows, EST enrollment — are limited per client. The client is the address read through the reverse proxies counted by TRUSTED_PROXY_HOPS (see below), the same address lockout, the CAPTCHA and the audit log use. Over the limit is 429 with a Retry-After header and the body { "detail", "retry_after" }, which is deliberately not the standard error envelope. The MFA token adds budgets of its own: five wrong codes burn it, and the check runs before the provider does, so a burnt token costs no TOTP maths and sends no push. One sign-in can also start at most five push or email challenges, at least 20 seconds apart.
Response headersEvery response carries X-Request-ID (echoed from the request if you send one — quote it in a support ticket) and X-API-Version: 1. A dataset or log export adds X-Taranac-Export-Rows (and, for a log export, X-Taranac-Export-Total), plus X-Taranac-Export-Truncated: true when the row ceiling cut it short — check it, or a truncated export looks complete.

Tags are grouped by domain — Authentication & Access, Identity, Network, AAA · TACACS+, AAA · RADIUS, AAA · Policy & Logs, NAC, NAC · PKI, Configuration Tracker, Monitoring & Ops, System Settings, Reports, System — which is the fastest way to find the endpoints behind a screen you are looking at in the UI.

Not everything that talks to this API logs in. These surfaces are described in the same schema, and they are worth knowing before you write a client — or point a scanner at an instance and wonder why something answered:

SurfaceHow it authenticates
GET /.well-known/est/cacerts, GET /.well-known/est/csrattrsNothing, per RFC 7030. Like the two below, they answer only while the nac.est_enabled setting is on.
POST /.well-known/est/simpleenrollHTTP Basic only — verified against the local users table by argon2id hash, so a directory-only account (which has no local hash) is refused. A client certificate does not authenticate a first enrollment. 10 requests a minute per client.
POST /.well-known/est/simplereenrollThe current client certificate, verified by the EST gateway on port 8445. 30 requests a minute per client. Both enrollment routes answer 202 with Retry-After: 30 when the request lands in the approval queue instead of being issued.
/scep, /scep/pkiclient.exe, /cgi-bin/pkiclient.exe on the EST gateway (plain HTTP on 8446, HTTPS on 8445)SCEP (RFC 8894): the shared challenge password inside the encrypted request for a first enrollment, a signature with the current certificate for a renewal. PKIOperation 10 requests a minute per client. Answers only while nac.scep_enabled is on. See SCEP enrollment.
GET /api/v1/public/crl/{ca_id}.crl and .pemNothing. This is the CRL distribution point embedded in every certificate the internal CA issues, so it has to answer clients that will never log in.
GET / POST /api/v1/public/cert-download/{token}The token in the URL. GET previews and spends nothing; POST consumes it, single-use.
GET / POST /api/v1/public/est-approval/{token}The one-time token from the approval e-mail. GET renders the request, POST decides it.
The captive portal’s guest flow under /api/v1/captive-portal/The guest session token; the sponsor’s emailed link carries a one-time secret of its own: GET /api/v1/captive-portal/sponsor/callback only describes the request, and POST to the same path decides it. Both are rate-limited per client. The /sessions routes on the same prefix are ordinary RBAC-gated admin endpoints.
/api/v1/mfa/internal/The X-Internal-Key shared secret — these are called by the RADIUS/NAC daemons during a live authentication, never by a browser.
/api/v1/ncm/collector/A one-time enrollment token, then per-request signing by the enrolled collector.
/api/v1/nac/dhcp-probe/collector/, /api/v1/nac/scanner/collector/The same enrolled collector identity: a site collector’s DHCP Probe observations and scanner findings.
/api/v1/captive-portal/heartbeat, /api/v1/captive-portal/datapath/report, /api/v1/nac/dhcp-probe/portal/The X-Portal-Token of a standalone captive portal: its heartbeat, its datapath report (and the answer telling it whom to admit), and the DHCP leases it grants. Rate-limited per address.

Devices use one URL, the EST gateway’s: https://<host>:8445/.well-known/est, the est-gateway container on port 8445 (EST_GATEWAY_PORT), which verifies the client certificate itself. It serves the reads, the first enrollment by password and renewal. The console’s HTTPS port (443) also serves the reads and simpleenroll, like the rest of the API, but a renewal (simplereenroll) there is always refused.

The gateway passes what it verified to the API as headers — X-SSL-Client-Verify, X-SSL-Client-S-DN, X-SSL-Client-Cert — and proves they are its own with X-Est-Proxy-Key, which must match EST_PROXY_KEY from .env (the installer generates it). The mTLS proxy secret setting (nac.est_mtls_proxy_secret) is a second accepted key, only for a client-certificate terminator of your own. Both are compared in constant time. With neither set, the API ignores those headers outright. The frontend vhost on 443 blanks all of them on every inbound request, so a client cannot smuggle its own.

A refused simpleenroll answers 401 with WWW-Authenticate: Basic realm="EST", so a client that sent no password retries with one. EST switched off, or a bad CSR, is 422. Every refusal is recorded in the audit log. The full flow, with the approval endpoints, is on EST enrollment.

The TACACS+ TLS and NAC (EAP) server certificates have the same endpoints under /api/v1/tacacs/server-cert and /api/v1/nac/server-cert: read the current one, /issue from an Internal CA (san_dns and san_ip, up to 20 each), /upload, /self-signed, /deploy and /expiry; GET …/issue-defaults returns what the issue form is prefilled with (the node’s name and addresses, the Default CA), and POST …/restore-previous makes the most recently deployed earlier certificate active again and deploys it. There is no CSR endpoint. For an external CA, generate the key and the CSR outside Taranac, have the CA sign it, then POST …/server-cert/upload the certificate and key.

PATCH /api/v1/tacacs/settings carries no certificate fields. Unlike most request bodies, this settings document does not reject a field it does not know, so a client that still sends tls_cert, tls_key or tls_ca gets no error, and the values do nothing. See TACACS+ over TLS.

The PKI endpoints behind Taranac under your corporate root, all under /api/v1/nac/pki; reads need nac_pki.view, changes nac_pki.edit:

EndpointWhat it does
GET / POST /ca-requestsList pending CA requests; generate a CA key and a CSR for your PKI to sign.
GET /ca-requests/{id}, GET /ca-requests/{id}/csrOne request; download its CSR.
POST /ca-requests/{id}/completePaste the signed certificate and its chain: the request becomes an Internal CA.
DELETE /ca-requests/{id}Delete a request and the key generated for it.
PUT /cas/{ca_id}/chainReplace chain of a CA signed outside Taranac.
PATCH /cas/{ca_id}/chain/{row_id}Set or clear the CRL URL of one upper-chain certificate.
POST / GET /cas/{ca_id}/renewal-csrCreate, then download, a renewal CSR signed with the CA’s stored key.
POST /cas/{ca_id}/renewInstall the renewed certificate and chain, same key.

An Internal CA’s certificate field takes exactly one certificate; a request with several is refused with their count, and the issuing chain goes in its own field. The EST and SCEP endpoints, the challenge password and the enrollment-guard blocks are listed on EST and SCEP.

  • CLI & scripts — the ./taranac wrapper and the .env file this flag lives in
  • Settings reference — the runtime settings surface, edited in the UI rather than in .env
  • RBAC — the permissions that gate every endpoint
  • Versioning & releases — what a version covers, and what that means for API stability