HTTP API

Reference for the control-plane management API, including transport, auth, and cluster-aware behavior.

This page is the reference for the control-plane HTTP API.

The authoritative machine-readable description is the generated OpenAPI document:

  • static docs artifact: /openapi/neuwerk-v1.json
  • runtime endpoint: GET /api/v1/openapi.json

Use the OpenAPI document for exact request and response shapes. Use this page for the transport and behavioral details that matter when integrating with the API.

Scope

The control-plane listener exposes three kinds of surfaces:

  • GET /health for liveness checks on the primary HTTPS listener
  • GET /ready for readiness checks on the primary HTTPS listener
  • GET /metrics on the separate metrics listener
  • /api/v1/... for the authenticated management API

Transport And Base Paths

  • The management API is served over HTTPS.
  • The API base path is /api/v1.
  • GET /metrics is served on the separate metrics bind address, not under /api/v1.
  • The control-plane also serves the embedded web UI from the same HTTPS listener, using a fallback route for non-API paths.

Authentication And Authorization

Protected API routes accept either of these credentials:

  • Authorization: Bearer <jwt>
  • Cookie: neuwerk_auth=<jwt>

The session-oriented login endpoint accepts a JSON body:

{
  "token": "Bearer eyJ..."
}

If performance mode is disabled, these endpoints return 503.

On success, POST /api/v1/auth/token-login returns the decoded identity and sets the neuwerk_auth cookie. POST /api/v1/auth/logout clears that cookie.

Protected routes apply two access levels:

  • GET requests require any valid token.
  • POST, PUT, and DELETE requests require the admin role.

Service-account tokens may carry either readonly or admin, and SSO-derived sessions are mapped to roles by the configured provider rules.

Cluster Behavior

When cluster mode is enabled, most state-changing and state-reading API calls are transparently proxied to the current leader. Clients do not need to discover the leader themselves.

There are three notable exceptions:

  • GET /api/v1/audit/findings/local always executes locally so the leader can fan out and aggregate results.
  • POST /api/v1/support/sysdump/node is an internal fan-out endpoint and is not leader-proxied.
  • GET /api/v1/wiretap/stream is leader-aware. Followers proxy the request to the leader, and the leader multiplexes streams from all cluster nodes.

If the cluster has no known leader, leader-routed requests fail with 503 and an error body.

Shared Request And Response Conventions

  • Request bodies are JSON unless noted otherwise.
  • Request bodies are limited to 2 MiB.
  • Errors are returned as JSON in the form:
{
  "error": "human-readable message"
}
  • Policy records can also be fetched as YAML with ?format=yaml.
  • Streaming endpoints use Server-Sent Events.
  • Sysdump endpoints return application/gzip.

Public Endpoints

Session And SSO

MethodPathPurposeNotes
POST/api/v1/auth/token-loginExchange a bearer token for a browser sessionPublic. Sets neuwerk_auth cookie on success. Rate-limited.
POST/api/v1/auth/logoutClear the browser session cookiePublic. Returns 204.
GET/api/v1/auth/sso/providersList enabled SSO providers for the login screenPublic. Returns provider id, name, and kind.
GET/api/v1/auth/sso/:id/startStart an SSO login flowPublic. Returns 302 to the IdP and sets temporary neuwerk_sso cookie.
GET/api/v1/auth/sso/:id/callbackComplete an SSO login flowPublic. Returns 302, sets neuwerk_auth, clears neuwerk_sso.

POST /api/v1/auth/token-login response shape:

{
  "sub": "operator@example.com",
  "sa_id": null,
  "exp": 1760000000,
  "roles": ["admin"]
}

Health And Metrics

MethodPathPurposeNotes
GET/healthLiveness checkUnauthenticated. Returns {"status":"ok"}.
GET/readyReadiness checkUnauthenticated. Returns 200 or 503 with readiness state.
GET/metricsPrometheus metricsExposed on the metrics listener, not the main API router.

Protected Endpoints

Identity

MethodPathPurposeNotes
GET/api/v1/auth/whoamiReturn the authenticated principal and rolesWorks for both direct bearer auth and cookie auth.

Policies

Policies are stored as records containing metadata plus a full PolicyConfig payload. The write paths validate referenced Kubernetes integrations, compile the policy before persisting it, and wait for policy activation before returning success.

