| Field | Value |
|---|---|
| Status | Active (core attach + WASM + sync shipped) |
| Last updated | 2026-07-18 (WS3 + RA0 concluded) |
| ADRs | 0001 · 0002 |
| Tracker | PLAN.md §4.6 |
ZCode is an IDE that does not depend on a server.
| Always true | Optional |
|---|---|
| VS Code Web UI in the browser | REH / remote PTY / native LSPs |
| Workspace + edit on client FS (OPFS / IDB) | Full remote filesystem / remote EH |
| Run / debug in browser via WASM (Node, Python) | Run on remote machine |
Git via isomorphic-git + /git-proxy |
System git on server |
Topology: same-origin / same-site only (user confirmed). No CDN shell ↔ separate runtime cookie work in this plan. Co-serve or same-site gateway is enough; reuse today’s cookie + REH proxy (R3b).
Cross-origin Topology B / OQ10: deferred indefinitely for this track.
- Same-site remote attach — browser workbench can connect to a co-served (or same-site) REH.
- Attach without losing the editor — user does not “start over”; ideally no full workbench reload; if upstream forces a reload, seamless state restore (tabs, layout, dirty buffers).
- Extensions own mode / runtime switching — prefer
zcode-*web extensions over core patches. - Browser execution without remote — WASM (or equivalent) runtimes for Python and Node-compatible JS, with terminal/tasks integrated in the editor.
- IDE agnostic of server — capabilities and “execution backend” are pluggable; absence of REH is a normal, polished mode.
| Capability | Status |
|---|---|
Browser IDE at / + OPFS FS + git |
Done (B*, M0*) |
Same-origin remote (zcode serve + cookie → REH) |
Done (R3b, M1, R6) |
Dual-mode at bootstrap (?mode=remote) |
Done — mode chosen before create() |
| Live attach / mode switch mid-session | In progress — RA1/RA2 |
zcode-remote |
Building (was upgrade stub) |
| Browser WASM Node / Python runtimes | Building (WB0–WB2) |
| Workspace browser↔remote sync ADR | Done — ADR 0002 (SA1) |
| Server-agnostic product ADR | Done — ADR 0001 (SA0) |
| Protocol execution backends | Done — SA2 |
┌─────────────────────────────────────────────────────────────┐
│ VS Code Web workbench (always browser UI) │
│ Workspace FS: zcode-opfs (primary) ± remote FS when attached │
└───────────────┬─────────────────────────────┬───────────────┘
│ │
┌──────────▼──────────┐ ┌──────────▼──────────┐
│ Execution backends │ │ Connection backends │
│ (extensions) │ │ (extensions) │
│ · browser-wasm │ │ · none (default) │
│ - python (Pyodide)│ │ · same-origin REH │
│ - node (WASM/WC) │ │ attach / detach │
│ · remote (REH PTY) │ │ │
└─────────────────────┘ └─────────────────────┘
Normative rules:
- Default boot = browser mode — no
remoteAuthorityrequired. Editor always loads. - Server is a capability, discovered via same-origin
/v1/session(already exists). If REH offline → browser + WASM still full product. - Do not invent a BackendFacade for editor IPC (KD2). Remote file/terminal/EH still use upstream VS Code remote protocol when fully attached.
- Day-to-day “run code” should go through an execution backend API (extension), so WASM vs remote is a setting/command — not a product reinstall.
Upstream VS Code typically bakes remoteAuthority into workbench create() and treats authority changes as window reload. We plan in two quality tiers so we ship value early.
User clicks Connect to Remote → login/cookie if needed → export/sync workspace (per ADR) → controlled reload to mode=remote with:
- Restore open editors, view state, layout (VS Code storage + our workspace id)
- Dirty buffer policy: flush to OPFS before reload; reopen from remote or rehydrate
- No secret query params; cookie already set (same-origin)
Feels continuous; technically one reload.
Stay on the same workbench instance:
| Approach | Pros | Cons |
|---|---|---|
| A. Remote as execution-only (extension PTY/tasks/LSP proxy to REH; workspace stays OPFS) | No reload; matches “agnostic IDE”; simpler | Not full remote FS/EH; partial remote |
B. Dynamic RemoteAuthorityResolver + open remote folder |
Closer to real VS Code remote | Web support may force reload; may need quilt |
C. Multi-root: keep OPFS root + add vscode-remote:// folder |
Hybrid | Often reloads; dual FS complexity |
Decision for this plan:
- Product default for “Run”: Tier-2 style A — execution backends switch without reload.
- Product “Open full remote workspace”: Tier 1 seamless reload first; pursue B/C only if spike proves no-reload on pin 1.129 web.
Spike (small, time-boxed) before large Tier 2 investment:
Spike S1: Can web workbench mid-session set remoteAuthority / open vscode-remote folder
without full reload on pin 1.129?
→ document outcome → choose B/C or stick to Tier1 + execution-only remote
| ID | Package | Deliverable |
|---|---|---|
| SA0 | ADR: server-agnostic IDE | Locks same-origin, execution backends, attach tiers, no cross-origin |
| SA1 | ADR: browser↔remote workspace sync | Format (git bundle vs tar), when sync runs, conflict policy — gates full remote open |
| SA2 | Protocol: ExecutionBackend + ConnectionState |
Types in @zcode/protocol (mode becomes capability matrix, not only browser|remote) |
Capabilities sketch (extend, don’t break):
// conceptual — refine in SA2
type ExecutionBackendId = 'browser-python' | 'browser-node' | 'remote-reh' | 'none';
interface ConnectionState {
remote: 'disconnected' | 'connecting' | 'attached' | 'error';
authority?: string; // host:port same-origin
/** full remote workspace vs execution-only */
scope: 'none' | 'execution' | 'workspace';
}
interface ProductCapabilities {
// existing fields…
executionBackends: ExecutionBackendId[];
defaultExecutionBackend: ExecutionBackendId;
}Update PLAN.md tracker: supersede cross-origin XO* queue with SA* / WB* / RT* IDs below.
| ID | Package | Deliverable |
|---|---|---|
| RA0 | Spike S1 (no-reload feasibility) | Written result in docs/spikes/remote-attach-no-reload.md |
| RA1 | zcode-remote extension (new or expand upgrade stub) |
Commands: Connect / Disconnect / Status; uses /v1/session + /login same-origin |
| RA2 | Tier 1 seamless attach | Cookie ready → sync (if SA1) → reload with state restore → remote workbench |
| RA3 | Execution-only remote (no reload) | When REH available: terminal/tasks target remote PTY without flipping whole workspace to remoteAuthority (if API allows; else soft-link to WASM until full attach) |
| RA4 | Status bar / diagnostics | Show Browser · WASM vs Remote · host · copy-safe report (extend zcode-diagnostics) |
| RA5 | E2E | Playwright: browser boot → connect remote (Tier 1) → terminal echo; no secrets in URL |
Reuse:
CookieTokenBridge, REH proxy,/v1/session— no new auth modelextensions/zcode-remote— Connect / Disconnect REH (upgrade command is an alias of Connect)- Same-origin only:
authority = location.hostwhen co-served
Out of scope for P1: cross-origin cookies, OIDC, multi-tenant session API.
Ship editor-integrated Run without REH.
| ID | Package | Deliverable |
|---|---|---|
| WB0 | Runtime provider interface | Shared extension API: startSession, runFile, runSelection, createTerminal, dispose |
| WB1 | Python backend (Pyodide) | zcode-runtime-python web extension; Run File / REPL terminal; packages via Pyodide micropip (document limits) |
| WB2 | Node / JS backend | zcode-runtime-node — choose after spike (see below) |
| WB3 | Tasks + CodeLens / Run button | Wire to active backend; honor zcode.execution.backend setting |
| WB4 | FS bridge | Runtimes read/write workspace via zcode-opfs / vscode.workspace.fs (not a second private FS) |
| WB5 | CSP / asset hosting | Serve .wasm + CDN or same-origin wasm assets; extend buildContentSecurityPolicy as needed |
| WB6 | E2E / unit | Python print("ok"); Node console.log("ok") in headless where feasible |
| Option | Fit | Notes |
|---|---|---|
WebContainers (@webcontainer/api) |
Strong Node compat | Licensing/CDN; commercial terms — verify for ZCode |
| Node WASI / wasm node builds | Lighter | Weaker npm compatibility |
| wasmer / wasi-sdk custom | Flexible | Higher eng cost |
| QuickJS / esm.sh worker | Fast path for pure JS | Not real Node |
Spike output: pick one primary Node backend + fallback (e.g. worker eval for simple JS). Prefer no core VS Code patch.
- Pyodide is the default recommendation (mature, in-browser CPython).
- Terminal UX: xterm via VS Code terminal API if possible, else webview panel v1.
- Document: no arbitrary native wheels; scientific stack partial.
| ID | Package | Deliverable |
|---|---|---|
| WS1 | Implement SA1 ADR | Export OPFS → git bundle or tar; import on remote session |
| WS2 | Pre-attach flush | Dirty editors save to OPFS; progress UI |
| WS3 | Optional detach | Remote → browser: pull changed files back (policy from ADR) |
Depends on RA2 for full remote workspace attach.
| ID | Item |
|---|---|
| PL1 | Default backend: auto (remote-reh if attached else browser-* by language) |
| PL2 | Welcome / walkthrough: “Run in browser” vs “Connect remote” |
| PL3 | PLAN.md / RESUME.md queue rewrite; retire cross-origin XO priorities |
| PL4 | CI: WASM runtime smoke + existing e2e:reh |
1. SA0 + SA1 + SA2 Product/ADR + protocol types
2. RA0 No-reload spike (1–2 days, gates Tier 2 ambition)
3. WB0 + WB1 Runtime interface + Python (high user value, no REH)
4. RA1 + RA2 + RA4 Extension connect + Tier 1 seamless attach + status
5. WB2 + WB3 + WB4 Node WASM + Run UX + FS bridge
6. RA3 / RA5 Execution-only remote if spike allows; e2e
7. WS1–WS3 Full workspace sync for remote open
8. PL* Polish + CI
Why this order:
- Same-origin remote already works at cold boot — biggest gap is live attach UX and serverless run.
- Python WASM delivers “IDE without server” immediately and does not block on REH.
- Tier 1 attach unblocks “connect remote” without betting the roadmap on a hard upstream spike.
- Workspace sync only matters for full remote workspace, not for WASM run.
| Extension | Role | Priority |
|---|---|---|
zcode-browser-fs |
OPFS provider (exists) | maintain |
zcode-git |
Browser SCM (exists) | maintain |
zcode-diagnostics |
Mode/backend report (extend) | P1 |
zcode-remote (from upgrade stub) |
Connect / disconnect / seamless attach | P1 |
zcode-runtime-python |
Pyodide backend | P2 |
zcode-runtime-node |
Node/JS WASM backend | P2 |
zcode-runtime-core (optional package) |
Shared provider registry, settings, Run commands | P2 |
Prefer extensions + thin shell over quilt patches. Quilt only if spike RA0 proves a one-line web remote attach hook is required.
Browser
└─ https://host/ workbench (always)
└─ https://host/vscode static assets
└─ https://host/v1/session
└─ https://host/login cookie zcode_sess
└─ https://host/* REH reverse proxy (when REH up)
└─ loopback REH
- Attach authority:
location.host(co-serve). - Cookie: existing
HttpOnly; SameSite=Lax. - No cross-site
SameSite=Nonework in this plan.
Optional later: same-site gateway (ide. + reh. under one registrable domain) — treat as ops, not a new product mode.
| Out | Why |
|---|---|
| Cross-origin CDN shell + separate REH cookies | User chose same-origin; OQ10 deferred |
| Full SaaS OIDC / multi-tenant session API | Not required for self-host attach |
| microVM | Unrelated |
| Replacing VS Code remote protocol with custom RPC | KD2 |
| Perfect native npm/pip in browser | Document WASM limits; remote for heavy native |
- Cold boot with no REH → full editor, OPFS workspace, git
- Run Python file via Pyodide without server
- Run Node/JS via chosen WASM backend without server
- Status clearly shows browser execution backend
- With REH: Connect to Remote from browser session
- Tier 1: after attach, remote terminal works (R6-level echo)
- Editor continuity: open files / layout restored (no blank restart)
- No connection secrets in URL
- Disconnect returns to browser capabilities (reload OK if state restored)
- Switch Run target WASM ↔ remote without workbench reload
- Spike-proven mid-session full remoteAuthority without reload (or documented impossible on pin)
| Risk | Mitigation |
|---|---|
| Upstream forces reload on remoteAuthority | Tier 1 + execution-only remote (RA3); don’t block WASM |
| WebContainers license | Spike WB2a before committing; have QuickJS/worker fallback |
| Pyodide bundle size | Lazy-load on first Run; cache; document download cost |
| Dual FS on hybrid attach | Prefer single workspace FS; execution-only remote avoids dual roots |
| Scope creep (OIDC, CDN) | Enforce non-goals; same-origin only |
| PR | Title | IDs |
|---|---|---|
| 1 | docs(adr): server-agnostic IDE + workspace sync |
SA0, SA1 |
| 2 | feat(protocol): execution backend + connection state |
SA2 |
| 3 | docs(spike): remote attach without reload |
RA0 |
| 4 | feat(ext): zcode-runtime-core + python (Pyodide) |
WB0, WB1, WB4–5 slice |
| 5 | feat(ext): zcode-remote connect + seamless Tier1 attach |
RA1, RA2, RA4 |
| 6 | feat(ext): zcode-runtime-node |
WB2, WB3 |
| 7 | test(e2e): attach + wasm run smoke |
RA5, WB6 |
| 8 | feat: workspace export/import for remote open |
WS1–WS3 |
| Previous (XO*) | This plan |
|---|---|
| OQ10 dual-origin cookies | Dropped (same-origin OK) |
| Path A gateway | Optional ops note only |
| Path B connectCode | Not needed |
| Live attach / WASM | New center of gravity |
- Write SA0 + SA1 ADRs under
docs/adr/. - Add SA2 types to
@zcode/protocol. - Run RA0 spike (document no-reload feasibility).
- Scaffold
zcode-runtime-python+ shared runtime interface (fastest user-visible “no server” win). - Implement
zcode-remoteTier 1 attach on same-origin.
No cross-origin server work unless product revisits that later.