Runtime & Deployment
Node Runtime
Section titled “Node Runtime”The primary way to run Zebric in development:
zebric dev --blueprint blueprint.toml --port 3000 --seedThis starts a Hono HTTP server with:
- Hot reload via WebSocket (blueprint changes apply instantly)
- SQLite database (auto-created from entity definitions)
- Server-side HTML rendering with Tailwind CSS
- CSRF protection, security headers, and session management
Engine Configuration
Section titled “Engine Configuration”When using the engine programmatically:
import { ZebricEngine } from '@zebric/runtime-node'
const engine = new ZebricEngine({ blueprintPath: './blueprint.toml', port: 3000, host: 'localhost', database: { url: 'sqlite://./data.db', }, dev: { hotReload: true, seed: true, logLevel: 'info', // debug | info | warn | error logQueries: false, },})
await engine.start()Database Options
Section titled “Database Options”SQLite (default):
database: { url: 'sqlite://./data.db'}PostgreSQL:
database: { url: 'postgres://user:pass@localhost:5432/mydb'}The current engine selects the adapter from the database.url scheme. Use sqlite://<path>, postgres://..., or postgresql://.... In development, dev.dbPath can set the SQLite path when database.url is omitted. Other fields on the DatabaseConfig type are not currently consumed by engine initialization.
Workers Runtime
Section titled “Workers Runtime”See the Cloudflare deployment guide for required setup, supported features, the maintained starter/blog, and adapter and operational limitations.
Deploy to Cloudflare Workers using @zebric/runtime-worker — same blueprint format, running on Cloudflare’s edge network. The fastest way to start is copying starters/cloudflare-workers, which is a single-file entry point that fails fast when auth is not explicitly configured:
import { createWorkerHandler } from '@zebric/runtime-worker'import blueprintToml from './blueprint.toml'
export default createWorkerHandler(env => { if (!env.BETTER_AUTH_URL || !env.BETTER_AUTH_SECRET || env.BETTER_AUTH_SECRET.length < 32) { throw new Error('Set BETTER_AUTH_URL and a BETTER_AUTH_SECRET of at least 32 characters') } const origin = new URL(env.BETTER_AUTH_URL) const local = ['localhost', '127.0.0.1', '[::1]'].includes(origin.hostname) if (origin.protocol !== 'https:' && !(local && origin.protocol === 'http:')) { throw new Error('BETTER_AUTH_URL must use HTTPS outside local development') } return { blueprintContent: blueprintToml, blueprintFormat: 'toml' }})Without these checks the engine falls back to a localhost auth URL and a development
secret, which is unsafe in a deployed Worker. Blueprints without [auth] can pass a
plain config object instead.
The handler retains one engine per isolate and initializes bundled plugins once.
Command receipts and event outbox entries persist in D1. Bind WORKFLOWS for
durable workflow jobs and submission replay across isolates.
WorkersEnv requires DB (D1). When the Blueprint declares [auth], the
Worker runtime uses Better Auth on that D1 binding and mounts the same
/api/auth/* and /auth/* paths as Node. Set BETTER_AUTH_URL to the public
origin, store BETTER_AUTH_SECRET with wrangler secret put, include Better
Auth’s generated tables in the D1 migrations, and enable nodejs_compat.
File-backed Blueprint templates can be passed through the engine’s templates
map after importing them as Wrangler text modules. TEMPLATES_KV is an
alternative: store each template under template:<source> and the engine will
preload referenced templates before rendering. SESSION_KV remains available
for custom KV-backed session integrations. CACHE_KV creates the adapter
returned by engine.getCache(), but request execution does not use it
automatically. FILES creates the R2 adapter returned by engine.getStorage() and serves
stored objects at /uploads/<key>, matching Node’s public file-serving route.
The reserved _zebric/ object prefix is private and cannot be served there.
These routes serve existing files; neither runtime provides a generic upload API.
The engine applies double-submit CSRF protection when auth or sessions are configured.
The separate KV session-backed CSRF adapter remains available for custom composition.
Workers also expose Node-compatible /api/<entities> CRUD routes. Blueprint
API keys are loaded from Worker secret bindings named by keyEnv, with roles,
entity scopes, constraints, and X-Agent-Run-ID attribution enforced. Clients
can inspect the supported Worker surface at /.well-known/zebric-agent.json
and /api/openapi.json.
Workers execute declarative commands through the shared core command pipeline.
Bundle custom command handlers in commandHandlers, keyed by their Blueprint
handler references. General workflows support intermediate query results, typed
assignments, nested conditions and loops, services, notifications, external effects,
and delays. Database-only transactional workflows support reads and declarative
commands with atomic D1 commits and rollback; custom handlers and external effects
are rejected inside transactional workflows on both runtimes.
With a WORKFLOWS binding, workflow jobs use Cloudflare’s durable checkpoints,
delays, retries, and owner-authorized polling, cancellation, and retry routes.
Without it, workflows run inline and their job metadata and submission replay
remain isolate-local; command receipts remain durable. Configure native workflows:
import { createWorkerHandler } from '@zebric/runtime-worker'import { createWorkflowEntrypoint } from '@zebric/runtime-worker/workflows'import blueprintToml from './blueprint.toml'
const config = { blueprintContent: blueprintToml, blueprintFormat: 'toml' as const }export class ZebricWorkflow extends createWorkflowEntrypoint(config) {}export default createWorkerHandler(config)[[workflows]]name = "zebric-workflows"binding = "WORKFLOWS"class_name = "ZebricWorkflow"Workers initialize Blueprint notifications automatically. Console and Slack use
portable adapters; Slack secrets come from Worker bindings and inbound requests
at /notifications/<adapter>/inbound require valid Slack signatures. Email writes
to a private R2 outbox under _zebric/email-outbox/; as with Node’s file outbox,
this development adapter does not deliver mail. Supply notificationFactories
for a delivery provider or another adapter type.
Statically import plugins into the handler’s plugins map, keyed by Blueprint
plugin name. Enabled modules initialize once before fetch, cron, or native workflow
execution. Their workflow actions and named service integrations are available to
the executor. All bundled plugins are trusted application code; Worker bundling
does not create a sandbox for a plugin declaring limited trust.
Command, CRUD mutation, and workflow history is persisted in D1. Workers also
record auth outcomes, CSRF violations, denied requests, and successful page/API
reads. /metrics uses the same Prometheus counters and histograms as Node, with
isolate-local HTTP/query totals. See runtime parity and platform differences
for the tested contract and deployment constraints.
name = "my-zebric-app"main = "worker.ts"compatibility_date = "2026-04-28"compatibility_flags = ["nodejs_compat"]
[[d1_databases]]binding = "DB"database_name = "my-zebric-db"database_id = "your-database-id" # from: wrangler d1 create my-zebric-db
# Optional[[kv_namespaces]]binding = "SESSION_KV"id = "your-session-kv-id" # from: wrangler kv namespace create SESSION_KVCreate the D1 database, apply your migrations, then run locally or deploy:
wrangler d1 create my-zebric-db # copy the database_id into wrangler.toml
wrangler dev # local dev at http://localhost:8787, with D1/KV emulationwrangler deploy # deploy to your-app.your-subdomain.workers.devFor a working reference, see starters/cloudflare-workers.
Deployment
Section titled “Deployment”Docker
Section titled “Docker”FROM node:24-slimWORKDIR /appCOPY package.json pnpm-lock.yaml ./RUN npm install -g pnpm && pnpm install --prodCOPY blueprint.toml ./EXPOSE 3000CMD ["npx", "zebric", "dev", "--blueprint", "blueprint.toml"]Cloudflare Workers
Section titled “Cloudflare Workers”wrangler deployObservability
Section titled “Observability”The filesystem, admin-port and local monitoring examples below apply to Node. Workers uses D1 audit history, isolate-local metrics and Cloudflare observability; see the Workers guide.
Audit Logging
Section titled “Audit Logging”Security events are written as JSON to ./data/audit.log (configurable). Events include authentication attempts, access control denials, CSRF violations, data access, and suspicious activity. Each entry includes timestamp, event type, severity, user context, and sanitized metadata.
Sensitive fields (passwords, tokens, API keys) are automatically redacted.
Metrics
Section titled “Metrics”The engine exposes Prometheus-format metrics with zbl_* prefixed counters and histograms, registered on both the main app port and the admin port:
GET http://localhost:3000/metrics # main app portGET http://localhost:3030/metrics # admin port (127.0.0.1 only)Available metrics:
zbl_requests_total— total HTTP requestszbl_request_duration_ms— request duration histogram (buckets: 25, 50, 100, 250, 500, 1000, 2000ms)zbl_requests_by_route_total— requests per routezbl_requests_by_status_total— requests by status code family (2xx, 4xx, etc.)zbl_query_duration_ms— database query duration histogram per entityzbl_route_cache_hits/zbl_route_cache_misses— route cache performance
Health Check
Section titled “Health Check”GET http://localhost:3000/health # main app portGET http://localhost:3030/health # admin portReturns health status with a 200 (healthy) or 503 (unhealthy) status code.
Local Prometheus + Grafana Stack
Section titled “Local Prometheus + Grafana Stack”docker-compose.dev.yml includes Prometheus and Grafana services pre-configured to scrape your local Zebric instance.
# Prometheus onlydocker compose -f docker-compose.dev.yml up zebric-dev prometheus-dev
# Prometheus + Grafanadocker compose -f docker-compose.dev.yml up zebric-dev prometheus-dev grafana-dev- Prometheus UI: http://localhost:9091 — check Status → Targets to confirm
zebric-engineshowsUP, or query directly at/graph?g0.expr=zbl_requests_total - Grafana: http://localhost:3001 (default credentials
admin/admin). It has no Prometheus data source pre-configured — add one manually under Connections → Data sources, type Prometheus, URLhttp://prometheus-dev:9090(the Docker service name, notlocalhost)
The scrape config lives at monitoring/prometheus-dev.yml (10s interval, targets zebric-dev:3000). Update the targets field there if you change the app port or container name.
Prometheus data persists in the prometheus_dev_data volume between runs; docker compose -f docker-compose.dev.yml down -v resets it.
Admin Server
Section titled “Admin Server”The admin server runs on a separate port (default: 3030, bound to 127.0.0.1 only) and exposes:
| Endpoint | Description |
|---|---|
/health | Engine health status |
/metrics | Prometheus-format metrics |
/blueprint | Current parsed blueprint |
/state | Engine state |
/entities | Entity summary |
/pages | Page routes and layouts |
/workflows | Workflow definitions, job counts, and stats |
/workflows/visualization | HTML workflow graph visualization |
/traces | Request traces (with ?limit=N) |
/traces/errors | Error traces |
/traces/slow | Slow traces (with ?threshold=1000) |
/traces/stats | Trace statistics |
/schema-diff | Pending database schema changes |
/plugins | Loaded plugin list |