v0.7 · AI-native · MIT · Bun · Workers · Vercel · Netlify

The backend you'd build yourself,
running at the edge.

Dynamic schema, permissions DSL, REST + GraphQL, realtime, edge functions, vector search, and a built-in MCP server for AI agents — one codebase that runs on Bun, Workers, Vercel, and Netlify. Self-host or use the managed Cloud.

330+edge cities
~50msp95 anywhere
4deploy targets
MITlicense
capabilities

Everything a backend ships with, none of the lock-in.

No black box. The collection editor in the admin runs the same DDL your migrations would have. The permission DSL the admin writes is the same string your client sends as ?filter.

Dynamic schema

Define collections in the admin, each one becomes a physical c_<slug> table. CREATE TABLE runs against your live DB, no redeploy.

drizzle · pg + sqlite/d1

Permissions DSL

JSON predicates that compile to Drizzle SQL fragments AND match in-process for realtime. Same syntax in roles, REST ?filter, and GraphQL.

$user.id · _eq · _in · $or

REST + GraphQL

Every collection auto-exposes /api/items/:slug with filter/sort/expand/projection. GraphQL schema synthesised from the same metadata.

openapi 3.1 · graphql-yoga

Realtime

SSE in Bun, Durable Object + WebSocket on Workers. Per-event permission filter means subscribers see only what their role can read.

sse · durable objects

Edge functions

JavaScript sandbox with a host bridge for ctx.db / fetch / email. Bun worker thread, QuickJS-WASM, or remote HTTP — auto-selected per runtime.

quickjs · bun worker · http

Storage + image transforms

Local FS in dev, R2 / S3 on edge. Image resize via Bun.Image, CF Image Resizing, or passthrough — same URL shape.

r2 · s3 · fs

Webhooks & Flows

HMAC-signed outbound webhooks per event, plus a visual flow builder for branching multi-step automations. Fire-and-forget — never blocks API responses.

items.* · auth.* · cron

Auth that's not yours to maintain

better-auth wrapper: email, OAuth (Google/GitHub/Apple), magic link, OTP, passkey, SAML 2.0 SSO, LDAP / AD. First user gets admin.

better-auth · passkey-first

Activity + revisions

One activity table, dot-namespaced actions (item.update, auth.login). Every CRUD writes a row to revisions — point-in-time restore included.

audit log · point-in-time
deploy

One codebase, four runtimes.
Pick the constraints you need.

The same apps/web source compiles to Bun, Cloudflare Workers, Vercel Functions, and Netlify Functions. context.ts::buildContext picks the right adapter at boot.

B

Bun

Self-host on a VPS, Docker, or your laptop. Single long-running process.

db
sqlite / pg
storage
fs / s3
realtime
in-proc + sse
sandbox
worker thread
W

Workers

Cloudflare edge. Free tier covers most of it. DO-backed realtime.

db
d1 / pg
storage
r2 / s3
realtime
durable object + ws
sandbox
quickjs / remote http
V

Vercel

Functions on Node 22. Neon HTTP for DB. Built via Build Output API.

db
pg (neon http)
storage
s3 (required)
realtime
sse (capped)
sandbox
quickjs
N

Netlify

Functions on Node 22. Scheduled functions tick cron. Same Neon HTTP.

db
pg (neon http)
storage
s3 (required)
realtime
sse (capped)
sandbox
quickjs
global edge

Your backend, where your users are.

Deploy to Cloudflare Workers and your API runs in 330+ cities on six continents. The request never crosses an ocean to find its origin — D1 reads, Durable Object subscribers, and R2 object fetches happen at the city closest to the user.

San JoseSJC · us-west
12 ms
AshburnIAD · us-east
9 ms
LondonLHR · eu-west
8 ms
FrankfurtFRA · eu-central
7 ms
IstanbulIST · me-east
11 ms
SingaporeSIN · ap-southeast
14 ms
TokyoNRT · ap-northeast
10 ms
SydneySYD · ap-east
16 ms
São PauloGRU · sa-east
15 ms
JohannesburgJNB · af-south
19 ms
live anycast routing · 330+ cities · same admin, same API at every PoP
D1

D1 reads stay local

SQLite-shaped reads run at the colo that holds the replica. Writes ship to the primary asynchronously; reads never cross-region.

DO

Realtime via Durable Objects

Each items:<slug> channel pins to a DO. Subscribers connect to the closest DO via WebSocket; SSE bridges to clients.

R2

R2 storage at the edge

Object reads served from R2 with zero egress. Image transforms run via Cloudflare Images at the PoP; payload never travels twice.

cloud · managed

Don't want to run it yourself? workeros Cloud hosts it for you.

Same MIT codebase, same admin, same API — running on Cloudflare's Workers + D1 + R2. 30 seconds from signup to first deploy, 50ms first request, 330+ edge cities. One-click export if you ever want to bring it back home.

