Storage
Truss provides S3-compatible file storage powered by MinIO. The dashboard gives you a file browser with drag-and-drop uploads. The API provides presigned URLs for direct client uploads and downloads.
Setup
Set these environment variables in apps/api/.env:
MINIO_S3_ENDPOINT=http://localhost:9000MINIO_CONSOLE_URL=http://localhost:9001MINIO_ACCESS_KEY=minioadminMINIO_SECRET_KEY=minioadminMINIO_REGION=us-east-1MINIO_FORCE_PATH_STYLE=trueBuckets
List buckets
curl http://localhost:8787/api/storage/bucketsCreate a bucket
curl -X POST http://localhost:8787/api/storage/buckets \ -H "Content-Type: application/json" \ -d '{"name": "my-bucket"}'Bucket names must be lowercase, 3-63 characters, using letters, numbers, dots, and hyphens.
Delete a bucket
# Delete empty bucketcurl -X DELETE http://localhost:8787/api/storage/buckets/my-bucket
# Force delete (empties bucket first)curl -X DELETE "http://localhost:8787/api/storage/buckets/my-bucket?force=true"Objects
List objects
curl "http://localhost:8787/api/storage/buckets/my-bucket/objects?prefix=images/&max_keys=50"Upload via presigned URL
The recommended upload flow: get a presigned URL from the API, then upload directly to S3.
# 1. Get presigned upload URLcurl -X POST http://localhost:8787/api/storage/buckets/my-bucket/objects/presign-upload \ -H "Content-Type: application/json" \ -d '{"key": "images/photo.jpg", "contentType": "image/jpeg"}'
# 2. Upload directly to the returned URLcurl -X PUT "$PRESIGNED_URL" \ -H "Content-Type: image/jpeg" \ --data-binary @photo.jpg// JavaScript: presigned uploadconst { url, headers } = await fetch('/api/storage/buckets/my-bucket/objects/presign-upload', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key: 'images/photo.jpg', contentType: 'image/jpeg' })}).then(r => r.json());
await fetch(url, { method: 'PUT', headers, body: file // File object from input});Download via presigned URL
curl -X POST http://localhost:8787/api/storage/buckets/my-bucket/objects/presign-download \ -H "Content-Type: application/json" \ -d '{"key": "images/photo.jpg", "expiresIn": 3600}'Returns a presigned GET URL valid for the specified duration (default 900 seconds, max 7 days).
Upload text content directly
For small text files, you can upload content directly through the API:
curl -X POST http://localhost:8787/api/storage/buckets/my-bucket/objects/upload-text \ -H "Content-Type: application/json" \ -d '{"key": "config.json", "content": "{\"version\": 1}", "contentType": "application/json"}'Delete objects
# Single objectcurl -X DELETE http://localhost:8787/api/storage/buckets/my-bucket/objects \ -H "Content-Type: application/json" \ -d '{"key": "images/photo.jpg"}'
# Bulk delete (up to 1000 keys)curl -X POST http://localhost:8787/api/storage/buckets/my-bucket/objects/bulk-delete \ -H "Content-Type: application/json" \ -d '{"keys": ["file1.txt", "file2.txt", "file3.txt"]}'Multipart uploads
For large files (>100MB), use multipart uploads:
# 1. Initializecurl -X POST http://localhost:8787/api/storage/buckets/my-bucket/objects/multipart/init \ -H "Content-Type: application/json" \ -d '{"key": "large-file.zip", "contentType": "application/zip"}'
# 2. Get presigned URL for each partcurl -X POST http://localhost:8787/api/storage/buckets/my-bucket/objects/multipart/presign-part \ -H "Content-Type: application/json" \ -d '{"key": "large-file.zip", "uploadId": "...", "partNumber": 1}'
# 3. Upload each part to its presigned URL
# 4. Complete the uploadcurl -X POST http://localhost:8787/api/storage/buckets/my-bucket/objects/multipart/complete \ -H "Content-Type: application/json" \ -d '{"key": "large-file.zip", "uploadId": "...", "parts": [{"partNumber": 1, "etag": "..."}]}'Bucket policies
Set access policies on buckets (e.g., public read):
# Get current policycurl http://localhost:8787/api/storage/buckets/my-bucket/policy
# Set policycurl -X PUT http://localhost:8787/api/storage/buckets/my-bucket/policy \ -H "Content-Type: application/json" \ -d '{"policy": {"Version": "2012-10-17", "Statement": [{"Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": ["arn:aws:s3:::my-bucket/*"]}]}}'Client API
Storage buckets are also available via the management API:
curl http://localhost:8787/v1/storage/buckets \ -H "apikey: truss_sk_your_key"