HTTP API Reference for Object Operations
HTTP API Reference
Section titled “HTTP API Reference”Stowry provides a REST API for object storage operations. All endpoints support presigned URL authentication.
Base URL
Section titled “Base URL”http://localhost:5708Authentication
Section titled “Authentication”Requests require presigned URL authentication unless auth.read or auth.write is public. See Authentication for details.
Endpoints
Section titled “Endpoints”List Objects
Section titled “List Objects”Store mode only. Not available in static or SPA modes.
List objects with optional prefix filtering and pagination.
GET /Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
prefix |
string | - | Filter objects by path prefix |
limit |
int | 100 | Maximum objects per page (1-1000) |
cursor |
string | - | Pagination cursor from previous response |
Response: 200 OK
{ "items": [ { "id": "550e8400-e29b-41d4-a716-446655440000", "path": "photos/vacation.jpg", "content_type": "image/jpeg", "etag": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e", "file_size_bytes": 1048576, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z" } ], "next_cursor": "MjAyNC0wMS0xNVQxMDozMDowMFp8cGhvdG9zL3ZhY2F0aW9uLmpwZw=="}Example:
# List all objectscurl "http://localhost:5708/"
# List with prefixcurl "http://localhost:5708/?prefix=photos/"
# Paginatecurl "http://localhost:5708/?limit=10&cursor=MjAyNC0wMS0..."Get Object
Section titled “Get Object”Download an object by path.
GET /{path}Path Parameters:
| Parameter | Description |
|---|---|
path |
Object path (e.g., photos/vacation.jpg) |
Response Headers:
| Header | Description |
|---|---|
Content-Type |
MIME type of the object |
ETag |
Object hash (SHA256) |
Content-Length |
Object size in bytes |
Last-Modified |
Last modification time |
Response: 200 OK with file content
Errors:
| Status | Error Code | Description |
|---|---|---|
| 400 | invalid_path |
Invalid path format |
| 404 | not_found |
Object not found |
Example:
# Download objectcurl -O http://localhost:5708/photos/vacation.jpgServer Mode Behavior:
- Store: Returns exact path or 404 (JSON)
- Static: Tries exact →
{path}.html→{path}/index.html→ 404 (HTML) - SPA: Falls back to
/index.htmlfor client-side routing
Head Object
Section titled “Head Object”Retrieve object metadata without downloading the file body.
HEAD /{path}Path Parameters:
| Parameter | Description |
|---|---|
path |
Object path (e.g., photos/vacation.jpg) |
Response Headers:
| Header | Description |
|---|---|
Content-Type |
MIME type of the object |
ETag |
Object hash (SHA256), quoted |
Content-Length |
Object size in bytes |
Last-Modified |
Last modification time |
Accept-Ranges |
Indicates byte-range support |
Conditional Headers:
| Header | Description |
|---|---|
If-None-Match |
Returns 304 Not Modified if ETag matches (weak comparison per RFC 9110) |
If-Modified-Since |
Returns 304 Not Modified if object hasn’t changed since the given time |
If-None-Match takes precedence over If-Modified-Since per RFC 7232.
Response: 200 OK (empty body)
Errors:
| Status | Error Code | Description |
|---|---|---|
| 304 | - | Not modified (conditional request) |
| 400 | invalid_path |
Invalid path format |
| 404 | not_found |
Object not found |
Example:
# Get metadatacurl -I http://localhost:5708/photos/vacation.jpg
# Conditional request (ETag)curl -I -H 'If-None-Match: "a591a6d..."' http://localhost:5708/photos/vacation.jpg
# Conditional request (time)curl -I -H "If-Modified-Since: Wed, 15 Jan 2024 10:30:00 GMT" http://localhost:5708/photos/vacation.jpgServer Mode Behavior:
Same fallback logic as GET — applies static and SPA mode path resolution. Returns 404 HTML in static mode.
Upload Object
Section titled “Upload Object”Store mode only. Returns
405 Method Not Allowedin static and SPA modes.
Upload or overwrite an object.
PUT /{path}Path Parameters:
| Parameter | Description |
|---|---|
path |
Object path (e.g., photos/vacation.jpg) |
Request Headers:
| Header | Required | Description |
|---|---|---|
Content-Type |
No | MIME type (auto-detected if not provided) |
If-Match |
No | Conditional update — ETag must match; returns 412 if object doesn’t exist (per RFC 9110 §13.1.1) |
Request Body: Raw file content
Response: 200 OK
{ "id": "550e8400-e29b-41d4-a716-446655440000", "path": "photos/vacation.jpg", "content_type": "image/jpeg", "etag": "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e", "file_size_bytes": 1048576, "created_at": "2024-01-15T10:30:00Z", "updated_at": "2024-01-15T10:30:00Z"}Errors:
| Status | Error Code | Description |
|---|---|---|
| 400 | invalid_path |
Invalid path format |
| 412 | precondition_failed |
ETag mismatch (If-Match) |
| 500 | internal_error |
Server error |
Example:
# Upload filecurl -X PUT \ -H "Content-Type: image/jpeg" \ --data-binary @vacation.jpg \ http://localhost:5708/photos/vacation.jpg
# Conditional updatecurl -X PUT \ -H "If-Match: a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e" \ --data-binary @vacation.jpg \ http://localhost:5708/photos/vacation.jpgDelete Object
Section titled “Delete Object”Store mode only. Returns
405 Method Not Allowedin static and SPA modes.
Delete an object: the metadata entry is soft-deleted and the stored file is removed.
DELETE /{path}Path Parameters:
| Parameter | Description |
|---|---|
path |
Object path (e.g., photos/vacation.jpg) |
Response: 204 No Content
Errors:
| Status | Error Code | Description |
|---|---|---|
| 400 | invalid_path |
Invalid path format |
| 404 | not_found |
Object not found |
Example:
curl -X DELETE http://localhost:5708/photos/vacation.jpgNote: A delete removes both the metadata entry and the stored file.
Error Response Format
Section titled “Error Response Format”All errors return JSON:
{ "error": "error_code", "message": "Human-readable error message"}Path Validation
Section titled “Path Validation”Valid paths must:
- Be relative (no leading
/) - Contain no path traversal (
..) - Contain no empty segments (
//) - Contain no invalid characters (
\ ? # ~) - Be valid UTF-8
- Have no trailing slashes
Valid paths:
file.txtphotos/vacation.jpgdata/2024/01/report.pdf
Invalid paths:
/file.txt(leading slash)../secret.txt(path traversal)folder//file.txt(empty segment)file?.txt(invalid character)
Content Type
Section titled “Content Type”Uploads must carry a Content-Type header. The server stores it verbatim and
never guesses: a PUT without one is rejected with 400 invalid_path.
The extension is used only where there is no header to read:
stowry uploaddetects it from the local file’s extension before sending, unless--content-typeoverrides itstowry populatedetects it for files already in the storage directory
Both use Go’s mime package, so the mapping is the platform’s, and modern Go
returns text/javascript; charset=utf-8 for .js. Anything unrecognised
becomes application/octet-stream.
Pagination
Section titled “Pagination”List responses use cursor-based pagination:
- Make initial request:
GET /?limit=10 - If
next_cursoris present in response, there are more results - Request next page:
GET /?limit=10&cursor={next_cursor} - Repeat until
next_cursoris empty or null
Benefits:
- Consistent results during concurrent modifications
- Efficient for large datasets
- No offset skipping issues