{
    "schemes": [
        "http",
        "https"
    ],
    "swagger": "2.0",
    "info": {
        "description": "REST API for authenticated Linux users to manage Containerlab deployments, topology files, node access, captures, and host networking tools.\n\nMost endpoints under /api/v1 require a JWT from POST /login. Send it as: Authorization: Bearer \u003ctoken\u003e.",
        "title": "Containerlab API Server",
        "contact": {
            "name": "Containerlab API Server maintainers",
            "url": "https://github.com/srl-labs/clab-api-server/issues"
        },
        "license": {
            "name": "Apache 2.0",
            "url": "https://www.apache.org/licenses/LICENSE-2.0.html"
        },
        "version": "1.0"
    },
    "paths": {
        "/api/v1/capture/wireshark-vnc-sessions": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Terminates all capture VNC sessions owned by the authenticated user. Superusers terminate all sessions.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Delete all Wireshark VNC sessions",
                "responses": {
                    "200": {
                        "description": "Capture sessions terminated",
                        "schema": {
                            "$ref": "#/definitions/models.CaptureCloseAllResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/capture/wireshark-vnc-sessions/{sessionId}": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Terminates a capture VNC session and stops the corresponding Wireshark container.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Delete Wireshark VNC session",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Capture session ID",
                        "name": "sessionId",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Capture session terminated",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Session not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/capture/wireshark-vnc-sessions/{sessionId}/ready": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns whether a capture VNC session is ready and its proxied URL path.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Get Wireshark VNC session readiness",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Capture session ID",
                        "name": "sessionId",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Readiness state",
                        "schema": {
                            "$ref": "#/definitions/models.CaptureWiresharkVncReadyResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Session not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/capture/wireshark-vnc-sessions/{sessionId}/vnc/{proxyPath}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Proxies noVNC HTTP assets for a capture session. WebSocket upgrades on this path are used by noVNC but are not represented in Swagger.",
                "produces": [
                    "text/html"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Proxy Wireshark VNC assets",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Capture session ID",
                        "name": "sessionId",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Proxy subpath under the noVNC root",
                        "name": "proxyPath",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Proxied noVNC asset response",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "Invalid request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Session not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "502": {
                        "description": "VNC proxy upstream error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/events": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Streams containerlab events in real time as NDJSON (one JSON object per line).\n\n**Notes**\n- The response stays open until the client disconnects.\n\n**Examples**\nNDJSON (one JSON object per line):\n```json\n{\"time\":1706918400,\"type\":\"container\",\"action\":\"start\",\"attributes\":{\"name\":\"clab-mylab-srl1\",\"lab\":\"mylab\",\"clab-node-name\":\"srl1\",\"clab-node-kind\":\"nokia_srlinux\"}}\n{\"time\":1706918405,\"type\":\"container\",\"action\":\"start\",\"attributes\":{\"name\":\"clab-mylab-srl2\",\"lab\":\"mylab\",\"clab-node-name\":\"srl2\",\"clab-node-kind\":\"nokia_srlinux\"}}\n```\n\n\nInterface stats (interfaceStats=true):\n```json\n{\"time\":1706918410,\"type\":\"interface-stats\",\"action\":\"stats\",\"attributes\":{\"name\":\"clab-mylab-srl1\",\"lab\":\"mylab\",\"interface\":\"e1-1\",\"rx_bytes\":123456,\"tx_bytes\":654321}}\n```",
                "produces": [
                    "application/x-ndjson"
                ],
                "tags": [
                    "Events"
                ],
                "summary": "Stream containerlab events",
                "parameters": [
                    {
                        "type": "boolean",
                        "default": false,
                        "description": "Include initial snapshot events when the stream starts.",
                        "name": "initialState",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "default": false,
                        "description": "Include interface stats events.",
                        "name": "interfaceStats",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "default": "10s",
                        "description": "Interval for interface stats collection (e.g., 10s). Requires interfaceStats=true.",
                        "name": "interfaceStatsInterval",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Event stream - NDJSON (one JSON object per line)",
                        "schema": {
                            "$ref": "#/definitions/models.EventResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/generate": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Generates a containerlab topology from CLOS definitions and can optionally deploy it for the authenticated user.\n\n**Notes**\n- Deployment is denied if a lab with the target name already exists.\n- The `images` and `licenses` fields map node kind to image or license path (e.g., {\"nokia_srlinux\":\"ghcr.io/...\"}).\n- When `deploy=true`, the topology is saved to the user's managed lab directory and `outputFile` is ignored.\n- When `deploy=false` and `outputFile` is empty, YAML is returned in the response.\n- When `deploy=false` and `outputFile` is set, the file is saved to that path on the server (requires API server write permissions).",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Topology Generation"
                ],
                "summary": "Generate containerlab topology",
                "parameters": [
                    {
                        "description": "Topology generation parameters. The 'images' field maps kind to image path.",
                        "name": "generate_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.GenerateRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Generation successful (YAML or deploy output)",
                        "schema": {
                            "$ref": "#/definitions/models.GenerateResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Conflict (Lab already exists and Deploy=true)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error or clab execution failed",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/health/metrics": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns detailed CPU, memory, and disk metrics for the API server. Requires authentication.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Health"
                ],
                "summary": "Get system metrics",
                "responses": {
                    "200": {
                        "description": "System metrics",
                        "schema": {
                            "$ref": "#/definitions/models.MetricsResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error gathering metrics",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/images": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Images"
                ],
                "summary": "List runtime images",
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.RuntimeImagesResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Images"
                ],
                "summary": "Remove a runtime image",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Image reference or ID",
                        "name": "reference",
                        "in": "query",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "Force image removal",
                        "name": "force",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.RuntimeImageActionResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/images/pull": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Images"
                ],
                "summary": "Pull a runtime image",
                "parameters": [
                    {
                        "description": "Image reference",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.RuntimeImagePullRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.RuntimeImageActionResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/lab-host": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the runtime the server runs labs with and, on c9s, the Kubernetes cluster and deploy defaults the signed-in user's labs get.\n\n**Notes**\n- `c9s` is only set when `runtime` is `c9s`. It names the kubeconfig context and cluster (`in-cluster` with the Pod's ServiceAccount) but never the cluster's address, credentials or file paths.\n- `namespacePattern` is the server's namespace template rendered for the signed-in user, with `{lab}` left in. `sharedNamespacePattern` is the namespace of labs in the shared workspace.\n- `metrics` reports whether the cluster serves `metrics.k8s.io`, which node stats need.\n- `defaultTimeout` is containerlab's timeout of a deploy, redeploy, apply or destroy without `timeout` (with every runtime).\n- `capture` is how the server captures packets, with every runtime: whether it runs Wireshark VNC sessions (`wiresharkVnc`; on c9s only while Docker answers on the server host), their image and pull policy and, with Docker and Podman, Edgeshark's packetflix port and the names (not values) of its extra environment variables.\n- `c9s.noTopologyCR` is false: the server deploys every lab with clabernetes' Topology resource.\n- The server must list `lab-host-info` in the `features` of `/health`.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Health"
                ],
                "summary": "Get the lab host",
                "responses": {
                    "200": {
                        "description": "Lab runtime and, on c9s, the cluster and deploy defaults",
                        "schema": {
                            "$ref": "#/definitions/models.LabHostResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns details for all running labs.\n\n**Notes**\n- Regular users see their own labs and labs in CLAB_SHARED_LABS_ROOT. Superusers see all labs.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "List labs",
                "responses": {
                    "200": {
                        "description": "All labs",
                        "schema": {
                            "$ref": "#/definitions/models.ClabInspectOutput"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deploys a containerlab topology.\n\n**Notes**\n- The request body must include either `topologyContent` or `topologySourceUrl` (not both).",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Deploy lab",
                "parameters": [
                    {
                        "description": "Deployment Source",
                        "name": "deploy_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.DeployRequest"
                        }
                    },
                    {
                        "type": "string",
                        "description": "Override lab name when deploying from a URL (optional)",
                        "name": "labNameOverride",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Allow overwriting an existing lab IF owned by the user",
                        "name": "reconfigure",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "Limit concurrent workers",
                        "name": "maxWorkers",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Custom Go template file for topology data export",
                        "name": "exportTemplate",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Comma-separated list of node names to deploy",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip post-deploy actions",
                        "name": "skipPostDeploy",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip setting extended ACLs on lab directory",
                        "name": "skipLabdirAcl",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network name (like --network); not allowed with multiple management networks",
                        "name": "network",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv4 subnet (like --ipv4-subnet)",
                        "name": "ipv4Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv6 subnet (like --ipv6-subnet)",
                        "name": "ipv6Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Restore nodes from \u003cnode\u003e.tar snapshots in this directory (like --restore-all); relative to the topology directory or absolute, inside the workspace",
                        "name": "restoreAll",
                        "in": "query"
                    },
                    {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "collectionFormat": "multi",
                        "description": "Restore one node from a snapshot file as node=path (like --restore); repeatable, overrides restoreAll",
                        "name": "restore",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "containerlab timeout like --timeout, a Go duration such as 10m or 1h; at most 2h. Defaults to 10m on c9s and 5m with Docker and Podman",
                        "name": "timeout",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deployed lab details",
                        "schema": {
                            "$ref": "#/definitions/models.ClabInspectOutput"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/archive": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deploys a containerlab topology provided as a .zip or .tar.gz archive.",
                "consumes": [
                    "multipart/form-data"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Deploy lab from archive",
                "parameters": [
                    {
                        "type": "file",
                        "description": "Lab archive (.zip or .tar.gz)",
                        "name": "labArchive",
                        "in": "formData",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Name for the lab",
                        "name": "labName",
                        "in": "query",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "Allow overwriting an existing lab",
                        "name": "reconfigure",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "Limit concurrent workers",
                        "name": "maxWorkers",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Custom Go template file for topology data export",
                        "name": "exportTemplate",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Comma-separated list of node names to deploy",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip post-deploy actions",
                        "name": "skipPostDeploy",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip setting extended ACLs on lab directory",
                        "name": "skipLabdirAcl",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network name (like --network); not allowed with multiple management networks",
                        "name": "network",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv4 subnet (like --ipv4-subnet)",
                        "name": "ipv4Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv6 subnet (like --ipv6-subnet)",
                        "name": "ipv6Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Restore nodes from \u003cnode\u003e.tar snapshots in this directory (like --restore-all); relative to the topology directory or absolute, inside the workspace",
                        "name": "restoreAll",
                        "in": "query"
                    },
                    {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "collectionFormat": "multi",
                        "description": "Restore one node from a snapshot file as node=path (like --restore); repeatable, overrides restoreAll",
                        "name": "restore",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "containerlab timeout like --timeout, a Go duration such as 10m or 1h; at most 2h. Defaults to 10m on c9s and 5m with Docker and Podman",
                        "name": "timeout",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deployed lab details",
                        "schema": {
                            "$ref": "#/definitions/models.ClabInspectOutput"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/topology/files": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Recursively discovers editable *.clab.yml, *.clab.yaml and *.clab.gotmpl files in the authenticated user's lab workspace. YAML and annotation paths are relative to the workspace root. Hidden directories, dependency caches, containerlab runtime directories, and symbolic links are excluded.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "List editable lab topology files",
                "responses": {
                    "200": {
                        "description": "Topology entries",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.TopologyEntry"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/topology/import-from-url": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Clones a supported Git repository URL and registers it as an undeployed lab.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Import topology repository as undeployed lab",
                "parameters": [
                    {
                        "description": "Topology source URL",
                        "name": "import_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.ImportTopologyFromURLRequest"
                        }
                    },
                    {
                        "type": "string",
                        "description": "Override imported lab name",
                        "name": "labNameOverride",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Import result",
                        "schema": {
                            "$ref": "#/definitions/models.ImportTopologyFromURLResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Lab already exists",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/workspace/directory": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a directory inside the authenticated user's editable lab workspace root.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Create lab workspace directory",
                "parameters": [
                    {
                        "description": "Directory path",
                        "name": "directory_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.WorkspaceDirectoryRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Directory create success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/workspace/events": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Streams create/change/delete/rename events inside the authenticated user's editable lab workspace root as NDJSON.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/x-ndjson"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Stream lab workspace file events",
                "responses": {
                    "200": {
                        "description": "Workspace file event stream",
                        "schema": {
                            "$ref": "#/definitions/models.WorkspaceFileEventResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/workspace/file": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Reads a text or binary file from the authenticated user's editable lab workspace root.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "text/plain"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Read lab workspace file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Relative file path inside the workspace root",
                        "name": "path",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "File content",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "File not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Writes a file inside the authenticated user's editable lab workspace root.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "consumes": [
                    "text/plain"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Write lab workspace file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Relative file path inside the workspace root",
                        "name": "path",
                        "in": "query",
                        "required": true
                    },
                    {
                        "description": "File content",
                        "name": "content",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Write success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deletes a file or directory inside the authenticated user's editable lab workspace root. Directories with children require recursive=true.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Delete lab workspace file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Relative file path inside the workspace root",
                        "name": "path",
                        "in": "query",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "Delete non-empty directories recursively",
                        "name": "recursive",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Delete success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/workspace/file/rename": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Renames or moves a file inside the authenticated user's editable lab workspace root.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Rename lab workspace file",
                "parameters": [
                    {
                        "description": "Old and new relative file paths",
                        "name": "rename_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.WorkspaceFileRenameRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Rename success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/workspace/tree": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Lists files and folders inside the authenticated user's editable lab workspace root.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "List lab workspace files",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Relative directory path inside the workspace root",
                        "name": "path",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Workspace entries",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.WorkspaceFileEntry"
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Directory not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns details for a specific running lab.\n\n**Notes**\n- `details=true` returns what `containerlab inspect --details` prints for the lab instead: a JSON object `{ \"\u003clabName\u003e\": [ runtime container objects ] }` with fields such as `Names`, `ID`, `ShortID`, `Image`, `State`, `Status`, `Labels`, `Pid`, `NetworkName`, `NetworkSettings`, `Mounts` and `Ports`. Internal helper containers are excluded.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Inspect lab",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab to inspect",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "Return runtime container details like containerlab inspect --details",
                        "name": "details",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Lab containers, or the details object when details=true",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.ClabContainerInfo"
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid lab name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Redeploys a lab by name.\n\n**Notes**\n- This operation destroys the lab and then deploys it again.\n- `stream=true` returns `application/x-ndjson` lifecycle events.\n- `includeLogs=true` includes captured lifecycle logs in the JSON response.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Redeploy lab",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab to redeploy",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "Remove containerlab lab artifacts during destroy phase",
                        "name": "cleanup",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Attempt graceful shutdown",
                        "name": "graceful",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override graceful shutdown timeout when graceful=true (for example 5s or 2m)",
                        "name": "gracefulTimeout",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Keep the management network",
                        "name": "keepMgmtNet",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "Limit concurrent workers",
                        "name": "maxWorkers",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Custom Go template file for topology data export",
                        "name": "exportTemplate",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip post-deploy actions",
                        "name": "skipPostDeploy",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip setting extended ACLs on lab directory",
                        "name": "skipLabdirAcl",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Template variables file (like --vars) relative to the lab's topology file directory, used for the destroy and deploy phases",
                        "name": "varsPath",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Stream lifecycle output as NDJSON events",
                        "name": "stream",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Include captured lifecycle logs in the JSON response",
                        "name": "includeLogs",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network name (like --network); not allowed with multiple management networks",
                        "name": "network",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv4 subnet (like --ipv4-subnet)",
                        "name": "ipv4Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv6 subnet (like --ipv6-subnet)",
                        "name": "ipv6Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "containerlab timeout like --timeout, a Go duration such as 10m or 1h; at most 2h. Defaults to 10m on c9s and 5m with Docker and Podman",
                        "name": "timeout",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Redeployed lab details",
                        "schema": {
                            "$ref": "#/definitions/models.ClabInspectOutput"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or vars file not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Destroys a lab by name after verifying access to an owned or shared lab, or superuser access.\n\n**Notes**\n- `stream=true` returns `application/x-ndjson` lifecycle events.\n- `includeLogs=true` includes captured lifecycle logs in the JSON response.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Destroy lab",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab to destroy",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "boolean",
                        "description": "Remove containerlab lab artifacts after destroy",
                        "name": "cleanup",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Purge topology parent directory below the personal or shared workspace root",
                        "name": "purgeLabDir",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Attempt graceful shutdown",
                        "name": "graceful",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override graceful shutdown timeout when graceful=true (for example 5s or 2m)",
                        "name": "gracefulTimeout",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Keep the management network",
                        "name": "keepMgmtNet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Destroy only specific nodes",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Template variables file (like --vars) relative to the lab's topology file directory",
                        "name": "varsPath",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Stream lifecycle output as NDJSON events",
                        "name": "stream",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Include captured lifecycle logs in the JSON response",
                        "name": "includeLogs",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "containerlab timeout like --timeout, a Go duration such as 10m or 1h; at most 2h. Defaults to 10m on c9s and 5m with Docker and Podman",
                        "name": "timeout",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Lab destroyed successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or vars file not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/apply": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Applies an on-disk topology from the authenticated user's lab directory. If the lab is not running, containerlab apply deploys it; otherwise it reconciles supported topology changes in place.\n\n**Notes**\n- `path` defaults to the running lab topology path when the lab exists, otherwise `\u003clabName\u003e.clab.yml`.\n- `dryRun=true` returns the apply plan without changing the lab.\n- `stream=true` returns `application/x-ndjson` lifecycle events.\n- `includeLogs=true` includes captured lifecycle logs in the JSON response.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Apply on-disk topology for lab",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative or legacy lab-relative topology path (defaults to running topology path or \u003clabName\u003e.clab.yml)",
                        "name": "path",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Show apply actions without applying them",
                        "name": "dryRun",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "Limit concurrent workers for new nodes",
                        "name": "maxWorkers",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Custom Go template file for topology data export",
                        "name": "exportTemplate",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip post-deploy actions for added nodes",
                        "name": "skipPostDeploy",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Stream lifecycle output as NDJSON events",
                        "name": "stream",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Include captured lifecycle logs in the JSON response",
                        "name": "includeLogs",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "containerlab timeout like --timeout, a Go duration such as 10m or 1h; at most 2h. Defaults to 10m on c9s and 5m with Docker and Podman",
                        "name": "timeout",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Apply result",
                        "schema": {
                            "$ref": "#/definitions/models.ApplyLabResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or topology file not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/capture/interfaces": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Lists the lab link interfaces of the lab's running nodes that packet capture accepts, with the far end of each link. The management interface is not capturable. With Docker and Podman the links come from the topology file the lab was deployed from; on c9s this runs as the signed-in user.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "List capturable interfaces",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Capturable interfaces, ordered by node and interface",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.CaptureInterfaceInfo"
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid lab name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden by Kubernetes RBAC",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Docker or Podman lab whose topology file is gone",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/capture/packetflix": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Builds packetflix URI(s) for the provided lab interface targets (non-VNC capture mode).",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Build packetflix capture URI",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Packetflix capture request",
                        "name": "capture_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.CapturePacketflixRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Packetflix capture URI payload",
                        "schema": {
                            "$ref": "#/definitions/models.CapturePacketflixResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or container not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "503": {
                        "description": "EdgeShark not running",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/capture/stream": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Captures lab link interfaces of a lab without Edgeshark: on c9s as the signed-in user through clabwire, with Docker and Podman through packet sockets in the nodes' network namespaces. It streams one pcapng section as it is produced: chunked, flushed per block. The section has one interface per target, named \u003cnode\u003e:\u003cinterface\u003e, before any packet; packets are in timestamp order. The stream ends when the duration passed, the packet limit was reached, or the client disconnects, which stops the capture in the lab. Errors before the first byte are JSON; later ones end the stream. On c9s, captures longer than clabwire's 1 h limit are chained by containerlab without duplicates.",
                "produces": [
                    "application/x-pcapng",
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Stream a packet capture",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "collectionFormat": "multi",
                        "description": "Interface to capture, \u003cnode\u003e:\u003cinterface\u003e (the interface name or its alias); repeat for several",
                        "name": "target",
                        "in": "query",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Capture duration (Go duration, e.g. 30s); default: until the client disconnects",
                        "name": "duration",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "Stop after this many packets across all targets",
                        "name": "packetLimit",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "Bytes kept per packet, 64 to 1048576 (default 262144)",
                        "name": "snapLength",
                        "in": "query"
                    },
                    {
                        "enum": [
                            "1"
                        ],
                        "type": "string",
                        "description": "1: send Content-Disposition: attachment; filename=\u003clab\u003e-\u003ctimestamp\u003e.pcapng",
                        "name": "download",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "pcapng stream",
                        "schema": {
                            "type": "file"
                        }
                    },
                    "400": {
                        "description": "Invalid target, duration or limit",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden by Kubernetes RBAC",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "A target node is not running, or a Docker or Podman lab's topology file is gone",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/capture/wireshark-vnc-sessions": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates one or more Wireshark noVNC capture sessions for the specified lab interface targets, one per container. With Docker and Podman the sessions capture through Edgeshark. With lab runtime c9s, containerName is the node's inspect name (\u003clab\u003e-\u003cnode\u003e) and interfaceName a capturable interface (or its alias); the server captures the session's interfaces as the signed-in user, in one pcapng stream, and writes it into a named pipe the session's Wireshark reads, without Edgeshark. That needs Docker on the server host (otherwise 501, and /health leaves out wireshark-vnc) and a Wireshark VNC image with the CLABWIRE_CAPTURE_PIPE mode of wireshark-vnc-docker. The capture ends with the session; when it fails, the session's container stops and its ready and VNC routes answer 500 with the reason.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Create Wireshark VNC capture session(s)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Wireshark VNC capture request",
                        "name": "capture_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.CaptureWiresharkVncRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Created capture sessions",
                        "schema": {
                            "$ref": "#/definitions/models.CaptureWiresharkVncCreateResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or container not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "A target node is not running (c9s)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "No Docker on the server host (c9s)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "503": {
                        "description": "EdgeShark not running",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/deploy": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deploys an on-disk topology from the authenticated user's lab directory.\n\n**Notes**\n- `path` defaults to `\u003clabName\u003e.clab.yml` when omitted.\n- `varsPath` selects the template variables file like `--vars`, relative to the topology file's directory and inside the same workspace; when omitted, containerlab uses `\u003ctopology name without extension\u003e_vars.{yaml,yml,json}` next to the topology.\n- `stream=true` returns `application/x-ndjson` lifecycle events.\n- `includeLogs=true` includes captured lifecycle logs in the JSON response.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Deploy on-disk topology for lab",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative or legacy lab-relative topology path (defaults to \u003clabName\u003e.clab.yml)",
                        "name": "path",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Template variables file relative to the topology file's directory (like --vars)",
                        "name": "varsPath",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Allow overwriting an existing lab",
                        "name": "reconfigure",
                        "in": "query"
                    },
                    {
                        "type": "integer",
                        "description": "Limit concurrent workers",
                        "name": "maxWorkers",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Custom Go template file for topology data export",
                        "name": "exportTemplate",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Comma-separated list of node names to deploy",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip post-deploy actions",
                        "name": "skipPostDeploy",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Skip setting extended ACLs on lab directory",
                        "name": "skipLabdirAcl",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Stream lifecycle output as NDJSON events",
                        "name": "stream",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Include captured lifecycle logs in the JSON response",
                        "name": "includeLogs",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network name (like --network); not allowed with multiple management networks",
                        "name": "network",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv4 subnet (like --ipv4-subnet)",
                        "name": "ipv4Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Override the management network IPv6 subnet (like --ipv6-subnet)",
                        "name": "ipv6Subnet",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Restore nodes from \u003cnode\u003e.tar snapshots in this directory (like --restore-all); relative to the topology directory or absolute, inside the workspace",
                        "name": "restoreAll",
                        "in": "query"
                    },
                    {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "collectionFormat": "multi",
                        "description": "Restore one node from a snapshot file as node=path (like --restore); repeatable, overrides restoreAll",
                        "name": "restore",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "containerlab timeout like --timeout, a Go duration such as 10m or 1h; at most 2h. Defaults to 10m on c9s and 5m with Docker and Podman",
                        "name": "timeout",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Deployed lab details",
                        "schema": {
                            "$ref": "#/definitions/models.ClabInspectOutput"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Topology or vars file not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Conflict",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/exec": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Executes a command on nodes within a lab.\n\n**Notes**\n- `nodes` (comma-separated topology node names) and `label` (repeatable `key=value`, like `containerlab exec --label`) combine with AND and always stay within the lab.\n- `nodeFilter` cannot be combined with `nodes` or `label`.\n- With `nodes` or `label`, a filter that matches no container returns 404.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Execute command in lab",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Execute only on this specific container",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Comma-separated topology node names to execute on",
                        "name": "nodes",
                        "in": "query"
                    },
                    {
                        "type": "array",
                        "items": {
                            "type": "string"
                        },
                        "collectionFormat": "multi",
                        "description": "Label filter in key=value form; repeat for several labels",
                        "name": "label",
                        "in": "query"
                    },
                    {
                        "description": "Command to execute",
                        "name": "exec_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.ExecRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Execution result",
                        "schema": {
                            "$ref": "#/definitions/models.ExecResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found or no node matches the filters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/fcli": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deprecated: use POST /api/v1/labs/{labName}/fcling. Runs the same fcling report.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Run fcling report (old path)",
                "deprecated": true,
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "fcling command request",
                        "name": "fcli_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.FclingCommandRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.FclingCommandResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/fcling": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Runs an fcling report once against the selected lab topology and returns its output.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Run fcling report",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "fcling command request",
                        "name": "fcling_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.FclingCommandRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.FclingCommandResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/fcling/api/{proxyPath}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Passes a request to the HTTP API of the lab's running fcling engine, with the engine's own token, and streams its answer. Only these reach it. GET: status, reports, reports/{name}, checks, incidents, acks, history, drift, configs, configs/diff, baselines, lenses, lenses/{name}, topology, events. POST: acks, baselines. DELETE: acks, baselines/{name}. Anything else is refused with 403. A lab without a running engine answers 409.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json",
                    "text/event-stream"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Proxy fcling engine API",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Path below the engine's /api, e.g. reports/bgp-peers",
                        "name": "proxyPath",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The engine's answer",
                        "schema": {
                            "type": "object"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Not part of the fabric API",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "The engine is not running",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "502": {
                        "description": "The engine is not reachable",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Passes a request to the HTTP API of the lab's running fcling engine, with the engine's own token, and streams its answer. Only these reach it. GET: status, reports, reports/{name}, checks, incidents, acks, history, drift, configs, configs/diff, baselines, lenses, lenses/{name}, topology, events. POST: acks, baselines. DELETE: acks, baselines/{name}. Anything else is refused with 403. A lab without a running engine answers 409.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json",
                    "text/event-stream"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Proxy fcling engine API",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Path below the engine's /api, e.g. reports/bgp-peers",
                        "name": "proxyPath",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The engine's answer",
                        "schema": {
                            "type": "object"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Not part of the fabric API",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "The engine is not running",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "502": {
                        "description": "The engine is not reachable",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Passes a request to the HTTP API of the lab's running fcling engine, with the engine's own token, and streams its answer. Only these reach it. GET: status, reports, reports/{name}, checks, incidents, acks, history, drift, configs, configs/diff, baselines, lenses, lenses/{name}, topology, events. POST: acks, baselines. DELETE: acks, baselines/{name}. Anything else is refused with 403. A lab without a running engine answers 409.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json",
                    "text/event-stream"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Proxy fcling engine API",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Path below the engine's /api, e.g. reports/bgp-peers",
                        "name": "proxyPath",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "The engine's answer",
                        "schema": {
                            "type": "object"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Not part of the fabric API",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "The engine is not running",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "502": {
                        "description": "The engine is not reachable",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/fcling/engine": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Tells how the lab's fcling engine is doing: `stopped` without one, `starting` until it answers, `ready` then, `failed` once its container has exited, with the end of its log.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Get fcling engine status",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.FclingEngineStatus"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Starts the lab's fcling engine, which streams its SR Linux nodes and keeps a history of what changes. An engine that runs is left alone, unless the request gives it another gNMI port: then it is replaced, and keeps its history. A port given is also used for the lab's reports and kept for the next start. The first start can take a while, since it pulls the image; poll the status until it says `ready`.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Start fcling engine",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "How to start the engine",
                        "name": "fcling_engine_request",
                        "in": "body",
                        "schema": {
                            "$ref": "#/definitions/models.FclingEngineStartRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.FclingEngineStatus"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Stops the lab's fcling engine. Its history is kept for the next start, and goes when the lab is destroyed.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Tools"
                ],
                "summary": "Stop fcling engine",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.FclingEngineStatus"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the configured lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/gotty/{action}": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Executes GoTTY share action (attach, detach, reattach) for a lab.\n\n**Notes**\n- The optional body applies to attach/reattach only and is ignored for detach. A body `port` wins over the `port` query.\n- `username` and `password` must be given together (defaults admin/admin); `shell` defaults to bash.\n- username: `^[A-Za-z0-9][A-Za-z0-9._-]{0,31}$`; password: `^[A-Za-z0-9._@%+=,!-]{1,64}$`, not starting with `-`; shell: `^/?[A-Za-z0-9_][A-Za-z0-9_./-]{0,63}$` without `..`.\n- The response reports the effective `username` and `shell`; the password is never returned.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Sharing"
                ],
                "summary": "GoTTY share action",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Action (attach|detach|reattach)",
                        "name": "action",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "integer",
                        "description": "GoTTY port (attach/reattach only)",
                        "name": "port",
                        "in": "query"
                    },
                    {
                        "description": "GoTTY options (attach/reattach only)",
                        "name": "gotty_request",
                        "in": "body",
                        "schema": {
                            "$ref": "#/definitions/models.GoTTYShareRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.ShareToolResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/graph/dot": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Generates a Graphviz dot graph from a lab topology like `containerlab graph --dot` and returns the file content.\n\n**Notes**\n- `path` defaults to the running lab topology path when the lab exists, otherwise `\u003clabName\u003e.clab.yml`. The lab does not need to be deployed.\n- The file is written to `\u003clabDir\u003e/graph/\u003ctopology name\u003e.dot`.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Graph"
                ],
                "summary": "Generate dot graph",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative or legacy lab-relative topology path (defaults to running topology path or \u003clabName\u003e.clab.yml)",
                        "name": "path",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GraphExportResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/graph/drawio": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Generates a draw.io diagram from a lab topology and returns the generated XML content.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Graph"
                ],
                "summary": "Generate draw.io graph",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Draw.io generation options",
                        "name": "drawio_request",
                        "in": "body",
                        "schema": {
                            "$ref": "#/definitions/models.DrawioGenerateRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.DrawioGenerateResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/graph/mermaid": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Generates a mermaid flowchart from a lab topology like `containerlab graph --mermaid` and returns the file content.\n\n**Notes**\n- `path` defaults to the running lab topology path when the lab exists, otherwise `\u003clabName\u003e.clab.yml`. The lab does not need to be deployed.\n- `direction` is one of TD, TB, BT, RL, LR (case-insensitive, default TD).\n- The file is written to `\u003clabDir\u003e/graph/\u003ctopology name\u003e.mermaid`.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Graph"
                ],
                "summary": "Generate mermaid graph",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative or legacy lab-relative topology path (defaults to running topology path or \u003clabName\u003e.clab.yml)",
                        "name": "path",
                        "in": "query"
                    },
                    {
                        "description": "Mermaid options",
                        "name": "graph_request",
                        "in": "body",
                        "schema": {
                            "$ref": "#/definitions/models.GraphExportRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GraphExportResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/host-script/describe": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Says what a script is right now, as the signed-in user can read it, for the confirmation before it runs. Needs API_HOST_SCRIPTS=true; not available with lab runtime c9s.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Describe a script button's script",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Topology file of a lab that is not deployed, from the topology listing",
                        "name": "path",
                        "in": "query"
                    },
                    {
                        "description": "The script",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.HostScriptSourceRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.HostScriptDescription"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "The account cannot run scripts or read this one",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or script not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Turned off, or a lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/host-script/stream": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Runs a script with bash on this host, from the lab's topology folder, as the signed-in user, and answers with NDJSON events: output as it comes, then the exit. Blank lines keep the connection open. Closing the connection stops the script and what it started. Needs API_HOST_SCRIPTS=true; not available with lab runtime c9s.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/x-ndjson"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Run a script button's script",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Topology file of a lab that is not deployed, from the topology listing",
                        "name": "path",
                        "in": "query"
                    },
                    {
                        "description": "The script",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.HostScriptRunRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.HostScriptEvent"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "The account cannot run scripts or read this one",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or script not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "The script is not the one that was confirmed",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "429": {
                        "description": "Too many running scripts",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Turned off, or a lab runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/interfaces": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns interface details for nodes in a lab.\n\nWith the lab runtime c9s the interfaces of the running nodes come from containerlab's c9s runtime, as the signed-in user: the lab link interfaces (with their aliases), eth0 and lo, with MAC, index, MTU, type and state; nodes are named \u003clab\u003e-\u003cnode\u003e.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "List lab interfaces",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Filter interfaces for a specific node",
                        "name": "node",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Interface details",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.NodeInterfaceInfo"
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid lab name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/links": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the links of a running lab as they currently exist in the kernel, including links created or removed after deployment.\n\n**Notes**\n- Each veth pair is listed once. Endpoint A is always in a lab node; endpoint B is in a lab node, the host namespace (e.g. a management or lab bridge port) or an external namespace.\n- macvlan interfaces are listed with their parent interface as endpoint B.\n- To follow changes, re-query when /api/v1/events reports interface create or delete events for the lab.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "List lab links",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Only return links with an endpoint on this node",
                        "name": "node",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Lab links",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.LabLink"
                            }
                        }
                    },
                    "400": {
                        "description": "Invalid lab name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/browser-ports": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns exposed host ports for a node suitable for opening in a browser.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Nodes"
                ],
                "summary": "Get node browser ports",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Node Name",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.NodeBrowserPortsResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/logs": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns logs for a lab node.\n\n**Notes**\n- When `follow=true`, the response streams as NDJSON (one JSON object per line) until the client disconnects or after 12 hours.\n- Streamed lines carry `stream` (`stdout` or `stderr`). A blank line is sent every 25 seconds while the node is quiet, so clients can tell a quiet node from a lost connection.",
                "produces": [
                    "application/json",
                    "application/x-ndjson"
                ],
                "tags": [
                    "Logs"
                ],
                "summary": "Get node logs",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Full name of the container (node)",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "default": "all",
                        "description": "Number of lines to show from the end of logs (default all). Use an integer or 'all'.",
                        "name": "tail",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Follow log output (stream logs as NDJSON). Note: In Swagger UI, streaming may not display correctly.",
                        "name": "follow",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Container logs (follow=false). When follow=true, response is NDJSON stream of LogLine objects.",
                        "schema": {
                            "$ref": "#/definitions/models.LogsResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input (lab name, node filter, etc.)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (not owner of the lab)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or node not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/pause": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Pauses a running node in a lab.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Nodes"
                ],
                "summary": "Pause node",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Node Name",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/ports/{port}/proxy/{proxyPath}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Proxies an HTTP request (including WebSocket upgrades) to a TCP port of a lab node, through a Kubernetes port-forward opened as the caller.\n\n**Notes**\n- Only available with a lab runtime (`runtime` `c9s` on `/health`); other runtimes answer 501 and publish host ports instead.\n- `browser-ports` lists the ports with their `proxyPath`.\n- The request is sent to the node without the `Authorization` header.",
                "tags": [
                    "Labs - Nodes"
                ],
                "summary": "Proxy HTTP to a node port",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Node name",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "integer",
                        "description": "Node TCP port",
                        "name": "port",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Path on the node",
                        "name": "proxyPath",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Response of the node",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "501": {
                        "description": "Not supported by the runtime",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "502": {
                        "description": "Node port not reachable",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/restart": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Restarts a node in a lab while preserving containerlab dataplane links.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Nodes"
                ],
                "summary": "Restart node",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Node Name",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/ssh": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates temporary SSH access to a lab node and returns connection details for an external SSH client.\nThis endpoint does not create a browser terminal session. For browser terminals, use `/api/v1/labs/{labName}/nodes/{nodeName}/terminal-sessions` with `protocol=ssh`.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "SSH Access"
                ],
                "summary": "Request temporary SSH access details",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Full container name of the node (e.g., clab-my-lab-srl1)",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "SSH access parameters",
                        "name": "sshRequest",
                        "in": "body",
                        "schema": {
                            "$ref": "#/definitions/models.SSHAccessRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "SSH connection details",
                        "schema": {
                            "$ref": "#/definitions/models.SSHAccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request parameters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (not owner of the lab)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or node not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/start": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Starts a stopped node in a lab.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Nodes"
                ],
                "summary": "Start node",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Node Name",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/stop": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Stops a running node in a lab.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Nodes"
                ],
                "summary": "Stop node",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Node Name",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/terminal-sessions": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a constrained interactive terminal session for an owned lab node using one of the supported protocols: `ssh`, `shell`, or `telnet`.\n\n**Protocol behavior**\n- `ssh`: launches the server-side `ssh` client in a PTY directly to the node management IP and streams that terminal over WebSocket.\n- `shell`: launches a PTY-backed `docker|podman exec -it \u003ccontainer\u003e \u003cserver-selected-command\u003e` session.\n- `telnet`: launches a PTY-backed `docker|podman exec -it \u003ccontainer\u003e telnet 127.0.0.1 \u003cport\u003e` session.\n\n**Security notes**\n- The backend chooses the launch command for every protocol. Clients choose only the protocol and terminal size.\n- Access is limited to owned nodes unless the caller is a configured superuser.\n\n**Resuming**\n- Set `resumable` to keep the session running when its stream disconnects, so the client can attach again and see the output it missed. The server must list `terminal-resume` in the `features` of `/health`.\n- Without `resumable`, the session ends as soon as its stream disconnects.\n\n**Relationship to `/api/v1/labs/{labName}/nodes/{nodeName}/ssh`**\n- This endpoint creates the interactive browser terminal session.\n- The separate `/ssh` endpoint returns temporary SSH access details for an external SSH client and is not required for browser terminals.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Terminal Sessions"
                ],
                "summary": "Create browser terminal session",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Full container name of the node (e.g., clab-my-lab-srl1)",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Terminal session creation parameters",
                        "name": "terminal_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.TerminalSessionRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Created terminal session",
                        "schema": {
                            "$ref": "#/definitions/models.TerminalSessionInfo"
                        }
                    },
                    "400": {
                        "description": "Invalid request parameters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or node not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "429": {
                        "description": "Too many active terminal sessions",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/nodes/{nodeName}/unpause": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Unpauses a paused node in a lab.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Nodes"
                ],
                "summary": "Unpause node",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Node Name",
                        "name": "nodeName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/restart": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Restarts all nodes in a deployed lab while preserving containerlab dataplane links.\n\n**Notes**\n- `stream=true` returns `application/x-ndjson` lifecycle events.\n- `includeLogs=true` includes captured lifecycle logs in the JSON response.\n- `nodeFilter` limits the action to the listed topology nodes; unknown nodes return 404.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Restart lab nodes",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Comma-separated topology node names to act on (default: all nodes)",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Stream lifecycle output as NDJSON events",
                        "name": "stream",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Include captured lifecycle logs in the JSON response",
                        "name": "includeLogs",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/save": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Saves the running configuration for nodes in a lab.\n\n**Notes**\n- `copy` works like `containerlab save --copy`: each saved config is also copied to `\u003ccopy\u003e/\u003clab dir\u003e/\u003cnode\u003e/\u003cfile\u003e-YYMMDD_HHMMSS.\u003cext\u003e` with a `\u003cfile\u003e` symlink to the newest copy. The path is relative to the topology directory (or absolute) and must stay inside the user's workspace, or the shared root for shared labs.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Save lab configuration",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Save config only for specific nodes",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Also copy saved configs into this directory (relative to the topology directory or absolute)",
                        "name": "copy",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Configuration saved",
                        "schema": {
                            "$ref": "#/definitions/models.SaveConfigResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/snapshots": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Saves VM snapshots of vrnetlab-based nodes, like `containerlab tools snapshot save`. Each snapshot is written to `\u003coutputDir\u003e/\u003cnode\u003e.tar`; non-vrnetlab nodes are skipped.\n\n**Notes**\n- `outputDir` is relative to the lab's topology directory (or absolute) and must stay inside the user's workspace, or the shared root for shared labs. Defaults to `snapshots`.\n- `timeout` is the per-node wait as a Go duration (default `5m`, maximum `30m`).\n- Nodes run concurrently. The response is 200 even when nodes fail; read each result's `status` (`success`, `failed`, `skipped`).\n- The lab is locked for other lifecycle operations while snapshots run.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Save VM snapshots",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Snapshot options",
                        "name": "snapshot_request",
                        "in": "body",
                        "schema": {
                            "$ref": "#/definitions/models.SnapshotSaveRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Per-node snapshot results",
                        "schema": {
                            "$ref": "#/definitions/models.SnapshotSaveResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or node not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Lab busy with another operation",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/sshx/{action}": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Executes SSHX share action (attach, detach, reattach) for a lab.\n\n**Notes**\n- The optional body applies to attach/reattach only and is ignored for detach.\n- `enableReaders=true` also creates a read-only access link, returned as `readOnlyLink`.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs - Sharing"
                ],
                "summary": "SSHX share action",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Action (attach|detach|reattach)",
                        "name": "action",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "SSHX options (attach/reattach only)",
                        "name": "sshx_request",
                        "in": "body",
                        "schema": {
                            "$ref": "#/definitions/models.SSHXShareRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.ShareToolResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/start": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Starts all nodes in a deployed lab while preserving containerlab dataplane links.\n\n**Notes**\n- `stream=true` returns `application/x-ndjson` lifecycle events.\n- `includeLogs=true` includes captured lifecycle logs in the JSON response.\n- `nodeFilter` limits the action to the listed topology nodes; unknown nodes return 404.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Start lab nodes",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Comma-separated topology node names to act on (default: all nodes)",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Stream lifecycle output as NDJSON events",
                        "name": "stream",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Include captured lifecycle logs in the JSON response",
                        "name": "includeLogs",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/stats": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Streams the CPU, memory and process count of every running node of a lab as NDJSON, one `stats` object per interval.\n\n**Notes**\n- `cpuPercent` is the usage since the previous sample, where 100 is one busy CPU; the first sample of a node has none.\n- Memory usage leaves out page cache that can be reclaimed, like `docker stats`.\n- Nodes that start or stop while streaming appear or disappear within 10 seconds.\n- The stream ends when the client disconnects or after 12 hours.",
                "produces": [
                    "application/x-ndjson"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Stream lab node resource usage",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Name of the lab",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "default": "2s",
                        "description": "Time between samples, from 1s to 20s.",
                        "name": "interval",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "NDJSON stream of samples",
                        "schema": {
                            "$ref": "#/definitions/models.LabStatsSample"
                        }
                    },
                    "400": {
                        "description": "Invalid lab name or interval",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/stop": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Stops all nodes in a deployed lab while preserving containerlab dataplane links.\n\n**Notes**\n- `stream=true` returns `application/x-ndjson` lifecycle events.\n- `includeLogs=true` includes captured lifecycle logs in the JSON response.\n- `nodeFilter` limits the action to the listed topology nodes; unknown nodes return 404.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Stop lab nodes",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Comma-separated topology node names to act on (default: all nodes)",
                        "name": "nodeFilter",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Stream lifecycle output as NDJSON events",
                        "name": "stream",
                        "in": "query"
                    },
                    {
                        "type": "boolean",
                        "description": "Include captured lifecycle logs in the JSON response",
                        "name": "includeLogs",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/tailscale": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Reports the Tailscale devices containerlab runs for a lab: the SSO proxy (`mgmt.tailscale.auth-mode: sso`), the auth-key sidecars of nodes, and nodes of the `tailscale` kind.\n\nThese are internal helper containers that are not listed as lab nodes. While the SSO proxy waits for authentication, `authUrl` holds the login URL to open; `forwards` lists the tailnet ports it forwards to nodes.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Get lab Tailscale status",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab Name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "schema": {
                            "$ref": "#/definitions/models.TailscaleStatusResponse"
                        }
                    },
                    "400": {
                        "description": "Bad Request",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Not Found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal Server Error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/topology/annotations": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns annotations for the specified lab. For deployed labs, the running annotations path is preferred and local files are used as fallback.",
                "produces": [
                    "text/plain"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Get lab annotations",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Annotations content",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "Invalid lab name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "File not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Updates annotations for the specified lab. For deployed labs, writes to the running annotations path when present and otherwise writes to local files.",
                "consumes": [
                    "text/plain"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Update lab annotations",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Annotations content",
                        "name": "content",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Write success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/topology/events": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Streams topology YAML/annotations change events for a single lab topology document pair as NDJSON.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/x-ndjson"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Stream topology document events",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative or legacy lab-relative topology YAML or annotations path",
                        "name": "path",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Topology document event stream",
                        "schema": {
                            "$ref": "#/definitions/models.TopologyDocEventResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/topology/file": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Reads a file from within the specified lab directory using a scoped relative path.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "text/plain"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Read lab topology file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative path from the topology listing, or legacy lab-relative path",
                        "name": "path",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "File content",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "File not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Writes a file inside the specified lab directory using a scoped relative path.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "consumes": [
                    "text/plain"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Write lab topology file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative path from the topology listing, or legacy lab-relative path",
                        "name": "path",
                        "in": "query",
                        "required": true
                    },
                    {
                        "description": "File content",
                        "name": "content",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Write success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deletes a file inside the specified lab directory using a scoped relative path.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Delete lab topology file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative path from the topology listing, or legacy lab-relative path",
                        "name": "path",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Delete success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid path",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "head": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Checks whether a file exists inside the specified lab directory.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "tags": [
                    "Labs"
                ],
                "summary": "Check lab topology file existence",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative path from the topology listing, or legacy lab-relative path",
                        "name": "path",
                        "in": "query",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "File exists"
                    },
                    "400": {
                        "description": "Invalid path"
                    },
                    "401": {
                        "description": "Unauthorized"
                    },
                    "404": {
                        "description": "File not found"
                    },
                    "500": {
                        "description": "Internal server error"
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/topology/file/rename": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Renames or moves a file inside the specified lab directory using scoped relative paths.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Rename lab topology file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Old and new relative file paths",
                        "name": "rename_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.TopologyFileRenameRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Rename success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/topology/rendered": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Renders a topology file the way containerlab does before deploying it: Go template execution with variables from `\u003ctopology name without extension\u003e_vars.{yaml,yml,json}` or `varsPath`, `clab_templates/*.gotmpl` subtemplates, and `${VAR:=default}` environment expansion. Nothing is deployed and no lab state changes.\n\n**Notes**\n- `path` resolves like the topology file endpoints. `varsPath` is relative to the topology file's directory (like `containerlab --vars` run from there) and must stay inside the topology's workspace.\n- `varsFiles` lists the variable files used, relative to the topology file's directory.\n- A topology that renders but is rejected by containerlab returns 200 with the rendered content and `error` set.\n- Template parse or execution errors return 422.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Render lab topology file",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative path from the topology listing, or legacy lab-relative path",
                        "name": "path",
                        "in": "query",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Template variables file relative to the topology file's directory (like --vars); defaults to containerlab's vars file discovery",
                        "name": "varsPath",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Rendered topology",
                        "schema": {
                            "$ref": "#/definitions/models.RenderedTopologyResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Topology or vars file not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "422": {
                        "description": "Topology template could not be rendered",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/topology/yaml": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the topology YAML for the specified lab. For deployed labs, the running topology source path is preferred and local files are used as fallback.",
                "produces": [
                    "text/plain"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Get lab topology YAML",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Topology YAML content",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "400": {
                        "description": "Invalid lab name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "File not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Updates the topology YAML for the specified lab. For deployed labs, writes to the running topology source when present and otherwise writes to local files.",
                "consumes": [
                    "text/plain"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Update lab topology YAML",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Topology YAML content",
                        "name": "content",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "type": "string"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Write success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/ui/icons": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the custom TopoViewer icons available for a lab, merging lab-local .clab-icons with the global ~/.clab/icons library. Lab-local icons take precedence.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "List lab custom icons",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Merged lab icon list",
                        "schema": {
                            "$ref": "#/definitions/models.IconListResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid lab name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/ui/icons/reconcile": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Copies used custom icons from the global ~/.clab/icons library into the lab-local .clab-icons directory and removes unused lab-local custom icons.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "Reconcile lab custom icons",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Used custom icon names",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.IconReconcileRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Reconcile success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/labs/{labName}/validate": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Parses and validates an on-disk topology like `containerlab validate`, without deploying it or changing any lab state.\n\n**Notes**\n- `path` defaults to the running lab topology path when the lab exists, otherwise `\u003clabName\u003e.clab.yml`.\n- A topology containerlab rejects returns 200 with `valid=false` and the validation error.\nShared workspace paths start with @shared/ when CLAB_SHARED_LABS_ROOT is configured; all authenticated API users can access them.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Labs"
                ],
                "summary": "Validate on-disk topology for lab",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Lab name",
                        "name": "labName",
                        "in": "path",
                        "required": true
                    },
                    {
                        "type": "string",
                        "description": "Workspace-relative or legacy lab-relative topology path (defaults to running topology path or \u003clabName\u003e.clab.yml)",
                        "name": "path",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Validation result",
                        "schema": {
                            "$ref": "#/definitions/models.ValidateTopologyResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Lab or topology file not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/ssh/sessions": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns active SSH sessions.\n\n**Notes**\n- Regular users see only their sessions.\n- Superusers can include all sessions via the `all` query parameter.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "SSH Access"
                ],
                "summary": "List SSH sessions",
                "parameters": [
                    {
                        "type": "boolean",
                        "description": "If true and user is superuser, shows sessions for all users (default: false)",
                        "name": "all",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "List of active SSH sessions",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.SSHSessionInfo"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (non-superuser attempting to list all sessions)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/ssh/sessions/{port}": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Terminates a specific SSH session by port.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "SSH Access"
                ],
                "summary": "Terminate SSH session",
                "parameters": [
                    {
                        "type": "integer",
                        "description": "SSH session port to terminate",
                        "name": "port",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Session terminated successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid port parameter",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (not owner of the session)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Session not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/terminal-sessions/{sessionId}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns metadata and lifecycle state for a terminal session.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Terminal Sessions"
                ],
                "summary": "Get terminal session",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Terminal session ID",
                        "name": "sessionId",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Terminal session metadata",
                        "schema": {
                            "$ref": "#/definitions/models.TerminalSessionInfo"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Session not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Terminates a terminal session owned by the caller (or any session for a superuser).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Terminal Sessions"
                ],
                "summary": "Terminate terminal session",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Terminal session ID",
                        "name": "sessionId",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Terminal session terminated",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Session not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/terminal-sessions/{sessionId}/stream": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Upgrades the request to a WebSocket connection for terminal input, output, resize, and close events.\n\n**Protocol notes**\n- Connect with a WebSocket client to this endpoint after creating a terminal session.\n- Client messages are JSON objects with `type` set to `input`, `resize`, or `close`.\n- Server messages are JSON objects with `type` set to `ready`, `output`, or `exit`.\n\n**Reconnecting**\n- A session created with `resumable` keeps running when the connection drops (`ready.resumable` is true). Connect again to continue: the first `output` message replays the session's recent output (`ready.replay` is true then).\n- Any other session ends when its connection closes.\n- Only one client is attached at a time. A new connection takes the session over, and the previous one is closed with code 4001.\n- Send `close` to end the session. A resumable session without a client ends after the server's terminal idle timeout (default 30 minutes).",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Terminal Sessions"
                ],
                "summary": "Stream terminal session",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Terminal session ID",
                        "name": "sessionId",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "101": {
                        "description": "Switching Protocols to WebSocket",
                        "schema": {
                            "type": "string"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Session not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "410": {
                        "description": "Session already exited",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/certs/ca": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a CA certificate and private key. Requires superuser privileges.\n\n**Notes**\n- Files are stored in the user's `~/.clab/certs/\u003cca_name\u003e/` directory on the server.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - Certificates"
                ],
                "summary": "Create certificate authority (CA)",
                "parameters": [
                    {
                        "description": "CA Generation Parameters",
                        "name": "ca_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.CACreateRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "CA created successfully",
                        "schema": {
                            "$ref": "#/definitions/models.CertResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error (filesystem, clab execution)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/certs/sign": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a certificate/key and signs it with a previously generated CA. Requires superuser privileges.\n\n**Notes**\n- Files are stored in the user's `~/.clab/certs/\u003cca_name\u003e/` directory.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - Certificates"
                ],
                "summary": "Sign certificate",
                "parameters": [
                    {
                        "description": "Certificate Signing Parameters",
                        "name": "sign_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.CertSignRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Certificate signed successfully",
                        "schema": {
                            "$ref": "#/definitions/models.CertResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters (name, hosts, CA name, etc.)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Specified CA not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error (filesystem, clab execution)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/disable-tx-offload": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Disables TX checksum offload on the eth0 interface of a container. Requires superuser privileges.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools"
                ],
                "summary": "Disable TX checksum offload",
                "parameters": [
                    {
                        "description": "Container Name",
                        "name": "tx_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.DisableTxOffloadRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Offload disabled successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Container not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error or clab execution failed",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/edgeshark/install": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Installs and starts EdgeShark services on the host runtime using compose.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Install EdgeShark",
                "responses": {
                    "200": {
                        "description": "EdgeShark installed",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Superuser privileges required",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/edgeshark/status": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns whether EdgeShark is reachable and its runtime details.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Get EdgeShark status",
                "responses": {
                    "200": {
                        "description": "EdgeShark status",
                        "schema": {
                            "$ref": "#/definitions/models.EdgeSharkStatusResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/edgeshark/uninstall": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Stops and removes EdgeShark services managed by compose.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Capture"
                ],
                "summary": "Uninstall EdgeShark",
                "responses": {
                    "200": {
                        "description": "EdgeShark uninstalled",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Superuser privileges required",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/netem/reset": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Resets (removes) netem impairments from a specific interface of a containerlab node owned by the authenticated user. Superusers may operate on any node.\n\nTarget the node either by `containerName`, or by `labName` and topology `nodeName`.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - Netem"
                ],
                "summary": "Reset link impairments (netem)",
                "parameters": [
                    {
                        "description": "Netem Reset Parameters",
                        "name": "netem_reset_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.NetemResetRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Impairments reset successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Node or interface not found, or node not owned by user",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/netem/set": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Sets netem impairments (delay, jitter, loss, rate limiting, corruption) on a specific interface of a containerlab node owned by the authenticated user. Superusers may operate on any node.\n\nTarget the node either by `containerName`, or by `labName` and topology `nodeName`. Interfaces of stitched links (e.g. vxlan-stitch) are impaired on their host-side interface.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - Netem"
                ],
                "summary": "Set link impairments (netem)",
                "parameters": [
                    {
                        "description": "Netem Set Parameters",
                        "name": "netem_set_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.NetemSetRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Impairments set successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Node or interface not found, or node not owned by user",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/netem/show": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Lists netem impairments for a containerlab node owned by the authenticated user. Superusers may inspect any node.\n\nTarget the node either by `containerName`, or by `labName` and topology `nodeName`. With `labName` alone, impairments of every deployed node of the lab are returned, keyed by node name.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - Netem"
                ],
                "summary": "Show link impairments (netem)",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Container name",
                        "name": "containerName",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Lab name (with nodeName, or alone to show all nodes)",
                        "name": "labName",
                        "in": "query"
                    },
                    {
                        "type": "string",
                        "description": "Topology node name (requires labName)",
                        "name": "nodeName",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Netem impairments",
                        "schema": {
                            "$ref": "#/definitions/models.NetemShowResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Node not found, or node not owned by user",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/veth": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a virtual Ethernet (vEth) pair between two endpoints (container, host, bridge, or ovs-bridge). Requires superuser privileges.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - vEth"
                ],
                "summary": "Create vEth pair",
                "parameters": [
                    {
                        "description": "vEth Creation Parameters",
                        "name": "veth_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.VethCreateRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "vEth pair created successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters (endpoints, MTU)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error (clab execution failed)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/tools/vxlan": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a VxLAN tunnel interface and sets up tc rules for traffic redirection. Requires superuser privileges.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - VxLAN"
                ],
                "summary": "Create VxLAN tunnel",
                "parameters": [
                    {
                        "description": "VxLAN Creation Parameters",
                        "name": "vxlan_request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.VxlanCreateRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "VxLAN tunnel created successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input parameters (remote, link, id, port, etc.)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error (clab execution failed)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deletes VxLAN tunnel interfaces that match the provided prefix (default: `vx-`). Requires superuser privileges.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Tools - VxLAN"
                ],
                "summary": "Delete VxLAN tunnels by prefix",
                "parameters": [
                    {
                        "type": "string",
                        "default": "vx-",
                        "description": "Prefix of VxLAN interfaces to delete",
                        "name": "prefix",
                        "in": "query"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "VxLAN tunnels deleted successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid prefix format",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized (JWT)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error (clab execution failed)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/ui/custom-nodes": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the authenticated user's persisted TopoViewer custom node templates. If none have been saved yet, seeded defaults matching VS Code are returned.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "List custom node templates",
                "responses": {
                    "200": {
                        "description": "Custom node templates",
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodesResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Replaces the authenticated user's full TopoViewer custom node template collection.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "Replace custom node templates",
                "parameters": [
                    {
                        "description": "Replacement custom node template collection",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodesReplaceRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Updated custom node templates",
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodesResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates or updates a single TopoViewer custom node template for the authenticated user.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "Save custom node template",
                "parameters": [
                    {
                        "description": "Custom node template payload",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodeTemplate"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Updated custom node templates",
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodesResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/ui/custom-nodes/default": {
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Sets the default TopoViewer custom node template for the authenticated user.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "Set default custom node template",
                "parameters": [
                    {
                        "description": "Default custom node selection",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodeDefaultRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Updated custom node templates",
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodesResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Custom node not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/ui/custom-nodes/{name}": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deletes a single TopoViewer custom node template for the authenticated user.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "Delete custom node template",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Custom node name",
                        "name": "name",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Updated custom node templates",
                        "schema": {
                            "$ref": "#/definitions/models.CustomNodesResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/ui/icons": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns the authenticated user's global custom TopoViewer icon library from ~/.clab/icons.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "List global custom icons",
                "responses": {
                    "200": {
                        "description": "Global custom icons",
                        "schema": {
                            "$ref": "#/definitions/models.IconListResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Uploads a new global custom TopoViewer icon into ~/.clab/icons for the authenticated user.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "Upload global custom icon",
                "parameters": [
                    {
                        "description": "Custom icon upload payload",
                        "name": "request",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.IconUploadRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Upload success",
                        "schema": {
                            "$ref": "#/definitions/models.IconUploadResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "Rejected due to built-in icon name conflict",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/ui/icons/{iconName}": {
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deletes a global custom TopoViewer icon from ~/.clab/icons for the authenticated user.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "UI"
                ],
                "summary": "Delete global custom icon",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Custom icon name",
                        "name": "iconName",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Delete success",
                        "schema": {
                            "$ref": "#/definitions/models.SimpleSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid icon name",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "Icon not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/users": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns a list of system users. Requires superuser privileges.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Users"
                ],
                "summary": "List users",
                "responses": {
                    "200": {
                        "description": "List of user details",
                        "schema": {
                            "type": "array",
                            "items": {
                                "$ref": "#/definitions/models.UserDetails"
                            }
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "post": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Creates a new system user. Requires superuser privileges.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Users"
                ],
                "summary": "Create user",
                "parameters": [
                    {
                        "description": "User creation details",
                        "name": "user",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.UserCreateRequest"
                        }
                    }
                ],
                "responses": {
                    "201": {
                        "description": "User created successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request body",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "409": {
                        "description": "User already exists",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/users/{username}": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns details for a specific user. Requires superuser privileges or the user's own account.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Users"
                ],
                "summary": "Get user details",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Username to get details for",
                        "name": "username",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "User details",
                        "schema": {
                            "$ref": "#/definitions/models.UserDetails"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (Not superuser or not the user's own account)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "User not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Updates an existing user. Regular users may update only their own profile fields. Group and superuser status changes require superuser privileges.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Users"
                ],
                "summary": "Update user",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Username to update",
                        "name": "username",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "User details to update",
                        "name": "user",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.UserUpdateRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "User updated successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request body",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (Not superuser or not the user's own account)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "User not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            },
            "delete": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Deletes a user from the system. Requires superuser privileges.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Users"
                ],
                "summary": "Delete user",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Username to delete",
                        "name": "username",
                        "in": "path",
                        "required": true
                    }
                ],
                "responses": {
                    "200": {
                        "description": "User deleted successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (User is not a superuser)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "User not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/users/{username}/password": {
            "put": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Changes a user's password. Requires superuser privileges or the user's own account.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Users"
                ],
                "summary": "Change user password",
                "parameters": [
                    {
                        "type": "string",
                        "description": "Username to change password for",
                        "name": "username",
                        "in": "path",
                        "required": true
                    },
                    {
                        "description": "Password change details",
                        "name": "passwordChange",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.PasswordChangeRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Password changed successfully",
                        "schema": {
                            "$ref": "#/definitions/models.GenericSuccessResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid request body",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized or incorrect current password",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "403": {
                        "description": "Forbidden (Not superuser or not the user's own account)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "404": {
                        "description": "User not found",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/version": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Returns version information for the containerlab library in use.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Version"
                ],
                "summary": "Get containerlab version",
                "responses": {
                    "200": {
                        "description": "Containerlab version details",
                        "schema": {
                            "$ref": "#/definitions/models.VersionResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/api/v1/version/check": {
            "get": {
                "security": [
                    {
                        "BearerAuth": []
                    }
                ],
                "description": "Checks whether a newer containerlab release is available.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Version"
                ],
                "summary": "Check containerlab updates",
                "responses": {
                    "200": {
                        "description": "Result of the version check",
                        "schema": {
                            "$ref": "#/definitions/models.VersionCheckResponse"
                        }
                    },
                    "401": {
                        "description": "Unauthorized",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        },
        "/health": {
            "get": {
                "description": "Returns basic health status for the API server.\n\n**Notes**\n- `features` lists optional API capabilities (for example `exec-filters` or `snapshot-save`) so clients can detect what this server supports. Features the configured runtime cannot serve are left out.\n- `runtime` is the lab runtime the server runs labs with: `docker`, `podman` or `c9s` (Kubernetes through clabernetes).\n- `namespaceMode` is set for `c9s`: `per-lab`, every lab of every user in its own namespace.",
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Health"
                ],
                "summary": "Get API server health",
                "responses": {
                    "200": {
                        "description": "Basic health information",
                        "schema": {
                            "$ref": "#/definitions/models.HealthResponse"
                        }
                    }
                }
            }
        },
        "/login": {
            "post": {
                "description": "Authenticates a user and returns a JWT token.",
                "consumes": [
                    "application/json"
                ],
                "produces": [
                    "application/json"
                ],
                "tags": [
                    "Auth"
                ],
                "summary": "Log in",
                "parameters": [
                    {
                        "description": "User Credentials",
                        "name": "credentials",
                        "in": "body",
                        "required": true,
                        "schema": {
                            "$ref": "#/definitions/models.LoginRequest"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "JWT token",
                        "schema": {
                            "$ref": "#/definitions/models.LoginResponse"
                        }
                    },
                    "400": {
                        "description": "Invalid input",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "401": {
                        "description": "Invalid credentials",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    },
                    "500": {
                        "description": "Internal server error (PAM config?)",
                        "schema": {
                            "$ref": "#/definitions/models.ErrorResponse"
                        }
                    }
                }
            }
        }
    },
    "definitions": {
        "models.ApplyLabResponse": {
            "type": "object",
            "properties": {
                "addedLinks": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "addedNodes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "deletedEndpoints": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "deletedNodes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "deployedLab": {
                    "type": "boolean"
                },
                "dryRun": {
                    "type": "boolean"
                },
                "labName": {
                    "type": "string"
                },
                "nodeChangeReasons": {
                    "type": "object",
                    "additionalProperties": {
                        "type": "string"
                    }
                },
                "recreatedNodes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "restartedNodes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "runtimePlan": {
                    "description": "RuntimePlan is the remote resource plan of a dry run on a lab runtime (c9s).",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.LabRuntimePlan"
                        }
                    ]
                },
                "startedNodes": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "models.C9sLabHost": {
            "type": "object",
            "properties": {
                "cluster": {
                    "description": "Cluster is the cluster name of that context; \"in-cluster\" for the Pod's ServiceAccount.",
                    "type": "string",
                    "example": "c9s"
                },
                "context": {
                    "description": "Context is the kubeconfig context the server uses; empty with the in-cluster\nServiceAccount.",
                    "type": "string",
                    "example": "c9s-provisioner"
                },
                "exposeType": {
                    "description": "ExposeType is the Service type of lab nodes (CLAB_C9S_EXPOSE_TYPE).",
                    "type": "string",
                    "enum": [
                        "None",
                        "ClusterIP",
                        "Headless",
                        "LoadBalancer"
                    ],
                    "example": "None"
                },
                "imagePullSecret": {
                    "description": "ImagePullSecret is the name of the image pull secret device Pods reference\n(CLAB_C9S_IMAGE_PULL_SECRET); empty when unset.",
                    "type": "string",
                    "example": "regcred"
                },
                "labQuota": {
                    "description": "LabQuota reports whether every lab namespace gets a ResourceQuota (CLAB_KUBE_LAB_QUOTA).",
                    "type": "boolean",
                    "example": false
                },
                "metrics": {
                    "description": "Metrics reports whether the cluster serves the resource metrics API (metrics.k8s.io)\nnode stats come from.",
                    "type": "boolean",
                    "example": true
                },
                "namespacePattern": {
                    "description": "NamespacePattern is the namespace of the signed-in user's labs, with {lab} left in\n(CLAB_KUBE_NAMESPACE_TEMPLATE). Names too long for a namespace are cut and get a hash.",
                    "type": "string",
                    "example": "c9s-alice-{lab}"
                },
                "noTopologyCR": {
                    "description": "NoTopologyCR reports whether labs deploy without clabernetes' Topology resource\n(containerlab's --no-topology-cr). The server always deploys with one: false.",
                    "type": "boolean",
                    "example": false
                },
                "persistence": {
                    "description": "Persistence reports whether node storage outlives Pod replacement and destroy\n(CLAB_C9S_PERSISTENCE).",
                    "type": "boolean",
                    "example": true
                },
                "sharedNamespacePattern": {
                    "description": "SharedNamespacePattern is the namespace of labs in the shared workspace, with {lab} left in.",
                    "type": "string",
                    "example": "c9s-shared-{lab}"
                }
            }
        },
        "models.CACreateRequest": {
            "type": "object",
            "properties": {
                "commonName": {
                    "description": "Defaults to \"containerlab.dev\"",
                    "type": "string",
                    "example": "ca.example.com"
                },
                "country": {
                    "description": "Defaults to \"Internet\"",
                    "type": "string",
                    "example": "US"
                },
                "expiry": {
                    "description": "Duration string, defaults to \"87600h\" (10 years)",
                    "type": "string",
                    "example": "8760h"
                },
                "locality": {
                    "description": "Defaults to \"Server\"",
                    "type": "string",
                    "example": "City"
                },
                "name": {
                    "description": "Defaults to \"ca\" if empty",
                    "type": "string",
                    "example": "my-root-ca"
                },
                "orgUnit": {
                    "description": "Defaults to \"Containerlab Tools\"",
                    "type": "string",
                    "example": "IT"
                },
                "organization": {
                    "description": "Defaults to \"Containerlab\"",
                    "type": "string",
                    "example": "MyOrg"
                }
            }
        },
        "models.CPUMetrics": {
            "type": "object",
            "properties": {
                "loadAvg1": {
                    "description": "1-minute load average",
                    "type": "number"
                },
                "loadAvg15": {
                    "description": "15-minute load average",
                    "type": "number"
                },
                "loadAvg5": {
                    "description": "5-minute load average",
                    "type": "number"
                },
                "numCPU": {
                    "description": "Number of CPUs/cores",
                    "type": "integer"
                },
                "processPercent": {
                    "description": "This process's CPU usage",
                    "type": "number"
                },
                "usagePercent": {
                    "description": "Overall CPU usage percentage",
                    "type": "number"
                }
            }
        },
        "models.CaptureCloseAllResponse": {
            "type": "object",
            "properties": {
                "closed": {
                    "type": "integer"
                },
                "message": {
                    "type": "string"
                }
            }
        },
        "models.CaptureInterfaceInfo": {
            "type": "object",
            "properties": {
                "alias": {
                    "description": "Alias is the topology's name for the interface when the node kind maps it.",
                    "type": "string",
                    "example": "ethernet-1/1"
                },
                "interface": {
                    "description": "Interface is the interface name in the node; capture output names it \u003cnode\u003e:\u003cinterface\u003e.",
                    "type": "string",
                    "example": "e1-1"
                },
                "node": {
                    "description": "Node is the node name as the topology spells it.",
                    "type": "string",
                    "example": "srl1"
                },
                "peerInterface": {
                    "description": "PeerInterface is the peer's interface as the topology wrote it.",
                    "type": "string",
                    "example": "e1-1"
                },
                "peerNode": {
                    "description": "PeerNode is the node at the other end of the link, \"host\" for a host endpoint.",
                    "type": "string",
                    "example": "srl2"
                }
            }
        },
        "models.CaptureLabHost": {
            "type": "object",
            "properties": {
                "edgeshark": {
                    "description": "Edgeshark is Edgeshark on the server host; only set for Docker and Podman.",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.EdgesharkLabHost"
                        }
                    ]
                },
                "wiresharkImage": {
                    "description": "WiresharkImage is the container image of a Wireshark VNC session\n(CAPTURE_WIRESHARK_DOCKER_IMAGE).",
                    "type": "string",
                    "example": "ghcr.io/srl-labs/wireshark-vnc-docker:latest"
                },
                "wiresharkPullPolicy": {
                    "description": "WiresharkPullPolicy is when the server pulls that image before a session\n(CAPTURE_WIRESHARK_PULL_POLICY).",
                    "type": "string",
                    "enum": [
                        "always",
                        "missing",
                        "never"
                    ],
                    "example": "always"
                },
                "wiresharkVnc": {
                    "description": "WiresharkVnc reports whether the server runs Wireshark VNC sessions (Wireshark in a\nbrowser window) for the user's labs; on c9s only while Docker answers on the server\nhost.",
                    "type": "boolean",
                    "example": true
                }
            }
        },
        "models.CapturePacketflixRequest": {
            "type": "object",
            "required": [
                "targets"
            ],
            "properties": {
                "remoteHostname": {
                    "type": "string"
                },
                "targets": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                        "$ref": "#/definitions/models.CaptureTarget"
                    }
                }
            }
        },
        "models.CapturePacketflixResponse": {
            "type": "object",
            "properties": {
                "captures": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.CapturePacketflixURI"
                    }
                }
            }
        },
        "models.CapturePacketflixURI": {
            "type": "object",
            "properties": {
                "containerName": {
                    "type": "string"
                },
                "interfaceNames": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "packetflixUri": {
                    "type": "string"
                }
            }
        },
        "models.CaptureTarget": {
            "type": "object",
            "required": [
                "containerName",
                "interfaceName"
            ],
            "properties": {
                "containerName": {
                    "type": "string"
                },
                "interfaceName": {
                    "type": "string"
                },
                "nodeNamespace": {
                    "description": "NodeNamespace marks InterfaceName as the port's name inside the node's\nnetwork namespace. It is captured there as-is, instead of being resolved\nthrough its alias to the host-side end of a stitched link, so a node's\nlinks and its own interfaces (e.g. eth0) can share one capture stream.",
                    "type": "boolean"
                }
            }
        },
        "models.CaptureWiresharkVncCreateResponse": {
            "type": "object",
            "properties": {
                "sessions": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.CaptureWiresharkVncSession"
                    }
                }
            }
        },
        "models.CaptureWiresharkVncReadyResponse": {
            "type": "object",
            "properties": {
                "ready": {
                    "type": "boolean"
                },
                "url": {
                    "type": "string"
                }
            }
        },
        "models.CaptureWiresharkVncRequest": {
            "type": "object",
            "required": [
                "targets"
            ],
            "properties": {
                "targets": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                        "$ref": "#/definitions/models.CaptureTarget"
                    }
                },
                "theme": {
                    "type": "string"
                }
            }
        },
        "models.CaptureWiresharkVncSession": {
            "type": "object",
            "properties": {
                "containerName": {
                    "type": "string"
                },
                "createdAt": {
                    "type": "string"
                },
                "expiresAt": {
                    "type": "string"
                },
                "interfaceNames": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "labName": {
                    "type": "string"
                },
                "sessionId": {
                    "type": "string"
                },
                "showVolumeTip": {
                    "type": "boolean"
                },
                "vncPath": {
                    "type": "string"
                }
            }
        },
        "models.CertResponse": {
            "type": "object",
            "properties": {
                "certPath": {
                    "description": "e.g., \"my-root-ca/my-root-ca.pem\" or \"my-root-ca/node1.example.com.pem\"",
                    "type": "string"
                },
                "csrPath": {
                    "description": "e.g., \"my-root-ca/my-root-ca.csr\" or \"my-root-ca/node1.example.com.csr\"",
                    "type": "string"
                },
                "keyPath": {
                    "description": "e.g., \"my-root-ca/my-root-ca.key\" or \"my-root-ca/node1.example.com.key\"",
                    "type": "string"
                },
                "message": {
                    "type": "string"
                }
            }
        },
        "models.CertSignRequest": {
            "type": "object",
            "required": [
                "caName",
                "hosts",
                "name"
            ],
            "properties": {
                "caName": {
                    "description": "Name of the CA cert/key files (without .pem/.key) previously generated",
                    "type": "string",
                    "example": "my-root-ca"
                },
                "commonName": {
                    "description": "Defaults to Name if empty",
                    "type": "string",
                    "example": "node1.example.com"
                },
                "country": {
                    "description": "Defaults to \"Internet\"",
                    "type": "string",
                    "example": "US"
                },
                "hosts": {
                    "description": "SANs (DNS names or IPs), comma-separated in clab, array here",
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "keySize": {
                    "description": "Defaults to 2048",
                    "type": "integer",
                    "example": 4096
                },
                "locality": {
                    "description": "Defaults to \"Server\"",
                    "type": "string",
                    "example": "City"
                },
                "name": {
                    "description": "Required",
                    "type": "string",
                    "example": "node1.example.com"
                },
                "orgUnit": {
                    "description": "Defaults to \"Containerlab Tools\"",
                    "type": "string",
                    "example": "Nodes"
                },
                "organization": {
                    "description": "Defaults to \"Containerlab\"",
                    "type": "string",
                    "example": "MyOrg"
                }
            }
        },
        "models.ClabContainerInfo": {
            "type": "object",
            "properties": {
                "absLabPath": {
                    "description": "Absolute path to topology file",
                    "type": "string"
                },
                "container": {
                    "description": "Container is the node's primary device container in the Pod.",
                    "type": "string",
                    "example": "node-srl1-6b1f"
                },
                "container_id": {
                    "description": "Docker container ID (short)",
                    "type": "string"
                },
                "exposed_ports": {
                    "description": "ExposedPorts are the node ports published through the node's Service.",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.ClabExposedPort"
                    }
                },
                "group": {
                    "description": "Group assigned in topology (Might not always be present)",
                    "type": "string"
                },
                "image": {
                    "description": "Container image used",
                    "type": "string"
                },
                "ipv4_address": {
                    "description": "Management IPv4 Address/Mask",
                    "type": "string"
                },
                "ipv6_address": {
                    "description": "Management IPv6 Address/Mask",
                    "type": "string"
                },
                "kind": {
                    "description": "e.g., \"linux\", \"nokia_srlinux\"",
                    "type": "string"
                },
                "labPath": {
                    "description": "Path to the topology file used (relative)",
                    "type": "string"
                },
                "lab_name": {
                    "description": "Name of the lab this node belongs to (redundant with map key but present)",
                    "type": "string"
                },
                "load_balancer_address": {
                    "description": "LoadBalancerAddress is the address of the LoadBalancer Service that exposes the node.",
                    "type": "string",
                    "example": "203.0.113.10"
                },
                "mgmt_network": {
                    "description": "MgmtNetwork is the management network the node is attached to; with\nseveral management networks (mgmt list in the topology) it tells them apart.",
                    "type": "string",
                    "example": "clab"
                },
                "name": {
                    "description": "Name of the container node (e.g., \"lab01-client1\")",
                    "type": "string"
                },
                "namespace": {
                    "description": "Namespace is the Kubernetes namespace of the lab.",
                    "type": "string",
                    "example": "c9s-alice-lab01"
                },
                "nodeName": {
                    "description": "Topology node name from containerlab labels",
                    "type": "string"
                },
                "node_uid": {
                    "description": "NodeUID is the UID of the clabernetes Node resource.",
                    "type": "string"
                },
                "owner": {
                    "description": "OS user from clab inspect output (Used for authorization)",
                    "type": "string"
                },
                "pod": {
                    "description": "Pod is the Pod that runs the node.",
                    "type": "string",
                    "example": "lab01-srl1-5c9d8f7b6-x2x7k"
                },
                "ports": {
                    "description": "Published ports, as in `clab inspect --format json`",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.ClabPortBinding"
                    }
                },
                "rootNodeName": {
                    "description": "RootNodeName is the topology node a sub-container belongs to, e.g. the\nnode of a distributed SR-SIM's CPM and card containers. Empty otherwise.",
                    "type": "string",
                    "example": "sr1"
                },
                "state": {
                    "description": "e.g., \"running\"",
                    "type": "string"
                },
                "status": {
                    "description": "e.g., \"Up 18 hours\"",
                    "type": "string"
                }
            }
        },
        "models.ClabExecInternalResult": {
            "type": "object",
            "properties": {
                "cmd": {
                    "description": "The command and its arguments as executed",
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "return-code": {
                    "description": "Exit code of the command inside the container",
                    "type": "integer"
                },
                "stderr": {
                    "description": "Standard error of the command",
                    "type": "string"
                },
                "stdout": {
                    "description": "Standard output of the command",
                    "type": "string"
                }
            }
        },
        "models.ClabExposedPort": {
            "type": "object",
            "properties": {
                "expose_port": {
                    "type": "integer",
                    "example": 22
                },
                "port": {
                    "type": "integer",
                    "example": 22
                },
                "protocol": {
                    "type": "string",
                    "example": "tcp"
                }
            }
        },
        "models.ClabInspectOutput": {
            "type": "object",
            "additionalProperties": {
                "type": "array",
                "items": {
                    "$ref": "#/definitions/models.ClabContainerInfo"
                }
            }
        },
        "models.ClabPortBinding": {
            "type": "object",
            "properties": {
                "host_ip": {
                    "type": "string",
                    "example": "0.0.0.0"
                },
                "host_port": {
                    "type": "integer",
                    "example": 8080
                },
                "port": {
                    "type": "integer",
                    "example": 80
                },
                "protocol": {
                    "type": "string",
                    "example": "tcp"
                }
            }
        },
        "models.CustomIconInfo": {
            "type": "object",
            "properties": {
                "dataUri": {
                    "type": "string"
                },
                "format": {
                    "type": "string",
                    "example": "svg"
                },
                "name": {
                    "type": "string"
                },
                "source": {
                    "type": "string",
                    "example": "global"
                }
            }
        },
        "models.CustomNodeDefaultRequest": {
            "type": "object",
            "required": [
                "name"
            ],
            "properties": {
                "name": {
                    "type": "string"
                }
            }
        },
        "models.CustomNodeTemplate": {
            "type": "object",
            "additionalProperties": true
        },
        "models.CustomNodesReplaceRequest": {
            "type": "object",
            "properties": {
                "customNodes": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.CustomNodeTemplate"
                    }
                }
            }
        },
        "models.CustomNodesResponse": {
            "type": "object",
            "properties": {
                "customNodes": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.CustomNodeTemplate"
                    }
                },
                "defaultNode": {
                    "type": "string"
                }
            }
        },
        "models.DeployRequest": {
            "type": "object",
            "properties": {
                "topologyContent": {
                    "description": "Option 1: Direct Topology Content.\nProvide the full containerlab topology YAML as a json string.\nIf this is provided, 'topologySourceUrl' MUST be empty.\n{\n\t\"topologyContent\": {\n\t  \"name\": \"your-lab-name\",\n\t  \"topology\": {\n\t\t\"kinds\": {\n\t\t  \"nokia_srlinux\": {\n\t\t\t\"type\": \"ixrd3\",\n\t\t\t\"image\": \"ghcr.io/nokia/srlinux\"\n\t\t  }\n\t\t},\n\t\t\"nodes\": {\n\t\t  \"srl1\": {\n\t\t\t\"kind\": \"nokia_srlinux\"\n\t\t  },\n\t\t  \"srl2\": {\n\t\t\t\"kind\": \"nokia_srlinux\"\n\t\t  }\n\t\t},\n\t\t\"links\": [\n\t\t  {\n\t\t\t\"endpoints\": [\n\t\t\t  \"srl1:e1-1\",\n\t\t\t  \"srl2:e1-1\"\n\t\t\t]\n\t\t  }\n\t\t]\n\t  }\n\t}\n  }",
                    "type": "object"
                },
                "topologySourceUrl": {
                    "description": "Option 2: Remote Topology Source URL.\nProvide a URL to a Git repository, a specific .clab.yml file in Git (github/gitlab), or a raw HTTP(S) URL.\nIf this is provided, 'topologyContent' MUST be empty.",
                    "type": "string"
                }
            }
        },
        "models.DisableTxOffloadRequest": {
            "type": "object",
            "required": [
                "containerName"
            ],
            "properties": {
                "containerName": {
                    "type": "string",
                    "example": "clab-my-lab-srl1"
                }
            }
        },
        "models.DiskMetrics": {
            "type": "object",
            "properties": {
                "freeDisk": {
                    "description": "Free disk space in bytes",
                    "type": "integer"
                },
                "path": {
                    "description": "Mount path (usually \"/\")",
                    "type": "string"
                },
                "totalDisk": {
                    "description": "Total disk space in bytes",
                    "type": "integer"
                },
                "usagePercent": {
                    "description": "Disk usage percentage",
                    "type": "number"
                },
                "usedDisk": {
                    "description": "Used disk space in bytes",
                    "type": "integer"
                }
            }
        },
        "models.DrawioGenerateRequest": {
            "type": "object",
            "properties": {
                "layout": {
                    "type": "string",
                    "example": "horizontal"
                },
                "theme": {
                    "type": "string",
                    "example": "nokia_modern"
                }
            }
        },
        "models.DrawioGenerateResponse": {
            "type": "object",
            "properties": {
                "content": {
                    "type": "string"
                },
                "fileName": {
                    "type": "string"
                },
                "layout": {
                    "type": "string"
                },
                "message": {
                    "type": "string"
                },
                "output": {
                    "type": "string"
                }
            }
        },
        "models.EdgeSharkStatusResponse": {
            "type": "object",
            "properties": {
                "packetflixPort": {
                    "type": "integer"
                },
                "running": {
                    "type": "boolean"
                },
                "runtime": {
                    "type": "string"
                },
                "version": {
                    "type": "string"
                }
            }
        },
        "models.EdgesharkLabHost": {
            "type": "object",
            "properties": {
                "extraEnvVars": {
                    "description": "ExtraEnvVars names the variables the Edgeshark containers get\n(CAPTURE_EDGESHARK_EXTRA_ENV_VARS), sorted; their values are not reported.",
                    "type": "array",
                    "items": {
                        "type": "string"
                    },
                    "example": [
                        "HTTP_PROXY",
                        "NO_PROXY"
                    ]
                },
                "packetflixPort": {
                    "description": "PacketflixPort is the port of Edgeshark's packetflix endpoint on the server host\n(CAPTURE_PACKETFLIX_PORT).",
                    "type": "integer",
                    "example": 5001
                }
            }
        },
        "models.ErrorResponse": {
            "type": "object",
            "properties": {
                "error": {
                    "type": "string",
                    "example": "Invalid credentials or user not in allowed group"
                }
            }
        },
        "models.EventAttributes": {
            "type": "object",
            "properties": {
                "clab-node-kind": {
                    "description": "Node kind (e.g., nokia_srlinux, linux)",
                    "type": "string",
                    "example": "nokia_srlinux"
                },
                "clab-node-name": {
                    "description": "Node name within the lab",
                    "type": "string",
                    "example": "srl1"
                },
                "clab-node-type": {
                    "description": "Node type",
                    "type": "string",
                    "example": "ixrd3"
                },
                "containerlab": {
                    "description": "Alternative lab name field (containerlab label)",
                    "type": "string",
                    "example": "mylab"
                },
                "exitCode": {
                    "description": "Exit code (for stop/die events)",
                    "type": "string",
                    "example": "0"
                },
                "image": {
                    "description": "Container image",
                    "type": "string",
                    "example": "ghcr.io/nokia/srlinux:latest"
                },
                "lab": {
                    "description": "Lab name this event belongs to",
                    "type": "string",
                    "example": "mylab"
                },
                "name": {
                    "description": "Container name",
                    "type": "string",
                    "example": "clab-mylab-srl1"
                }
            }
        },
        "models.EventResponse": {
            "type": "object",
            "properties": {
                "action": {
                    "description": "Event action (e.g., \"start\", \"stop\", \"die\", \"stats\")",
                    "type": "string",
                    "example": "start"
                },
                "attributes": {
                    "description": "Event attributes containing details about the event",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.EventAttributes"
                        }
                    ]
                },
                "time": {
                    "description": "Unix timestamp when the event occurred",
                    "type": "integer",
                    "example": 1706918400
                },
                "type": {
                    "description": "Type of event (e.g., \"container\", \"interface-stats\")",
                    "type": "string",
                    "example": "container"
                }
            }
        },
        "models.ExecRequest": {
            "type": "object",
            "required": [
                "command"
            ],
            "properties": {
                "command": {
                    "type": "string",
                    "example": "ip addr show eth1"
                }
            }
        },
        "models.ExecResponse": {
            "type": "object",
            "additionalProperties": {
                "type": "array",
                "items": {
                    "$ref": "#/definitions/models.ClabExecInternalResult"
                }
            }
        },
        "models.FclingCommandRequest": {
            "type": "object",
            "required": [
                "command"
            ],
            "properties": {
                "command": {
                    "type": "string",
                    "example": "bgp-peers"
                }
            }
        },
        "models.FclingCommandResponse": {
            "type": "object",
            "properties": {
                "command": {
                    "type": "string"
                },
                "output": {
                    "type": "string"
                }
            }
        },
        "models.FclingEngineStartRequest": {
            "type": "object",
            "properties": {
                "gnmiPort": {
                    "description": "GnmiPort is a new port for the lab's nodes, used for its reports too and kept for the\nnext start. An engine on another port is replaced; its history stays. Without it, the\nlab's port stays.",
                    "type": "integer",
                    "example": 57410
                }
            }
        },
        "models.FclingEngineStatus": {
            "type": "object",
            "properties": {
                "containerName": {
                    "type": "string",
                    "example": "clab-lab01-fcling"
                },
                "error": {
                    "description": "Error is the end of the engine's log, when it failed.",
                    "type": "string"
                },
                "gnmiPort": {
                    "description": "GnmiPort is the port the engine reaches the lab's nodes on, or will when it starts.",
                    "type": "integer",
                    "example": 57400
                },
                "image": {
                    "type": "string",
                    "example": "ghcr.io/srl-labs/fcling:0.9.0"
                },
                "startedAt": {
                    "type": "string",
                    "example": "2026-11-05T14:08:47Z"
                },
                "state": {
                    "description": "State is stopped without an engine, starting until it answers, ready then, and failed\nonce its container has exited.",
                    "type": "string",
                    "enum": [
                        "stopped",
                        "starting",
                        "ready",
                        "failed"
                    ],
                    "example": "ready"
                }
            }
        },
        "models.GenerateNodeTier": {
            "type": "object",
            "required": [
                "count"
            ],
            "properties": {
                "count": {
                    "description": "Number of nodes in this tier",
                    "type": "integer",
                    "minimum": 1,
                    "example": 4
                },
                "kind": {
                    "description": "Node kind (defaults to 'srl'/'nokia_srlinux' if omitted)",
                    "type": "string",
                    "example": "nokia_srlinux"
                },
                "type": {
                    "description": "Node type within the kind",
                    "type": "string",
                    "example": "ixrd3"
                }
            }
        },
        "models.GenerateRequest": {
            "type": "object",
            "required": [
                "name",
                "tiers"
            ],
            "properties": {
                "defaultKind": {
                    "type": "string",
                    "example": "nokia_srlinux"
                },
                "deploy": {
                    "type": "boolean",
                    "example": true
                },
                "groupPrefix": {
                    "type": "string",
                    "example": "clos-tier"
                },
                "images": {
                    "type": "object",
                    "additionalProperties": {
                        "type": "string"
                    },
                    "example": {
                        "nokia_srlinux": "ghcr.io/nokia/srlinux:latest"
                    }
                },
                "ipv4Subnet": {
                    "type": "string",
                    "example": "172.20.20.0/24"
                },
                "ipv6Subnet": {
                    "type": "string",
                    "example": "2001:172:20:20::/64"
                },
                "licenses": {
                    "type": "object",
                    "additionalProperties": {
                        "type": "string"
                    },
                    "example": {
                        "nokia_srlinux": "/opt/licenses/srl.lic"
                    }
                },
                "managementNetwork": {
                    "type": "string",
                    "example": "clos-mgmt"
                },
                "maxWorkers": {
                    "type": "integer",
                    "example": 4
                },
                "name": {
                    "type": "string",
                    "example": "3-tier-clos"
                },
                "nodePrefix": {
                    "type": "string",
                    "example": "clos-node"
                },
                "outputFile": {
                    "type": "string",
                    "example": "clos.yml"
                },
                "tiers": {
                    "type": "array",
                    "minItems": 1,
                    "items": {
                        "$ref": "#/definitions/models.GenerateNodeTier"
                    }
                }
            }
        },
        "models.GenerateResponse": {
            "type": "object",
            "properties": {
                "deployOutput": {
                    "description": "The output from the deploy command (only if Deploy=true). JSON object keyed by lab name.\nUse swaggertype:\"object\" to represent json.RawMessage in Swagger.",
                    "type": "object"
                },
                "message": {
                    "description": "Message indicating success or failure.",
                    "type": "string"
                },
                "savedFilePath": {
                    "description": "Path where the file was saved (if Deploy=true, it's the path in the user's managed lab directory; if Deploy=false, it's the OutputFile path if provided).",
                    "type": "string"
                },
                "topologyYaml": {
                    "description": "The generated topology YAML (only if Deploy=false and OutputFile is empty).",
                    "type": "string"
                }
            }
        },
        "models.GenericSuccessResponse": {
            "type": "object",
            "properties": {
                "message": {
                    "type": "string"
                }
            }
        },
        "models.GoTTYShareRequest": {
            "type": "object",
            "properties": {
                "password": {
                    "type": "string",
                    "example": "secret"
                },
                "port": {
                    "type": "integer",
                    "example": 8080
                },
                "shell": {
                    "type": "string",
                    "example": "bash"
                },
                "username": {
                    "type": "string",
                    "example": "admin"
                }
            }
        },
        "models.GraphExportRequest": {
            "type": "object",
            "properties": {
                "direction": {
                    "description": "Direction of the mermaid flowchart: TD, TB, BT, RL or LR (default TD).",
                    "type": "string",
                    "example": "TD"
                }
            }
        },
        "models.GraphExportResponse": {
            "type": "object",
            "properties": {
                "content": {
                    "type": "string"
                },
                "direction": {
                    "type": "string",
                    "example": "TD"
                },
                "fileName": {
                    "type": "string",
                    "example": "lab01.clab.mermaid"
                },
                "format": {
                    "type": "string",
                    "enum": [
                        "mermaid",
                        "dot"
                    ],
                    "example": "mermaid"
                },
                "output": {
                    "type": "string"
                }
            }
        },
        "models.HealthResponse": {
            "type": "object",
            "properties": {
                "features": {
                    "description": "Features lists optional API features this server supports, so clients can\nhide what older servers do not provide.",
                    "type": "array",
                    "items": {
                        "type": "string"
                    },
                    "example": [
                        "validate",
                        "exec-filters"
                    ]
                },
                "namespaceMode": {
                    "description": "NamespaceMode is how c9s labs get namespaces: per-lab (one namespace per lab of each\nuser). It is only set for c9s.",
                    "type": "string",
                    "enum": [
                        "per-lab",
                        "fixed"
                    ],
                    "example": "per-lab"
                },
                "runtime": {
                    "description": "Runtime is the lab runtime the server runs labs with: docker, podman or c9s.",
                    "type": "string",
                    "enum": [
                        "docker",
                        "podman",
                        "c9s"
                    ],
                    "example": "docker"
                },
                "startTime": {
                    "description": "When the server started",
                    "type": "string"
                },
                "status": {
                    "description": "\"healthy\" or other status indicators",
                    "type": "string"
                },
                "uptime": {
                    "description": "Human-readable uptime",
                    "type": "string"
                },
                "version": {
                    "description": "API server version",
                    "type": "string"
                }
            }
        },
        "models.HostScriptDescription": {
            "type": "object",
            "properties": {
                "content": {
                    "description": "The start of the script.",
                    "type": "string"
                },
                "foreign": {
                    "description": "Foreign means the user may not have written this: another user's file or lab, or a\nshared folder.",
                    "type": "boolean"
                },
                "kind": {
                    "description": "file or inline",
                    "type": "string",
                    "example": "file"
                },
                "owner": {
                    "type": "string"
                },
                "resolvedPath": {
                    "type": "string"
                },
                "sha256": {
                    "type": "string"
                },
                "size": {
                    "type": "integer"
                },
                "truncated": {
                    "type": "boolean"
                }
            }
        },
        "models.HostScriptEvent": {
            "type": "object",
            "properties": {
                "error": {
                    "type": "string"
                },
                "result": {
                    "$ref": "#/definitions/models.HostScriptResult"
                },
                "stream": {
                    "description": "stdout or stderr",
                    "type": "string"
                },
                "text": {
                    "type": "string"
                },
                "type": {
                    "description": "output, exit or error",
                    "type": "string",
                    "example": "output"
                }
            }
        },
        "models.HostScriptResult": {
            "type": "object",
            "properties": {
                "cancelled": {
                    "type": "boolean"
                },
                "durationMs": {
                    "type": "integer"
                },
                "exitCode": {
                    "description": "ExitCode is null when a signal stopped the script.",
                    "type": "integer"
                },
                "timedOut": {
                    "type": "boolean"
                }
            }
        },
        "models.HostScriptRunRequest": {
            "type": "object",
            "properties": {
                "expectedSha256": {
                    "description": "ExpectedSha256 is the hash of the description the user agreed to. Anything else\nis refused with 409.",
                    "type": "string"
                },
                "script": {
                    "type": "string"
                },
                "scriptPath": {
                    "type": "string"
                },
                "timeoutSeconds": {
                    "description": "TimeoutSeconds stops the script after this long; at most, and by default, one day.",
                    "type": "number"
                }
            }
        },
        "models.HostScriptSourceRequest": {
            "type": "object",
            "properties": {
                "script": {
                    "type": "string"
                },
                "scriptPath": {
                    "type": "string"
                }
            }
        },
        "models.IconListResponse": {
            "type": "object",
            "properties": {
                "icons": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.CustomIconInfo"
                    }
                }
            }
        },
        "models.IconReconcileRequest": {
            "type": "object",
            "properties": {
                "usedIcons": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "models.IconUploadRequest": {
            "type": "object",
            "required": [
                "dataBase64",
                "fileName"
            ],
            "properties": {
                "contentType": {
                    "type": "string",
                    "example": "image/svg+xml"
                },
                "dataBase64": {
                    "type": "string"
                },
                "fileName": {
                    "type": "string",
                    "example": "my-router.svg"
                }
            }
        },
        "models.IconUploadResponse": {
            "type": "object",
            "properties": {
                "iconName": {
                    "type": "string",
                    "example": "my-router"
                },
                "success": {
                    "type": "boolean",
                    "example": true
                }
            }
        },
        "models.ImportTopologyFromURLRequest": {
            "type": "object",
            "required": [
                "topologySourceUrl"
            ],
            "properties": {
                "topologySourceUrl": {
                    "type": "string"
                }
            }
        },
        "models.ImportTopologyFromURLResponse": {
            "type": "object",
            "properties": {
                "fileName": {
                    "type": "string"
                },
                "labName": {
                    "type": "string"
                },
                "success": {
                    "type": "boolean"
                },
                "topology": {
                    "$ref": "#/definitions/models.TopologyEntry"
                }
            }
        },
        "models.InterfaceInfo": {
            "type": "object",
            "properties": {
                "alias": {
                    "description": "Interface alias (e.g., \"ethernet-1/1\", \"\" if none)",
                    "type": "string"
                },
                "ifindex": {
                    "description": "Interface index",
                    "type": "integer"
                },
                "mac": {
                    "description": "MAC Address",
                    "type": "string"
                },
                "mtu": {
                    "description": "MTU size",
                    "type": "integer"
                },
                "name": {
                    "description": "Interface name (e.g., \"eth0\", \"e1-1\")",
                    "type": "string"
                },
                "state": {
                    "description": "Interface state (e.g., \"up\", \"down\", \"unknown\")",
                    "type": "string"
                },
                "type": {
                    "description": "Interface type (e.g., \"veth\", \"device\", \"dummy\")",
                    "type": "string"
                }
            }
        },
        "models.LabHostResponse": {
            "type": "object",
            "properties": {
                "c9s": {
                    "description": "C9s describes the Kubernetes cluster and deploy defaults; only set for c9s.",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.C9sLabHost"
                        }
                    ]
                },
                "capture": {
                    "description": "Capture is how the server captures packets of the user's labs: Wireshark VNC and, with\nDocker and Podman, Edgeshark.",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.CaptureLabHost"
                        }
                    ]
                },
                "defaultTimeout": {
                    "description": "DefaultTimeout is containerlab's timeout of a deploy, redeploy, apply or destroy that\npasses no `timeout`, as a Go duration: 10m on c9s, 5m with Docker and Podman.",
                    "type": "string",
                    "example": "10m"
                },
                "runtime": {
                    "description": "Runtime is the lab runtime: docker, podman or c9s.",
                    "type": "string",
                    "enum": [
                        "docker",
                        "podman",
                        "c9s"
                    ],
                    "example": "c9s"
                }
            }
        },
        "models.LabLink": {
            "type": "object",
            "properties": {
                "a": {
                    "$ref": "#/definitions/models.LinkEndpoint"
                },
                "b": {
                    "$ref": "#/definitions/models.LinkEndpoint"
                },
                "type": {
                    "description": "Link type: \"veth\" for veth pairs, \"macvlan\" for macvlan interfaces whose\nB endpoint is the parent interface.",
                    "type": "string",
                    "example": "veth"
                }
            }
        },
        "models.LabRuntimePlan": {
            "type": "object",
            "properties": {
                "changes": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.LabRuntimePlanChange"
                    }
                },
                "namespace": {
                    "type": "string",
                    "example": "c9s-alice-lab01"
                }
            }
        },
        "models.LabRuntimePlanChange": {
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "example": "recreate"
                },
                "kind": {
                    "type": "string",
                    "example": "Node"
                },
                "name": {
                    "type": "string",
                    "example": "srl1"
                },
                "namespace": {
                    "type": "string"
                }
            }
        },
        "models.LabStatsSample": {
            "type": "object",
            "properties": {
                "intervalSeconds": {
                    "description": "IntervalSeconds is the time between samples.",
                    "type": "number",
                    "example": 2
                },
                "nodes": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.NodeStats"
                    }
                },
                "time": {
                    "type": "string"
                },
                "type": {
                    "description": "Type is always \"stats\".",
                    "type": "string",
                    "example": "stats"
                }
            }
        },
        "models.LinkEndpoint": {
            "type": "object",
            "properties": {
                "alias": {
                    "description": "Interface alias (e.g. \"ethernet-1/1\").",
                    "type": "string",
                    "example": "ethernet-1/1"
                },
                "container": {
                    "description": "Container name, set when Namespace is \"node\".",
                    "type": "string",
                    "example": "clab-mylab-srl1"
                },
                "ifindex": {
                    "description": "Interface index inside its namespace.",
                    "type": "integer",
                    "example": 12
                },
                "interface": {
                    "description": "Interface name, empty when the interface could not be looked up.",
                    "type": "string",
                    "example": "e1-1"
                },
                "mac": {
                    "type": "string",
                    "example": "aa:c1:ab:00:00:01"
                },
                "master": {
                    "description": "Device the interface is enslaved to, such as a bridge.",
                    "type": "string",
                    "example": "br-clab"
                },
                "mtu": {
                    "type": "integer",
                    "example": 9500
                },
                "namespace": {
                    "description": "Namespace the interface lives in: \"node\", \"host\" or \"external\".",
                    "type": "string",
                    "example": "node"
                },
                "node": {
                    "description": "Topology node name, set when Namespace is \"node\".",
                    "type": "string",
                    "example": "srl1"
                },
                "state": {
                    "type": "string",
                    "example": "up"
                }
            }
        },
        "models.LoginRequest": {
            "type": "object",
            "required": [
                "password",
                "username"
            ],
            "properties": {
                "password": {
                    "type": "string"
                },
                "sessionDuration": {
                    "type": "string",
                    "example": "24h"
                },
                "username": {
                    "type": "string"
                }
            }
        },
        "models.LoginResponse": {
            "type": "object",
            "properties": {
                "token": {
                    "type": "string",
                    "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiZXhwIjoxNzA2OTIyMDAwfQ.abc123"
                }
            }
        },
        "models.LogsResponse": {
            "type": "object",
            "properties": {
                "containerName": {
                    "type": "string"
                },
                "logs": {
                    "type": "string"
                }
            }
        },
        "models.MemMetrics": {
            "type": "object",
            "properties": {
                "availableMem": {
                    "description": "Available memory in bytes",
                    "type": "integer"
                },
                "processMemMB": {
                    "description": "This process's memory in MB",
                    "type": "number"
                },
                "processMemPct": {
                    "description": "This process's memory percentage",
                    "type": "number"
                },
                "totalMem": {
                    "description": "Total physical memory in bytes",
                    "type": "integer"
                },
                "usagePercent": {
                    "description": "Memory usage percentage",
                    "type": "number"
                },
                "usedMem": {
                    "description": "Used physical memory in bytes",
                    "type": "integer"
                }
            }
        },
        "models.Metrics": {
            "type": "object",
            "properties": {
                "cpu": {
                    "description": "CPU usage metrics",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.CPUMetrics"
                        }
                    ]
                },
                "disk": {
                    "description": "Disk usage metrics",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.DiskMetrics"
                        }
                    ]
                },
                "mem": {
                    "description": "Memory usage metrics",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.MemMetrics"
                        }
                    ]
                }
            }
        },
        "models.MetricsResponse": {
            "type": "object",
            "properties": {
                "metrics": {
                    "description": "Detailed system metrics",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.Metrics"
                        }
                    ]
                },
                "serverInfo": {
                    "description": "Basic server information",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.ServerInfo"
                        }
                    ]
                }
            }
        },
        "models.NetemInterfaceInfo": {
            "type": "object",
            "properties": {
                "corruption": {
                    "description": "Percentage (might be missing in older clab versions)",
                    "type": "number"
                },
                "delay": {
                    "description": "Duration string or empty",
                    "type": "string"
                },
                "interface": {
                    "description": "Interface name",
                    "type": "string"
                },
                "jitter": {
                    "description": "Duration string or empty",
                    "type": "string"
                },
                "packet_loss": {
                    "description": "Percentage",
                    "type": "number"
                },
                "rate": {
                    "description": "Kbit/s",
                    "type": "integer"
                }
            }
        },
        "models.NetemResetRequest": {
            "type": "object",
            "required": [
                "interface"
            ],
            "properties": {
                "containerName": {
                    "description": "Container name to reset impairments on (e.g., \"clab-my-lab-srl1\").",
                    "type": "string",
                    "example": "clab-my-lab-srl1"
                },
                "interface": {
                    "description": "Interface name or interface alias to reset impairments on (e.g., \"eth1\" or \"mgmt0\").",
                    "type": "string",
                    "example": "eth1"
                },
                "labName": {
                    "description": "Lab name of the topology node to reset impairments on. Used with nodeName instead of containerName.",
                    "type": "string",
                    "example": "my-lab"
                },
                "nodeName": {
                    "description": "Topology node name to reset impairments on. Used with labName instead of containerName.",
                    "type": "string",
                    "example": "srl1"
                }
            }
        },
        "models.NetemSetRequest": {
            "type": "object",
            "required": [
                "interface"
            ],
            "properties": {
                "containerName": {
                    "description": "Container name to apply impairments on (e.g., \"clab-my-lab-srl1\").",
                    "type": "string",
                    "example": "clab-my-lab-srl1"
                },
                "corruption": {
                    "description": "Percentage (0.0 to 100.0)",
                    "type": "number",
                    "example": 0.1
                },
                "delay": {
                    "description": "Duration string (e.g., \"100ms\", \"1s\")",
                    "type": "string",
                    "example": "50ms"
                },
                "interface": {
                    "description": "Interface name or interface alias to apply impairments on (e.g., \"eth1\" or \"mgmt0\").",
                    "type": "string",
                    "example": "eth1"
                },
                "jitter": {
                    "description": "Duration string, requires Delay",
                    "type": "string",
                    "example": "5ms"
                },
                "labName": {
                    "description": "Lab name of the topology node to apply impairments on. Used with nodeName instead of containerName.",
                    "type": "string",
                    "example": "my-lab"
                },
                "loss": {
                    "description": "Percentage (0.0 to 100.0)",
                    "type": "number",
                    "example": 10.5
                },
                "nodeName": {
                    "description": "Topology node name to apply impairments on. Used with labName instead of containerName.",
                    "type": "string",
                    "example": "srl1"
                },
                "rate": {
                    "description": "Kbit/s (non-negative integer)",
                    "type": "integer",
                    "example": 1000
                }
            }
        },
        "models.NetemShowResponse": {
            "type": "object",
            "additionalProperties": {
                "type": "array",
                "items": {
                    "$ref": "#/definitions/models.NetemInterfaceInfo"
                }
            }
        },
        "models.NodeBrowserPort": {
            "type": "object",
            "properties": {
                "containerPort": {
                    "type": "integer"
                },
                "description": {
                    "type": "string"
                },
                "hostIp": {
                    "type": "string"
                },
                "hostPort": {
                    "type": "integer"
                },
                "protocol": {
                    "type": "string"
                },
                "proxyPath": {
                    "description": "ProxyPath is set on a lab runtime (c9s), where nodes have no host ports: the\nserver proxies HTTP to the node's port under this path, with the caller's token.",
                    "type": "string",
                    "example": "/api/v1/labs/lab01/nodes/srl1/ports/80/proxy/"
                }
            }
        },
        "models.NodeBrowserPortsResponse": {
            "type": "object",
            "properties": {
                "containerName": {
                    "type": "string"
                },
                "nodeName": {
                    "type": "string"
                },
                "ports": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.NodeBrowserPort"
                    }
                }
            }
        },
        "models.NodeInterfaceInfo": {
            "type": "object",
            "properties": {
                "interfaces": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.InterfaceInfo"
                    }
                },
                "name": {
                    "description": "Name of the container node",
                    "type": "string"
                }
            }
        },
        "models.NodeStats": {
            "type": "object",
            "properties": {
                "cpuCount": {
                    "description": "CPUCount is the number of CPUs the node can use, when known.",
                    "type": "integer",
                    "example": 8
                },
                "cpuPercent": {
                    "description": "CPUPercent is the CPU usage since the previous sample, where 100 is one busy CPU.\nIt is missing in the first sample of a node.",
                    "type": "number",
                    "example": 12.5
                },
                "memoryBytes": {
                    "type": "integer",
                    "example": 536870912
                },
                "memoryLimitBytes": {
                    "type": "integer",
                    "example": 8589934592
                },
                "name": {
                    "description": "Name is the container name.",
                    "type": "string",
                    "example": "clab-my-lab-srl1"
                },
                "nodeName": {
                    "description": "NodeName is the node name in the topology.",
                    "type": "string",
                    "example": "srl1"
                },
                "pids": {
                    "type": "integer",
                    "example": 42
                }
            }
        },
        "models.PasswordChangeRequest": {
            "type": "object",
            "required": [
                "newPassword"
            ],
            "properties": {
                "currentPassword": {
                    "description": "Required if not superuser",
                    "type": "string"
                },
                "newPassword": {
                    "type": "string"
                }
            }
        },
        "models.RenderedTopologyResponse": {
            "type": "object",
            "properties": {
                "content": {
                    "description": "Content is the rendered topology YAML.",
                    "type": "string",
                    "example": "name: lab01"
                },
                "error": {
                    "description": "Error is containerlab's error when it rejects the rendered YAML.",
                    "type": "string"
                },
                "labName": {
                    "description": "LabName is the rendered top-level name with Git name variables expanded.",
                    "type": "string",
                    "example": "lab01"
                },
                "path": {
                    "description": "Path is the requested workspace-relative topology path.",
                    "type": "string",
                    "example": "lab01/lab01.clab.gotmpl"
                },
                "varsFiles": {
                    "description": "VarsFiles lists the variable files used for rendering relative to the\ntopology file's directory, including files containerlab discovered next to it.",
                    "type": "array",
                    "items": {
                        "type": "string"
                    },
                    "example": [
                        "lab01.clab_vars.yml"
                    ]
                }
            }
        },
        "models.RuntimeImageActionResponse": {
            "type": "object",
            "properties": {
                "image": {
                    "type": "string"
                },
                "message": {
                    "type": "string"
                },
                "output": {
                    "type": "string"
                },
                "success": {
                    "type": "boolean"
                }
            }
        },
        "models.RuntimeImagePullRequest": {
            "type": "object",
            "required": [
                "image"
            ],
            "properties": {
                "image": {
                    "type": "string"
                }
            }
        },
        "models.RuntimeImageSummary": {
            "type": "object",
            "properties": {
                "createdAt": {
                    "type": "string"
                },
                "id": {
                    "type": "string"
                },
                "repoDigests": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "repoTags": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "shortId": {
                    "type": "string"
                },
                "size": {
                    "type": "string"
                },
                "virtualSize": {
                    "type": "string"
                }
            }
        },
        "models.RuntimeImagesResponse": {
            "type": "object",
            "properties": {
                "images": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.RuntimeImageSummary"
                    }
                },
                "runtime": {
                    "type": "string"
                }
            }
        },
        "models.SSHAccessRequest": {
            "type": "object",
            "properties": {
                "duration": {
                    "description": "How long the access should be valid for (e.g., \"1h\", \"30m\")",
                    "type": "string"
                },
                "sshUsername": {
                    "description": "Optional override for container's SSH user",
                    "type": "string"
                }
            }
        },
        "models.SSHAccessResponse": {
            "type": "object",
            "properties": {
                "command": {
                    "description": "Example SSH command",
                    "type": "string"
                },
                "expiration": {
                    "description": "When this access expires",
                    "type": "string"
                },
                "host": {
                    "description": "API server's hostname or IP",
                    "type": "string"
                },
                "port": {
                    "description": "Allocated port on API server",
                    "type": "integer"
                },
                "username": {
                    "description": "Username to use for SSH",
                    "type": "string"
                }
            }
        },
        "models.SSHSessionInfo": {
            "type": "object",
            "properties": {
                "created": {
                    "description": "When this access was created",
                    "type": "string"
                },
                "expiration": {
                    "description": "When this access expires",
                    "type": "string"
                },
                "labName": {
                    "description": "Lab name",
                    "type": "string"
                },
                "nodeName": {
                    "description": "Node name",
                    "type": "string"
                },
                "port": {
                    "description": "Allocated port on API server",
                    "type": "integer"
                },
                "username": {
                    "description": "SSH username",
                    "type": "string"
                }
            }
        },
        "models.SSHXShareRequest": {
            "type": "object",
            "properties": {
                "enableReaders": {
                    "description": "EnableReaders also creates a read-only access link.",
                    "type": "boolean",
                    "example": true
                }
            }
        },
        "models.SaveConfigResponse": {
            "type": "object",
            "properties": {
                "copyDir": {
                    "description": "CopyDir is the \u003ccopy\u003e/\u003clab dir\u003e directory the configs were copied to when\ncopy was requested: workspace-relative, @shared/... or absolute.",
                    "type": "string",
                    "example": "lab1/backups/clab-lab1"
                },
                "message": {
                    "description": "Message indicating overall success.",
                    "type": "string"
                },
                "output": {
                    "description": "Detailed output from the 'clab save' command (often from stderr).",
                    "type": "string"
                }
            }
        },
        "models.ServerInfo": {
            "type": "object",
            "properties": {
                "startTime": {
                    "description": "When the server started",
                    "type": "string"
                },
                "uptime": {
                    "description": "Human-readable uptime",
                    "type": "string"
                },
                "version": {
                    "description": "API server version",
                    "type": "string"
                }
            }
        },
        "models.ShareToolResponse": {
            "type": "object",
            "properties": {
                "link": {
                    "type": "string"
                },
                "message": {
                    "type": "string"
                },
                "output": {
                    "type": "string"
                },
                "readOnlyLink": {
                    "description": "ReadOnlyLink is the SSHX read-only access link when readers are enabled.",
                    "type": "string"
                },
                "shell": {
                    "description": "Shell is the effective GoTTY shell (attach/reattach only).",
                    "type": "string"
                },
                "username": {
                    "description": "Username is the effective GoTTY login name (attach/reattach only).",
                    "type": "string"
                }
            }
        },
        "models.SimpleSuccessResponse": {
            "type": "object",
            "properties": {
                "success": {
                    "type": "boolean",
                    "example": true
                }
            }
        },
        "models.SnapshotNodeResult": {
            "type": "object",
            "properties": {
                "durationMs": {
                    "type": "integer",
                    "example": 45000
                },
                "error": {
                    "type": "string"
                },
                "node": {
                    "type": "string",
                    "example": "r1"
                },
                "path": {
                    "description": "Path is the snapshot file, workspace-relative (or @shared/...) when inside a workspace root.",
                    "type": "string",
                    "example": "lab1/snapshots/r1.tar"
                },
                "reason": {
                    "description": "Reason explains skipped nodes.",
                    "type": "string",
                    "example": "not a vrnetlab node (kind=nokia_srlinux, image=ghcr.io/nokia/srlinux)"
                },
                "sizeBytes": {
                    "type": "integer",
                    "example": 104857600
                },
                "status": {
                    "description": "Status is \"success\", \"failed\" or \"skipped\".",
                    "type": "string",
                    "enum": [
                        "success",
                        "failed",
                        "skipped"
                    ],
                    "example": "success"
                }
            }
        },
        "models.SnapshotSaveRequest": {
            "type": "object",
            "properties": {
                "nodes": {
                    "description": "Nodes limits the snapshot to these topology node names (all nodes when empty).",
                    "type": "array",
                    "items": {
                        "type": "string"
                    },
                    "example": [
                        "r1",
                        "r2"
                    ]
                },
                "outputDir": {
                    "description": "OutputDir is a path relative to the topology directory, or an absolute path,\ninside the user's workspace (or the shared root for shared labs). Defaults to \"snapshots\".",
                    "type": "string",
                    "example": "snapshots"
                },
                "timeout": {
                    "description": "Timeout is the per-node wait as a Go duration (default 5m, maximum 30m).",
                    "type": "string",
                    "example": "5m"
                }
            }
        },
        "models.SnapshotSaveResponse": {
            "type": "object",
            "properties": {
                "outputDir": {
                    "description": "OutputDir is workspace-relative (or @shared/...) when inside a workspace root.",
                    "type": "string",
                    "example": "lab1/snapshots"
                },
                "results": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.SnapshotNodeResult"
                    }
                },
                "summary": {
                    "type": "string",
                    "example": "2 succeeded, 0 failed, 1 skipped"
                }
            }
        },
        "models.TailscaleDevice": {
            "type": "object",
            "properties": {
                "authUrl": {
                    "description": "AuthURL is the login URL to open while the device waits for authentication.",
                    "type": "string",
                    "example": "https://login.tailscale.com/a/0123456789ab"
                },
                "backendState": {
                    "description": "BackendState is the tailscaled state, e.g. NeedsLogin or Running.",
                    "type": "string",
                    "example": "NeedsLogin"
                },
                "containerName": {
                    "type": "string",
                    "example": "clab-mylab-ts"
                },
                "dnsName": {
                    "type": "string",
                    "example": "clab-mylab.tailnet-1234.ts.net"
                },
                "error": {
                    "description": "Error reports why the tailnet state could not be read.",
                    "type": "string"
                },
                "forwards": {
                    "description": "Forwards lists the tailnet ports the SSO proxy forwards to nodes (/ts ports).",
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.TailscaleForward"
                    }
                },
                "hostName": {
                    "type": "string",
                    "example": "clab-mylab"
                },
                "nodeName": {
                    "type": "string",
                    "example": "srl1"
                },
                "online": {
                    "type": "boolean"
                },
                "role": {
                    "description": "Role is \"proxy\" for the SSO proxy (mgmt.tailscale.auth-mode: sso), \"sidecar\"\nfor the auth-key sidecar of nodeName, or \"node\" for a node of the tailscale kind.",
                    "type": "string",
                    "example": "proxy"
                },
                "state": {
                    "type": "string",
                    "example": "running"
                },
                "tailscaleIps": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                }
            }
        },
        "models.TailscaleForward": {
            "type": "object",
            "properties": {
                "listenPort": {
                    "type": "integer",
                    "example": 2201
                },
                "nodeName": {
                    "type": "string",
                    "example": "srl1"
                },
                "target": {
                    "type": "string",
                    "example": "172.20.20.2:22"
                }
            }
        },
        "models.TailscaleStatusResponse": {
            "type": "object",
            "properties": {
                "devices": {
                    "type": "array",
                    "items": {
                        "$ref": "#/definitions/models.TailscaleDevice"
                    }
                },
                "labName": {
                    "type": "string",
                    "example": "mylab"
                }
            }
        },
        "models.TerminalProtocol": {
            "type": "string",
            "enum": [
                "ssh",
                "shell",
                "telnet"
            ],
            "x-enum-varnames": [
                "TerminalProtocolSSH",
                "TerminalProtocolShell",
                "TerminalProtocolTelnet"
            ]
        },
        "models.TerminalSessionInfo": {
            "type": "object",
            "properties": {
                "attached": {
                    "description": "Attached reports whether a client is connected to the session's stream.",
                    "type": "boolean"
                },
                "createdAt": {
                    "type": "string"
                },
                "error": {
                    "type": "string"
                },
                "exitCode": {
                    "type": "integer"
                },
                "expiresAt": {
                    "type": "string"
                },
                "labName": {
                    "type": "string"
                },
                "lastActivity": {
                    "type": "string"
                },
                "nodeName": {
                    "type": "string"
                },
                "protocol": {
                    "$ref": "#/definitions/models.TerminalProtocol"
                },
                "resumable": {
                    "description": "Resumable reports whether the session outlives a disconnected stream.",
                    "type": "boolean"
                },
                "sessionId": {
                    "type": "string"
                },
                "state": {
                    "type": "string"
                },
                "username": {
                    "type": "string"
                }
            }
        },
        "models.TerminalSessionRequest": {
            "type": "object",
            "required": [
                "protocol"
            ],
            "properties": {
                "cols": {
                    "description": "Cols is the requested terminal width in characters.",
                    "type": "integer"
                },
                "protocol": {
                    "description": "Protocol selects the terminal transport: ssh, shell, or telnet.",
                    "allOf": [
                        {
                            "$ref": "#/definitions/models.TerminalProtocol"
                        }
                    ]
                },
                "resumable": {
                    "description": "Resumable keeps the session running when its stream disconnects, so the\nclient can attach again. Without it the session ends with its stream.",
                    "type": "boolean"
                },
                "rows": {
                    "description": "Rows is the requested terminal height in characters.",
                    "type": "integer"
                },
                "sshUsername": {
                    "description": "SSHUsername optionally overrides the SSH username for protocol=ssh.",
                    "type": "string"
                },
                "telnetPort": {
                    "description": "TelnetPort optionally overrides the telnet destination port for protocol=telnet.",
                    "type": "integer"
                }
            }
        },
        "models.TopologyDocEventResponse": {
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "example": "change"
                },
                "documentKind": {
                    "type": "string",
                    "example": "annotations"
                },
                "labName": {
                    "type": "string",
                    "example": "mylab"
                },
                "path": {
                    "type": "string",
                    "example": "mylab.clab.yml.annotations.json"
                },
                "revision": {
                    "type": "string",
                    "example": "yaml=123-456;annotations=78-910"
                },
                "type": {
                    "type": "string",
                    "example": "topology-doc"
                }
            }
        },
        "models.TopologyEntry": {
            "type": "object",
            "properties": {
                "absolutePath": {
                    "description": "Exact source path for matching runtime lab paths; file operations use yamlFileName.",
                    "type": "string"
                },
                "annotationsFileName": {
                    "description": "Sidecar path relative to the managed workspace root.",
                    "type": "string"
                },
                "deploymentState": {
                    "description": "undeployed (runtime state is derived from events stream)",
                    "type": "string"
                },
                "hasAnnotations": {
                    "type": "boolean"
                },
                "labName": {
                    "type": "string"
                },
                "yamlFileName": {
                    "description": "Topology path relative to the managed workspace root.",
                    "type": "string"
                }
            }
        },
        "models.TopologyFileRenameRequest": {
            "type": "object",
            "required": [
                "newPath",
                "oldPath"
            ],
            "properties": {
                "newPath": {
                    "type": "string",
                    "example": "configs/startup.bak.cfg"
                },
                "oldPath": {
                    "type": "string",
                    "example": "configs/startup.cfg"
                }
            }
        },
        "models.UserCreateRequest": {
            "type": "object",
            "required": [
                "password",
                "username"
            ],
            "properties": {
                "displayName": {
                    "type": "string"
                },
                "groups": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "isSuperuser": {
                    "type": "boolean"
                },
                "password": {
                    "type": "string"
                },
                "shell": {
                    "description": "Default to /bin/bash if empty",
                    "type": "string"
                },
                "username": {
                    "type": "string"
                }
            }
        },
        "models.UserDetails": {
            "type": "object",
            "properties": {
                "displayName": {
                    "description": "Full name from GECOS field",
                    "type": "string"
                },
                "gid": {
                    "type": "string"
                },
                "groups": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "homeDir": {
                    "type": "string"
                },
                "isApiUser": {
                    "type": "boolean"
                },
                "isSuperuser": {
                    "type": "boolean"
                },
                "shell": {
                    "type": "string"
                },
                "uid": {
                    "type": "string"
                },
                "username": {
                    "type": "string"
                }
            }
        },
        "models.UserUpdateRequest": {
            "type": "object",
            "properties": {
                "displayName": {
                    "type": "string"
                },
                "groups": {
                    "type": "array",
                    "items": {
                        "type": "string"
                    }
                },
                "isSuperuser": {
                    "type": "boolean"
                },
                "shell": {
                    "type": "string"
                }
            }
        },
        "models.ValidateTopologyResponse": {
            "type": "object",
            "properties": {
                "error": {
                    "description": "Error is containerlab's validation error when Valid is false.",
                    "type": "string"
                },
                "labName": {
                    "type": "string",
                    "example": "lab01"
                },
                "links": {
                    "type": "integer",
                    "example": 1
                },
                "nodes": {
                    "type": "integer",
                    "example": 2
                },
                "path": {
                    "type": "string",
                    "example": "/home/alice/.clab/lab01/lab01.clab.yml"
                },
                "valid": {
                    "type": "boolean",
                    "example": true
                }
            }
        },
        "models.VersionCheckResponse": {
            "type": "object",
            "properties": {
                "checkResult": {
                    "description": "Raw output string from 'clab version check'.",
                    "type": "string",
                    "example": "🎉 A newer containerlab version (0.62.2) is available!\nRelease notes: https://containerlab.dev/rn/0.62/#0622\nRun 'sudo clab version upgrade' or see https://containerlab.dev/install/ for installation options."
                }
            }
        },
        "models.VersionResponse": {
            "type": "object",
            "properties": {
                "versionInfo": {
                    "description": "Raw output string from 'clab version'.",
                    "type": "string",
                    "example": "version: 0.50.0\ncommit: 1234abcd\ndate: 2024-01-15T10:00:00Z\ngithub: https://github.com/srl-labs/containerlab\nrelease notes: https://containerlab.dev/rn/0.50/"
                }
            }
        },
        "models.VethCreateRequest": {
            "type": "object",
            "required": [
                "aEndpoint",
                "bEndpoint"
            ],
            "properties": {
                "aEndpoint": {
                    "description": "Endpoint A definition. Format: \u003cnode-name\u003e:\u003cinterface-name\u003e OR \u003ckind\u003e:\u003cnode-name\u003e:\u003cinterface-name\u003e\nExample: \"clab-demo-node1:eth1\" or \"bridge:br-1:br-eth1\" or \"host:veth-eth1\"",
                    "type": "string",
                    "example": "clab-demo-node1:eth1"
                },
                "bEndpoint": {
                    "description": "Endpoint B definition. Format: \u003cnode-name\u003e:\u003cinterface-name\u003e OR \u003ckind\u003e:\u003cnode-name\u003e:\u003cinterface-name\u003e\nExample: \"clab-demo-node2:eth1\" or \"ovs-bridge:ovsbr-1:br-eth1\"",
                    "type": "string",
                    "example": "clab-demo-node2:eth1"
                },
                "mtu": {
                    "description": "MTU for the vEth pair. Defaults to 9500 if omitted.",
                    "type": "integer",
                    "example": 1500
                }
            }
        },
        "models.VxlanCreateRequest": {
            "type": "object",
            "required": [
                "link",
                "remote"
            ],
            "properties": {
                "dev": {
                    "description": "Optional: Linux device to use for the tunnel source. Auto-detected if omitted.",
                    "type": "string",
                    "example": "eth0"
                },
                "id": {
                    "description": "VxLAN Network Identifier (VNI). Defaults to 10 if omitted.",
                    "type": "integer",
                    "example": 100
                },
                "link": {
                    "description": "Name of the existing interface in the root namespace to bridge traffic with.",
                    "type": "string",
                    "example": "srl_e1-1"
                },
                "mtu": {
                    "description": "Optional: MTU for the VxLAN interface. Auto-calculated if omitted.",
                    "type": "integer",
                    "example": 1400
                },
                "port": {
                    "description": "UDP port number for the VxLAN tunnel. Defaults to 14789 if omitted.",
                    "type": "integer",
                    "example": 4789
                },
                "remote": {
                    "description": "Remote VTEP IP address.",
                    "type": "string",
                    "example": "10.0.0.20"
                }
            }
        },
        "models.WorkspaceDirectoryRequest": {
            "type": "object",
            "required": [
                "path"
            ],
            "properties": {
                "path": {
                    "type": "string",
                    "example": "lab1/configs"
                }
            }
        },
        "models.WorkspaceFileEntry": {
            "type": "object",
            "properties": {
                "hasChildren": {
                    "type": "boolean"
                },
                "kind": {
                    "description": "file or directory",
                    "type": "string"
                },
                "modifiedAt": {
                    "type": "string"
                },
                "name": {
                    "type": "string"
                },
                "path": {
                    "type": "string"
                },
                "size": {
                    "type": "integer"
                }
            }
        },
        "models.WorkspaceFileEventResponse": {
            "type": "object",
            "properties": {
                "action": {
                    "type": "string",
                    "example": "delete"
                },
                "kind": {
                    "type": "string",
                    "example": "file"
                },
                "parentPath": {
                    "type": "string",
                    "example": "lab1/configs"
                },
                "path": {
                    "type": "string",
                    "example": "lab1/configs/startup.cfg"
                },
                "type": {
                    "type": "string",
                    "example": "workspace-file"
                }
            }
        },
        "models.WorkspaceFileRenameRequest": {
            "type": "object",
            "required": [
                "newPath",
                "oldPath"
            ],
            "properties": {
                "newPath": {
                    "type": "string",
                    "example": "lab1/configs/startup.bak.cfg"
                },
                "oldPath": {
                    "type": "string",
                    "example": "lab1/configs/startup.cfg"
                }
            }
        }
    },
    "securityDefinitions": {
        "BearerAuth": {
            "description": "Type \"Bearer\" followed by a space and JWT token. Example: \"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\"",
            "type": "apiKey",
            "name": "Authorization",
            "in": "header"
        }
    }
}