Skip to main content

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

  1. Open the Supabase dashboard → your project → Storage → Settings → S3 Access Keys
  2. Click New access key, scope to the bucket(s) you want MIRAGE to see
  3. 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 })

3. Configure CORS on the bucket

Supabase Storage’s S3 API respects the bucket’s CORS configuration, which is set in the Supabase dashboard:
  1. Project → Storage → Settings → Policies / CORS configuration
  2. Add http://localhost:5173 (and any production origins)
  3. Methods: GET, PUT, HEAD, DELETE, POST
  4. Headers: *
  5. Exposed headers: ETag, Content-Length, Content-Type, Last-Modified
See Python Supabase Setup for the equivalent Python wiring.