{"openapi":"3.1.0","info":{"title":"次元之灵 · 小程序与固件接入 API","version":"1.0.0","description":"本部署为显式免登录模式：资源接口与主播运维无需登录；写操作仍遵循后台的跨站请求限制。设备检查和固件下载始终要求出厂身份 HMAC。这里只说明已实现的服务器 HTTP 接口；BLE check_ota/start_ota/get_ota_status 不是 HTTP。小程序专用用户登录、收藏和按角色同步任务接口尚未在本服务实现。可用接口不代表真实固件发布或真机验收已经完成。"},"servers":[{"url":"/","description":"当前资源后台同源地址；设备必须能通过自己的 Wi-Fi 访问。"}],"paths":{"/api/creator-ota/auth-config":{"get":{"tags":["Operator"],"summary":"Read the current deployment's operator login requirement","security":[],"responses":{"200":{"description":"True requires an independent operator login. False enables local-network management without a browser session.","content":{"application/json":{"schema":{"type":"object","required":["required"],"properties":{"required":{"type":"boolean"}}}}}}}}},"/v1/device/ota":{"post":{"tags":["Device"],"summary":"Bootstrap protocol or explicitly check for a newer compatible application","description":"HMAC over exact raw body and original path/query; no trailing LF in canonical input. bootstrap always firmware=null. Product decisions use signed body plus registered identity. See CREATOR_OTA_SERVER.md and public signature vectors.","security":[{"deviceHmac":[]}],"parameters":[{"name":"Serial-Number","in":"header","required":true,"schema":{"type":"string"}},{"name":"Device-Id","in":"header","required":true,"schema":{"type":"string"}},{"name":"Hardware-Version","in":"header","required":true,"schema":{"type":"string"}},{"name":"Firmware-Version","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+(?:\\.[0-9]+){0,3}$","maxLength":31}},{"name":"Activation-Id","in":"header","required":false,"schema":{"type":"string"}},{"name":"Client-Id","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Device-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]{1,12}$","description":"Unix seconds; exact header text signed"}},{"name":"X-Device-Nonce","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{16,128}$"}},{"name":"X-Device-Signature-Alg","in":"header","required":true,"schema":{"const":"HMAC-SHA256"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRequest"}}}},"responses":{"200":{"description":"Top-level result; no data envelope","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceResponse"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/device/ota/firmware/{release_id}.bin":{"get":{"tags":["Device"],"summary":"Authenticated complete application download","description":"Requires a prior signed bootstrap/check version observation. The caller cannot authorize downgrade by changing unsigned Firmware-Version. Only current published compatible release is available; Range is rejected. No redirect, compression or chunked dependency.","security":[{"deviceHmac":[]}],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}},{"name":"Serial-Number","in":"header","required":true,"schema":{"type":"string"}},{"name":"Device-Id","in":"header","required":true,"schema":{"type":"string"}},{"name":"Hardware-Version","in":"header","required":true,"schema":{"type":"string"}},{"name":"Firmware-Version","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]+(?:\\.[0-9]+){0,3}$","maxLength":31}},{"name":"Activation-Id","in":"header","required":false,"schema":{"type":"string"}},{"name":"Client-Id","in":"header","required":false,"schema":{"type":"string"}},{"name":"X-Device-Timestamp","in":"header","required":true,"schema":{"type":"string","pattern":"^[0-9]{1,12}$","description":"Unix seconds; exact header text signed"}},{"name":"X-Device-Nonce","in":"header","required":true,"schema":{"type":"string","pattern":"^[A-Za-z0-9_-]{16,128}$"}},{"name":"X-Device-Signature-Alg","in":"header","required":true,"schema":{"const":"HMAC-SHA256"}}],"responses":{"200":{"description":"Exact original application bytes; not installation confirmation","headers":{"Content-Length":{"schema":{"type":"integer","minimum":1,"maximum":8388608}},"Content-Encoding":{"schema":{"const":"identity"}}},"content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"head":{"summary":"Not supported","responses":{"405":{"description":"Use full authenticated GET"}}}},"/api/creator-ota/login":{"post":{"tags":["Operator"],"summary":"Independent operator login","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"username":{"type":"string"},"password":{"type":"string","writeOnly":true}},"required":["username","password"]}}}}}},"/api/creator-ota/logout":{"post":{"tags":["Operator"],"summary":"Revoke current operator session","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[]}},"/api/creator-ota/me":{"get":{"tags":["Operator"],"summary":"Read current operator identity","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[]}},"/api/creator-ota/snapshot":{"get":{"tags":["Operator"],"summary":"Read settings, artifacts, device observations and audit","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Snapshot"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[]}},"/api/creator-ota/settings":{"put":{"tags":["Operator"],"summary":"Operator: update reachable service URLs or production mode","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Settings"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Settings"}}}}}},"/api/creator-ota/devices":{"post":{"tags":["Operator"],"summary":"Operator: register actual factory identity and migration evidence","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceRegistration"}}}},"description":"Creates a new serial identity only; duplicate registration returns 409."}},"/api/creator-ota/devices/{serial}":{"patch":{"tags":["Operator"],"summary":"Operator: amend device identity, migration evidence or enabled state","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"parameters":[{"name":"serial","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"serial_number":{"type":"string"},"device_id":{"type":"string"},"hardware_model":{"type":"string"},"hardware_version":{"type":"string"},"board":{"type":"string"},"product":{"const":"own_creator"},"partition_layout":{"const":"p4_32m_v2"},"enabled":{"type":"boolean"},"migration":{"$ref":"#/components/schemas/Migration"}}}}}},"description":"Factory device_key/secret cannot be replaced through this endpoint."}},"/api/creator-ota/releases":{"post":{"tags":["Operator"],"summary":"Operator: upload immutable application as draft","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"file":{"type":"string","format":"binary","description":"Standard ESP32-P4 application, <= 8 MiB"},"metadata":{"type":"string","description":"JSON serialization of ReleaseMetadata"},"evidence":{"type":"string","format":"binary","description":"ZIP whose paths/hashes match metadata.evidence.artifacts"}},"required":["file","metadata"]}}}}}},"/api/creator-ota/releases/{release_id}":{"patch":{"tags":["Operator"],"summary":"Operator: amend draft metadata/evidence without replacing binary","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}}],"requestBody":{"required":true,"content":{"multipart/form-data":{"schema":{"type":"object","properties":{"metadata":{"type":"string","description":"JSON serialization of ReleaseMetadata"},"evidence":{"type":"string","format":"binary","description":"ZIP whose paths/hashes match metadata.evidence.artifacts"}},"required":["metadata"]}}}}}},"/api/creator-ota/releases/{release_id}/publish":{"post":{"tags":["Operator"],"summary":"Operator: publish release","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rollout_percent":{"type":"integer","minimum":0,"maximum":100}}}}}}}},"/api/creator-ota/releases/{release_id}/withdraw":{"post":{"tags":["Operator"],"summary":"Operator: withdraw release","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rollout_percent":{"type":"integer","minimum":0,"maximum":100}}}}}}}},"/api/creator-ota/releases/{release_id}/pause":{"post":{"tags":["Operator"],"summary":"Operator: pause release","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rollout_percent":{"type":"integer","minimum":0,"maximum":100}}}}}}}},"/api/creator-ota/releases/{release_id}/resume":{"post":{"tags":["Operator"],"summary":"Operator: resume release","responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Release"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","properties":{"rollout_percent":{"type":"integer","minimum":0,"maximum":100}}}}}}}},"/api/creator-ota/releases/{release_id}/download":{"get":{"tags":["Operator"],"summary":"Operator: download original artifact for review","responses":{"200":{"description":"Application original","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Malformed request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"401":{"description":"Missing/invalid/replayed signature or invalid timestamp","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"403":{"description":"Unregistered, disabled, incompatible, unmigrated or unavailable release","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"404":{"description":"Object not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"409":{"description":"Conflicting immutable version or release state","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"413":{"description":"Request too large","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"429":{"description":"Request rate limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}},"503":{"description":"Service or artifact unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OperatorError"}}}}},"security":[],"parameters":[{"name":"release_id","in":"path","required":true,"schema":{"type":"string","pattern":"^[0-9a-f]{32}$"}}]}},"/api/bootstrap":{"get":{"tags":["资源与角色"],"summary":"读取后台能力与资源鉴权模式","description":"公开接口。auth_required 只描述资源令牌；主播运维账号另见 /api/creator-ota/auth-config，设备始终使用 HMAC。","security":[],"responses":{"200":{"description":"当前部署能力，不含凭据。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceBootstrap"}}}}}}},"/health":{"get":{"tags":["资源与角色"],"summary":"读取板端资源概况","description":"","security":[],"responses":{"200":{"description":"当前资源清单统计。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceHealth"}}},"headers":{"Content-Length":{"description":"完整表示的字节数；HEAD 同样返回该长度。","schema":{"type":"integer","minimum":0}}}}}},"head":{"tags":["资源与角色"],"summary":"读取板端资源概况响应头","description":"","security":[],"responses":{"200":{"description":"与 GET 相同的响应头，无响应正文。","headers":{"Content-Length":{"description":"完整表示的字节数；HEAD 同样返回该长度。","schema":{"type":"integer","minimum":0}}}}}}},"/api/simulator/roles":{"get":{"tags":["资源与角色"],"summary":"读取现有角色与表情","description":"现有后台/模拟器角色目录，不是独立的小程序平台账号或收藏接口。expression.path 为资源相对路径，无虚构 thumbnail_url/download_url 字段。启用令牌时还会设置仅用于浏览器 Live2D 资源的限时 HttpOnly cookie。","security":[],"responses":{"200":{"description":"角色列表与可选表情目录。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceRoles"}}}}}}},"/api/thumbnail":{"get":{"tags":["资源与角色"],"summary":"生成视频或 MJP 的 JPEG 预览","description":"仅支持当前预览实现接受的视频/MJP；路径不存在或文件类型不支持返回 400，媒体处理或参数验证失败返回 422。","security":[],"responses":{"200":{"description":"JPEG 图片。","content":{"image/jpeg":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"请求失败；detail 含错误说明。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceHttpError"}}}},"422":{"description":"请求失败；detail 含错误说明。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceHttpError"}}}}},"parameters":[{"name":"path","in":"query","required":true,"description":"角色 expressions[].path 或其他已存在的视频/MJP 相对路径。","schema":{"type":"string","minLength":1,"maxLength":500}}]}},"/manifest.tsv":{"get":{"tags":["资源与角色"],"summary":"下载板端资源清单","description":"前四行依次为 # JCYX_SD_MANIFEST\\t1、# profile\\t<profile>、# files\\t<count>、# bytes\\t<total>；后续每行是 sha256、字节数、相对路径，以 TAB 分隔。实际 profile 决定文件范围，角色目录与本清单不等价；隐藏元数据不会作为板端资源下载。","security":[],"responses":{"200":{"description":"UTF-8 TSV，行末 LF。","content":{"text/tab-separated-values":{"schema":{"type":"string"}}},"headers":{"Content-Length":{"description":"完整表示的字节数；HEAD 同样返回该长度。","schema":{"type":"integer","minimum":0}}}}}},"head":{"tags":["资源与角色"],"summary":"读取清单响应头","description":"","security":[],"responses":{"200":{"description":"与 GET 相同的响应头，无响应正文。","headers":{"Content-Length":{"description":"完整表示的字节数；HEAD 同样返回该长度。","schema":{"type":"integer","minimum":0}}}}}}},"/files/{relative_path}":{"get":{"tags":["资源与角色"],"summary":"下载清单中的板端资源","description":"只接受清单里的路径；未知文件 404，清单生成后文件发生变化 409。完整下载后核对长度与 SHA-256。本接口不实现 Range/断点续传；请完整 GET。它不是固件 OTA 下载入口。","security":[],"responses":{"200":{"description":"原始文件字节。","content":{"application/octet-stream":{"schema":{"type":"string","format":"binary"}}},"headers":{"Content-Length":{"description":"完整表示的字节数；HEAD 同样返回该长度。","schema":{"type":"integer","minimum":0}},"X-Content-SHA256":{"description":"清单中的 SHA-256 小写十六进制。","schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}}}},"404":{"description":"请求失败；detail 含错误说明。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceHttpError"}}}},"409":{"description":"请求失败；detail 含错误说明。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceHttpError"}}}}},"parameters":[{"name":"relative_path","in":"path","required":true,"description":"manifest.tsv 中的完整相对路径，可含斜杠；逐段 URL 编码并保留路径分隔符。","schema":{"type":"string"}}]},"head":{"tags":["资源与角色"],"summary":"读取板端资源长度与 SHA-256","description":"","security":[],"responses":{"200":{"description":"与 GET 相同的响应头，无响应正文。","headers":{"Content-Length":{"description":"完整表示的字节数；HEAD 同样返回该长度。","schema":{"type":"integer","minimum":0}},"X-Content-SHA256":{"description":"清单中的 SHA-256 小写十六进制。","schema":{"type":"string","pattern":"^[0-9a-f]{64}$"}}}},"404":{"description":"请求失败；detail 含错误说明。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceHttpError"}}}},"409":{"description":"请求失败；detail 含错误说明。","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResourceHttpError"}}}}},"parameters":[{"name":"relative_path","in":"path","required":true,"description":"manifest.tsv 中的完整相对路径，可含斜杠；逐段 URL 编码并保留路径分隔符。","schema":{"type":"string"}}]}}},"components":{"securitySchemes":{"deviceHmac":{"type":"apiKey","in":"header","name":"X-Device-Signature","description":"Base64URL(no padding) HMAC-SHA256 of exact canonical request; see local signing client and fixed vectors. Other required identity/signing headers are explicit operation parameters."},"operatorSession":{"type":"http","scheme":"bearer","description":"Independent operator login session; write operations require operator role, viewer is read-only."},"resourceBearer":{"type":"http","scheme":"bearer","description":"资源管理令牌，不是设备出厂密钥，也不是主播运维登录会话。"},"resourceHeaderToken":{"type":"apiKey","in":"header","name":"X-Resource-Token","description":"资源管理令牌的另一种传递方式；与 resourceBearer 二选一。同时发送时 Authorization Bearer 优先。"}},"schemas":{"Error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"}},"required":["code","message"]},"DeviceRequest":{"type":"object","properties":{"serial_number":{"type":"string"},"device_id":{"type":"string"},"hardware_model":{"type":"string"},"hardware_version":{"type":"string"},"product":{"const":"own_creator"},"partition_layout":{"const":"p4_32m_v2"},"intent":{"type":"string","enum":["bootstrap","check_ota"]},"board_type":{"type":"string"},"current_version":{"type":"string","pattern":"^[0-9]+(?:\\.[0-9]+){0,3}$","maxLength":31},"application":{"type":"object","properties":{"version":{"type":"string","pattern":"^[0-9]+(?:\\.[0-9]+){0,3}$","maxLength":31}}},"partition_table":{},"chip_info":{},"uuid":{"type":"string"}},"required":["intent","serial_number","hardware_model","hardware_version","product","partition_layout","board_type","current_version"]},"Firmware":{"type":"object","properties":{"version":{"type":"string","pattern":"^[0-9]+(?:\\.[0-9]+){0,3}$","maxLength":31},"url":{"type":"string","format":"uri"},"size":{"type":"integer","minimum":1,"maximum":8388608},"sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"},"hardware_model":{"type":"string"},"hardware_version":{"type":"string"},"board":{"type":"string"},"product":{"const":"own_creator"},"partition_layout":{"const":"p4_32m_v2"}},"required":["version","url","size","sha256","hardware_model","hardware_version","board","product","partition_layout"]},"DeviceResponse":{"type":"object","properties":{"firmware":{"oneOf":[{"$ref":"#/components/schemas/Firmware"},{"type":"null"}]},"server_time":{"type":"object","properties":{"timestamp":{"type":"integer","description":"UTC Unix milliseconds, never offset-adjusted"},"timezone_offset":{"type":"integer","description":"Display offset in minutes"}},"required":["timestamp","timezone_offset"]},"websocket":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"version":{"const":1},"token":{"type":"string","description":"Empty if actual dialogue authentication is disabled/bypassed"},"expires_in":{"type":"integer","maximum":3600}},"required":["url","version","token"]},"mqtt":{"type":"object","properties":{"endpoint":{"type":"string"},"client_id":{"type":"string"},"username":{"type":"string"},"password":{"type":"string"},"publish_topic":{"type":"string"},"subscribe_topic":{"type":"string"}}}},"required":["firmware","server_time"]},"BuildEvidence":{"type":"object","properties":{"schema":{"const":1},"build_commit":{"type":"string","pattern":"^[0-9a-f]{40}$"},"board":{"type":"string"},"hardware_model":{"type":"string"},"product":{"const":"own_creator"},"partition_layout":{"const":"p4_32m_v2"},"flash_model":{"type":"string"},"record_ref":{"type":"string"},"app_sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"},"bootloader_sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"},"artifacts":{"type":"object","properties":{"app_config":{"type":"object","properties":{"path":{"type":"string","description":"Relative ZIP member path, never a server filesystem path"},"sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"}},"required":["path","sha256"]},"bootloader_config":{"type":"object","properties":{"path":{"type":"string","description":"Relative ZIP member path, never a server filesystem path"},"sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"}},"required":["path","sha256"]},"bootloader":{"type":"object","properties":{"path":{"type":"string","description":"Relative ZIP member path, never a server filesystem path"},"sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"}},"required":["path","sha256"]},"hardware_record":{"type":"object","properties":{"path":{"type":"string","description":"Relative ZIP member path, never a server filesystem path"},"sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"}},"required":["path","sha256"]}},"required":["app_config","bootloader_config","bootloader","hardware_record"]}},"required":["schema","build_commit","board","hardware_model","product","partition_layout","flash_model","record_ref","app_sha256","bootloader_sha256","artifacts"]},"ReleaseMetadata":{"type":"object","properties":{"board":{"type":"string"},"hardware_model":{"type":"string"},"hardware_versions":{"type":"array","items":{"type":"string"},"minItems":1,"maxItems":32},"product":{"const":"own_creator"},"partition_layout":{"const":"p4_32m_v2"},"build_commit":{"type":"string","description":"Full build Git SHA; mandatory for publication"},"notes":{"type":"string"},"evidence":{"$ref":"#/components/schemas/BuildEvidence"}},"required":["board","hardware_model","hardware_versions","product","partition_layout"]},"Migration":{"type":"object","properties":{"migrated":{"type":"boolean"},"bootloader_sha256":{"type":"string","pattern":"^[0-9a-fA-F]{64}$"},"flash_model":{"type":"string"},"record_ref":{"type":"string"}},"required":["migrated"]},"DeviceRegistration":{"type":"object","properties":{"serial_number":{"type":"string"},"device_id":{"type":"string"},"hardware_model":{"type":"string"},"hardware_version":{"type":"string"},"board":{"type":"string"},"product":{"const":"own_creator"},"partition_layout":{"const":"p4_32m_v2"},"device_key":{"type":"string","writeOnly":true,"minLength":16,"description":"Raw factory UTF-8 secret, not hex-decoded"},"enabled":{"type":"boolean"},"migration":{"$ref":"#/components/schemas/Migration"}},"required":["serial_number","device_id","hardware_model","hardware_version","board","product","partition_layout","device_key"]},"Settings":{"type":"object","properties":{"public_url":{"type":"string","format":"uri"},"websocket_url":{"type":"string"},"production":{"type":"boolean"}}},"Release":{"type":"object","description":"Parsed immutable image metadata, target dimensions, state, rollout and evidence metadata; no server file paths"},"Snapshot":{"type":"object","properties":{"user":{"type":"object"},"settings":{"$ref":"#/components/schemas/Settings"},"releases":{"type":"array","items":{"$ref":"#/components/schemas/Release"}},"devices":{"type":"array","items":{"type":"object"}},"events":{"type":"array","items":{"type":"object","description":"Observed checks and downloads, never installation completion"}},"audit":{"type":"array","items":{"type":"object"}}}},"OperatorError":{"type":"object","properties":{"detail":{"oneOf":[{"$ref":"#/components/schemas/Error"},{"type":"string"},{"type":"array","items":{"type":"object"}}]}},"required":["detail"]},"ResourceBootstrap":{"type":"object","properties":{"name":{"type":"string"},"version":{"type":"string"},"auth_required":{"type":"boolean"},"simulator_only":{"type":"boolean"},"role_config_enabled":{"type":"boolean"},"shared_config_enabled":{"type":"boolean"}},"required":["name","version","auth_required","simulator_only","role_config_enabled","shared_config_enabled"],"description":"资源后台能力与资源令牌要求；不含令牌。角色配置权限不代表小程序用户账号权限。"},"ResourceHealth":{"type":"object","properties":{"ok":{"const":true},"manifest_version":{"const":1},"profile":{"type":"string"},"files":{"type":"integer","minimum":0},"bytes":{"type":"integer","minimum":0},"admin_version":{"type":"string"}},"required":["ok","manifest_version","profile","files","bytes","admin_version"]},"ResourceExpression":{"type":"object","properties":{"id":{"type":"string"},"path":{"type":"string","description":"资源根目录下的相对路径；不是 URL。按查询参数编码后交给 /api/thumbnail。"},"size":{"type":"integer","minimum":0},"modified_at":{"type":"string"},"convention_fps":{"type":["number","null"]},"width":{"type":["integer","null"]},"height":{"type":["integer","null"]},"frame_count":{"type":["integer","null"]}},"required":["id","path","size","modified_at","convention_fps","width","height","frame_count"]},"ResourceExpressionCatalogItem":{"type":"object","properties":{"id":{"type":"string"},"recipe_id":{"type":"string"},"mode":{"type":"string"},"label":{"type":"string"},"emoji":{"type":"string"}},"required":["id","recipe_id","mode","label","emoji"]},"ResourceLive2dExpression":{"type":"object","properties":{"id":{"type":"string"},"recipe_id":{"type":"string"},"renderer":{"const":"live2d"}},"required":["id","recipe_id","renderer"],"description":"其余字段来自本地 Live2D recipe；保留扩展字段，不将其当作固件 MJP 下载清单。"},"ResourceLive2d":{"type":"object","properties":{"revision":{"type":"string"},"version":{"type":"string"},"asset_revision":{"type":"string"},"asset_base":{"type":"string"},"model_url":{"type":"string"},"manifest_url":{"type":"string"},"url":{"type":"string"},"expressions":{"type":"array","items":{"$ref":"#/components/schemas/ResourceLive2dExpression"}}},"required":["revision","version","asset_revision","asset_base","model_url","manifest_url","url","expressions"],"description":"可用时返回的浏览器 Live2D 元数据。URL 是该响应实际返回的相对地址；不是额外的角色固件包下载接口。"},"ResourceRole":{"type":"object","properties":{"id":{"type":"string"},"display_name":{"type":"string"},"prompt":{"type":"string"},"prompt_origin":{"type":"string"},"greeting":{"type":"string"},"voice_id":{"type":"string"},"speech_style":{"type":"string"},"speech_rate":{"type":"integer","minimum":-50,"maximum":100},"pitch":{"type":"integer","minimum":-12,"maximum":12},"dialect":{"type":"string"},"default_expression":{"type":"string"},"expressions":{"type":"array","items":{"$ref":"#/components/schemas/ResourceExpression"}},"updated_at":{"type":["string","null"]},"live2d":{"anyOf":[{"$ref":"#/components/schemas/ResourceLive2d"},{"type":"null"}]}},"required":["id","display_name","prompt","prompt_origin","greeting","voice_id","speech_style","speech_rate","pitch","dialect","default_expression","expressions","updated_at","live2d"]},"ResourceRoles":{"type":"object","properties":{"roles":{"type":"array","items":{"$ref":"#/components/schemas/ResourceRole"}},"expression_catalog":{"type":"array","items":{"$ref":"#/components/schemas/ResourceExpressionCatalogItem"}}},"required":["roles","expression_catalog"]},"ResourceHttpError":{"type":"object","properties":{"detail":{"description":"业务错误为文字；参数验证失败可能为 FastAPI 验证错误数组。","oneOf":[{"type":"string"},{"type":"array","items":{"type":"object"}}]}},"required":["detail"]}}},"externalDocs":{"description":"接入说明与完整后台 API 索引","url":"/docs"}}