feat(apiproxy): settings/credentials/llm wire domains, frames, and write guard

Eight compiler-locked methods: settings.describe/update/replace serve
redacted layered namespace views (secrets structurally absent from every
layer, write-only in the update direction) and fold seam refusals into
settings-rejected; credentials.describe/set/unset expose value-free views
with credential-rejected on shadowed writes; llm.providers merges the
configurable directory with live routes and llm.models claims the
host-scoped catalog reservation through the buildModelCatalog extraction
session.models now shares. Three HostFrame invalidations bridge the seam
events (host/settings-changed, host/credentials-changed,
host/models-changed), and the connection route generalizes the native-
dialog check into a privileged-method set covering all four writes. The
fixture and both fake clients grow the same face.
This commit is contained in:
Yichen Jiang
2026-07-30 00:13:12 +08:00
parent a5c8136cb3
commit 191067559e
30 changed files with 1349 additions and 102 deletions
+18 -2
View File
@@ -15,6 +15,22 @@ export const name = 'client-connection'
/** Services required before mounting the route. */
export const inject = ['httpServer', 'apiProxy']
/**
* Methods gated on the trusted same-origin loopback check. Native dialogs act
* on the host machine; settings and credential writes mutate the user's
* configuration and secret store. Under `--host 0.0.0.0` every other method
* is reachable LAN-wide, but these stay browser-same-origin-on-loopback until
* a real authentication layer exists.
*/
const PRIVILEGED_METHODS = new Set([
'host.pickDirectory',
'host.openPath',
'settings.update',
'settings.replace',
'credentials.set',
'credentials.unset',
])
/**
* Mounts the API gateway under the browser transport prefix.
* @param ctx - Host plugin context.
@@ -26,8 +42,8 @@ export function apply(ctx: Context): void {
path: API_PATH,
handler: async (req, res) => {
const pathname = new URL(req.url ?? '/', 'http://dsh.internal').pathname
if ((pathname === `${API_PATH}/host.pickDirectory`
|| pathname === `${API_PATH}/host.openPath`)
if (pathname.startsWith(`${API_PATH}/`)
&& PRIVILEGED_METHODS.has(pathname.slice(API_PATH.length + 1))
&& !isTrustedNativeDialogRequest(req)) {
res.writeHead(403)
res.end('forbidden')