Skip to content

swagger-extras: Raw Handler API

api/swagger-extras.yml is the companion contract for five endpoint groups dispatched directly by the API server's global middleware. They do not use the normal generated handler pipeline, even when the same path is also mentioned in the primary Swagger document.

Development-source behavior

Authentication parity for these raw handlers is implemented in the current development source but has not completed release qualification. Confirm the behavior against the exact image you deploy.

Contract and authentication

  • Base path: /netlox/v1
  • Media type: application/json
  • Contract source: api/swagger-extras.yml
  • Client behavior: use a raw HTTP client unless your SDK explicitly adds the companion contract.

The development implementation calls the same management authenticator and role authorizer before every raw handler. A viewer can call read-only GET operations; mutations require an administrator. A missing or invalid management credential returns 401, an authenticated viewer mutation returns 403, and a recognized management credential-store unavailable condition returns 503. Some live driver failures can still fall through to the generic fail-closed 401 path, so correlate authentication failures with store health.

No management authentication mode means unrestricted access

RequireManagementAuth follows the configured authenticator. If no user, OAuth, or manual-token mode is enabled, it receives an unrestricted principal and permits the raw route. Protect port 11111, enable a management authentication mode, and verify a missing-credential mutation returns 401.

Prepare a protected header file once:

export CONTROL_API="https://gateway.example.com/netlox/v1"
install -m 600 /dev/null ./control-plane.headers
printf 'Authorization: Bearer %s\n' "$CONTROL_PLANE_TOKEN" > ./control-plane.headers

Endpoint groups

Group Methods and path Purpose
AI KV inventory GET /config/ai/kv/inventory Inspect per-endpoint KV block hashes
DPU debug GET, POST /config/dpu/debug Inspect DPU state or trigger a guarded debug action
DPU hardware counters GET /config/dpu/hwcounters Read per-flow hardware packet and byte counters
OPA watcher GET, POST, DELETE /config/opa/watcher Inspect, configure, or remove the OPA L4 watcher
AI key update PATCH /config/ai/apikey/{key_id} Replace a key allow-list and/or change its enabled state

AI KV inventory

GET /config/ai/kv/inventory reads the block-hash inventory for one endpoint of one AI service.

Query Required Meaning
service_id Yes Numeric service identifier (uint32)
ep_idx Yes Endpoint index inside the service

The 200 body includes service_id, ep_idx, hash_algo, blocks[], and total. Each block has block_idx and hash_uint64. block_idx is only a synthetic sequence from map iteration; it is not a semantic position in the backend cache.

curl --fail-with-body --silent --show-error \
  --header @control-plane.headers \
  "$CONTROL_API/config/ai/kv/inventory?service_id=1&ep_idx=0" \
  | jq '{service_id, ep_idx, hash_algo, total}'

Expected failures are 400 for invalid parameters, 404 for an unknown service or endpoint, 405 for another method, and 503 when no inventory provider is registered.

DPU debug

Read state and counters

GET /config/dpu/debug returns aggregate offload counters, per-pipe counters, loaded plugins, circuit-breaker state, and optional per-entry detail.

Query Meaning
flows=1 Include expensive flow, FDB, route, and ACL counter arrays
pipe=<name> Restrict filtered detail to one supported hardware pipe
svc=<name> Filter detail by service name
ep=<address:port> Filter detail by endpoint
limit=<n> Limit detail rows; default 200, clamped to 2000

Supplying pipe, svc, ep, or limit selects the filtered-detail path and populates doca_entry_details. Treat hashed entry handles and flow tuples as sensitive operational metadata.

Supported pipe values are rss, to_kernel, egress_dispatch, ct_fwd_5tuple, ct_rev_5tuple, root_l3l4_dispatch, fdb_l2, deny, and allow.

curl --fail-with-body --silent --show-error \
  --get "$CONTROL_API/config/dpu/debug" \
  --header @control-plane.headers \
  --data-urlencode 'pipe=ct_fwd_5tuple' \
  --data-urlencode 'limit=50' \
  | jq '{enabled, offload_active, circuit_breaker_open, entries: (.doca_entry_details | length)}'

Trigger a debug action

POST /config/dpu/debug accepts two actions:

Body Effect
{"action":"unregister","plugin":"<name>"} Unload the named DPU plugin
{"action":"cb_force","mode":"open"} Force the offload circuit breaker open for testing
{"action":"cb_force","mode":"close"} Force it closed

These operations can alter forwarding behavior. Run them only in an approved maintenance or test window, verify the selected node, and capture sanitized before-and-after state. Invalid input returns 400, another method returns 405, and an unavailable DPU manager returns 503.

DPU hardware counters

GET /config/dpu/hwcounters returns flows[] and total_flows. Each flow may include the raw flow identifier, protocol, source and destination addresses, packet count, and byte count. An unregistered provider returns an empty list; that is different from an HTTP failure.

curl --fail-with-body --silent --show-error \
  --header @control-plane.headers \
  "$CONTROL_API/config/dpu/hwcounters" \
  | jq '{total_flows, sample: (.flows[0] // null)}'

Do not publish raw flow identifiers or addresses in public diagnostics. Only GET is supported; another method returns 405.

OPA L4 policy watcher

Method Purpose
GET Return configuration and runtime status
POST Configure or replace the watcher and begin polling
DELETE Stop and remove the watcher; succeeds when none exists

POST accepts:

Field Required Default Meaning
opa_url Yes none OPA base URL
policy_path No loxilb/l4 Policy path queried by the watcher
poll_interval_sec No 30 Poll interval in seconds
fail_open No false Stored and reported compatibility field; it does not currently change outage behavior. Fetch failures preserve the rules already applied for either value.

The handler applies URL validation and rejects addresses blocked by its SSRF policy. Validate the address form used by your deployment before rollout; do not weaken the guard merely to reach an internal service.

curl --fail-with-body --silent --show-error \
  --request POST "$CONTROL_API/config/opa/watcher" \
  --header @control-plane.headers \
  --header 'Content-Type: application/json' \
  --data '{
    "opa_url": "https://opa.example.com",
    "policy_path": "loxilb/l4",
    "poll_interval_sec": 30,
    "fail_open": false
  }'

curl --fail-with-body --silent --show-error \
  --header @control-plane.headers \
  "$CONTROL_API/config/opa/watcher" \
  | jq '{status, last_sync_at, rules_count, circuit_breaker_state, last_error}'

The path is also declared in the primary Swagger file, but the global middleware intercepts it first when the raw handler is registered. Use the runtime behavior and companion contract for response details.

Update an AI API key

PATCH /config/ai/apikey/{key_id} updates only the fields present in the body:

  • allowed_models: replacement array; an empty array removes the model restriction;
  • enabled: false soft-disables the key and true re-enables it.
curl --fail-with-body --silent --show-error \
  --request PATCH \
  --header @control-plane.headers \
  --header 'Content-Type: application/json' \
  --data '{"allowed_models":["example-chat-model"],"enabled":false}' \
  "$CONTROL_API/config/ai/apikey/$KEY_ID"

Expected success is 204 No Content. Invalid JSON or an empty key ID returns 400; an unknown key returns 404. In the development implementation, an unconfigured or unavailable key store returns 503 even though the current companion specification still lists only a generic 500 for update failure.

The update evicts local key caches and sends best-effort peer invalidation. Peer delivery is not an instantaneous cluster-wide revocation guarantee; see AI Key Store Operations.

Cleanup

rm -f ./control-plane.headers
unset CONTROL_PLANE_TOKEN KEY_ID