The live MCP endpoint on 127.0.0.1 was a bypass of the collaboration
admission model. Its per-instance token authenticated only the ping probe
and shutdown, so document reads and writes were available to any local
process, and nothing validated Origin or Host — a page in a browser on the
same machine could reach it by DNS rebinding. During a session that is the
shared document, not just this user's file.
Every stateful call now requires the instance token, compared without an
early exit. Host must be a numeric loopback literal on the bound port, and
an Origin, when present, must match it; a request with no Origin still
works, which is what real CLI clients send. OPTIONS, initialize, and ping
stay tokenless so CLI discovery keeps working, and CollabGatePolicy is
untouched — this sits in front of it.
The `op` CLI did not send the token, so authenticating tool calls would
have returned 401 for every `op` invocation against a live editor. The
token was already in the port file next to the port; it is now resolved
with the port and travels as a header. Ping and shutdown keep their
existing tokenless wire contract.
Bootstrap cache: reads now degrade like writes already did. An unreadable
or corrupt cache leaves this start with no anti-rollback generation floor,
which is the position an absent cache has always left it in, and which the
threat model already accepts because deleting the file achieves the same
thing with no more privilege than corrupting it. Refusing bought no
security and cost the ability to collaborate at all. The tests state the
price plainly: with no floor the lower-generation document is accepted,
and `rollback_floor_armed` has to report it.
Threat model: correct an overstatement. Peer admission requires the remote
ticket's subject to equal the local account, so the product pairs only
devices of one account today. A relay operator reconstructs which devices
of an account sync and when — not a cross-account collaboration graph.
Also records that the relay reads exactly one field out of the ticket it
verifies, the expiry, which makes the identity disclosure gratuitous
rather than load-bearing, and states what a minimized credential would and
would not buy.
Completes the stringly-error conversion: 341 remaining
Result<_, String> signatures drop to two documented boundary sites
(a String-compat wrapper consumed across a crate boundary and a
test-only diagnostic helper). Eighty-plus enums follow the
established recipe — one enum per failure domain in a sibling
module, byte-identical Display text, From impls replacing map_err
adapters. mcp_live is fully typed (the screenshot channel included)
and all six temporary String bridges are deleted. The flaky
cli-model-discovery trio is made load-proof: exec'd sleeps so the
probe's kill actually closes the pipes, a serializing test lock,
and an escalating budget with a deadline-boundedness assertion.
Pure code motion: twenty-eight remaining oversized modules split into
spine + sibling layouts with re-exports keeping every import path and
test name stable. op-codegen, op-smoke, op-mcp, op-design-lint,
op-host-services, op-host-desktop, op-cli, op-i18n, op-editor-core,
op-editor-ui, and op-pen-loader no longer have any file over the cap.
CliError/SkillInstallError carry every op-cli failure (94 sites),
ProgramError types batch_program (14), WebCanvasError maps route
failures onto their existing HTTP statuses (23 -> 6 deliberate
boundary adapters). Display output is byte-identical to the old
strings so caller-visible text and tests are unchanged.
New leaf crate op-util single-sources hex-color parsing (9 divergent
copies, one with a non-ASCII panic), JSON escaping (one copy was
lossy), and HTML/XML escaping (one copy missed the quote entity — an
attribute-injection gap). Desktop now delegates image generate/search,
settings payload serde, and the --mcp/--serve-web argv dispatch to
op-host-services / op-editor-host-core instead of carrying drifted
copies. Byte-identical widget_host twin files collapse into shared
op-editor-core host_ui_transitions. Auth routes, the MCP port, product
name, env-var names, service URLs, and status colors move to single
shared constants / theme tokens; the stale claude-sonnet-4-5 default
model id is corrected.
The opencode target wrote a plugin entry the loader can never use (no
JS entrypoint, and plugin packages' skills/ are never scanned), so
'op install --target opencode' delivered nothing. Mirror the codex
layout instead: bundle under ~/.config/opencode/openpencil-skill with
a skills/ symlink opencode's {skill,skills}/**/SKILL.md scan picks up.
The installer-owned entry is recreated on every install so stale
squatters can't shadow it; remove_path only treats NotFound as absent
and handles dangling Windows directory symlinks.
Preflight headless document loading so malformed or binary archives fail with a clear, actionable error before the MCP server starts.
Keep the CLI parser dependency lightweight by disabling op-pen-loader default features and include the updated lockfile plus regression coverage.
op design gains explicit script routing (--script flag, implied for
@file.js/.mjs payloads) and the vendored openpencil-skill bundle
(0.8.0) teaches script mode with the same contract the internal
SCRIPT_FORMAT prompt uses.
Four Windows runtime defects from the platform audit:
- The binary stayed in the console subsystem, parking a console window
behind the GUI when launched from Explorer. Release builds now set
windows_subsystem = "windows"; debug keeps stderr tracing visible.
- Background CLI probes (model discovery, provider version checks) and
the vendored Claude SDK's per-turn spawns lacked CREATE_NO_WINDOW,
flashing console windows once the GUI detaches from the console.
- MCP stdio servers naming .cmd/.bat shims (npx and most npm-installed
servers) could not spawn: CreateProcess cannot execute shims and Rust
1.77+ refuses them as program names. vendor/agent now resolves the
command PATHEXT-style against the PATH the server will actually see
(per-server env override wins) and routes only genuine shims through
cmd /c — real executables keep direct spawn semantics.
- cmd /C start truncated URLs at `&` (every OAuth authorize URL). The
URL now travels double-quoted via raw_arg so cmd keeps it literal.