The only browser-trust guard covered host.pickDirectory, while the consequential methods (session.prompt drives bash) accepted any Host — open to DNS rebinding, where a rebound page reads and writes the API as if same-origin and only the Host header betrays the attacker's domain. The pickDirectory-specific loopback guard becomes a prefix-wide fence: Host must be loopback or an exact host[:port] from the new trustedHosts config, an attached Origin must equal that authority, and explicit cross-site markers are refused; requests without browser markers (curl, tests, native clients) pass, because without a browser there is no confused deputy. The loopback-socket check is dropped — binding policy expresses reachability, and the fence is not an auth layer. The Agent Note records the full threat model and the alternatives.
@deepseek-ai/dsh-client-connection
English | 中文
Wire consumer layer: the client plugin's apply mounts ctx.connection (shared api client + single-consumer stream-loop starter); the export face carries the wire contract types, the AbstractApiClient seam, and the loop's sink/config types. The platform subclasses (WebApiClient/FixtureApiClient), the ConnectionController loop, and the fixture data source are package-internal — apply selects and drives them; tests reach them via src. Contract: api-contracts v3 §3.
/api browser-trust fence
The node half guards every request under /api before bridging (src/api-request-trust.ts): the Host header must be a loopback authority or an exact host[:port] entry from the plugin's trustedHosts config (DNS-rebinding defense), an attached Origin must equal that authority, and an explicit sec-fetch-site: cross-site marker is refused. Requests without browser markers (curl, tests, native clients) pass — without a browser there is no confused deputy. Failures answer plain 403 before any RPC dispatch. A non-loopback (--host 0.0.0.0) deployment must therefore list the authorities it is reached by in trustedHosts; the fence is deliberately not an authentication layer — reachability policy stays with the webserver binding, and auth remains deferred work. Decision record: the api browser-trust boundary Agent Note.
Keyless fixture
Any fixture query parameter selects the in-memory carrier. fixture=empty starts with no Workspace or Session; fixturePrompt=reject rejects prompts before acceptance; fixtureAttach=fail publishes a Session but rejects its Workspace attachment; fixtureSessionCreate=drop-response publishes and frames a Session before dropping the create response; and fixtureFrames=workspace-first reverses the default session-first create-frame order. Workspace creation by name/path and caller-preallocated SessionIds remain deterministic enough for assembled Web tests to reconcile list and frame arrival.
Model Experience
None, as the wire consumer layer moves already-composed messages between browser and host; nothing here reaches a model request.
KV Cache effect
None; this package neither assembles nor sends a provider request.
Known Limitations and Deferred Work
- history's implicit resume is arguable — opening history on an unattached session pulls an agent up host-side; the pure-persistence-read alternative is recorded in the rt-core reconciliation ledger, unchanged in P-I. This package's consumers see it as latency on first open.
ToolEventView/ToolCallView/ToolResultViewre-exports are scheduled for removal — they fall when the toolview migration deletes the hostviewForline (presentation belongs to the client); the fixture keeps a localviewFormirror until then.