Boot Sequence
What happens when Yoke starts — migrations, Deno worker, definitions, instances, fan-out, and drain loops.
When your server calls YokeRuntime::start (or you run yoke start ./my-package while authoring), the runtime executes a deterministic boot sequence. Understanding this order helps debug startup failures and reason about what exists before the first event is appended.
Sequence
1. Connect Postgres, run migrations
2. Boot embedded Deno worker (libdeno)
3. Load user package (resolve npm deps, transpile TS)
4. Execute yoke.ts → collect defineRuntime() definitions
5. Register definitions in kernel
6. Load instance records from Postgres
7. Rebuild fan-out index (declarative + filterFromConfig)
8. Provision SQLite per instance (pg_sqlite BYTEA or S3)
9. Ensure portal singletons exist in Postgres
10. Run one-shot runtime bootstrap() if defined
11. Register portal crons and HTTP routes
12. Start actor drain loops (scheduler)
13. Append runtime.started to stream
14. Run forever (wake on events, HTTP, schedules — then idle)Step-by-step detail
1. Postgres and migrations
Connect using DATABASE_URL. Run SQL migrations automatically. Creates tables: stream_events, actor_instances, actor_queues, actor_sqlite, conversations schema, multi-tenant tables, etc.
2–4. Deno worker and package
Spawn embedded worker. Resolve user package deno.json, npm lockfile, import maps. Execute yoke.ts default export.
Failure here: missing deps, TypeScript errors, invalid defineRuntime() shape.
5. Register definitions
Extract portal and actor definitions — config schemas, state schemas, queue policies, filterFromConfig, queueSelectFromConfig, routes, interfaces, inference stack.
Definitions are a fixed set — no dynamic registration after this point.
6. Load persisted instances
Read actor_instances and portal_instances for the tenant. Instances created in previous runs are restored.
7. Fan-out index
Rebuild fast-path index from declarative filters. Register function-filter instances for slow-path eval_filter. Load fan-out cursor from runtime_metadata.
8. SQLite provisioning
For each actor instance: load BYTEA from actor_sqlite or download from S3 → open rusqlite. Call onCreate only for new instances.
9. Portal provisioning
For each portal definition: ensure a singleton portal instance row exists in Postgres (create with {} config if missing). No long-running portal loops.
10. Runtime bootstrap
If the package defines defineRuntime().bootstrap(fn), run it once (must complete — no infinite loops). Typical use: idempotent ctx.instances.create({ id, ... }) for fixed topology.
11. Portal crons
Register declarative crons on portal definitions as scheduled route invocations (yoke.portal.route internally).
12. HTTP routes
Mount portal routes on the executor HTTP server.
System routes available immediately: GET /health, GET /metrics.
13. Scheduler
Start drain loops for all active actor instances owned by this worker. Claim orphaned instances (worker_id IS NULL) in multi-worker mode.
14. runtime.started
Append system event to stream — signals boot complete. Fan-out delivers to any instance filtering on this type (uncommon but useful for coordinators).
CLI invocation
export DATABASE_URL=postgres://yoke:yoke@localhost:5432/yoke
export YOKE_STORAGE=pg_sqlite
yoke dev ./your-package \
--database "$DATABASE_URL" \
--storage pg_sqlite| Flag | Purpose |
|---|---|
--package | Path to user package |
--database | Postgres URL |
--storage | pg_sqlite or s3 |
--tenant-id | Multi-tenant scope (default default) |
--port | Portal HTTP port |
Control plane (separate binary invocation): port 8090 default via YOKE_CONTROL_PLANE_PORT.
Boot failure modes
| Symptom | Likely cause |
|---|---|
| Migration error | Postgres version, permissions, stale schema |
| Package load error | deno.json, missing @yoke/sdk, TS compile error |
| Config validation error | instance config doesn't match Zod schema |
| Portal start throw | Uncaught exception in start() — check logs |
| Health check fails | Boot did not reach step 12 |
Multi-tenant boot
Tenant executors boot the same sequence scoped by YOKE_TENANT_ID. Cold start triggered by RuntimeLifecycleService when ingress activity arrives. Package ref from YOKE_PACKAGE_REF (S3/git).
Per-phase boot timings are recorded when YOKE_BOOT_METRICS is enabled (default on). Filter phases with YOKE_BOOT_METRIC_PHASES (all or comma-separated names like deno_spawn,package_load).
After boot
- Append events via
ctx.append, portal routes, CLIyoke append, or control plane API - Fan-out delivers to queues asynchronously
- Scheduler drains continuously until shutdown
Graceful shutdown: flush S3 (lazy mode), stop scheduler, tear down Deno worker, release leases.