Yoke.sh
Runtime

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
FlagPurpose
--packagePath to user package
--databasePostgres URL
--storagepg_sqlite or s3
--tenant-idMulti-tenant scope (default default)
--portPortal HTTP port

Control plane (separate binary invocation): port 8090 default via YOKE_CONTROL_PLANE_PORT.

Boot failure modes

SymptomLikely cause
Migration errorPostgres version, permissions, stale schema
Package load errordeno.json, missing @yoke/sdk, TS compile error
Config validation errorinstance config doesn't match Zod schema
Portal start throwUncaught exception in start() — check logs
Health check failsBoot 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, CLI yoke 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.

On this page