MethodPathPurposeNotes
GET/api/v1/policiesList policy recordsReturns full policy records, not just metadata.
POST/api/v1/policiesCreate a policyRequires admin. JSON only.
GET/api/v1/policies/:idFetch a policy by idSupports ?format=yaml.
PUT/api/v1/policies/:idReplace a policy by idRequires admin.
DELETE/api/v1/policies/:idDelete a policy by idRequires admin.
GET/api/v1/policies/by-name/:nameFetch a policy by stable nameSupports ?format=yaml.
PUT/api/v1/policies/by-name/:nameCreate or replace a policy by stable nameRequires admin.

Create and update requests use this envelope:

{
  "mode": "enforce",
  "name": "office-egress",
  "policy": {
    "default_policy": "deny",
    "source_groups": []
  }
}

The policy object is the full Neuwerk policy model. That schema is large enough to deserve its own dedicated reference page.

Integrations

The current API only supports Kubernetes integrations.

MethodPathPurposeNotes
GET/api/v1/integrationsList integrationsReturns sanitized views, not sealed secrets.
POST/api/v1/integrationsCreate a Kubernetes integrationRequires admin. kind must be kubernetes.
GET/api/v1/integrations/:nameFetch an integration by nameReturns 404 if missing.
PUT/api/v1/integrations/:nameUpdate an integration by nameRequires admin.
DELETE/api/v1/integrations/:nameDelete an integration by nameRequires admin.

Create request:

{
  "name": "cluster-a",
  "kind": "kubernetes",
  "api_server_url": "https://10.0.0.1:6443",
  "ca_cert_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n",
  "service_account_token": "..."
}

Response shape:

{
  "id": "uuid",
  "created_at": "2026-03-14T12:00:00Z",
  "name": "cluster-a",
  "kind": "kubernetes",
  "api_server_url": "https://10.0.0.1:6443",
  "ca_cert_pem": "-----BEGIN CERTIFICATE-----\n...\n",
  "auth_type": "service_account_token",
  "token_configured": true
}

Service Accounts

MethodPathPurposeNotes
GET/api/v1/service-accountsList service accountsReturns account records with role and status.
POST/api/v1/service-accountsCreate a service accountRequires admin.
PUT/api/v1/service-accounts/:idUpdate a service accountRequires admin.
DELETE/api/v1/service-accounts/:idDisable a service account and revoke its tokensRequires admin. Returns 204.
GET/api/v1/service-accounts/:id/tokensList token metadata for an accountDoes not return the raw token value.
POST/api/v1/service-accounts/:id/tokensMint a new token for an accountRequires admin. Returns the raw token once.
DELETE/api/v1/service-accounts/:id/tokens/:token_idRevoke a tokenRequires admin. Returns 204.

Service-account create request:

{
  "name": "terraform",
  "description": "automation account",
  "role": "admin"
}

Token mint request:

{
  "name": "ci",
  "ttl": "24h",
  "role": "readonly"
}

Token mint response:

{
  "token": "eyJ...",
  "token_meta": {
    "id": "uuid",
    "service_account_id": "uuid",
    "name": "ci",
    "created_at": "2026-03-14T12:00:00Z",
    "created_by": "operator@example.com",
    "expires_at": "2026-03-15T12:00:00Z",
    "revoked_at": null,
    "last_used_at": null,
    "kid": "current-signing-key",
    "role": "readonly",
    "status": "active"
  }
}

Notes:

  • ttl and eternal are mutually exclusive.
  • A minted token cannot exceed the role of the parent service account.
  • Disabling a service account automatically revokes all of its tokens.

Audit

Audit queries are only available when performance mode is enabled.

MethodPathPurposeNotes
GET/api/v1/audit/findingsQuery audit findings, cluster-awareAggregates across nodes in cluster mode.
GET/api/v1/audit/findings/localQuery only the local node audit storeUsed internally by the leader fan-out path.

Query parameters:

  • policy_id
  • finding_type
  • source_group
  • since
  • until
  • limit

finding_type and source_group can be repeated. limit defaults to 500 and is clamped to 1..=10000.

Response shape:

{
  "items": [
    {
      "finding_type": "dns_deny",
      "policy_id": "uuid",
      "source_group": "branch-office",
      "hostname": "blocked.example.com",
      "dst_ip": "203.0.113.20",
      "dst_port": 443,
      "proto": 6,
      "fqdn": null,
      "sni": null,
      "icmp_type": null,
      "icmp_code": null,
      "query_type": null,
      "first_seen": 1760000000,
      "last_seen": 1760003600,
      "count": 17,
      "node_ids": ["node-a"]
    }
  ],
  "partial": false,
  "node_errors": [],
  "nodes_queried": 3,
  "nodes_responded": 3
}