Start free — no card → See pricing private beta · 100K requests / mo on free

Multi-region routing

3 regions by default (IAD, FRA, IST). Up to 11 on Scale. Optional region pinning for data residency.

anycast · region pin

Auto backup + PITR

Daily D1 snapshots, 7-day rollback (Pro), 30 days (Scale). One-click restore from the admin.

d1 snapshot · r2 versioning

Custom domain + edge SSL

Bring your own domain. Certificates rotate on Cloudflare. No add-on fee, no config.

api.acme.app · cname

Preview branches

Git push → ephemeral environment. Same admin, isolated DB and storage. Auto-cleanup on PR close.

preview-247.workeros.dev

Usage panel + alerts

Requests, CPU, D1 r/w, R2, bandwidth — live in dashboard. Email at 80% and 100%, no surprise bills.

slack · email · webhooks

Your exit is preserved

One-click full export — schema + data + storage. Same MIT workeros, so your own server runs it in minutes.

sql + r2 archive · 1-click
option a

Self-host

MIT, on GitHub. Your server, your credentials.

€0 / forever
Full controlDB credentials, secrets, infra all yours
4 deploy targetsBun, Workers, Vercel, Netlify
Unlimited scalestart on Cloudflare's own free tier
You set it up — wrangler, DB, secret rotation, monitoring
You configure backups
Migrations and upgrades are on you
option b · cloud

Cloud (managed)

Same workeros, we run it. On Cloudflare.

from €0
Live in 30ssign up, project, deploy
Multi-region default3 regions, more on Scale
Backup + PITRdaily snapshots, 7-day rollback
Usage panel + alertsemail before overage
One-click exportswitch to self-host any time
You depend on Cloudflare's capacity (limits clearly stated)

Transparent limits

Numbers from Cloudflare's vendor docs — same numbers you see here. No marketing copy.

cloudflare native
Resource Free Pro Scale Powered by
Monthly requests100K10M100MCF Workers
CPU / request10ms30s5minCF Workers
DB size (D1)500 MB5 GB50 GBCF D1
Storage (R2)1 GB25 GB500 GBCF R2
EgressR2 zero egress
Realtime conn.505K50KCF DO
Postgres optionNeon add-onNeon
Changes in Cloudflare's plans flow through to ours. We email 30 days ahead of any tightening.
enterprise

A backend that passes vendor review.

SAML SSO, SCIM provisioning, SOC 2 (in progress), DPA + MSA, dedicated single-tenant cluster with region pinning. Annual invoicing for finance, redlines for legal.

SSO
SAML 2.0 · SCIM
Okta, Azure AD, Google, JumpCloud
Compliance
SOC 2 · GDPR
DPA + MSA templates
SLA
99.99% uptime
4-hour first response, 24/7
Isolation
Dedicated cluster
Single-tenant, region-pinned
quickstart

Five minutes from clone to your first item.

# clone, install, run — one terminal
$ git clone github.com/your/workeros && cd workeros
$ bun install
$ cp apps/web/.dev.vars.example apps/web/.dev.vars

# migrate local sqlite, then start vite + miniflare
$ bun run db:migrate:sqlite
$ bun run dev

   ready in 2.3s on http://localhost:5173
   sign up the first user at /sign-up
   first user auto-gets the admin role

Sign up the first user. They get admin.

better-auth's databaseHooks.user.create.after counts users; if it's 1, the role is admin, else authenticated. No bootstrap script, no env var, no command-line invitation flow to manage.

From there, define a collection in the admin and the API endpoint exists immediately — no redeploy, no codegen step, no schema versioning gymnastics. Drop a column, the column is gone. Rename a collection, the API path moves with it.

ai-native · mcp

Ask your data anything.
Agents speak the same API.

Natural-language prompts compile into the same Directus-shaped filter your client sends — every plan is reviewable, every tool call runs through your permissions DSL. A streamable MCP server ships in the box, so Claude, Cursor, or any agent can read & write through the same guards.

Ask in natural language
claude-haiku-4-5
top customers by lifetime_value last month, limit 10
to run · scoped to pak_8f2a…
Clear Run
Plan collections.list read
Interpreted as a list query against app_users with revenue sort. No write semantics detected — read-only plan.
{
  "collection": "app_users",
  "filter": { "last_order_at": { "_gte": $NOW(-30 days) } },
  "sort": ["-lifetime_value"],
  "limit": 10,
  "fields": ["id", "name", "email", "lifetime_value", "last_order_at"]
}
Permissions DSL will filter rows
Reject Run
Result collections.list
tokens 380 latency 28ms rows 10
name email lifetime_value last_order_at
Lena Voss lena@hexagon.studio $28,142 2026-05-22
Theo Ranganathan theo@frostgate.io $22,005 2026-05-19
Yuki Okafor yuki@altair-labs.com $18,830 2026-05-23
Mira Halvorsen mira@palebrook.co $17,210 2026-05-21
Salim Tessari salim@northbore.org $14,902 2026-05-18
Permissions DSL applied · 0 fields redacted
+5 more rows
Recent runs 9
ai.query → collections.list 2m
top customers last month
10 rows · 412ms · 380 tok
collections.bulk_update 6m
set status=archived for 47 posts
47 rows · 1184ms
ai.suggest_schema 14m
design a support_tickets collection
8 fields · pending review
schema.create_collection 2h
design a faq collection
blocked · mcp_read_only=true
ai.import_csv 1d
customers.csv → app_users.create
1,284 rows · 8.4s

