DSH plugin WebRoute: matching order and the fallback seat

Plugin DevelopmentPublished 2026-10-17Author: DeepSeek Plugin Market
DeepSeek HarnessDSH pluginWebRoutefallbackindex injectionHTTP
How a DSH plugin registers HTTP routes: exact then prefix then fallback, a single-owner fallback seat, index injection and listen semantics.

The DSH plugin HTTP carrier deliberately knows nothing about DeepSeek Harness: it offers named routes, optional gzip, index transform callbacks and one claimable fallback handler. Every functional route — the /api bridge, plugin bundles, HMR event streams — is registered by other plugins. That positioning yields three rules to remember: matching order is fixed as exact → longest prefix → fallback, registration order carries no semantics; the fallback has exactly one owner; and it serves browsers only (Electron loads file:// and talks over IPC, never through this server).

DSH plugin HTTP carrier: browser transport only

Bottom line: the carrier provides transport; plugins register functional routes; it is neither the agent loop nor a capability seam.

  1. What it is: a node:http plugin providing ctx.webServer.
  2. What it includes: a named route registry, optional gzip, index transform callbacks and a claimable fallback.
  3. What it excludes: TLS, authentication and Origin policy.
  4. Who it serves: browsers only; the desktop app uses file:// plus IPC.
  5. Output ownership: the package prints nothing; the startup URL line belongs to the shell.

DSH plugin WebRoute and matching order: exact, prefix, fallback

Bottom line: matching order is fixed and registration order does not participate.

ts
interface WebRoute {
  kind: 'exact' | 'prefix'
  path: string   // absolute pathname, no trailing slash
  handler: (req, res) => void | Promise<void>
}
  1. exact matches the pathname verbatim.
  2. prefix matches the prefix and any subpath.
  3. Order: the exact table, then the longest matching prefix, then the fallback.
  4. Disjoint routes: named routes must not overlap; a duplicate (kind, path) throws.
  5. Upgrade routes: one protocol owner per path; duplicates throw as well.
  6. Handler duties: own the full response lifecycle, including keeping a connection open for SSE.

DSH plugin fallback seat: exactly one owner

Bottom line: the fallback handler is unique, a second registration throws, and unclaimed requests return 404 during startup.

  1. Semantics: answers every request no named route matches.
  2. Uniqueness: two fallbacks cannot compose, so only one owner exists.
  3. Shipped claimant: the frontend static plugin claims the seat with fixed SPA dist semantics.
  4. Static details: authenticate before reading dist; non-index assets stay public; non-GET/HEAD returns 405; traversal outside the root returns 403; misses return an empty 404; unknown extensions go out as octet-stream.
  5. Startup window: while the seat is unclaimed, unmatched requests answer 404 until the owner registers.

DSH plugin index injection: structured rows then raw taps

Bottom line: render structured injection rows first, then apply raw html transforms in registration order.

  1. Structured rows: collectIndexInjections() gathers one webserver/index-inject emit where subscribers push their current rows.
  2. Fresh per call: subscribers can read live state such as the module graph and theme preference.
  3. Raw escape hatch: tapIndex(transform) registers a pure html-to-html function.
  4. Application order: renderIndex(html) renders structured rows first, then taps in registration order.
  5. Who calls it: the fallback owner on every index response it renders.
  6. Real example: the client modules package responds with startup manifest rows.

DSH plugin configuration and lifecycle: listen, compress and tear down

Bottom line: listen on activation and fail initialization on error; teardown must force-close connections.

ConfigValuesDefault
host127.0.0.1 or 0.0.0.0loopback
portnumber; 0 asks the OS—
compressionnone / gzipnone
compressionLevel0–91
compressionThresholdBytesminimum size to compress1024
  1. Binding is exposure: the carrier owns no TLS, authentication or Origin policy, so a non-loopback bind is deliberate exposure.
  2. The shipped command is stricter: dsh web picks loopback and rejects --host 0.0.0.0; its Connection plugin handles Host/Origin checks and browser authentication.
  3. Compression scope: eligible socket responses only; existing encodings, no-transform, ranges, SSE, ZIP and pre-compressed files stay identity.
  4. Listen failure: initialization is rejected and boot reports the failed fiber.
  5. Handler errors: logged as warnings and answered with 400 (or a destroyed socket once headers were sent), never crashing the process.
  6. Teardown: close() paired with closeAllConnections(), because SSE-style handlers keep responses open and would otherwise hang the shutdown.

DSH plugin HTTP troubleshooting

Bottom line: separate unmatched routes, a claimed seat and missing index injection.

  1. 404s — no named route matched and no fallback is claimed, or the path differs from registration.
  2. Route conflict errors — duplicate (kind, path) or upgrade paths, both configuration errors.
  3. Fallback registration throws — an owner already exists; check for double claiming.
  4. Injection missing — confirm the fallback owner calls renderIndex and rows are pushed during the emit.
  5. No gzip — the response is in the identity allowlist (ranges, SSE, pre-compressed).
  6. Repair status, stated honestly: matching order, seat uniqueness, index injection and configuration semantics follow the official HTTP server page; confirm static claimant details against the package source.

Sources: HTTP server, dsh-host-webserver source, dsh-host-frontend-static.

FAQ

How are DSH plugin routes matched, and does registration order matter?

DSH plugin route matching is fixed: the exact table first, then the longest matching prefix, then the fallback. Registration order carries no request-facing semantics, and named routes must be disjoint at the composition level.

Can more than one handler claim the fallback seat in a DSH plugin deployment?

No: the DSH plugin fallback seat has exactly one owner and a second registration throws, because two fallbacks cannot compose. The shipped Web composition has the frontend static plugin claim the seat.

What happens when a DSH plugin registers the same route twice?

It throws: a duplicate (kind, path) is a configuration error in DSH plugin routing because route patterns are a composition-level contract. HTTP upgrade routes behave the same way, since one socket has one protocol owner.

What are the two ways to inject into index.html in a DSH plugin web server?

DSH plugin provides two paths: structured IndexInjection rows and raw html-to-html tapIndex transforms. Rendering applies the structured rows first, then the registered taps in registration order.

What happens when the DSH plugin web server cannot listen on its port?

The DSH plugin WebServer listens immediately on activation, and a listen failure such as EADDRINUSE rejects initialization. The boot process reports the failed fiber instead of degrading silently.

Related Terms

WebRoute
WebRoute is one named route on the DSH plugin HTTP carrier, combining an exact or prefix match kind, an absolute path and a handler that owns the full response lifecycle.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web-server
WebRouteKind
WebRouteKind decides how a path matches: exact matches the pathname verbatim while prefix matches that prefix and any subpath. Matching order is always exact, then longest prefix, then fallback.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web-server
fallback seat
The fallback seat is the single fallback handler on the DSH plugin HTTP carrier, answering every request no named route matches. It has exactly one owner and a second registration throws.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web-server
IndexInjection
IndexInjection is a structured index.html injection row collected through one webserver/index-inject emit, fresh on every call so subscribers read live state.— https://deepseek-harness.github.io/deepseek-harness/reference/subsystems/web-server

Sources