Wiretap

Wiretap is a live SSE stream and is only available when performance mode is enabled.

MethodPathPurposeNotes
GET/api/v1/wiretap/streamStream matching flow eventsCluster-aware. Followers proxy to the leader.

Supported query parameters:

  • src_cidr
  • dst_cidr
  • hostname
  • proto
  • src_port
  • dst_port

Each parameter may be repeated. Port fields accept either a single port or a range such as 1000-2000.

SSE event example:

event: flow
data: {"flow_id":"...","src_ip":"10.0.0.10","dst_ip":"203.0.113.20","src_port":53124,"dst_port":443,"proto":"tcp","packets_in":4,"packets_out":6,"last_seen":1760000000,"hostname":"api.example.com","node_id":"node-a"}

The stream emits both flow and flow_end events.

If performance mode is disabled, this endpoint returns 503.

Runtime Settings

MethodPathPurposeNotes
GET/api/v1/settings/performance-modeRead the performance-mode flagReturns the value and whether it came from local or cluster state.
PUT/api/v1/settings/performance-modeEnable or disable performance modeRequires admin.
GET/api/v1/settings/tls-intercept-caRead TLS-intercept CA statusReturns configured flag, source, and SHA-256 fingerprint.
PUT/api/v1/settings/tls-intercept-caUpload TLS-intercept CA materialRequires admin.
DELETE/api/v1/settings/tls-intercept-caRemove TLS-intercept CA materialRequires admin.
GET/api/v1/settings/tls-intercept-ca/certDownload the CA certificate PEMReturns 404 if not configured.
POST/api/v1/settings/tls-intercept-ca/generateGenerate and persist a new CARequires admin.
GET/api/v1/settings/sso/providersList configured SSO providersReturns sanitized views.
POST/api/v1/settings/sso/providersCreate an SSO providerRequires admin.
GET/api/v1/settings/sso/providers/:idFetch an SSO providerReturns sanitized view.
PUT/api/v1/settings/sso/providers/:idUpdate an SSO providerRequires admin.
DELETE/api/v1/settings/sso/providers/:idDelete an SSO providerRequires admin.
POST/api/v1/settings/sso/providers/:id/testTest provider reachabilityRequires admin.

Performance mode request:

{
  "enabled": true
}

Performance mode notes:

  • the default is enabled when no explicit setting has been written yet
  • source: "local" means the setting is stored on the node
  • source: "cluster" means the setting is coming from replicated cluster state
  • source: null means the runtime is using the built-in default
  • disabling performance mode makes the audit and wiretap APIs unavailable

TLS intercept CA upload request:

{
  "ca_cert_pem": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n",
  "ca_key_pem": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
}

For TLS intercept uploads, provide exactly one of:

  • ca_key_pem
  • ca_key_der_b64

SSO provider creation currently supports a broad configuration surface, including:

  • provider kind
  • issuer and endpoint overrides
  • client id and secret
  • scopes
  • PKCE requirement
  • subject, email, and groups claim mapping
  • default role
  • admin and readonly matching rules
  • allowed email domains
  • session TTL

Diagnostics And Runtime State

MethodPathPurposeNotes
GET/api/v1/dns-cacheReturn grouped DNS cache contentsResponse shape is { "entries": [...] }.
GET/api/v1/statsReturn an aggregated runtime stats snapshotIncludes dataplane, DNS, TLS, DHCP, and cluster stats.
POST/api/v1/support/sysdump/clusterBuild a cluster-wide sysdump archiveRequires cluster mode. Returns application/gzip.
POST/api/v1/support/sysdump/nodeBuild a node-local sysdump archiveInternal fan-out endpoint. Requires special header.

DNS cache response:

{
  "entries": [
    {
      "hostname": "api.example.com",
      "ips": ["203.0.113.20"],
      "last_seen": 1760000000
    }
  ]
}

Stats response top-level shape:

{
  "dataplane": {},
  "dns": {},
  "tls": {},
  "dhcp": {},
  "cluster": {}
}

The cluster section includes node catch-up information when the Neuwerk is running in cluster mode.

Current Gaps

This page is still a hand-maintained reference, even though it is derived from source. The next improvement should be code-generated OpenAPI output from the Axum handlers so that request and response schemas stop drifting from implementation.