Files

106 lines
4.4 KiB
Markdown

# Fileserver HTTP contract
Every public endpoint is listed here. `FileserverDocumentationCoverageTest` scans the controllers
and fails if one is missing, so this file cannot silently fall behind the code.
## Public endpoints
| Method | Path | Success | Notes |
|---|---|---|---|
| POST | `/v1/files` | `201` READY, `202` VERIFYING | multipart single upload |
| POST | `/v1/files:raw` | `201`, `202` | the whole request body is the file |
| POST | `/v1/files:batch` | `200` | ordered per-part results; explicitly non-atomic |
| GET | `/v1/files/{fileId}` | `200` | public metadata; never a content key or path |
| GET | `/v1/files/{fileId}/content` | `200`, `206`, `304` | download |
| HEAD | `/v1/files/{fileId}/content` | `200`, `304` | identical headers, no body |
| DELETE | `/v1/files/{fileId}` | `202`, `204` | logical delete first |
| POST | `/v1/files/{fileId}:copy` | `202` | create-only target |
| POST | `/v1/files/{fileId}:move` | `200` | logical namespace change only |
| OPTIONS | `/v1/uploads` | `204` | tus capability discovery |
| POST | `/v1/uploads` | `201` | tus creation |
| HEAD | `/v1/uploads/{uploadId}` | `204` | tus offset |
| PATCH | `/v1/uploads/{uploadId}` | `204` | tus append |
| DELETE | `/v1/uploads/{uploadId}` | `204` | tus termination |
| POST | `/v1/experimental/draft12/uploads` | `201` | Experimental; off by default |
| PATCH | `/v1/experimental/draft12/uploads/{uploadId}` | `204` | Experimental; off by default |
## Management endpoints
Reachable only where both `app.fileserver-platform.enabled=true` and
`app.fileserver-platform.admin.enabled=true`, gated at the servlet chain on
`app.fileserver-platform.security.admin-roles`, and intended for a management
port rather than the public one.
| Method | Path |
|---|---|
| GET | `/internal/fileserver/storage-health` |
| GET | `/internal/fileserver/capabilities` |
| GET | `/internal/fileserver/orphans` |
| POST | `/internal/fileserver/orphans:reconcile` |
| POST | `/internal/fileserver/files/{fileId}:reverify` |
| POST | `/internal/fileserver/files/{fileId}:force-delete` |
| GET | `/internal/fileserver/uploads/incomplete` |
| POST | `/internal/fileserver/uploads:cleanup` |
## Status codes
| Status | Condition |
|---:|---|
| `200` | metadata, full GET, batch result, move |
| `201` | file or upload created |
| `202` | verification or physical cleanup deferred |
| `204` | append, cancel, bodyless update |
| `206` | satisfiable Range |
| `304` | validator matched on GET or HEAD |
| `400` | malformed header or header combination |
| `401` | unauthenticated |
| `403` / `404` | denied, or hidden under the existence-hiding profile |
| `409` | state, offset, or lease conflict |
| `410` | expired upload resource |
| `411` | `require-content-length` profile with no length |
| `412` | precondition failed |
| `413` | size or quota policy violation |
| `415` | upload media type not accepted |
| `416` | unsatisfiable Range; carries the real length |
| `422` | digest, signature, or scanner rejection |
| `429` | transfer admission or rate limit |
| `503` | storage or scanner unavailable |
| `504` | downstream timeout |
| `507` | out of storage capacity |
## Failure body
Every failure answers `application/problem+json` with a stable code and its URN:
```json
{
"type": "urn:fileserver:problem:upload-offset-mismatch",
"title": "Upload offset mismatch",
"status": 409,
"code": "UPLOAD_OFFSET_MISMATCH",
"retryable": true,
"ambiguous": false,
"reconciliationRequired": false,
"traceId": "..."
}
```
The server-side exception message never appears. `ambiguous` is the field a client must read before
retrying: an ambiguous failure may already have taken effect.
## Header contract
| Header | Contract |
|---|---|
| `Content-Type` | client value is a claim; the verified type is stored separately |
| `Content-Disposition` | `attachment` by default; scriptable types are never inline |
| `Accept-Ranges` | `bytes` |
| `Range` | single range by default; multi-range only under an explicit budget |
| `Content-Range` | actual range on `206`; the unsatisfied form on `416` |
| `ETag` | strong validator derived from the SHA-256 |
| `Last-Modified` | metadata publication instant, never a filesystem timestamp |
| `Cache-Control` | `private, no-store` by default |
| `X-Content-Type-Options` | always `nosniff` on a download |
| `Retry-After` | on retryable `409`, `429`, `503`, and `504` |
| `X-Accel-Redirect` | internal only; never forwarded to a client |