{"openapi":"3.0.3","info":{"title":"Edgewatch Endpoint Server API","version":"0.1.0","description":"Control-plane API for Edgewatch Bastion. Two surfaces:\n\n- **Node API** (`/v1/*`) — consumed by `ew-node-agent` over HTTPS + **mTLS**. Every route except `POST /v1/enroll` requires a valid, active node client certificate. mTLS cannot be expressed as an OpenAPI security scheme, so these routes are tagged \"Node API (mTLS)\" and documented here for reference.\n- **Admin API** (`/admin/*`) — consumed by the operator frontend over HTTPS with an **operator JWT**. Every route except `POST /admin/auth/login` requires `Authorization: Bearer <token>`.\n\nAll error responses use the shared envelope `{ \"error\": { \"code\", \"message\", \"requestId\", \"details?\" } }`."},"components":{"securitySchemes":{"operatorJwt":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Operator JWT obtained from `POST /admin/auth/login`."}},"schemas":{}},"paths":{"/healthz":{"get":{"summary":"Liveness probe","tags":["Health"],"description":"Unauthenticated health check. Returns server status and time.","responses":{"200":{"description":"Server is healthy.","content":{"application/json":{"schema":{"description":"Server is healthy.","type":"object","properties":{"status":{"type":"string","example":"ok"},"time":{"type":"string","format":"date-time"}},"required":["status","time"]}}}}}}},"/v1/enroll":{"post":{"summary":"Enroll a node (no client cert)","tags":["Node API (mTLS)"],"description":"First contact. Validates a one-time enrollment token, signs the node CSR, and returns identity material plus the initial signed config. The only `/v1` route that does not require a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string","minLength":1},"csr":{"type":"string","pattern":"CERTIFICATE\\ REQUEST"},"hostname":{"type":"string","minLength":1,"maxLength":253},"machine_id":{"type":"string","minLength":1,"maxLength":256}},"required":["token","csr","hostname","machine_id"],"additionalProperties":false}}}},"responses":{"201":{"description":"Node enrolled; identity material issued.","content":{"application/json":{"schema":{"description":"Node enrolled; identity material issued.","type":"object","properties":{"node_id":{"type":"string","example":"node-..."},"node_certificate":{"type":"string","description":"PEM node client certificate."},"ca_certificate":{"type":"string","description":"PEM CA certificate."},"endpoint_public_key":{"type":"string","description":"PEM endpoint signing public key."},"initial_config":{"type":"object","description":"Signed config bundle. Verify `signature` over the canonical bundle.","properties":{"config_version":{"type":"string","example":"sha256:..."},"bundle":{"type":"object","additionalProperties":true},"signature":{"type":"string","description":"Base64 Ed25519 signature."},"issued_at":{"type":"string","format":"date-time"}},"required":["config_version","bundle","signature","issued_at"]}},"required":["node_id","node_certificate","ca_certificate","endpoint_public_key","initial_config"]}}}},"400":{"description":"Bad request body or malformed CSR.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body or malformed CSR.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Invalid, expired, or already-used token.","content":{"application/json":{"schema":{"type":"object","description":"Invalid, expired, or already-used token.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Token consumed concurrently.","content":{"application/json":{"schema":{"type":"object","description":"Token consumed concurrently.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/whoami":{"get":{"summary":"Resolve node identity from client cert","tags":["Node API (mTLS)"],"description":"Diagnostic. Returns the node identity resolved from the presented mTLS client certificate. Requires a valid, active node client certificate.","responses":{"200":{"description":"Resolved node identity.","content":{"application/json":{"schema":{"description":"Resolved node identity.","type":"object","properties":{"node_id":{"type":"string"},"hostname":{"type":"string"},"status":{"type":"string","example":"active"},"config_version":{"type":"string","nullable":true}},"required":["node_id","hostname","status"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/config":{"get":{"summary":"Fetch desired signed config","tags":["Node API (mTLS)"],"description":"Returns the node's desired, signed config bundle. Conditional fetch via the `x-config-version` request header or `?have=<version>`; returns 304 if the node already holds the current version. Requires a node client certificate.","parameters":[{"schema":{"type":"string"},"in":"query","name":"have","required":false}],"responses":{"200":{"description":"Current signed config bundle.","content":{"application/json":{"schema":{"description":"Current signed config bundle.","type":"object","properties":{"config_version":{"type":"string","example":"sha256:..."},"bundle":{"type":"object","additionalProperties":true,"properties":{"hosts":{"type":"array","items":{}},"upstreams":{"type":"array","items":{}},"firewall":{"type":"object","additionalProperties":true},"waf_policies":{"type":"array","items":{}}}},"signature":{"type":"string","description":"Base64 Ed25519 signature."},"issued_at":{"type":"string","format":"date-time"}},"required":["config_version","bundle","signature","issued_at"]}}}},"304":{"description":"Config unchanged (node already holds this version)."},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/heartbeat":{"post":{"summary":"Heartbeat: status report + command poll","tags":["Node API (mTLS)"],"description":"Combined status report and command poll. Updates `last_seen_at` and the applied config version, then returns outstanding commands and a `force_pull` hint when the desired config differs from the applied one. Requires a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"config_version":{"type":"string"},"services":{"type":"object","additionalProperties":{}},"cert_expiry_seconds":{"type":"number"},"csync2":{"type":"object","additionalProperties":{}},"apply_success_total":{"type":"number"},"apply_failures_total":{"type":"number"},"csync2_conflicts":{"type":"number"},"firewall_apply_failures_total":{"type":"number"},"last_apply_success_at":{"type":"string"}},"additionalProperties":true}}}},"responses":{"200":{"description":"Outstanding commands and force-pull hint.","content":{"application/json":{"schema":{"description":"Outstanding commands and force-pull hint.","type":"object","properties":{"commands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"cmd-..."},"type":{"type":"string","example":"apply_config"},"payload":{"type":"object","additionalProperties":true,"nullable":true},"deadline":{"type":"string","format":"date-time","nullable":true}},"required":["id","type"]}},"force_pull":{"type":"boolean"}},"required":["commands","force_pull"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/commands/{id}/ack":{"post":{"summary":"Acknowledge a command result","tags":["Node API (mTLS)"],"description":"The node reports the result of a command. Idempotent — acking the same command twice does not double-apply. Requires a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"detail":{}},"required":["success"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Command acknowledged.","content":{"application/json":{"schema":{"description":"Command acknowledged.","type":"object","properties":{"id":{"type":"string","example":"cmd-..."},"status":{"type":"string","enum":["done","failed"]},"already_acked":{"type":"boolean"}},"required":["id","status","already_acked"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Command not found for this node.","content":{"application/json":{"schema":{"type":"object","description":"Command not found for this node.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/telemetry":{"post":{"summary":"Ingest a batch of telemetry samples","tags":["Node API (mTLS)"],"description":"Batch metric ingestion (up to 1000 samples per request). Requires a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"samples":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"value":{"type":"number"},"ts":{"type":"string","format":"date-time"},"labels":{"type":"object","additionalProperties":{}}},"required":["name","value"],"additionalProperties":false},"maxItems":1000}},"required":["samples"],"additionalProperties":true}}}},"responses":{"202":{"description":"Samples accepted for ingestion.","content":{"application/json":{"schema":{"description":"Samples accepted for ingestion.","type":"object","properties":{"ingested":{"type":"integer","minimum":0}},"required":["ingested"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/summary":{"post":{"summary":"Ingest a telemetry summary snapshot","tags":["Node API (mTLS)"],"description":"Upserts the latest VeryNginx-style summary snapshot (per-URI + named collect aggregates) for this node, keyed by `type` (long|short). Requires a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["long","short"]},"uri":{"type":"object","additionalProperties":{"type":"object","properties":{"count":{"type":"number"},"size":{"type":"number"},"time":{"type":"number"},"status":{"type":"object","additionalProperties":{"type":"number"},"default":{}}},"required":["count","size","time"],"additionalProperties":true},"default":{}},"collect":{"type":"object","additionalProperties":{"type":"object","properties":{"count":{"type":"number"},"size":{"type":"number"},"time":{"type":"number"},"status":{"type":"object","additionalProperties":{"type":"number"},"default":{}}},"required":["count","size","time"],"additionalProperties":true},"default":{}}},"required":["type"],"additionalProperties":true}}}},"responses":{"202":{"description":"Summary snapshot accepted and stored.","content":{"application/json":{"schema":{"description":"Summary snapshot accepted and stored.","type":"object","properties":{"stored":{"type":"boolean"}},"required":["stored"]}}}},"400":{"description":"Bad summary payload.","content":{"application/json":{"schema":{"type":"object","description":"Bad summary payload.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/inventory":{"post":{"summary":"Ingest a node inventory snapshot","tags":["Node API (mTLS)"],"description":"Upserts the node-ops inventory (services/packages/diagnostics/etc.) reported by the agent. Read-only data; requires a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"sections":{"type":"object","additionalProperties":{},"default":{}}},"additionalProperties":true}}}},"responses":{"202":{"description":"Inventory accepted and stored.","content":{"application/json":{"schema":{"description":"Inventory accepted and stored.","type":"object","properties":{"stored":{"type":"boolean"}},"required":["stored"]}}}},"400":{"description":"Bad inventory payload.","content":{"application/json":{"schema":{"type":"object","description":"Bad inventory payload.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/logs":{"post":{"summary":"Ship recent nginx log lines","tags":["Node API (mTLS)"],"description":"Ingests a bounded tail of nginx access/error log lines. Size-limited; requires a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"stream":{"type":"string","enum":["access","error"]},"lines":{"type":"array","items":{"type":"object","properties":{"line":{"type":"string"},"ts":{"type":"string"},"vhost":{"type":"string"}},"required":["line"],"additionalProperties":false},"maxItems":500}},"required":["stream","lines"],"additionalProperties":true}}}},"responses":{"202":{"description":"Log lines accepted.","content":{"application/json":{"schema":{"description":"Log lines accepted.","type":"object","properties":{"ingested":{"type":"number"}},"required":["ingested"]}}}},"400":{"description":"Bad logs payload.","content":{"application/json":{"schema":{"type":"object","description":"Bad logs payload.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/v1/modsecurity/events":{"post":{"summary":"Ship ModSecurity audit events","tags":["Node API (mTLS)"],"description":"Ingests a bounded batch of normalized ModSecurity audit transactions. Idempotent on (node_id, transaction_id). Requires a node client certificate.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"events":{"type":"array","items":{"type":"object","properties":{"event_id":{"type":"string","minLength":1},"transaction_id":{"type":"string","minLength":1},"occurred_at":{"type":"string","minLength":1},"parser_version":{"type":"string","minLength":1},"format":{"type":"string","minLength":1},"site_name":{"type":"string"},"host":{"type":"string"},"client_ip":{"type":"string"},"client_port":{"type":"integer"},"server_ip":{"type":"string"},"server_port":{"type":"integer"},"method":{"type":"string"},"uri":{"type":"string"},"http_version":{"type":"string"},"status":{"type":"integer"},"response_size":{"type":"integer"},"action":{"type":"string","minLength":1},"engine_mode":{"type":"string"},"highest_severity":{"type":"integer"},"producer":{"type":"string"},"server_software":{"type":"string"},"rule_hits":{"type":"array","items":{"type":"object","properties":{"rule_id":{"type":"string"},"message":{"type":"string"},"severity":{"type":"integer"},"tags":{"type":"array","items":{"type":"string"}},"data":{"type":"string"},"file":{"type":"string"},"line":{"type":"integer"},"disruptive":{"type":"boolean"}},"additionalProperties":true}},"messages":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"type":"string"}},"raw_base64":{"type":"string"},"raw_sha256":{"type":"string","minLength":1},"raw_bytes":{"type":"integer","minimum":0},"raw_truncated":{"type":"boolean"}},"required":["event_id","transaction_id","occurred_at","parser_version","format","action","raw_sha256","raw_bytes"],"additionalProperties":true},"maxItems":100}},"required":["events"],"additionalProperties":true}}}},"responses":{"202":{"description":"Batch accepted (may include duplicates/rejections).","content":{"application/json":{"schema":{"description":"Batch accepted (may include duplicates/rejections).","type":"object","properties":{"accepted":{"type":"array","items":{"type":"string"}},"duplicates":{"type":"array","items":{"type":"string"}},"rejected":{"type":"array","items":{"type":"string"}}},"required":["accepted","duplicates","rejected"]}}}},"400":{"description":"Bad ModSecurity events payload.","content":{"application/json":{"schema":{"type":"object","description":"Bad ModSecurity events payload.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid node client certificate.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid node client certificate.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/auth/login":{"post":{"summary":"Operator login","tags":["Admin API"],"description":"Exchanges operator credentials for a JWT. Public route (no bearer required). Rate-limited tighter than the global default.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","minLength":1}},"required":["email","password"],"additionalProperties":false}}}},"responses":{"200":{"description":"Authenticated; JWT issued.","content":{"application/json":{"schema":{"description":"Authenticated; JWT issued.","type":"object","properties":{"token":{"type":"string"},"expires_in_hours":{"type":"integer","example":12},"operator":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"role":{"type":"string","example":"admin"},"tenantId":{"type":"string","format":"uuid"},"tenantName":{"type":"string"},"isSuperadmin":{"type":"boolean"}},"required":["id","email","role","tenantId","tenantName","isSuperadmin"]}},"required":["token","expires_in_hours","operator"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Invalid credentials.","content":{"application/json":{"schema":{"type":"object","description":"Invalid credentials.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/tenants":{"get":{"summary":"List tenants","tags":["Admin API"],"description":"Lists all tenants with basic resource counts. Requires superadmin.","security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Tenant listing.","content":{"application/json":{"schema":{"description":"Tenant listing.","type":"object","properties":{"tenants":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["active","suspended"]},"contact_name":{"type":"string","nullable":true},"contact_email":{"type":"string","nullable":true},"contact_phone":{"type":"string","nullable":true},"billing_address":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"created_by":{"type":"string","nullable":true},"user_count":{"type":"integer"},"node_count":{"type":"integer"},"cluster_count":{"type":"integer"}},"required":["id","name","slug","status","created_at"]}}},"required":["tenants"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Superadmin required.","content":{"application/json":{"schema":{"type":"object","description":"Superadmin required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"post":{"summary":"Create a tenant with its first admin","tags":["Admin API"],"description":"Creates a new tenant and its first admin operator in one transaction. Requires superadmin.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":128},"slug":{"type":"string","minLength":1,"maxLength":64,"pattern":"^[a-z0-9]+(?:-[a-z0-9]+)*$"},"contact_name":{"type":"string","maxLength":256},"contact_email":{"type":"string","format":"email","maxLength":320},"contact_phone":{"type":"string","maxLength":64},"billing_address":{"type":"string","maxLength":1024},"notes":{"type":"string","maxLength":4096},"admin_email":{"type":"string","format":"email","maxLength":320},"admin_password":{"type":"string","minLength":8,"maxLength":256}},"required":["name","admin_email","admin_password"],"additionalProperties":false}}}},"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Tenant created.","content":{"application/json":{"schema":{"description":"Tenant created.","type":"object","properties":{"tenant":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["active","suspended"]},"contact_name":{"type":"string","nullable":true},"contact_email":{"type":"string","nullable":true},"contact_phone":{"type":"string","nullable":true},"billing_address":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"created_by":{"type":"string","nullable":true},"user_count":{"type":"integer"},"node_count":{"type":"integer"},"cluster_count":{"type":"integer"}},"required":["id","name","slug","status","created_at"]},"admin":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string"}},"required":["id","email","role"]}},"required":["tenant","admin"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Superadmin required.","content":{"application/json":{"schema":{"type":"object","description":"Superadmin required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Name, slug, or admin email already exists.","content":{"application/json":{"schema":{"type":"object","description":"Name, slug, or admin email already exists.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/tenants/{id}":{"get":{"summary":"Get a tenant","tags":["Admin API"],"description":"Returns a single tenant. Requires superadmin.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Tenant detail.","content":{"application/json":{"schema":{"description":"Tenant detail.","type":"object","properties":{"tenant":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["active","suspended"]},"contact_name":{"type":"string","nullable":true},"contact_email":{"type":"string","nullable":true},"contact_phone":{"type":"string","nullable":true},"billing_address":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"created_by":{"type":"string","nullable":true},"user_count":{"type":"integer"},"node_count":{"type":"integer"},"cluster_count":{"type":"integer"}},"required":["id","name","slug","status","created_at"]}},"required":["tenant"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Superadmin required.","content":{"application/json":{"schema":{"type":"object","description":"Superadmin required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","description":"Tenant not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"patch":{"summary":"Update a tenant","tags":["Admin API"],"description":"Updates tenant company details and/or status (active/suspended). Requires superadmin.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":128},"status":{"type":"string","enum":["active","suspended"]},"contact_name":{"type":"string","maxLength":256,"nullable":true},"contact_email":{"type":"string","format":"email","maxLength":320,"nullable":true},"contact_phone":{"type":"string","maxLength":64,"nullable":true},"billing_address":{"type":"string","maxLength":1024,"nullable":true},"notes":{"type":"string","maxLength":4096,"nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Tenant updated.","content":{"application/json":{"schema":{"description":"Tenant updated.","type":"object","properties":{"tenant":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string"},"status":{"type":"string","enum":["active","suspended"]},"contact_name":{"type":"string","nullable":true},"contact_email":{"type":"string","nullable":true},"contact_phone":{"type":"string","nullable":true},"billing_address":{"type":"string","nullable":true},"notes":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"created_by":{"type":"string","nullable":true},"user_count":{"type":"integer"},"node_count":{"type":"integer"},"cluster_count":{"type":"integer"}},"required":["id","name","slug","status","created_at"]}},"required":["tenant"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Superadmin required.","content":{"application/json":{"schema":{"type":"object","description":"Superadmin required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Tenant not found.","content":{"application/json":{"schema":{"type":"object","description":"Tenant not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Tenant name already exists.","content":{"application/json":{"schema":{"type":"object","description":"Tenant name already exists.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes":{"get":{"summary":"List nodes","tags":["Admin API"],"description":"Lists the fleet with derived online/status fields. Requires operator JWT.","security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Fleet listing.","content":{"application/json":{"schema":{"description":"Fleet listing.","type":"object","properties":{"nodes":{"type":"array","items":{"type":"object","additionalProperties":true,"description":"Node record with derived online/status fields.","properties":{"id":{"type":"string","example":"node-..."},"hostname":{"type":"string"},"machine_id":{"type":"string","nullable":true},"cluster_id":{"type":"string","nullable":true},"status":{"type":"string","example":"active"},"online":{"type":"boolean"},"config_version":{"type":"string","nullable":true},"cert_serial":{"type":"string","nullable":true},"cert_fingerprint":{"type":"string","nullable":true},"last_seen_at":{"type":"string","format":"date-time","nullable":true},"enrolled_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"revoked_at":{"type":"string","format":"date-time","nullable":true},"cert_expires_at":{"type":"string","format":"date-time","nullable":true},"last_apply_at":{"type":"string","format":"date-time","nullable":true},"apply_failures_total":{"type":"integer","nullable":true},"csync2_conflicts":{"type":"integer","nullable":true},"firewall_apply_failures_total":{"type":"integer","nullable":true},"bootstrap_config_sync":{"type":"string","enum":["pending","completed","failed"]},"bootstrap_config_sync_error":{"type":"string","nullable":true}}}}},"required":["nodes"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"post":{"summary":"Register a node","tags":["Admin API"],"description":"Registers a node and returns a one-time enrollment token plus a copyable enroll command. Requires operator JWT.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hostname":{"type":"string","minLength":1,"maxLength":253},"cluster_id":{"type":"string","format":"uuid"},"ttl_minutes":{"type":"integer","exclusiveMinimum":true,"minimum":0}},"required":["hostname"],"additionalProperties":false}}}},"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Node registered; enrollment material issued.","content":{"application/json":{"schema":{"description":"Node registered; enrollment material issued.","type":"object","properties":{"node":{"type":"object","additionalProperties":true,"description":"Node record with derived online/status fields.","properties":{"id":{"type":"string","example":"node-..."},"hostname":{"type":"string"},"machine_id":{"type":"string","nullable":true},"cluster_id":{"type":"string","nullable":true},"status":{"type":"string","example":"active"},"online":{"type":"boolean"},"config_version":{"type":"string","nullable":true},"cert_serial":{"type":"string","nullable":true},"cert_fingerprint":{"type":"string","nullable":true},"last_seen_at":{"type":"string","format":"date-time","nullable":true},"enrolled_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"revoked_at":{"type":"string","format":"date-time","nullable":true},"cert_expires_at":{"type":"string","format":"date-time","nullable":true},"last_apply_at":{"type":"string","format":"date-time","nullable":true},"apply_failures_total":{"type":"integer","nullable":true},"csync2_conflicts":{"type":"integer","nullable":true},"firewall_apply_failures_total":{"type":"integer","nullable":true},"bootstrap_config_sync":{"type":"string","enum":["pending","completed","failed"]},"bootstrap_config_sync_error":{"type":"string","nullable":true}}},"enrollment":{"type":"object","properties":{"token":{"type":"string","example":"enk-..."},"expires_at":{"type":"string","format":"date-time"},"endpoint_url":{"type":"string","example":"https://endpoint.example.com"},"enroll_command":{"type":"string"}},"required":["token","expires_at","endpoint_url","enroll_command"]}},"required":["node","enrollment"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}":{"get":{"summary":"Get node detail","tags":["Admin API"],"description":"Returns the node record plus a summary of its current config. Requires operator JWT.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Node detail.","content":{"application/json":{"schema":{"description":"Node detail.","type":"object","properties":{"node":{"type":"object","additionalProperties":true,"description":"Node record with derived online/status fields.","properties":{"id":{"type":"string","example":"node-..."},"hostname":{"type":"string"},"machine_id":{"type":"string","nullable":true},"cluster_id":{"type":"string","nullable":true},"status":{"type":"string","example":"active"},"online":{"type":"boolean"},"config_version":{"type":"string","nullable":true},"cert_serial":{"type":"string","nullable":true},"cert_fingerprint":{"type":"string","nullable":true},"last_seen_at":{"type":"string","format":"date-time","nullable":true},"enrolled_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"revoked_at":{"type":"string","format":"date-time","nullable":true},"cert_expires_at":{"type":"string","format":"date-time","nullable":true},"last_apply_at":{"type":"string","format":"date-time","nullable":true},"apply_failures_total":{"type":"integer","nullable":true},"csync2_conflicts":{"type":"integer","nullable":true},"firewall_apply_failures_total":{"type":"integer","nullable":true},"bootstrap_config_sync":{"type":"string","enum":["pending","completed","failed"]},"bootstrap_config_sync_error":{"type":"string","nullable":true}}},"current_config":{"type":"object","nullable":true,"properties":{"version":{"type":"string","example":"sha256:..."},"created_at":{"type":"string","format":"date-time"}},"required":["version","created_at"]}},"required":["node"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"patch":{"summary":"Update a node","tags":["Admin API"],"description":"Updates a node's cluster membership. Pass `cluster_id` to assign a cluster or `null` to remove the node from its cluster. Requires operator JWT.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"cluster_id":{"type":"string","format":"uuid","nullable":true}},"required":["cluster_id"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Node updated.","content":{"application/json":{"schema":{"description":"Node updated.","type":"object","properties":{"node":{"type":"object","additionalProperties":true,"description":"Node record with derived online/status fields.","properties":{"id":{"type":"string","example":"node-..."},"hostname":{"type":"string"},"machine_id":{"type":"string","nullable":true},"cluster_id":{"type":"string","nullable":true},"status":{"type":"string","example":"active"},"online":{"type":"boolean"},"config_version":{"type":"string","nullable":true},"cert_serial":{"type":"string","nullable":true},"cert_fingerprint":{"type":"string","nullable":true},"last_seen_at":{"type":"string","format":"date-time","nullable":true},"enrolled_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"revoked_at":{"type":"string","format":"date-time","nullable":true},"cert_expires_at":{"type":"string","format":"date-time","nullable":true},"last_apply_at":{"type":"string","format":"date-time","nullable":true},"apply_failures_total":{"type":"integer","nullable":true},"csync2_conflicts":{"type":"integer","nullable":true},"firewall_apply_failures_total":{"type":"integer","nullable":true},"bootstrap_config_sync":{"type":"string","enum":["pending","completed","failed"]},"bootstrap_config_sync_error":{"type":"string","nullable":true}}}},"required":["node"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or cluster not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or cluster not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"delete":{"summary":"Decommission a node","tags":["Admin API"],"description":"Revokes (decommissions) the node. Requires operator JWT.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Node revoked.","content":{"application/json":{"schema":{"description":"Node revoked.","type":"object","properties":{"node":{"type":"object","additionalProperties":true,"description":"Node record with derived online/status fields.","properties":{"id":{"type":"string","example":"node-..."},"hostname":{"type":"string"},"machine_id":{"type":"string","nullable":true},"cluster_id":{"type":"string","nullable":true},"status":{"type":"string","example":"active"},"online":{"type":"boolean"},"config_version":{"type":"string","nullable":true},"cert_serial":{"type":"string","nullable":true},"cert_fingerprint":{"type":"string","nullable":true},"last_seen_at":{"type":"string","format":"date-time","nullable":true},"enrolled_at":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"revoked_at":{"type":"string","format":"date-time","nullable":true},"cert_expires_at":{"type":"string","format":"date-time","nullable":true},"last_apply_at":{"type":"string","format":"date-time","nullable":true},"apply_failures_total":{"type":"integer","nullable":true},"csync2_conflicts":{"type":"integer","nullable":true},"firewall_apply_failures_total":{"type":"integer","nullable":true},"bootstrap_config_sync":{"type":"string","enum":["pending","completed","failed"]},"bootstrap_config_sync_error":{"type":"string","nullable":true}}}},"required":["node"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config":{"get":{"summary":"Get node config + history","tags":["Admin API"],"description":"Returns the current desired config plus recent version history. Requires operator JWT.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Current config and history.","content":{"application/json":{"schema":{"description":"Current config and history.","type":"object","properties":{"current":{"type":"object","nullable":true,"properties":{"version":{"type":"string","example":"sha256:..."},"bundle":{"type":"object","additionalProperties":true,"properties":{"hosts":{"type":"array","items":{}},"upstreams":{"type":"array","items":{}},"firewall":{"type":"object","additionalProperties":true},"waf_policies":{"type":"array","items":{}}}},"signature":{"type":"string"},"created_at":{"type":"string","format":"date-time"}},"required":["version","bundle","signature","created_at"]},"history":{"type":"array","items":{"type":"object","properties":{"version":{"type":"string"},"is_current":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"},"created_by":{"type":"string","nullable":true}},"required":["version","is_current","created_at"]}}},"required":["history"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"put":{"summary":"Set node desired config","tags":["Admin API"],"description":"Sets a new desired config bundle. The server canonicalizes, re-signs and bumps the version. Returns 201 for a new version or 200 when unchanged. Requires operator JWT.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"hosts":{"type":"array","items":{},"default":[]},"upstreams":{"type":"array","items":{},"default":[]},"firewall":{"type":"object","additionalProperties":{},"default":{}},"waf_policies":{"type":"array","items":{},"default":[]},"matchers":{"type":"object","additionalProperties":{"type":"object","properties":{"uri":{"type":"object","properties":{"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["operator","value"],"additionalProperties":true},"ip":{"type":"object","properties":{"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["operator","value"],"additionalProperties":true},"user_agent":{"type":"object","properties":{"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["operator","value"],"additionalProperties":true},"referer":{"type":"object","properties":{"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["operator","value"],"additionalProperties":true},"host":{"type":"object","properties":{"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["operator","value"],"additionalProperties":true},"method":{"type":"object","properties":{"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["operator","value"],"additionalProperties":true},"args":{"type":"object","properties":{"name_operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"name_value":{"type":"string"},"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["name_operator","name_value","operator","value"],"additionalProperties":true},"header":{"type":"object","properties":{"name_operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"name_value":{"type":"string"},"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["name_operator","name_value","operator","value"],"additionalProperties":true},"cookie":{"type":"object","properties":{"name_operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"name_value":{"type":"string"},"operator":{"type":"string","enum":["eq","ne","contains","regex","nregex","exist","nexist"]},"value":{"type":"string"}},"required":["name_operator","name_value","operator","value"],"additionalProperties":true}},"additionalProperties":true}},"responses":{"type":"object","additionalProperties":{"type":"object","properties":{"content_type":{"type":"string"},"body":{"type":"string"}},"required":["content_type","body"],"additionalProperties":true}},"filter":{"type":"object","properties":{"enable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"action":{"type":"string","enum":["accept","block"]},"code":{"type":"integer"},"response":{"type":"string"}},"required":["enable","matcher","action"],"additionalProperties":true}}},"required":["enable","rules"],"additionalProperties":true},"rate_limits":{"type":"object","properties":{"enable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"window_seconds":{"type":"number"},"max_count":{"type":"number"},"code":{"type":"integer"},"response":{"type":"string"},"separate":{"type":"array","items":{"type":"string","enum":["ip","uri"]}}},"required":["enable","matcher","window_seconds","max_count"],"additionalProperties":true}}},"required":["enable","rules"],"additionalProperties":true},"redirects":{"type":"object","properties":{"enable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"replace_re":{"type":"string"},"to_uri":{"type":"string"}},"required":["enable","matcher","to_uri"],"additionalProperties":true}}},"required":["enable","rules"],"additionalProperties":true},"uri_rewrites":{"type":"object","properties":{"enable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"replace_re":{"type":"string"},"to_uri":{"type":"string"}},"required":["enable","matcher","to_uri"],"additionalProperties":true}}},"required":["enable","rules"],"additionalProperties":true},"scheme_lock":{"type":"object","properties":{"enable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"scheme":{"type":"string","enum":["http","https","none"]}},"required":["enable","matcher","scheme"],"additionalProperties":true}}},"required":["enable","rules"],"additionalProperties":true},"browser_verify":{"type":"object","properties":{"enable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"type":{"type":"array","items":{"type":"string","enum":["cookie","javascript"]}}},"required":["enable","matcher","type"],"additionalProperties":true}}},"required":["enable","rules"],"additionalProperties":true},"anti_ddos":{"type":"object","properties":{"enable":{"type":"boolean"},"auto_protection":{"type":"boolean","default":false},"auto_threshold_rps":{"type":"integer","minimum":1,"default":100},"under_attack_mode":{"type":"boolean","default":false},"difficulty":{"type":"integer","minimum":1,"maximum":7,"default":3},"rate_limit_per_minute":{"type":"integer","minimum":1,"default":10},"session_expire_seconds":{"type":"integer","minimum":60,"default":604800},"proxy_mode":{"type":"string","enum":["direct","cloudflare","proxy"],"default":"direct"},"secret":{"type":"string"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"difficulty":{"type":"integer","minimum":1,"maximum":7}},"required":["enable","matcher"],"additionalProperties":true},"default":[]}},"required":["enable"],"additionalProperties":true},"proxy":{"type":"object","properties":{"enable":{"type":"boolean"},"upstreams":{"type":"object","additionalProperties":{"type":"object","properties":{"method":{"type":"string","enum":["random","ip_hash"]},"nodes":{"type":"array","items":{"type":"object","properties":{"scheme":{"type":"string","enum":["http","https"]},"host":{"type":"string"},"port":{"type":"integer"},"weight":{"type":"number"}},"required":["scheme","host"],"additionalProperties":true}},"tags":{"type":"array","items":{"type":"string"}}},"required":["method","nodes"],"additionalProperties":true}},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"upstream":{"type":"string"},"proxy_host":{"type":"string"}},"required":["enable","matcher","upstream"],"additionalProperties":true}}},"required":["enable","upstreams","rules"],"additionalProperties":true},"static_files":{"type":"object","properties":{"enable":{"type":"boolean"},"rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"root":{"type":"string"},"expires":{"type":"string"}},"required":["enable","matcher","root"],"additionalProperties":true}}},"required":["enable","rules"],"additionalProperties":true},"summary":{"type":"object","properties":{"request_enable":{"type":"boolean"},"with_host":{"type":"boolean"},"group_persistent_enable":{"type":"boolean"},"group_temporary_enable":{"type":"boolean"},"temporary_period_seconds":{"type":"number"},"collect_enable":{"type":"boolean"},"collect_rules":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"matcher":{"type":"string"},"collect_name":{"type":"string"}},"required":["enable","matcher","collect_name"],"additionalProperties":true}}},"required":["request_enable","with_host","group_persistent_enable","group_temporary_enable","temporary_period_seconds","collect_enable","collect_rules"],"additionalProperties":true},"sites":{"type":"object","properties":{"enable":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"name":{"type":"string"},"server_names":{"type":"array","items":{"type":"string"}},"listen":{"type":"array","items":{"type":"integer"}},"tls":{"type":"object","properties":{"enable":{"type":"boolean"},"certificate":{"type":"string"}},"required":["enable"],"additionalProperties":true},"locations":{"type":"array","items":{"type":"object","properties":{"path":{"type":"string"},"handler":{"type":"string","enum":["proxy","static","return"]},"upstream":{"type":"string"},"root":{"type":"string"},"return_code":{"type":"integer"},"return_target":{"type":"string"}},"required":["path","handler"],"additionalProperties":true}},"tags":{"type":"array","items":{"type":"string"}}},"required":["enable","name","server_names","listen","locations"],"additionalProperties":true}}},"required":["enable","items"],"additionalProperties":true},"streams":{"type":"object","properties":{"enable":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"name":{"type":"string"},"listen":{"type":"integer"},"protocol":{"type":"string","enum":["tcp","udp"]},"upstream":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}}},"required":["enable","name","listen","protocol","upstream"],"additionalProperties":true}}},"required":["enable","items"],"additionalProperties":true},"blocklists":{"type":"object","properties":{"enable":{"type":"boolean"},"items":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean"},"cidr":{"type":"string"},"note":{"type":"string"}},"required":["enable","cidr"],"additionalProperties":true}}},"required":["enable","items"],"additionalProperties":true},"cti_feeds":{"type":"object","properties":{"enable":{"type":"boolean"},"base_url":{"type":"string","format":"uri"},"sync_interval_minutes":{"type":"integer","minimum":5,"maximum":1440},"feeds":{"type":"array","items":{"type":"object","properties":{"type":{"type":"string","enum":["blacklist","brute-force","ids-alert","scanner","ddos","spam","malware-distribution","exploit","infected-system","c2-server","unauthorized-use-of-resources","other","undetermined","phishing","information-disclosure","system-compromise"]},"enable":{"type":"boolean"}},"required":["type","enable"],"additionalProperties":true},"default":[]}},"required":["enable"],"additionalProperties":true},"waf":{"type":"object","properties":{"enable":{"type":"boolean","default":false},"mode":{"type":"string","enum":["off","detect","block"],"default":"off"},"scopes":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean","default":true},"scope":{"type":"string","enum":["global","domain","proxy","uri","parameter"]},"target":{"type":"string"},"mode":{"type":"string","enum":["off","detect","block"]},"reason":{"type":"string"},"updated_by":{"type":"string"},"updated_at":{"type":"string","format":"date-time"}},"required":["scope","mode"],"additionalProperties":true},"default":[]},"modsecurity_exceptions":{"type":"array","items":{"type":"object","properties":{"enable":{"type":"boolean","default":true},"rule_id":{"type":"string","minLength":1},"scope":{"type":"string","enum":["global","domain","proxy","uri","parameter"]},"target":{"type":"string"},"uri":{"type":"string"},"parameter":{"type":"string"},"mode":{"type":"string","enum":["off","detect"]},"reason":{"type":"string","minLength":1},"author":{"type":"string"},"expires_at":{"type":"string","format":"date-time"},"revision_id":{"type":"string"}},"required":["rule_id","scope","mode","reason"],"additionalProperties":true},"default":[]}},"additionalProperties":true},"fail2ban":{"type":"object","properties":{"enable":{"type":"boolean","default":false},"jails":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","pattern":"^[A-Za-z0-9._-]+$"},"enable":{"type":"boolean","default":true},"filter":{"type":"string","minLength":1},"logpath":{"type":"string","minLength":1},"backend":{"type":"string","default":"auto"},"bantime":{"type":"integer","exclusiveMinimum":true,"minimum":0},"findtime":{"type":"integer","exclusiveMinimum":true,"minimum":0},"maxretry":{"type":"integer","exclusiveMinimum":true,"minimum":0},"action":{"type":"string","default":""},"ignoreip":{"type":"array","items":{"type":"string"},"default":[]}},"required":["name","filter","logpath"],"additionalProperties":true},"default":[]}},"additionalProperties":true},"openresty_defaults":{"type":"object","properties":{"enable":{"type":"boolean","default":true},"pages":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","enum":["index","default","fallback","403","404","500","502","503","504"]},"status":{"anyOf":[{"type":"number","enum":[403]},{"type":"number","enum":[404]},{"type":"number","enum":[500]},{"type":"number","enum":[502]},{"type":"number","enum":[503]},{"type":"number","enum":[504]}]},"content_type":{"type":"string","enum":["text/html","text/html; charset=utf-8"]},"body":{"type":"string","maxLength":262144}},"required":["name","content_type","body"],"additionalProperties":true},"minItems":1}},"required":["pages"],"additionalProperties":true},"certificates":{"type":"object","properties":{"items":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"cert_pem":{"type":"string"},"key_pem":{"type":"string"}},"required":["name","cert_pem","key_pem"],"additionalProperties":true}}},"required":["items"],"additionalProperties":true}},"additionalProperties":true}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Config unchanged (version reused).","content":{"application/json":{"schema":{"description":"Config unchanged (version reused).","type":"object","properties":{"version":{"type":"string","example":"sha256:..."},"is_new":{"type":"boolean","example":false},"enqueued_commands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"cmd-..."},"type":{"type":"string","example":"firewall_apply_safe"},"section":{"type":"string","example":"firewall"}},"required":["id","type","section"]}}},"required":["version","is_new"]}}}},"201":{"description":"New config version created.","content":{"application/json":{"schema":{"description":"New config version created.","type":"object","properties":{"version":{"type":"string","example":"sha256:..."},"is_new":{"type":"boolean","example":true},"enqueued_commands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"cmd-..."},"type":{"type":"string","example":"firewall_apply_safe"},"section":{"type":"string","example":"firewall"}},"required":["id","type","section"]},"description":"Enforcement commands auto-queued because a section changed (e.g. firewall → firewall_apply_safe). Track status in the node command history."}},"required":["version","is_new"]}}}},"400":{"description":"Bad config bundle.","content":{"application/json":{"schema":{"type":"object","description":"Bad config bundle.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/revisions/{version}":{"get":{"summary":"Get config revision audit detail","tags":["Admin API"],"description":"Returns a best-effort audit view for a desired-config revision, including changed sections and apply command results.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"version","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Revision audit detail.","content":{"application/json":{"schema":{"description":"Revision audit detail.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or revision not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or revision not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/drift":{"get":{"summary":"Get config drift status","tags":["Admin API"],"description":"Compares the current desired config with the node-reported active policy.json and exposes import/reject availability.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Drift status.","content":{"application/json":{"schema":{"description":"Drift status.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/manual-drift":{"get":{"summary":"List manual Nginx/OpenResty drift events","tags":["Admin API"],"description":"Lists pending and resolved drift events reported by the node from managed Nginx/OpenResty include paths.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Manual drift events.","content":{"application/json":{"schema":{"description":"Manual drift events.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/manual-drift/{driftId}":{"get":{"summary":"Get manual Nginx/OpenResty drift detail","tags":["Admin API"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"driftId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Manual drift detail.","content":{"application/json":{"schema":{"description":"Manual drift detail.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or drift event not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or drift event not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/manual-drift/{driftId}/validate":{"post":{"summary":"Validate manual Nginx/OpenResty drift","tags":["Admin API"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"driftId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Manual drift validation.","content":{"application/json":{"schema":{"description":"Manual drift validation.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or drift event not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or drift event not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/manual-drift/{driftId}/import":{"post":{"summary":"Import manual Nginx/OpenResty drift","tags":["Admin API"],"description":"Promotes a validated manual drift event into revision metadata so the change is attributable and auditable.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"driftId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Manual drift imported.","content":{"application/json":{"schema":{"description":"Manual drift imported.","type":"object","additionalProperties":true}}}},"400":{"description":"Manual drift failed validation.","content":{"application/json":{"schema":{"type":"object","description":"Manual drift failed validation.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or drift event not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or drift event not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/manual-drift/{driftId}/reject":{"post":{"summary":"Reject manual Nginx/OpenResty drift","tags":["Admin API"],"description":"Queues apply_config so the node restores desired generated config.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"driftId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Restore command queued.","content":{"application/json":{"schema":{"description":"Restore command queued.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or drift event not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or drift event not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/vhosts":{"get":{"summary":"List discovered Nginx/OpenResty vhosts","tags":["Admin API"],"description":"Lists server blocks discovered on the node via the nginx_vhosts inventory section.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Discovered vhosts.","content":{"application/json":{"schema":{"description":"Discovered vhosts.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/vhosts/{vhostId}":{"get":{"summary":"Get discovered vhost detail","tags":["Admin API"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"vhostId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Discovered vhost detail.","content":{"application/json":{"schema":{"description":"Discovered vhost detail.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or vhost not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or vhost not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/vhosts/{vhostId}/validate":{"post":{"summary":"Validate discovered vhost for adoption","tags":["Admin API"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"vhostId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Vhost adoption validation.","content":{"application/json":{"schema":{"description":"Vhost adoption validation.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or vhost not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or vhost not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/vhosts/{vhostId}/adopt":{"post":{"summary":"Adopt discovered vhost into managed Sites","tags":["Admin API"],"description":"Appends a sites.items entry (and optional proxy upstreams) derived from the discovered server block, signs a new config version, and typically enqueues apply_config.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"upstream_map":{"type":"object","additionalProperties":{"type":"string"}},"create_upstreams":{"type":"boolean"},"site_name":{"type":"string"},"disable_site":{"type":"boolean"},"retire_source":{"type":"boolean"}},"additionalProperties":false,"default":{}}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"vhostId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Vhost adopted into a new desired revision.","content":{"application/json":{"schema":{"description":"Vhost adopted into a new desired revision.","type":"object","additionalProperties":true}}}},"400":{"description":"Adoption rejected (validation / decisions).","content":{"application/json":{"schema":{"type":"object","description":"Adoption rejected (validation / decisions).","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or vhost not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or vhost not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/vhosts/{vhostId}/ignore":{"post":{"summary":"Ignore discovered vhost","tags":["Admin API"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true},{"schema":{"type":"string","minLength":1},"in":"path","name":"vhostId","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Vhost ignored.","content":{"application/json":{"schema":{"description":"Vhost ignored.","type":"object","additionalProperties":true}}}},"400":{"description":"Cannot ignore in current status.","content":{"application/json":{"schema":{"type":"object","description":"Cannot ignore in current status.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node or vhost not found.","content":{"application/json":{"schema":{"type":"object","description":"Node or vhost not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/bootstrap-sync":{"get":{"summary":"Get enrollment bootstrap config sync status","tags":["Admin API"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Bootstrap sync status.","content":{"application/json":{"schema":{"description":"Bootstrap sync status.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"post":{"summary":"Retry enrollment bootstrap config sync","tags":["Admin API"],"description":"Resets bootstrap_config_sync to pending and immediately attempts adopt+retire from current discovered_vhosts.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Bootstrap sync result.","content":{"application/json":{"schema":{"description":"Bootstrap sync result.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/drift/import":{"post":{"summary":"Import node-reported config drift","tags":["Admin API"],"description":"Promotes the node-reported active policy sections to a new desired config revision when importable drift exists.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Drift imported as a new desired revision.","content":{"application/json":{"schema":{"description":"Drift imported as a new desired revision.","type":"object","additionalProperties":true}}}},"400":{"description":"No importable drift.","content":{"application/json":{"schema":{"type":"object","description":"No importable drift.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/drift/reject":{"post":{"summary":"Reject node-reported config drift","tags":["Admin API"],"description":"Queues apply_config so the node restores the current desired config on next heartbeat.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Restore command queued.","content":{"application/json":{"schema":{"description":"Restore command queued.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/blocklists/validate-load":{"post":{"summary":"Validate blocklist load size","tags":["Admin API"],"description":"Dry-run validator for staging blocklist scale tests. It does not mutate config or touch nodes.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"requested_count":{"type":"integer","minimum":0},"entries":{"type":"array","items":{}}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Load validation result.","content":{"application/json":{"schema":{"description":"Load validation result.","type":"object","additionalProperties":true}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/propagation":{"get":{"summary":"Get config propagation status","tags":["Admin API"],"description":"Shows desired/applied convergence for the node cluster and why multi-node proof may be blocked.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Propagation status.","content":{"application/json":{"schema":{"description":"Propagation status.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/config/diff":{"get":{"summary":"Diff desired vs applied config","tags":["Admin API"],"description":"Compares the desired config against the last applied one. Requires operator JWT.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Config diff.","content":{"application/json":{"schema":{"description":"Config diff.","type":"object","additionalProperties":true,"properties":{"from_version":{"type":"string","nullable":true},"to_version":{"type":"string","nullable":true},"changed":{"type":"boolean"},"sections":{"type":"object","additionalProperties":{"type":"string","enum":["same","changed"]}},"applied":{"nullable":true},"desired":{"nullable":true}}}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/commands":{"post":{"summary":"Queue a command","tags":["Admin API"],"description":"Queues a command for the node to pick up on its next heartbeat. Requires operator JWT.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"type":{"type":"string","enum":["apply_config","rollback","cert_renew","cluster_sync","validate","firewall_apply_safe","reload_nginx","service_action","packages_update","backup_run","backup_restore","diagnostics_run","fail2ban_action","ipset_action","auditd_action","retire_unmanaged_vhosts","cti_feed_sync"]},"payload":{"type":"object","additionalProperties":{}},"deadline":{"type":"string","format":"date-time"}},"required":["type"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Command queued.","content":{"application/json":{"schema":{"description":"Command queued.","type":"object","properties":{"command":{"type":"object","properties":{"id":{"type":"string","example":"cmd-..."},"type":{"type":"string"},"status":{"type":"string","example":"queued"},"payload":{"type":"object","additionalProperties":true,"nullable":true},"result":{"nullable":true},"deadline":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"},"delivered_at":{"type":"string","format":"date-time","nullable":true},"claimed_at":{"type":"string","format":"date-time","nullable":true},"completed_at":{"type":"string","format":"date-time","nullable":true},"created_by":{"type":"string","nullable":true}},"required":["id","type","status","created_at"]}},"required":["command"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"get":{"summary":"List node commands","tags":["Admin API"],"description":"Returns recent command history for the node. Requires operator JWT.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Command history.","content":{"application/json":{"schema":{"description":"Command history.","type":"object","properties":{"commands":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","example":"cmd-..."},"type":{"type":"string"},"status":{"type":"string","example":"queued"},"payload":{"type":"object","additionalProperties":true,"nullable":true},"result":{"nullable":true},"deadline":{"type":"string","format":"date-time","nullable":true},"created_at":{"type":"string","format":"date-time"},"delivered_at":{"type":"string","format":"date-time","nullable":true},"claimed_at":{"type":"string","format":"date-time","nullable":true},"completed_at":{"type":"string","format":"date-time","nullable":true},"created_by":{"type":"string","nullable":true}},"required":["id","type","status","created_at"]}}},"required":["commands"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/telemetry":{"get":{"summary":"Get recent node telemetry","tags":["Admin API"],"description":"Returns recent telemetry samples for charting. Filter by `names` (comma-separated), `since_hours`, and `limit`. Requires operator JWT.","parameters":[{"schema":{"type":"string"},"in":"query","name":"names","required":false},{"schema":{"type":"number","exclusiveMinimum":true,"minimum":0},"in":"query","name":"since_hours","required":false},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":5000},"in":"query","name":"limit","required":false},{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Telemetry samples.","content":{"application/json":{"schema":{"description":"Telemetry samples.","type":"object","properties":{"samples":{"type":"array","items":{"type":"object","properties":{"ts":{"type":"string","format":"date-time"},"name":{"type":"string"},"value":{"type":"number"},"labels":{"type":"object","additionalProperties":true,"nullable":true}},"required":["ts","name","value"]}}},"required":["samples"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/clusters":{"get":{"summary":"List clusters","tags":["Admin API"],"description":"Lists all clusters. Requires operator JWT.","security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Cluster listing.","content":{"application/json":{"schema":{"description":"Cluster listing.","type":"object","properties":{"clusters":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"created_by":{"type":"string","nullable":true}},"required":["id","name"]}}},"required":["clusters"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"post":{"summary":"Create a cluster","tags":["Admin API"],"description":"Creates a new cluster. Requires operator JWT.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":128},"description":{"type":"string","maxLength":512}},"required":["name"],"additionalProperties":false}}}},"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Cluster created.","content":{"application/json":{"schema":{"description":"Cluster created.","type":"object","properties":{"cluster":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"created_by":{"type":"string","nullable":true}},"required":["id","name"]}},"required":["cluster"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/clusters/{id}":{"patch":{"summary":"Update a cluster","tags":["Admin API"],"description":"Updates a cluster's name and/or description. Pass either or both fields. Requires operator JWT.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":128},"description":{"type":"string","maxLength":512,"nullable":true}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Cluster updated.","content":{"application/json":{"schema":{"description":"Cluster updated.","type":"object","properties":{"cluster":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time","nullable":true},"created_by":{"type":"string","nullable":true}},"required":["id","name"]}},"required":["cluster"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Cluster not found.","content":{"application/json":{"schema":{"type":"object","description":"Cluster not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Cluster name already exists.","content":{"application/json":{"schema":{"type":"object","description":"Cluster name already exists.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/nodes/{id}/summary":{"get":{"summary":"Get node telemetry summary","tags":["Admin API"],"description":"Returns the latest stored summary snapshot (per-URI + named collect aggregates) for the node. Use `type=long|short` (default `long`). Requires operator JWT.","parameters":[{"schema":{"type":"string","enum":["long","short"],"default":"long"},"in":"query","name":"type","required":false},{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Summary snapshot.","content":{"application/json":{"schema":{"description":"Summary snapshot.","type":"object","properties":{"uri":{"type":"object","additionalProperties":{"type":"object","additionalProperties":true,"properties":{"count":{"type":"number"},"size":{"type":"number"},"time":{"type":"number"},"status":{"type":"object","additionalProperties":{"type":"number"}}},"required":["count","size","time"]}},"collect":{"type":"object","additionalProperties":{"type":"object","additionalProperties":true,"properties":{"count":{"type":"number"},"size":{"type":"number"},"time":{"type":"number"},"status":{"type":"object","additionalProperties":{"type":"number"}}},"required":["count","size","time"]}}},"required":["uri","collect"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found.","content":{"application/json":{"schema":{"type":"object","description":"Node not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/api-credentials":{"get":{"summary":"List API credentials","tags":["Admin API"],"description":"Lists Edgewatch API credentials for the current tenant (secrets masked). Available to any authenticated operator.","security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Credential listing.","content":{"application/json":{"schema":{"description":"Credential listing.","type":"object","properties":{"credentials":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"api_scope":{"type":"string"},"client_id":{"type":"string"},"client_secret_last4":{"type":"string"},"tenant_id":{"type":"string"},"created_by":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":"string","format":"date-time","nullable":true}},"required":["id","name","api_scope","client_id","client_secret_last4","created_at"]}}},"required":["credentials"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"post":{"summary":"Create an API credential","tags":["Admin API"],"description":"Stores a pasted Edgewatch client_id + client_secret scoped to one API (or general). Requires admin role. The secret is encrypted at rest.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":128},"api_scope":{"type":"string","enum":["general","breachspot-api","cti-api","explorer-api","pdns-api","crt-api","exploits-api","cvefixes-api","entities-api","pulsewire-api"]},"client_id":{"type":"string","minLength":1,"maxLength":256},"client_secret":{"type":"string","minLength":1,"maxLength":2048}},"required":["name","api_scope","client_id","client_secret"],"additionalProperties":false}}}},"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Credential created (secret not returned).","content":{"application/json":{"schema":{"description":"Credential created (secret not returned).","type":"object","properties":{"credential":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"api_scope":{"type":"string"},"client_id":{"type":"string"},"client_secret_last4":{"type":"string"},"tenant_id":{"type":"string"},"created_by":{"type":"string","nullable":true},"created_at":{"type":"string","format":"date-time"},"last_used_at":{"type":"string","format":"date-time","nullable":true}},"required":["id","name","api_scope","client_id","client_secret_last4","created_at"]}},"required":["credential"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/api-credentials/{id}/reveal":{"post":{"summary":"Reveal an API credential secret","tags":["Admin API"],"description":"Decrypts and returns the client_secret. Requires admin role.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Decrypted client secret.","content":{"application/json":{"schema":{"description":"Decrypted client secret.","type":"object","properties":{"client_secret":{"type":"string"}},"required":["client_secret"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Credential not found.","content":{"application/json":{"schema":{"type":"object","description":"Credential not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/api-credentials/{id}":{"delete":{"summary":"Delete an API credential","tags":["Admin API"],"description":"Hard-deletes a credential. Requires admin role.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"204":{"description":"Credential deleted."},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Credential not found.","content":{"application/json":{"schema":{"type":"object","description":"Credential not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/status":{"get":{"summary":"Pulsewire connection status","tags":["Admin API"],"description":"Reports whether a pulsewire-api (or general) credential is configured and optionally validates the token against Pulsewire.","security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Status.","content":{"application/json":{"schema":{"description":"Status.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/search-terms":{"get":{"summary":"List Pulsewire search terms","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Search terms.","content":{"application/json":{"schema":{"description":"Search terms.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"post":{"summary":"Create a Pulsewire search term","tags":["Admin API"],"description":"Adds an IP, domain, or hostname to the org Pulsewire watchlist.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":253},"kind":{"type":"string","enum":["ip","domain","hostname"]},"label":{"type":"string","maxLength":256}},"required":["value","kind"],"additionalProperties":false}}}},"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Created search term.","content":{"application/json":{"schema":{"description":"Created search term.","type":"object","additionalProperties":true}}}},"400":{"description":"Invalid search term.","content":{"application/json":{"schema":{"type":"object","description":"Invalid search term.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/search-terms/{id}":{"put":{"summary":"Update a Pulsewire search term","tags":["Admin API"],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"value":{"type":"string","minLength":1,"maxLength":253},"kind":{"type":"string","enum":["ip","domain","hostname"]},"label":{"type":"string","maxLength":256}},"required":["value","kind"],"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Updated search term.","content":{"application/json":{"schema":{"description":"Updated search term.","type":"object","additionalProperties":true}}}},"400":{"description":"Invalid search term.","content":{"application/json":{"schema":{"type":"object","description":"Invalid search term.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"delete":{"summary":"Delete a Pulsewire search term","tags":["Admin API"],"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"204":{"description":"Deleted."},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/match-results":{"get":{"summary":"Pulsewire match results","tags":["Admin API"],"description":"Open Pulsewire watchlist matching results (pw-results), including counts_by_search_term.","parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0},"in":"query","name":"page","required":false},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":200},"in":"query","name":"page_size","required":false},{"schema":{"type":"string"},"in":"query","name":"search_term_uuid","required":false}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Match results.","content":{"application/json":{"schema":{"description":"Match results.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/watchlists":{"get":{"summary":"List named Pulsewire watchlists","tags":["Admin API"],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0},"in":"query","name":"offset","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"string"},"in":"query","name":"ordering","required":false}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Watchlists.","content":{"application/json":{"schema":{"description":"Watchlists.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/events":{"get":{"summary":"List Pulsewire events (enhanced)","tags":["Admin API"],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0},"in":"query","name":"offset","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"string"},"in":"query","name":"ordering","required":false},{"schema":{"type":"string"},"in":"query","name":"event_type","required":false},{"schema":{"anyOf":[{"type":"string","enum":["true"]},{"type":"string","enum":["false"]},{"type":"boolean"}]},"in":"query","name":"is_publishable","required":false}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Events.","content":{"application/json":{"schema":{"description":"Events.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/news":{"get":{"summary":"Pulsewire news feed","tags":["Admin API"],"parameters":[{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":200},"in":"query","name":"limit","required":false},{"schema":{"type":"integer","minimum":0},"in":"query","name":"offset","required":false},{"schema":{"type":"string"},"in":"query","name":"search","required":false},{"schema":{"type":"string"},"in":"query","name":"ordering","required":false},{"schema":{"type":"string"},"in":"query","name":"event_type","required":false}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"News.","content":{"application/json":{"schema":{"description":"News.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/pulsewire/news/{id}":{"get":{"summary":"Pulsewire news item detail","tags":["Admin API"],"parameters":[{"schema":{"anyOf":[{"type":"string"},{"type":"number"}]},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"News item.","content":{"application/json":{"schema":{"description":"News item.","type":"object","additionalProperties":true}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Edgewatch credential required.","content":{"application/json":{"schema":{"type":"object","description":"Edgewatch credential required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/users":{"get":{"summary":"List operators","tags":["Admin API"],"description":"Lists operator users in the active tenant. Requires admin role.","security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Operator listing.","content":{"application/json":{"schema":{"description":"Operator listing.","type":"object","properties":{"users":{"type":"array","items":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","operator","viewer"]},"tenant_id":{"type":"string"},"is_superadmin":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}},"required":["id","email","role","tenant_id","is_superadmin","created_at"]}}},"required":["users"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"post":{"summary":"Create an operator","tags":["Admin API"],"description":"Creates an operator user in the active tenant. Requires admin role.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email","maxLength":320},"password":{"type":"string","minLength":8,"maxLength":256},"role":{"type":"string","enum":["admin","operator","viewer"]},"is_superadmin":{"type":"boolean"}},"required":["email","password","role"],"additionalProperties":false}}}},"security":[{"operatorJwt":[]}],"responses":{"201":{"description":"Operator created.","content":{"application/json":{"schema":{"description":"Operator created.","type":"object","properties":{"user":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","operator","viewer"]},"tenant_id":{"type":"string"},"is_superadmin":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}},"required":["id","email","role","tenant_id","is_superadmin","created_at"]}},"required":["user"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"409":{"description":"Email already exists.","content":{"application/json":{"schema":{"type":"object","description":"Email already exists.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/users/{id}":{"patch":{"summary":"Update an operator","tags":["Admin API"],"description":"Updates an operator's role and/or password. Requires admin role.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"role":{"type":"string","enum":["admin","operator","viewer"]},"password":{"type":"string","minLength":8,"maxLength":256},"is_superadmin":{"type":"boolean"}},"additionalProperties":false}}}},"parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Operator updated.","content":{"application/json":{"schema":{"description":"Operator updated.","type":"object","properties":{"user":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","operator","viewer"]},"tenant_id":{"type":"string"},"is_superadmin":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}},"required":["id","email","role","tenant_id","is_superadmin","created_at"]}},"required":["user"]}}}},"400":{"description":"Bad request body.","content":{"application/json":{"schema":{"type":"object","description":"Bad request body.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Operator not found.","content":{"application/json":{"schema":{"type":"object","description":"Operator not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}},"delete":{"summary":"Delete an operator","tags":["Admin API"],"description":"Deletes an operator user. Requires admin role.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Operator deleted.","content":{"application/json":{"schema":{"description":"Operator deleted.","type":"object","properties":{"user":{"type":"object","additionalProperties":true,"properties":{"id":{"type":"string"},"email":{"type":"string"},"role":{"type":"string","enum":["admin","operator","viewer"]},"tenant_id":{"type":"string"},"is_superadmin":{"type":"boolean"},"created_at":{"type":"string","format":"date-time"}},"required":["id","email","role","tenant_id","is_superadmin","created_at"]}},"required":["user"]}}}},"400":{"description":"Cannot remove the last admin.","content":{"application/json":{"schema":{"type":"object","description":"Cannot remove the last admin.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Operator not found.","content":{"application/json":{"schema":{"type":"object","description":"Operator not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/incidents":{"get":{"summary":"List incidents","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}},"post":{"summary":"Create an incident","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}}},"/admin/incidents/{id}":{"get":{"summary":"Get an incident","tags":["Admin API"],"security":[{"operatorJwt":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/admin/incidents/{id}/ack":{"post":{"summary":"Acknowledge an incident","tags":["Admin API"],"security":[{"operatorJwt":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/admin/incidents/{id}/close":{"post":{"summary":"Close an incident","tags":["Admin API"],"security":[{"operatorJwt":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/admin/notifications":{"get":{"summary":"List in-app notifications","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}}},"/admin/notifications/{id}/read":{"post":{"summary":"Mark a notification read","tags":["Admin API"],"security":[{"operatorJwt":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/admin/notifications/read-all":{"post":{"summary":"Mark all notifications read","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}}},"/admin/notification-channels":{"get":{"summary":"List notification channels","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}},"post":{"summary":"Create a notification channel","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}}},"/admin/notification-channels/{id}":{"delete":{"summary":"Delete a notification channel","tags":["Admin API"],"security":[{"operatorJwt":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/admin/license":{"get":{"summary":"Get license info","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}},"put":{"summary":"Set license key","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}}},"/admin/nodes/{id}/inventory":{"get":{"summary":"Get node inventory","tags":["Admin API"],"description":"Returns the node-ops inventory (services/packages/etc.). Requires operator JWT.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Node inventory snapshot.","content":{"application/json":{"schema":{"description":"Node inventory snapshot.","type":"object","properties":{"sections":{"type":"object","additionalProperties":true},"updated_at":{"type":"string","format":"date-time","nullable":true}},"required":["sections"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/certificates":{"get":{"summary":"List certificates","tags":["Admin API"],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}},"post":{"summary":"Import a certificate","tags":["Admin API"],"description":"Imports a cert + private key (key encrypted at rest). Requires admin role.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":1,"maxLength":200},"cert_pem":{"type":"string","minLength":1},"key_pem":{"type":"string","minLength":1}},"required":["name","cert_pem","key_pem"],"additionalProperties":false}}}},"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Default Response"}}}},"/admin/certificates/{id}":{"delete":{"summary":"Delete a certificate","tags":["Admin API"],"security":[{"operatorJwt":[]}],"parameters":[{"schema":{"type":"string"},"in":"path","name":"id","required":true}],"responses":{"200":{"description":"Default Response"}}}},"/admin/nodes/{id}/logs":{"get":{"summary":"Get recent node logs","tags":["Admin API"],"description":"Returns the recent bounded nginx access/error tail, optionally filtered by managed site (vhost). Pass a comma-separated `vhost` list to include related tags (e.g. `web,web-adopted`). Requires operator JWT.","parameters":[{"schema":{"type":"string","minLength":1},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Recent log lines (newest first).","content":{"application/json":{"schema":{"description":"Recent log lines (newest first).","type":"object","properties":{"lines":{"type":"array","items":{"type":"object","properties":{"line":{"type":"string"},"ts":{"type":"string"},"vhost":{"type":"string","nullable":true}},"required":["line","ts"]}}},"required":["lines"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Node not found in tenant.","content":{"application/json":{"schema":{"type":"object","description":"Node not found in tenant.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/modsecurity/events":{"get":{"summary":"List ModSecurity events","tags":["Admin API"],"description":"Cursor-paginated normalized ModSecurity events for exactly one scope (cluster_id | node_id | node_id+site_name).","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"query","name":"cluster_id","required":false},{"schema":{"type":"string","minLength":1},"in":"query","name":"node_id","required":false},{"schema":{"type":"string","minLength":1},"in":"query","name":"site_name","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":false},{"schema":{"type":"string","enum":["blocked","detected","passed","unknown"]},"in":"query","name":"action","required":false},{"schema":{"type":"string"},"in":"query","name":"rule_id","required":false},{"schema":{"type":"string"},"in":"query","name":"client_ip","required":false},{"schema":{"type":"integer"},"in":"query","name":"severity_min","required":false},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":200},"in":"query","name":"limit","required":false}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Event page.","content":{"application/json":{"schema":{"description":"Event page.","type":"object"}}}},"400":{"description":"Invalid scope or query.","content":{"application/json":{"schema":{"type":"object","description":"Invalid scope or query.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/modsecurity/stats":{"get":{"summary":"ModSecurity statistics","tags":["Admin API"],"description":"Detection-vs-blocked timeline, totals, and rankings for one scope.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"query","name":"cluster_id","required":false},{"schema":{"type":"string","minLength":1},"in":"query","name":"node_id","required":false},{"schema":{"type":"string","minLength":1},"in":"query","name":"site_name","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"from","required":false},{"schema":{"type":"string","format":"date-time"},"in":"query","name":"to","required":false},{"schema":{"type":"string","enum":["blocked","detected","passed","unknown"]},"in":"query","name":"action","required":false},{"schema":{"type":"string"},"in":"query","name":"rule_id","required":false},{"schema":{"type":"string"},"in":"query","name":"client_ip","required":false},{"schema":{"type":"integer"},"in":"query","name":"severity_min","required":false},{"schema":{"type":"string"},"in":"query","name":"cursor","required":false},{"schema":{"type":"integer","exclusiveMinimum":true,"minimum":0,"maximum":200},"in":"query","name":"limit","required":false}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Stats payload.","content":{"application/json":{"schema":{"description":"Stats payload.","type":"object"}}}},"400":{"description":"Invalid scope or query.","content":{"application/json":{"schema":{"type":"object","description":"Invalid scope or query.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/modsecurity/events/{id}":{"get":{"summary":"Get ModSecurity event detail","tags":["Admin API"],"parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Event detail.","content":{"application/json":{"schema":{"description":"Event detail.","type":"object"}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Event not found.","content":{"application/json":{"schema":{"type":"object","description":"Event not found.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/modsecurity/events/{id}/raw":{"get":{"summary":"Reveal encrypted ModSecurity raw transaction (admin only)","tags":["Admin API"],"description":"Decrypts and returns the full audit transaction as escaped text. Every reveal is written to the audit log. Admin role required.","parameters":[{"schema":{"type":"string","format":"uuid"},"in":"path","name":"id","required":true}],"security":[{"operatorJwt":[]}],"responses":{"200":{"description":"Raw transaction text.","content":{"application/json":{"schema":{"description":"Raw transaction text.","type":"object"}}}},"401":{"description":"Missing or invalid operator JWT.","content":{"application/json":{"schema":{"type":"object","description":"Missing or invalid operator JWT.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"403":{"description":"Admin role required.","content":{"application/json":{"schema":{"type":"object","description":"Admin role required.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}},"404":{"description":"Raw not available.","content":{"application/json":{"schema":{"type":"object","description":"Raw not available.","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"not_found"},"message":{"type":"string","example":"node not found"},"requestId":{"type":"string"},"details":{"description":"Optional structured detail (e.g. Zod issues)."}},"required":["code","message","requestId"]}},"required":["error"]}}}}}}},"/admin/cti-feeds/catalog":{"get":{"summary":"List Edgewatch CTI feed types (metadata only)","tags":["CTI"],"responses":{"200":{"description":"Default Response","content":{"application/json":{"schema":{"type":"object","properties":{"feeds":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"type":{"type":"string"},"tags":{"type":"array","items":{"type":"string"}},"attacker_count":{"type":"integer"}}}},"cached":{"type":"boolean"},"fetched_at":{"type":"string"}}}}}}}}}},"tags":[{"name":"Health","description":"Unauthenticated liveness probe."},{"name":"Node API (mTLS)","description":"Node-facing protocol under `/v1` (HTTPS + mTLS). All routes except enroll require a node client certificate."},{"name":"Admin API","description":"Operator/frontend-facing API under `/admin` (HTTPS + JWT bearer)."}]}