Use our capnweb fork with WebSocket-over-RPC support#1474
Merged
Conversation
Pins capnweb to iterate/capnweb (which adds support for passing WebSocket-bearing upgrade Responses over RPC, iterate/capnweb#1) via a pnpm override, so all workspace packages and transitive dependents get the fork. Installed from git pinned to a sha; the fork's prepare script builds dist on install, and capnweb is allowlisted in onlyBuiltDependencies so pnpm runs it. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CI showed that building the git dependency at install time is environment-sensitive: the Linux runners produced a dist without type declarations (runtime imports worked, typecheck failed), while local installs built it completely. Instead of depending on install-time builds at all, install a tarball built once (npm ci && npm run build && npm pack at iterate/capnweb@4d384fa) and attached to the fork's v0.8.0-websocket.1 release. The lockfile pins it by integrity hash, and capnweb no longer needs to be in onlyBuiltDependencies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
… legacy define compat, nested workspace.git (#1476) The parked debts from itx-next.md, bandaid pulled to the max per review — **no backcompat anywhere** (prd/preview/dev itx data is erasable). ## CodemodeSession tombstone — deleted The tombstone DO class, its `codemode-session-local` namespace, the `CODEMODE_SESSION` binding, and the vitest wrangler entries are gone. Alchemy emits the `deleted_classes` migration (mechanism proven in #1464). Streams that still carry durable subscriber events dialing the namespace will error on delivery — accepted, the data is being erased. ## executeCodemodeFunctionCall — protocol fully deleted (~2.5k lines) Every capability entrypoint loses its legacy dispatch method (agents, gmail, repos, secrets, slack, streams, workspace, openapi-bridge, AiCapability, OrpcCapability, test entries), along with `legacy-codemode-call.ts` and each wrapper's dead arg-validation helpers. Two callers were still alive and got clean replacements: - the agent chat/debug tool path now dials `AgentDurableObject.callAgentTool({ tool, path, args, callId })` - the ingress test entry upserts secrets via `OrpcCapability.call({ path: ["secrets", "upsert"], … })` `packages/shared/src/codemode/` → `packages/shared/src/type-tree/` (context-proxy deleted — only importer was a deleted legacy test provider); generated typing identifiers de-codemoded (`ItxConsole`, `generateContextTypesFromJsonSchema`, …). ## Registry legacy compat — deleted `caps.define` takes a `target` (SerializableCapTarget) **only**, end to end (registry, ContextDO, Project DO, handle, REPL typings): the legacy `source`/`kind: "worker" | "facet"` inputs, the `codeId` spelling of `cacheKey`, stored `worker`/`facet` kinds, the `source_json` rollback column + sync writes, and all read-side normalization are gone. All callers (browser REPL examples, every e2e suite) spell rpc/source targets directly. ## Nested `itx.workspace.git.*` — deleted Nested RpcTargets returned from entrypoint getters don't survive RPC boundaries; the flat `gitClone`/`gitAdd`/`gitCommit`/`gitPush`/`gitStatus` methods are the surface. The agent preset prompt was actively teaching the broken nested spelling — fixed. ## allowedHosts debt — closed as misdiagnosis The config is `allowedHosts: true`; the 403/502s came from a wedged vite process behind the still-connected cloudflared tunnel. Documented in itx-next.md; e2e through `os.iterate-dev-jonas.com` verified passing. ## Verification `pnpm typecheck` / `lint` / `knip` / `format` green; apps/os unit tests green; workerd suites (`test:project-ingress` 6/6, `test:itx-server-handle` 5/5 pre-#1472, `test:type-tree` 113/113) green; itx e2e (itx, fork, http, subscribe — 20 tests) green against a local dev server on the merged tree, including the post-#1474 capnweb fork. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Large breaking change to capability registration, MCP/ingress execution paths, and production streams that may still dial removed CodemodeSession subscribers; preview/dev data is intentionally erasable. > > **Overview** > This PR **removes the last codemode-era wiring** and tightens the itx capability model with **no read-side backcompat**. > > **CodemodeSession** is fully removed: the tombstone Durable Object, `CODEMODE_SESSION` binding/namespace in Alchemy and vitest wrangler configs, worker exports, and the dedicated test script. MCP and tests now assume execution goes through the shared itx runner. > > **Legacy `executeCodemodeFunctionCall` dispatch is deleted** across capability entrypoints (~2.5k lines), along with `legacy-codemode-call.ts` and shared `codemode/context-proxy`. Call sites move to itx-native paths (`callAgentTool`, `OrpcCapability.call`, OpenAPI `call({ path, args })`). **`packages/shared` codemode** is renamed to **`type-tree`** with de-codemoded type-generation names. > > **`caps.define` is target-only**: required `SerializableCapTarget`, no `source`/`kind`/`codeId`, no `source_json` column or `normalizeCapTarget` on read—stored rows use `target_json` verbatim. Docs, REPL typings, e2e, and browser REPL examples all use `{ type: "rpc", worker: { type: "source", source: { cacheKey, … } } }`. > > **Workspace git** drops the nested `git` RpcTarget getter; agents, presets, e2e, and preview scripts use flat **`gitClone` / `gitAdd` / `gitCommit` / `gitPush` / `gitStatus`**. **itx-next.md** records resolved debts (legacy define, nested git, allowedHosts misdiagnosis). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 9cf0edd. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "deployed", "updatedAt": "2026-06-10T20:40:22.067Z", "headSha": "9cf0eddaebfbcf1516be75f3e6acffdc31004f08", "message": null, "publicUrl": "https://os.iterate-preview-6.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27304663825", "shortSha": "9cf0edd" }, "semaphore": { "appDisplayName": "Semaphore", "appSlug": "semaphore", "status": "deployed", "updatedAt": "2026-06-10T20:31:34.680Z", "headSha": "0f50df98f943df63d8133e6be05dcdb33e77305c", "message": null, "publicUrl": "https://semaphore.iterate-preview-6.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27304258909", "shortSha": "0f50df9" } }, "environmentConfigLease": { "dopplerConfig": "preview_6", "leasedUntil": 1781127403360, "leaseId": "6fe10f72-e7bb-431a-9402-1abae9841129", "slug": "preview-6", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-6` Doppler config: `preview_6` Type: `environment-config-lease` Leased until: 2026-06-10T21:36:43.360Z ### OS Status: deployed Commit: `9cf0edd` Preview: https://os.iterate-preview-6.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27304663825) Updated: 2026-06-10T20:40:22.067Z ### Semaphore Status: deployed Commit: `0f50df9` Preview: https://semaphore.iterate-preview-6.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27304258909) Updated: 2026-06-10T20:31:34.680Z <!-- /CLOUDFLARE_PREVIEW --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
jonastemplestein
added a commit
that referenced
this pull request
Jun 10, 2026
… delegation; durable-object refs (#1482) The remaining itx roadmap on latest main, no backcompat (prd is redeployable): §8's endgame — the hardwired kernel becomes ordinary capability definitions — plus the last unimplemented WorkerRef kind. ## repos / workspace / worker are platform defaults now All three move into the `platform:project` code context (`src/itx/code-contexts.ts`): `repos` and `workspace` as ReposCapability/WorkspaceCapability loopbacks, `worker` through the ProjectWorker forwarder (path-call hop, members inner replay against the default export). Their handle getters are deleted, their names leave the reserved list (shadowable — the point), and `callWorkerFunction` on the Project DO dies (the forwarder path covers it). What remains hardwired is exactly the composition a cap definition cannot express: `caps`, `streams` (the access model and global-namespace gating live there), `fetch` (Law 5), `fork`, `project`, `projects`, `describe`. ## Chain delegation carries the originating context The enabling protocol change (allowed by no-backcompat): `itxInvoke` gains `origin`, set by the first delegating hop and preserved up the chain; the registry's dial-time `context` attribution prop uses it. This is what makes a context-SCOPED cap correct as an inherited definition — `workspace` resolves to `itx:ctx_…` for a forked child and `itx` for the project even though the definition lives two links up. WorkspaceCapability now derives its workspace id from the injected context (explicit `workspaceId` still wins; the agent DO's pre-clone wiring is unchanged and uses the same derivation). New e2e: a child fork writes to its workspace; neither the project context nor a sibling fork can read the file. ## durable-object WorkerRef refs ship, config-gated `{ type: "durable-object", binding, name }` resolves via `env[binding].getByName(name)` with the normal members/path-call dispatch. The namespace allowlist (`DIALABLE_DURABLE_OBJECTS`) is **empty by default**: every platform namespace (PROJECT, STREAM, …) is keyed across all projects, so allowlisting one would let any project handle dial any other project's objects by name. Deployments opt in via `APP_CONFIG_ITX.dialableDurableObjects`; per-project name scoping is the documented open design before any namespace can join the defaults. e2e asserts the define-time refusal. ## Behavioral changes - `itx.worker.fetch` no longer special-cases to project ingress — it replays `fetch` on the worker's default export like any other member. The project homepage is the ingress URL. - `describe()` on a fresh project now lists four inherited caps (`ai`, `repos`, `workspace`, `worker`) with `owner: "platform:project"`. ## Deferred, with reasons (in itx-next.md) - `streams` stays kernel: resolution checks access, which a cap definition cannot express. - REPL-consumes-types.ts (§5): the hand-maintained editor typings stay, comments updated. - UrlDial through the egress intercept tunnel: the capnweb fork (#1474) only helps capnweb hops; the UrlDial → Project DO hop is Workers jsrpc, which still cannot carry a WebSocket-bearing Response. ## Verification `pnpm typecheck` / `lint` / `knip` / `format` green; apps/os unit tests green (210); itx e2e (itx, fork incl. the new isolation test, http, subscribe — 21 tests) green against a local dev server. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **High Risk** > Protocol and dispatch changes (`origin`, workspace scoping, removed `callWorkerFunction`) affect fork isolation and worker routing; empty-default DO allowlist is security-critical if misconfigured. > > **Overview** > Continues **§8 “cap #0 disappears”**: **`repos`**, **`workspace`**, and **`worker`** move from hardwired `Itx` getters into the **`platform:project`** code context as ordinary loopback caps (`ReposCapability`, `WorkspaceCapability`, `ProjectWorker`). Those names leave the reserved list (shadowable); **`callWorkerFunction`** on the Project DO is removed in favor of the existing forwarder path. > > **Chain delegation now carries the originating context**: **`itxInvoke`** accepts optional **`origin`**, set on the first delegating hop and forwarded up the chain; the registry uses it for dial-time **`context`** attribution so inherited caps stay **context-scoped**—notably **`workspace`** (`itx` vs `itx:ctx_…` per fork). **`WorkspaceCapability`** derives **`workspaceId`** from injected **`context`** when not explicit. > > **`durable-object`** WorkerRefs are implemented behind an **empty-by-default** **`DIALABLE_DURABLE_OBJECTS`** allowlist, widened only via **`APP_CONFIG_ITX.dialableDurableObjects`**. > > **Security / behavior**: **`itx.project`** blocks **`itx*`** registry methods (spoofing **`origin`**); **`itx.worker.fetch`** no longer routes to ingress—it replays the worker default export like any member. Docs, REPL typings, and e2e (workspace isolation, DO define refusal) updated. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit e3e0f0a. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> <!-- CLOUDFLARE_PREVIEW --> ## Environment Config Lease <!-- CLOUDFLARE_PREVIEW_STATE --> <!-- { "apps": { "os": { "appDisplayName": "OS", "appSlug": "os", "status": "deployed", "updatedAt": "2026-06-10T22:03:33.498Z", "headSha": "e3e0f0af06e6e51ae95a5c2c316eaf6271f9dc3d", "message": null, "publicUrl": "https://os.iterate-preview-5.com", "runUrl": "https://github.com/iterate/iterate/actions/runs/27309107590", "shortSha": "e3e0f0a" } }, "environmentConfigLease": { "dopplerConfig": "preview_5", "leasedUntil": 1781132433085, "leaseId": "826c2bc4-fe41-46ae-a4d4-19a1b088d8d7", "slug": "preview-5", "type": "environment-config-lease" } } --> <!-- /CLOUDFLARE_PREVIEW_STATE --> Lease: `preview-5` Doppler config: `preview_5` Type: `environment-config-lease` Leased until: 2026-06-10T23:00:33.085Z ### OS Status: deployed Commit: `e3e0f0a` Preview: https://os.iterate-preview-5.com [Workflow run](https://github.com/iterate/iterate/actions/runs/27309107590) Updated: 2026-06-10T22:03:33.498Z <!-- /CLOUDFLARE_PREVIEW --> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pins
capnwebto our fork via a pnpm override inpnpm-workspace.yaml, so every workspace package (packages/streams,apps/os, the example app) and any transitive dependent (e.g. captun) resolves to it.The fork adds support for passing WebSocket-bearing upgrade
Responses over RPC — see iterate/capnweb#1 for the full design (stream-pair tunneling with flow control, claim-on-first-use lifetime, transport-equivalence test battery). This unblocks capnweb-over-capnweb tunneling for e2e tests of WebSocket-using services (upstream issue: cloudflare/capnweb#187).How it's wired:
overrides.capnwebpoints at a prebuilt tarball attached to the fork's v0.8.0-websocket.1 release, built from iterate/capnweb@4d384fa (npm ci && npm run build && npm pack). The lockfile pins it by integrity hash. Same pattern as captun, which already installs from apkg.pr.newtarball URL.npm packin the fork, attach to a new release, bump the URL,pnpm install.The first revision of this PR installed straight from git (
github:iterate/capnweb#<sha>), relying on the fork'spreparescript to builddist/at install time. That worked locally but turned out to be environment-sensitive: CI's Linux runners produced a dist with JS but no type declarations (thetestjob passed,lint-typecheckfailed with TS7016). The prebuilt tarball sidesteps install-time builds entirely and is byte-identical everywhere.Verified locally: fresh install pulls the tarball with full
dist/including declarations,newWebSocketRpcSessionet al. import fine, andpackages/streamspasses typecheck and all 67 node tests against it.🤖 Generated with Claude Code