bash, pwsh and run_code reach the network
through a child process or a worker thread, and the harness exposes no seam, event or registry
that observes a byte either sends: ctx.tools.guard() and the tools/pre-execute waterfall
both run before the spawn and are handed the model-authored argument and nothing else. So this
arm decides the destinations a command writes out and nothing else. A destination behind a
variable, a base64 round trip, a file read, or an address the program computes names nothing it
can decide, and neither does npm ci, whose registry comes from configuration rather than from
the command. Closing that needs the sandbox or a network-layer control.shell.readTextHosts adds the bare and
site: spellings the search-query filter reads. It is off because in a command those tokens
are usually filenames — main.cc, Makefile.in, Makefile.am and socket.io are all read as
hosts by that heuristic.shell.maxCommandLength is refused unscanned, in enforce mode, against the
(command) marker — the same fail-closed rule an over-length search query gets.plugin_manager is read through its two arguments, and only those. The
install_bundle action fetches a package with a direct execa inside the plugin-manager
package, so no shell tool and no routed subprocess carries it and the tool call is the only
point at which a mounted plugin sees the destination. registry and target are read the same
way a command is: a URL written out is decided, and anything else is not. An install that names
no registry falls back to pnpm’s own configuration chain — the profile’s .npmrc, the
plugin-manager row’s configured registry, and its configured fallbacks — none of which this
arm can see. The install’s own second request, a git ls-remote against a git spec’s host, and
its preflight pnpm view, are likewise not separately visible: they are consequences of the
one call this arm decided.plugin_manager does not exist in any release this package’s peer ranges admit.
@deepseek-ai/dsh-plugin-manager is first published at 0.1.6-alpha.2, and the declared peer
ranges stop at the 0.1.2-alpha line, so the default entry for it is inert on every harness
this package is currently proven against. It is shipped as a default because the entry costs
nothing where the tool is absent and is the difference between a recorded and an unrecorded
install where it is present.logrotate at the spool
on a long-lived installation, and at neither sidecar; see the configuration section.hostMemoryFlushMs (5 seconds by default) and
flushed when the plugin unloads — on ordinary completion and on SIGINT and SIGTERM alike. A
kill that runs no handler costs the sightings of the last interval: a host first contacted
inside it is reported first-seen once more, and its counts resume from the last write. Deleting
the file resets first_seen_host and report --suggest for the whole installation.allow: ['*.s3.amazonaws.com'] compiles and
works; the boot writes one line saying what it opens, and every request it clears is recorded
with wide_wildcard on it. Set wideWildcards: refuse for the load-time failure 0.5.0 through
0.6.0 had unconditionally. Whichever posture you choose, an entry naming a namespace neither
table carries produces nothing at all: no advisory, no record field, no refusal.*.sharepoint.com, *.my.salesforce.com, *.googleusercontent.com, *.gitpod.io,
*.statuspage.io, *.serveo.net and *.zoom.us all compile with nothing said about them.
Close any of these with a deny entry.src/suffixes.ts records the VERSION and
COMMIT of the file it was generated from, and nothing in this package fetches a newer one —
not at build, not at load, not at request time. A namespace added to the list after your release
is not wide here and nothing warns about it, which is the dangerous direction; a namespace that
stops being self-service is still wide here, which costs one allowWideWildcards line.
Upgrading dsh-netguard is how the data moves.*.salesforce.com, *.linode.com, *.render.com, *.fastly.net,
*.rit.edu and *.fedoraproject.org each get an advisory under the shipped posture and each
would fail the boot under refuse. Entries scoped to a tenant or to a name you hold are
unaffected: over the corpus in tests/unit/hosts.spec.ts — twelve tenant-scoped shapes, seven
ordinary corporate names, and the host patterns this documentation prints — nothing moved except
*.oslo.no and *.ny.us, which carrying the list whole is what added.site:, inurl:, link: and a full URL are read as destinations regardless.is_alert when one session issues more than alerts.distinctUrlsPerHost distinct URLs against
one host; a channel that stays under the threshold, spreads itself over several sessions, or
splits itself across several allowed hosts never trips it. It refuses nothing, it cannot tell a
covert channel from a crawl, and the count itself is bounded — past 256 distinct URLs for one
session and host it stops rising, and only the 64 most recently counted session-and-host pairs
are tracked at all.correlation_uid; the two maps are capped so a long session cannot grow them
without limit. A redirect chain is joined through the URL the model asked for, so every hop of
one chain carries the same id.search.delegate, result URLs are not filtered — only the outbound query is
checked, because the vendor provider the seam selected answers the seam directly and never
passes through this package.WEB_PROVIDER_AMBIGUOUS at the first web_fetch instead.WebRuntime (fetchProviders,
searchProviders, fetchProviderId, searchProviderId). That is a deliberate coupling to a harness version; when a rename makes
them unreadable, the check says it could not verify the composition and tells you to pin the
provider explicitly rather than inventing a verdict. See ADR.md.globalThis.fetch is not patched and the undici global dispatcher is not touched. Both
were considered and rejected: the harness’s own model traffic goes through the same function,
so a deny arm bricks the agent and a log arm writes x-api-key and Authorization headers
into the sink.Desktop is an Electron shell around the Web application. The Cordis plugin tree — and therefore
this package — runs in a separate ELECTRON_RUN_AS_NODE child process started by the shell
(apps/desktop/src/host-process.ts:188-201 at dsh-v0.1.7-rc.2). Everything below was read from
that tag’s source; none of it has been executed, because this package’s peer ranges do not admit
0.1.7-rc.2 and the end-to-end harness cannot drive it.
WebContentsView, the
sidebar <webview> guests, and every shell.openExternal handoff to the system browser all
run in the main process, on the other side of a stdio/ipc boundary. There is no seam. A
plugin in the Host child cannot observe, decide, or record any of it.packages/client/ui-sidebar-browser
states it outright: “Model Experience: None, as Browser tabs are user-facing presentation state
and register no tool, prompt section, or Session event.” A model-authored link becomes a guest
navigation only when a person clicks it and the link preference selects the in-app sidebar
(packages/client/ui-chat/src/client/apply.ts:227-229). This is not a gap in this package; it
is a control that does not apply.load_workspace_dependencies opens no socket and names no destination. It installs a
bundled payload offline and returns absolute interpreter, pnpm-script and library paths
(packages/skill/tool-workspace-dependencies/src/index.ts:249-276). It reaches
ctx.tools.guard() like every other tool, and this package abstains on it because there is no
destination to decide. What the agent then does with those paths runs through bash, pwsh or
run_code, which this arm already reads — to exactly the same ceiling it reads anything else
by. A bundled Python invoked by absolute path is not a fourth execution channel; it is the
third one with a longer program name.plugin_manager. Desktop supplies
its own Node and pnpm to package operations
(apps/desktop-host/src/index.ts:31-41), so an install needs no pnpm on PATH.report() and advise() write to
process.stderr because ctx.logger’s default exporter is an in-memory ring buffer. The
Desktop shell buffers the Host child’s stderr into a capped string and surfaces it only in the
message it builds when that child exits non-zero or stops
(apps/desktop/src/host-process.ts:200-229). So on a healthy Desktop run the wide-wildcard
advisory and the audit-sink-write-failure report reach nobody. Nothing a plugin can register
changes that.dsh-base pins web.fetchProvider: http
from 0.1.7-rc.2 (packages/bundle/base/cordis.patch.yml:475; the 0.1.0-rc.6 bundle has no
such line). Without the documented cordis.patch.yml override this package’s composition check
throws, the loader reports 1 entry did not activate, and the plugin does not mount — so the
command arm and both providers are absent together while the harness runs on. Combined with the
previous point, on Desktop that report is not seen. Confirmed by running this package’s
end-to-end suite against an installed @deepseek-ai/dsh@0.1.7-rc.2.