Yoke.sh
Runtime

Deno Worker

Embedded in-process Deno via libdeno — package loading, JSON-RPC IPC, and yoke ops bridge.

User TypeScript runs in an embedded in-process Deno runtime via libdeno. The Rust kernel communicates with the worker over a unix socket using newline-delimited JSON-RPC — the same protocol as the earlier subprocess model, but without an external deno binary.

Architecture

YokeRuntime (Rust)
    ↕ unix socket, JSON-RPC
Embedded Deno worker
    → loads user package yoke.ts
    → registers definitions
    → handles dispatch_process, eval_filter, portal_invoke
EnvPurpose
YOKE_WORKERWorker entry script (runtime default; rarely overridden)
YOKE_WORKER_PROJECTDeno project dir for worker deno.json (runtime default)

Package loading

On boot:

  1. Resolve user package path from CLI --package or YOKE_PACKAGE_REF
  2. Load deno.json, import maps, npm dependencies
  3. Transpile and execute yoke.ts
  4. Collect defineRuntime() definitions
  5. Register actor/portal specs in kernel via get_actor_specs

The worker supports npm, jsr, node: builtins, and TypeScript transpilation — full Deno module resolution.

RPC protocol

TypeScript → Rust (host calls)

OpPurpose
stream_appendPublish event (Zod-validated in worker)
instances_createCreate actor instance
instances_destroyTear down instance
sqlite_execActor SQLite query
state_get / state_set / state_casTyped state with optimistic locking
portal_callInvoke portal interface method
schedule_upsert / schedule_cancel / schedule_listKeyed delayed events
conversation_*Conversation store CRUD

Rust → TypeScript (worker calls)

RPCPurpose
eval_filterFunction filter check for fan-out slow path
eval_queue_selectCustom queue selector over pending events
dispatch_processRun actor process(); dequeue on success
get_actor_specsQueue policy, default state, filter hints for boot
portal_invokeExecute portal interface method from actor

dispatch_process flow

Rust scheduler claims queue event
  → RPC dispatch_process { instanceId, event }
  → Worker builds ActorCtx for instance
  → await definition.process(ctx, event)
  → ctx.append() → stream_append op → Postgres → fan-out
  → ctx.portals.* → portal_call → portal_invoke
  → return success | throw
  → Rust: complete or requeue

SDK as thin wrapper

User code never calls ops directly. @yoke/sdk wraps each op:

// Simplified — @yoke/sdk wraps host ops
async append(input) {
  return callHost("stream_append", { ...input, correlation: resolved });
}

Rust serializes payloads; it does not interpret business logic.

Validation in worker

Event payloads are validated against defineEvents() Zod schemas in the worker before stream_append reaches Postgres. Invalid payloads throw in user code with schema errors.

Instance config is validated against configSchema on instances_create.

Portal interface dispatch

Actor: ctx.portals.slack.sendMessage("#general", "hi")
  → portal_call op (Rust)
  → portal_invoke RPC (Deno)
  → portal definition interface.sendMessage(...)

Portal start() loops and route handlers run in the same worker isolate.

Permissions

Currently allow_all_permissions for trusted host code.

Worker entry

The embedded worker boots the RPC server, registers op handlers, and exposes the host bridge to @yoke/sdk context factories. libdeno embedding replaces an external Deno subprocess — no external Deno process is required at runtime (Deno 2.x is still needed for development tooling).

On this page