Skip to content

id: PoC: one example world running unmodified in a Worker isolate — journal equality vs the native wasmtime host (from #2057) #2271

Closed

Description

@dezren39

From the #2057 audit (world-VM Wasm portability)

Audit #2057 (queue task 3359, result posted on that issue 2026-10-29) classified pkgs/id world execution against Workers Wasm reality and defined the portability contract in terms of the existing manifest + limits machinery: guest module unchanged, host surface reimplemented per tier, portability = wasm | native declared in the world manifest and validated against world_limits-style caps. This issue is the proof of concept for the isolate tier. Not the permanent feature (program rule, epic #2056).

PoC scope

  1. Take one existing example world program (suggested: tic-tac-toe or roc-counter — small, turn-based) unmodified as a guest module.
  2. Run it inside a Cloudflare Worker isolate (Durable Object host) with the journal/store/clock host surface reimplemented in TypeScript against DO SQLite.
  3. Play a scripted sequence of turns through the isolate host and through the native wasmtime host; the resulting journals must be identical (same events, same state hashes) — that equality, not "it ran", is the PoC's pass condition.
  4. Enforce the audit's caps in the isolate host (memory declaration check, per-invocation compute bound) and demonstrate a refusal: a manifest declaring portability = native (or memory above the isolate ceiling) is rejected by the hosted loader with the reason named.

Out of scope

The full host surface (presentation/SSH/network rows stay native per the audit), Roc compilation in the isolate (pre-compiled modules only), any production hosted-world offering.

Acceptance criteria

  • Journal-equality demonstration (scope point 3) passes and is documented on this issue
  • Manifest refusal case (point 4) demonstrated
  • Findings note: which host functions the example actually used, and any surface that behaved differently across hosts
  • No production portability promise claimed or merged under this issue

Program: ID platform expansion, epic #2056. Parent audit: #2057. Related: #2044 (Workers fit), #2045/#2046 (hosted data / serverless edition), #2026 (app packaging).

Activity

  1. added
    priority/highImportant and time-sensitive
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Oct 29, 2026
  2. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  3. added
    work/assignedQueue worker lifecycle state
    queue/claimedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Oct 30, 2026
  4. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  5. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    Triage — #2271 PoC: example world in a Worker isolate, journal equality vs native host (queue task 3426)

    Verdict: feasible, and smaller than the issue's framing suggests — with three scope clarifications below that the implementation must follow. No hard blockers. One open question (real Cloudflare deploy vs local workerd) is resolved by recommendation, not escalated: it does not change the pass condition.

    Evidence gathered fresh this run against current main (laptop primary ~/git/code, HEAD 7b76c791):

    1. The guest needs NO host imports — the "TS host surface" is the plaza ABI, not a host-import reimplementation

    • pkgs/id/src/sandbox.rs:9-10: "The guest receives no imports: no WASI, filesystem, sockets, clocks, or random source."
    • pkgs/id/src/sandbox.rs:184-191: Sandbox::compile rejects any module with imports (guest imports are not allowed). Checked-in examples confirm it: examples/roc-counter/README.md — "The module has no Wasm imports."
    • All interaction is host→guest through the export allowlist (sandbox.rs, compile validation): memory, plaza_alloc, plaza_free, plaza_init(i64)->i32, plaza_update(i32,i32,i32,i32)->i32 (model ptr, participant, event ptr, event len), plaza_view, plaza_layout (optional), plaza_records (optional), plaza_wants (optional), plaza_snapshot/plaza_restore (both or neither), plaza_out_len, plaza_error_ptr/len.

    So the isolate host to write in TypeScript is: instantiate the import-free module with WebAssembly.instantiate, drive the plaza_* byte ABI (alloc/write event bytes, call update, read result via plaza_out_len + memory), and persist inputs/outputs. There is no journal/store/clock import surface to port for these guests. The findings-note acceptance item ("which host functions the example actually used") is already answerable: none — zero imports; the host surface exercised is exactly the plaza_* exports above.

    2. Example choice: tic-tac-toe primary, roc-counter as pipeline smoke

    • examples/roc-counter (seed + inc only, view = count=N, records {"count":N}) is the right first smoke — the native sandbox tests already pin its behaviour (src/sandbox.rs: checked_in_roc_counter_compiles_and_initializes_in_the_import_free_sandbox, seed 7 → count 0/1/2).
    • examples/tic-tac-toe is the right equality subject: pure update(model, pid, event) (pid is ignored; turn = ply parity), occupied/off-board/non-digit/post-win moves are ignored, snapshot = the 9-char board string and restore reconstructs plays+winner from it (examples/tic-tac-toe/main.roc). Suggested scripted sequence (X wins top row, with the edge cases interleaved): 0, 4, 1, 4 (occupied, ignored), 9 (not a move, ignored), 8, 2 (X wins), 3 (after win, ignored) — then snapshot mid/end, restore into a fresh instance on both hosts, and confirm identical continuation.

    3. "Journal equality" must be defined as per-step state equality — NOT native journal-file byte equality (scope clarification 1)

    The native world journal (src/world/mod.rs, src/world/core.rs) contains server-side entries (membership, capability grants/digests, schedules, provenance/origin metadata) that an isolate host does not and should not reproduce byte-for-byte. The property the #2057 contract actually rests on is: the same input sequence folds to the same state under either host. Define the PoC comparison as, after every scripted input: equality of (view, records, snapshot) and a hash of the snapshot (state hash), for the identical (seed, participant, event-bytes) sequence on both hosts. This matches the packet's stated test ("journal/state-hash comparison") and mirrors the existing native determinism machinery in src/world_fixture.rs (verify replays a fixture and compares records after every event).

    4. Cold start is part of the PoC, not an extra (scope clarification 2)

    Native hosting keeps one persistent wasmtime instance per world with the model pointer in guest linear memory (sandbox.rs module doc). An isolate/DO instance can be evicted at any time, so the DO's SQLite journal is the source of truth: on cold start the TS host must re-instantiate and either replay the journaled inputs or plaza_restore the last snapshot, then continue. The demonstration must include at least one forced cold start mid-sequence (fresh DO instance / fresh isolate) with equality holding across it — a warm-instance-only pass does not prove the hosted claim.

    5. Manifest refusal: the portability field does not exist yet (scope clarification 3)

    • grep portability over pkgs/id/src and pkgs/id/examples: no hits. src/world_manifest.rs WorldManifest today has module_hash, module_source, seed, grants, ceiling, profile, schedules — no portability declaration. Memory is declared via profile.wasm_memory_bytes (src/world_limits.rs:43-45, validated ≥1 page, clamped to the server ceiling).
    • Therefore the refusal demo is a hosted-loader behaviour in the PoC: the TS loader reads the manifest, refuses portability = native with the reason named, and refuses a declared wasm_memory_bytes above the id: investigate: world-VM Wasm portability audit — what of the worlds runtime runs inside a Worker #2057 hosted ceiling (64 MB) with the reason named. Adding the validated portability field to the native WorldManifest is a separate small Rust follow-up — it should NOT gate this PoC, and this PoC must not claim it landed.

    6. Known behavioural differences to pre-register in the findings note

    • Fuel: native enforces per-invocation fuel (SandboxLimits::default = 10,000,000; src/sandbox.rs). Workers has no fuel; the bound is CPU time. A guest cannot be fuel-metered identically in the isolate — for the chosen examples fuel use is trivial, but the findings note must state this difference explicitly rather than discovering it later.
    • Memory ceiling semantics differ: native default sandbox memory is 16 MB with trap_on_grow_failure; the isolate ceiling from the audit is a declared-memory check (≤64 MB) against a 128 MB whole-isolate budget. The refusal in (5) is a manifest check, not a runtime trap equivalence.

    7. Infrastructure: greenfield, and local workerd is sufficient

    No Workers/wrangler/Durable Object code exists in pkgs/id today (searched: no wrangler config, no DO host). The PoC creates it new (suggest pkgs/id/examples/roc-world/worker/ or a sibling pkgs/id/worker-poc/ — implementer's choice, keep it out of the native crate). Recommendation: run the isolate side under local workerd/Miniflare with DO SQLite emulation. Journal equality is a runtime-semantics question and workerd is the same isolate engine; a real Cloudflare deploy adds an account/credential dependency and proves nothing extra about equality. Real deploy = optional stretch, explicitly not part of the pass condition.

    Sizing & routing

    • Triage outcome: triaged — feasible as scoped above. Implementation is larger than a trivial fix (new TS host + DO journal store + native reference runner + comparison harness): estimate effort l, timebox at the queue max and expect it to span worker shifts via checkpoints.
    • Follow-on: implementation task enqueued from this triage (id in the closing agent note below). This issue stays open as the tracking issue; its acceptance criteria stand as written, read with clarifications 1–3 above.

    🤖 Agent note · triage findings — queue worker (repo-worker-code), lane D, task 3426.

  6. dezren39 commented on Oct 30, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    • Agent: queue worker (repo-worker-code), lane D
    • Session / ID: queue task 3426
    • Found via: queue claim
    • Task: Triage complete for id: PoC: one example world running unmodified in a Worker isolate — journal equality vs the native wasmtime host (from #2057) #2271. Verdict: feasible, smaller than framed — guests are import-free, so the isolate host drives only the plaza_* ABI; equality defined as per-step (view, records, snapshot) + snapshot hash including a forced cold start; manifest portability field does not exist yet, so refusal is a hosted-loader behaviour in the PoC. Full findings posted above on this issue.
    • Expectation: done = triage findings posted + follow-up routed. Implementation follow-on enqueued as queue task 3950 (small_fix, priority 30); this issue stays open as its tracking issue, labelled queue/queued for that task.
    • Links: triage findings comment on this issue; queue task 3950
  7. 87 remaining items

  8. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 1, 2026
  9. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    Parked: Queue task 5598 is parked for 30 minutes. Fresh GitHub shows issue #2271 OPEN and PR #3726 OPEN, authored by Drew on the exact task branch queue/5598-2271-worker-isolate-poc. The Drew-owned PR hard stop applies; I did not inspect its reviews/checks, touch its worktree, run tests, merge it, or create competing work.

    Resume condition: PR #3726 is merged/closed or task 5598 is explicitly rerouted; revalidate issue and PR fresh before proceeding.

  10. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state: picked-up (small_fix task 5598, OpenChamber scheduled lane)

  11. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — state: parked (small_fix task 5598, OpenChamber scheduled lane)

  12. added 2 commits that reference this issue on Nov 1, 2026
  13. dezren39 commented on Nov 1, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

  14. removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedQueue worker lifecycle state
    on Nov 1, 2026
  15. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    Agent note — triage done (task 5152, lane E)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)priority/highImportant and time-sensitive

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions