Skip to content

Dev Server & Debugging

When you start Zebric in development mode - zebric dev - two servers open: the app server on port 3000 serving your application, and the admin server on port 3030 (localhost only) exposing debugging and inspection endpoints.

Here are the services they provide for you, the developer:

The dev server watches your blueprint file. Any time you save a change, it:

  1. Parses and validates the new blueprint
  2. Swaps the in-memory blueprint (no server restart)
  3. Broadcasts a reload signal to connected browsers
  4. Browsers reload with a smooth View Transitions animation (if supported)

If the new blueprint fails validation, the server keeps the previous version running and shows an error overlay in the browser with the exact validation error. Fix the file, save again, and the error clears.

Hot reload is active whenever you start with zebric dev. It only runs on localhost — the reload WebSocket is not injected in production.

Adding or removing entity fields triggers a schema diff. The hot reload still applies, but a banner in the admin server’s /schema-diff endpoint will show which migrations are pending. Run zebric dev --seed to re-apply schema changes automatically in development.

The admin server runs at http://127.0.0.1:3030 and is automatically disabled in production. It binds to localhost only and requires no authentication — it’s a local development tool.

EndpointDescription
GET /Lists all available endpoints
GET /healthEngine health status
GET /metricsPrometheus-format metrics
GET /blueprintFull parsed blueprint (as JSON)
GET /stateEngine status, version, uptime, pending schema diff
GET /entitiesSummary of all entities (name, field count, relation count)
GET /pagesAll page routes with layout and auth requirements
GET /schema-diffPending schema changes (fields added/removed/changed)
GET /pluginsLoaded plugins and their hook registrations
GET /workflowsWorkflow definitions, job queue stats
GET /workflows/visualizationHTML graph of workflow definitions
GET /tracesRecent request traces (?limit=N)
GET /traces/errorsTraces for 4xx/5xx responses
GET /traces/slowSlow traces (?threshold=1000 in ms)
GET /traces/statsAggregate latency stats (p50, p95, p99)
DELETE /tracesClear all traces from memory

Quick start — hit the root to discover everything:

Terminal window
curl http://127.0.0.1:3030/ | jq

Every HTTP request the app server handles produces a trace. Traces are stored in memory (up to 1,000, capped at 5 minutes) and are accessible via the admin server.

Each trace has a Trace ID that is also returned as the X-Trace-ID header on every app server response. This makes it easy to correlate a specific browser request with its trace.

Terminal window
# Capture the trace ID from a response header
TRACE_ID=$(curl -sI http://localhost:3000/api/posts \
| grep -i x-trace-id | awk '{print $2}' | tr -d '\r')
# View the full trace
curl "http://127.0.0.1:3030/traces/$TRACE_ID" | jq

The trace shows a hierarchy of spans — HTTP request, route matching, auth check, database queries — each with its own start time, end time, and duration in milliseconds.

Terminal window
# Requests slower than 500ms
curl "http://127.0.0.1:3030/traces/slow?threshold=500" | jq '[.[] | {path: .metadata.path, ms: .duration}]'
Terminal window
curl http://127.0.0.1:3030/traces/errors | jq '[.[] | {path: .metadata.path, status: .metadata.statusCode}]'
Terminal window
curl http://127.0.0.1:3030/traces/stats | jq '{p50: .p50Duration, p95: .p95Duration, p99: .p99Duration, errors: .errorCount}'

Inspect Workflow Definitions and Job State

Section titled “Inspect Workflow Definitions and Job State”
Terminal window
curl http://127.0.0.1:3030/workflows | jq

This returns every workflow registered from the blueprint along with its trigger type, step count, and the current job queue state — how many jobs are pending, running, completed, or failed.

Open http://127.0.0.1:3030/workflows/visualization in a browser to see an HTML graph of all your workflows and their step chains. Useful for verifying complex conditional logic before testing it.

Workflows triggered by entity events leave traces in the request that triggered them. To see a workflow execution:

  1. Trigger the entity event (create/update/delete a record)
  2. Fetch recent error traces:
    Terminal window
    curl http://127.0.0.1:3030/traces/errors | jq '.[0]'
  3. Look for spans with type = "workflow" inside the trace — they show which step failed and why

For manually triggered workflows (action bar buttons), the trace ID is in the HTTP response to the action POST request.

Workflow not triggering on entity event

  • Check the entity name and event type in the blueprint (entity, event)
  • For conditional triggers, verify before.* and after.* match expected field values
  • Look at /workflows to confirm the workflow is registered

Workflow step failing silently

  • The notify step requires a configured notification adapter — check [notifications] in your blueprint
  • Webhook steps to localhost or private IPs are blocked in production (SSRF protection); use dev mode for local targets
  • Template variables that don’t resolve (typo in {{ variable.path }}) cause step failures — compare against the template variable reference

Job stuck in pending state

  • Check /workflows for maxConcurrent — if it’s saturated, jobs queue up
  • A crashed runtime loses in-memory jobs; they do not persist across restarts

When you modify entity fields in development, check what changed:

Terminal window
curl http://127.0.0.1:3030/schema-diff | jq

A null response means the schema is in sync. Otherwise you’ll see:

{
"hasChanges": true,
"hasBreakingChanges": false,
"entitiesAdded": [],
"fieldsAdded": [{ "entity": "Post", "field": "summary", "fieldType": "Text" }],
"fieldsRemoved": [],
"fieldsChanged": []
}

hasBreakingChanges: true means a field was removed or its type changed — this requires a manual migration in production. In development, restart with --seed to reset.

If port 3030 conflicts with another service, override it:

const engine = new ZebricEngine({
blueprintPath: './blueprint.toml',
port: 3000,
dev: {
hotReload: true,
adminHost: '127.0.0.1',
adminPort: 4000,
},
})

The admin server has no authentication and exposes your blueprint schema, request data, user IDs in traces, and performance details. It binds to 127.0.0.1 by default and will not start outside of dev mode. Never expose it publicly.