{"openapi":"3.1.0","info":{"title":"LUCIHUB Server","description":"REST API for the LUCIHUB analog-computing platform.","contact":{"name":"Anabrid","url":"https://anabrid.com/","email":"info@anabrid.com"},"license":{"name":"Proprietary"},"version":"0.1.0"},"paths":{"/api/v1/system/status":{"get":{"tags":["System"],"summary":"Get Status","description":"Return system version, domain, and uptime in seconds.","operationId":"get_status_api_v1_system_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SystemStatusResponse"}}}}}}},"/api/v1/devices":{"get":{"tags":["Devices"],"summary":"List Devices","description":"Return every registered device with its current waiting-queue length.\n\n``queue_length`` is sourced from the RabbitMQ management API\n(``messages_ready`` on each device's queue), queried in parallel.  A\ndevice whose broker call fails reports ``None``.","operationId":"list_devices_api_v1_devices_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/DeviceResponse"},"type":"array","title":"Response List Devices Api V1 Devices Get"}}}}}}},"/api/v1/devices/{device_name}/entity":{"get":{"tags":["Devices"],"summary":"Get Device Entity","description":"Return the raw device-entity blob as application/octet-stream.\n\nReturns 404 when the device is not found or has no entity blob stored.\n\nArgs:\n    device_name: Name of the device whose entity blob is requested.\n    request: FastAPI request carrying app.state.device_manager.","operationId":"get_device_entity_api_v1_devices__device_name__entity_get","parameters":[{"name":"device_name","in":"path","required":true,"schema":{"type":"string","title":"Device Name"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/compile":{"post":{"tags":["Tasks"],"summary":"Create Compile Task","description":"Submit an ODE source for compilation.\n\nDecodes the base64 module, persists it, creates the DB row and queues\nthe task.  Returns 400 if the backend is SIMULATOR or module is not\nvalid base64.","operationId":"create_compile_task_api_v1_tasks_compile_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CompileTaskRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/run":{"post":{"tags":["Tasks"],"summary":"Create Run Task","description":"Submit a compiled module for execution on a device.\n\nPerforms four ordered pre-flight checks before accepting:\n1. Device registered → 404.\n2. Device available → 503.\n3. User has access → 403.\n4. Within rate limits → 429.","operationId":"create_run_task_api_v1_tasks_run_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RunTaskRequest"}}},"required":true},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreatedResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks":{"get":{"tags":["Tasks"],"summary":"List Tasks","description":"Return all tasks owned by the authenticated user with optional filters.","operationId":"list_tasks_api_v1_tasks_get","parameters":[{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/TaskStateEnum"},{"type":"null"}],"title":"State"}},{"name":"type","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/TaskTypeEnum"},{"type":"null"}],"title":"Type"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/TaskSummaryResponse"},"title":"Response List Tasks Api V1 Tasks Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}":{"get":{"tags":["Tasks"],"summary":"Get Task Detail","description":"Return detailed information for a single task owned by the caller.","operationId":"get_task_detail_api_v1_tasks__task_id__get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/CompileTaskDetailResponse"},{"$ref":"#/components/schemas/RunTaskDetailResponse"}],"title":"Response Get Task Detail Api V1 Tasks  Task Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["Tasks"],"summary":"Delete Task","description":"Delete a task and its associated blobs.\n\nReturns 409 when the task is INPROGRESS and the latest INPROGRESS\nstate transition occurred within the last 2 minutes.","operationId":"delete_task_api_v1_tasks__task_id__delete","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"204":{"description":"Successful Response"},"409":{"description":"State conflict — the task is INPROGRESS and its latest INPROGRESS transition is less than 2 minutes old. The grace window protects an in-flight worker run from having its database row removed underneath it."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}/status":{"get":{"tags":["Tasks"],"summary":"Get Task Status","description":"Return the current state and full state history for a task.","operationId":"get_task_status_api_v1_tasks__task_id__status_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}/log":{"get":{"tags":["Tasks"],"summary":"Get Task Log","description":"Return the log lines for a task; empty list when no log is available.","operationId":"get_task_log_api_v1_tasks__task_id__log_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskLogResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}/result":{"get":{"tags":["Tasks"],"summary":"Get Task Result","description":"Return the task result; 404 if not yet SUCCEEDED.\n\nCompile tasks: raw bytes as application/octet-stream.\nRun tasks: JSON-decoded unpickled Python value.","operationId":"get_task_result_api_v1_tasks__task_id__result_get","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/tasks/{task_id}/cancel":{"post":{"tags":["Tasks"],"summary":"Cancel Task","description":"Cancel a QUEUED task; returns 409 if the task is not QUEUED.","operationId":"cancel_task_api_v1_tasks__task_id__cancel_post","parameters":[{"name":"task_id","in":"path","required":true,"schema":{"type":"string","title":"Task Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskStatusResponse"}}}},"409":{"description":"State conflict — the task is not QUEUED. Cancel only withdraws queued work; once a worker has picked the task up (INPROGRESS) or it has reached a terminal state (SUCCEEDED, FAILED, CANCELLED), the cancel request is refused."},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/users/me":{"get":{"tags":["Users"],"summary":"Get Current User","description":"Return the authenticated user's profile and remaining rate-limit quota.\n\nComposes `AuthService.get_user_info` (credential-file profile) with\n`AuthService.get_remaining_quota` (DB-backed per-class remaining values).","operationId":"get_current_user_api_v1_users_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfoResponse"}}}}}}}},"components":{"schemas":{"CompileTaskDetailResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"type":{"$ref":"#/components/schemas/TaskTypeEnum"},"state":{"$ref":"#/components/schemas/TaskStateEnum"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"state_history":{"items":{"$ref":"#/components/schemas/TaskStateEntry"},"type":"array","title":"State History"},"backend":{"$ref":"#/components/schemas/DeviceTypeEnum"},"ode_source":{"type":"string","title":"Ode Source"},"k0":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K0"},"has_result":{"type":"boolean","title":"Has Result"},"has_log":{"type":"boolean","title":"Has Log"}},"type":"object","required":["id","type","state","created_at","state_history","backend","ode_source","k0","has_result","has_log"],"title":"CompileTaskDetailResponse","description":"Detailed compile task response for GET /api/v1/tasks/{task_id}."},"CompileTaskRequest":{"properties":{"ode_source":{"type":"string","title":"Ode Source"},"backend":{"$ref":"#/components/schemas/DeviceTypeEnum"},"k0":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"K0"},"module":{"type":"string","title":"Module"}},"additionalProperties":false,"type":"object","required":["ode_source","backend","module"],"title":"CompileTaskRequest","description":"POST /api/v1/tasks/compile request body."},"DeviceResponse":{"properties":{"name":{"type":"string","title":"Name"},"type":{"$ref":"#/components/schemas/DeviceTypeEnum"},"available":{"type":"boolean","title":"Available"},"queue_length":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Queue Length"}},"type":"object","required":["name","type","available","queue_length"],"title":"DeviceResponse","description":"Device information for GET /api/v1/devices responses.\n\n``queue_length`` is ``None`` when the broker management API cannot be\nqueried (e.g. RabbitMQ outage); consumers treat that as an unknown\ncount rather than zero."},"DeviceTypeEnum":{"type":"string","enum":["LUCIDAC","REDAC","SIMULATOR"],"title":"DeviceTypeEnum","description":"Device types supported by the system."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"RateLimitStatus":{"properties":{"hourly_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Hourly Limit"},"hourly_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Hourly Remaining"},"daily_limit":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Limit"},"daily_remaining":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Daily Remaining"}},"type":"object","title":"RateLimitStatus","description":"Rate limit status for one device class in a user's quota information."},"RunTaskDetailResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"type":{"$ref":"#/components/schemas/TaskTypeEnum"},"state":{"$ref":"#/components/schemas/TaskStateEnum"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"state_history":{"items":{"$ref":"#/components/schemas/TaskStateEntry"},"type":"array","title":"State History"},"device_name":{"type":"string","title":"Device Name"},"sample_rate":{"type":"integer","title":"Sample Rate"},"op_time":{"type":"number","title":"Op Time"},"has_result":{"type":"boolean","title":"Has Result"},"has_log":{"type":"boolean","title":"Has Log"}},"type":"object","required":["id","type","state","created_at","state_history","device_name","sample_rate","op_time","has_result","has_log"],"title":"RunTaskDetailResponse","description":"Detailed run task response for GET /api/v1/tasks/{task_id}."},"RunTaskRequest":{"properties":{"device_name":{"type":"string","title":"Device Name"},"sample_rate":{"type":"integer","title":"Sample Rate","default":10000},"op_time":{"type":"number","title":"Op Time","default":0.1},"module":{"type":"string","title":"Module"}},"type":"object","required":["device_name","module"],"title":"RunTaskRequest","description":"POST /api/v1/tasks/run request body."},"SystemStatusResponse":{"properties":{"version":{"type":"string","title":"Version"},"domain":{"type":"string","title":"Domain"},"uptime":{"type":"integer","title":"Uptime"}},"type":"object","required":["version","domain","uptime"],"title":"SystemStatusResponse","description":"GET /api/v1/system/status response."},"TaskCreatedResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"type":{"$ref":"#/components/schemas/TaskTypeEnum"},"state":{"$ref":"#/components/schemas/TaskStateEnum"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","type","state","created_at"],"title":"TaskCreatedResponse","description":"Response returned after successfully creating a task (HTTP 201)."},"TaskLogResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"lines":{"items":{"type":"string"},"type":"array","title":"Lines"}},"type":"object","required":["id","lines"],"title":"TaskLogResponse","description":"GET /api/v1/tasks/{task_id}/log response."},"TaskStateEntry":{"properties":{"state":{"$ref":"#/components/schemas/TaskStateEnum"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"}},"type":"object","required":["state","timestamp"],"title":"TaskStateEntry","description":"Single state transition entry in a task's history."},"TaskStateEnum":{"type":"string","enum":["queued","inprogress","succeeded","failed","cancelled"],"title":"TaskStateEnum","description":"Task lifecycle states."},"TaskStatusResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"state":{"$ref":"#/components/schemas/TaskStateEnum"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"state_history":{"items":{"$ref":"#/components/schemas/TaskStateEntry"},"type":"array","title":"State History"}},"type":"object","required":["id","state","state_history"],"title":"TaskStatusResponse","description":"GET /api/v1/tasks/{task_id}/status response."},"TaskSummaryResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"type":{"$ref":"#/components/schemas/TaskTypeEnum"},"state":{"$ref":"#/components/schemas/TaskStateEnum"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"state_history":{"items":{"$ref":"#/components/schemas/TaskStateEntry"},"type":"array","title":"State History"}},"type":"object","required":["id","type","state","created_at","state_history"],"title":"TaskSummaryResponse","description":"Base task summary used in GET /api/v1/tasks list endpoint."},"TaskTypeEnum":{"type":"string","enum":["compile","run"],"title":"TaskTypeEnum","description":"Task type discriminator for API responses."},"UserInfoResponse":{"properties":{"name":{"type":"string","title":"Name"},"email":{"type":"string","title":"Email"},"subscription":{"type":"string","title":"Subscription"},"devices":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Devices"},"rate_limits":{"additionalProperties":{"$ref":"#/components/schemas/RateLimitStatus"},"type":"object","title":"Rate Limits"}},"type":"object","required":["name","email","subscription","rate_limits"],"title":"UserInfoResponse","description":"GET /api/v1/users/me response."},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}