# Deployment Guide > [简体中文](../zh/DEPLOYMENT.md) | English ## Ports and listening addresses ### Port: use `dsh web --port ` directly The bundle patch follows `ctx.webStartup` (the same mechanism as web-app's own webserver row); the port is controlled by the CLI flag: ```bash dsh web --port 8080 # external URL 8080, internal webserver auto-moves to 8081 dsh web # default: external 3080, internal 3081 ``` Derivation rule: **external port = `--port` (default 3080), internal port = external + 1**. Port range 1–65534 (the internal port must stay valid after +1, otherwise config validation fails at startup). > `--port 0` (OS-assigned) is not supported: the gateway needs a fixed internal port to forward to. ### Why `--host 0.0.0.0` cannot be used `dsh web --host 0.0.0.0` is rejected outright by dsh (web-app built-in security restriction, hardcoded in `startup.ts`). This is a dsh code-level limit that configuration cannot lift — and **does not need to be lifted**: - The internal webserver must listen only on `127.0.0.1` (security-critical: an internal port exposed to the outside bypasses the auth gateway); - External listening is handled by the plugin's `listenHost` (default `0.0.0.0`), which does not pass through web-startup validation. The correct way to expose externally: **do not pass `--host`; use the default**. ### Local-only access Set the `listenHost` of the `dsh-auth-gateway` row to `127.0.0.1` (local machine only). ### Fixed ports (without `--port`) Override both the `webserver` and `dsh-auth-gateway` rows in the profile's own `cordis.patch.yml` (the user patch layer takes priority over the bundle layer); note that `webserver.port` must equal `dsh-auth-gateway.config.upstreamPort`. Once overridden manually, the `--port` flag no longer takes effect. ## Network and security recommendations - **LAN deployment**: keep it on a trusted network; the gateway listens on all interfaces by default — configure a firewall before exposing it across networks; - **HTTPS**: the plugin currently serves plain HTTP (the `Secure` cookie flag is not enabled). For production, put a TLS reverse proxy (nginx/Caddy etc.) in front of the gateway port and configure `trustedHosts` (see below); the `Secure` cookie flag can be added in a later version once TLS is enabled; - **trustedHosts**: when accessed via a reverse proxy / custom domain, configure `trustedHosts` on the `dsh-client-connection` row (the authorization authority of the internal fence; this plugin rewrites Host/Origin to loopback on forwarded requests, so normally no configuration is needed — follow the dsh docs for special topologies); - **Backups**: credentials live in `$DSH_HOME/auth-gate/` (password.json, otp.json, otp-master.key); encrypt them when backing up (the OTP secret is already AES-256-GCM encrypted, but the master key `otp-master.key` needs equal protection — see SECURITY.md); - **OTP master key management**: an `auth-gate/otp-master.key` is auto-generated by default — key and ciphertext live in the same directory (local trust model). To isolate disk disclosure, set the `DSH_AUTH_GATEWAY_MASTER_KEY` environment variable before deployment (32 bytes, hex or base64 encoded) and store it on an encrypted volume or in external key management (KMS / Docker secret / systemd credentials etc.); once the environment variable is set, the key file is neither generated nor read. Generate one with: `node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"`. See the "OTP 密钥加密" section of [SECURITY.md](SECURITY.md) for details. ## Deployment behind nginx / a reverse proxy The gateway can serve directly or sit behind nginx (or any reverse proxy). Full topologies with complete config examples (bare-metal direct connection, subdomain deployment, sub-path deployment, Docker nginx container) are in: - [NGINX-DEPLOYMENT.md](NGINX-DEPLOYMENT.md)(简体中文)| [简体中文](../zh/NGINX-DEPLOYMENT.md) (English) Quick summary: - **Subdomain deployment (recommended)**: root-path deployment on `dsh.example.com`, nginx reverse-proxies 443 to the gateway port — zero conflicts, zero maintenance; - **Sub-path deployment**: configure `basePath: /dsh` on the gateway (override it in the deployer's profile patch — note that `config:` is a whole-object replacement, so all bundle-patch fields must be kept); nginx must additionally proxy the root-path resources dsh references (`/assets/`, `/api/`, `/plugins/`, etc.); - Behind a reverse proxy you must forward the `Upgrade` / `Connection` headers (WebSocket) and raise `proxy_read_timeout` / `proxy_send_timeout` (SSE long connections), otherwise event streams get cut off at 60s. ## Troubleshooting | Symptom | Cause & fix | |---|---| | Page shows "加载提供方目录失败: crypto.randomUUID is not a function" | Browser Web Crypto restriction in a non-secure context (HTTP + non-localhost); the plugin injects a polyfill — make sure the latest version is installed and dsh web was restarted | | Many `/api/*` 403 after login | The internal fence rejects external Host — confirm the bundle patch is in effect (webserver should be `127.0.0.1:`) and the gateway rewrites Host/Origin | | Cannot reach `http://:` | Check whether the gateway listens on `0.0.0.0` (`listenHost` config) and the firewall rules | | Login rejected with 429 | Global rate limit or OTP throttle triggered — wait for the window to reset (1 minute / 5-minute lockout) | | `--port 65535` fails to start | Internal port 65536 is invalid — port range is 1–65534 | | Duplicate installation (two dsh-auth-gateway rows in the composition tree) | The second gateway must fail with EADDRINUSE on the same port — remove the duplicate row | ## Credential reset and uninstall commands The plugin package ships two commands (`bin` in `package.json`): `dsh-auth-gateway-reset` (deletes only the password record) and `dsh-auth-gateway-uninstall` (deletes the whole `$DSH_HOME/auth-gate/`). `dsh plugin add` links the commands into the profile's `node_modules/.bin` via pnpm — **that directory is not on PATH by default**, so typing the short name gives "command not found". Two ways to run them: ```bash # Option 1: full path (replace the profile name with yours) ~/.dsh/profiles/web/node_modules/.bin/dsh-auth-gateway-reset # Option 2: add the profile bin to PATH (append to ~/.zshrc / ~/.bashrc and reopen the terminal) export PATH="$HOME/.dsh/profiles/web/node_modules/.bin:$PATH" dsh-auth-gateway-reset ``` > **Where is `$DSH_HOME`**: credentials live in `$DSH_HOME/auth-gate/` (password.json, otp.json, otp-master.key). `$DSH_HOME` defaults to `~/.dsh` (i.e. `$HOME/.dsh`) and can be overridden with an environment variable — dsh and the plugin read the same value. After `dsh-auth-gateway-reset` deletes the password record, you **must restart dsh web**: the initial password is generated and printed to the console at startup (the plugin has no "set password" page); log in and go through onboarding to set a personal password. If the authenticator was lost, also delete `$DSH_HOME/auth-gate/otp.json` (or just run `dsh-auth-gateway-uninstall`). > **Order reminder**: if you also want to remove the plugin, **run the credential command above first, then `dsh plugin --profile web remove dsh-auth-gateway`** — remove deletes the plugin dependency from the profile, which breaks the bin links of both commands (dangling references to deleted targets). ## Upgrades After `dsh plugin --profile web add file:...` you must **remove + add** to refresh the pnpm snapshot (a `file:` dependency is snapshotted at install time), then restart dsh web. The plugin ships its own `dsh-auth-gateway-reset` / `dsh-auth-gateway-uninstall` commands (no need to run them when upgrading).