Cloudflare Workers
The same core on Workers and D1 — one binding, the schema applied by wrangler, and a Cron Trigger if you want the index drained.
The Worker owns bindings and nothing else. Routing, authorization, ranking and lifecycle live in the shared core, so the same 60-test contract suite runs against Bun with SQLite and against Workers with D1, and neither runtime can quietly drift from the other.
Five commands
wrangler d1 create titen
# copy the printed database_id into wrangler.jsonc
pnpm titen schema | wrangler d1 execute titen --file=-
pnpm titen bootstrap --org 'My Org' --print-sql | wrangler d1 execute titen --file=-
pnpm deploy:worker
curl https://titen.<your-subdomain>.workers.dev/healthz
titen schema prints every migration statement plus the titen_migrations bookkeeping
insert, so the schema reaches D1 through wrangler d1 execute rather than through
application code. bootstrap --print-sql does the same for the first organization: the
INSERT statements go to stdout, and the raw key goes to stderr so a pipe cannot
capture it into a file.
--print-sql puts only the key hash into the SQL. The raw titen_sk_… value is printed
to your terminal and never recoverable afterwards. Store it before you run anything else.
The binding is the configuration
{
"name": "titen",
"main": "src/runtime/cloudflare/worker.ts",
"compatibility_date": "2026-07-01",
"workers_dev": true,
"observability": { "enabled": true },
"vars": {
"TITEN_REVISION": "dev",
"TITEN_AUTO_MIGRATE": "0"
},
"d1_databases": [
{
"binding": "DB",
"database_name": "titen",
"database_id": "replace-with-your-d1-database-id"
}
]
}
TITEN_REVISION is a non-secret build marker echoed by /healthz and /readyz.
TITEN_AUTO_MIGRATE=1 lets the Worker apply pending migrations on cold start instead: one
attempt per isolate, and an isolate that loses the race swallows the error and continues,
because readiness reports the real applied version either way.
Leaving it at 0 is the safer default: the migration then lands when you run it, instead of
racing several isolates during a traffic spike. If the schema is missing, /readyz answers
503 NOT_READY with checks.migrations: "pending (applied 0, expected 9)" rather than
serving half a service.
Background repair is external here
A Worker has no in-process timer, so the Bun runtime’s maintenance loop becomes a
scheduled handler driven by a Cron Trigger. The pass runs inside waitUntil so the
platform does not cancel it when the handler returns.
"triggers": { "crons": ["*/1 * * * *"] }
The Worker reports background_repair: "external" unconditionally, because it cannot
observe whether a Cron Trigger is configured. Without one, drive POST /v1/index/drain and
POST /v1/webhooks/deliver yourself.
What you do not need
The memory loop and every collaboration route run on the D1 binding alone.
| Not required | Why |
|---|---|
nodejs_compat |
The core is plain fetch, URL and Web Crypto |
| Vectorize, Workers AI | Retrieval is lexical FTS5 on this runtime today |
| Cron Trigger | Only indexing and webhook delivery need one |
| KV, R2, Queues, Durable Objects | D1 is the only store; the outbox is a table |
Planned, not yet active
Vectorize plus Workers AI. A Vectorize store and a Workers AI embedder exist in src/runtime/cloudflare/vectors.ts, and the capability is built only when both the VECTORIZE and AI bindings are present. Neither is declared in wrangler.jsonc, so it returns nothing and readiness reports vector: "disabled". Semantic retrieval is live on Bun and SQLite; see Semantic vectors.
Channel serving through a scoped service credential for a CRM or chatbot gateway.
Memory Atlas as a read-only browser client against authenticated REST.
Secrets and verification
Store bootstrap and model secrets with wrangler secret put, never in wrangler.jsonc, and
issue a distinct revocable key per agent or service. The release gate for this runtime: an
unauthenticated protected request returns a JSON 401, cross-tenant access returns a
non-disclosing not-found, observation and context compile work with no Vectorize binding, a
cache-busted production response reports the deployed revision, and the rollback artifact plus
migration compatibility are known before release.
Cloudflare’s own limits and pricing in the repo’s blueprint must be refreshed before you rely on them for production sizing. The loop latency figure in the deployment doc was taken against local D1, not production.