{
  "openapi": "3.0.4",
  "info": {
    "title": "NetavoBNG Management API",
    "description": "The management REST API for a NetavoBNG appliance — the same API the bundled web UI is built on.\nUse it to observe and operate the appliance: subscriber sessions, routing, CG-NAT, L2TP tunnels,\nlogs, metrics, configuration, and licensing.\n\n## Base URL and TLS\n\nThe API is served over HTTPS by the appliance's web listeners (`webUi.listeners`, default\n`https://127.0.0.1:8443`). A factory appliance presents a self-signed certificate until an\noperator uploads one (`POST /api/v1/config/webui/certificate`) or enables ACME. All paths below\nare relative to the listener root, e.g. `https://bng1.example.net:8443/api/v1/sessions`.\n\n## Authentication\n\n**Use a named bearer token.** Every endpoint except `POST /api/v1/auth/login`,\n`POST /api/v1/auth/enrol` and `GET /api/v1/system/health` requires one. A super-user creates a\ntoken with `POST /api/v1/auth/tokens`; the plaintext (`bngtok_…`) is returned exactly once at\ncreation and only a SHA-256 hash is stored on the appliance, so a lost token is replaced rather\nthan recovered. Send it on every request:\n\n```\nAuthorization: Bearer bngtok_…\n```\n\nEach token carries a fixed role (`read-only`, `operator` or `super-user`) and is exempt from CSRF\nchecks. This is the single credential you need for scripts, integrations and monitoring.\n\nAccounts and tokens share one identity model with the appliance CLI (`login.users`). Repeated\nauthentication failures lock the account/source-IP pair out temporarily (HTTP 423 on login).\n\n### Session cookies (bundled web UI only)\n\nThe appliance also accepts an interactive session cookie, which is how the bundled web UI\nauthenticates. It is documented for completeness — **API clients should not use it.** It is bound\nto a login session and obliges the caller to manage a CSRF token, neither of which suits\nautomation.\n\n`POST /api/v1/auth/login` with username + password (and a TOTP code when enrolled) sets the\n`bng_session` cookie and returns a `csrfToken` (also available from `GET /api/v1/auth/whoami`).\nCookie-authenticated **mutating** requests (anything other than GET/HEAD/OPTIONS) must also send\nthat value in the `X-CSRF-Token` header or they are rejected with 403.\n\n## Roles and authorization\n\nEach user/token carries one role; every request is checked against a role floor:\n\n| Request | Minimum role |\n|---|---|\n| Any GET/HEAD/OPTIONS | `read-only` |\n| Most mutations (e.g. disconnect a session) | `operator` |\n| Mutations under `/auth`, `/config`, `/vrfs`, `/routing/config`, `/cgn` | `super-user` |\n\nA request below the floor is answered with 403. Per-endpoint docs note the effective floor where\nit isn't obvious from the table.\n\n## Errors\n\nErrors use RFC 9457 problem details (`application/problem+json`) with `title`, `status` and a\nhuman-readable `detail`, e.g.:\n\n```json\n{ \"type\": \"about:blank\", \"title\": \"Session not found\", \"status\": 404, \"detail\": \"No active session 42.\" }\n```\n\n## Conventions\n\n* **Pagination** — list endpoints take `offset`/`limit` query parameters and return `total`,\n  `offset` and the page of items, so clients can page deterministically.\n* **Timestamps** are UTC, ISO 8601. **Rates** are bits/second (`…Bps`) or packets/second (`…Pps`).\n* **Live streaming** — `GET /api/v1/logs/stream` is a server-sent-events (`text/event-stream`)\n  endpoint; everything else is JSON.\n* **Fleet proxying** — on multi-appliance deployments, GET endpoints can be read on a peer via\n  `GET /api/v1/fleet/{appliance}/{path}`. Mutations are always local to the appliance.\n* **Configuration commits** — config-changing endpoints run the CLI's candidate → validate →\n  commit pipeline. They return `422` when validation rejects the change, and `restartRequired`\n  hints when a commit only takes full effect after a restart.\n\n## Interactive documentation\n\nThis document is served at `/api/v1/openapi/v1.json`; an interactive reference UI (browse,\ngenerate client snippets, try requests) is at `/api/v1/docs`. Both require authentication, like\nthe rest of the API.",
    "contact": {
      "name": "Netavo",
      "url": "https://www.netavo.com/"
    },
    "version": "v1"
  },
  "paths": {
    "/api/v1/address-pools": {
      "get": {
        "tags": [
          "Address pools"
        ],
        "summary": "List address pools with live utilisation on this appliance.",
        "description": "One row per configured pool, with a row per subnet. `used` counts committed and offered\n    blocks (a DHCP OFFER or a PPP negotiation holds its block until it completes or its hold\n    lapses); `quarantined` blocks were declined by a DHCP client and return after an hour.\n    `cgnTranslated` is true for an IPv4 pool in a VRF bound to on-box CGN — its addresses are\n    private, never announced, and may be reused on every appliance. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AddressPool"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/address-pools/{name}/allocations": {
      "get": {
        "tags": [
          "Address pools"
        ],
        "summary": "List the sessions holding blocks from one address pool.",
        "description": "Ordered by prefix. `static` marks a statically-assigned address that falls inside the pool\n    (held so the allocator never hands it out, but not disconnected if its subnet is removed).\n    Fleet-proxyable.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "Pool name.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Rows to skip.",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size (1-1000, default 100).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AddressPoolAllocationsPage"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/alarms": {
      "get": {
        "tags": [
          "Alarms"
        ],
        "summary": "Current active alarms (most-severe first), with a per-severity summary.",
        "responses": {
          "200": {
            "description": "The active-alarm snapshot.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlarmsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/alarms/test": {
      "post": {
        "tags": [
          "Alarms"
        ],
        "summary": "Inject a synthetic alert through the real fan-out to verify sink delivery end-to-end\n    (a \"test alerting\" action). Returns the exact AlertNotification payload that push\n    sinks receive — the same contract the webhook posts and the message-bus sink will publish.",
        "description": "Admin-only (mutating). Set `severity` at/above a sink's floor to exercise it —\n    e.g. `major` reaches a default webhook sink, `critical` reaches SMTP.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/AlertTestRequest"
                  }
                ]
              }
            },
            "text/json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/AlertTestRequest"
                  }
                ]
              }
            },
            "application/*+json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/AlertTestRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "The alert was dispatched; the delivered payload is returned.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlertNotification"
                }
              }
            }
          },
          "409": {
            "description": "Alerting is disabled, so there is no sink fan-out to deliver to.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/alarms/clear": {
      "post": {
        "tags": [
          "Alarms"
        ],
        "summary": "Clear (dismiss) an active alarm by id + optional instance. Admin-only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AlarmClearRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/AlarmClearRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/AlarmClearRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AlarmClearResult"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/providers/oidc": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "List OIDC providers (client secrets never returned).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OidcProviderAdmin"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/providers/oidc/{name}": {
      "put": {
        "tags": [
          "Authentication"
        ],
        "summary": "Create or replace an OIDC provider (matched by name). Omit/blank clientSecret to keep\n    the stored one.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebUiOidcProviderConfig"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/WebUiOidcProviderConfig"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/WebUiOidcProviderConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Authentication"
        ],
        "summary": "Delete an OIDC provider.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/providers/tacacs": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Current TACACS+ configuration (shared secret never returned).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TacacsAdmin"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Authentication"
        ],
        "summary": "Replace the TACACS+ configuration. Omit/blank secret to keep the stored one.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebUiTacacsConfig"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/WebUiTacacsConfig"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/WebUiTacacsConfig"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/providers/passkey": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Current passkey policy.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PasskeyPolicy"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Authentication"
        ],
        "summary": "Set the passkey policy (off | secondFactor | passwordless).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyPolicy"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyPolicy"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyPolicy"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/captures": {
      "get": {
        "tags": [
          "Captures"
        ],
        "summary": "List active packet captures.",
        "description": "One row per session currently being mirrored. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Capture"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions/{id}/capture": {
      "post": {
        "tags": [
          "Captures"
        ],
        "summary": "Start a packet capture on a session.",
        "description": "Mirrors the session's traffic to `destinationIp` over GRE. Limits stop the\n    capture automatically; a session can have at most one active capture. The action is\n    audited with the acting identity. Operator role or above; local appliance only.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The BNG session id to capture.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "uint32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CaptureStartRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CaptureStartRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CaptureStartRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Capture"
                }
              }
            }
          },
          "400": {
            "description": "Invalid destination IP or a negative limit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "404": {
            "description": "No active session has this id, or the destination is not on the mirror allowlist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Captures"
        ],
        "summary": "Stop the packet capture on a session.",
        "description": "Audited with the acting identity. Operator role or above; local appliance only.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The BNG session id whose capture to stop.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "uint32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaptureStopResult"
                }
              }
            }
          },
          "404": {
            "description": "No active capture on this session.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cgn/status": {
      "get": {
        "tags": [
          "CGN"
        ],
        "summary": "CG-NAT status: configuration, live binding counts, and counters.",
        "description": "Merges the configured CGN section (enabled flag, limits, IP groups) with live\n    state: binding counts, per-group pool utilisation, native data-plane counters, and\n    compliance-logger loss counters. `running: false` means CGN is configured but not active\n    in this control-plane process (it activates at startup) — the config half still renders.\n    Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnStatus"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cgn/groups": {
      "get": {
        "tags": [
          "CGN"
        ],
        "summary": "List CGN IP groups (public address pools) with live utilisation.",
        "description": "One row per configured group; `utilisation` and `activeBindings` are live values\n    (null/0 when CGN isn't running or the group was added since startup). Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CgnGroup"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "CGN"
        ],
        "summary": "Create a CGN IP group.",
        "description": "Commits through the configuration store. The CGN section is restart-required:\n    the response carries `restartRequired: true` and the new group only becomes active after\n    a control-plane restart. Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CgnGroupWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CgnGroupWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CgnGroupWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnCommitResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing/duplicate name or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cgn/groups/{name}": {
      "put": {
        "tags": [
          "CGN"
        ],
        "summary": "Update a CGN IP group.",
        "description": "Full-replace semantics (the name in the URL is the immutable key). While the\n    group carries live bindings, structural fields (chunk size, public subnets, outside VRF)\n    cannot change — drain the pool first; cosmetic fields (description, timeouts, caps) can.\n    Restart-required like create. Super-user only; local appliance only.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The IP group to update.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CgnGroupWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CgnGroupWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CgnGroupWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnCommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The group does not exist or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Structural change on a group with active bindings, or configuration locked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "CGN"
        ],
        "summary": "Delete a CGN IP group.",
        "description": "Refused while the group carries live bindings (a restart with subscribers still\n    mapped through the pool would strand their NAT state). Restart-required like create.\n    Super-user only; local appliance only.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The IP group to delete.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnCommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The group does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "The group has active bindings, or the configuration is locked.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cgn/settings": {
      "get": {
        "tags": [
          "CGN"
        ],
        "summary": "Read the section-level CGN settings.",
        "description": "The configured (not necessarily running) values. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnSettings"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "CGN"
        ],
        "summary": "Update the section-level CGN settings.",
        "description": "Commits through the configuration store; restart-required (the tables are sized\n    at startup). Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CgnSettings"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CgnSettings"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CgnSettings"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnCommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cgn/bindings": {
      "get": {
        "tags": [
          "CGN"
        ],
        "summary": "List live NAT bindings (filtered, paged).",
        "description": "Filters combine with AND: `group` and `vrf` are exact (case-insensitive; \"default\"\n    matches the default table), `ip` is a prefix match against both the inside and the public\n    address. `running: false` means CGN isn't active — the list is then empty.\n    Fleet-proxyable.",
        "parameters": [
          {
            "name": "group",
            "in": "query",
            "description": "Filter by CGN IP group name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "vrf",
            "in": "query",
            "description": "Filter by inside VRF name.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ip",
            "in": "query",
            "description": "Prefix match on the inside or public IPv4 address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Rows to skip (default 0).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, clamped to 1–1000 (default 100).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 100
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnBindingsPage"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/cgn/bindings/{bindingIndex}/identify": {
      "post": {
        "tags": [
          "CGN"
        ],
        "summary": "Attach a subscriber identity to an auto-learned binding.",
        "description": "Auto-learned bindings start anonymous; this enriches one with the subscriber\n    identity used in compliance/abuse-resolution logging (an Identify event is emitted per\n    port block, and the acting principal is audited). Only auto-learned bindings can be\n    identified. Super-user only; local appliance only.",
        "parameters": [
          {
            "name": "bindingIndex",
            "in": "path",
            "description": "The binding's index (from the bindings list).",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "uint32"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/CgnIdentifyBinding"
                  }
                ]
              }
            },
            "text/json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/CgnIdentifyBinding"
                  }
                ]
              }
            },
            "application/*+json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/CgnIdentifyBinding"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CgnBindingRow"
                }
              }
            }
          },
          "400": {
            "description": "Missing/oversized subscriber identity.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "404": {
            "description": "No binding with this index exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "CGN is not running, or the binding cannot be identified (not auto-learned).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/diagnostics/ping": {
      "post": {
        "tags": [
          "System"
        ],
        "summary": "Ping from the data plane, optionally inside a VRF.",
        "description": "Originated by the forwarding plane, not the host: a service VRF carries no kernel\n    addresses, so the host cannot source a packet in one at all. The reply traverses the full\n    ingress path. A payload larger than the path MTU with `dontFragment` set is sent\n    deliberately — \"would fragment\" is a result, not an input error. Operator role or above;\n    local appliance only (the fleet proxy is GET-only).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PingRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PingRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PingRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PingResult"
                }
              }
            }
          },
          "400": {
            "description": "Unparseable address, mismatched address families, or an unknown VRF.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "404": {
            "description": "No source address is available in that VRF.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/diagnostics/inject": {
      "post": {
        "tags": [
          "System"
        ],
        "summary": "Inject a synthetic packet at ingress and report what the pipeline does with it.",
        "description": "Enters the pipeline where received traffic does, so unlike ping it reaches punt\n     classification, MPLS pop, source-VRF classification, uRPF, CGN, session lookup and QoS.\n\n     By default this is a DRY RUN: the packet traverses everything and is dropped at the egress\n     staging point, so a forwarding question can be asked of a live customer VRF without putting\n     a frame on anyone's wire. Set `emit` to actually transmit — that sends an operator-chosen\n     frame out a real port, and is audited as such. Operator role or above.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/InjectRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/InjectRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/InjectRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InjectResult"
                }
              }
            }
          },
          "400": {
            "description": "Unparseable address or mismatched address families.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "504": {
            "description": "The data plane did not return a trace — it may not be running.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/l2tp/tunnels": {
      "get": {
        "tags": [
          "L2TP"
        ],
        "summary": "List live L2TP tunnels (both directions).",
        "description": "\"downstream\" tunnels arrive from LACs and terminate on this appliance (LNS role);\n    \"upstream\" tunnels carry wholesale PPPoE subscribers to a remote LNS (LAC role). Rows are\n    ordered downstream-first, then by local tunnel id. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/L2tpTunnelList"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/l2tp/tunnels/{id}": {
      "get": {
        "tags": [
          "L2TP"
        ],
        "summary": "Full detail for one L2TP tunnel.",
        "description": "Everything known about the tunnel — peer identity and vendor, endpoints, VRF,\n    state, timestamps — plus the subscriber sessions carried in it. Fleet-proxyable.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The tunnel's local tunnel id (from the tunnel list).",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "type": "integer",
              "format": "int32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/L2tpTunnelDetail"
                }
              }
            }
          },
          "404": {
            "description": "No L2TP tunnel with this local id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/licensing": {
      "get": {
        "tags": [
          "Licensing"
        ],
        "summary": "Licence status.",
        "description": "The appliance's current licence state, band, metered counts and expiry/renewal\n    timestamps. Any authenticated role.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicensingStatus"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/licensing/telemetry": {
      "get": {
        "tags": [
          "Licensing"
        ],
        "summary": "Preview the licensing telemetry payload.",
        "description": "Returns the exact JSON payload the appliance sends to the licensing service at\n    its next renewal (null fields omitted), so operators can audit what leaves the box. The\n    shape follows the licensing telemetry contract (design doc 27), not a schema in this\n    document; an empty object is returned when telemetry collection isn't running. Any\n    authenticated role.",
        "responses": {
          "200": {
            "description": "The telemetry payload as it would be sent (shape per design doc 27).",
            "content": {
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/licensing/activate": {
      "post": {
        "tags": [
          "Licensing"
        ],
        "summary": "Activate the appliance against the licensing service.",
        "description": "Enrols the appliance using an activation token issued with the licence. Works in\n    every licence state — including lapsed (state 3), where it is the recovery path.\n    Super-user only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivateRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ActivateRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ActivateRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseActivateResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing token or the licensing service rejected the activation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "503": {
            "description": "The licensing worker is not available in this process.",
            "content": {
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/licensing/renew-now": {
      "post": {
        "tags": [
          "Licensing"
        ],
        "summary": "Renew the licence lease now.",
        "description": "Contacts the licensing service immediately instead of waiting for the scheduled\n    renewal, and returns the resulting state. Super-user only.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseRenewResult"
                }
              }
            }
          },
          "502": {
            "description": "The licensing service could not be reached or refused the renewal.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "503": {
            "description": "The licensing worker is not available in this process.",
            "content": {
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/licensing/load-offline": {
      "post": {
        "tags": [
          "Licensing"
        ],
        "summary": "Install an offline licence lease.",
        "description": "The request body is the signed offline lease JSON issued by Netavo (posted\n    verbatim). It is persisted to the configured offline-licence path and applied immediately —\n    the air-gapped alternative to online activation/renewal. Super-user only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonElement"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/JsonElement"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/JsonElement"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LicenseOfflineResult"
                }
              }
            }
          },
          "400": {
            "description": "The lease was written but could not be applied (bad signature/expired/wrong box).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "500": {
            "description": "The lease file could not be written.",
            "content": {
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscribers/local": {
      "get": {
        "tags": [
          "LocalSubscribers"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscribers/local/ppp": {
      "post": {
        "tags": [
          "LocalSubscribers"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PppSubscriberWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PppSubscriberWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PppSubscriberWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscribers/local/ipoe": {
      "post": {
        "tags": [
          "LocalSubscribers"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IpoeSubscriberWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/IpoeSubscriberWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/IpoeSubscriberWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscribers/local/pppoe": {
      "post": {
        "tags": [
          "LocalSubscribers"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PppoeSubscriberWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PppoeSubscriberWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PppoeSubscriberWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscribers/local/ppp/{key}": {
      "put": {
        "tags": [
          "LocalSubscribers"
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PppSubscriberWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PppSubscriberWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PppSubscriberWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "LocalSubscribers"
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscribers/local/ipoe/{key}": {
      "put": {
        "tags": [
          "LocalSubscribers"
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IpoeSubscriberWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/IpoeSubscriberWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/IpoeSubscriberWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "LocalSubscribers"
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/subscribers/local/pppoe/{key}": {
      "put": {
        "tags": [
          "LocalSubscribers"
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PppoeSubscriberWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PppoeSubscriberWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PppoeSubscriberWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "LocalSubscribers"
        ],
        "parameters": [
          {
            "name": "key",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/metrics/history": {
      "get": {
        "tags": [
          "Metrics"
        ],
        "summary": "Dashboard time series: session counts and per-port throughput.",
        "description": "Returns parallel arrays keyed by the shared `times` axis: total sessions,\n    sessions per access encapsulation, session up/down rates, and per-port RX/TX bit rates.\n    Samples are kept in memory only (up to 24 h, 10 s resolution; longer ranges are\n    downsampled), so history restarts with the control plane. Fleet-proxyable.",
        "parameters": [
          {
            "name": "range",
            "in": "query",
            "description": "Window to return: \"1h\", \"6h\" or \"24h\" (default \"1h\"; anything else\n    falls back to 1h).",
            "schema": {
              "type": "string",
              "default": "1h"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MetricsHistory"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/oidc/providers": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "List enabled OIDC providers for the login page.",
        "description": "Anonymous. Returns each enabled provider's `name` (used in the login URL) and a\n    `displayName` button label. Empty when no providers are configured.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OidcProvider"
                  }
                }
              },
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OidcProvider"
                  }
                }
              },
              "text/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/OidcProvider"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/oidc/{provider}/login": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Begin OIDC login — redirect the browser to the identity provider.",
        "description": "Anonymous. Issues a 302 to the IdP authorization endpoint with PKCE + state + nonce.\n    returnUrl (optional, same-origin relative path only) is where the user lands\n    after a successful callback.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "description": "Provider name from `GET providers`.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "returnUrl",
            "in": "query",
            "description": "Same-origin relative path to return to (e.g. `/sessions`).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Found"
          },
          "404": {
            "description": "No such enabled provider, or discovery failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/oidc/{provider}/callback": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "OIDC callback — validate the response and establish the session.",
        "description": "Anonymous. The IdP redirects here with `code` + `state`. On success sets the\n    `bng_session` cookie and 302s to the return URL (or app root). On failure 302s to\n    `/login?error=…` so the SPA can show the reason.",
        "parameters": [
          {
            "name": "provider",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "code",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "state",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "error",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Found"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/passkey/register/begin": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Begin registering a passkey for the current account.",
        "description": "Interactive sessions only (not bearer tokens). Returns a ceremony `id` and the\n    WebAuthn `CredentialCreateOptions` to pass to `navigator.credentials.create()`;\n    finish with `POST register/finish`. Unavailable (400) when `webUi.hostname` is\n    unset or the passkey policy is `off`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PasskeyBeginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/passkey/register/finish": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Finish registering a passkey (verify attestation, store the credential).",
        "description": "Interactive sessions only. Send the ceremony `id` from `register/begin`,\n    the authenticator's attestation response, and an optional label.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyRegisterFinishRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyRegisterFinishRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyRegisterFinishRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/passkey/login/begin": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Begin a passkey login (assertion challenge).",
        "description": "Anonymous. Optionally supply a username to scope the allow-list; omit it for\n    username-less (resident-key) login. Returns a ceremony `id` and the WebAuthn\n    `AssertionOptions` for `navigator.credentials.get()`. Only available when the\n    passkey policy is `passwordless`.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/PasskeyLoginBeginRequest"
                  }
                ]
              }
            },
            "text/json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/PasskeyLoginBeginRequest"
                  }
                ]
              }
            },
            "application/*+json": {
              "schema": {
                "oneOf": [
                  {
                    "nullable": true
                  },
                  {
                    "$ref": "#/components/schemas/PasskeyLoginBeginRequest"
                  }
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PasskeyBeginResponse"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/passkey/login/finish": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Finish a passkey login: verify the assertion and start a session.",
        "description": "Anonymous. On success sets the `bng_session` cookie and returns the session's\n    `csrfToken` — exactly like a password login (identity source: passkey).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyLoginFinishRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyLoginFinishRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/PasskeyLoginFinishRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Unauthorized",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/passkey/credentials": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "List the current account's registered passkeys.",
        "description": "Interactive sessions only. Returns each credential's id, label, AAGUID, transports\n    and creation time (no secret material).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/PasskeyCredential"
                  }
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/passkey/credentials/{id}": {
      "delete": {
        "tags": [
          "Authentication"
        ],
        "summary": "Remove one of the current account's passkeys.",
        "description": "Interactive sessions only.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The base64url credential id (from the credentials list).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "400": {
            "description": "Bad Request",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "404": {
            "description": "Not Found",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/radius/servers": {
      "get": {
        "tags": [
          "RADIUS"
        ],
        "summary": "RADIUS server pool status.",
        "description": "Live/dead counts for the pool and per-server health: dead-marking state,\n    consecutive failures, request counters, and last success/failure timestamps.\n    `configured: false` means no RADIUS auth driver is running on this appliance (the server\n    list is then empty). Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RadiusServers"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions/summary": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "Session counts for dashboard tiles.",
        "description": "Returns the total number of active subscriber sessions plus breakdowns by session\n    type, access encapsulation, subscriber-facing port, and VRF, and the number of sessions\n    currently in the walled garden. Cheap to poll; fleet-proxyable.",
        "responses": {
          "200": {
            "description": "Aggregated counts over the currently active sessions.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionsSummary"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "List active sessions (filtered, newest first, paged).",
        "description": "All filters combine with AND. String filters are case-insensitive; `username` and\n    `ip` are prefix/substring matches, the rest are exact. Fleet-proxyable.",
        "parameters": [
          {
            "name": "username",
            "in": "query",
            "description": "Substring match on the subscriber username.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "vrf",
            "in": "query",
            "description": "Exact VRF name (\"default\" for the global table).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "Session type, e.g. \"ipoe\" or \"ppp\".",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "encap",
            "in": "query",
            "description": "Access encapsulation: ipoe | pppoe-local | pppoe-lac | l2tp.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ip",
            "in": "query",
            "description": "Prefix match on the assigned IPv4 or IPv6 WAN address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "walledGarden",
            "in": "query",
            "description": "true returns only walled-garden sessions.",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "port",
            "in": "query",
            "description": "Subscriber-facing data-plane port id (excludes LNS-terminated sessions).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, clamped to 1–500 (default 100).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 100
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Items to skip from the start of the (filtered, sorted) result.",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The filtered total plus one page of session summaries.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionListPage"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions/{id}": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "Full detail for one active session.",
        "description": "Includes addressing, VLANs, service plan, live data-plane traffic counters and —\n    for L2TP-carried sessions — the tunnel endpoints. Fields that don't apply to the session's\n    encapsulation are null. Fleet-proxyable.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The BNG session id (from the list endpoint).",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "uint32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionDetail"
                }
              }
            }
          },
          "404": {
            "description": "No active session has this id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Sessions"
        ],
        "summary": "Force-disconnect a session.",
        "description": "Tears the session down exactly like the CLI `request subscribers disconnect`\n    command: the subscriber is disconnected, accounting Stop is emitted, and the action is\n    written to the audit log with the acting identity. Requires the operator role (or above);\n    local appliance only — not available through the fleet proxy.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The BNG session id to disconnect.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "uint32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionDisconnectResult"
                }
              }
            }
          },
          "404": {
            "description": "No active session has this id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/sessions/{id}/live": {
      "get": {
        "tags": [
          "Sessions"
        ],
        "summary": "High-resolution live traffic rates for one session.",
        "description": "2-second samples for the session-detail live chart (requirements: watched\n    sessions). Polling this endpoint IS the watch keepalive — the appliance samples a session\n    only while something polls it, so the first call may return an empty series. Returns\n    parallel arrays keyed by the shared `times` axis. Fleet-proxyable (watching a remote\n    appliance's session registers the watch on that appliance).",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "The BNG session id.",
            "required": true,
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "uint32"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SessionLiveRates"
                }
              }
            }
          },
          "404": {
            "description": "No active session has this id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/upgrade": {
      "get": {
        "tags": [
          "SystemUpgrade"
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/upgrade/stage": {
      "post": {
        "tags": [
          "SystemUpgrade"
        ],
        "summary": "Stage package bytes only. Apply is a separate reviewed action.",
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/upgrade/apply": {
      "post": {
        "tags": [
          "SystemUpgrade"
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApplyRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ApplyRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ApplyRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/webui/certificate": {
      "post": {
        "tags": [
          "Configuration"
        ],
        "summary": "Upload a TLS certificate for the web listeners.",
        "description": "Validates that the PEM pair loads as a usable server certificate before anything\n    is written, persists it atomically, switches `webUi.tls.mode` to \"custom\" via a config\n    commit, and hot-reloads the listeners — no restart, existing sessions undisturbed.\n    Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CertificateUploadRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CertificateUploadRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CertificateUploadRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CertificateInstallResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing/unloadable PEM pair, or the certificate is expired.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "500": {
            "description": "The certificate was staged but the config commit failed.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/health": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Liveness probe.",
        "description": "Unauthenticated (the only anonymous GET on the API) — for load balancers and\n    monitoring. Answers whenever the control plane can serve requests; use\n    `GET /api/v1/system/status` for real health detail (data plane, memory, disk).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Health"
                }
              }
            }
          }
        },
        "security": [
          { }
        ]
      }
    },
    "/api/v1/system/info": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "What product this API belongs to, and what it can serve.",
        "description": "The counterpart of the NetavoVPE management plane's identical endpoint, so a client\n    — the shared web UI in particular — discovers which product it is talking to POSITIVELY\n    rather than inferring it from which endpoints 404.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProductInfo"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/status": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Appliance status: host health + control-plane/data-plane state.",
        "description": "Hostname, uptime, OS/kernel, load, memory, root filesystem, control-plane\n    version/uptime, and live data-plane state (running, heartbeat age, PAL backend). The\n    data-plane block reflects shared-memory liveness when available; this endpoint never\n    errors over a dead data plane — reporting that is its purpose. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SystemStatus"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/hardware": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Hardware inventory.",
        "description": "CPU, memory, storage, board and NIC inventory. Static — gathered once and cached\n    until `POST /api/v1/system/hardware/refresh`. Fields a platform cannot provide are null.\n    Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HardwareInventory"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/hardware/refresh": {
      "post": {
        "tags": [
          "System"
        ],
        "summary": "Re-gather the hardware inventory.",
        "description": "Invalidates the cached inventory so the next read reflects hardware changes.\n    Operator role or above.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HardwareRefreshResult"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/restart": {
      "post": {
        "tags": [
          "System"
        ],
        "summary": "Restart the control plane and data plane.",
        "description": "For config changes marked \"restart required\" that can't be hot-applied. Drops all\n    active sessions immediately; established subscriber sessions re-establish on their own once\n    the appliance is back up. Not fleet-proxyable — always acts on the box actually serving the\n    request, never a remote appliance selected in the fleet view. Admin role required.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RestartResult"
                }
              }
            }
          },
          "409": {
            "description": "A package upgrade is in progress; wait for its terminal status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/system/interfaces": {
      "get": {
        "tags": [
          "System"
        ],
        "summary": "Network interface table.",
        "description": "All host interfaces with state, speed, addresses and counters, including\n    data-plane ports (whose link state comes from the data plane when the kernel cannot see\n    them). Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Interface"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/login": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Log in with username/password (+ TOTP when enrolled).",
        "description": "Anonymous. On success sets the `bng_session` cookie and returns the session's\n            `csrfToken` — cookie-authenticated mutating requests must echo it in the `X-CSRF-Token`\n            header. The response `status` field also signals the TOTP flows: `totpCodeRequired` means\n            retry login with `totpCode` set; `totpEnrolmentRequired` means the account must enrol —\n            load the returned `secret`/`otpauthUri` into an authenticator app and finish with\n            `POST /api/v1/auth/enrol`. Repeated failures lock the account/source-IP pair out (423).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/LoginRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Username, password, or TOTP code incorrect.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "423": {
            "description": "Too many failed attempts — locked out temporarily (or until a CLI unlock).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          }
        },
        "security": [
          { }
        ]
      }
    },
    "/api/v1/auth/enrol": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Complete a mandatory TOTP enrolment and log in.",
        "description": "Anonymous. Follows a login that returned `totpEnrolmentRequired`: send the\n            enrolment token plus the first authenticator code. On success behaves exactly like a\n            successful login (session cookie + CSRF token).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EnrolRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/EnrolRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/EnrolRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LoginResponse"
                }
              }
            }
          },
          "401": {
            "description": "Enrolment token expired or TOTP code incorrect.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          }
        },
        "security": [
          { }
        ]
      }
    },
    "/api/v1/auth/logout": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Log out the current interactive session.",
        "description": "Invalidates the server-side session and clears the cookie. Still 200 when no\n            session cookie is present. Any authenticated role.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/whoami": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "Who am I — the authenticated identity of this request.",
        "description": "Returns the identity name, effective role, whether the credential is an\n            interactive session or a bearer token, and (sessions only) the CSRF token to send on\n            mutating requests.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WhoAmI"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/totp/begin": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Begin voluntary TOTP enrolment for the current session's account.",
        "description": "Interactive sessions only (not bearer tokens). Returns the new secret and an\n            `otpauth://` URI to load into an authenticator app; confirm with\n            `POST /api/v1/auth/totp/confirm` to activate it. Any authenticated role.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TotpEnrolment"
                }
              }
            }
          },
          "400": {
            "description": "The request is not authenticated by an interactive session.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/totp/confirm": {
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Confirm a voluntary TOTP enrolment.",
        "description": "Send the current authenticator code to prove the secret was captured; from then\n            on login requires a TOTP code. Any authenticated role (interactive sessions only).",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpConfirmRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/TotpConfirmRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/TotpConfirmRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "400": {
            "description": "No interactive session, code incorrect, or enrolment expired.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/tokens": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "List named API tokens.",
        "description": "Returns each token's name and role. The token plaintext is never retrievable —\n            it is shown exactly once at creation. Super-user only.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApiTokenInfo"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Create a named API token.",
        "description": "Returns the plaintext token (`bngtok_…`) exactly once — store it now; the\n            appliance keeps only a SHA-256 hash. Use it as `Authorization: Bearer bngtok_…`.\n            Super-user only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTokenRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTokenRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CreateTokenRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiTokenCreated"
                }
              }
            }
          },
          "409": {
            "description": "A token with this name already exists (or the name is invalid).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/tokens/{name}": {
      "delete": {
        "tags": [
          "Authentication"
        ],
        "summary": "Delete a named API token.",
        "description": "Requests using the token stop authenticating immediately. Super-user only.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The token name (from the token list).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "404": {
            "description": "No token with this name exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/tokens/device": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "List the caller's own device tokens.",
        "description": "Any authenticated role. Only self-service device tokens minted for the caller are\n            returned; named automation tokens are not shown here.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ApiTokenInfo"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Mint a bearer token for the caller's current account and role.",
        "description": "Interactive sessions only (not bearer tokens), with CSRF. The plaintext token is\n            returned exactly once; only a SHA-256 hash is stored. Device tokens expire after 90 days\n            without successful use.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDeviceTokenRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDeviceTokenRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CreateDeviceTokenRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiTokenCreated"
                }
              }
            }
          },
          "400": {
            "description": "No interactive session, feature disabled, or invalid label.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/tokens/device/{name}": {
      "delete": {
        "tags": [
          "Authentication"
        ],
        "summary": "Revoke one of the caller's own device tokens.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "404": {
            "description": "No device token by that name belongs to the caller.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/users": {
      "get": {
        "tags": [
          "Authentication"
        ],
        "summary": "List accounts (web UI + CLI share one identity model).",
        "description": "Returns each account's username, effective API role, and whether TOTP is\n            enrolled. Super-user only.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/UserInfo"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Authentication"
        ],
        "summary": "Create an account.",
        "description": "The account can log in to the web UI/API and the appliance CLI (one identity\n            model). Committed to the appliance configuration and audited. Super-user only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CreateUserRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "409": {
            "description": "The username already exists or the request is invalid.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/auth/users/{username}": {
      "put": {
        "tags": [
          "Authentication"
        ],
        "summary": "Update an account (password / role / clear TOTP).",
        "description": "Only the supplied fields change; each change is committed and audited\n            individually. Super-user only.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "description": "The account to update.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/UpdateUserRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "404": {
            "description": "No account with this username exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "The change was rejected (e.g. demoting the last super-user).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Authentication"
        ],
        "summary": "Delete an account.",
        "description": "Removes the account from the appliance configuration (web UI/API and CLI).\n            Deleting the last super-user is rejected. Super-user only.",
        "parameters": [
          {
            "name": "username",
            "in": "path",
            "description": "The account to delete.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AuthActionResult"
                }
              }
            }
          },
          "409": {
            "description": "The account does not exist or cannot be deleted.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/schema": {
      "get": {
        "tags": [
          "Configuration"
        ],
        "summary": "Configuration schema: every editable field, grouped by section.",
        "description": "Field-level schema for the whole appliance configuration, grouped by section\n            (first CLI-path token) — the same registry the CLI uses, so paths, types, enum values and\n            secret handling match the CLI exactly. Field `path` values are the keys for reads and\n            writes; `secret: true` fields read back as a redaction placeholder; `visibleWhen`\n            describes fields that only apply for a particular driver/mode selection; sections with\n            `requiresRestart: true` only take full effect after a restart. Super-user surface (reads\n            allowed for any role).",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigSchema"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config": {
      "get": {
        "tags": [
          "Configuration"
        ],
        "summary": "Current configuration values (secrets redacted).",
        "description": "Every field of the active configuration keyed by its schema `path`. List-mode\n            fields join their elements with newlines; secret fields return the redaction placeholder\n            (write the placeholder back to mean \"unchanged\"). Keyed lists (e.g. RADIUS servers, login\n            users) return one item per key with the child values keyed by child path.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigValues"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Configuration"
        ],
        "summary": "Apply a change set (validate + commit).",
        "description": "Runs the CLI's commit pipeline: exclusive candidate edit → validate → atomic\n            persist → hot-apply event. Hot-appliable sections take effect immediately; others (see\n            the schema's `requiresRestart`) need a restart. The previous version goes into the\n            rollback history. Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeSet"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeSet"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeSet"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigCommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The change set is invalid (unknown field, bad value type, unknown VRF…).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/validate": {
      "post": {
        "tags": [
          "Configuration"
        ],
        "summary": "Validate a change set without persisting (dry-run).",
        "description": "Applies the change set to a throwaway candidate and runs full validation; nothing\n            is persisted regardless of outcome. Validation problems are reported in the 200 body\n            (`valid: false` + `error`), not as an error status. Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeSet"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeSet"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/ChangeSet"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigValidateResult"
                }
              }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/cache-refresh": {
      "post": {
        "tags": [
          "Configuration"
        ],
        "summary": "Refresh the subscriber cache from the auth database.",
        "description": "Re-reads subscribers from the configured auth database into the in-memory cache\n            without a config commit (migrated from the retired X-API-Key surface). `refreshed: false`\n            means the refresh could not run (e.g. no database auth driver). Audited. Super-user only;\n            local appliance only.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CacheRefreshResult"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/history": {
      "get": {
        "tags": [
          "Configuration"
        ],
        "summary": "Configuration version history (rollback points).",
        "description": "Newest first: version number, commit time, comment and committing identity. Any\n            version listed here can be restored with `POST /api/v1/config/rollback`.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ConfigVersion"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/config/rollback": {
      "post": {
        "tags": [
          "Configuration"
        ],
        "summary": "Roll back to a historical configuration version.",
        "description": "Stages the historical version as a candidate and commits it (CLI semantics), so\n            the rollback itself becomes a new history entry and hot-appliable changes take effect\n            immediately. Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RollbackRequest"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/RollbackRequest"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/RollbackRequest"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConfigRollbackResult"
                }
              }
            }
          },
          "400": {
            "description": "The requested version does not exist or could not be loaded.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "The historical version no longer passes validation.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fleet/appliances": {
      "get": {
        "tags": [
          "Fleet"
        ],
        "summary": "List the appliances this appliance knows about.",
        "description": "Always includes this appliance (`self: true`) followed by the peers configured\n            under `webUi.peers`. Peer names are the `{appliance}` key for the proxy endpoint.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/FleetAppliance"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/fleet/{appliance}/{path}": {
      "get": {
        "tags": [
          "Fleet"
        ],
        "summary": "Read an API endpoint on a peer appliance (GET-only proxy).",
        "description": "Forwards `GET /api/v1/{path}` (with the query string) to the named peer using the\n            peer credentials configured under `webUi.peers`, and relays the peer's status code, body\n            and content type verbatim — so the response schema is that of the proxied endpoint.\n            Only GETs are proxyable; `fleet/…` (loop) and `auth/…` (the peer's identity surface)\n            are refused. Example: `GET /api/v1/fleet/bng2/sessions/summary`.",
        "parameters": [
          {
            "name": "appliance",
            "in": "path",
            "description": "Peer appliance name from the appliance list.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "path",
            "in": "path",
            "description": "The peer-relative API path (everything after `/api/v1/`).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The peer's response, relayed verbatim (status, body, and content type)."
          },
          "400": {
            "description": "The named appliance is this appliance — call the local endpoint directly.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Upgrade, fleet, auth and noncanonical paths cannot be proxied.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "404": {
            "description": "No configured peer has this name.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/gre-circuits/listeners": {
      "get": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "List the shared local tunnel endpoints CPE circuits terminate on.",
        "description": "Usually a single entry per box. Every circuit references one by id.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GreTunnelListener"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/gre-circuits/listeners/{id}": {
      "put": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "Create or replace a tunnel listener.",
        "description": "The id in the URL is the key; an existing listener with that id is replaced.\n            Changing a listener's local address re-provisions every circuit that references it.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Listener id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/GreTunnelListenerWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/GreTunnelListenerWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/GreTunnelListenerWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "Delete a tunnel listener.",
        "description": "Refused while circuits still reference it — those circuits would have no local\n            endpoint to build a tunnel from, and config validation would reject the commit anyway.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Listener id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The listener does not exist, or circuits still reference it.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/gre-circuits": {
      "get": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "List the configured CPE circuits.",
        "description": "Configuration only — see `GET /api/v1/gre-circuits/status` for live tunnel\n            and BGP state. Optionally filtered to one customer VRF.",
        "parameters": [
          {
            "name": "vrf",
            "in": "query",
            "description": "Only circuits in this customer VRF.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CpeGreCircuit"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/gre-circuits/{id}": {
      "get": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "Get one circuit's configuration.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Circuit id (the SIM/site reference).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CpeGreCircuit"
                }
              }
            }
          },
          "404": {
            "description": "No circuit with that id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "Create or replace a CPE circuit.",
        "description": "The id in the URL is the key (and what the kernel device name is derived from), so\n            a PUT to an existing id edits that circuit in place rather than creating a second one.\n            Committing re-provisions: the tunnel converges to the new endpoints and FRR picks up the\n            neighbour, without touching the other circuits.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Circuit id (the SIM/site reference).",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CpeGreCircuitWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/CpeGreCircuitWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/CpeGreCircuitWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit (unknown listener or VRF, address family mismatch, duplicate neighbour, …).",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "Delete a CPE circuit.",
        "description": "Committing removes the kernel tunnel and de-configures the circuit's BGP neighbour\n            (doc 40 §40.6a) — the other circuits' sessions stay established.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "description": "Circuit id.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "No circuit with that id.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/gre-circuits/status": {
      "get": {
        "tags": [
          "GRE Circuits"
        ],
        "summary": "Live status of the configured circuits: tunnel state, BGP state, prefixes received.",
        "description": "The declared circuits joined to `ip link` and `vtysh` output — the same\n            data, from the same reporter, as `show circuits` and `netavo-vpe --show circuits`.",
        "parameters": [
          {
            "name": "vrf",
            "in": "query",
            "description": "Only circuits in this customer VRF.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/CpeCircuitStatus"
                  }
                }
              }
            }
          },
          "503": {
            "description": "FRR integration is not configured on this host.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/logs/tail": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Recent log entries (or entries after a sequence number).",
        "description": "Without `sinceSeq`, returns the most recent `count` entries. With `sinceSeq`,\n            returns up to `count` entries newer than that sequence number — poll incrementally by\n            echoing the previous response's `lastSeq`. Entries come from the appliance's in-memory\n            ring buffer (the same events shipped to syslog), so very old entries age out.\n            Fleet-proxyable.",
        "parameters": [
          {
            "name": "count",
            "in": "query",
            "description": "Maximum entries to return, clamped to 1–1000 (default 100).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 100
            }
          },
          {
            "name": "sinceSeq",
            "in": "query",
            "description": "Return only entries with a sequence number greater than this.",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogTail"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/logs/stream": {
      "get": {
        "tags": [
          "Logs"
        ],
        "summary": "Live log stream (server-sent events).",
        "description": "A `text/event-stream` response: each new log entry is pushed as an SSE event\n            named `log` whose `data` is one JSON object shaped like the tail endpoint's entries. The\n            stream first replays entries newer than `sinceSeq` (default: only entries after the moment\n            of connection), then stays open; a `: keepalive` comment is emitted every 15 seconds. Local\n            appliance only — not available through the fleet proxy (poll /logs/tail instead).",
        "parameters": [
          {
            "name": "sinceSeq",
            "in": "query",
            "description": "Replay entries newer than this sequence number before going live.",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int64"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "SSE stream of `event: log` events; each data line is one JSON log entry.",
            "content": {
              "text/event-stream": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/config": {
      "get": {
        "tags": [
          "Routing"
        ],
        "summary": "Read the routing configuration.",
        "description": "Global BGP/router settings plus the configured BGP peers. When the appliance has\n            no routing section at all, returns `configured: false` with every other field omitted.\n            Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RoutingConfig"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Routing"
        ],
        "summary": "Update the global BGP/router settings.",
        "description": "Validates and commits through the configuration store. Super-user only; local\n            appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Global"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/Global"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/Global"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/config/peers": {
      "post": {
        "tags": [
          "Routing"
        ],
        "summary": "Add a BGP peer.",
        "description": "Validates and commits; FRR is reprovisioned from the committed config.\n            Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Peer"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/Peer"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/Peer"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing/duplicate peer address or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/config/peers/{address}": {
      "put": {
        "tags": [
          "Routing"
        ],
        "summary": "Update a BGP peer.",
        "description": "Full-replace semantics for the peer's attributes; the address in the URL is the\n            immutable key. Super-user only; local appliance only.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "description": "The peer to update.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/Peer"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/Peer"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/Peer"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The peer does not exist or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Routing"
        ],
        "summary": "Delete a BGP peer.",
        "description": "Validates and commits; the peering is removed from FRR. Super-user only; local\n            appliance only.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "description": "The peer to delete.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The peer does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/bgp/summary": {
      "get": {
        "tags": [
          "Routing"
        ],
        "summary": "BGP peer summary (all address families).",
        "description": "One row per peer per AFI/SAFI with state, uptime and prefix counts — the API\n            equivalent of FRR's `show bgp summary`. `reachable: false` means FRR could not be queried\n            (the peer list is then empty); it is never an error status. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BgpSummary"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/isis/neighbors": {
      "get": {
        "tags": [
          "Routing"
        ],
        "summary": "IS-IS adjacencies.",
        "description": "System id/hostname, interface, level, state, hold time and SNPA per neighbor —\n            the API equivalent of `show isis neighbor`. `reachable: false` means FRR could not be\n            queried. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IsisNeighbors"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/ospf/neighbors": {
      "get": {
        "tags": [
          "Routing"
        ],
        "summary": "OSPF adjacencies (OSPFv2 and OSPFv3).",
        "description": "Router-id, interface, state, priority, dead timer and neighbour address per\n            adjacency — the API equivalent of `show ip ospf neighbor` and `show ipv6 ospf6 neighbor`,\n            merged into one list with a `protocol` field of `ospfv2` or `ospfv3`. A dual-stack box\n            legitimately lists the same neighbour twice: unlike IS-IS, OSPFv2 and OSPFv3 are separate\n            protocols forming separate adjacencies. `reachable: false` means FRR could not be queried.\n            Empty on a box whose configured IGP is not OSPF. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/OspfNeighbors"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/vrf/{vrf}/routes": {
      "get": {
        "tags": [
          "Routing"
        ],
        "summary": "Route table for a VRF (paged, filterable).",
        "description": "Server-side paged and filtered: `filter` is a case-insensitive substring match\n            across prefix, next-hop, AS-path and protocol. Use VRF name \"default\" for the global\n            table. Fleet-proxyable.",
        "parameters": [
          {
            "name": "vrf",
            "in": "path",
            "description": "VRF name (\"default\" for the global table).",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Rows to skip (default 0).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, clamped to 1–500 (default 50).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 50
            }
          },
          {
            "name": "filter",
            "in": "query",
            "description": "Substring filter across prefix / next-hop / AS-path / protocol.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouteTablePage"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/bgp/neighbor/{address}": {
      "get": {
        "tags": [
          "Routing"
        ],
        "summary": "Key facts for one BGP neighbor.",
        "description": "State, uptime, timers, router ids, BFD, and per-AFI/SAFI prefix counters.\n            `reachable: false` when FRR can't be asked or the peer is unknown (never an error\n            status). Fleet-proxyable.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "description": "The neighbor address as configured (e.g. \"10.255.255.2\").",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BgpNeighborDetail"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/routing/bgp/neighbor/{address}/routes": {
      "get": {
        "tags": [
          "Routing"
        ],
        "summary": "Routes advertised to / received from a BGP neighbor (paged, filterable).",
        "description": "`type=advertised` (default) returns what this appliance advertises to the\n            neighbor; `type=received` what it receives. `af` selects the address family using FRR's\n            AFI/SAFI names (e.g. \"ipv4Unicast\", \"ipv4Vpn\"); omitted = the default AF. Same paging and\n            filtering as the VRF route table. Fleet-proxyable.",
        "parameters": [
          {
            "name": "address",
            "in": "path",
            "description": "The neighbor address.",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "type",
            "in": "query",
            "description": "\"advertised\" (default) or \"received\".",
            "schema": {
              "type": "string",
              "default": "advertised"
            }
          },
          {
            "name": "af",
            "in": "query",
            "description": "AFI/SAFI name, e.g. \"ipv4Unicast\" or \"ipv4Vpn\" (null = default).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "in": "query",
            "description": "Rows to skip (default 0).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 0
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "Page size, clamped to 1–500 (default 50).",
            "schema": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32",
              "default": 50
            }
          },
          {
            "name": "filter",
            "in": "query",
            "description": "Substring filter across prefix / next-hop / AS-path / protocol.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RouteTablePage"
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/source-vrf-rules": {
      "get": {
        "tags": [
          "Source VRF Rules"
        ],
        "summary": "List configured source-VRF classification rules.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/SourceVrfRule"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Source VRF Rules"
        ],
        "summary": "Create a source-VRF classification rule.",
        "description": "Validates and commits through the configuration store; hot-applied without a\n            restart. Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SourceVrfRuleWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/SourceVrfRuleWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/SourceVrfRuleWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing/duplicate name or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/source-vrf-rules/programmed-rules": {
      "get": {
        "tags": [
          "Source VRF Rules"
        ],
        "summary": "List source-VRF classification rules programmed in the data plane.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/ProgrammedSourceVrfRule"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/source-vrf-rules/{name}": {
      "put": {
        "tags": [
          "Source VRF Rules"
        ],
        "summary": "Update a source-VRF classification rule.",
        "description": "Full-replace semantics: the body's attributes become the rule's attributes (the\n            name in the URL is the immutable key). Super-user only; local appliance only.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The rule to update.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/SourceVrfRuleWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/SourceVrfRuleWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/SourceVrfRuleWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The rule does not exist or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Source VRF Rules"
        ],
        "summary": "Delete a source-VRF classification rule.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The rule to delete.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The rule does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vrfs": {
      "get": {
        "tags": [
          "VRFs"
        ],
        "summary": "List VRFs (configured ∪ observed on live sessions).",
        "description": "Configured VRFs (routing.vrfs) come with their full routing attributes and\n            `configured: true`; VRF names that only appear on live sessions (e.g. \"default\") are\n            listed with counts only and `configured: false`. Fleet-proxyable.",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Vrf"
                  }
                }
              }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "VRFs"
        ],
        "summary": "Create a VRF.",
        "description": "Validates and commits through the configuration store; VRF add is hot-applied (no\n            restart). Super-user only; local appliance only.",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VrfWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/VrfWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/VrfWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing/duplicate name or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/vrfs/{name}": {
      "put": {
        "tags": [
          "VRFs"
        ],
        "summary": "Update a VRF.",
        "description": "Full-replace semantics: the body's attributes become the VRF's attributes (the\n            name in the URL is the immutable key). Super-user only; local appliance only.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The VRF to update.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/VrfWrite"
              }
            },
            "text/json": {
              "schema": {
                "$ref": "#/components/schemas/VrfWrite"
              }
            },
            "application/*+json": {
              "schema": {
                "$ref": "#/components/schemas/VrfWrite"
              }
            }
          },
          "required": true
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The VRF does not exist or the change was rejected.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "Configuration is locked by another editor, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "VRFs"
        ],
        "summary": "Delete a VRF.",
        "description": "Refused while the VRF carries live sessions — disconnect or drain them first.\n            Super-user only; local appliance only.",
        "parameters": [
          {
            "name": "name",
            "in": "path",
            "description": "The VRF to delete.",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CommitResult"
                }
              }
            }
          },
          "400": {
            "description": "The VRF does not exist.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "409": {
            "description": "The VRF has active sessions, the configuration is locked, or no routing config exists.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "422": {
            "description": "Config validation rejected the commit.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              },
              "application/json": { }
            }
          },
          "401": {
            "description": "Unauthenticated — no valid session cookie or bearer token was presented. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          },
          "403": {
            "description": "Forbidden — the authenticated identity's role is below the operation's role floor, or a cookie-authenticated mutation is missing a valid X-CSRF-Token header. The body is an RFC 9457 `application/problem+json` object.",
            "content": {
              "application/problem+json": {
                "schema": {
                  "$ref": "#/components/schemas/ProblemDetails"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "ActivateRequest": {
        "required": [
          "token"
        ],
        "type": "object",
        "properties": {
          "token": {
            "type": "string",
            "description": "Activation token issued with the licence."
          },
          "licenceId": {
            "type": "string",
            "description": "Licence id, when the token alone is ambiguous.",
            "nullable": true
          }
        },
        "description": "POST /api/v1/licensing/activate body.",
        "example": {
          "token": "bnglic_4f9a2c7e51b8",
          "licenceId": "LIC-2026-0042"
        }
      },
      "AddressPool": {
        "required": [
          "name",
          "family",
          "vrf",
          "delegationLength",
          "description",
          "size",
          "used",
          "free",
          "utilisationPercent",
          "allocationFailures",
          "cgnTranslated",
          "exclude",
          "subnets"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Pool name — the reference AAA returns (Framed-Pool and friends)."
          },
          "family": {
            "type": "string",
            "description": "ipv4, ipv6-wan or ipv6-pd."
          },
          "vrf": {
            "type": "string",
            "description": "VRF the pool's addresses live in; null = the default table.",
            "nullable": true
          },
          "delegationLength": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Prefix length handed to each subscriber (32, 64 or the PD length).",
            "format": "int32"
          },
          "description": {
            "type": "string",
            "description": "Free-text description."
          },
          "size": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Allocatable blocks on this appliance (excluded addresses not counted).",
            "format": "uint64"
          },
          "used": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks held: committed to sessions, on offer, or quarantined.",
            "format": "uint64"
          },
          "free": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks available.",
            "format": "uint64"
          },
          "utilisationPercent": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Used as a percentage of size.",
            "format": "double"
          },
          "allocationFailures": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Allocations refused because the pool had no free block (since start).",
            "format": "int64"
          },
          "cgnTranslated": {
            "type": "boolean",
            "description": "True for an IPv4 pool in a VRF translated by on-box CGN."
          },
          "exclude": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Addresses or prefixes never allocated."
          },
          "subnets": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AddressPoolSubnet"
            },
            "description": "The subnets this appliance allocates from, in preference order."
          }
        },
        "description": "A dynamic address pool with this appliance's live utilisation.",
        "example": {
          "name": "res-v4",
          "family": "ipv4",
          "vrf": "internet",
          "delegationLength": 32,
          "description": "Residential IPv4",
          "size": 2044,
          "used": 1712,
          "free": 332,
          "utilisationPercent": 83.8,
          "allocationFailures": 0,
          "cgnTranslated": false,
          "exclude": [
            "100.70.0.1"
          ],
          "subnets": [
            {
              "prefix": "100.70.0.0/22",
              "announce": true,
              "size": 1023,
              "used": 1001,
              "free": 22,
              "offered": 3,
              "quarantined": 0
            },
            {
              "prefix": "100.70.4.0/22",
              "announce": true,
              "size": 1021,
              "used": 711,
              "free": 310,
              "offered": 0,
              "quarantined": 1
            }
          ]
        }
      },
      "AddressPoolAllocation": {
        "required": [
          "prefix",
          "kind",
          "sessionId",
          "username",
          "vrf",
          "static"
        ],
        "type": "object",
        "properties": {
          "prefix": {
            "type": "string",
            "description": "The block (\"address/32\", \"prefix/64\" or the delegated prefix)."
          },
          "kind": {
            "type": "string",
            "description": "ipv4, ipv6-wan or ipv6-pd."
          },
          "sessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Holding session.",
            "format": "uint32"
          },
          "username": {
            "type": "string",
            "description": "Subscriber username, when known.",
            "nullable": true
          },
          "vrf": {
            "type": "string",
            "description": "The session's VRF; null = the default table.",
            "nullable": true
          },
          "static": {
            "type": "boolean",
            "description": "A static address inside the pool rather than a pool allocation."
          }
        },
        "description": "A block held by a session.",
        "example": {
          "prefix": "100.70.1.17/32",
          "kind": "ipv4",
          "sessionId": 40213,
          "username": "alice@example.net",
          "vrf": "internet",
          "static": false
        }
      },
      "AddressPoolAllocationsPage": {
        "required": [
          "total",
          "offset",
          "limit",
          "items"
        ],
        "type": "object",
        "properties": {
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Allocations in the pool (before paging).",
            "format": "int32"
          },
          "offset": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The offset this page was taken at.",
            "format": "int32"
          },
          "limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The effective page size.",
            "format": "int32"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AddressPoolAllocation"
            },
            "description": "The page."
          }
        },
        "description": "A page of an address pool's allocations.",
        "example": {
          "total": 1712,
          "offset": 0,
          "limit": 100,
          "items": [
            {
              "prefix": "100.70.1.17/32",
              "kind": "ipv4",
              "sessionId": 40213,
              "username": "alice@example.net",
              "vrf": "internet",
              "static": false
            }
          ]
        }
      },
      "AddressPoolSubnet": {
        "required": [
          "prefix",
          "announce",
          "size",
          "used",
          "free",
          "offered",
          "quarantined"
        ],
        "type": "object",
        "properties": {
          "prefix": {
            "type": "string",
            "description": "The subnet (CIDR)."
          },
          "announce": {
            "type": "boolean",
            "description": "Announced into BGP as an aggregate."
          },
          "size": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Allocatable blocks in the subnet.",
            "format": "uint64"
          },
          "used": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks held (committed, offered or quarantined).",
            "format": "uint64"
          },
          "free": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks available.",
            "format": "uint64"
          },
          "offered": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks held by an OFFER or a PPP negotiation not yet committed.",
            "format": "uint64"
          },
          "quarantined": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks declined by a DHCP client, out of service for an hour.",
            "format": "uint64"
          }
        },
        "description": "One subnet of an address pool.",
        "example": {
          "prefix": "100.70.0.0/22",
          "announce": true,
          "size": 1023,
          "used": 1001,
          "free": 22,
          "offered": 3,
          "quarantined": 0
        }
      },
      "Alarm": {
        "required": [
          "id",
          "instance",
          "severity",
          "firstRaisedUtc",
          "ageSeconds",
          "text",
          "context"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "instance": {
            "type": "string",
            "nullable": true
          },
          "severity": {
            "type": "string"
          },
          "firstRaisedUtc": {
            "type": "string",
            "format": "date-time"
          },
          "ageSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          },
          "text": {
            "type": "string"
          },
          "context": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "description": "One active alarm.",
        "example": {
          "id": "cgn.pool.high",
          "instance": "cgn-public-a",
          "severity": "major",
          "firstRaisedUtc": "2026-08-03T10:24:00Z",
          "ageSeconds": 1847,
          "text": "CGN group cgn-public-a is 91% allocated (8192 of 9000 port blocks in use)",
          "context": {
            "group": "cgn-public-a",
            "usedChunks": "8192",
            "totalChunks": "9000"
          }
        }
      },
      "AlarmClearRequest": {
        "required": [
          "id",
          "instance"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The alarm's catalog ID (e.g. `cgn.pool.high`)."
          },
          "instance": {
            "type": "string",
            "description": "The instance discriminator (e.g. a pool/peer name), or null for a singleton.",
            "nullable": true
          }
        },
        "description": "Body for `POST /api/v1/alarms/clear`.",
        "example": {
          "id": "cgn.pool.high",
          "instance": "cgn-public-a"
        }
      },
      "AlarmClearResult": {
        "required": [
          "cleared",
          "id",
          "instance"
        ],
        "type": "object",
        "properties": {
          "cleared": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "instance": {
            "type": "string",
            "nullable": true
          }
        },
        "description": "Result of a manual alarm clear.",
        "example": {
          "cleared": true,
          "id": "cgn.pool.high",
          "instance": "cgn-public-a"
        }
      },
      "AlarmsResponse": {
        "required": [
          "active",
          "count",
          "countBySeverity",
          "stats"
        ],
        "type": "object",
        "properties": {
          "active": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Alarm"
            }
          },
          "count": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "countBySeverity": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            }
          },
          "stats": {
            "$ref": "#/components/schemas/AlarmStats"
          }
        },
        "description": "The active-alarm snapshot.",
        "example": {
          "active": [
            {
              "id": "cgn.pool.high",
              "instance": "cgn-public-a",
              "severity": "major",
              "firstRaisedUtc": "2026-08-03T10:24:00Z",
              "ageSeconds": 1847,
              "text": "CGN group cgn-public-a is 91% allocated (8192 of 9000 port blocks in use)",
              "context": {
                "group": "cgn-public-a",
                "usedChunks": "8192",
                "totalChunks": "9000"
              }
            }
          ],
          "count": 1,
          "countBySeverity": {
            "major": 1
          },
          "stats": {
            "raised": 37,
            "cleared": 34,
            "events": 71,
            "eventsDropped": 0
          }
        }
      },
      "AlarmStats": {
        "required": [
          "raised",
          "cleared",
          "events",
          "eventsDropped"
        ],
        "type": "object",
        "properties": {
          "raised": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          },
          "cleared": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          },
          "events": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          },
          "eventsDropped": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          }
        },
        "description": "Lifetime counters for the alarm manager.",
        "example": {
          "raised": 37,
          "cleared": 34,
          "events": 71,
          "eventsDropped": 0
        }
      },
      "AlertNotification": {
        "required": [
          "source",
          "id",
          "kind",
          "severity",
          "state",
          "raised",
          "instance",
          "text",
          "timestampUtc",
          "context"
        ],
        "type": "object",
        "properties": {
          "source": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "kind": {
            "type": "string"
          },
          "severity": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "raised": {
            "type": "boolean"
          },
          "instance": {
            "type": "string",
            "nullable": true
          },
          "text": {
            "type": "string"
          },
          "timestampUtc": {
            "type": "string",
            "format": "date-time"
          },
          "context": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            }
          }
        },
        "description": "The canonical serialized alert payload delivered to <b>push</b> sinks. One contract for\n            every transport: the HTTP webhook sink posts this as its JSON body today, and the message-bus\n            sink (fast-follow) will publish this same shape — an integrator writes one parser. Documented in\n            the OpenAPI spec via `POST /api/v1/alarms/test`. Distinct from `AlarmDto` (the active\n            -table query shape): this is the per-event notification, carrying raise/clear and kind.",
        "example": {
          "source": "bng1.example.net",
          "id": "CGN_POOL_EXHAUSTION",
          "kind": "alarm",
          "severity": "major",
          "state": "raised",
          "raised": true,
          "instance": "public-a",
          "text": "CG-NAT IP group 'public-a' is 94% allocated (7712 of 8192 port blocks).",
          "timestampUtc": "2026-08-03T10:24:00Z",
          "context": {
            "group": "public-a",
            "utilisationPercent": 94
          }
        }
      },
      "AlertTestRequest": {
        "required": [
          "severity",
          "text"
        ],
        "type": "object",
        "properties": {
          "severity": {
            "type": "string",
            "description": "critical|major|minor|warning|info (default major).",
            "nullable": true
          },
          "text": {
            "type": "string",
            "description": "Optional custom alert text.",
            "nullable": true
          }
        },
        "description": "Body for `POST /api/v1/alarms/test`.",
        "example": {
          "severity": "major",
          "text": "synthetic alert raised by noc-alice"
        }
      },
      "ApiTokenCreated": {
        "required": [
          "name",
          "token"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Token name."
          },
          "token": {
            "type": "string",
            "description": "The bearer token plaintext (bngtok_…). Store it now; it cannot be\n            retrieved again."
          }
        },
        "description": "POST /api/v1/auth/tokens result — the only time the plaintext token is revealed.",
        "example": {
          "name": "monitoring",
          "token": "bngtok_9f4c2a7e15b84d03ae6172c9d5083b41"
        }
      },
      "ApiTokenInfo": {
        "required": [
          "name",
          "role",
          "device",
          "deviceUser",
          "label",
          "createdUtc",
          "lastUsedUtc"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Token name."
          },
          "role": {
            "type": "string",
            "description": "Role the token grants: \"read-only\", \"operator\" or \"admin\"."
          },
          "device": {
            "type": "boolean"
          },
          "deviceUser": {
            "type": "string",
            "nullable": true
          },
          "label": {
            "type": "string",
            "nullable": true
          },
          "createdUtc": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "lastUsedUtc": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          }
        },
        "description": "One named API token (the plaintext is never listed).",
        "example": {
          "name": "monitoring",
          "role": "read-only",
          "device": false,
          "deviceUser": null,
          "label": null,
          "createdUtc": null,
          "lastUsedUtc": null
        }
      },
      "ApplyRequest": {
        "required": [
          "id"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "drain": {
            "type": "boolean",
            "default": false
          }
        }
      },
      "AuthActionResult": {
        "required": [
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "What happened: \"loggedOut\", \"enrolled\", \"created\", \"updated\", \"deleted\"."
          },
          "username": {
            "type": "string",
            "description": "The affected account, when the action targets one (omitted otherwise).",
            "nullable": true
          }
        },
        "description": "Small status-only result used by several auth actions.",
        "example": {
          "status": "ok",
          "username": "noc-alice"
        }
      },
      "AuthRoleRule": {
        "type": "object",
        "properties": {
          "match": {
            "type": "string",
            "description": "An IdP group/claim value, a TACACS+ attribute value, or \"*\" (catch-all)."
          },
          "role": {
            "type": "string",
            "description": "Role to grant on a match: \"admin\", \"operator\", or \"readonly\"."
          }
        },
        "description": "One group/claim/attribute → role rule for external auth (OIDC/TACACS+). Rules are\n            evaluated in order, first match wins; string AuthRoleRule.Match \"*\" is a catch-all. No matching rule\n            means deny (fail closed). See design doc 33 §33.2.1. Role is admin/operator/readonly.",
        "example": {
          "match": "cn=noc-operators,ou=groups,dc=example,dc=net",
          "role": "operator"
        }
      },
      "BgpAfiSafi": {
        "required": [
          "afiSafi",
          "received",
          "accepted",
          "advertised"
        ],
        "type": "object",
        "properties": {
          "afiSafi": {
            "type": "string"
          },
          "received": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "accepted": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "advertised": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          }
        },
        "example": {
          "afiSafi": "ipv4Vpn",
          "received": 4218,
          "accepted": 4218,
          "advertised": 1863
        }
      },
      "BgpNeighborDetail": {
        "type": "object",
        "properties": {
          "reachable": {
            "type": "boolean"
          },
          "peer": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "remoteAs": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "localAs": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "state": {
            "type": "string"
          },
          "uptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "format": "double",
            "nullable": true
          },
          "routerId": {
            "type": "string",
            "nullable": true
          },
          "localRouterId": {
            "type": "string",
            "nullable": true
          },
          "holdTime": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "keepAlive": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "bfd": {
            "type": "boolean"
          },
          "addressFamilies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BgpAfiSafi"
            },
            "description": "Per-AFI/SAFI prefix counters (accepted/advertised) for the neighbor."
          }
        },
        "description": "GET /api/v1/routing/bgp/neighbor/{address} — the key facts for a neighbor.",
        "example": {
          "reachable": true,
          "peer": "10.255.255.2",
          "description": "pe1.lon1 route reflector",
          "remoteAs": 65000,
          "localAs": 65000,
          "state": "Established",
          "uptimeSeconds": 86412.538912,
          "routerId": "10.255.255.2",
          "localRouterId": "10.255.255.10",
          "holdTime": 90,
          "keepAlive": 30,
          "bfd": true,
          "addressFamilies": [
            {
              "afiSafi": "ipv4Vpn",
              "received": 4218,
              "accepted": 4218,
              "advertised": 1863
            }
          ]
        }
      },
      "BgpPeer": {
        "required": [
          "address",
          "description",
          "vpnOnly",
          "bfd"
        ],
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "Peer address (the key for the peer endpoints)."
          },
          "description": {
            "type": "string",
            "description": "Free-text description, or null.",
            "nullable": true
          },
          "vpnOnly": {
            "type": "boolean",
            "description": "True = only VPN address families are activated toward this peer."
          },
          "bfd": {
            "type": "boolean",
            "description": "Whether BFD is enabled toward this peer."
          }
        },
        "description": "One configured BGP peer.",
        "example": {
          "address": "10.255.255.2",
          "description": "pe1.lon1 route reflector",
          "vpnOnly": true,
          "bfd": true
        }
      },
      "BgpPeerStatus": {
        "type": "object",
        "properties": {
          "peer": {
            "type": "string"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "remoteAs": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "state": {
            "type": "string"
          },
          "uptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "format": "double",
            "nullable": true
          },
          "prefixesReceived": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "prefixesSent": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "afiSafi": {
            "type": "string",
            "description": "FRR AFI/SAFI section the peer row came from (e.g. \"ipv4Unicast\", \"ipv4Vpn\")."
          }
        },
        "example": {
          "peer": "10.255.255.2",
          "description": "pe1.lon1 route reflector",
          "remoteAs": 65000,
          "state": "Established",
          "uptimeSeconds": 86412.538912,
          "prefixesReceived": 4218,
          "prefixesSent": 1863,
          "afiSafi": "ipv4Vpn"
        }
      },
      "BgpSummary": {
        "type": "object",
        "properties": {
          "reachable": {
            "type": "boolean"
          },
          "peers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BgpPeerStatus"
            }
          }
        },
        "description": "GET /api/v1/routing/bgp/summary.",
        "example": {
          "reachable": true,
          "peers": [
            {
              "peer": "10.255.255.2",
              "description": "pe1.lon1 route reflector",
              "remoteAs": 65000,
              "state": "Established",
              "uptimeSeconds": 86412.538912,
              "prefixesReceived": 4218,
              "prefixesSent": 1863,
              "afiSafi": "ipv4Vpn"
            }
          ]
        }
      },
      "BlockDevice": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "model": {
            "type": "string",
            "nullable": true
          },
          "sizeBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "rotational": {
            "type": "boolean",
            "nullable": true
          },
          "transport": {
            "type": "string",
            "description": "Best-effort transport classification: nvme / scsi / virtio / mmc.",
            "nullable": true
          }
        },
        "example": {
          "name": "nvme0n1",
          "model": "SAMSUNG MZQL2960HCJR-00A07",
          "sizeBytes": 960197124096,
          "rotational": false,
          "transport": "nvme"
        }
      },
      "BoardInfo": {
        "type": "object",
        "properties": {
          "systemVendor": {
            "type": "string",
            "nullable": true
          },
          "systemProduct": {
            "type": "string",
            "nullable": true
          },
          "boardVendor": {
            "type": "string",
            "nullable": true
          },
          "boardProduct": {
            "type": "string",
            "nullable": true
          },
          "biosVendor": {
            "type": "string",
            "nullable": true
          },
          "biosVersion": {
            "type": "string",
            "nullable": true
          },
          "biosDate": {
            "type": "string",
            "nullable": true
          }
        },
        "example": {
          "systemVendor": "Supermicro",
          "systemProduct": "SYS-111C-NR",
          "boardVendor": "Supermicro",
          "boardProduct": "X13SEI-F",
          "biosVendor": "American Megatrends International, LLC.",
          "biosVersion": "1.4",
          "biosDate": "05/22/2025"
        }
      },
      "CacheRefreshResult": {
        "required": [
          "refreshed"
        ],
        "type": "object",
        "properties": {
          "refreshed": {
            "type": "boolean",
            "description": "Whether the subscriber cache was actually refreshed from the auth\n            database (false when no database auth driver is running)."
          }
        },
        "description": "POST /api/v1/config/cache-refresh result.",
        "example": {
          "refreshed": true
        }
      },
      "Capture": {
        "required": [
          "sessionId",
          "destinationIp",
          "maxPackets",
          "maxDurationSeconds",
          "startedUtc"
        ],
        "type": "object",
        "properties": {
          "sessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The captured session's BNG session id.",
            "format": "uint32"
          },
          "destinationIp": {
            "type": "string",
            "description": "Remote GRE collector receiving the mirrored traffic."
          },
          "maxPackets": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Packet limit, or null when unlimited.",
            "format": "int32",
            "nullable": true
          },
          "maxDurationSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Duration limit in seconds, or null when unlimited.",
            "format": "int32",
            "nullable": true
          },
          "startedUtc": {
            "type": "string",
            "description": "UTC time the capture started.",
            "format": "date-time"
          }
        },
        "description": "One active packet capture (GRE mirror of a session's traffic).",
        "example": {
          "sessionId": 40213,
          "destinationIp": "10.20.2.40",
          "maxPackets": 50000,
          "maxDurationSeconds": 300,
          "startedUtc": "2026-08-03T10:24:00Z"
        }
      },
      "CaptureStartRequest": {
        "required": [
          "destinationIp",
          "maxPackets",
          "maxDurationSeconds"
        ],
        "type": "object",
        "properties": {
          "destinationIp": {
            "type": "string",
            "description": "Remote collector to mirror the session's traffic to (GRE).\n    When a capture mirror allowlist is configured, must be one of the sanctioned collectors."
          },
          "maxPackets": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Stop automatically after this many packets (null/0 = unlimited).",
            "format": "int32",
            "nullable": true
          },
          "maxDurationSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Stop automatically after this many seconds (null/0 = unlimited).",
            "format": "int32",
            "nullable": true
          }
        },
        "description": "POST /api/v1/sessions/{id}/capture body.",
        "example": {
          "destinationIp": "10.20.2.40",
          "maxPackets": 50000,
          "maxDurationSeconds": 300
        }
      },
      "CaptureStopResult": {
        "required": [
          "status",
          "sessionId"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"stopped\"."
          },
          "sessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The session whose capture was stopped.",
            "format": "uint32"
          }
        },
        "description": "DELETE /api/v1/sessions/{id}/capture result.",
        "example": {
          "status": "stopped",
          "sessionId": 40213
        }
      },
      "CertificateInstallResult": {
        "required": [
          "status",
          "subject",
          "notAfter"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"installed\"."
          },
          "subject": {
            "type": "string",
            "description": "Subject DN of the installed certificate."
          },
          "notAfter": {
            "type": "string",
            "description": "Expiry of the installed certificate.",
            "format": "date-time"
          }
        },
        "description": "POST /api/v1/config/webui/certificate result.",
        "example": {
          "status": "installed",
          "subject": "CN=lns1.hul1.example.net",
          "notAfter": "2027-08-03T10:24:00Z"
        }
      },
      "CertificateUploadRequest": {
        "required": [
          "certPem",
          "keyPem"
        ],
        "type": "object",
        "properties": {
          "certPem": {
            "type": "string",
            "description": "Server certificate in PEM format (leaf first, then any chain)."
          },
          "keyPem": {
            "type": "string",
            "description": "Matching private key in PEM format."
          }
        },
        "description": "POST /api/v1/config/webui/certificate body.",
        "example": {
          "certPem": "-----BEGIN CERTIFICATE-----\nMIIDdzCCAl+gAwIBAgIJAK...\n-----END CERTIFICATE-----\n",
          "keyPem": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0B...\n-----END PRIVATE KEY-----\n"
        }
      },
      "CgnAutoLearn": {
        "required": [
          "insideVrf",
          "prefixes"
        ],
        "type": "object",
        "properties": {
          "insideVrf": {
            "type": "string",
            "description": "Inside VRF the prefixes apply to (null = default table).",
            "nullable": true
          },
          "prefixes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Inside-subscriber prefixes eligible for auto-learned bindings."
          }
        },
        "description": "Auto-learn scope inside one inside-VRF.",
        "example": {
          "insideVrf": "residential",
          "prefixes": [
            "100.64.0.0/12"
          ]
        }
      },
      "CgnBindingRow": {
        "required": [
          "bindingIndex",
          "group",
          "insideVrf",
          "insideIp",
          "publicIp",
          "sessionId",
          "subscriberId",
          "autoLearned",
          "portBlocks",
          "chunkSize",
          "portCap",
          "createdUtc"
        ],
        "type": "object",
        "properties": {
          "bindingIndex": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Stable index of the binding — the key for the identify endpoint.",
            "format": "uint32"
          },
          "group": {
            "type": "string",
            "description": "CGN IP group the public address came from."
          },
          "insideVrf": {
            "type": "string",
            "description": "Inside VRF of the subscriber (\"default\" = global table)."
          },
          "insideIp": {
            "type": "string",
            "description": "Subscriber's inside IPv4 address."
          },
          "publicIp": {
            "type": "string",
            "description": "Public IPv4 address the subscriber is translated to."
          },
          "sessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Associated BNG session id (0 for anonymous auto-learned bindings).",
            "format": "uint32"
          },
          "subscriberId": {
            "type": "string",
            "description": "Subscriber identity for compliance logging, or null if not identified.",
            "nullable": true
          },
          "autoLearned": {
            "type": "boolean",
            "description": "True when the binding was auto-learned rather than session-driven."
          },
          "portBlocks": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Allocated port ranges, as \"start-end\" strings."
          },
          "chunkSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Ports per block for this binding.",
            "format": "uint16"
          },
          "portCap": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Port cap in force for this binding.",
            "format": "uint16"
          },
          "createdUtc": {
            "type": "string",
            "description": "UTC time the binding was created.",
            "format": "date-time"
          }
        },
        "description": "One live NAT binding (a subscriber's inside address mapped to a public address with\n    one or more port blocks).",
        "example": {
          "bindingIndex": 10427,
          "group": "cgn-public-a",
          "insideVrf": "residential",
          "insideIp": "100.64.12.37",
          "publicIp": "45.84.200.113",
          "sessionId": 40213,
          "subscriberId": "alice@example.net",
          "autoLearned": false,
          "portBlocks": [
            "21504-22015",
            "22016-22527"
          ],
          "chunkSize": 512,
          "portCap": 4096,
          "createdUtc": "2026-08-03T10:24:00Z"
        }
      },
      "CgnBindingsPage": {
        "required": [
          "running",
          "total",
          "offset",
          "limit",
          "items"
        ],
        "type": "object",
        "properties": {
          "running": {
            "type": "boolean",
            "description": "Whether CGN is active in this control-plane process."
          },
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Bindings matching the filters (before paging).",
            "format": "int32"
          },
          "offset": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The offset this page was taken at.",
            "format": "int32"
          },
          "limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The effective page size (after clamping).",
            "format": "int32"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CgnBindingRow"
            },
            "description": "The page of binding rows."
          }
        },
        "description": "GET /api/v1/cgn/bindings page. Running=false means CGN isn't active (rows empty).",
        "example": {
          "running": true,
          "total": 1842,
          "offset": 0,
          "limit": 50,
          "items": [
            {
              "bindingIndex": 10427,
              "group": "cgn-public-a",
              "insideVrf": "residential",
              "insideIp": "100.64.12.37",
              "publicIp": "45.84.200.113",
              "sessionId": 40213,
              "subscriberId": "alice@example.net",
              "autoLearned": false,
              "portBlocks": [
                "21504-22015",
                "22016-22527"
              ],
              "chunkSize": 512,
              "portCap": 4096,
              "createdUtc": "2026-08-03T10:24:00Z"
            }
          ]
        }
      },
      "CgnCommitResult": {
        "required": [
          "status",
          "name",
          "committedUtc",
          "restartRequired"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"committed\"."
          },
          "name": {
            "type": "string",
            "description": "The group/settings object the commit touched."
          },
          "committedUtc": {
            "type": "string",
            "description": "UTC timestamp the new configuration version became active.",
            "format": "date-time",
            "nullable": true
          },
          "restartRequired": {
            "type": "boolean",
            "description": "True — CGN builds its allocators from config at startup, so the\n    change only takes full effect after a control-plane restart."
          }
        },
        "description": "Result of a CGN configuration commit. The CGN section is restart-required, so\n    bool CgnCommitResultDto.RestartRequired is always true.",
        "example": {
          "status": "committed",
          "name": "cgn-public-a",
          "committedUtc": "2026-08-03T10:24:11Z",
          "restartRequired": true
        }
      },
      "CgnGroup": {
        "required": [
          "name",
          "description",
          "outsideVrf",
          "publicSubnets",
          "chunkSize",
          "portCap",
          "setupsPerSec",
          "bindingTimeoutSeconds",
          "quarantineSeconds",
          "bypassPrefixes",
          "autoLearn",
          "timeouts",
          "activeBindings",
          "utilisation"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Group name (the key; service plans reference it)."
          },
          "description": {
            "type": "string",
            "description": "Free-text description."
          },
          "outsideVrf": {
            "type": "string",
            "description": "VRF holding the public side, or null for the default table.",
            "nullable": true
          },
          "publicSubnets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Public IPv4 subnets (CIDR) the group allocates from."
          },
          "chunkSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Ports per allocated port block.",
            "format": "int32"
          },
          "portCap": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Maximum ports one subscriber may hold.",
            "format": "int32"
          },
          "setupsPerSec": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Per-subscriber NAT setup rate limit.",
            "format": "int32"
          },
          "bindingTimeoutSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Idle teardown for auto-learned bindings.",
            "format": "int32"
          },
          "quarantineSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Quarantine before a freed port block is reused.",
            "format": "int32"
          },
          "bypassPrefixes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Destinations that bypass NAT (CIDR)."
          },
          "autoLearn": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CgnAutoLearn"
            },
            "description": "Auto-learn scopes (inside VRF + prefixes)."
          },
          "timeouts": {
            "$ref": "#/components/schemas/CgnTimeouts"
          },
          "activeBindings": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Live bindings currently in this group.",
            "format": "int32"
          },
          "utilisation": {
            "oneOf": [
              {
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/CgnGroupUtilisation"
              }
            ]
          }
        },
        "description": "One configured CGN IP group merged with live utilisation (utilisation is null when\n    CGN isn't running, or the group was added since startup).",
        "example": {
          "name": "cgn-public-a",
          "description": "Residential CGN pool",
          "outsideVrf": "internet",
          "publicSubnets": [
            "45.84.200.0/24"
          ],
          "chunkSize": 512,
          "portCap": 4096,
          "setupsPerSec": 200,
          "bindingTimeoutSeconds": 300,
          "quarantineSeconds": 120,
          "bypassPrefixes": [
            "100.64.0.0/10"
          ],
          "autoLearn": [
            {
              "insideVrf": "residential",
              "prefixes": [
                "100.64.0.0/12"
              ]
            }
          ],
          "timeouts": {
            "tcpEstablished": 7440,
            "tcpTransitory": 240,
            "udp": 300,
            "icmp": 60
          },
          "activeBindings": 1842,
          "utilisation": {
            "totalChunks": 9000,
            "freeChunks": 712,
            "usedChunks": 8192,
            "quarantinedChunks": 96
          }
        }
      },
      "CgnGroupUtilisation": {
        "required": [
          "totalChunks",
          "freeChunks",
          "usedChunks",
          "quarantinedChunks"
        ],
        "type": "object",
        "properties": {
          "totalChunks": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Total port blocks in the pool.",
            "format": "int32"
          },
          "freeChunks": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks free for allocation.",
            "format": "int32"
          },
          "usedChunks": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Blocks currently allocated to subscribers.",
            "format": "int32"
          },
          "quarantinedChunks": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Recently-freed blocks still in reuse quarantine.",
            "format": "int32"
          }
        },
        "description": "Port-block (chunk) utilisation of one IP group's public pool.",
        "example": {
          "totalChunks": 9000,
          "freeChunks": 712,
          "usedChunks": 8192,
          "quarantinedChunks": 96
        }
      },
      "CgnGroupWrite": {
        "required": [
          "name",
          "description",
          "outsideVrf",
          "publicSubnets",
          "chunkSize",
          "portCap",
          "setupsPerSec",
          "bindingTimeoutSeconds",
          "quarantineSeconds",
          "bypassPrefixes",
          "autoLearn",
          "timeouts"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Group name — the immutable key (service plans reference it)."
          },
          "description": {
            "type": "string",
            "description": "Free-text description.",
            "nullable": true
          },
          "outsideVrf": {
            "type": "string",
            "description": "VRF holding the public side (null/empty = default table).",
            "nullable": true
          },
          "publicSubnets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Public IPv4 subnets (CIDR) the group allocates from.",
            "nullable": true
          },
          "chunkSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Ports per allocated port block.",
            "format": "int32"
          },
          "portCap": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Maximum ports one subscriber may hold before extensions stop.",
            "format": "int32"
          },
          "setupsPerSec": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Per-subscriber NAT setup rate limit.",
            "format": "int32"
          },
          "bindingTimeoutSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Idle teardown for auto-learned bindings.",
            "format": "int32"
          },
          "quarantineSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Quarantine before a freed port block is reused.",
            "format": "int32"
          },
          "bypassPrefixes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Destinations that bypass NAT (CIDR).",
            "nullable": true
          },
          "autoLearn": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CgnAutoLearn"
            },
            "description": "Auto-learn scopes (inside VRF + prefixes).",
            "nullable": true
          },
          "timeouts": {
            "oneOf": [
              {
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/CgnTimeouts"
              }
            ]
          }
        },
        "description": "Request body for CGN IP group create/update.",
        "example": {
          "name": "cgn-public-a",
          "description": "Residential CGN pool",
          "outsideVrf": "internet",
          "publicSubnets": [
            "45.84.200.0/24"
          ],
          "chunkSize": 512,
          "portCap": 4096,
          "setupsPerSec": 200,
          "bindingTimeoutSeconds": 300,
          "quarantineSeconds": 120,
          "bypassPrefixes": [
            "100.64.0.0/10"
          ],
          "autoLearn": [
            {
              "insideVrf": "residential",
              "prefixes": [
                "100.64.0.0/12"
              ]
            }
          ],
          "timeouts": {
            "tcpEstablished": 7440,
            "tcpTransitory": 240,
            "udp": 300,
            "icmp": 60
          }
        }
      },
      "CgnIdentifyBinding": {
        "required": [
          "subscriberId",
          "sessionId"
        ],
        "type": "object",
        "properties": {
          "subscriberId": {
            "type": "string",
            "description": "Subscriber identity for compliance logging (≤ 256 chars).",
            "nullable": true
          },
          "sessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "BNG session id to associate, when known.",
            "format": "uint32",
            "nullable": true
          }
        },
        "description": "POST …/identify body — attach a subscriber identity to an auto-learned binding.",
        "example": {
          "subscriberId": "alice@example.net",
          "sessionId": 40213
        }
      },
      "CgnNativeCounters": {
        "required": [
          "autolearnMisses",
          "noMapping",
          "extensionWanted",
          "eventRingDrops",
          "flowCapDrops",
          "mappingCapDrops"
        ],
        "type": "object",
        "properties": {
          "autolearnMisses": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Reverse-path misses eligible for auto-learn classification.",
            "format": "uint64"
          },
          "noMapping": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Packets to a configured public address dropped for lack of a mapping.",
            "format": "uint64"
          },
          "extensionWanted": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Port-exhaustion events requesting an extension port block.",
            "format": "uint64"
          },
          "eventRingDrops": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "NAT events dropped because the event ring was full.",
            "format": "uint64"
          },
          "flowCapDrops": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Creates refused by the dynamic per-binding flow cap.",
            "format": "uint64"
          },
          "mappingCapDrops": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Creates refused because the mapping table was full.",
            "format": "uint64"
          }
        },
        "description": "Native (data-plane) CGN counters, cumulative since data-plane start.",
        "example": {
          "autolearnMisses": 18342,
          "noMapping": 407,
          "extensionWanted": 1265,
          "eventRingDrops": 0,
          "flowCapDrops": 3,
          "mappingCapDrops": 1
        }
      },
      "CgnSettings": {
        "required": [
          "enabled",
          "maxBindings",
          "maxFlows",
          "loggerSpoolSize"
        ],
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether CGN activates at control-plane start."
          },
          "maxBindings": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Global cap on concurrent NAT bindings (table size).",
            "format": "int32"
          },
          "maxFlows": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Global cap on concurrent NAT flows (table size).",
            "format": "int32"
          },
          "loggerSpoolSize": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Compliance-logger spool size (events buffered per driver).",
            "format": "int32"
          }
        },
        "description": "Section-level CGN settings (also the PUT body).",
        "example": {
          "enabled": true,
          "maxBindings": 65536,
          "maxFlows": 2097152,
          "loggerSpoolSize": 16384
        }
      },
      "CgnStatus": {
        "required": [
          "enabled",
          "running",
          "maxBindings",
          "maxFlows",
          "activeBindings",
          "autoLearnedBindings",
          "groups",
          "nativeCounters",
          "eventsLostByDriver"
        ],
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean",
            "description": "Whether CGN is enabled in configuration."
          },
          "running": {
            "type": "boolean",
            "description": "Whether CGN is active in this control-plane process (activates at startup)."
          },
          "maxBindings": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Configured global cap on concurrent NAT bindings.",
            "format": "int32"
          },
          "maxFlows": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Configured global cap on concurrent NAT flows.",
            "format": "int32"
          },
          "activeBindings": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Live NAT bindings right now (0 when not running).",
            "format": "int32"
          },
          "autoLearnedBindings": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "How many of the live bindings were auto-learned.",
            "format": "int32"
          },
          "groups": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CgnGroup"
            },
            "description": "The configured IP groups merged with live utilisation."
          },
          "nativeCounters": {
            "$ref": "#/components/schemas/CgnNativeCounters"
          },
          "eventsLostByDriver": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int64"
            },
            "description": "Compliance-log events lost per logger driver (null when the\n    logger pump isn't running).",
            "nullable": true
          }
        },
        "description": "GET /api/v1/cgn/status.",
        "example": {
          "enabled": true,
          "running": true,
          "maxBindings": 65536,
          "maxFlows": 2097152,
          "activeBindings": 1842,
          "autoLearnedBindings": 37,
          "groups": [
            {
              "name": "cgn-public-a",
              "description": "Residential CGN pool",
              "outsideVrf": "internet",
              "publicSubnets": [
                "45.84.200.0/24"
              ],
              "chunkSize": 512,
              "portCap": 4096,
              "setupsPerSec": 200,
              "bindingTimeoutSeconds": 300,
              "quarantineSeconds": 120,
              "bypassPrefixes": [
                "100.64.0.0/10"
              ],
              "autoLearn": [
                {
                  "insideVrf": "residential",
                  "prefixes": [
                    "100.64.0.0/12"
                  ]
                }
              ],
              "timeouts": {
                "tcpEstablished": 7440,
                "tcpTransitory": 240,
                "udp": 300,
                "icmp": 60
              },
              "activeBindings": 1842,
              "utilisation": {
                "totalChunks": 9000,
                "freeChunks": 712,
                "usedChunks": 8192,
                "quarantinedChunks": 96
              }
            }
          ],
          "nativeCounters": {
            "autolearnMisses": 18342,
            "noMapping": 407,
            "extensionWanted": 1265,
            "eventRingDrops": 0
          },
          "eventsLostByDriver": {
            "syslog": 0
          }
        }
      },
      "CgnTimeouts": {
        "required": [
          "tcpEstablished",
          "tcpTransitory",
          "udp",
          "icmp"
        ],
        "type": "object",
        "properties": {
          "tcpEstablished": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Idle timeout for established TCP flows.",
            "format": "int32"
          },
          "tcpTransitory": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Timeout for transitory TCP states (handshake/teardown).",
            "format": "int32"
          },
          "udp": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Idle timeout for UDP flows.",
            "format": "int32"
          },
          "icmp": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Idle timeout for ICMP (echo) flows.",
            "format": "int32"
          }
        },
        "description": "NAT mapping timeouts (seconds).",
        "example": {
          "tcpEstablished": 7440,
          "tcpTransitory": 240,
          "udp": 300,
          "icmp": 60
        }
      },
      "ChangeSet": {
        "required": [
          "fields",
          "listOps",
          "comment"
        ],
        "type": "object",
        "properties": {
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Field writes keyed by schema `path`. null clears a field; list-mode\n            fields take newline/comma-separated elements with replace semantics; a secret field set to\n            the redaction placeholder is left unchanged.",
            "nullable": true
          },
          "listOps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KeyedListOp"
            },
            "description": "Keyed-list operations (add/update/delete items such as RADIUS\n            servers or login users).",
            "nullable": true
          },
          "comment": {
            "type": "string",
            "description": "Commit comment recorded in the config history (apply only).",
            "nullable": true
          }
        },
        "description": "A batch of configuration changes (the body of validate and apply).",
        "example": {
          "fields": {
            "syslog enabled": "true",
            "syslog server": "10.20.2.40"
          },
          "listOps": [
            {
              "listPath": "auth radius server",
              "key": "10.20.2.31",
              "delete": false,
              "fields": {
                "auth radius server authPort": "1812",
                "auth radius server secret": "s3cr3t"
              }
            }
          ],
          "comment": "point syslog at the new collector"
        }
      },
      "CommitResult": {
        "required": [
          "status",
          "name",
          "committedUtc"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"committed\"."
          },
          "name": {
            "type": "string",
            "description": "The object the commit touched (VRF name, CGN group name, peer address, …)."
          },
          "committedUtc": {
            "type": "string",
            "description": "UTC timestamp the new configuration version became active.",
            "format": "date-time",
            "nullable": true
          }
        },
        "description": "Result of a successful configuration commit performed by a mutating endpoint.",
        "example": {
          "status": "committed",
          "name": "residential",
          "committedUtc": "2026-08-03T10:24:11Z"
        }
      },
      "ConfigCommitResult": {
        "required": [
          "status",
          "committedUtc"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"committed\"."
          },
          "committedUtc": {
            "type": "string",
            "description": "UTC timestamp the new configuration version became active.",
            "format": "date-time",
            "nullable": true
          }
        },
        "description": "PUT /api/v1/config result.",
        "example": {
          "status": "committed",
          "committedUtc": "2026-08-03T10:24:11Z"
        }
      },
      "ConfigListItem": {
        "required": [
          "key",
          "values"
        ],
        "type": "object",
        "properties": {
          "key": {
            "type": "string",
            "description": "The item's key (e.g. the RADIUS server host)."
          },
          "values": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Child-field values keyed by child path (secrets redacted)."
          }
        },
        "description": "One item of a keyed configuration list.",
        "example": {
          "key": "10.20.2.31",
          "values": {
            "auth radius server authPort": "1812",
            "auth radius server secret": "********"
          }
        }
      },
      "ConfigRollbackResult": {
        "required": [
          "status",
          "version",
          "committedUtc"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"rolledBack\"."
          },
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The restored version number.",
            "format": "int32"
          },
          "committedUtc": {
            "type": "string",
            "description": "UTC timestamp the rollback commit became active.",
            "format": "date-time",
            "nullable": true
          }
        },
        "description": "POST /api/v1/config/rollback result.",
        "example": {
          "status": "rolledBack",
          "version": 41,
          "committedUtc": "2026-08-03T10:26:02Z"
        }
      },
      "ConfigSchema": {
        "required": [
          "sections"
        ],
        "type": "object",
        "properties": {
          "sections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SectionSchema"
            },
            "description": "Every configuration section, alphabetical by section key."
          }
        },
        "description": "GET /api/v1/config/schema response.",
        "example": {
          "sections": [
            {
              "section": "syslog",
              "display": "Syslog",
              "group": "Monitoring",
              "groupOrder": 7,
              "requiresRestart": false,
              "fields": [
                {
                  "path": "syslog server",
                  "label": "Server",
                  "description": "Collector the appliance exports RFC 5424 events to.",
                  "type": "IpAddress",
                  "list": false,
                  "allowedValues": null,
                  "allowedValueLabels": null,
                  "secret": false,
                  "visibleWhen": {
                    "field": "syslog enabled",
                    "equals": [
                      "true"
                    ],
                    "contains": false
                  }
                }
              ],
              "keyedLists": [ ]
            }
          ]
        }
      },
      "ConfigValidateResult": {
        "required": [
          "valid",
          "error"
        ],
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean",
            "description": "Whether the change set passes validation against the current config."
          },
          "error": {
            "type": "string",
            "description": "The validation error when Valid is false.",
            "nullable": true
          }
        },
        "description": "POST /api/v1/config/validate result. Validation problems are reported here with a\n            200 status — an error status means the request itself failed.",
        "example": {
          "valid": false,
          "error": "syslog server: '10.20.2' is not a valid IP address"
        }
      },
      "ConfigValues": {
        "required": [
          "fields",
          "lists"
        ],
        "type": "object",
        "properties": {
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Field values keyed by schema path (secrets redacted; list fields joined\n            with newlines; null = unset)."
          },
          "lists": {
            "type": "object",
            "additionalProperties": {
              "type": "array",
              "items": {
                "$ref": "#/components/schemas/ConfigListItem"
              }
            },
            "description": "Keyed-list contents keyed by list path."
          }
        },
        "description": "GET /api/v1/config response — the active configuration's values.",
        "example": {
          "fields": {
            "syslog enabled": "true",
            "syslog server": "10.20.2.40"
          },
          "lists": {
            "auth radius server": [
              {
                "key": "10.20.2.31",
                "values": {
                  "auth radius server authPort": "1812",
                  "auth radius server secret": "********"
                }
              }
            ]
          }
        }
      },
      "ConfigVersion": {
        "required": [
          "version",
          "committedUtc",
          "comment",
          "username"
        ],
        "type": "object",
        "properties": {
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Version number for `POST /api/v1/config/rollback`.",
            "format": "int32"
          },
          "committedUtc": {
            "type": "string",
            "description": "When this version was committed.",
            "format": "date-time"
          },
          "comment": {
            "type": "string",
            "description": "The commit comment, or null.",
            "nullable": true
          },
          "username": {
            "type": "string",
            "description": "The committing identity, or null.",
            "nullable": true
          }
        },
        "description": "One configuration history entry (a rollback point).",
        "example": {
          "version": 42,
          "committedUtc": "2026-08-03T10:24:11Z",
          "comment": "point syslog at the new collector",
          "username": "noc-alice"
        }
      },
      "ControlPlaneStatus": {
        "type": "object",
        "properties": {
          "uptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "format": "double",
            "nullable": true
          },
          "version": {
            "type": "string",
            "description": "Informational assembly version of the control plane build.",
            "nullable": true
          },
          "drainState": {
            "type": "string",
            "description": "Placeholder until drain orchestration exists; always \"normal\" for now."
          }
        },
        "example": {
          "uptimeSeconds": 86412.538912,
          "version": "1.4.0",
          "drainState": "normal"
        }
      },
      "CpeCircuitStatus": {
        "required": [
          "id",
          "vrf",
          "device",
          "remote",
          "localTunnelAddress",
          "remoteTunnelAddress",
          "peerAsn",
          "description",
          "tunnelState",
          "rxBytes",
          "txBytes",
          "bgpState",
          "bgpUptime",
          "prefixesReceived"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "vrf": {
            "type": "string"
          },
          "device": {
            "type": "string"
          },
          "remote": {
            "type": "string"
          },
          "localTunnelAddress": {
            "type": "string"
          },
          "remoteTunnelAddress": {
            "type": "string"
          },
          "peerAsn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint32"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "tunnelState": {
            "type": "string"
          },
          "rxBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          },
          "txBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          },
          "bgpState": {
            "type": "string"
          },
          "bgpUptime": {
            "type": "string"
          },
          "prefixesReceived": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          }
        }
      },
      "CpeGreCircuit": {
        "required": [
          "id",
          "listenerId",
          "vrf",
          "remote",
          "localTunnelAddress",
          "remoteTunnelAddress",
          "peerAsn"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "listenerId": {
            "type": "string"
          },
          "vrf": {
            "type": "string"
          },
          "remote": {
            "type": "string"
          },
          "localTunnelAddress": {
            "type": "string"
          },
          "remoteTunnelAddress": {
            "type": "string"
          },
          "peerAsn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint32"
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "maximumPrefix": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint32",
            "nullable": true
          },
          "bfd": {
            "type": "boolean",
            "default": false
          },
          "key": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint32",
            "nullable": true
          },
          "clampMss": {
            "type": "boolean",
            "default": true
          },
          "mtu": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "default": 0
          }
        },
        "description": "doc 40 §40.4: one CPE/SIM's GRE tunnel + the BGP session that runs inside it. Always 1:1, so\nmodeled as a single object rather than two lists (a tunnel-interface list and a BGP-peer list)\nan operator would otherwise have to keep address-matched by hand across ~250 entries — the same\nclass of drift risk string? GreTunnelListener.UnderlayInterface avoids, at 250x the\nsurface area.\n\nThis is ordinary PE-CE eBGP inside a plain GRE tunnel, NOT the remote-PE mechanism\nLocalInterfaceConfig's `gre` type implements (MPLS-in-GRE transport to a hub,\ncarrying vpn-only sessions). Nothing here is MPLS-capable and nothing here talks to the core."
      },
      "CpeGreCircuitWrite": {
        "type": "object",
        "properties": {
          "listenerId": {
            "type": "string",
            "nullable": true
          },
          "vrf": {
            "type": "string",
            "nullable": true
          },
          "remote": {
            "type": "string",
            "nullable": true
          },
          "localTunnelAddress": {
            "type": "string",
            "nullable": true
          },
          "remoteTunnelAddress": {
            "type": "string",
            "nullable": true
          },
          "peerAsn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint32",
            "nullable": true
          },
          "description": {
            "type": "string",
            "nullable": true
          },
          "maximumPrefix": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint32",
            "nullable": true
          },
          "bfd": {
            "type": "boolean",
            "nullable": true
          },
          "key": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint32",
            "nullable": true
          },
          "clampMss": {
            "type": "boolean",
            "nullable": true
          },
          "mtu": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          }
        },
        "description": "Body for a circuit write. On a PUT over an existing circuit, an absent field keeps\n            its current value, so a caller can flip one knob without restating the whole circuit."
      },
      "CpuInfo": {
        "type": "object",
        "properties": {
          "model": {
            "type": "string",
            "nullable": true
          },
          "vendor": {
            "type": "string",
            "nullable": true
          },
          "sockets": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "cores": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "threads": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "mhz": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "format": "double",
            "nullable": true
          },
          "numaNodes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "flags": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Curated subset of CPU flags relevant to the BNG (sse4_2 for the CRC32C ECMP\n    path, avx2, aes) — never the full flag soup."
          }
        },
        "example": {
          "model": "Intel(R) Xeon(R) Gold 6338N CPU @ 2.20GHz",
          "vendor": "GenuineIntel",
          "sockets": 2,
          "cores": 32,
          "threads": 64,
          "mhz": 2194.867,
          "numaNodes": 2,
          "flags": [
            "sse4_2",
            "avx2",
            "aes"
          ]
        }
      },
      "CreateDeviceTokenRequest": {
        "required": [
          "label"
        ],
        "type": "object",
        "properties": {
          "label": {
            "type": "string",
            "description": "Operator-facing label for this phone/tablet/laptop."
          }
        },
        "description": "POST /api/v1/auth/tokens/device body.",
        "example": {
          "label": "Chris iPhone"
        }
      },
      "CreateTokenRequest": {
        "required": [
          "name",
          "role"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Unique token name (shown in listings and audit logs)."
          },
          "role": {
            "type": "string",
            "description": "Role the token grants: \"read-only\", \"operator\" or \"admin\" (super-user)."
          }
        },
        "description": "POST /api/v1/auth/tokens body.",
        "example": {
          "name": "prov-automation",
          "role": "operator"
        }
      },
      "CreateUserRequest": {
        "required": [
          "username",
          "password",
          "role"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "New account name."
          },
          "password": {
            "type": "string",
            "description": "Initial password."
          },
          "role": {
            "type": "string",
            "description": "Role: \"read-only\", \"operator\" or \"admin\" (super-user)."
          }
        },
        "description": "POST /api/v1/auth/users body.",
        "example": {
          "username": "noc-alice",
          "password": "correct horse battery staple",
          "role": "operator"
        }
      },
      "DataPlaneStatus": {
        "type": "object",
        "properties": {
          "running": {
            "type": "boolean"
          },
          "heartbeatAgeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "format": "double",
            "nullable": true
          },
          "palBackend": {
            "type": "string",
            "description": "PAL backend: dpdk / af_xdp / mock (config dataPlane.mode)."
          }
        },
        "example": {
          "running": true,
          "heartbeatAgeSeconds": 0.184,
          "palBackend": "dpdk"
        }
      },
      "EnrolRequest": {
        "required": [
          "enrolmentToken",
          "totpCode"
        ],
        "type": "object",
        "properties": {
          "enrolmentToken": {
            "type": "string",
            "description": "The short-lived token returned by the login response."
          },
          "totpCode": {
            "type": "string",
            "description": "First code generated by the authenticator app for the new secret."
          }
        },
        "description": "POST /api/v1/auth/enrol body — completes a mandatory TOTP enrolment started by login.",
        "example": {
          "enrolmentToken": "bngenr_5c1f0a94d27b",
          "totpCode": "418244"
        }
      },
      "FieldSchema": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "The field's full path — the key used in reads and change sets."
          },
          "label": {
            "type": "string",
            "description": "Human-friendly field label."
          },
          "description": {
            "type": "string",
            "description": "What the field does (from the config mapping registry)."
          },
          "type": {
            "type": "string",
            "description": "Value type: String, Int, Bool, IpAddress, IpV4Address, IpV6Address, MacAddress…"
          },
          "list": {
            "type": "boolean",
            "description": "True for multi-element fields (written as newline/comma-separated elements)."
          },
          "allowedValues": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Allowed values when the field is an enum/dropdown; null for free-form fields.",
            "nullable": true
          },
          "allowedValueLabels": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Optional value→friendly-label map for the dropdown (driver name, humanized enum, …).",
            "nullable": true
          },
          "secret": {
            "type": "boolean",
            "description": "True for secrets: reads return a placeholder, writing the placeholder means \"unchanged\"."
          },
          "visibleWhen": {
            "oneOf": [
              {
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/VisibleWhenSchema"
              }
            ]
          }
        },
        "description": "One editable configuration field.",
        "example": {
          "path": "syslog server",
          "label": "Server",
          "description": "Collector the appliance exports RFC 5424 events to.",
          "type": "IpAddress",
          "list": false,
          "allowedValues": null,
          "allowedValueLabels": null,
          "secret": false,
          "visibleWhen": {
            "field": "syslog enabled",
            "equals": [
              "true"
            ],
            "contains": false
          }
        }
      },
      "Filesystem": {
        "type": "object",
        "properties": {
          "mountPoint": {
            "type": "string"
          },
          "fsType": {
            "type": "string",
            "nullable": true
          },
          "totalBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "availableBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          }
        },
        "example": {
          "mountPoint": "/",
          "fsType": "ext4",
          "totalBytes": 491921162240,
          "availableBytes": 463138689024
        }
      },
      "FleetAppliance": {
        "required": [
          "name",
          "self",
          "url"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Appliance name — the `{appliance}` key for the fleet proxy."
          },
          "self": {
            "type": "boolean",
            "description": "True for the appliance answering this request."
          },
          "url": {
            "type": "string",
            "description": "The peer's base URL (from webUi.peers); null for self.",
            "nullable": true
          }
        },
        "description": "One row of GET /api/v1/fleet/appliances.",
        "example": {
          "name": "lns1-hul1",
          "self": true,
          "url": null
        }
      },
      "Global": {
        "required": [
          "asn",
          "routerId",
          "routerId6",
          "isisNet",
          "hostname",
          "enableVrfIpv6",
          "comment"
        ],
        "type": "object",
        "properties": {
          "asn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Local autonomous system number.",
            "format": "uint32"
          },
          "routerId": {
            "type": "string",
            "description": "IPv4 router id; null/empty keeps the current value.",
            "nullable": true
          },
          "routerId6": {
            "type": "string",
            "description": "IPv6 router id; null clears it.",
            "nullable": true
          },
          "isisNet": {
            "type": "string",
            "description": "IS-IS NET (area + system id); null/empty keeps the current value.\n            Only meaningful when IgpProtocol is `isis`.",
            "nullable": true
          },
          "hostname": {
            "type": "string",
            "description": "FRR hostname; null/empty keeps the current value.",
            "nullable": true
          },
          "enableVrfIpv6": {
            "type": "boolean",
            "description": "Enable IPv6 (6VPE) address families in VRFs."
          },
          "comment": {
            "type": "string",
            "description": "Optional commit comment for the config history.",
            "nullable": true
          },
          "originateDefaultRoute": {
            "type": "boolean",
            "description": "Originate an IPv4 default route into BGP in the default\n            (global) table — a standalone CGN appliance advertising itself as the internet exit.",
            "default": false
          },
          "igpProtocol": {
            "type": "string",
            "description": "Interior gateway protocol: `isis`, `ospf` or\n            `none`. Null/empty keeps the current value. Switching this restarts FRR, because\n            starting or stopping a routing daemon cannot be done by a configuration reload.",
            "nullable": true
          },
          "igpInterfaces": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Full IGP interface list (loopbacks are made passive; the rest\n            are treated as point-to-point core links and also carry LDP). Null keeps the current list;\n            an empty list clears it.",
            "nullable": true
          },
          "ospfArea": {
            "type": "string",
            "description": "Default OSPF area as a dotted quad or integer; null/empty keeps the\n            current value. Only meaningful when IgpProtocol is `ospf`.",
            "nullable": true
          }
        },
        "description": "PUT /api/v1/routing/config body — global BGP/router settings.",
        "example": {
          "asn": 65000,
          "routerId": "10.255.255.10",
          "routerId6": "2001:db8:255::10",
          "isisNet": "49.0001.0102.5525.5010.00",
          "hostname": "lns1-hul1",
          "enableVrfIpv6": true,
          "comment": "enable 6VPE on the core links",
          "originateDefaultRoute": false,
          "igpProtocol": "isis",
          "igpInterfaces": [
            "lo",
            "core0",
            "core1"
          ],
          "ospfArea": "0.0.0.0"
        }
      },
      "GreTunnelListener": {
        "required": [
          "id",
          "localAddress"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "localAddress": {
            "type": "string"
          },
          "underlayInterface": {
            "type": "string",
            "nullable": true
          },
          "ttl": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "default": 64
          }
        },
        "description": "doc 40 §40.4: a shared local endpoint many CPE tunnels terminate on. Usually just one per box;\nmore than one only if the box has multiple public IPs/uplinks CPEs are provisioned to reach."
      },
      "GreTunnelListenerWrite": {
        "required": [
          "localAddress"
        ],
        "type": "object",
        "properties": {
          "localAddress": {
            "type": "string",
            "nullable": true
          },
          "underlayInterface": {
            "type": "string",
            "nullable": true
          },
          "ttl": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          }
        },
        "description": "Body for a listener write. Absent fields take their schema defaults."
      },
      "HardwareInventory": {
        "type": "object",
        "properties": {
          "cpu": {
            "$ref": "#/components/schemas/CpuInfo"
          },
          "memory": {
            "$ref": "#/components/schemas/MemoryInfo"
          },
          "storage": {
            "$ref": "#/components/schemas/StorageInfo"
          },
          "board": {
            "$ref": "#/components/schemas/BoardInfo"
          },
          "nics": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Nic"
            }
          }
        },
        "description": "GET /api/v1/system/hardware — static inventory, cached until refresh.",
        "example": {
          "cpu": {
            "model": "Intel(R) Xeon(R) Gold 6338N CPU @ 2.20GHz",
            "vendor": "GenuineIntel",
            "sockets": 2,
            "cores": 32,
            "threads": 64,
            "mhz": 2194.867,
            "numaNodes": 2,
            "flags": [
              "sse4_2",
              "avx2",
              "aes"
            ]
          },
          "memory": {
            "totalBytes": 135223480320,
            "numaNodes": [
              {
                "node": 0,
                "totalBytes": 67611740160
              },
              {
                "node": 1,
                "totalBytes": 67611740160
              }
            ],
            "hugepages": [
              {
                "sizeKb": 1048576,
                "total": 32,
                "free": 8
              }
            ]
          },
          "storage": {
            "blockDevices": [
              {
                "name": "nvme0n1",
                "model": "SAMSUNG MZQL2960HCJR-00A07",
                "sizeBytes": 960197124096,
                "rotational": false,
                "transport": "nvme"
              }
            ],
            "filesystems": [
              {
                "mountPoint": "/",
                "fsType": "ext4",
                "totalBytes": 491921162240,
                "availableBytes": 463138689024
              }
            ]
          },
          "board": {
            "systemVendor": "Supermicro",
            "systemProduct": "SYS-111C-NR",
            "boardVendor": "Supermicro",
            "boardProduct": "X13SEI-F",
            "biosVendor": "American Megatrends International, LLC.",
            "biosVersion": "1.4",
            "biosDate": "05/22/2025"
          },
          "nics": [
            {
              "pciAddress": "0000:5e:00.0",
              "vendorId": "0x8086",
              "deviceId": "0x1572",
              "vendorName": "Intel Corporation",
              "model": "Ethernet Controller X710 for 10GbE SFP+",
              "driver": "vfio-pci",
              "numaNode": 0,
              "kernelInterface": null
            }
          ]
        }
      },
      "HardwareRefreshResult": {
        "required": [
          "refreshed"
        ],
        "type": "object",
        "properties": {
          "refreshed": {
            "type": "boolean",
            "description": "Always true — the cached inventory was invalidated."
          }
        },
        "description": "POST /api/v1/system/hardware/refresh result.",
        "example": {
          "refreshed": true
        }
      },
      "Health": {
        "required": [
          "status"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"ok\" while the control plane can serve requests."
          }
        },
        "description": "Liveness probe response.",
        "example": {
          "status": "ok"
        }
      },
      "HugepagePool": {
        "type": "object",
        "properties": {
          "sizeKb": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          },
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "free": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          }
        },
        "example": {
          "sizeKb": 1048576,
          "total": 32,
          "free": 8
        }
      },
      "InjectRequest": {
        "required": [
          "port",
          "source",
          "destination",
          "sVlan",
          "cVlan",
          "mplsLabel",
          "protocol",
          "sourcePort",
          "destinationPort",
          "size",
          "emit"
        ],
        "type": "object",
        "properties": {
          "port": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Ingress port index to pretend the frame arrived on.",
            "format": "int32"
          },
          "source": {
            "type": "string",
            "description": "Source address. What a source-VRF rule matches on."
          },
          "destination": {
            "type": "string",
            "description": "Destination address."
          },
          "sVlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Outer S-VLAN (0/null = none).",
            "format": "int32",
            "nullable": true
          },
          "cVlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Inner C-VLAN (0/null = none).",
            "format": "int32",
            "nullable": true
          },
          "mplsLabel": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "L3VPN label to arrive under (0/null = none).",
            "format": "int64",
            "nullable": true
          },
          "protocol": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "IP protocol number; 17 = UDP, 6 = TCP, 1 = ICMP.",
            "format": "int32",
            "nullable": true
          },
          "sourcePort": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "L4 source port for TCP/UDP.",
            "format": "int32",
            "nullable": true
          },
          "destinationPort": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "L4 destination port for TCP/UDP.",
            "format": "int32",
            "nullable": true
          },
          "size": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Payload bytes after the L4 header.",
            "format": "int32",
            "nullable": true
          },
          "emit": {
            "type": "boolean",
            "description": "Actually transmit. Default false — trace and drop before egress.",
            "nullable": true
          }
        },
        "description": "POST /api/v1/diagnostics/inject body — a packet described, not hex-encoded.",
        "example": {
          "port": 1,
          "source": "172.31.224.233",
          "destination": "10.254.0.12",
          "sVlan": null,
          "cVlan": null,
          "mplsLabel": 100,
          "protocol": 17,
          "sourcePort": 5000,
          "destinationPort": 53,
          "size": 32,
          "emit": false
        }
      },
      "InjectResult": {
        "required": [
          "frameBytes",
          "emitted",
          "dryRun",
          "steps"
        ],
        "type": "object",
        "properties": {
          "frameBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Size of the frame that was built.",
            "format": "int32"
          },
          "emitted": {
            "type": "boolean",
            "description": "True if it was actually transmitted rather than traced and dropped."
          },
          "dryRun": {
            "type": "boolean",
            "description": "True when the packet was dropped before egress."
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TraceStep"
            },
            "description": "Per-stage decisions, in the order the pipeline took them."
          }
        },
        "description": "What the data plane did with an injected packet.",
        "example": {
          "frameBytes": 82,
          "emitted": false,
          "dryRun": true,
          "steps": [
            {
              "stage": "SourceVrf",
              "verdict": "Ok",
              "a": 7,
              "b": 16
            }
          ]
        }
      },
      "Interface": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "label": {
            "type": "string",
            "description": "Operator label from dataPlane.*Interfaces[].label (\"MS3 NNI 2\", ...).",
            "nullable": true
          },
          "pciAddress": {
            "type": "string",
            "nullable": true
          },
          "mac": {
            "type": "string",
            "nullable": true
          },
          "driver": {
            "type": "string",
            "nullable": true
          },
          "operState": {
            "type": "string",
            "nullable": true
          },
          "speedMbps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "mtu": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "role": {
            "type": "string",
            "description": "subscriber | network | management | loopback | other (derived from config)."
          },
          "owner": {
            "type": "string",
            "description": "dataplane | kernel | kernel-vrf."
          },
          "vrfMaster": {
            "type": "string",
            "description": "VRF master device name when owner is kernel-vrf.",
            "nullable": true
          }
        },
        "description": "GET /api/v1/system/interfaces — unified interface table joining kernel netdevs,\n    PCI NIC inventory, and the BNG config-derived role.",
        "example": {
          "name": "core0",
          "label": "MS3 NNI 2",
          "pciAddress": "0000:5e:00.1",
          "mac": "a4:2b:8c:11:04:02",
          "driver": "vfio-pci",
          "operState": "up",
          "speedMbps": 10000,
          "mtu": 9000,
          "role": "network",
          "owner": "dataplane",
          "vrfMaster": null
        }
      },
      "IpoeSubscriberWrite": {
        "type": "object",
        "properties": {
          "vrfName": {
            "type": "string",
            "nullable": true
          },
          "framedIp": {
            "type": "string",
            "nullable": true
          },
          "framedIpv6": {
            "type": "string",
            "nullable": true
          },
          "ipv6PdPrefix": {
            "type": "string",
            "nullable": true
          },
          "ipv6PdLength": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint8",
            "nullable": true
          },
          "dns1": {
            "type": "string",
            "nullable": true
          },
          "dns2": {
            "type": "string",
            "nullable": true
          },
          "dnsV6_1": {
            "type": "string",
            "nullable": true
          },
          "dnsV6_2": {
            "type": "string",
            "nullable": true
          },
          "planName": {
            "type": "string",
            "nullable": true
          },
          "downloadRateKbps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "uploadRateKbps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "downloadRatePps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "uploadRatePps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "downloadBurstKb": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "uploadBurstKb": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "additionalRoutes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          },
          "port": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint8"
          },
          "vlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint16"
          },
          "sVlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint16"
          },
          "option82CircuitId": {
            "type": "string",
            "nullable": true
          },
          "subscriberServiceId": {
            "type": "string",
            "nullable": true
          }
        },
        "description": "Create/update body for a locally-defined IPoE (DHCP) subscriber.",
        "example": {
          "vrfName": "residential",
          "framedIp": "100.64.12.38",
          "framedIpv6": "2001:db8:64:12::26",
          "ipv6PdPrefix": "2001:db8:a012:3500::",
          "ipv6PdLength": 56,
          "dns1": "1.1.1.1",
          "dns2": "1.0.0.1",
          "dnsV6_1": "2606:4700:4700::1111",
          "dnsV6_2": "2606:4700:4700::1001",
          "planName": "Fibre 500",
          "downloadRateKbps": 500000,
          "uploadRateKbps": 100000,
          "downloadRatePps": 0,
          "uploadRatePps": 0,
          "downloadBurstKb": 2048,
          "uploadBurstKb": 1024,
          "additionalRoutes": [ ],
          "port": 0,
          "vlan": 712,
          "sVlan": 101,
          "option82CircuitId": "MS3-HUL1-0712",
          "subscriberServiceId": "SVC-100413"
        }
      },
      "IsisNeighbor": {
        "type": "object",
        "properties": {
          "systemId": {
            "type": "string"
          },
          "hostname": {
            "type": "string",
            "nullable": true
          },
          "interface": {
            "type": "string"
          },
          "level": {
            "type": "string"
          },
          "state": {
            "type": "string"
          },
          "holdTimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "snpa": {
            "type": "string",
            "nullable": true
          }
        },
        "example": {
          "systemId": "0102.5525.5002",
          "hostname": "pe1.lon1",
          "interface": "core0",
          "level": "L2",
          "state": "Up",
          "holdTimeSeconds": 27,
          "snpa": "a4:2b:8c:11:04:02"
        }
      },
      "IsisNeighbors": {
        "type": "object",
        "properties": {
          "reachable": {
            "type": "boolean"
          },
          "neighbors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/IsisNeighbor"
            }
          }
        },
        "description": "GET /api/v1/routing/isis/neighbors.",
        "example": {
          "reachable": true,
          "neighbors": [
            {
              "systemId": "0102.5525.5002",
              "hostname": "pe1.lon1",
              "interface": "core0",
              "level": "L2",
              "state": "Up",
              "holdTimeSeconds": 27,
              "snpa": "a4:2b:8c:11:04:02"
            }
          ]
        }
      },
      "JsonElement": { },
      "KeyedListOp": {
        "required": [
          "listPath",
          "key",
          "delete",
          "fields"
        ],
        "type": "object",
        "properties": {
          "listPath": {
            "type": "string",
            "description": "The list's schema `path` (e.g. \"auth radius server\")."
          },
          "key": {
            "type": "string",
            "description": "The item key (e.g. the server host). Created if it doesn't exist."
          },
          "delete": {
            "type": "boolean",
            "description": "true deletes the item (Fields is then ignored)."
          },
          "fields": {
            "type": "object",
            "additionalProperties": {
              "type": "string"
            },
            "description": "Child-field writes keyed by child path; same secret/list semantics\n            as top-level field writes.",
            "nullable": true
          }
        },
        "description": "One operation on a keyed configuration list.",
        "example": {
          "listPath": "auth radius server",
          "key": "10.20.2.31",
          "delete": false,
          "fields": {
            "auth radius server authPort": "1812",
            "auth radius server secret": "s3cr3t"
          }
        }
      },
      "KeyedListSchema": {
        "type": "object",
        "properties": {
          "path": {
            "type": "string",
            "description": "The list's full path — the `listPath` used in keyed-list operations."
          },
          "label": {
            "type": "string",
            "description": "Human-friendly list title (e.g. \"RADIUS Server\", \"Login User\")."
          },
          "keyName": {
            "type": "string",
            "description": "What the item key is (e.g. \"host\", \"name\")."
          },
          "keyLabel": {
            "type": "string",
            "description": "Display form of string KeyedListSchema.KeyName for the key column header. Supplied by the\n            server so every label in the editor comes from ONE acronym table — the client's own\n            humaniser has none, which is how \"Cpe GRE Circuit\" and \"Frr\" reached the page."
          },
          "description": {
            "type": "string",
            "description": "What the list configures (from the config mapping registry)."
          },
          "children": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldSchema"
            },
            "description": "Schema of each item's child fields."
          },
          "visibleWhen": {
            "oneOf": [
              {
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/VisibleWhenSchema"
              }
            ]
          }
        },
        "description": "A keyed configuration list (e.g. \"RADIUS servers\", keyed by host).",
        "example": {
          "path": "auth radius server",
          "label": "RADIUS Server",
          "keyName": "host",
          "keyLabel": "Host",
          "description": "RADIUS servers the authentication driver queries, in preference order.",
          "children": [
            {
              "path": "auth radius server authPort",
              "label": "Auth Port",
              "description": "UDP port for Access-Request.",
              "type": "Int",
              "list": false,
              "allowedValues": null,
              "allowedValueLabels": null,
              "secret": false,
              "visibleWhen": null
            }
          ],
          "visibleWhen": {
            "field": "auth driver",
            "equals": [
              "radius"
            ],
            "contains": false
          }
        }
      },
      "L2tpTunnel": {
        "type": "object",
        "properties": {
          "direction": {
            "type": "string",
            "description": "\"downstream\" (from a LAC, terminated here) or \"upstream\" (to a wholesale LNS)."
          },
          "localTunnelId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Tunnel id on this appliance — the key for the detail endpoint.",
            "format": "int32"
          },
          "peerTunnelId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Tunnel id on the peer.",
            "format": "int32"
          },
          "peerIp": {
            "type": "string",
            "description": "Peer tunnel endpoint IP, or null when unknown.",
            "nullable": true
          },
          "peerHostname": {
            "type": "string",
            "description": "The peer's L2TP Host Name AVP, or null when unknown.",
            "nullable": true
          },
          "peerVendor": {
            "type": "string",
            "description": "The peer's Vendor Name AVP (RFC 2661 §4.4.3) — the LAC/LNS software/vendor string.\n    Only the live manager captures it; null on session-derived rows unless in demo mode.",
            "nullable": true
          },
          "state": {
            "type": "string",
            "description": "Tunnel state: idle | wait-ctl | established | closed."
          },
          "sessions": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Subscriber sessions currently carried in the tunnel.",
            "format": "int32"
          }
        },
        "description": "One row of GET /api/v1/l2tp/tunnels. Direction: \"downstream\" (LNS: tunnel from a\n    LAC) or \"upstream\" (LAC: tunnel to a wholesale LNS).",
        "example": {
          "direction": "downstream",
          "localTunnelId": 5,
          "peerTunnelId": 41027,
          "peerIp": "203.0.113.10",
          "peerHostname": "lac1.wholesale.example.net",
          "peerVendor": "Cisco Systems, Inc.",
          "state": "established",
          "sessions": 303
        }
      },
      "L2tpTunnelDetail": {
        "type": "object",
        "properties": {
          "found": {
            "type": "boolean",
            "description": "Always true on a 200 (unknown tunnels return 404)."
          },
          "direction": {
            "type": "string",
            "description": "\"downstream\" (from a LAC, terminated here) or \"upstream\" (to a wholesale LNS)."
          },
          "localTunnelId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Tunnel id on this appliance.",
            "format": "int32"
          },
          "peerTunnelId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Tunnel id on the peer.",
            "format": "int32"
          },
          "peerIp": {
            "type": "string",
            "description": "Peer tunnel endpoint IP, or null when unknown.",
            "nullable": true
          },
          "peerPort": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Peer UDP port, or null when unknown.",
            "format": "int32",
            "nullable": true
          },
          "peerHostname": {
            "type": "string",
            "description": "The peer's L2TP Host Name AVP, or null when unknown.",
            "nullable": true
          },
          "peerVendor": {
            "type": "string",
            "description": "The peer's Vendor Name AVP, or null when unknown.",
            "nullable": true
          },
          "localEndpoint": {
            "type": "string",
            "description": "Local tunnel endpoint IP on this appliance, or null when unknown.",
            "nullable": true
          },
          "vrfName": {
            "type": "string",
            "description": "Transport VRF the tunnel rides in, or null for the global table.",
            "nullable": true
          },
          "state": {
            "type": "string",
            "description": "Tunnel state: idle | wait-ctl | established | closed."
          },
          "sessions": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Subscriber sessions currently carried in the tunnel.",
            "format": "int32"
          },
          "createdUtc": {
            "type": "string",
            "description": "UTC time the tunnel was established.",
            "format": "date-time",
            "nullable": true
          },
          "uptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "How long the tunnel has been up, in seconds (from DateTime? L2tpTunnelDetailDto.CreatedUtc to now),\n    or null when the establishment time is unknown.",
            "format": "double",
            "nullable": true
          },
          "lastActivityUtc": {
            "type": "string",
            "description": "UTC time of the last session activity seen in the tunnel.",
            "format": "date-time",
            "nullable": true
          },
          "sessionList": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/L2tpTunnelSession"
            },
            "description": "The subscriber sessions carried in the tunnel."
          }
        },
        "description": "GET /api/v1/l2tp/tunnels/{id} — full detail for one tunnel + its subscriber sessions.",
        "example": {
          "found": true,
          "direction": "downstream",
          "localTunnelId": 5,
          "peerTunnelId": 41027,
          "peerIp": "203.0.113.10",
          "peerPort": 1701,
          "peerHostname": "lac1.wholesale.example.net",
          "peerVendor": "Cisco Systems, Inc.",
          "localEndpoint": "45.84.200.69",
          "vrfName": "transit",
          "state": "established",
          "sessions": 2,
          "createdUtc": "2026-08-02T09:11:04Z",
          "uptimeSeconds": 90546.214773,
          "lastActivityUtc": "2026-08-03T10:23:59Z",
          "sessionList": [
            {
              "id": 40213,
              "username": "alice@example.net",
              "encap": "l2tp",
              "ipv4": "100.64.12.37",
              "ipv6": "2001:db8:64:12::1",
              "localSessionId": 118,
              "peerSessionId": 9,
              "uptimeSeconds": 86412.538912
            }
          ]
        }
      },
      "L2tpTunnelList": {
        "required": [
          "tunnels"
        ],
        "type": "object",
        "properties": {
          "tunnels": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/L2tpTunnel"
            },
            "description": "Live tunnels, downstream first, then by local tunnel id."
          }
        },
        "description": "GET /api/v1/l2tp/tunnels response.",
        "example": {
          "tunnels": [
            {
              "direction": "downstream",
              "localTunnelId": 5,
              "peerTunnelId": 41027,
              "peerIp": "203.0.113.10",
              "peerHostname": "lac1.wholesale.example.net",
              "peerVendor": "Cisco Systems, Inc.",
              "state": "established",
              "sessions": 303
            }
          ]
        }
      },
      "L2tpTunnelSession": {
        "required": [
          "id",
          "username",
          "encap",
          "ipv4",
          "ipv6",
          "localSessionId",
          "peerSessionId",
          "uptimeSeconds"
        ],
        "type": "object",
        "properties": {
          "id": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "BNG session id (key into /api/v1/sessions/{id}).",
            "format": "uint32"
          },
          "username": {
            "type": "string",
            "description": "Subscriber username, or null when unknown.",
            "nullable": true
          },
          "encap": {
            "type": "string",
            "description": "Access encapsulation (\"l2tp\" downstream, \"pppoe-lac\" upstream).",
            "nullable": true
          },
          "ipv4": {
            "type": "string",
            "description": "Assigned IPv4 address, or null.",
            "nullable": true
          },
          "ipv6": {
            "type": "string",
            "description": "IPv6 WAN address, or null.",
            "nullable": true
          },
          "localSessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "L2TP session id on this appliance.",
            "format": "int32"
          },
          "peerSessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "L2TP session id on the peer.",
            "format": "int32"
          },
          "uptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Seconds since the session was established.",
            "format": "double"
          }
        },
        "description": "One subscriber session inside an L2TP tunnel.",
        "example": {
          "id": 40213,
          "username": "alice@example.net",
          "encap": "l2tp",
          "ipv4": "100.64.12.37",
          "ipv6": "2001:db8:64:12::1",
          "localSessionId": 118,
          "peerSessionId": 9,
          "uptimeSeconds": 86412.538912
        }
      },
      "LicenseActivateResult": {
        "required": [
          "activated",
          "instanceId"
        ],
        "type": "object",
        "properties": {
          "activated": {
            "type": "boolean",
            "description": "Always true on a 200."
          },
          "instanceId": {
            "type": "string",
            "description": "The instance id assigned by the licensing service.",
            "nullable": true
          }
        },
        "description": "POST /api/v1/licensing/activate result.",
        "example": {
          "activated": true,
          "instanceId": "6f2c0a3e-8b41-4d92-9f0e-1c7d5a83b204"
        }
      },
      "LicenseOfflineResult": {
        "required": [
          "applied",
          "state"
        ],
        "type": "object",
        "properties": {
          "applied": {
            "type": "boolean",
            "description": "Always true on a 200."
          },
          "state": {
            "type": "string",
            "description": "Licence state after applying the offline lease."
          }
        },
        "description": "POST /api/v1/licensing/load-offline result.",
        "example": {
          "applied": true,
          "state": "Licensed"
        }
      },
      "LicenseRenewResult": {
        "required": [
          "renewed",
          "state",
          "expiresAtUtc",
          "daysRemaining"
        ],
        "type": "object",
        "properties": {
          "renewed": {
            "type": "boolean",
            "description": "Always true on a 200."
          },
          "state": {
            "type": "string",
            "description": "Licence state after the renewal."
          },
          "expiresAtUtc": {
            "type": "string",
            "description": "New lease expiry, or null.",
            "format": "date-time",
            "nullable": true
          },
          "daysRemaining": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Days until the new expiry, or null.",
            "format": "int32",
            "nullable": true
          }
        },
        "description": "POST /api/v1/licensing/renew-now result.",
        "example": {
          "renewed": true,
          "state": "Licensed",
          "expiresAtUtc": "2026-09-02T10:24:00Z",
          "daysRemaining": 30
        }
      },
      "LicensingStatus": {
        "required": [
          "state",
          "mode",
          "role",
          "licenceId",
          "instanceId",
          "bandName",
          "meter",
          "bandMax",
          "currentCount",
          "peakCount",
          "expiresAtUtc",
          "contractEndUtc",
          "lastRenewalUtc",
          "daysRemaining",
          "adminFreezeActive",
          "isUnlicensed",
          "isEnrolled"
        ],
        "type": "object",
        "properties": {
          "state": {
            "type": "string",
            "description": "Licence state machine value (e.g. \"Dev\", \"Licensed\", \"Grace\", \"Lapsed\")."
          },
          "mode": {
            "type": "string",
            "description": "Licensing mode configured on the box (e.g. \"dev\", \"online\", \"offline\")."
          },
          "role": {
            "type": "string",
            "description": "Licensed product role of the appliance."
          },
          "licenceId": {
            "type": "string",
            "description": "The licence this box is enrolled under, or null when unenrolled.",
            "nullable": true
          },
          "instanceId": {
            "type": "string",
            "description": "This appliance's instance id under the licence, or null.",
            "nullable": true
          },
          "bandName": {
            "type": "string",
            "description": "Capacity band name."
          },
          "meter": {
            "type": "string",
            "description": "What the band meters (e.g. sessions)."
          },
          "bandMax": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Metered cap of the band.",
            "format": "int64"
          },
          "currentCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Current metered count.",
            "format": "int64"
          },
          "peakCount": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Peak metered count in the current period.",
            "format": "int64"
          },
          "expiresAtUtc": {
            "type": "string",
            "description": "Lease expiry, or null.",
            "format": "date-time",
            "nullable": true
          },
          "contractEndUtc": {
            "type": "string",
            "description": "Contract end date, or null.",
            "format": "date-time",
            "nullable": true
          },
          "lastRenewalUtc": {
            "type": "string",
            "description": "Last successful renewal, or null.",
            "format": "date-time",
            "nullable": true
          },
          "daysRemaining": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Days until lease expiry, or null.",
            "format": "int32",
            "nullable": true
          },
          "adminFreezeActive": {
            "type": "boolean",
            "description": "True when a lapsed licence freezes config commits."
          },
          "isUnlicensed": {
            "type": "boolean",
            "description": "True when the appliance has no usable licence."
          },
          "isEnrolled": {
            "type": "boolean",
            "description": "True when the appliance is enrolled with the licensing service."
          }
        },
        "description": "GET /api/v1/licensing — the appliance's licence status.",
        "example": {
          "state": "Licensed",
          "mode": "online",
          "role": "bng",
          "licenceId": "LIC-2026-0042",
          "instanceId": "6f2c0a3e-8b41-4d92-9f0e-1c7d5a83b204",
          "bandName": "10k",
          "meter": "sessions",
          "bandMax": 10000,
          "currentCount": 1842,
          "peakCount": 2104,
          "expiresAtUtc": "2026-09-02T10:24:00Z",
          "contractEndUtc": "2027-07-31T00:00:00Z",
          "lastRenewalUtc": "2026-08-03T04:12:44Z",
          "daysRemaining": 30,
          "adminFreezeActive": false,
          "isUnlicensed": false,
          "isEnrolled": true
        }
      },
      "LogEntry": {
        "required": [
          "seq",
          "timestamp",
          "severity",
          "msgId",
          "fields"
        ],
        "type": "object",
        "properties": {
          "seq": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Monotonic sequence number within this control-plane process.",
            "format": "int64"
          },
          "timestamp": {
            "type": "string",
            "description": "UTC time the event was logged.",
            "format": "date-time"
          },
          "severity": {
            "type": "string",
            "description": "Syslog severity name (e.g. \"notice\", \"warning\")."
          },
          "msgId": {
            "type": "string",
            "description": "Stable machine-readable message id (e.g. \"SESSION_UP\")."
          },
          "fields": {
            "type": "object",
            "description": "Structured event fields; keys vary by message id."
          }
        },
        "description": "One appliance log entry (the JSON body of the RFC 5424 syslog event).",
        "example": {
          "seq": 184213,
          "timestamp": "2026-08-03T10:24:07.418Z",
          "severity": "notice",
          "msgId": "SESSION_UP",
          "fields": {
            "sessionId": 40213,
            "username": "alice@example.net",
            "vrf": "residential",
            "framedIp": "100.64.12.37"
          }
        }
      },
      "LoginRequest": {
        "required": [
          "username",
          "password",
          "totpCode"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "Account name (shared with the appliance CLI's login.users)."
          },
          "password": {
            "type": "string",
            "description": "Account password."
          },
          "totpCode": {
            "type": "string",
            "description": "Current 6-digit TOTP code; required once the account is enrolled\n            (omit on the first attempt — the response says whether a code is needed).",
            "nullable": true
          }
        },
        "description": "POST /api/v1/auth/login body.",
        "example": {
          "username": "noc-alice",
          "password": "correct horse battery staple",
          "totpCode": "418244"
        }
      },
      "LoginResponse": {
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "\"success\" (logged in, cookie set), \"totpCodeRequired\" (retry login with a code),\n            or \"totpEnrolmentRequired\" (enrol via POST /api/v1/auth/enrol)."
          },
          "username": {
            "type": "string",
            "description": "Logged-in username (status \"success\").",
            "nullable": true
          },
          "role": {
            "type": "string",
            "description": "Effective role: \"read-only\", \"operator\" or \"admin\" (status \"success\").",
            "nullable": true
          },
          "csrfToken": {
            "type": "string",
            "description": "CSRF token to send in X-CSRF-Token on cookie-authenticated mutations (status \"success\").",
            "nullable": true
          },
          "enrolmentToken": {
            "type": "string",
            "description": "Short-lived token identifying this enrolment (status \"totpEnrolmentRequired\");\n            echo it in POST /api/v1/auth/enrol.",
            "nullable": true
          },
          "secret": {
            "type": "string",
            "description": "Base32 TOTP secret to load into an authenticator app (status \"totpEnrolmentRequired\").",
            "nullable": true
          },
          "otpauthUri": {
            "type": "string",
            "description": "otpauth:// URI for QR-code enrolment (status \"totpEnrolmentRequired\").",
            "nullable": true
          }
        },
        "description": "POST /api/v1/auth/login and /enrol response. Which fields are present depends on\n            string LoginResponseDto.Status; absent fields are omitted from the JSON.",
        "example": {
          "status": "success",
          "username": "noc-alice",
          "role": "operator",
          "csrfToken": "kR3s9Xq1TfL8vB2wYc7Nd0",
          "enrolmentToken": null,
          "secret": null,
          "otpauthUri": null
        }
      },
      "LogTail": {
        "required": [
          "entries",
          "lastSeq"
        ],
        "type": "object",
        "properties": {
          "entries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LogEntry"
            },
            "description": "The requested log entries, oldest first."
          },
          "lastSeq": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Sequence number of the newest entry the buffer has seen — pass it as\n            `sinceSeq` on the next poll to fetch only newer entries.",
            "format": "int64"
          }
        },
        "description": "GET /api/v1/logs/tail response.",
        "example": {
          "entries": [
            {
              "seq": 184213,
              "timestamp": "2026-08-03T10:24:07.418Z",
              "severity": "notice",
              "msgId": "SESSION_UP",
              "fields": {
                "sessionId": 40213,
                "username": "alice@example.net",
                "vrf": "residential",
                "framedIp": "100.64.12.37"
              }
            }
          ],
          "lastSeq": 184213
        }
      },
      "MemoryInfo": {
        "type": "object",
        "properties": {
          "totalBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "numaNodes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/NumaNodeMemory"
            }
          },
          "hugepages": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/HugepagePool"
            }
          }
        },
        "example": {
          "totalBytes": 135223480320,
          "numaNodes": [
            {
              "node": 0,
              "totalBytes": 67611740160
            },
            {
              "node": 1,
              "totalBytes": 67611740160
            }
          ],
          "hugepages": [
            {
              "sizeKb": 1048576,
              "total": 32,
              "free": 8
            }
          ]
        }
      },
      "MetricsByEncap": {
        "required": [
          "ipoe",
          "pppoeLocal",
          "pppoeLac",
          "l2tp"
        ],
        "type": "object",
        "properties": {
          "ipoe": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "IPoE (DHCP) sessions."
          },
          "pppoeLocal": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "Locally-terminated PPPoE sessions."
          },
          "pppoeLac": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "PPPoE sessions tunnelled to a wholesale LNS (LAC role)."
          },
          "l2tp": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "LNS-terminated sessions arriving from LACs over L2TP."
          }
        },
        "description": "Session-count series per access encapsulation (aligned to the shared times axis).",
        "example": {
          "ipoe": [
            210,
            210,
            211
          ],
          "pppoeLocal": [
            1418,
            1419,
            1419
          ],
          "pppoeLac": [
            96,
            96,
            96
          ],
          "l2tp": [
            116,
            116,
            116
          ]
        }
      },
      "MetricsHistory": {
        "required": [
          "intervalSeconds",
          "times",
          "totalSessions",
          "byEncap",
          "sessionsUp",
          "sessionsDown",
          "ports"
        ],
        "type": "object",
        "properties": {
          "intervalSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Native sampling interval (seconds); longer ranges are downsampled.",
            "format": "int32"
          },
          "times": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            },
            "description": "UTC sample timestamps; every other array aligns to this axis."
          },
          "totalSessions": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "Active session count per sample."
          },
          "byEncap": {
            "$ref": "#/components/schemas/MetricsByEncap"
          },
          "sessionsUp": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "Sessions established during each interval."
          },
          "sessionsDown": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "Sessions torn down during each interval."
          },
          "ports": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PortMetrics"
            },
            "description": "Per-port RX/TX throughput series."
          }
        },
        "description": "GET /api/v1/metrics/history — parallel series keyed by the shared\n    List&lt;DateTime&gt; MetricsHistoryDto.Times axis.",
        "example": {
          "intervalSeconds": 10,
          "times": [
            "2026-08-03T10:23:40Z",
            "2026-08-03T10:23:50Z",
            "2026-08-03T10:24:00Z"
          ],
          "totalSessions": [
            1840,
            1841,
            1842
          ],
          "byEncap": {
            "ipoe": [
              210,
              210,
              211
            ],
            "pppoeLocal": [
              1418,
              1419,
              1419
            ],
            "pppoeLac": [
              96,
              96,
              96
            ],
            "l2tp": [
              116,
              116,
              116
            ]
          },
          "sessionsUp": [
            3,
            2,
            4
          ],
          "sessionsDown": [
            1,
            1,
            3
          ],
          "ports": [
            {
              "portId": 0,
              "label": "MS3 NNI 2",
              "rxBps": [
                4182946816.0,
                4190338048.0,
                4176510976.0
              ],
              "txBps": [
                812304384.0,
                818941952.0,
                809467904.0
              ]
            }
          ]
        }
      },
      "Nic": {
        "type": "object",
        "properties": {
          "pciAddress": {
            "type": "string"
          },
          "vendorId": {
            "type": "string",
            "description": "Raw PCI vendor id, e.g. \"0x8086\".",
            "nullable": true
          },
          "deviceId": {
            "type": "string",
            "description": "Raw PCI device id, e.g. \"0x1572\".",
            "nullable": true
          },
          "vendorName": {
            "type": "string",
            "description": "Friendly vendor name resolved from pci.ids / built-ins, e.g. \"Intel Corporation\".",
            "nullable": true
          },
          "model": {
            "type": "string",
            "description": "Friendly device/chipset model, e.g. \"Ethernet Controller X710 for 10GbE SFP+\".",
            "nullable": true
          },
          "driver": {
            "type": "string",
            "description": "Kernel driver in use (\"vfio-pci\" = DPDK-bound).",
            "nullable": true
          },
          "numaNode": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "kernelInterface": {
            "type": "string",
            "description": "Kernel netdev name bound to this PCI function, if any.",
            "nullable": true
          }
        },
        "description": "One network-class (0x02xxxx) PCI device.",
        "example": {
          "pciAddress": "0000:5e:00.0",
          "vendorId": "0x8086",
          "deviceId": "0x1572",
          "vendorName": "Intel Corporation",
          "model": "Ethernet Controller X710 for 10GbE SFP+",
          "driver": "vfio-pci",
          "numaNode": 0,
          "kernelInterface": null
        }
      },
      "NumaNodeMemory": {
        "type": "object",
        "properties": {
          "node": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "totalBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          }
        },
        "example": {
          "node": 0,
          "totalBytes": 67611740160
        }
      },
      "OidcProvider": {
        "required": [
          "name",
          "displayName"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Provider id used in the login URL (/api/v1/auth/oidc/{name}/login)."
          },
          "displayName": {
            "type": "string",
            "description": "Button label (\"Sign in with Entra ID\")."
          }
        },
        "description": "One enabled OIDC provider for the login page.",
        "example": {
          "name": "entra",
          "displayName": "Sign in with Entra ID"
        }
      },
      "OidcProviderAdmin": {
        "required": [
          "name",
          "enabled",
          "displayName",
          "issuer",
          "clientId",
          "scopes",
          "usernameClaim",
          "groupsClaim",
          "fetchUserInfo",
          "redirectUri",
          "roleMap",
          "hasClientSecret"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "displayName": {
            "type": "string"
          },
          "issuer": {
            "type": "string"
          },
          "clientId": {
            "type": "string"
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "usernameClaim": {
            "type": "string"
          },
          "groupsClaim": {
            "type": "string"
          },
          "fetchUserInfo": {
            "type": "boolean"
          },
          "redirectUri": {
            "type": "string"
          },
          "roleMap": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthRoleRule"
            }
          },
          "hasClientSecret": {
            "type": "boolean"
          }
        },
        "description": "Admin view of an OIDC provider — no client secret, just whether one is set.",
        "example": {
          "name": "entra",
          "enabled": true,
          "displayName": "Sign in with Entra ID",
          "issuer": "https://login.microsoftonline.com/9f3c1b8e-4d27-4a51-b0e6-52f7c0a91d34/v2.0",
          "clientId": "1c7d5a83-b204-4f0e-9d92-8b416f2c0a3e",
          "scopes": [
            "openid",
            "profile",
            "email"
          ],
          "usernameClaim": "preferred_username",
          "groupsClaim": "groups",
          "fetchUserInfo": false,
          "redirectUri": "https://lns1.hul1.example.net:8443/api/v1/auth/oidc/entra/callback",
          "roleMap": [
            {
              "match": "bng-admins",
              "role": "admin"
            },
            {
              "match": "bng-noc",
              "role": "operator"
            }
          ],
          "hasClientSecret": true
        }
      },
      "OspfNeighbor": {
        "type": "object",
        "properties": {
          "routerId": {
            "type": "string",
            "description": "The neighbour's OSPF router-id (a dotted quad in both v2 and v3)."
          },
          "interface": {
            "type": "string"
          },
          "state": {
            "type": "string",
            "description": "FRR's adjacency state, including the DR/BDR role where it applies (e.g. \"Full/DR\").\n            On the point-to-point core links the appliance configures, this is normally \"Full/-\"."
          },
          "priority": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "deadTimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "address": {
            "type": "string",
            "description": "The neighbour's interface address (v2) or link-local address (v3).",
            "nullable": true
          },
          "protocol": {
            "type": "string",
            "description": "\"ospfv2\" or \"ospfv3\"."
          }
        },
        "description": "One OSPF adjacency. Unlike IS-IS — which carries both address families over a single\n            adjacency via multi-topology — OSPFv2 and OSPFv3 form separate adjacencies, so the same\n            neighbour legitimately appears twice on a dual-stack box, once per protocol.",
        "example": {
          "routerId": "10.255.255.2",
          "interface": "core0",
          "state": "Full/-",
          "priority": 1,
          "deadTimeSeconds": 33,
          "address": "10.255.1.1",
          "protocol": "ospfv2"
        }
      },
      "OspfNeighbors": {
        "type": "object",
        "properties": {
          "reachable": {
            "type": "boolean"
          },
          "neighbors": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OspfNeighbor"
            }
          }
        },
        "description": "GET /api/v1/routing/ospf/neighbors.",
        "example": {
          "reachable": true,
          "neighbors": [
            {
              "routerId": "10.255.255.2",
              "interface": "core0",
              "state": "Full/-",
              "priority": 1,
              "deadTimeSeconds": 33,
              "address": "10.255.1.1",
              "protocol": "ospfv2"
            }
          ]
        }
      },
      "PasskeyBeginResponse": {
        "required": [
          "id",
          "options"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "Short-lived ceremony id; echo it on the matching `finish`."
          },
          "options": {
            "$ref": "#/components/schemas/JsonElement"
          }
        },
        "description": "Result of a passkey `begin` ceremony."
      },
      "PasskeyCredential": {
        "required": [
          "credentialId",
          "label",
          "aaguid",
          "createdUtc",
          "transports"
        ],
        "type": "object",
        "properties": {
          "credentialId": {
            "type": "string",
            "description": "base64url credential id."
          },
          "label": {
            "type": "string",
            "description": "Operator-facing label, if set.",
            "nullable": true
          },
          "aaguid": {
            "type": "string",
            "description": "Authenticator model id (AAGUID), if reported.",
            "nullable": true
          },
          "createdUtc": {
            "type": "string",
            "description": "When the passkey was registered.",
            "format": "date-time"
          },
          "transports": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Reported transports (usb, nfc, ble, internal, hybrid)."
          }
        },
        "description": "One registered passkey (no secret material).",
        "example": {
          "credentialId": "AaR1bHNfa2V5X2lkXzAwMQ",
          "label": "noc-alice YubiKey 5C",
          "aaguid": "ee882879-721c-4913-9775-3dfcce97072a",
          "createdUtc": "2026-08-03T10:24:00Z",
          "transports": [
            "usb",
            "nfc"
          ]
        }
      },
      "PasskeyLoginBeginRequest": {
        "required": [
          "username"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "Optional account name to scope the credential allow-list; omit for\n    username-less (resident-key) login.",
            "nullable": true
          }
        },
        "description": "POST body for `login/begin`.",
        "example": {
          "username": "noc-alice"
        }
      },
      "PasskeyLoginFinishRequest": {
        "required": [
          "id",
          "response"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The ceremony id returned by `login/begin`."
          },
          "response": {
            "$ref": "#/components/schemas/JsonElement"
          }
        },
        "description": "POST body for `login/finish`."
      },
      "PasskeyPolicy": {
        "required": [
          "mode"
        ],
        "type": "object",
        "properties": {
          "mode": {
            "type": "string"
          }
        },
        "description": "Passkey policy: off | secondFactor | passwordless.",
        "example": {
          "mode": "secondFactor"
        }
      },
      "PasskeyRegisterFinishRequest": {
        "required": [
          "id",
          "response",
          "label"
        ],
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "description": "The ceremony id returned by `register/begin`."
          },
          "response": {
            "$ref": "#/components/schemas/JsonElement"
          },
          "label": {
            "type": "string",
            "description": "Optional operator-facing label (\"Chris's YubiKey\").",
            "nullable": true
          }
        },
        "description": "POST body for `register/finish`."
      },
      "Peer": {
        "required": [
          "address",
          "description",
          "vpnOnly",
          "bfd",
          "comment"
        ],
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "description": "Peer address — the immutable key (ignored in the body on update)."
          },
          "description": {
            "type": "string",
            "description": "Free-text description shown in status views.",
            "nullable": true
          },
          "vpnOnly": {
            "type": "boolean",
            "description": "True = activate only the VPN address families toward this peer\n            (route-reflector style), not ipv4 unicast."
          },
          "bfd": {
            "type": "boolean",
            "description": "Enable BFD fast failure detection toward this peer."
          },
          "comment": {
            "type": "string",
            "description": "Optional commit comment for the config history.",
            "nullable": true
          }
        },
        "description": "BGP peer body for create/update.",
        "example": {
          "address": "10.255.255.2",
          "description": "pe1.lon1 route reflector",
          "vpnOnly": true,
          "bfd": true,
          "comment": "add the lon1 route reflector"
        }
      },
      "PingProbe": {
        "required": [
          "sequence",
          "rttMs",
          "ttl"
        ],
        "type": "object",
        "properties": {
          "sequence": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "1-based sequence number.",
            "format": "int32"
          },
          "rttMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Round-trip time in milliseconds, or null if nothing returned.",
            "format": "double",
            "nullable": true
          },
          "ttl": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "TTL / hop limit the reply carried, or null.",
            "format": "int32",
            "nullable": true
          }
        },
        "description": "One echo probe.",
        "example": {
          "sequence": 1,
          "rttMs": 1.24,
          "ttl": 62
        }
      },
      "PingRequest": {
        "required": [
          "destination",
          "vrf",
          "source",
          "count",
          "size",
          "dontFragment",
          "ttl",
          "timeoutMs"
        ],
        "type": "object",
        "properties": {
          "destination": {
            "type": "string",
            "description": "Address to ping, IPv4 or IPv6."
          },
          "vrf": {
            "type": "string",
            "description": "VRF to send from; omit for the default table.",
            "nullable": true
          },
          "source": {
            "type": "string",
            "description": "Address to source from; defaults to that VRF's configured gateway.",
            "nullable": true
          },
          "count": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Number of echoes (capped).",
            "format": "int32",
            "nullable": true
          },
          "size": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "ICMP payload bytes (capped).",
            "format": "int32",
            "nullable": true
          },
          "dontFragment": {
            "type": "boolean",
            "description": "Set DF. Ignored for IPv6, which never fragments in transit.",
            "nullable": true
          },
          "ttl": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "TTL / hop limit.",
            "format": "int32",
            "nullable": true
          },
          "timeoutMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Per-probe wait.",
            "format": "int32",
            "nullable": true
          }
        },
        "description": "POST /api/v1/diagnostics/ping body.",
        "example": {
          "destination": "192.0.2.50",
          "vrf": "cust-alt-jat",
          "source": null,
          "count": 5,
          "size": 56,
          "dontFragment": false,
          "ttl": 64,
          "timeoutMs": 1000
        }
      },
      "PingResult": {
        "required": [
          "destination",
          "source",
          "vrf",
          "sent",
          "received",
          "minMs",
          "avgMs",
          "maxMs",
          "probes"
        ],
        "type": "object",
        "properties": {
          "destination": {
            "type": "string",
            "description": "Address pinged."
          },
          "source": {
            "type": "string",
            "description": "Address actually sourced from — worth reporting, since a VRF gateway in\n    documentation space may never see a reply."
          },
          "vrf": {
            "type": "string",
            "description": "VRF used, or null for the default table.",
            "nullable": true
          },
          "sent": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Echoes sent.",
            "format": "int32"
          },
          "received": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Replies matched.",
            "format": "int32"
          },
          "minMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Fastest round trip.",
            "format": "double",
            "nullable": true
          },
          "avgMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Mean round trip.",
            "format": "double",
            "nullable": true
          },
          "maxMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Slowest round trip.",
            "format": "double",
            "nullable": true
          },
          "probes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PingProbe"
            },
            "description": "Per-probe detail."
          }
        },
        "description": "The result of a ping run.",
        "example": {
          "destination": "192.0.2.50",
          "source": "10.254.0.14",
          "vrf": "cust-alt-jat",
          "sent": 5,
          "received": 5,
          "minMs": 0.9,
          "avgMs": 1.2,
          "maxMs": 1.8,
          "probes": [
            {
              "sequence": 1,
              "rttMs": 1.24,
              "ttl": 62
            }
          ]
        }
      },
      "PortMetrics": {
        "required": [
          "portId",
          "label",
          "rxBps",
          "txBps"
        ],
        "type": "object",
        "properties": {
          "portId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Data-plane port id (subscriber interfaces first, then network interfaces).",
            "format": "int32"
          },
          "label": {
            "type": "string",
            "description": "Operator-friendly label from config (label/ifname/PCI)."
          },
          "rxBps": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
              "format": "double"
            },
            "description": "Received bits/second per sample."
          },
          "txBps": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
              "format": "double"
            },
            "description": "Transmitted bits/second per sample."
          }
        },
        "description": "Throughput series for one data-plane port.",
        "example": {
          "portId": 0,
          "label": "MS3 NNI 2",
          "rxBps": [
            4182946816.0,
            4190338048.0,
            4176510976.0
          ],
          "txBps": [
            812304384.0,
            818941952.0,
            809467904.0
          ]
        }
      },
      "PortSessionCount": {
        "required": [
          "portId",
          "label",
          "count"
        ],
        "type": "object",
        "properties": {
          "portId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Data-plane port id (subscriber interfaces first, then network interfaces).",
            "format": "uint8"
          },
          "label": {
            "type": "string",
            "description": "Operator-friendly port label from config (label/ifname/PCI), or null.",
            "nullable": true
          },
          "count": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Active sessions on this port.",
            "format": "int32"
          }
        },
        "description": "Per-port session count row of the sessions summary.",
        "example": {
          "portId": 0,
          "label": "xe-0/0/0",
          "count": 1503
        }
      },
      "PppoeSubscriberWrite": {
        "type": "object",
        "properties": {
          "tunnelEndpoints": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/TunnelEndpointWrite"
            },
            "nullable": true
          },
          "port": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint8"
          },
          "vlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint16"
          },
          "sVlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint16"
          },
          "option82CircuitId": {
            "type": "string",
            "nullable": true
          },
          "subscriberServiceId": {
            "type": "string",
            "nullable": true
          }
        },
        "description": "Create/update body for a wholesale (L2TP-relayed) PPPoE subscriber.",
        "example": {
          "tunnelEndpoints": [
            {
              "address": "45.84.200.80",
              "port": 1701,
              "vrfName": "wholesale-a",
              "sharedSecret": "correct horse battery staple",
              "assignmentId": "acme-ltd",
              "priority": 1
            }
          ],
          "port": 0,
          "vlan": 713,
          "sVlan": 101,
          "option82CircuitId": "MS3-HUL1-0713",
          "subscriberServiceId": "SVC-100414"
        }
      },
      "PppSubscriberWrite": {
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "nullable": true
          },
          "password": {
            "type": "string",
            "nullable": true
          },
          "subscriberServiceId": {
            "type": "string",
            "nullable": true
          },
          "vrfName": {
            "type": "string",
            "nullable": true
          },
          "framedIp": {
            "type": "string",
            "nullable": true
          },
          "framedIpv6": {
            "type": "string",
            "nullable": true
          },
          "ipv6PdPrefix": {
            "type": "string",
            "nullable": true
          },
          "ipv6PdLength": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint8",
            "nullable": true
          },
          "dns1": {
            "type": "string",
            "nullable": true
          },
          "dns2": {
            "type": "string",
            "nullable": true
          },
          "dnsV6_1": {
            "type": "string",
            "nullable": true
          },
          "dnsV6_2": {
            "type": "string",
            "nullable": true
          },
          "planName": {
            "type": "string",
            "nullable": true
          },
          "downloadRateKbps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "uploadRateKbps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "downloadRatePps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "uploadRatePps": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "additionalRoutes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "nullable": true
          }
        },
        "description": "Create/update body for a PPP (username/password) subscriber.\n    On update, a null/empty password keeps the stored one.",
        "example": {
          "username": "alice@example.net",
          "password": "correct horse battery staple",
          "subscriberServiceId": "SVC-100412",
          "vrfName": "residential",
          "framedIp": "100.64.12.37",
          "framedIpv6": "2001:db8:64:12::25",
          "ipv6PdPrefix": "2001:db8:a012:3400::",
          "ipv6PdLength": 56,
          "dns1": "1.1.1.1",
          "dns2": "1.0.0.1",
          "dnsV6_1": "2606:4700:4700::1111",
          "dnsV6_2": "2606:4700:4700::1001",
          "planName": "Fibre 500",
          "downloadRateKbps": 500000,
          "uploadRateKbps": 100000,
          "downloadRatePps": 0,
          "uploadRatePps": 0,
          "additionalRoutes": [
            "192.0.2.64/28"
          ]
        }
      },
      "ProblemDetails": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "nullable": true
          },
          "title": {
            "type": "string",
            "nullable": true
          },
          "status": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "detail": {
            "type": "string",
            "nullable": true
          },
          "instance": {
            "type": "string",
            "nullable": true
          }
        },
        "example": {
          "type": "https://tools.ietf.org/html/rfc9110#section-15.5.5",
          "title": "VRF not found",
          "status": 404,
          "detail": "No VRF named 'wholesale-b' is configured.",
          "instance": "/api/v1/vrfs/wholesale-b"
        }
      },
      "ProductInfo": {
        "required": [
          "product",
          "version",
          "hostname",
          "features"
        ],
        "type": "object",
        "properties": {
          "product": {
            "type": "string",
            "description": "Package identity: `netavo-bng` or `netavo-vpe`."
          },
          "version": {
            "type": "string",
            "description": "Running software version."
          },
          "hostname": {
            "type": "string",
            "description": "Configured hostname."
          },
          "features": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Capability tags the client may branch on, e.g. `gre-circuits`."
          }
        },
        "description": "What product a management API belongs to, and what it can serve.\n\nBoth hosts answer `GET /api/v1/system/info` with this, so a client discovers what it is\ntalking to POSITIVELY — rather than inferring it from which endpoints 404, which is how a UI\nends up showing an operator a page that cannot work. The shared web UI uses it to decide which\nnavigation sections apply.",
        "example": {
          "product": "netavo-vpe",
          "version": "1.0.0",
          "hostname": "vpe-cpe1",
          "features": [
            "routing",
            "vrfs",
            "gre-circuits",
            "configuration",
            "logs"
          ]
        }
      },
      "ProgrammedSourceVrfRule": {
        "required": [
          "prefix",
          "ingestVrfId",
          "targetVrfId",
          "hits"
        ],
        "type": "object",
        "properties": {
          "prefix": {
            "type": "string"
          },
          "ingestVrfId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint16"
          },
          "targetVrfId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint16"
          },
          "hits": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint64"
          }
        },
        "description": "One row of GET /api/v1/source-vrf-rules/programmed-rules."
      },
      "RadiusServerHealth": {
        "required": [
          "host",
          "port",
          "weight",
          "isDead",
          "consecutiveFailures",
          "requestsSent",
          "lastFailure",
          "lastSuccess"
        ],
        "type": "object",
        "properties": {
          "host": {
            "type": "string",
            "description": "Server host (IP) as configured."
          },
          "port": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Authentication UDP port.",
            "format": "int32"
          },
          "weight": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Load-balancing weight.",
            "format": "int32"
          },
          "isDead": {
            "type": "boolean",
            "description": "True while the server is marked dead and skipped for new requests."
          },
          "consecutiveFailures": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Consecutive request failures since the last success.",
            "format": "int32"
          },
          "requestsSent": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Total requests sent to this server.",
            "format": "int64"
          },
          "lastFailure": {
            "type": "string",
            "description": "UTC time of the last failed request, or null.",
            "format": "date-time",
            "nullable": true
          },
          "lastSuccess": {
            "type": "string",
            "description": "UTC time of the last successful reply, or null.",
            "format": "date-time",
            "nullable": true
          }
        },
        "description": "Health of one configured RADIUS server.",
        "example": {
          "host": "10.20.2.20",
          "port": 1812,
          "weight": 10,
          "isDead": false,
          "consecutiveFailures": 0,
          "requestsSent": 184203,
          "lastFailure": null,
          "lastSuccess": "2026-08-03T10:24:09Z"
        }
      },
      "RadiusServers": {
        "required": [
          "configured",
          "totalServers",
          "liveServers",
          "deadServers",
          "servers"
        ],
        "type": "object",
        "properties": {
          "configured": {
            "type": "boolean",
            "description": "False when no RADIUS auth driver is running (servers then empty)."
          },
          "totalServers": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Configured servers in the pool.",
            "format": "int32"
          },
          "liveServers": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Servers currently considered live.",
            "format": "int32"
          },
          "deadServers": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Servers currently marked dead (skipped for new requests).",
            "format": "int32"
          },
          "servers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/RadiusServerHealth"
            },
            "description": "Per-server health rows."
          }
        },
        "description": "GET /api/v1/radius/servers — pool summary plus per-server health.",
        "example": {
          "configured": true,
          "totalServers": 2,
          "liveServers": 2,
          "deadServers": 0,
          "servers": [
            {
              "host": "10.20.2.20",
              "port": 1812,
              "weight": 10,
              "isDead": false,
              "consecutiveFailures": 0,
              "requestsSent": 184203,
              "lastFailure": null,
              "lastSuccess": "2026-08-03T10:24:09Z"
            }
          ]
        }
      },
      "RestartResult": {
        "required": [
          "status",
          "message"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"restarting\" — the request was accepted."
          },
          "message": {
            "type": "string",
            "description": "Human-readable detail for display."
          }
        },
        "description": "POST /api/v1/system/restart result.",
        "example": {
          "status": "restarting",
          "message": "Restarting the control plane and data plane now. Active sessions will drop and re-establish automatically."
        }
      },
      "RollbackRequest": {
        "required": [
          "version"
        ],
        "type": "object",
        "properties": {
          "version": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The history version number to restore.",
            "format": "int32"
          }
        },
        "description": "POST /api/v1/config/rollback body.",
        "example": {
          "version": 41
        }
      },
      "Route": {
        "required": [
          "prefix"
        ],
        "type": "object",
        "properties": {
          "prefix": {
            "type": "string"
          },
          "nextHop": {
            "type": "string",
            "nullable": true
          },
          "protocol": {
            "type": "string",
            "nullable": true
          },
          "interface": {
            "type": "string",
            "nullable": true
          },
          "metric": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "localPref": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32",
            "nullable": true
          },
          "asPath": {
            "type": "string",
            "nullable": true
          },
          "origin": {
            "type": "string",
            "nullable": true
          },
          "best": {
            "type": "boolean",
            "nullable": true
          }
        },
        "description": "One route row. Fields are optional because they differ between the VRF FIB view\n            (protocol/interface/metric) and the BGP Adj-RIB view (as-path/local-pref/best).",
        "example": {
          "prefix": "100.64.12.0/24",
          "nextHop": "10.255.1.1",
          "protocol": "bgp",
          "interface": "core0",
          "metric": 0,
          "localPref": 100,
          "asPath": "65001 64512",
          "origin": "IGP",
          "best": true
        }
      },
      "RouteTablePage": {
        "required": [
          "reachable",
          "total",
          "offset",
          "limit",
          "routes"
        ],
        "type": "object",
        "properties": {
          "reachable": {
            "type": "boolean",
            "description": "False when FRR could not be queried — the table is then empty."
          },
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Routes matching the filter (before paging).",
            "format": "int32"
          },
          "offset": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The offset this page was taken at.",
            "format": "int32"
          },
          "limit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The effective page size (after clamping).",
            "format": "int32"
          },
          "routes": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Route"
            },
            "description": "The page of route rows."
          }
        },
        "description": "One page of a route table (VRF FIB or BGP Adj-RIB view).",
        "example": {
          "reachable": true,
          "total": 4218,
          "offset": 0,
          "limit": 50,
          "routes": [
            {
              "prefix": "100.64.12.0/24",
              "nextHop": "10.255.1.1",
              "protocol": "bgp",
              "interface": "core0",
              "metric": 0,
              "localPref": 100,
              "asPath": "65001 64512",
              "origin": "IGP",
              "best": true
            }
          ]
        }
      },
      "RoutingConfig": {
        "type": "object",
        "properties": {
          "configured": {
            "type": "boolean",
            "description": "Whether a routing configuration exists on this appliance."
          },
          "asn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Local autonomous system number.",
            "format": "uint32",
            "nullable": true
          },
          "routerId": {
            "type": "string",
            "description": "IPv4 router id.",
            "nullable": true
          },
          "routerId6": {
            "type": "string",
            "description": "IPv6 router id (empty when unset).",
            "nullable": true
          },
          "isisNet": {
            "type": "string",
            "description": "IS-IS NET (area + system id). Empty unless the IGP is IS-IS.",
            "nullable": true
          },
          "igpProtocol": {
            "type": "string",
            "description": "Interior gateway protocol: `isis`, `ospf` or `none`.",
            "nullable": true
          },
          "igpInterfaces": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Interfaces the IGP runs on. Loopbacks are passive; the rest are point-to-point\n            core links and also carry LDP.",
            "nullable": true
          },
          "ospfArea": {
            "type": "string",
            "description": "Default OSPF area. Only meaningful when the IGP is OSPF.",
            "nullable": true
          },
          "hostname": {
            "type": "string",
            "description": "FRR hostname.",
            "nullable": true
          },
          "enableVrfIpv6": {
            "type": "boolean",
            "description": "Whether IPv6 (6VPE) address families are enabled in VRFs.",
            "nullable": true
          },
          "originateDefaultRoute": {
            "type": "boolean",
            "description": "Whether an IPv4 default route is originated into BGP in the default (global)\n            table (standalone-CGN internet-exit advertisement).",
            "nullable": true
          },
          "peers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BgpPeer"
            },
            "description": "The configured BGP peers.",
            "nullable": true
          }
        },
        "description": "GET /api/v1/routing/config. When bool RoutingConfigDto.Configured is false the appliance has\n            no routing section and every other field is omitted.",
        "example": {
          "configured": true,
          "asn": 65000,
          "routerId": "10.255.255.10",
          "routerId6": "2001:db8:255::10",
          "isisNet": "49.0001.0102.5525.5010.00",
          "igpProtocol": "isis",
          "igpInterfaces": [
            "lo",
            "core0",
            "core1"
          ],
          "ospfArea": "0.0.0.0",
          "hostname": "lns1-hul1",
          "enableVrfIpv6": true,
          "originateDefaultRoute": false,
          "peers": [
            {
              "address": "10.255.255.2",
              "description": "pe1.lon1 route reflector",
              "vpnOnly": true,
              "bfd": true
            }
          ]
        }
      },
      "SectionSchema": {
        "type": "object",
        "properties": {
          "section": {
            "type": "string",
            "description": "Section key (first CLI-path token(s), e.g. \"syslog\", \"protocols dhcp\")."
          },
          "display": {
            "type": "string",
            "description": "Human-friendly card title (e.g. \"DHCP\", \"PPPoE\")."
          },
          "group": {
            "type": "string",
            "description": "Editor area this section belongs to — the tab an editing client files it under\n            (e.g. \"Routing\", \"Subscribers\", \"Monitoring\"). Sections sharing a group share an\n            int SectionSchema.GroupOrder; a section the server does not classify is \"Advanced\"."
          },
          "groupOrder": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Display order of string SectionSchema.Group relative to the other groups (ascending;\n            \"Advanced\" sorts last). Sections within a group keep the response's alphabetical order.",
            "format": "int32"
          },
          "requiresRestart": {
            "type": "boolean",
            "description": "True when changes in this section only take full effect after a restart."
          },
          "fields": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FieldSchema"
            },
            "description": "The section's scalar/list fields.",
            "nullable": true
          },
          "keyedLists": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/KeyedListSchema"
            },
            "description": "The section's keyed lists (e.g. RADIUS servers keyed by host).",
            "nullable": true
          }
        },
        "description": "One configuration section (a card in the UI): its fields and keyed lists.",
        "example": {
          "section": "syslog",
          "display": "Syslog",
          "group": "Monitoring",
          "groupOrder": 7,
          "requiresRestart": false,
          "fields": [
            {
              "path": "syslog server",
              "label": "Server",
              "description": "Collector the appliance exports RFC 5424 events to.",
              "type": "IpAddress",
              "list": false,
              "allowedValues": null,
              "allowedValueLabels": null,
              "secret": false,
              "visibleWhen": {
                "field": "syslog enabled",
                "equals": [
                  "true"
                ],
                "contains": false
              }
            }
          ],
          "keyedLists": [ ]
        }
      },
      "SessionDetail": {
        "type": "object",
        "properties": {
          "id": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "BNG session id.",
            "format": "uint32"
          },
          "type": {
            "type": "string",
            "description": "Session type: \"ipoe\" or \"ppp\"."
          },
          "encap": {
            "type": "string",
            "description": "Access encapsulation — drives which fields apply (requirements 21.7):\n    ipoe | pppoe-local | pppoe-lac | l2tp (LNS-terminated, subscriber behind a LAC)."
          },
          "username": {
            "type": "string",
            "description": "Subscriber username."
          },
          "ipv4": {
            "type": "string",
            "description": "Assigned IPv4 address, or null if IPv6-only.",
            "nullable": true
          },
          "ipv6": {
            "type": "string",
            "description": "IPv6 WAN address, or null.",
            "nullable": true
          },
          "ipv6PdPrefix": {
            "type": "string",
            "description": "Delegated IPv6 prefix (DHCPv6-PD), or null.",
            "nullable": true
          },
          "vrf": {
            "type": "string",
            "description": "VRF carrying the session (\"default\" = global table)."
          },
          "mac": {
            "type": "string",
            "description": "Subscriber MAC address, or null when unknown.",
            "nullable": true
          },
          "svlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Outer (service) VLAN id; 0 when untagged.",
            "format": "uint16"
          },
          "vlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Inner (customer) VLAN id; 0 when untagged.",
            "format": "uint16"
          },
          "portId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Data-plane port the session terminates on.",
            "format": "uint8"
          },
          "portLabel": {
            "type": "string",
            "description": "Operator-friendly label for that port, or null.",
            "nullable": true
          },
          "plan": {
            "type": "string",
            "description": "Service plan name, or null.",
            "nullable": true
          },
          "walledGarden": {
            "type": "boolean",
            "description": "True while the session is redirected to the walled garden."
          },
          "pppoeSessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "PPPoE session id on the access side, or null for non-PPPoE sessions.",
            "format": "uint16",
            "nullable": true
          },
          "additionalRoutes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Extra framed routes installed for this subscriber, as \"prefix/len\" strings."
          },
          "traffic": {
            "oneOf": [
              {
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/SessionTraffic"
              }
            ]
          },
          "createdUtc": {
            "type": "string",
            "description": "UTC time the session was established.",
            "format": "date-time"
          },
          "lastActivityUtc": {
            "type": "string",
            "description": "UTC time of the last packet seen from the subscriber.",
            "format": "date-time"
          },
          "uptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Seconds since the session was established.",
            "format": "double"
          },
          "sessionTimeoutSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "RADIUS Session-Timeout being enforced; 0 = none.",
            "format": "uint32"
          },
          "idleTimeoutSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "RADIUS Idle-Timeout being enforced; 0 = none.",
            "format": "uint32"
          },
          "l2tp": {
            "oneOf": [
              {
                "nullable": true
              },
              {
                "$ref": "#/components/schemas/SessionL2tp"
              }
            ]
          }
        },
        "description": "GET /api/v1/sessions/{id} — full detail for one session.",
        "example": {
          "id": 40213,
          "type": "ppp",
          "encap": "pppoe-local",
          "username": "alice@example.net",
          "ipv4": "100.64.12.37",
          "ipv6": "2001:db8:64:12::1",
          "ipv6PdPrefix": "2001:db8:1234:5600::/56",
          "vrf": "residential",
          "mac": "a4:2b:8c:11:9f:03",
          "svlan": 101,
          "vlan": 1337,
          "portId": 0,
          "portLabel": "xe-0/0/0",
          "plan": "fibre-900",
          "walledGarden": false,
          "pppoeSessionId": 4118,
          "additionalRoutes": [
            "198.51.100.64/29"
          ],
          "traffic": {
            "bytesIn": 184320000,
            "bytesOut": 2216755200,
            "packetsIn": 812004,
            "packetsOut": 1904321,
            "dropsUrpf": 0,
            "dropsRateLimit": 152
          },
          "createdUtc": "2026-08-02T10:24:00Z",
          "lastActivityUtc": "2026-08-03T10:23:58Z",
          "uptimeSeconds": 86412.538912,
          "sessionTimeoutSeconds": 0,
          "idleTimeoutSeconds": 1800,
          "l2tp": null
        }
      },
      "SessionDisconnectResult": {
        "required": [
          "status",
          "id",
          "username"
        ],
        "type": "object",
        "properties": {
          "status": {
            "type": "string",
            "description": "Always \"disconnected\"."
          },
          "id": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The disconnected session's id.",
            "format": "uint32"
          },
          "username": {
            "type": "string",
            "description": "The disconnected subscriber's username."
          }
        },
        "description": "DELETE /api/v1/sessions/{id} result.",
        "example": {
          "status": "disconnected",
          "id": 40213,
          "username": "alice@example.net"
        }
      },
      "SessionL2tp": {
        "required": [
          "localTunnelId",
          "localSessionId",
          "peerTunnelId",
          "peerSessionId",
          "peerIp",
          "remoteHostname"
        ],
        "type": "object",
        "properties": {
          "localTunnelId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Tunnel id on this appliance.",
            "format": "uint16"
          },
          "localSessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Session id inside the tunnel on this appliance.",
            "format": "uint16"
          },
          "peerTunnelId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Tunnel id on the peer (LAC or LNS).",
            "format": "uint16"
          },
          "peerSessionId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Session id on the peer.",
            "format": "uint16"
          },
          "peerIp": {
            "type": "string",
            "description": "Peer tunnel endpoint IP, or null when unknown.",
            "nullable": true
          },
          "remoteHostname": {
            "type": "string",
            "description": "The peer's L2TP Host Name AVP, or null when unknown.",
            "nullable": true
          }
        },
        "description": "L2TP identifiers for a session carried inside an L2TP tunnel.",
        "example": {
          "localTunnelId": 5,
          "localSessionId": 118,
          "peerTunnelId": 41027,
          "peerSessionId": 9,
          "peerIp": "203.0.113.10",
          "remoteHostname": "lac1.wholesale.example.net"
        }
      },
      "SessionListPage": {
        "required": [
          "total",
          "offset",
          "items"
        ],
        "type": "object",
        "properties": {
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Total sessions matching the filters (before paging).",
            "format": "int32"
          },
          "offset": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "The offset this page was taken at.",
            "format": "int32"
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/SessionSummary"
            },
            "description": "The page of session summaries, newest session first."
          }
        },
        "description": "GET /api/v1/sessions — one page of the filtered session list.",
        "example": {
          "total": 1842,
          "offset": 0,
          "items": [
            {
              "id": 40213,
              "type": "ppp",
              "encap": "pppoe-local",
              "username": "alice@example.net",
              "ipv4": "100.64.12.37",
              "ipv6": "2001:db8:64:12::1",
              "vrf": "residential",
              "mac": "a4:2b:8c:11:9f:03",
              "svlan": 101,
              "vlan": 1337,
              "plan": "fibre-900",
              "walledGarden": false,
              "uptimeSeconds": 86412.538912
            }
          ]
        }
      },
      "SessionLiveRates": {
        "required": [
          "intervalSeconds",
          "times",
          "rxBps",
          "txBps",
          "rxPps",
          "txPps"
        ],
        "type": "object",
        "properties": {
          "intervalSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Sampling interval of the series (seconds).",
            "format": "int32"
          },
          "times": {
            "type": "array",
            "items": {
              "type": "string",
              "format": "date-time"
            },
            "description": "UTC sample timestamps; all rate arrays align to this axis."
          },
          "rxBps": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
              "format": "double"
            },
            "description": "Bits/second received from the subscriber (upload)."
          },
          "txBps": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
              "format": "double"
            },
            "description": "Bits/second sent to the subscriber (download)."
          },
          "rxPps": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
              "format": "double"
            },
            "description": "Packets/second received from the subscriber."
          },
          "txPps": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
              "format": "double"
            },
            "description": "Packets/second sent to the subscriber."
          }
        },
        "description": "GET /api/v1/sessions/{id}/live — parallel rate series keyed by the shared\n    List&lt;DateTime&gt; SessionLiveRatesDto.Times axis. Arrays are empty until the watch has collected samples.",
        "example": {
          "intervalSeconds": 5,
          "times": [
            "2026-08-03T10:24:00Z",
            "2026-08-03T10:24:05Z"
          ],
          "rxBps": [
            18422137.6,
            17980412.8
          ],
          "txBps": [
            214880993.6,
            221304476.8
          ],
          "rxPps": [
            2140.4,
            2098.2
          ],
          "txPps": [
            19040.8,
            19422.6
          ]
        }
      },
      "SessionsSummary": {
        "required": [
          "total",
          "byType",
          "byEncap",
          "byPort",
          "byVrf",
          "walledGarden"
        ],
        "type": "object",
        "properties": {
          "total": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Number of active subscriber sessions.",
            "format": "int32"
          },
          "byType": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "Session count per session type (e.g. \"ipoe\", \"ppp\")."
          },
          "byEncap": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "Session count per access encapsulation (ipoe | pppoe-local | pppoe-lac | l2tp)."
          },
          "byPort": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PortSessionCount"
            },
            "description": "Session count per subscriber-facing port (LNS-terminated sessions excluded)."
          },
          "byVrf": {
            "type": "object",
            "additionalProperties": {
              "pattern": "^-?(?:0|[1-9]\\d*)$",
              "format": "int32"
            },
            "description": "Session count per VRF; sessions in the global table count under \"default\"."
          },
          "walledGarden": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Number of sessions currently redirected to the walled garden.",
            "format": "int32"
          }
        },
        "description": "GET /api/v1/sessions/summary — dashboard counts over the active sessions.",
        "example": {
          "total": 1842,
          "byType": {
            "ppp": 1503,
            "ipoe": 339
          },
          "byEncap": {
            "pppoe-local": 1200,
            "l2tp": 303,
            "ipoe": 339
          },
          "byPort": [
            {
              "portId": 0,
              "label": "xe-0/0/0",
              "count": 1503
            }
          ],
          "byVrf": {
            "residential": 1789,
            "default": 53
          },
          "walledGarden": 7
        }
      },
      "SessionSummary": {
        "type": "object",
        "properties": {
          "id": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "BNG session id — the key for the detail/live/disconnect endpoints.",
            "format": "uint32"
          },
          "type": {
            "type": "string",
            "description": "Session type: \"ipoe\" or \"ppp\"."
          },
          "encap": {
            "type": "string",
            "description": "Access encapsulation: ipoe | pppoe-local | pppoe-lac | l2tp (LNS-terminated)."
          },
          "username": {
            "type": "string",
            "description": "Subscriber username (PPP username, or the IPoE circuit identity)."
          },
          "ipv4": {
            "type": "string",
            "description": "Assigned IPv4 address, or null if the session is IPv6-only.",
            "nullable": true
          },
          "ipv6": {
            "type": "string",
            "description": "IPv6 WAN address, or null if not dual-stack.",
            "nullable": true
          },
          "vrf": {
            "type": "string",
            "description": "VRF carrying the session (\"default\" = global table)."
          },
          "mac": {
            "type": "string",
            "description": "Subscriber MAC address (aa:bb:cc:dd:ee:ff), or null when unknown.",
            "nullable": true
          },
          "svlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Outer (service) VLAN id; 0 when untagged.",
            "format": "uint16"
          },
          "vlan": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Inner (customer) VLAN id; 0 when untagged.",
            "format": "uint16"
          },
          "plan": {
            "type": "string",
            "description": "Service plan name, or null when no plan is applied.",
            "nullable": true
          },
          "walledGarden": {
            "type": "boolean",
            "description": "True while the session is redirected to the walled garden."
          },
          "uptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "description": "Seconds since the session was established.",
            "format": "double"
          }
        },
        "description": "One row of the session list.",
        "example": {
          "id": 40213,
          "type": "ppp",
          "encap": "pppoe-local",
          "username": "alice@example.net",
          "ipv4": "100.64.12.37",
          "ipv6": "2001:db8:64:12::1",
          "vrf": "residential",
          "mac": "a4:2b:8c:11:9f:03",
          "svlan": 101,
          "vlan": 1337,
          "plan": "fibre-900",
          "walledGarden": false,
          "uptimeSeconds": 86412.538912
        }
      },
      "SessionTraffic": {
        "required": [
          "bytesIn",
          "bytesOut",
          "packetsIn",
          "packetsOut",
          "dropsUrpf",
          "dropsRateLimit"
        ],
        "type": "object",
        "properties": {
          "bytesIn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Bytes received from the subscriber.",
            "format": "uint64"
          },
          "bytesOut": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Bytes sent to the subscriber.",
            "format": "uint64"
          },
          "packetsIn": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Packets received from the subscriber.",
            "format": "uint64"
          },
          "packetsOut": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Packets sent to the subscriber.",
            "format": "uint64"
          },
          "dropsUrpf": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Upstream packets dropped by the uRPF source check.",
            "format": "uint64"
          },
          "dropsRateLimit": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Packets dropped by the plan rate limiter.",
            "format": "uint64"
          }
        },
        "description": "Live data-plane counters for a session. \"In\" is traffic FROM the subscriber\n    (upload), \"Out\" is traffic TO the subscriber (download).",
        "example": {
          "bytesIn": 184320000,
          "bytesOut": 2216755200,
          "packetsIn": 812004,
          "packetsOut": 1904321,
          "dropsUrpf": 0,
          "dropsRateLimit": 152
        }
      },
      "SourceVrfRule": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Rule name.",
            "example": "alt-jat"
          },
          "ingestVrf": {
            "type": "string",
            "description": "The VRF traffic arrives in before classification."
          },
          "sourceSubnets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Source CIDRs that route into string SourceVrfRuleDto.TargetVrf."
          },
          "targetVrf": {
            "type": "string",
            "description": "The VRF matching traffic is routed into (no NAT)."
          }
        },
        "description": "One row of GET /api/v1/source-vrf-rules.",
        "example": {
          "name": "wholesale-a-handoff",
          "ingestVrf": "residential",
          "sourceSubnets": [
            "100.64.32.0/19"
          ],
          "targetVrf": "wholesale-a"
        }
      },
      "SourceVrfRuleWrite": {
        "required": [
          "name",
          "ingestVrf",
          "sourceSubnets",
          "targetVrf",
          "comment"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Rule name — the immutable key (ignored in the body on update; the URL wins)."
          },
          "ingestVrf": {
            "type": "string",
            "description": "The VRF traffic arrives in before classification."
          },
          "sourceSubnets": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Source CIDRs that route into TargetVrf.",
            "nullable": true
          },
          "targetVrf": {
            "type": "string",
            "description": "The VRF matching traffic is routed into (no NAT)."
          },
          "comment": {
            "type": "string",
            "description": "Optional commit comment for the config history.",
            "nullable": true
          }
        },
        "description": "Request body for source-VRF-rule create/update.",
        "example": {
          "name": "wholesale-a-handoff",
          "ingestVrf": "residential",
          "sourceSubnets": [
            "100.64.32.0/19"
          ],
          "targetVrf": "wholesale-a",
          "comment": "route the wholesale-a range into its own VRF"
        }
      },
      "StorageInfo": {
        "type": "object",
        "properties": {
          "blockDevices": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BlockDevice"
            }
          },
          "filesystems": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Filesystem"
            }
          }
        },
        "example": {
          "blockDevices": [
            {
              "name": "nvme0n1",
              "model": "SAMSUNG MZQL2960HCJR-00A07",
              "sizeBytes": 960197124096,
              "rotational": false,
              "transport": "nvme"
            }
          ],
          "filesystems": [
            {
              "mountPoint": "/",
              "fsType": "ext4",
              "totalBytes": 491921162240,
              "availableBytes": 463138689024
            }
          ]
        }
      },
      "SystemStatus": {
        "type": "object",
        "properties": {
          "hostname": {
            "type": "string",
            "description": "OS hostname of the appliance (kernel hostname, not webUi.hostname).",
            "nullable": true
          },
          "systemUptimeSeconds": {
            "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
            "format": "double",
            "nullable": true
          },
          "osDescription": {
            "type": "string",
            "nullable": true
          },
          "kernelVersion": {
            "type": "string",
            "nullable": true
          },
          "loadAverages": {
            "type": "array",
            "items": {
              "pattern": "^-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?$",
              "format": "double"
            },
            "nullable": true
          },
          "memoryTotalBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "memoryAvailableBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "rootFsTotalBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "rootFsAvailableBytes": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64",
            "nullable": true
          },
          "controlPlane": {
            "$ref": "#/components/schemas/ControlPlaneStatus"
          },
          "dataPlane": {
            "$ref": "#/components/schemas/DataPlaneStatus"
          }
        },
        "description": "GET /api/v1/system/status — dynamic health, poll/stream-friendly.",
        "example": {
          "hostname": "lns1-hul1",
          "systemUptimeSeconds": 1042318.72,
          "osDescription": "Debian GNU/Linux 13 (trixie)",
          "kernelVersion": "6.12.32-amd64",
          "loadAverages": [
            1.42,
            1.31,
            1.28
          ],
          "memoryTotalBytes": 135223480320,
          "memoryAvailableBytes": 118429581312,
          "rootFsTotalBytes": 491921162240,
          "rootFsAvailableBytes": 463138689024,
          "controlPlane": {
            "uptimeSeconds": 86412.538912,
            "version": "1.4.0",
            "drainState": "normal"
          },
          "dataPlane": {
            "running": true,
            "heartbeatAgeSeconds": 0.184,
            "palBackend": "dpdk"
          }
        }
      },
      "TacacsAdmin": {
        "required": [
          "enabled",
          "servers",
          "transportVrf",
          "timeoutMs",
          "authType",
          "authorizeService",
          "roleAttribute",
          "roleMap",
          "perCommandAuthorizationEnabled",
          "commandAccountingEnabled",
          "commandAuthorizationFallback",
          "hasSecret"
        ],
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "servers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebUiTacacsServer"
            }
          },
          "transportVrf": {
            "type": "string",
            "nullable": true
          },
          "timeoutMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "authType": {
            "type": "string"
          },
          "authorizeService": {
            "type": "string"
          },
          "roleAttribute": {
            "type": "string"
          },
          "roleMap": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthRoleRule"
            }
          },
          "perCommandAuthorizationEnabled": {
            "type": "boolean"
          },
          "commandAccountingEnabled": {
            "type": "boolean"
          },
          "commandAuthorizationFallback": {
            "type": "string"
          },
          "hasSecret": {
            "type": "boolean"
          }
        },
        "description": "Admin view of the TACACS+ config — no shared secret, just whether one is set.",
        "example": {
          "enabled": true,
          "servers": [
            {
              "host": "10.20.2.31",
              "port": 49
            },
            {
              "host": "10.20.2.32",
              "port": 49
            }
          ],
          "transportVrf": "mgmt",
          "timeoutMs": 5000,
          "authType": "pap",
          "authorizeService": "bng",
          "roleAttribute": "priv-lvl",
          "roleMap": [
            {
              "match": "15",
              "role": "admin"
            },
            {
              "match": "5",
              "role": "operator"
            }
          ],
          "perCommandAuthorizationEnabled": false,
          "commandAccountingEnabled": false,
          "commandAuthorizationFallback": "local",
          "hasSecret": true
        }
      },
      "TotpConfirmRequest": {
        "required": [
          "totpCode"
        ],
        "type": "object",
        "properties": {
          "totpCode": {
            "type": "string",
            "description": "Current code from the authenticator app, proving the secret was captured."
          }
        },
        "description": "POST /api/v1/auth/totp/confirm body.",
        "example": {
          "totpCode": "418244"
        }
      },
      "TotpEnrolment": {
        "required": [
          "secret",
          "otpauthUri"
        ],
        "type": "object",
        "properties": {
          "secret": {
            "type": "string",
            "description": "Base32 TOTP secret."
          },
          "otpauthUri": {
            "type": "string",
            "description": "otpauth:// URI for QR-code enrolment."
          }
        },
        "description": "POST /api/v1/auth/totp/begin — material for the authenticator app.",
        "example": {
          "secret": "JBSWY3DPEHPK3PXP",
          "otpauthUri": "otpauth://totp/NetavoBNG:noc-alice?secret=JBSWY3DPEHPK3PXP&amp;issuer=NetavoBNG"
        }
      },
      "TraceStep": {
        "required": [
          "stage",
          "verdict",
          "a",
          "b"
        ],
        "type": "object",
        "properties": {
          "stage": {
            "type": "string"
          },
          "verdict": {
            "type": "string"
          },
          "a": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          },
          "b": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int64"
          }
        },
        "description": "One pipeline stage's decision. `a` and `b` are stage-specific — see dp_trace.h, which\n    is the single definition of what each carries (for `SourceVrf`, `a` is the target VRF id and\n    `b` the matched prefix length).",
        "example": {
          "stage": "SourceVrf",
          "verdict": "Ok",
          "a": 7,
          "b": 16
        }
      },
      "TunnelEndpointWrite": {
        "type": "object",
        "properties": {
          "address": {
            "type": "string",
            "nullable": true
          },
          "port": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint16"
          },
          "vrfName": {
            "type": "string",
            "nullable": true
          },
          "sharedSecret": {
            "type": "string",
            "nullable": true
          },
          "assignmentId": {
            "type": "string",
            "nullable": true
          },
          "priority": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "uint8"
          }
        },
        "description": "Tunnel endpoint write body; a blank sharedSecret on update keeps the stored secret\n    of the endpoint with the same address.",
        "example": {
          "address": "45.84.200.80",
          "port": 1701,
          "vrfName": "wholesale-a",
          "sharedSecret": "correct horse battery staple",
          "assignmentId": "acme-ltd",
          "priority": 1
        }
      },
      "UpdateUserRequest": {
        "required": [
          "password",
          "role",
          "clearTotp"
        ],
        "type": "object",
        "properties": {
          "password": {
            "type": "string",
            "description": "New password, or null to keep the current one.",
            "nullable": true
          },
          "role": {
            "type": "string",
            "description": "New role, or null to keep the current one.",
            "nullable": true
          },
          "clearTotp": {
            "type": "boolean",
            "description": "true clears the account's TOTP enrolment (they re-enrol at next\n            login when TOTP is required).",
            "nullable": true
          }
        },
        "description": "PUT /api/v1/auth/users/{username} body. Only the supplied fields change.",
        "example": {
          "password": null,
          "role": "admin",
          "clearTotp": false
        }
      },
      "UserInfo": {
        "required": [
          "username",
          "role",
          "totpEnrolled"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "Account name."
          },
          "role": {
            "type": "string",
            "description": "Effective API role mapped from the account's login class."
          },
          "totpEnrolled": {
            "type": "boolean",
            "description": "True when the account has an active TOTP enrolment."
          }
        },
        "description": "One account row of GET /api/v1/auth/users.",
        "example": {
          "username": "noc-alice",
          "role": "operator",
          "totpEnrolled": true
        }
      },
      "VisibleWhenSchema": {
        "type": "object",
        "properties": {
          "field": {
            "type": "string",
            "description": "Path of the discriminator field."
          },
          "equals": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Values of the discriminator for which this field/list applies."
          },
          "contains": {
            "type": "boolean",
            "description": "True when the discriminator is a multi-select list: the gate matches if the list\n            CONTAINS any of string[] VisibleWhenSchema.Equals (e.g. show the SNMP sink fields while\n            `alerting sinks` includes \"snmp\"), rather than equalling one value. Omitted from JSON\n            when false so single-value gates (auth/accounting) serialize exactly as before."
          }
        },
        "description": "Conditional visibility: show the field/list only while a discriminator field\n            (e.g. \"auth driver\") holds one of the listed values.",
        "example": {
          "field": "syslog enabled",
          "equals": [
            "true"
          ],
          "contains": false
        }
      },
      "Vrf": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "VRF name (\"default\" = the global routing table).",
            "example": "residential"
          },
          "tableId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Linux route table id backing the VRF (configured VRFs only).",
            "format": "uint32",
            "nullable": true,
            "example": 2501
          },
          "rd": {
            "type": "string",
            "description": "BGP route distinguisher, e.g. \"65000:2501\".",
            "nullable": true
          },
          "importRt": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "VPN route targets imported into the VRF."
          },
          "exportRt": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "VPN route targets exported from the VRF."
          },
          "gatewayIp": {
            "type": "string",
            "description": "Subscriber IPv4 gateway address inside the VRF.",
            "nullable": true
          },
          "gatewayIpv6": {
            "type": "string",
            "description": "Subscriber IPv6 gateway address inside the VRF.",
            "nullable": true
          },
          "l2tpEndpointIp": {
            "type": "string",
            "description": "Per-VRF L2TP endpoint address, when L2TP transport rides this VRF.",
            "nullable": true
          },
          "originateDefaultRoute": {
            "type": "boolean",
            "description": "Whether an IPv4 default route is originated into BGP inside this VRF\n            (standalone-CGN internet-exit advertisement)."
          },
          "sessions": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Live sessions currently carried in this VRF.",
            "format": "int32"
          },
          "configured": {
            "type": "boolean",
            "description": "True if this VRF has a routing.vrfs entry (editable); false for session-only rows."
          }
        },
        "description": "One row of GET /api/v1/vrfs. Config-derived fields are null when the VRF has live\n            sessions but no routing.vrfs entry (or the field is unset in config).",
        "example": {
          "name": "residential",
          "tableId": 2501,
          "rd": "65000:2501",
          "importRt": [
            "65000:2501"
          ],
          "exportRt": [
            "65000:2501"
          ],
          "gatewayIp": "100.64.0.1",
          "gatewayIpv6": "2001:db8:64::1",
          "l2tpEndpointIp": "45.84.200.69",
          "originateDefaultRoute": false,
          "sessions": 1842,
          "configured": true
        }
      },
      "VrfWrite": {
        "required": [
          "name",
          "tableId",
          "rd",
          "importRt",
          "exportRt",
          "gatewayIp",
          "gatewayIpv6",
          "l2tpEndpointIp",
          "comment"
        ],
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "VRF name — the immutable key (ignored in the body on update; the URL wins)."
          },
          "tableId": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Linux route table id backing the VRF (must be unique).",
            "format": "uint32"
          },
          "rd": {
            "type": "string",
            "description": "BGP route distinguisher, e.g. \"65000:2501\" (empty = none).",
            "nullable": true
          },
          "importRt": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "VPN route targets imported into the VRF.",
            "nullable": true
          },
          "exportRt": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "VPN route targets exported from the VRF.",
            "nullable": true
          },
          "gatewayIp": {
            "type": "string",
            "description": "Subscriber IPv4 gateway address inside the VRF.",
            "nullable": true
          },
          "gatewayIpv6": {
            "type": "string",
            "description": "Subscriber IPv6 gateway address inside the VRF.",
            "nullable": true
          },
          "l2tpEndpointIp": {
            "type": "string",
            "description": "Per-VRF L2TP endpoint /32 (LNS/wholesale-LAC transport), or null.",
            "nullable": true
          },
          "comment": {
            "type": "string",
            "description": "Optional commit comment for the config history.",
            "nullable": true
          },
          "originateDefaultRoute": {
            "type": "boolean",
            "description": "Originate an IPv4 default route into BGP inside this VRF\n            (standalone-CGN inside VRF — this appliance advertises itself as the VRF's internet exit).",
            "default": false
          }
        },
        "description": "Request body for VRF create/update.",
        "example": {
          "name": "wholesale-a",
          "tableId": 2502,
          "rd": "65000:2502",
          "importRt": [
            "65000:2502"
          ],
          "exportRt": [
            "65000:2502"
          ],
          "gatewayIp": "100.64.32.1",
          "gatewayIpv6": "2001:db8:64:32::1",
          "l2tpEndpointIp": "45.84.200.70",
          "comment": "add the wholesale-a VRF",
          "originateDefaultRoute": false
        }
      },
      "WebUiOidcProviderConfig": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "description": "Stable id used in the callback URL (/api/v1/auth/oidc/{name}/callback)."
          },
          "enabled": {
            "type": "boolean",
            "description": "Enabled flag; a disabled provider is not offered on the login page."
          },
          "displayName": {
            "type": "string",
            "description": "Button label on the login page (\"Sign in with Entra ID\")."
          },
          "issuer": {
            "type": "string",
            "description": "OIDC issuer (authority) URL; discovery document is at {issuer}/.well-known/openid-configuration."
          },
          "clientId": {
            "type": "string"
          },
          "clientSecret": {
            "type": "string",
            "description": "Confidential-client secret. Write-only across API/CLI; stored like other shared secrets."
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Requested scopes; \"openid\" is always included."
          },
          "usernameClaim": {
            "type": "string",
            "description": "ID-token claim used as the session username."
          },
          "groupsClaim": {
            "type": "string",
            "description": "ID-token (or UserInfo) claim carrying group/role membership."
          },
          "fetchUserInfo": {
            "type": "boolean",
            "description": "Query the UserInfo endpoint for groups when they aren't in the ID token."
          },
          "redirectUri": {
            "type": "string",
            "description": "Explicit redirect_uri override (split-horizon DNS); else derived from webUi.hostname."
          },
          "roleMap": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthRoleRule"
            },
            "description": "Group/claim → role rules (fail closed on no match)."
          }
        },
        "example": {
          "name": "entra",
          "enabled": true,
          "displayName": "Sign in with Microsoft Entra ID",
          "issuer": "https://login.microsoftonline.com/00000000-0000-0000-0000-000000000000/v2.0",
          "clientId": "6f1a3c94-2f7e-4b41-9d0a-71c5b8e3f2aa",
          "clientSecret": "##SECRET-DATA##",
          "scopes": [
            "openid",
            "profile",
            "email",
            "groups"
          ],
          "usernameClaim": "preferred_username",
          "groupsClaim": "groups",
          "fetchUserInfo": false,
          "redirectUri": "https://bng1.example.net:8443/api/v1/auth/oidc/entra/callback",
          "roleMap": {
            "bng-superusers": "super-user",
            "bng-operators": "operator",
            "bng-noc": "read-only"
          }
        }
      },
      "WebUiTacacsConfig": {
        "type": "object",
        "properties": {
          "enabled": {
            "type": "boolean"
          },
          "servers": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebUiTacacsServer"
            },
            "description": "Ordered server list; tried in order, next on connect/timeout/error (not on auth-fail)."
          },
          "secret": {
            "type": "string",
            "description": "Shared key for RFC 8907 body obfuscation. Write-only across API/CLI. Empty = unencrypted\n            bodies, refused in production config validation."
          },
          "transportVrf": {
            "type": "string",
            "description": "Linux VRF device to bind the client socket into via SO_BINDTODEVICE (like RADIUS).",
            "nullable": true
          },
          "timeoutMs": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "description": "Per-server connect+read deadline; on total failure login falls through to local auth.",
            "format": "int32"
          },
          "authType": {
            "type": "string",
            "description": "\"ascii\" (interactive GETUSER/GETPASS) or \"pap\" (single-exchange)."
          },
          "authorizeService": {
            "type": "string",
            "description": "Authorization service requested to obtain the role (default \"shell\")."
          },
          "roleAttribute": {
            "type": "string",
            "description": "AV pair used to derive the role: \"priv-lvl\" (15→admin, ≥1→operator) or a custom pair."
          },
          "roleMap": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/AuthRoleRule"
            },
            "description": "Optional refinement of the role over the raw priv-lvl/attribute value."
          },
          "perCommandAuthorizationEnabled": {
            "type": "boolean",
            "description": "When true, TACACS+-authenticated CLI sessions ask the server before each command."
          },
          "commandAccountingEnabled": {
            "type": "boolean",
            "description": "When true, TACACS+-authenticated CLI sessions emit START/STOP accounting per command."
          },
          "commandAuthorizationFallback": {
            "type": "string",
            "description": "Fallback for per-command authorization ERROR/transport failure: \"local\" applies the\n            appliance role check; \"deny\" refuses the command. Default local preserves break-glass behaviour."
          }
        },
        "description": "TACACS+ (RFC 8907) operator authentication for web/API + appliance CLI login, with\n            local fallback (design doc 33 §33.5).",
        "example": {
          "enabled": true,
          "servers": [
            {
              "host": "10.20.2.30",
              "port": 49
            }
          ],
          "secret": "##SECRET-DATA##",
          "transportVrf": "management",
          "timeoutMs": 5000,
          "authType": "pap",
          "authorizeService": "bng",
          "roleAttribute": "priv-lvl",
          "roleMap": {
            "15": "super-user",
            "7": "operator",
            "1": "read-only"
          },
          "perCommandAuthorizationEnabled": false,
          "commandAccountingEnabled": false,
          "commandAuthorizationFallback": "local"
        }
      },
      "WebUiTacacsServer": {
        "type": "object",
        "properties": {
          "host": {
            "type": "string"
          },
          "port": {
            "pattern": "^-?(?:0|[1-9]\\d*)$",
            "format": "int32"
          }
        },
        "example": {
          "host": "10.20.2.30",
          "port": 49
        }
      },
      "WhoAmI": {
        "required": [
          "username",
          "role",
          "kind",
          "csrfToken"
        ],
        "type": "object",
        "properties": {
          "username": {
            "type": "string",
            "description": "Identity name (account username or token name)."
          },
          "role": {
            "type": "string",
            "description": "Effective role: \"read-only\", \"operator\" or \"admin\"."
          },
          "kind": {
            "type": "string",
            "description": "\"session\" (cookie) or \"token\" (bearer)."
          },
          "csrfToken": {
            "type": "string",
            "description": "The session's CSRF token (null for bearer tokens).",
            "nullable": true
          }
        },
        "description": "GET /api/v1/auth/whoami — the authenticated identity of the request.",
        "example": {
          "username": "noc-alice",
          "role": "operator",
          "kind": "local",
          "csrfToken": "kR3s9Xq1TfL8vB2wYc7Nd0"
        }
      }
    },
    "securitySchemes": {
      "bearerToken": {
        "type": "http",
        "description": "Named API token — the recommended credential for automation. See Authentication in the API overview.",
        "scheme": "bearer",
        "bearerFormat": "bngtok_… (opaque)"
      },
      "sessionCookie": {
        "type": "apiKey",
        "description": "Interactive session cookie used by the bundled web UI. Not the integration path for API clients — use a bearer token. See Authentication in the API overview.",
        "name": "bng_session",
        "in": "cookie"
      }
    }
  },
  "security": [
    {
      "bearerToken": [ ]
    }
  ],
  "tags": [
    {
      "name": "Address pools"
    },
    {
      "name": "Alarms"
    },
    {
      "name": "Authentication",
      "description": "Login/logout, TOTP enrolment, named API tokens, and web/API user management."
    },
    {
      "name": "Captures",
      "description": "Per-session packet captures: start/stop a GRE mirror to a remote collector and list active captures."
    },
    {
      "name": "CGN",
      "description": "Carrier-grade NAT (NAT44): status, IP groups (public pools), live bindings, and binding identity enrichment."
    },
    {
      "name": "System",
      "description": "Appliance health, status, hardware inventory, and network interface table."
    },
    {
      "name": "L2TP",
      "description": "Live L2TP tunnels — downstream (from LACs, terminated on this LNS) and upstream (to wholesale LNSes)."
    },
    {
      "name": "Licensing",
      "description": "Licence status, telemetry preview, activation, renewal, and offline licence install."
    },
    {
      "name": "LocalSubscribers"
    },
    {
      "name": "Metrics",
      "description": "Time-series metrics for dashboards: session counts and per-port throughput."
    },
    {
      "name": "RADIUS",
      "description": "RADIUS server pool health: live/dead state and per-server counters."
    },
    {
      "name": "Sessions",
      "description": "Live subscriber sessions: list/filter, detail with traffic counters, high-resolution live rates, and force-disconnect."
    },
    {
      "name": "SystemUpgrade"
    },
    {
      "name": "Configuration",
      "description": "Schema-driven appliance configuration: read, validate, commit, history, and rollback — the same candidate/commit pipeline as the CLI."
    },
    {
      "name": "Fleet",
      "description": "Multi-appliance support: the configured appliance list and a read-only proxy to peer appliances."
    },
    {
      "name": "GRE Circuits"
    },
    {
      "name": "Logs",
      "description": "Recent appliance log entries and a live server-sent-events stream."
    },
    {
      "name": "Routing",
      "description": "Live BGP and IGP (IS-IS or OSPF) status, per-VRF and per-neighbor route tables, and the editable BGP/routing configuration."
    },
    {
      "name": "Source VRF Rules"
    },
    {
      "name": "VRFs",
      "description": "Configured VRFs merged with live per-VRF session counts, plus VRF create/edit/delete."
    }
  ]
}