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.

GCS exposes an S3-compatible API when you use HMAC keys, so MIRAGE signs GCS requests with the same AWS Signature V4 used for S3, against the endpoint https://storage.googleapis.com. Credentials (HMAC access key + secret for a service account) are created the same way in both runtimes, see GCS Credentials.

Node (server-side)

pnpm add @struktoai/mirage-node
import { GCSResource, MountMode, Workspace } from '@struktoai/mirage-node'

const gcs = new GCSResource({
  bucket: process.env.GCS_BUCKET!,
  accessKeyId: process.env.GCS_ACCESS_KEY_ID!,
  secretAccessKey: process.env.GCS_SECRET_ACCESS_KEY!,
})

const ws = new Workspace({ '/bucket/': gcs }, { mode: MountMode.READ })
const res = await ws.execute('ls /bucket/')
console.log(res.stdoutText)

Browser (presigned URLs)

pnpm add @struktoai/mirage-browser
The browser GCSResource is secret-free, your backend signs each operation using your HMAC keys and hands back a URL. Since GCS accepts AWS Signature V4, the same @aws-sdk/s3-request-presigner code used for S3 works, pointed at the GCS endpoint.

1. Server: sign URLs with the GCS endpoint

import {
  CopyObjectCommand,
  DeleteObjectCommand,
  GetObjectCommand,
  HeadObjectCommand,
  ListObjectsV2Command,
  PutObjectCommand,
  S3Client,
} from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'

const client = new S3Client({
  region: 'auto',
  endpoint: 'https://storage.googleapis.com',
  credentials: {
    accessKeyId: process.env.GCS_ACCESS_KEY_ID!,
    secretAccessKey: process.env.GCS_SECRET_ACCESS_KEY!,
  },
})
const BUCKET = process.env.GCS_BUCKET!

app.post('/presign/gcs', 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 }) })
})

2. Browser: wire it up

import { GCSResource, MountMode, Workspace } from '@struktoai/mirage-browser'

const gcs = new GCSResource({
  bucket: 'my-gcs-bucket',
  presignedUrlProvider: async (path, op, opts) => {
    const r = await fetch('/presign/gcs', {
      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/': gcs }, { mode: MountMode.READ })
If you prefer Google’s native signer (Google-provided IAM-signed URLs via @google-cloud/storage’s getSignedUrl), you can use it here too, the presigner contract only cares that the returned URL works with fetch(). Native signing avoids shipping HMAC keys to your backend.

3. Configure CORS on the bucket

GCS rejects S3-flavored PutBucketCors calls, its CORS schema differs. Use either the native XML API (via HMAC keys, same creds your presigner uses) or gsutil cors set. Option A, helper script (no gcloud install): The browser example ships a signer that crafts the GCS-shaped CORS XML and submits it via AWS SigV4 to https://storage.googleapis.com/<bucket>?cors:
cd examples/typescript/browser
npx tsx scripts/configure-gcs-cors.ts http://localhost:5173 https://app.example.com
Option B, gsutil:
brew install google-cloud-sdk
gcloud auth login

cat > cors.json <<'EOF'
[
  {
    "origin": ["http://localhost:5173", "https://app.example.com"],
    "method": ["GET", "PUT", "HEAD", "DELETE", "POST"],
    "responseHeader": ["Content-Type", "ETag", "Last-Modified"],
    "maxAgeSeconds": 3000
  }
]
EOF

gsutil cors set cors.json gs://$GCS_BUCKET
GCS’s XML API accepts <CorsConfig><Cors><Origins>…</Origins><Methods>…</Methods><ResponseHeaders>…</ResponseHeaders><MaxAgeSec>…</MaxAgeSec></Cors></CorsConfig>. There is no <Headers> / <AllowedHeaders> element, GCS allows all request headers through automatically.
See Python GCS Setup for the equivalent Python wiring.