{"components":{"schemas":{"UpdateResourceRequest":{"properties":{"metadata":{"type":"object"},"status":{"type":"string"}},"type":"object"},"ResourceLocation":{"properties":{"node":{"oneOf":[{"$ref":"#/components/schemas/Node"},{"type":"null"}]},"node_id":{"nullable":true,"type":"string"},"resource_id":{"type":"string"}},"type":"object"},"RegisterNodeRequest":{"properties":{"capacity":{"type":"object"},"group":{"type":"string"},"id":{"type":"string"},"metadata":{"type":"object"},"type":{"enum":["live","bot"],"type":"string"}},"required":["id","type"],"type":"object"},"AddDestinationRequest":{"properties":{"destination":{"type":"object"}},"required":["destination"],"type":"object"},"UpdateDestinationsRequest":{"properties":{"destinations":{"items":{"type":"object"},"type":"array"}},"required":["destinations"],"type":"object"},"ResourceList":{"properties":{"resources":{"items":{"$ref":"#/components/schemas/Resource"},"type":"array"}},"type":"object"},"ResourceRemoved":{"properties":{"removed":{"type":"boolean"},"resource":{"$ref":"#/components/schemas/Resource"}},"type":"object"},"RegisterResourceRequest":{"properties":{"id":{"type":"string"},"metadata":{"type":"object"},"node_id":{"type":"string"},"platform":{"enum":["twitch","youtube","kick","trovo","dlive","irc"],"type":"string"},"type":{"enum":["stream","channel"],"type":"string"}},"required":["id","type"],"type":"object"},"DestinationsUpdated":{"properties":{"changes":{"type":"object"},"stream_id":{"type":"string"}},"type":"object"},"ClusterHealthy":{"properties":{"leader":{"type":"string"},"members":{"type":"integer"},"status":{"enum":["healthy"],"type":"string"}},"type":"object"},"NodeList":{"properties":{"nodes":{"items":{"$ref":"#/components/schemas/Node"},"type":"array"}},"type":"object"},"ChannelConfigUpdated":{"properties":{"channel_id":{"type":"string"},"status":{"enum":["updated"],"type":"string"}},"type":"object"},"Resource":{"properties":{"assigned_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"metadata":{"type":"object"},"node_id":{"nullable":true,"type":"string"},"platform":{"enum":["twitch","youtube","kick","trovo","dlive","irc"],"nullable":true,"type":"string"},"started_at":{"format":"date-time","type":"string"},"status":{"enum":["active","pending","orphaned"],"type":"string"},"type":{"enum":["stream","channel"],"type":"string"}},"type":"object"},"Node":{"properties":{"capacity":{"type":"object"},"connected_at":{"format":"date-time","type":"string"},"group":{"type":"string"},"id":{"type":"string"},"last_heartbeat":{"format":"date-time","type":"string"},"metadata":{"type":"object"},"status":{"type":"string"},"type":{"enum":["live","bot"],"type":"string"}},"type":"object"},"NodeRemoved":{"properties":{"orphaned_resources":{"type":"integer"},"removed":{"type":"boolean"}},"type":"object"},"NodeDetail":{"properties":{"node":{"$ref":"#/components/schemas/Node"},"resources":{"items":{"$ref":"#/components/schemas/Resource"},"type":"array"}},"type":"object"},"ClusterStatus":{"properties":{"nodes":{"properties":{"bot":{"type":"integer"},"live":{"type":"integer"},"total":{"type":"integer"}},"type":"object"},"raft":{"properties":{"healthy":{"type":"boolean"},"leader":{"type":"string"},"members":{"type":"integer"}},"type":"object"},"resources":{"properties":{"channels":{"type":"integer"},"orphaned":{"type":"integer"},"streams":{"type":"integer"},"total":{"type":"integer"}},"type":"object"}},"type":"object"},"StreamDetail":{"properties":{"assigned_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"metadata":{"type":"object"},"node_id":{"nullable":true,"type":"string"},"started_at":{"format":"date-time","type":"string"},"status":{"type":"string"},"type":{"type":"string"}},"type":"object"},"MigrateResourceRequest":{"properties":{"target_node_id":{"type":"string"}},"required":["target_node_id"],"type":"object"},"CreateStreamRequest":{"properties":{"audio_channels":{"type":"integer"},"destinations":{"items":{"type":"object"},"type":"array"},"input_url":{"format":"uri","type":"string"},"layers":{"items":{"type":"object"},"type":"array"},"protocol":{"default":"srt","enum":["rtmp","srt"],"type":"string"},"stream_id":{"type":"string"}},"required":["stream_id","input_url"],"type":"object"},"StreamStopped":{"properties":{"status":{"enum":["stopped"],"type":"string"},"stream_id":{"type":"string"}},"type":"object"},"DestinationRemoved":{"properties":{"destination_id":{"type":"string"},"status":{"enum":["destination_removed"],"type":"string"}},"type":"object"},"ClusterUnhealthy":{"properties":{"error":{"type":"string"},"status":{"enum":["unhealthy"],"type":"string"}},"type":"object"},"UpdateChannelConfigRequest":{"properties":{"config":{"type":"object"},"destinations":{"items":{"type":"object"},"type":"array"},"enabled":{"default":true,"type":"boolean"},"layers":{"items":{"type":"object"},"type":"array"},"limits":{"type":"object"},"settings":{"type":"object"},"stream_id":{"type":"string"},"stream_key":{"type":"string"},"vod_retention_days":{"nullable":true,"type":"integer"},"vod_timeout_seconds":{"nullable":true,"type":"integer"}},"type":"object"},"StreamStarted":{"properties":{"node_id":{"type":"string"},"status":{"enum":["started"],"type":"string"},"stream_id":{"type":"string"}},"type":"object"},"ChannelRemoved":{"properties":{"channel_id":{"type":"string"},"status":{"enum":["removed"],"type":"string"}},"type":"object"},"HeartbeatRequest":{"properties":{"capacity":{"type":"object"},"status":{"type":"string"}},"type":"object"},"ResourceWrapper":{"properties":{"resource":{"$ref":"#/components/schemas/Resource"}},"type":"object"},"Error":{"properties":{"error":{"type":"string"},"reason":{"type":"string"}},"required":["error"],"type":"object"},"DestinationAdded":{"properties":{"destination_id":{"type":"string"},"status":{"enum":["destination_added"],"type":"string"}},"type":"object"},"NodeWrapper":{"properties":{"node":{"$ref":"#/components/schemas/Node"}},"type":"object"},"StreamList":{"properties":{"streams":{"items":{"properties":{"assigned_at":{"format":"date-time","type":"string"},"id":{"type":"string"},"node_id":{"nullable":true,"type":"string"},"started_at":{"format":"date-time","type":"string"},"status":{"type":"string"},"type":{"type":"string"}},"type":"object"},"type":"array"}},"type":"object"},"ResourceMigrated":{"properties":{"migrated_to":{"type":"string"},"resource":{"$ref":"#/components/schemas/Resource"}},"type":"object"}},"securitySchemes":{"ed25519Signature":{"description":"Ed25519 signature over the request method, path, body, and x-* headers. Include x-timestamp and x-public-key headers alongside the signature.","in":"header","name":"x-signature","type":"apiKey"}}},"info":{"description":"Cluster coordination API for managing nodes, resources, and streams across the MediaFuse network.","title":"MediaFuse Coordinator API","version":"0.1.0"},"openapi":"3.1.0","paths":{"/":{"get":{"operationId":"getOpenApiSpec","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"OpenAPI 3.1.0 specification"}},"summary":"Get OpenAPI specification","tags":["Spec"]},"options":{"operationId":"getOpenApiSpec","responses":{"200":{"content":{"application/json":{"schema":{"type":"object"}}},"description":"OpenAPI 3.1.0 specification"}},"summary":"Get OpenAPI specification","tags":["Spec"]}},"/api/channels/{id}":{"delete":{"operationId":"deleteChannel","parameters":[{"description":"Channel ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelRemoved"}}},"description":"Channel removed"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Internal error"}},"security":[{"ed25519Signature":[]}],"summary":"Remove a channel","tags":["Channels"]}},"/api/channels/{id}/config":{"put":{"operationId":"updateChannelConfig","parameters":[{"description":"Channel ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateChannelConfigRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChannelConfigUpdated"}}},"description":"Channel config updated"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Internal error"}},"security":[{"ed25519Signature":[]}],"summary":"Push channel config update","tags":["Channels"]}},"/api/cluster/health":{"get":{"operationId":"getClusterHealth","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterHealthy"}}},"description":"Cluster is healthy"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterUnhealthy"}}},"description":"Cluster is unhealthy"}},"summary":"Get cluster health","tags":["Cluster"]}},"/api/cluster/status":{"get":{"operationId":"getClusterStatus","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClusterStatus"}}},"description":"Cluster status"}},"summary":"Get cluster status overview","tags":["Cluster"]}},"/api/nodes":{"get":{"operationId":"listNodes","parameters":[{"description":"Filter by node type","in":"query","name":"type","required":false,"schema":{"enum":["live","bot"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeList"}}},"description":"Node list"}},"security":[{"ed25519Signature":[]}],"summary":"List nodes","tags":["Nodes"]},"post":{"operationId":"registerNode","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterNodeRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeWrapper"}}},"description":"Node registered"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"}},"security":[{"ed25519Signature":[]}],"summary":"Register a node","tags":["Nodes"]}},"/api/nodes/{id}":{"delete":{"operationId":"removeNode","parameters":[{"description":"Node ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeRemoved"}}},"description":"Node removed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Internal error"}},"security":[{"ed25519Signature":[]}],"summary":"Remove a node and orphan its resources","tags":["Nodes"]},"get":{"operationId":"showNode","parameters":[{"description":"Node ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeDetail"}}},"description":"Node details"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"ed25519Signature":[]}],"summary":"Get node details with assigned resources","tags":["Nodes"]}},"/api/nodes/{id}/drain":{"post":{"operationId":"drainNode","parameters":[{"description":"Node ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeWrapper"}}},"description":"Node draining"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Internal error"}},"security":[{"ed25519Signature":[]}],"summary":"Mark node as draining","tags":["Nodes"]}},"/api/nodes/{id}/heartbeat":{"post":{"operationId":"nodeHeartbeat","parameters":[{"description":"Node ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/HeartbeatRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/NodeWrapper"}}},"description":"Heartbeat accepted"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Internal error"}},"security":[{"ed25519Signature":[]}],"summary":"Send node heartbeat","tags":["Nodes"]}},"/api/resources":{"get":{"operationId":"listResources","parameters":[{"description":"Filter by type","in":"query","name":"type","required":false,"schema":{"enum":["stream","channel"],"type":"string"}},{"description":"Filter by status","in":"query","name":"status","required":false,"schema":{"enum":["active","orphaned"],"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceList"}}},"description":"Resource list"}},"security":[{"ed25519Signature":[]}],"summary":"List resources","tags":["Resources"]},"post":{"operationId":"registerResource","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RegisterResourceRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceWrapper"}}},"description":"Resource registered"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid parameters"}},"security":[{"ed25519Signature":[]}],"summary":"Register a resource","tags":["Resources"]}},"/api/resources/{id}":{"delete":{"operationId":"unregisterResource","parameters":[{"description":"Resource ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceRemoved"}}},"description":"Resource removed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Internal error"}},"security":[{"ed25519Signature":[]}],"summary":"Unregister a resource","tags":["Resources"]},"get":{"operationId":"showResource","parameters":[{"description":"Resource ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceWrapper"}}},"description":"Resource details"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"ed25519Signature":[]}],"summary":"Get resource details","tags":["Resources"]},"put":{"operationId":"updateResource","parameters":[{"description":"Resource ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateResourceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceWrapper"}}},"description":"Resource updated"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"500":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Internal error"}},"security":[{"ed25519Signature":[]}],"summary":"Update a resource","tags":["Resources"]}},"/api/resources/{id}/location":{"get":{"operationId":"resourceLocation","parameters":[{"description":"Resource ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceLocation"}}},"description":"Resource location"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found or not assigned"}},"security":[{"ed25519Signature":[]}],"summary":"Get resource node assignment","tags":["Resources"]}},"/api/resources/{id}/migrate":{"post":{"operationId":"migrateResource","parameters":[{"description":"Resource ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/MigrateResourceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceMigrated"}}},"description":"Resource migrated"},"400":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid target"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"ed25519Signature":[]}],"summary":"Migrate resource to another node","tags":["Resources"]}},"/api/streams":{"get":{"operationId":"listStreams","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamList"}}},"description":"Stream list"}},"security":[{"ed25519Signature":[]}],"summary":"List all streams","tags":["Streams"]},"post":{"operationId":"createStream","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateStreamRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamStarted"}}},"description":"Stream started"},"409":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Stream already exists"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Missing stream_id"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Upstream error"},"503":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"No available nodes"}},"security":[{"ed25519Signature":[]}],"summary":"Create and start a stream","tags":["Streams"]}},"/api/streams/{id}":{"delete":{"operationId":"deleteStream","parameters":[{"description":"Stream ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamStopped"}}},"description":"Stream stopped"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Upstream error"}},"security":[{"ed25519Signature":[]}],"summary":"Stop and delete a stream","tags":["Streams"]},"get":{"operationId":"showStream","parameters":[{"description":"Stream ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StreamDetail"}}},"description":"Stream details"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"}},"security":[{"ed25519Signature":[]}],"summary":"Get stream details with live info","tags":["Streams"]}},"/api/streams/{id}/destinations":{"post":{"operationId":"addDestination","parameters":[{"description":"Stream ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddDestinationRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationAdded"}}},"description":"Destination added"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"422":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Invalid destination"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Upstream error"}},"security":[{"ed25519Signature":[]}],"summary":"Add a destination to a stream","tags":["Streams"]},"put":{"operationId":"updateDestinations","parameters":[{"description":"Stream ID","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateDestinationsRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationsUpdated"}}},"description":"Destinations updated"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Upstream error"}},"security":[{"ed25519Signature":[]}],"summary":"Replace stream destinations","tags":["Streams"]}},"/api/streams/{id}/destinations/{destination_id}":{"delete":{"operationId":"removeDestination","parameters":[{"description":"Stream ID","in":"path","name":"id","required":true,"schema":{"type":"string"}},{"description":"Destination ID","in":"path","name":"destination_id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DestinationRemoved"}}},"description":"Destination removed"},"404":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Not found"},"502":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}},"description":"Upstream error"}},"security":[{"ed25519Signature":[]}],"summary":"Remove a destination from a stream","tags":["Streams"]}}},"servers":[{"description":"This instance","url":"/"}],"tags":[{"description":"Cluster status and health","name":"Cluster"},{"description":"Node registration and lifecycle","name":"Nodes"},{"description":"Resource tracking and placement","name":"Resources"},{"description":"Stream operations (proxied to Live nodes)","name":"Streams"},{"description":"Channel configuration (push from Services)","name":"Channels"},{"description":"OpenAPI specification","name":"Spec"}]}