{
  "openapi": "3.0.3",
  "info": {
    "title": "Topology Design System API",
    "version": "1.0.0",
    "description": "RESTful API wrapper for the Topology Design System — a cinematic network topology visualization engine. Provides CRUD operations for topologies, nodes, links, anchors, acts, steps, and glossary terms, plus graph analysis endpoints.",
    "contact": {
      "name": "Topology DS Team"
    },
    "license": {
      "name": "ISC"
    }
  },
  "servers": [
    {
      "url": "http://localhost:3000",
      "description": "Local dev"
    }
  ],
  "tags": [
    {
      "name": "Topologies",
      "description": "Topology lifecycle (create, list, get, delete)"
    },
    {
      "name": "Nodes",
      "description": "Network node CRUD"
    },
    {
      "name": "Links",
      "description": "Connection/link CRUD"
    },
    {
      "name": "Anchors",
      "description": "Invisible anchor point CRUD"
    },
    {
      "name": "Acts",
      "description": "Presentation act management"
    },
    {
      "name": "Steps",
      "description": "Choreography step management"
    },
    {
      "name": "Glossary",
      "description": "Glossary term management"
    },
    {
      "name": "Analysis",
      "description": "Graph analysis & algorithms"
    },
    {
      "name": "Playback",
      "description": "Playback state control (limited REST compat)"
    },
    {
      "name": "Export",
      "description": "Serialization & export"
    },
    {
      "name": "Layers",
      "description": "Visual layer management (physical, flow, policy)"
    },
    {
      "name": "FlowPaths",
      "description": "Animated overlay flow paths through the topology"
    },
    {
      "name": "PolicyMarkers",
      "description": "Policy action badges on nodes"
    },
    {
      "name": "Meta",
      "description": "Registered types & system info"
    }
  ],
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "properties": {
          "error": {
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "error"
        ]
      },
      "TopologyConfig": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "example": "SD-WAN Network"
          },
          "subtitle": {
            "type": "string",
            "example": "Branch Architecture"
          },
          "viewBox": {
            "type": "string",
            "example": "0 0 1050 700"
          },
          "phaseMs": {
            "type": "number",
            "example": 600
          },
          "drawDuration": {
            "type": "number",
            "example": 0.7
          },
          "fadeDuration": {
            "type": "number",
            "example": 0.55
          },
          "mode": {
            "type": "string",
            "enum": [
              "manual",
              "auto",
              "presenter"
            ],
            "default": "manual"
          },
          "speedMs": {
            "type": "number",
            "example": 4000
          },
          "routing": {
            "type": "boolean",
            "default": true
          }
        }
      },
      "TopologySummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": "string"
          },
          "subtitle": {
            "type": "string"
          },
          "nodeCount": {
            "type": "integer"
          },
          "linkCount": {
            "type": "integer"
          },
          "stepCount": {
            "type": "integer"
          }
        }
      },
      "TopologyExport": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string"
          },
          "subtitle": {
            "type": "string"
          },
          "viewBox": {
            "type": "string"
          },
          "phaseMs": {
            "type": "number"
          },
          "drawDuration": {
            "type": "number"
          },
          "fadeDuration": {
            "type": "number"
          },
          "nodes": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/NodeConfig"
            }
          },
          "anchors": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/AnchorPosition"
            }
          },
          "links": {
            "type": "object",
            "additionalProperties": {
              "$ref": "#/components/schemas/LinkConfig"
            }
          },
          "acts": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ActConfig"
            }
          },
          "steps": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StepConfig"
            }
          },
          "glossary": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GlossaryTerm"
            }
          }
        }
      },
      "NodeConfig": {
        "type": "object",
        "required": [
          "type",
          "x",
          "y"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "ec",
              "switch",
              "switchEnterprise",
              "cloud",
              "host",
              "connector",
              "apps",
              "saas",
              "server",
              "router",
              "firewall",
              "database",
              "idcard",
              "ap",
              "overlayCloud",
              "text",
              "shapeArrow",
              "shapeSquare",
              "shapeRectangle",
              "shapeTriangle",
              "shapeCircle",
              "shapeEllipse",
              "shapeDiamond",
              "shapePentagon",
              "shapeHexagon",
              "shapeStar",
              "shapeCross"
            ]
          },
          "x": {
            "type": "number",
            "example": 200
          },
          "y": {
            "type": "number",
            "example": 300
          },
          "label": {
            "type": "string",
            "example": "Seattle DC"
          },
          "sublabel": {
            "type": "string"
          },
          "labelColor": {
            "type": "string",
            "example": "#e6e8e9"
          },
          "labelOffset": {
            "type": "number",
            "example": 24
          },
          "labelY": {
            "type": "number"
          },
          "color": {
            "type": "string",
            "example": "#01a982"
          },
          "variant": {
            "type": "string",
            "enum": [
              "generic",
              "virtual",
              "physical",
              "aws",
              "azure",
              "gcp",
              "oracle"
            ],
            "description": "EC node variant only"
          },
          "vrrp": {
            "type": "string",
            "enum": [
              "active",
              "standby"
            ]
          },
          "sub1": {
            "type": "string",
            "description": "Cloud sub-label 1"
          },
          "sub2": {
            "type": "string",
            "description": "Cloud sub-label 2"
          },
          "innerClouds": {
            "type": "string",
            "enum": [
              "both",
              "left",
              "right"
            ]
          },
          "managed": {
            "type": "boolean"
          },
          "agent": {
            "type": "boolean"
          },
          "spans": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Node IDs for overlayCloud spanning"
          },
          "sideLabel": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number"
              },
              "y": {
                "type": "number"
              },
              "label": {
                "type": "string"
              },
              "sublabel": {
                "type": "string"
              },
              "color": {
                "type": "string"
              },
              "anchor": {
                "type": "string"
              }
            }
          },
          "zoneIndicator": {
            "type": "string",
            "enum": [
              "left",
              "right"
            ]
          },
          "zoneLabel": {
            "type": "object",
            "properties": {
              "text": {
                "type": "string"
              },
              "color": {
                "type": "string"
              }
            }
          }
        }
      },
      "LinkConfig": {
        "type": "object",
        "required": [
          "type",
          "from",
          "to"
        ],
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "line",
              "tunnel",
              "wireguard",
              "flow",
              "packet",
              "blocked",
              "wifi",
              "poe",
              "optical"
            ]
          },
          "from": {
            "type": "string",
            "description": "Source node or anchor ID"
          },
          "to": {
            "type": "string",
            "description": "Destination node or anchor ID"
          },
          "color": {
            "type": "string",
            "example": "#01a982"
          },
          "label": {
            "type": "string"
          },
          "path": {
            "type": "string",
            "description": "Custom SVG path (flow type)"
          },
          "labelPos": {
            "type": "object",
            "properties": {
              "x": {
                "type": "number"
              },
              "y": {
                "type": "number"
              }
            }
          },
          "dashed": {
            "type": "boolean"
          },
          "dots": {
            "type": "boolean",
            "default": true
          },
          "fromLabel": {
            "type": "string",
            "description": "Port label at source"
          },
          "toLabel": {
            "type": "string",
            "description": "Port label at destination"
          },
          "sublabel": {
            "type": "string"
          },
          "reason": {
            "type": "string",
            "description": "Reason for blocked link"
          }
        }
      },
      "AnchorPosition": {
        "type": "object",
        "required": [
          "x",
          "y"
        ],
        "properties": {
          "x": {
            "type": "number",
            "example": 400
          },
          "y": {
            "type": "number",
            "example": 250
          }
        }
      },
      "ActConfig": {
        "type": "object",
        "required": [
          "label"
        ],
        "properties": {
          "label": {
            "type": "string",
            "example": "Network Foundation"
          },
          "color": {
            "type": "string",
            "example": "#01a982"
          },
          "intro": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Introduction lines shown at act start"
          }
        }
      },
      "Phase": {
        "type": "object",
        "properties": {
          "show": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Element IDs to reveal"
          },
          "diff": {
            "type": "string",
            "description": "Narrator text for this phase"
          }
        }
      },
      "StepConfig": {
        "type": "object",
        "required": [
          "act"
        ],
        "properties": {
          "act": {
            "type": "string",
            "description": "Parent act ID"
          },
          "name": {
            "type": "string",
            "example": "The Network"
          },
          "goal": {
            "type": "string",
            "description": "Description of what this step shows"
          },
          "focus": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Element IDs to spotlight (empty = all)"
          },
          "phases": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Phase"
            }
          }
        }
      },
      "GlossaryTerm": {
        "type": "object",
        "required": [
          "t",
          "d"
        ],
        "properties": {
          "t": {
            "type": "string",
            "description": "Term",
            "example": "SD-WAN"
          },
          "d": {
            "type": "string",
            "description": "Definition",
            "example": "Software-Defined WAN"
          }
        }
      },
      "AnalysisFinding": {
        "type": "object",
        "properties": {
          "type": {
            "type": "string",
            "enum": [
              "single-point-of-failure",
              "bridge-link",
              "isolated-node",
              "disconnected-graph"
            ]
          },
          "severity": {
            "type": "string",
            "enum": [
              "critical",
              "warning",
              "info"
            ]
          },
          "message": {
            "type": "string"
          },
          "elements": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "BlastRadius": {
        "type": "object",
        "properties": {
          "isolated": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Nodes completely cut off"
          },
          "affected": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Nodes with degraded connectivity"
          }
        }
      },
      "PathResult": {
        "type": "object",
        "properties": {
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "path": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "length": {
            "type": "integer"
          }
        }
      },
      "PlaybackState": {
        "type": "object",
        "properties": {
          "step": {
            "type": "integer"
          },
          "totalSteps": {
            "type": "integer"
          },
          "playing": {
            "type": "boolean"
          },
          "mode": {
            "type": "string",
            "enum": [
              "manual",
              "auto",
              "presenter"
            ]
          },
          "speedMs": {
            "type": "number"
          }
        }
      },
      "PositionKeyframes": {
        "type": "object",
        "additionalProperties": {
          "type": "object",
          "properties": {
            "x": {
              "type": "number"
            },
            "y": {
              "type": "number"
            }
          }
        },
        "description": "Map of step number to {x, y} position",
        "example": {
          "1": {
            "x": 200,
            "y": 300
          },
          "5": {
            "x": 500,
            "y": 300
          }
        }
      }
    },
    "parameters": {
      "topologyId": {
        "name": "topologyId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string",
          "format": "uuid"
        },
        "description": "Topology UUID"
      },
      "nodeId": {
        "name": "nodeId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Node identifier"
      },
      "linkId": {
        "name": "linkId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Link identifier"
      },
      "anchorId": {
        "name": "anchorId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "description": "Anchor identifier"
      }
    }
  },
  "paths": {
    "/api/topologies": {
      "get": {
        "tags": [
          "Topologies"
        ],
        "summary": "List all topologies",
        "responses": {
          "200": {
            "description": "Array of topology summaries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/TopologySummary"
                  }
                }
              }
            }
          }
        }
      },
      "post": {
        "tags": [
          "Topologies"
        ],
        "summary": "Create a new topology",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TopologyConfig"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Created topology",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopologySummary"
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}": {
      "get": {
        "tags": [
          "Topologies"
        ],
        "summary": "Get full topology export (toJSON)",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Full topology data",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopologyExport"
                }
              }
            }
          },
          "404": {
            "description": "Topology not found",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Topologies"
        ],
        "summary": "Replace entire topology (fromJSON)",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TopologyExport"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated topology",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopologySummary"
                }
              }
            }
          },
          "404": {
            "description": "Topology not found"
          }
        }
      },
      "delete": {
        "tags": [
          "Topologies"
        ],
        "summary": "Delete a topology",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Topology not found"
          }
        }
      }
    },
    "/api/topologies/import": {
      "post": {
        "tags": [
          "Topologies"
        ],
        "summary": "Create topology from full JSON export",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/TopologyExport"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Imported topology",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopologySummary"
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/nodes": {
      "get": {
        "tags": [
          "Nodes"
        ],
        "summary": "List all nodes",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Map of node ID to config",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/NodeConfig"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Topology not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/nodes/{nodeId}": {
      "get": {
        "tags": [
          "Nodes"
        ],
        "summary": "Get a single node",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/nodeId"
          }
        ],
        "responses": {
          "200": {
            "description": "Node config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeConfig"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        }
      },
      "put": {
        "tags": [
          "Nodes"
        ],
        "summary": "Create or update a node",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/nodeId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/NodeConfig"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Node created/updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/NodeConfig"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Nodes"
        ],
        "summary": "Delete a node (and connected links)",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/nodeId"
          }
        ],
        "responses": {
          "200": {
            "description": "Node deleted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "removedLinks": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Node not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/nodes/{nodeId}/neighbors": {
      "get": {
        "tags": [
          "Nodes"
        ],
        "summary": "Get neighbor node IDs",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/nodeId"
          }
        ],
        "responses": {
          "200": {
            "description": "Neighbor node IDs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "nodeId": {
                      "type": "string"
                    },
                    "neighbors": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "degree": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/nodes/{nodeId}/blast-radius": {
      "get": {
        "tags": [
          "Analysis"
        ],
        "summary": "Compute blast radius of a node failure",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/nodeId"
          }
        ],
        "responses": {
          "200": {
            "description": "Blast radius result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BlastRadius"
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/nodes/{nodeId}/positions": {
      "put": {
        "tags": [
          "Nodes"
        ],
        "summary": "Set node position keyframes for choreography animation",
        "description": "**Limited REST compatibility.** Position keyframes define where a node\nmoves to at each step for smooth animation. This is a visual/choreography\nfeature that only takes effect during DOM-based rendering.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/nodeId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PositionKeyframes"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Keyframes applied",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "nodeId": {
                      "type": "string"
                    },
                    "keyframes": {
                      "$ref": "#/components/schemas/PositionKeyframes"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/links": {
      "get": {
        "tags": [
          "Links"
        ],
        "summary": "List all links",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Map of link ID to config",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/LinkConfig"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/links/{linkId}": {
      "get": {
        "tags": [
          "Links"
        ],
        "summary": "Get a single link",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/linkId"
          }
        ],
        "responses": {
          "200": {
            "description": "Link config",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkConfig"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        }
      },
      "put": {
        "tags": [
          "Links"
        ],
        "summary": "Create or update a link",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/linkId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LinkConfig"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Link created/updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LinkConfig"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Links"
        ],
        "summary": "Delete a link",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/linkId"
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/anchors": {
      "get": {
        "tags": [
          "Anchors"
        ],
        "summary": "List all anchors",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Map of anchor ID to position",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "$ref": "#/components/schemas/AnchorPosition"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/anchors/{anchorId}": {
      "get": {
        "tags": [
          "Anchors"
        ],
        "summary": "Get an anchor",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/anchorId"
          }
        ],
        "responses": {
          "200": {
            "description": "Anchor position",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnchorPosition"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        }
      },
      "put": {
        "tags": [
          "Anchors"
        ],
        "summary": "Create or update an anchor",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/anchorId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/AnchorPosition"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Anchor created/updated",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AnchorPosition"
                }
              }
            }
          }
        }
      },
      "delete": {
        "tags": [
          "Anchors"
        ],
        "summary": "Delete an anchor (and connected links)",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "$ref": "#/components/parameters/anchorId"
          }
        ],
        "responses": {
          "200": {
            "description": "Anchor deleted with list of removed links",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "deleted": {
                      "type": "boolean"
                    },
                    "removedLinks": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/acts": {
      "get": {
        "tags": [
          "Acts"
        ],
        "summary": "List all acts",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of acts",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "allOf": [
                      {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          }
                        }
                      },
                      {
                        "$ref": "#/components/schemas/ActConfig"
                      }
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/acts/{actId}": {
      "put": {
        "tags": [
          "Acts"
        ],
        "summary": "Create or update an act",
        "description": "Acts are ordered; new acts are appended. Updating an existing act\nmodifies it in place. **Note:** acts cannot be reordered via REST —\nuse PUT on the full topology to reorder.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "name": "actId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ActConfig"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Act created/updated"
          }
        }
      },
      "delete": {
        "tags": [
          "Acts"
        ],
        "summary": "Delete an act",
        "description": "Removes the act but does NOT remove its steps. Orphaned steps\nremain and can be reassigned to another act.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "name": "actId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/steps": {
      "get": {
        "tags": [
          "Steps"
        ],
        "summary": "List all steps",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Ordered array of steps",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "allOf": [
                      {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "index": {
                            "type": "integer"
                          }
                        }
                      },
                      {
                        "$ref": "#/components/schemas/StepConfig"
                      }
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/steps/{stepId}": {
      "put": {
        "tags": [
          "Steps"
        ],
        "summary": "Create or update a step",
        "description": "New steps are appended to the end. Existing steps are updated in place.\n**Note:** The `onRender` and `phase.custom` callback properties from the\nJS API are NOT supported — they require JavaScript functions which cannot\nbe serialized over REST.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "name": "stepId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/StepConfig"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Step created/updated"
          },
          "201": {
            "description": "Step created"
          }
        }
      },
      "delete": {
        "tags": [
          "Steps"
        ],
        "summary": "Delete a step",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "name": "stepId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/glossary": {
      "get": {
        "tags": [
          "Glossary"
        ],
        "summary": "Get glossary terms",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of glossary terms",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GlossaryTerm"
                  }
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Glossary"
        ],
        "summary": "Replace all glossary terms",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/GlossaryTerm"
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Glossary updated",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/GlossaryTerm"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/analysis": {
      "get": {
        "tags": [
          "Analysis"
        ],
        "summary": "Run full topology analysis",
        "description": "Identifies single points of failure, bridge links, isolated nodes,\nand disconnected components.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of findings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AnalysisFinding"
                  }
                }
              }
            }
          },
          "501": {
            "description": "Graph module not loaded"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/paths": {
      "get": {
        "tags": [
          "Analysis"
        ],
        "summary": "Find shortest path between two nodes",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "name": "from",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Shortest path result",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PathResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing from/to query parameters"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/components": {
      "get": {
        "tags": [
          "Analysis"
        ],
        "summary": "Find connected components",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of connected components (each an array of node IDs)",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "components": {
                      "type": "array",
                      "items": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/bridges": {
      "get": {
        "tags": [
          "Analysis"
        ],
        "summary": "Find bridge links (single points of failure)",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of bridge link IDs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "bridges": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/articulation-points": {
      "get": {
        "tags": [
          "Analysis"
        ],
        "summary": "Find articulation points (nodes whose removal disconnects the graph)",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Array of articulation point node IDs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "articulationPoints": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/bfs": {
      "get": {
        "tags": [
          "Analysis"
        ],
        "summary": "Breadth-first search traversal from a node",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          },
          {
            "name": "start",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "BFS traversal order",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "start": {
                      "type": "string"
                    },
                    "order": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/subgraph": {
      "post": {
        "tags": [
          "Analysis"
        ],
        "summary": "Extract a subgraph for the given node IDs",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "nodeIds"
                ],
                "properties": {
                  "nodeIds": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Extracted subgraph",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "nodes": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/NodeConfig"
                      }
                    },
                    "links": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/LinkConfig"
                      }
                    },
                    "anchors": {
                      "type": "object",
                      "additionalProperties": {
                        "$ref": "#/components/schemas/AnchorPosition"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/playback": {
      "get": {
        "tags": [
          "Playback"
        ],
        "summary": "Get current playback state",
        "description": "**Limited REST compatibility.** Playback is inherently stateful and\ntime-based. This endpoint exposes the current step and mode, but\nreal-time play/pause requires a WebSocket connection (not provided).\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Current playback state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaybackState"
                }
              }
            }
          }
        }
      },
      "put": {
        "tags": [
          "Playback"
        ],
        "summary": "Set playback state (step, mode)",
        "description": "**Limited REST compatibility.** Can set the current step number and mode,\nbut continuous play/pause/animation timing cannot be driven via REST.\nThe `playing` field is read-only in this context.\n",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "step": {
                    "type": "integer",
                    "description": "Jump to step number"
                  },
                  "mode": {
                    "type": "string",
                    "enum": [
                      "manual",
                      "auto",
                      "presenter"
                    ]
                  },
                  "speedMs": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated playback state",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlaybackState"
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/export": {
      "get": {
        "tags": [
          "Export"
        ],
        "summary": "Export topology as JSON (same as GET topology, explicit export intent)",
        "parameters": [
          {
            "$ref": "#/components/parameters/topologyId"
          }
        ],
        "responses": {
          "200": {
            "description": "Full topology JSON",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TopologyExport"
                }
              }
            }
          }
        }
      }
    },
    "/api/meta/node-types": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "List all registered node types (built-in + plugins)",
        "responses": {
          "200": {
            "description": "Array of node type names",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "nodeTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/meta/link-types": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "List all registered link types (built-in + plugins)",
        "responses": {
          "200": {
            "description": "Array of link type names",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "linkTypes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/health": {
      "get": {
        "tags": [
          "Meta"
        ],
        "summary": "Health check",
        "responses": {
          "200": {
            "description": "Service status",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "example": "ok"
                    },
                    "topologyCount": {
                      "type": "integer"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/layers": {
      "get": {
        "tags": [
          "Layers"
        ],
        "summary": "List all layers",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Array of layer objects",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "name": {
                        "type": "string"
                      },
                      "type": {
                        "type": "string",
                        "enum": [
                          "physical",
                          "flow",
                          "policy"
                        ]
                      },
                      "visible": {
                        "type": "boolean"
                      },
                      "opacity": {
                        "type": "number"
                      },
                      "locked": {
                        "type": "boolean"
                      },
                      "color": {
                        "type": "string"
                      },
                      "order": {
                        "type": "number"
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/layers/{layerId}": {
      "put": {
        "tags": [
          "Layers"
        ],
        "summary": "Create or update a layer",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "layerId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "physical",
                      "flow",
                      "policy"
                    ]
                  },
                  "visible": {
                    "type": "boolean"
                  },
                  "opacity": {
                    "type": "number"
                  },
                  "locked": {
                    "type": "boolean"
                  },
                  "color": {
                    "type": "string"
                  },
                  "order": {
                    "type": "number"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated layer"
          }
        }
      },
      "delete": {
        "tags": [
          "Layers"
        ],
        "summary": "Delete a layer",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "layerId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Layer deleted"
          },
          "404": {
            "description": "Layer not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/flow-paths": {
      "get": {
        "tags": [
          "FlowPaths"
        ],
        "summary": "List all flow paths",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Object of flow path entries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/flow-paths/{flowPathId}": {
      "put": {
        "tags": [
          "FlowPaths"
        ],
        "summary": "Create or update a flow path",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "flowPathId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "waypoints": {
                    "type": "array",
                    "items": {
                      "type": "string"
                    }
                  },
                  "color": {
                    "type": "string"
                  },
                  "animation": {
                    "type": "string",
                    "enum": [
                      "particles",
                      "dashed",
                      "pulse"
                    ]
                  },
                  "speed": {
                    "type": "number"
                  },
                  "direction": {
                    "type": "string",
                    "enum": [
                      "forward",
                      "reverse",
                      "bidirectional"
                    ]
                  },
                  "width": {
                    "type": "number"
                  },
                  "opacity": {
                    "type": "number"
                  },
                  "label": {
                    "type": "string"
                  },
                  "layer": {
                    "type": "string"
                  },
                  "name": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated flow path"
          }
        }
      },
      "delete": {
        "tags": [
          "FlowPaths"
        ],
        "summary": "Delete a flow path",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "flowPathId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Flow path deleted"
          },
          "404": {
            "description": "Flow path not found"
          }
        }
      }
    },
    "/api/topologies/{topologyId}/policy-markers": {
      "get": {
        "tags": [
          "PolicyMarkers"
        ],
        "summary": "List all policy markers",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Object of policy marker entries",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "additionalProperties": {
                    "type": "object"
                  }
                }
              }
            }
          }
        }
      }
    },
    "/api/topologies/{topologyId}/policy-markers/{markerId}": {
      "put": {
        "tags": [
          "PolicyMarkers"
        ],
        "summary": "Create or update a policy marker",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "markerId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "nodeId": {
                    "type": "string"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "inspect",
                      "allow",
                      "deny",
                      "redirect",
                      "encrypt",
                      "decrypt",
                      "nat",
                      "load-balance",
                      "log"
                    ]
                  },
                  "color": {
                    "type": "string"
                  },
                  "label": {
                    "type": "string"
                  },
                  "flowPathId": {
                    "type": "string"
                  },
                  "layer": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated policy marker"
          }
        }
      },
      "delete": {
        "tags": [
          "PolicyMarkers"
        ],
        "summary": "Delete a policy marker",
        "parameters": [
          {
            "name": "topologyId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "markerId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Policy marker deleted"
          },
          "404": {
            "description": "Policy marker not found"
          }
        }
      }
    }
  }
}