Replies: 1 comment
|
对照当前 main HEAD 现状核验(packages/host/webserver/src/index.ts)
intercept 签名核验(packages/client/connection/src/rpc.ts)
api/gate 事件核验 connection 包 rpc.ts / index.ts 全文无 对提案的补充意见(按设计点顺序)
方向正确且是纯增量(不改 connection、不改路由表语义),值得转 PR。 |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Problem
Third-party plugins currently cannot apply any request-level policy on the shared
/apichannel. Three paths, all closed:ctx.webServer.register({ kind: 'prefix', path: '/api', ... })— duplicate(kind, path)registration throws, andclient-connectionalready owns the/apiprefix.ctx.connection.rpc.intercept('/api', matches, handler)— the handler receives(endpoint, payload, signal)only: no headers, no cookies, no socket facts, so no plugin can even answer "who is calling". The interceptor slot is also exclusive (a second registration throws), and the WebSocket upgrade path (/api/eventsdownlinks) bypasses interceptors entirely.Concrete impact (real incident, observed on
dsh-host-webserver/dsh-client-connection0.1.0-rc.7): a community plugin ships a scan-to-pair remote-control feature and documents a pairing gate ("unpaired devices get 403 on every/apicall"). The intended seam — anapi/gatewaterfall event the plugin listens for — is never emitted by any published SDK version, so the gate is dead code. On a deployment that trusts a public tunnel host (--trusted-host <tunnel-domain>), an unpaired browser from any network gets a full 200 through/api: session listing, prompts, the whole desktop surface. The Host fence being "not an auth layer" is documented and correct — but today there is nowhere for a plugin to put an auth layer.The underlying need is not one more event: it is the ability for a plugin to observe request-level facts (cookies, headers, socket address) before the RPC bridge, composable across plugins.
Proposal
Add a connect-style middleware chain to
dsh-host-webserver, dispatched before the exact/prefix route tables:This generalizes the registration-order tap chain the webserver already runs for
index.htmlbodies — applied to requests.Why a middleware layer
IncomingMessage: cookies, Host, Origin, socket address all visible/api, WebSocket upgrades, static pages — all pass the chain before route lookupDesign points to settle
next()use('upgrade', fn)variantuse('/api', fn)prefix bindingConsumer example (pairing fence)
Keeping both the Host check and the socket check in loopback classification means a LAN client forging
Host: localhoststill fails (its socket is not loopback) — a fact only the native request object carries.Sketch of the SDK-side change
In
dsh-host-webserverdispatch, before exact/prefix lookup:Registration mirrors
register()/registerFallback()with append-only list semantics (dispose removes on effect teardown).Alternatives considered
api/gatewaterfall event fromclient-connectionper/apirequest with theIncomingMessage: workable, but channel-specific, single-consumer-shaped, and it still misses upgrades and non-/apiroutes.Requestobject torpc.intercepthandlers: keeps the exclusive-slot problem (the slot would need to become a chain first) and still no upgrade coverage; at that point a middleware layer is the simpler, more general primitive.Happy to turn this into a PR if the direction sounds right.
All reactions