MCP tool catalog

32 tools

Every admin capability is also an MCP tool — grouped, type-validated, and DSL-filtered before it runs.

schema 5
collections 7
storage 5
vector 2
graphql 1
permissions 7
api keys 3
flows 7
functions 2
ai-native 3
read write destruct admin

Per-key guards

pak_8f2a…

Bind an allowlist of tools and a read-only flag to any personal access key. Agents inherit the guards of the key they authenticated with.

Read-only Blocks every write tool at the dispatcher.
Tool allowlist Hide everything not switched on.
24 tools
# patch the active key's guards
$ curl -X PATCH $URL/api-keys/$id/mcp-guards \
   -d '{"mcpReadOnly":true,
       "mcpTools":["ai.query",
                  "collections.list",…]}'

Connect a client

streamable http

Stateless POST /mcp for tenant agents, /api/admin/mcp for ops bots. JSON-RPC over HTTP — no SSE, no socket churn.

claude desktop · cursor
{
  "mcpServers": {
    "workeros": {
      "command": "bun",
      "args": [
        "workeros", "mcp",
        "--url", "$URL/mcp",
        "--key", "pak_…"
      ]
    }
  }
}
docs/mcp → claude · cursor · curl
Try Ask AI on Cloud MCP spec Built on @modelcontextprotocol/sdk · model picker per workspace · falls back to ANTHROPIC_API_KEY
stack

The boring choices, made for you.

Every cross-runtime concern hides behind an interface in @workeros/core/adapters. Pick the runtime, the adapters fall into place.

api
Hono on Bun + Workers + Vercel + Netlify edge. Same routes, same middleware, same response shapes everywhere.
orm
Drizzle v1 for both Postgres and SQLite / D1. Hand-written migrations, lockstepped between dialects.
auth
better-auth — email, OAuth, magic link, OTP, passkey, SAML 2.0, LDAP / AD.
storage
fs (Bun dev) · R2 · S3 via aws4fetch on anything that runs JS. Image transforms via Bun.Image / CF Image Resizing.
vectors
pgvector on Postgres · Cloudflare Vectorize on Workers. Same write/query interface.
realtime
SSE in Bun · Durable Objects + WS on Workers. Permission filter runs per event.
sandbox
Bun worker thread · QuickJS-WASM · remote HTTP executor — auto-selected by priority.
admin
Vite + React 18 + shadcn/ui + Tailwind v4. Lazy-loaded per route. Lingui-translated.
sdk + cli
@workeros/client with generated types per collection. workeros CLI for migrations + type generation.
why workeros

The trade-offs, in one table.

Other tools do parts of this. workeros's design choice is to keep every part under one source tree, behind one API, deployable to four runtimes — without forking.

Capability workeros Supabase Directus Roll-your-own
Self-host, MIT licensed partial
Runs on Cloudflare Workers no no if you build it
Dynamic schema (runtime DDL) migrations only depends
Adopt existing tables without DDL no limited depends
Permissions DSL that compiles to SQL RLS build it
Per-event realtime permission filter RLS-based no build it
Edge functions sandbox + host bridge no build it
Visual flow builder no no
SAML 2.0 + LDAP / AD enterprise enterprise build it
Vendor-managed cloud option self-host only

No vendor-managed cloud is on purpose — every team we've talked to wanted to own the DB credentials themselves. If you'd like one, fork and run it.

philosophy
Everything an operator needs lives behind one URL. The schema editor writes the same DDL your migrations would have. The permission DSL the admin writes is the same string your client sends as a query parameter. There is no second layer to learn. — from docs/DESIGN.md

Why these tradeoffs?

  • Hybrid schema instead of all-Drizzle — matches the Directus mental model where the user's data shape evolves at runtime, but keeps system tables under static migration control.
  • Own permission DSL instead of CASL — needed something the SQL compiler could read; CASL is row-evaluation-only.
  • Three sandbox providers instead of one — free-tier Workers users shouldn't lose function execution; paid users shouldn't be stuck on WASM-slow QuickJS.
  • Passkey-first auth — phishing-resistant, hardware-backed, better UX than TOTP.

The backend ships with the docs. Start there.

Five-minute getting-started, the permission DSL spec, four deploy walkthroughs, and a sandbox guide. All in one place.