{"_id":"@crckss/dsh-remote-trust","_rev":"2-cca0c45d0f2ddcf4330dd744c3d15ee3","name":"@crckss/dsh-remote-trust","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.0":{"name":"@crckss/dsh-remote-trust","version":"0.1.0","keywords":["dsh-plugin","deepseek-harness","cordis","remote","trusted-host"],"license":"MIT","_id":"@crckss/dsh-remote-trust@0.1.0","maintainers":[{"name":"crckss","email":"crckss@gmail.com"}],"dist":{"shasum":"66012a0998d56d5d8840ad1d95a3087d75ba3aea","tarball":"https://registry.npmjs.org/@crckss/dsh-remote-trust/-/dsh-remote-trust-0.1.0.tgz","fileCount":4,"integrity":"sha512-vJnDiNekNyzgXuI0LQPbWBkqZ3h1lN8YmGn26OEc28mRnse79BXq1XdCeb+QwNeezMcGb0HgwUkdkwvK+XZc7A==","signatures":[{"sig":"MEYCIQDrE7eDVrh4u4eSMFIbFu6fN+mAvOnK1x/sbZgh9ZNKQgIhAM9OWL2r91BIEUp0JJ9xNwCE95ClWxUDmKv5pqOZEWXc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":14769},"main":"lib/index.js","type":"module","engines":{"node":">=22.19.0"},"_npmUser":{"name":"crckss","email":"crckss@gmail.com"},"_npmVersion":"11.8.0","description":"Open DSH privileged /api methods (settings, credentials, model discovery) to configured remote authorities","directories":{},"_nodeVersion":"25.6.0","dependencies":{"@deepseek-ai/dsh-host-apiproxy":"0.1.0"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/dsh-remote-trust_0.1.0_1786868137104_0.21906433965094574","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@crckss/dsh-remote-trust","version":"0.1.1","type":"module","main":"lib/index.js","description":"Open DSH privileged /api methods (settings, credentials, model discovery) to configured remote authorities","keywords":["dsh-plugin","deepseek-harness","cordis","remote","trusted-host"],"license":"MIT","engines":{"node":">=22.19.0"},"dependencies":{"@deepseek-ai/dsh-host-apiproxy":"^0.1.0-rc.6"},"_id":"@crckss/dsh-remote-trust@0.1.1","_nodeVersion":"25.6.0","_npmVersion":"11.8.0","dist":{"integrity":"sha512-fMP8X61Ezi732h0kTqU0lxcF/+CecmUW7GDaD6khpNXlGxgHxcrsQhHp5xymxsaeglzT3RnMD31v0EQK7Lwyaw==","shasum":"c49c3a9755f87de0b196273b6676700b058f9380","tarball":"https://registry.npmjs.org/@crckss/dsh-remote-trust/-/dsh-remote-trust-0.1.1.tgz","fileCount":4,"unpackedSize":14775,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICNH+Nc5OQ4CAnrbyRLkUrJtsH0OS7YnAETqQ/DctablAiBHXi61Uz3etv7lIAXaIkcqZvR9D2k8YAG9APNvNbElCw=="}]},"_npmUser":{"name":"crckss","email":"crckss@gmail.com"},"directories":{},"maintainers":[{"name":"crckss","email":"crckss@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dsh-remote-trust_0.1.1_1786868472531_0.13971163654271135"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-16T08:15:36.995Z","modified":"2026-08-16T08:21:12.828Z","0.1.0":"2026-08-16T08:15:37.249Z","0.1.1":"2026-08-16T08:21:12.682Z"},"license":"MIT","keywords":["dsh-plugin","deepseek-harness","cordis","remote","trusted-host"],"description":"Open DSH privileged /api methods (settings, credentials, model discovery) to configured remote authorities","maintainers":[{"name":"crckss","email":"crckss@gmail.com"}],"readme":"# @crckss/dsh-remote-trust\n\nOpen DSH's privileged `/api` methods to remote trusted hosts, so a browser\nvisiting the Web UI through a reverse proxy (or from another machine on your\nnetwork) can configure the harness — **Settings → Models**, provider and\ncredential management — instead of getting `403 forbidden`.\n\n```yaml\n# ~/.dsh/profiles/web/cordis.patch.yml\n- insert:\n    - id: remote-trust\n      name: \"@crckss/dsh-remote-trust\"\n      config:\n        hosts:\n          - dsh.example.com\n```\n\n## Why this exists\n\nDeepSeek Harness binds its Web server to loopback for safety (`--host 0.0.0.0`\nis refused outright) and its `/api` browser-trust fence has two layers:\n\n1. A **general fence** — the request's `Host` must be loopback or a\n   `--trusted-host` authority, with Origin and `sec-fetch-site` checks.\n2. A **privileged-method pin** — `settings.*`, `credentials.*`,\n   `agentPreset.*`, and `llm.discoverModels` are additionally FORCED to\n   loopback with an empty trust list, even when `--trusted-host` names your\n   domain.\n\nThat pin is hardcoded in `@deepseek-ai/dsh-client-connection`, so a deployment\nbehind Traefik/Caddy/nginx can browse sessions but can never open the settings\nplane. This plugin removes the pin **for endpoints you declare**, without\ntouching node_modules.\n\n## How it works\n\nThe webserver's route table is checked **exact-first, then longest-prefix**.\nThe built-in `/api` route is a *prefix* route, so this plugin registers\n*exact* routes for each privileged endpoint (`/api/settings.describe`, …).\nThose exact routes win, run the same browser-trust fence (Host + Origin +\n`sec-fetch-site`) against loopback **plus** your configured hosts, and then\nforward the request to `apiProxy` exactly like the built-in bridge would.\n\nThe two host-native methods (`host.pickDirectory`, `host.openPath`) are **not**\nopened by default because they drive native dialogs on the server machine.\n\n## Install\n\n### Manually (local profile)\n\n```bash\n# from your DSH profile directory\ncd \"${DSH_HOME:-~/.dsh}/profiles/web\"   # or profiles/tui, …\n\n# pnpm must be on PATH (dsh plugin forwards to pnpm)\ndsh plugin --profile web add @crckss/dsh-remote-trust\n# or, from a local checkout:\ndsh plugin --profile web add /path/to/dsh-remote-trust\n```\n\n### From npm\n\n```bash\ndsh plugin --profile web add @crckss/dsh-remote-trust\n```\n\n\n## Configuration\n\n```yaml\n- insert:\n    - id: remote-trust\n      name: \"@crckss/dsh-remote-trust\"\n      config:\n        # Authorities allowed to call privileged methods.\n        # Port-less host matches any port; host:port matches exactly.\n        hosts:\n          - dsh.example.com\n          - 100.34.48.118\n        # Optional: open privileged methods beyond the defaults\n        # (these are host-native actions, opt-in).\n        extraEndpoints:\n          - host.pickDirectory\n        # Optional: request body ceiling for the forwarded bridge.\n        maxRequestBodyBytes: 4194304\n```\n\nTrusted authorities are **merged** from two places:\n\n- `config.hosts` — explicit entries in the patch layer;\n- `ctx.webStartup.trustedHosts` — whatever the invocation declared with\n  `--trusted-host`, plus LAN IP literals derived from the bind (when bound to\n  all interfaces).\n\nSo the plugin follows the deployment's existing trust declaration: if you\nalready run `dsh web --trusted-host dsh.example.com`, the plugin picks that up\neven with an empty `hosts` list.\n\n### Opened by default\n\n```\nagentPreset.read          agentPreset.copy\nagentPreset.openDocument  agentPreset.remove\nsettings.describe         settings.openDocument\nsettings.update           settings.replace\nsettings.mutate\ncredentials.describe      credentials.set\ncredentials.unset\nllm.discoverModels\n```\n\n`host.pickDirectory` and `host.openPath` stay loopback-only unless listed in\n`extraEndpoints`.\n\n## Security model\n\nThe plugin reuses the harness's own browser-trust logic — it does not add a\nlooser check:\n\n- `Host` must be loopback **or** a configured authority (DNS-rebinding gate);\n- `Origin`, when a browser sends one, must match the `Host`;\n- `sec-fetch-site: cross-site` is always refused.\n\nNothing becomes *unauthenticated*: the fence is not authentication, and it\nnever was. What changes is that the config plane stops being loopback-only and\nbecomes reachable from the authorities you already trust with\n`--trusted-host`.\n\n> **Do not** expose the DSH port (3080) or the whole machine through a public\n> proxy unless you add your own authentication. The fence protects against\n> rebinding and cross-site requests, not against a stranger who can reach the\n> port.\n\n## Verify\n\n```bash\nBODY='{\"type\":\"client-request\",\"rpcId\":\"v\",\"method\":\"settings.describe\",\"payload\":{}}'\n# remote host -> should be 200 (was 403 without the plugin)\ncurl -i -H 'Host: dsh.example.com' -H 'Content-Type: application/json' \\\n  -d \"$BODY\" http://127.0.0.1:3080/api/settings.describe\n# cross-site / attacker host -> must stay 403\ncurl -i -H 'Host: dsh.example.com' -H 'Origin: https://evil.example' \\\n  -H 'sec-fetch-site: cross-site' -H 'Content-Type: application/json' \\\n  -d \"$BODY\" http://127.0.0.1:3080/api/settings.describe\ncurl -i -H 'Host: evil.example' -H 'Content-Type: application/json' \\\n  -d \"$BODY\" http://127.0.0.1:3080/api/settings.describe\n```\n\n## Known limitations\n\n- **Tracks the harness release.** The pin lives in\n  `@deepseek-ai/dsh-client-connection`; this plugin pins\n  `@deepseek-ai/dsh-host-apiproxy` to a matching version so the forwarded\n  bridge stays compatible. After upgrading `dsh`, bump that dependency.\n- **It opens, it does not delete.** Deleting a privileged method still needs a\n  `cordis.yml` composition change; the settings seam's merge semantics apply.\n- **No auth added.** Add your own if the endpoint can be reached by people you\n  do not trust.\n\n## License\n\nMIT","readmeFilename":"README.md"}