Strix exposes two APIs:
- S3 API (port 9000) - Amazon S3-compatible API for object storage operations
- Admin API (port 9001) - REST API for server administration
The S3 API is compatible with AWS SDKs and S3 clients. All requests must be signed using AWS Signature Version 4.
All S3 API requests must include AWS Signature V4 authentication headers:
Authorization: AWS4-HMAC-SHA256 Credential=.../s3/aws4_request, SignedHeaders=..., Signature=...
X-Amz-Date: 20240101T000000Z
X-Amz-Content-Sha256: <payload-hash>
PUT /{bucket} HTTP/1.1
Host: localhost:9000Response: 200 OK
DELETE /{bucket} HTTP/1.1
Host: localhost:9000Response: 204 No Content
Error: 409 Conflict if bucket is not empty
GET / HTTP/1.1
Host: localhost:9000Response:
<?xml version="1.0" encoding="UTF-8"?>
<ListAllMyBucketsResult>
<Owner>
<ID>owner-id</ID>
<DisplayName>owner</DisplayName>
</Owner>
<Buckets>
<Bucket>
<Name>my-bucket</Name>
<CreationDate>2024-01-01T00:00:00.000Z</CreationDate>
</Bucket>
</Buckets>
</ListAllMyBucketsResult>HEAD /{bucket} HTTP/1.1
Host: localhost:9000Response: 200 OK if bucket exists, 404 Not Found otherwise
PUT /{bucket}/{key} HTTP/1.1
Host: localhost:9000
Content-Length: <size>
Content-Type: <mime-type>
<binary-data>Response Headers:
ETag: "<md5-hash>"
GET /{bucket}/{key} HTTP/1.1
Host: localhost:9000Optional Headers:
Range: bytes=0-1023- Request partial content
Response Headers:
Content-Length: <size>Content-Type: <mime-type>ETag: "<md5-hash>"Last-Modified: <date>
DELETE /{bucket}/{key} HTTP/1.1
Host: localhost:9000Response: 204 No Content
HEAD /{bucket}/{key} HTTP/1.1
Host: localhost:9000Response Headers:
Content-Length: <size>Content-Type: <mime-type>ETag: "<md5-hash>"Last-Modified: <date>
GET /{bucket}?list-type=2 HTTP/1.1
Host: localhost:9000Query Parameters:
prefix- Filter by prefixdelimiter- Group by delimiter (typically/)max-keys- Maximum number of keys (default: 1000)continuation-token- Pagination token
Response:
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult>
<Name>my-bucket</Name>
<Prefix></Prefix>
<MaxKeys>1000</MaxKeys>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>file.txt</Key>
<LastModified>2024-01-01T00:00:00.000Z</LastModified>
<ETag>"abc123"</ETag>
<Size>1024</Size>
<StorageClass>STANDARD</StorageClass>
</Contents>
<CommonPrefixes>
<Prefix>folder/</Prefix>
</CommonPrefixes>
</ListBucketResult>PUT /{bucket}/{key} HTTP/1.1
Host: localhost:9000
X-Amz-Copy-Source: /source-bucket/source-keyResponse:
<?xml version="1.0" encoding="UTF-8"?>
<CopyObjectResult>
<LastModified>2024-01-01T00:00:00.000Z</LastModified>
<ETag>"abc123"</ETag>
</CopyObjectResult>POST /{bucket}?delete HTTP/1.1
Host: localhost:9000
Content-Type: application/xml
<?xml version="1.0" encoding="UTF-8"?>
<Delete>
<Object>
<Key>file1.txt</Key>
</Object>
<Object>
<Key>file2.txt</Key>
</Object>
</Delete>POST /{bucket}/{key}?uploads HTTP/1.1
Host: localhost:9000Response:
<?xml version="1.0" encoding="UTF-8"?>
<InitiateMultipartUploadResult>
<Bucket>my-bucket</Bucket>
<Key>large-file.zip</Key>
<UploadId>abc123</UploadId>
</InitiateMultipartUploadResult>PUT /{bucket}/{key}?partNumber=1&uploadId=abc123 HTTP/1.1
Host: localhost:9000
Content-Length: <part-size>
<binary-data>Response Headers:
ETag: "<part-etag>"
POST /{bucket}/{key}?uploadId=abc123 HTTP/1.1
Host: localhost:9000
<?xml version="1.0" encoding="UTF-8"?>
<CompleteMultipartUpload>
<Part>
<PartNumber>1</PartNumber>
<ETag>"part1-etag"</ETag>
</Part>
<Part>
<PartNumber>2</PartNumber>
<ETag>"part2-etag"</ETag>
</Part>
</CompleteMultipartUpload>DELETE /{bucket}/{key}?uploadId=abc123 HTTP/1.1
Host: localhost:9000The Admin API is a REST API for managing the Strix server. All endpoints are under /api/v1/.
GET /api/v1/info HTTP/1.1
Host: localhost:9001Response:
{
"version": "0.1.0",
"commit": "abc123",
"mode": "standalone",
"uptime": 3600,
"region": "us-east-1"
}GET /api/v1/health HTTP/1.1
Host: localhost:9001Response: 200 OK
GET /api/v1/users HTTP/1.1
Host: localhost:9001Response:
{
"users": [
{
"username": "alice",
"arn": "arn:aws:iam:::user/alice",
"created_at": "2024-01-01T00:00:00Z",
"status": "active",
"policies": ["ReadWriteAccess"]
}
]
}POST /api/v1/users HTTP/1.1
Host: localhost:9001
Content-Type: application/json
{
"username": "alice"
}Response:
{
"user": {
"username": "alice",
"arn": "arn:aws:iam:::user/alice",
"created_at": "2024-01-01T00:00:00Z",
"status": "active"
},
"access_key": {
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"username": "alice",
"created_at": "2024-01-01T00:00:00Z",
"status": "active"
}
}GET /api/v1/users/{username} HTTP/1.1
Host: localhost:9001DELETE /api/v1/users/{username} HTTP/1.1
Host: localhost:9001Response: 204 No Content
GET /api/v1/users/{username}/access-keys HTTP/1.1
Host: localhost:9001Response:
{
"access_keys": [
{
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"username": "alice",
"created_at": "2024-01-01T00:00:00Z",
"status": "active"
}
]
}POST /api/v1/users/{username}/access-keys HTTP/1.1
Host: localhost:9001Response:
{
"access_key_id": "AKIAIOSFODNN7EXAMPLE",
"secret_access_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"username": "alice",
"created_at": "2024-01-01T00:00:00Z",
"status": "active"
}DELETE /api/v1/access-keys/{access_key_id} HTTP/1.1
Host: localhost:9001GET /api/v1/users/{username}/policies HTTP/1.1
Host: localhost:9001POST /api/v1/users/{username}/policies HTTP/1.1
Host: localhost:9001
Content-Type: application/json
{
"policy": {
"name": "MyPolicy",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": ["arn:aws:s3:::my-bucket/*"]
}
]
}
}DELETE /api/v1/users/{username}/policies/{policy_name} HTTP/1.1
Host: localhost:9001GET /api/v1/buckets HTTP/1.1
Host: localhost:9001POST /api/v1/buckets HTTP/1.1
Host: localhost:9001
Content-Type: application/json
{
"name": "my-bucket"
}DELETE /api/v1/buckets/{name} HTTP/1.1
Host: localhost:9001GET /api/v1/buckets/{bucket}/policy HTTP/1.1
Host: localhost:9001Response:
{
"policy": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::my-bucket/*"]
}
]
}
}PUT /api/v1/buckets/{bucket}/policy HTTP/1.1
Host: localhost:9001
Content-Type: application/json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::my-bucket/*"]
}
]
}DELETE /api/v1/buckets/{bucket}/policy HTTP/1.1
Host: localhost:9001GET /api/v1/buckets/{bucket}/objects?prefix=folder/&delimiter=/ HTTP/1.1
Host: localhost:9001DELETE /api/v1/buckets/{bucket}/objects/{key} HTTP/1.1
Host: localhost:9001DELETE /api/v1/buckets/{bucket}/objects HTTP/1.1
Host: localhost:9001
Content-Type: application/json
{
"keys": ["file1.txt", "file2.txt"]
}POST /api/v1/presign HTTP/1.1
Host: localhost:9001
Content-Type: application/json
{
"bucket": "my-bucket",
"key": "file.txt",
"method": "GET",
"expires_in": 3600
}Response:
{
"url": "http://localhost:9000/my-bucket/file.txt?X-Amz-Algorithm=...",
"expires_in": 3600,
"method": "GET"
}GET /api/v1/usage HTTP/1.1
Host: localhost:9001Response:
{
"buckets": [
{
"name": "my-bucket",
"created_at": "2024-01-01T00:00:00Z",
"object_count": 100,
"total_size": 1073741824
}
],
"total_buckets": 1,
"total_objects": 100,
"total_size": 1073741824
}S3 API errors are returned as XML:
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchBucket</Code>
<Message>The specified bucket does not exist</Message>
<Resource>/my-bucket</Resource>
<RequestId>abc123</RequestId>
</Error>Common error codes:
NoSuchBucket- Bucket does not existNoSuchKey- Object does not existBucketAlreadyExists- Bucket name is takenBucketNotEmpty- Cannot delete non-empty bucketAccessDenied- Permission deniedInvalidArgument- Invalid request parameterSignatureDoesNotMatch- Invalid signature
Admin API errors are returned as JSON:
{
"error": "User not found",
"message": "The user 'alice' does not exist"
}HTTP status codes:
400 Bad Request- Invalid request401 Unauthorized- Authentication required403 Forbidden- Permission denied404 Not Found- Resource not found409 Conflict- Resource conflict (e.g., bucket not empty)500 Internal Server Error- Server error