SFabDocs
Getting Started

Installation

Stand up your own SFab factory on Cloudflare, end to end.

This guide takes you from a clone to a deployed factory on your own Cloudflare account. It is written to be followed by a human or handed to an AI agent; see Deploy with your agent for the one-prompt version.

What you're deploying

The platform is a single Cloudflare Worker (apps/platform) plus the resources it binds: a D1 database, two R2 buckets, Durable Objects, and container images for the agent sandboxes (built automatically from the repo's Dockerfiles on deploy). Containers and Durable Objects require the Workers Paid plan.

Prerequisites

  • Node.js 20+ and pnpm
  • A Cloudflare account on the Workers Paid plan, with wrangler authenticated (npx wrangler login)
  • A GitHub account that can create a GitHub App
  • Optional: a Resend API key for outbound email (invitations). You can skip it with MOCK_SEND_EMAIL=true.

1. Clone and install

git clone https://github.com/sfab-oss/sfab.git
cd sfab
pnpm install

2. Create the Cloudflare resources

cd apps/platform
npx wrangler d1 create sfab-platform-db
npx wrangler r2 bucket create sfab-workspace
npx wrangler r2 bucket create sfab-sandbox-backups

d1 create prints a database_id; you'll need it next.

Sandbox backups need more than the R2 binding

wrangler.jsonc wires a BACKUP_BUCKET binding to sfab-sandbox-backups for dev-session sleep/wake snapshots (and historically sandbox-agent mid-run resume — that ACP path was removed in ALW-469). That binding lets the Worker read/write backups locally and is not enough in production: the sandbox runtime uploads checkpoints via presigned URLs from inside the container. You also need four Worker secrets (step 5) with R2 API-token credentials scoped to this bucket. Without them, checkpoints fail with InvalidBackupConfigError / backup_failed until the secrets are set.

3. Point the config at your account

Edit apps/platform/wrangler.jsonc:

  • Replace database_id under d1_databases with the id from step 2.
  • Replace the routes entry (app.sfab.dev) with your own custom domain, or delete the routes block to serve from the default *.workers.dev URL.

Everything else (Durable Objects, containers, bucket bindings) works as-is. Decide your platform URL now; the GitHub App and several secrets reference it.

4. Create the GitHub App

The factory works through a GitHub App you own: it receives PR and CI webhooks, posts the AI review as a check, and syncs tasks and documents into your repos.

On GitHub: Settings → Developer settings → GitHub Apps → New GitHub App.

  • Homepage URL: your platform URL
  • Callback URL: https://<your-platform-domain>/api/auth/callback/github
  • Webhook URL: https://<your-platform-domain>/webhooks/github
  • Webhook secret: generate one (openssl rand -hex 32) and keep it
  • Repository permissions:
    • Pull requests: Read & write
    • Contents: Read & write
    • Checks: Read & write
    • Administration: Read & write — project fabrication creates the new repo
    • Workflows: Read & write — fabrication seeds .github/workflows/ files, and GitHub 403s any API write touching that path without this permission
    • Secrets: Read & write — sealed-box write of the Cloudflare deploy token and Worker secret_text values into repo Actions secrets
    • Metadata: Read (default)
  • Account permissions: Email addresses: Read
  • Subscribe to events: Pull request, Check suite

After creating it, collect: the App ID, the app slug (from its URL), a generated private key (.pem), the Client ID, and a generated client secret.

5. Set the secrets

From apps/platform, set each with npx wrangler secret put <NAME>:

SecretValue
BETTER_AUTH_SECRETRandom, 32+ chars (openssl rand -base64 32)
BETTER_AUTH_URLYour platform URL, e.g. https://factory.example.com
BETTER_AUTH_TRUSTED_ORIGINSSame URL (comma-separate extras if any)
ALLOWED_EMAILSComma-separated emails allowed to sign in; empty allows anyone
ENCRYPTION_KEYBase64 of exactly 32 bytes (openssl rand -base64 32); encrypts stored provider keys
GITHUB_APP_SLUGThe app slug from step 4
GITHUB_APP_IDThe App ID
GITHUB_APP_PRIVATE_KEYThe full private key PEM
GITHUB_WEBHOOK_SECRETThe webhook secret from step 4
GITHUB_CLIENT_IDThe Client ID
GITHUB_CLIENT_SECRETThe client secret
RESEND_API_KEYResend key, or skip and set MOCK_SEND_EMAIL to true
EMAIL_SENDERFrom-address for platform email
MOCK_SEND_EMAILtrue to log emails instead of sending

Sandbox backup secrets (required in production). Production uploads off-container checkpoints via presigned R2 URLs; the BACKUP_BUCKET binding alone is insufficient — set these in addition to creating the bucket in step 2. Local dev skips them and uses CONTRACTOR_BACKUP_LOCAL=true in .dev.vars instead (see Local development).

Create an R2 API token in the Cloudflare Dashboard: R2 → Manage R2 API Tokens → Create API token. Choose Object Read & Write, scope it to the sfab-sandbox-backups bucket (or your chosen backup bucket name), and copy the Access Key ID and Secret Access Key once shown.

SecretValue
R2_ACCESS_KEY_IDAccess Key ID from the R2 API token above
R2_SECRET_ACCESS_KEYSecret Access Key from the same token
BACKUP_BUCKET_NAMEsfab-sandbox-backups (must match the bucket from step 2 and the BACKUP_BUCKET binding in wrangler.jsonc)
CLOUDFLARE_ACCOUNT_IDYour Cloudflare account ID (Dashboard home sidebar, or npx wrangler whoami)

Checkpoints expire after three days (SDK default TTL); that bounds R2 storage cost for session backups.

LLM keys for the agents (e.g. Anthropic) are not Worker secrets; you add them in the platform UI after signing in, under settings, and they're stored encrypted with ENCRYPTION_KEY.

6. Deploy and migrate

pnpm deploy           # builds the Worker + container images, deploys
pnpm db:migrate:prod  # applies the D1 migrations to the remote database

The first deploy takes a few minutes while Cloudflare builds the sandbox container images.

7. Sign in and connect GitHub

  1. Open your platform URL and sign up (the first account is yours; set ALLOWED_EMAILS if the instance is public).
  2. Add an LLM provider key in settings so agents can run.
  3. In Settings → GitHub, install your GitHub App on the org/repos you want the factory to manage, then link the installation and link each repo to a project.

Linking matters: webhooks for unlinked installations or repos are skipped by design. If something doesn't fire, the /logs page (filter eventType=webhook.github) shows exactly which gate skipped and why.

Local development

cp apps/platform/.dev.vars.example apps/platform/.dev.vars  # fill it in
pnpm dev          # platform on http://localhost:7322

pnpm dev:reset:local (from apps/platform) resets and seeds the local database. GitHub webhooks can't reach localhost; use a Cloudflare Tunnel pointing at port 7322 and use the tunnel URL as a dev GitHub App's webhook URL.