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| Env | Purpose |
|---|---|
YOKE_WORKER | Worker entry script (runtime default; rarely overridden) |
YOKE_WORKER_PROJECT | Deno project dir for worker deno.json (runtime default) |
Package loading
On boot:
- Resolve user package path from CLI
--packageorYOKE_PACKAGE_REF - Load
deno.json, import maps, npm dependencies - Transpile and execute
yoke.ts - Collect
defineRuntime()definitions - 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)
| Op | Purpose |
|---|---|
stream_append | Publish event (Zod-validated in worker) |
instances_create | Create actor instance |
instances_destroy | Tear down instance |
sqlite_exec | Actor SQLite query |
state_get / state_set / state_cas | Typed state with optimistic locking |
portal_call | Invoke portal interface method |
schedule_upsert / schedule_cancel / schedule_list | Keyed delayed events |
conversation_* | Conversation store CRUD |
Rust → TypeScript (worker calls)
| RPC | Purpose |
|---|---|
eval_filter | Function filter check for fan-out slow path |
eval_queue_select | Custom queue selector over pending events |
dispatch_process | Run actor process(); dequeue on success |
get_actor_specs | Queue policy, default state, filter hints for boot |
portal_invoke | Execute 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 requeueSDK 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).