Documentation Index
Fetch the complete documentation index at: https://docs.mirage.strukto.ai/llms.txt
Use this file to discover all available pages before exploring further.
Supabase Storage exposes an S3-compatible API at https://<project-ref>.storage.supabase.co/storage/v1/s3, signed with AWS Signature V4 using Supabase S3 Access Keys (not your project anon / service-role JWT). MIRAGE derives the endpoint from your project_ref automatically and forces path-style URLs.
Credentials
1. Create an S3 access key
- Open the Supabase dashboard → your project → Storage → Settings → S3 Access Keys
- Click New access key, scope to the bucket(s) you want MIRAGE to see
- Copy the Access key ID and Secret access key, the secret is shown once
2. Environment variables
# .env.development
SUPABASE_BUCKET=my-bucket
SUPABASE_REGION=us-east-1
SUPABASE_PROJECT_REF=abcdefghijklmnop
SUPABASE_ACCESS_KEY_ID=...
SUPABASE_SECRET_ACCESS_KEY=...
SUPABASE_REGION is metadata only, Supabase Storage accepts any region string but requires one for SigV4 signing. Use the region you selected when creating the project.
Node (server-side)
pnpm add @struktoai/mirage-node
import { MountMode, SupabaseResource, Workspace } from '@struktoai/mirage-node'
const sb = new SupabaseResource({
bucket: process.env.SUPABASE_BUCKET!,
region: process.env.SUPABASE_REGION!,
projectRef: process.env.SUPABASE_PROJECT_REF!,
accessKeyId: process.env.SUPABASE_ACCESS_KEY_ID!,
secretAccessKey: process.env.SUPABASE_SECRET_ACCESS_KEY!,
})
const ws = new Workspace({ '/bucket/': sb }, { mode: MountMode.READ })
const res = await ws.execute('ls /bucket/')
console.log(res.stdoutText)
Browser (presigned URLs)
pnpm add @struktoai/mirage-browser
Same pattern as every other S3-compat backend: the browser calls a presignedUrlProvider callback that hits your server, which signs each S3 operation against the Supabase endpoint and returns the URL. The browser fetch()es directly.
1. Server: sign URLs with the Supabase endpoint
import {
CopyObjectCommand,
DeleteObjectCommand,
GetObjectCommand,
HeadObjectCommand,
ListObjectsV2Command,
PutObjectCommand,
S3Client,
} from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
const PROJECT_REF = process.env.SUPABASE_PROJECT_REF!
const REGION = process.env.SUPABASE_REGION!
const client = new S3Client({
region: REGION,
endpoint: `https://${PROJECT_REF}.storage.supabase.co/storage/v1/s3`,
forcePathStyle: true,
credentials: {
accessKeyId: process.env.SUPABASE_ACCESS_KEY_ID!,
secretAccessKey: process.env.SUPABASE_SECRET_ACCESS_KEY!,
},
})
const BUCKET = process.env.SUPABASE_BUCKET!
app.post('/presign/supabase', async (req, res) => {
const { path, op, opts } = req.body
const key = path.replace(/^\/+/, '')
const ttl = typeof opts?.ttlSec === 'number' ? opts.ttlSec : 300
let cmd
switch (op) {
case 'GET': cmd = new GetObjectCommand({ Bucket: BUCKET, Key: key }); break
case 'PUT': cmd = new PutObjectCommand({ Bucket: BUCKET, Key: key, ContentType: opts?.contentType }); break
case 'HEAD': cmd = new HeadObjectCommand({ Bucket: BUCKET, Key: key }); break
case 'DELETE': cmd = new DeleteObjectCommand({ Bucket: BUCKET, Key: key }); break
case 'LIST': cmd = new ListObjectsV2Command({
Bucket: BUCKET,
Prefix: opts?.listPrefix,
Delimiter: opts?.listDelimiter,
ContinuationToken: opts?.listContinuationToken,
}); break
case 'COPY': cmd = new CopyObjectCommand({
Bucket: BUCKET,
Key: key,
CopySource: `${BUCKET}/${opts?.copySource}`,
}); break
}
res.json({ url: await getSignedUrl(client, cmd, { expiresIn: ttl }) })
})
Supabase requires path-style URLs (forcePathStyle: true). Virtual-hosted style is not supported.
2. Browser: wire it up
import { MountMode, SupabaseResource, Workspace } from '@struktoai/mirage-browser'
const sb = new SupabaseResource({
bucket: 'my-bucket',
projectRef: 'abcdefghijklmnop',
presignedUrlProvider: async (path, op, opts) => {
const r = await fetch('/presign/supabase', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ path, op, opts }),
})
const { url } = await r.json()
return url
},
})
const ws = new Workspace({ '/bucket/': sb }, { mode: MountMode.READ })
Supabase Storage’s S3 API respects the bucket’s CORS configuration, which is set in the Supabase dashboard:
- Project → Storage → Settings → Policies / CORS configuration
- Add
http://localhost:5173 (and any production origins)
- Methods:
GET, PUT, HEAD, DELETE, POST
- Headers:
*
- Exposed headers:
ETag, Content-Length, Content-Type, Last-Modified
See Python Supabase Setup for the equivalent Python wiring.