# Iron API Design ## 1. Overview This document defines the MVP API surface for the Iron gateway. Principles: - REST-first - browser-first product usage, with backend auth that can evolve - `tus` for resumable upload transport - stable logical file identifiers - backend topology hidden from normal users Base prefix: - `/api` Non-API upload transport prefix: - `/files` Notes: - `tus` endpoints intentionally live outside the normal JSON API shape because they follow protocol-specific request and response semantics. - All timestamps should use ISO 8601 UTC strings. - All entity IDs should use UUIDv7 or another sortable unique ID format. ## 2. Authentication MVP auth model: - single local user - bearer token session for the current backend implementation - Web UI can later wrap this with cookie-based session handling if desired ### 2.1 Login - `POST /api/auth/login` Request: ```json { "username": "admin", "password": "secret" } ``` Response: ```json { "access_token": "iron_xxx", "token_type": "bearer", "expires_at": "2026-04-20T00:00:00Z", "user": { "id": "usr_01", "username": "admin" } } ``` Behavior: - implemented in current codebase - creates a persisted auth session and returns a bearer token - returns `401` for invalid credentials ### 2.2 Logout - `POST /api/auth/logout` Response: ```json { "success": true } ``` Behavior: - implemented in current codebase - revokes the current bearer-token session ### 2.3 Current Session - `GET /api/auth/me` Response: ```json { "user": { "id": "usr_01", "username": "admin" } } ``` Behavior: - implemented in current codebase - requires `Authorization: Bearer ` ## 3. Directories Route protection: - all directory, file, upload, and backend routes require authentication in the current codebase ### 3.1 List Directory Children - `GET /api/directories/{directory_id}/children` Query params: - `cursor` - `limit` - `sort` - `order` Response: ```json { "directory": { "id": "dir_root", "name": "/", "parent_id": null }, "items": [ { "kind": "directory", "id": "dir_01", "name": "photos", "created_at": "2026-04-13T00:00:00Z", "updated_at": "2026-04-13T00:00:00Z" }, { "kind": "file", "id": "fil_01", "name": "movie.mp4", "mime_type": "video/mp4", "size_bytes": 1048576, "cache_status": "partial", "preview_status": "ready", "created_at": "2026-04-13T00:00:00Z", "updated_at": "2026-04-13T00:00:00Z" } ], "next_cursor": null } ``` ### 3.2 Create Directory - `POST /api/directories` Request: ```json { "parent_id": "dir_root", "name": "photos" } ``` Implemented status: - implemented in current codebase - currently supports create under an existing parent directory - duplicate sibling names are rejected ### 3.3 Rename Directory - `POST /api/directories/{directory_id}/rename` Request: ```json { "name": "vacation-photos" } ``` Implemented status: - implemented in current codebase ### 3.4 Move Directory - `POST /api/directories/{directory_id}/move` Request: ```json { "target_parent_id": "dir_02" } ``` Implemented status: - implemented in current codebase - moving a directory updates descendant logical path keys ### 3.5 Delete Directory - `DELETE /api/directories/{directory_id}` Behavior: - soft delete in MVP - reject if non-empty unless `recursive=true` is explicitly supported later Current implementation status: - `GET /api/directories/{directory_id}/children` is implemented - current response can contain both directory items and file items - items are currently returned with directories first, then files ## 4. Files ### 4.1 Get File Details - `GET /api/files/{file_id}` Response: ```json { "file": { "id": "fil_01", "directory_id": "dir_root", "name": "movie.mp4", "mime_type": "video/mp4", "size_bytes": 1048576, "current_version_id": "ver_01", "preview_status": "ready", "cache_status": "partial", "created_at": "2026-04-13T00:00:00Z", "updated_at": "2026-04-13T00:00:00Z" }, "preview_artifacts": [ { "id": "prv_01", "artifact_type": "thumbnail", "blob_id": "blb_01", "status": "ready" } ] } ``` Implemented status: - implemented in current codebase - returns logical file metadata from the gateway database - now includes preview artifact metadata when background generation has run ### 4.2 Rename File - `POST /api/files/{file_id}/rename` Request: ```json { "name": "movie-final.mp4" } ``` Implemented status: - implemented in current codebase ### 4.3 Move File - `POST /api/files/{file_id}/move` Request: ```json { "target_parent_id": "dir_02" } ``` Implemented status: - implemented in current codebase ### 4.4 Delete File - `DELETE /api/files/{file_id}` Behavior: - current implementation moves the file to a recycle-bin state - deleted files disappear from normal directory listings - deleted files can be listed and restored through recycle-bin APIs ### 4.5 Download File - `GET /api/files/{file_id}/download` Behavior: - returns attachment response - gateway resolves best available replica and streams to client Implemented status: - implemented in current codebase for files persisted to the default local backend - current implementation resolves the ready local replica and serves it directly ### 4.6 Stream File - `GET /api/files/{file_id}/stream` Behavior: - supports `Range` requests - intended for video and browser-native preview flows Implemented status: - implemented in current codebase for local persisted objects - supports full reads and single-range byte requests ### 4.7 File Thumbnail - `GET /api/files/{file_id}/thumbnail` Behavior: - returns preview derivative if present - returns `404` if not yet available ### 4.7a File Preview - `GET /api/files/{file_id}/preview` Behavior: - returns an inline response for preview-supported file types Implemented status: - implemented in current codebase for images and PDFs persisted to the default local backend - unsupported media types currently return a validation-style error ### 4.8 Recycle Bin - `GET /api/files/recycle-bin` - `POST /api/files/{file_id}/restore` Current implementation status: - both endpoints are implemented - restore returns a conflict if the original directory now contains an active item with the same file name ## 5. Tus Upload Flow Iron uses `tus` as the upload transport and adds one gateway-specific finalize step. ### 5.1 Create Upload Resource - `POST /files` Required headers: - `Tus-Resumable` - `Upload-Length` - `Upload-Metadata` Behavior: - creates a new upload session - returns `201` with `Location` Implemented status: - implemented in current codebase - currently supports sequential append uploads with declared total length - returns a gateway-managed upload resource under `/files/{upload_id}` ### 5.2 Query Upload Offset - `HEAD /files/{upload_id}` Behavior: - returns current `Upload-Offset` Implemented status: - implemented in current codebase ### 5.3 Upload Bytes - `PATCH /files/{upload_id}` Required headers: - `Tus-Resumable` - `Upload-Offset` - `Content-Type: application/offset+octet-stream` Behavior: - appends bytes to temporary ingest file - updates upload progress Implemented status: - implemented in current codebase - currently requires exact offset matching and sequential writes ### 5.4 Finalize Upload - `POST /api/uploads/{upload_id}/finalize` Request: ```json { "directory_id": "dir_root", "filename": "movie.mp4" } ``` Response: ```json { "file": { "id": "fil_01", "name": "movie.mp4" }, "upload": { "id": "upl_01", "status": "finalized" } } ``` Behavior: - verifies upload completion - computes content metadata - creates file and file version records - performs first durable write - only exposes the logical file after minimum durability is met Implemented status: - implemented in current codebase - currently finalizes into metadata records and persists the blob into the default local backend - creates a `blob_replicas` record for the local persisted object - enqueues replication jobs for enabled non-local backends such as `s3` ## 6. Backends ### 6.1 List Backends - `GET /api/backends` ### 6.2 Create Backend - `POST /api/backends` Request example: ```json { "name": "local-main", "type": "local", "stability_class": "local", "read_priority": 100, "write_priority": 100, "config": { "base_path": "/srv/iron/storage" } } ``` ### 6.3 Update Backend - `PATCH /api/backends/{backend_id}` ### 6.4 Check Backend Health - `POST /api/backends/{backend_id}/check` Response: ```json { "backend_id": "bkd_01", "status": "healthy", "checked_at": "2026-04-13T00:00:00Z" } ``` ### 6.5 Disable Backend - `POST /api/backends/{backend_id}/disable` ## 7. Placement Policies ### 7.1 List Placement Policies - `GET /api/policies/placement` Behavior: - implemented in current codebase - returns file-class placement policy rows - current policy shape includes: - `require_local` - `stable_replica_count` - `opportunistic_replica_count` - ordered `preferred_backend_ids` - explicit `excluded_backend_ids` - optional `max_non_local_size_bytes` ### 7.2 Upsert Placement Policy - `PUT /api/policies/placement/{file_class}` Request example: ```json { "require_local": true, "stable_replica_count": 1, "opportunistic_replica_count": 1, "preferred_backend_ids": ["bkd_s3_primary", "bkd_s3_archive"], "excluded_backend_ids": ["bkd_s3_legacy"], "max_non_local_size_bytes": 104857600 } ``` Behavior: - implemented in current codebase - validates that referenced backend ids exist - rejects overlap between preferred and excluded backend ids - rejects negative replica counts ### 7.3 Preview Placement Decision - `GET /api/policies/placement/preview` Query params: - `mime_type` - `size_bytes` Behavior: - implemented in current codebase - returns the matched file class, active policy, and selected backends for the given mime type and optional size - marks whether non-local replicas are currently allowed under the policy ## 8. Jobs ### 8.1 List Jobs - `GET /api/jobs` Query params: - `kind` - `status` - `limit` ### 8.2 Get Job - `GET /api/jobs/{job_id}` ### 8.3 Retry Job - `POST /api/jobs/{job_id}/retry` ### 8.4 Run Pending Jobs - `POST /api/jobs/run-pending` ### 8.5 Enqueue Backend Health Checks - `POST /api/jobs/enqueue-health-checks` ### 8.6 Enqueue Full Reconcile - `POST /api/jobs/enqueue-full-reconcile` ## 9. Exports ### 9.1 Metadata Export - `GET /api/exports/metadata` ### 9.2 Metadata Import - `POST /api/exports/metadata/import` Notes: - current backend requires `confirm_replace=true` for destructive import - current backend also requires a fresh `validation_token` from `POST /api/exports/metadata/restore-plan` - successful import currently schedules follow-up reconcile and backend health-check jobs ## 9.3 Reconciliation - `POST /api/files/{file_id}/reconcile` Implemented status: - implemented in current codebase - the backend intention is "make this file converge to policy-defined replica state" ## 9.4 Metadata Validation and Integrity - `POST /api/exports/metadata/validate` - `GET /api/exports/metadata/integrity` ## 9.5 Restore Plan - `POST /api/exports/metadata/restore-plan` ## 10. System ### 10.1 Health Check - `GET /api/system/health` Response: ```json { "status": "ok" } ``` ### 10.2 Readiness - `GET /api/system/ready` Response: ```json { "status": "ready", "database": "ok" } ``` ## 10.3 Backends - `GET /api/backends` - `POST /api/backends` - `POST /api/backends/{backend_id}/check` - `POST /api/backends/{backend_id}/disable` Implemented status: - implemented in current codebase - supports local and s3 backend configuration metadata - local and s3 backend health checks are implemented Additional implemented status: - `POST /api/jobs/run-pending` is implemented in current codebase - `POST /api/jobs/enqueue-health-checks` is implemented in current codebase - `GET /api/exports/metadata` is implemented in current codebase - `POST /api/exports/metadata/validate` is implemented in current codebase - `GET /api/exports/metadata/integrity` is implemented in current codebase - `POST /api/exports/metadata/import` is implemented in current codebase - `POST /api/files/{file_id}/reconcile` is implemented in current codebase ## 11. Error Shape All JSON API errors should use one consistent envelope: ```json { "error": { "code": "file_not_found", "message": "File does not exist.", "details": null } } ``` Suggested error codes: - `unauthorized` - `forbidden` - `validation_error` - `directory_not_found` - `file_not_found` - `upload_not_found` - `backend_unavailable` - `backend_config_invalid` - `conflict` - `internal_error` ## 12. Versioning Approach MVP can stay unversioned internally if the API is changing quickly. Recommendation: - keep `/api` in code - be prepared to introduce `/api/v1` before external client surface becomes stable ## 13. Implementation Notes - `tus` transport handlers should be separated from normal JSON routers. - Finalization logic must stay in application services, not inside protocol handlers. - Download and stream endpoints should rely on the same replica resolution service. - Avoid exposing backend IDs in normal file responses unless operationally useful.