{"openapi":"3.1.0","info":{"title":"Felania Edge Platform API","version":"1.0.0","description":"Manage an edge fleet with Platform API keys and receive device telemetry with device tokens."},"servers":[{"url":"https://felania.com/api/v1"}],"tags":[{"name":"Fleet","description":"Fleet inventory and status"},{"name":"Devices","description":"Device lifecycle and telemetry"},{"name":"Operations","description":"Allowlisted remote device operations"},{"name":"Releases","description":"Guarded Docker Compose application rollouts"},{"name":"Blueprints","description":"Validated, versioned application deployment templates"},{"name":"Applications","description":"Audited workload lifecycle control"},{"name":"Registries","description":"Verified private Registry V2 catalogs, tags, manifests and immutable digests"},{"name":"Runbooks","description":"Validated multi-step operational automation"},{"name":"Ingest","description":"Device-to-cloud telemetry ingestion"},{"name":"Agent","description":"Felania Agent control plane"}],"components":{"securitySchemes":{"platformKey":{"type":"http","scheme":"bearer","bearerFormat":"fla_...","description":"Platform API key created in the Felania console"},"deviceToken":{"type":"http","scheme":"bearer","bearerFormat":"fle_...","description":"Per-device enrollment token returned when creating a device"}},"schemas":{"DeviceInput":{"type":"object","required":["name"],"properties":{"name":{"type":"string","example":"warehouse-gateway-01"},"site":{"type":"string","example":"Frankfurt warehouse"},"kind":{"type":"string","enum":["linux","openwrt","esp32","gateway","other"]}}},"Heartbeat":{"type":"object","properties":{"status":{"type":"string","enum":["online","warning"]},"version":{"type":"string","example":"1.7.0"},"metrics":{"type":"object","properties":{"cpu":{"type":"number","minimum":0,"maximum":100},"memory":{"type":"number","minimum":0,"maximum":100},"disk":{"type":"number","minimum":0,"maximum":100},"rssi":{"type":"integer","minimum":-150,"maximum":10},"latency":{"type":"number","minimum":0,"maximum":120000}}},"metadata":{"type":"object","additionalProperties":true,"properties":{"platform":{"type":"string","example":"linux"},"architecture":{"type":"string","example":"aarch64"},"containerPlatform":{"type":"string","example":"linux/arm64"}}}}},"CommandInput":{"type":"object","required":["commandType"],"properties":{"commandType":{"type":"string","enum":["collect_diagnostics","list_services","service_status","service_logs","restart_service","network_check","reboot","set_agent_interval","update_agent"]},"service":{"type":"string","description":"Required for service_status, service_logs and restart_service"},"lines":{"type":"integer","minimum":20,"maximum":200,"description":"Optional line count for service_logs"},"target":{"type":"string","description":"Required hostname or IP address for network_check"},"interval":{"type":"integer","minimum":15,"maximum":3600,"description":"Required for set_agent_interval"}}},"ReleaseInput":{"type":"object","required":["applicationName","version","composeYaml","deviceIds"],"properties":{"applicationName":{"type":"string","example":"Media Control"},"version":{"type":"string","example":"2.4.0"},"composeYaml":{"type":"string","description":"Docker Compose YAML, maximum 96 KB"},"healthTarget":{"type":"string","example":"http://127.0.0.1:8080/health","description":"Optional localhost HTTP health URL"},"registryCredentialId":{"type":"string","description":"Optional private-registry credential ID created in the console"},"strategy":{"type":"string","enum":["canary","all_at_once"],"default":"canary"},"deviceIds":{"type":"array","minItems":1,"maxItems":300,"items":{"type":"string"}}}},"BlueprintDeployInput":{"type":"object","required":["version","values","deviceIds"],"properties":{"applicationName":{"type":"string","description":"Optional application name override"},"version":{"type":"string","example":"2.3.2"},"values":{"type":"object","additionalProperties":{"type":"string"},"description":"Values declared by the selected Blueprint. Secret values are encrypted and excluded from rendered Compose."},"registryCredentialId":{"type":"string","description":"Optional private-registry credential ID"},"strategy":{"type":"string","enum":["canary","all_at_once"],"default":"canary"},"deviceIds":{"type":"array","minItems":1,"maxItems":300,"items":{"type":"string"}}}},"BlueprintPreviewInput":{"type":"object","required":["values"],"properties":{"applicationName":{"type":"string"},"version":{"type":"string"},"values":{"type":"object","additionalProperties":{"type":"string"}}}},"ReleaseRedeployInput":{"type":"object","required":["deviceIds"],"properties":{"strategy":{"type":"string","enum":["canary","all_at_once"],"default":"canary"},"deviceIds":{"type":"array","minItems":1,"maxItems":300,"items":{"type":"string"}}}},"ApplicationActionInput":{"type":"object","required":["action","deviceIds"],"properties":{"action":{"type":"string","enum":["stop","start","restart","uninstall","purge"]},"deviceIds":{"type":"array","minItems":1,"maxItems":300,"items":{"type":"string"}},"confirmation":{"type":"string","description":"Required for purge and must exactly match the application slug"}}},"RunbookRunInput":{"type":"object","required":["deviceIds"],"properties":{"parameters":{"type":"object","additionalProperties":{"type":"string"},"description":"Values declared by the selected Runbook"},"strategy":{"type":"string","enum":["canary","all_at_once"],"default":"canary"},"deviceIds":{"type":"array","minItems":1,"maxItems":300,"items":{"type":"string"}}}},"Error":{"type":"object","properties":{"error":{"type":"string"}}}}},"paths":{"/summary":{"get":{"tags":["Fleet"],"summary":"Get fleet summary","security":[{"platformKey":[]}],"responses":{"200":{"description":"Fleet totals by state"},"401":{"description":"Invalid API key"}}}},"/devices":{"get":{"tags":["Devices"],"summary":"List devices","security":[{"platformKey":[]}],"responses":{"200":{"description":"Device inventory"}}},"post":{"tags":["Devices"],"summary":"Create and enroll a device","security":[{"platformKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DeviceInput"}}}},"responses":{"201":{"description":"Device and one-time device token"},"409":{"description":"Device name already exists"}}}},"/devices/archived":{"get":{"tags":["Devices"],"summary":"List archived device identities","description":"Returns retained identities and whether each can be permanently purged without deleting deployment, Runbook or command history.","security":[{"platformKey":[]}],"responses":{"200":{"description":"Archived device inventory"}}}},"/devices/{id}":{"get":{"tags":["Devices"],"summary":"Get one device","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Device details"},"404":{"description":"Not found"}}},"delete":{"tags":["Devices"],"summary":"Archive a device","description":"Invalidates the device token and preserves history. Active deployments, Runbooks and commands block the request.","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Archived"},"404":{"description":"Not found"},"409":{"description":"Device has active work"}}}},"/devices/{id}/restore":{"post":{"tags":["Devices"],"summary":"Restore an archived identity","description":"Returns the device and a new one-time fle_ token. The previous token stays invalid.","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Restored device and rotated token"},"404":{"description":"Archived identity not found"}}}},"/devices/{id}/purge":{"delete":{"tags":["Devices"],"summary":"Permanently purge an unused archived identity","description":"Rejected when the device has deployment, Runbook or command history. The body confirmation must exactly match the device name.","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["confirmation"],"properties":{"confirmation":{"type":"string"}}}}}},"responses":{"200":{"description":"Permanently purged"},"409":{"description":"Audit history requires archive retention"}}}},"/devices/{id}/telemetry":{"get":{"tags":["Devices"],"summary":"Get the latest 100 telemetry points","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Telemetry in chronological order"}}}},"/devices/{id}/update-policy":{"patch":{"tags":["Operations"],"summary":"Set automatic or manual agent updates","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["mode"],"properties":{"mode":{"type":"string","enum":["automatic","manual"]}}}}}},"responses":{"200":{"description":"Update policy changed"},"404":{"description":"Not found"}}}},"/devices/{id}/commands":{"get":{"tags":["Operations"],"summary":"List recent device commands","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Command history and results"}}},"post":{"tags":["Operations"],"summary":"Queue an allowlisted operation","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CommandInput"}}}},"responses":{"201":{"description":"Command queued"}}}},"/releases":{"get":{"tags":["Releases"],"summary":"List deployments, application inventory and version drift","security":[{"platformKey":[]}],"responses":{"200":{"description":"Recent deployments and per-device application state"}}},"post":{"tags":["Releases"],"summary":"Create a Docker Compose release and start rollout","security":[{"platformKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseInput"}}}},"responses":{"201":{"description":"Deployment created"},"400":{"description":"Invalid or incompatible release"}}}},"/releases/preflight":{"post":{"tags":["Releases"],"summary":"Inspect and pin release images without deploying","description":"Resolves selected-registry tags to immutable digests, compares manifest platforms with target device platforms and returns per-device blockers plus pinned Compose.","security":[{"platformKey":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseInput"}}}},"responses":{"200":{"description":"Image metadata, pinned Compose and per-device compatibility"},"400":{"description":"Invalid release, image or registry response"}}}},"/blueprints":{"get":{"tags":["Blueprints"],"summary":"List available application Blueprints","security":[{"platformKey":[]}],"responses":{"200":{"description":"Ready and Draft Blueprints with variables and readiness blockers"}}}},"/blueprints/{id}/preview":{"post":{"tags":["Blueprints"],"summary":"Validate and render a Blueprint without deploying","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlueprintPreviewInput"}}}},"responses":{"200":{"description":"Rendered Compose, health target and configured secret names"},"400":{"description":"Invalid variable value"},"404":{"description":"Blueprint not found"}}}},"/blueprints/{id}/deploy":{"post":{"tags":["Blueprints"],"summary":"Create a guarded release from a Ready Blueprint","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BlueprintDeployInput"}}}},"responses":{"201":{"description":"Deployment created"},"400":{"description":"Invalid variables or incompatible targets"},"409":{"description":"Blueprint is Draft or another release is active"}}}},"/releases/{id}/redeploy":{"post":{"tags":["Releases"],"summary":"Deploy a stored release as a new guarded rollout","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReleaseRedeployInput"}}}},"responses":{"201":{"description":"Deployment created with a new protected snapshot"},"404":{"description":"Release not found or archived"},"409":{"description":"Another application rollout is active"}}}},"/releases/{id}/archive":{"post":{"tags":["Releases"],"summary":"Archive a release while preserving its audit history","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Release archived"},"404":{"description":"Release not found or already archived"},"409":{"description":"Release has an active deployment"}}}},"/applications/{slug}/actions":{"post":{"tags":["Applications"],"summary":"Queue an audited application lifecycle action","description":"Stop, start, restart, uninstall while preserving volumes, or purge after exact slug confirmation.","security":[{"platformKey":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplicationActionInput"}}}},"responses":{"201":{"description":"One command queued per selected device"},"400":{"description":"Invalid action or purge confirmation"},"404":{"description":"Application or target not found"},"409":{"description":"An application rollout is active"}}}},"/registry-credentials":{"get":{"tags":["Registries"],"summary":"List private registry identities","description":"Returns names, hosts and usernames. Encrypted passwords and tokens are never returned.","security":[{"platformKey":[]}],"responses":{"200":{"description":"Non-secret registry credential list"}}}},"/registry-credentials/{id}/catalog":{"get":{"tags":["Registries"],"summary":"Browse Registry V2 repositories","description":"Proxies a bounded, HTTPS-only page from /v2/_catalog without exposing the saved credential.","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"name":"last","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Repository page and next cursor"},"400":{"description":"Credential rejected or unsafe target"},"502":{"description":"Registry unavailable or invalid response"}}}},"/registry-credentials/{id}/tags":{"get":{"tags":["Registries"],"summary":"List tags for one Registry V2 repository","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"repository","in":"query","required":true,"schema":{"type":"string"}},{"name":"limit","in":"query","schema":{"type":"integer","minimum":1,"maximum":200,"default":100}},{"name":"last","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"Tags, complete image references and next cursor"},"400":{"description":"Invalid repository or credential"},"502":{"description":"Registry unavailable or invalid response"}}}},"/registry-credentials/{id}/images/inspect":{"get":{"tags":["Registries"],"summary":"Inspect one image manifest","description":"Returns its immutable digest, pinned reference, media type, size, creation time when available and normalized OS/architecture platforms.","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"repository","in":"query","required":true,"schema":{"type":"string"}},{"name":"reference","in":"query","required":true,"schema":{"type":"string","description":"Tag or sha256 digest"}}],"responses":{"200":{"description":"Registry image intelligence"},"400":{"description":"Invalid reference or rejected credential"},"502":{"description":"Registry manifest unavailable or invalid"}}}},"/runbooks":{"get":{"tags":["Runbooks"],"summary":"List Runbooks and recent execution history","security":[{"platformKey":[]}],"responses":{"200":{"description":"System and workspace Runbooks plus recent runs"}}}},"/runbooks/{id}/run":{"post":{"tags":["Runbooks"],"summary":"Start a validated Runbook","description":"Steps execute sequentially. Canary strategy completes the first target before queueing the remaining fleet.","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunbookRunInput"}}}},"responses":{"201":{"description":"Runbook started"},"400":{"description":"Invalid parameter or target selection"},"409":{"description":"A selected device already has an active Runbook"}}}},"/runbook-runs/{id}":{"get":{"tags":["Runbooks"],"summary":"Read a Runbook transcript","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Run, targets, sequential steps and bounded output"},"404":{"description":"Run not found"}}}},"/runbook-runs/{id}/cancel":{"post":{"tags":["Runbooks"],"summary":"Cancel an active Runbook","description":"A claimed step finishes, but Felania queues no additional steps or fleet phases.","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cancellation requested"},"409":{"description":"Run is not active"}}}},"/deployments/{id}/cancel":{"post":{"tags":["Releases"],"summary":"Cancel an active rollout and recover completed targets","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cancellation requested"},"409":{"description":"Deployment is not active"}}}},"/deployments/{id}/retry":{"post":{"tags":["Releases"],"summary":"Retry a failed, cancelled or rolled-back release","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Release retry queued with its original protected snapshot"},"409":{"description":"Deployment cannot be retried"}}}},"/deployments/{id}/rollback":{"post":{"tags":["Releases"],"summary":"Manually roll back a successful release","security":[{"platformKey":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Fleet rollback queued"},"409":{"description":"Deployment is not successful"}}}},"/ingest/heartbeat":{"post":{"tags":["Ingest"],"summary":"Send a device heartbeat","security":[{"deviceToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Heartbeat"}}}},"responses":{"200":{"description":"Heartbeat accepted"},"401":{"description":"Invalid device token"},"429":{"description":"More than one heartbeat per second"}}}}}}