Password + TOTP two-factor authentication gateway for the dsh web UI: every HTTP request and WebSocket upgrade is refused until login, with per-source lockout, global rate limits and one-time backup codes.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:xbzbing/dsh-auth-gateway
Any plugin you install runs third-party code with your own permissions — it can read your files, use your credentials, and reach the network, and tool approvals don’t sandbox it. GitHub-sourced plugins also run build scripts at install time. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
A Cordis plugin that puts an authentication gate in front of the DeepSeek Harness Web UI: password auth + TOTP two-factor authentication + layered brute-force protection + session management, with real interception of every request (HTTP and WebSocket) at the gateway layer — unauthenticated traffic never reaches the backend.
dsh web ships with no authentication layer (its built-in trust fence is a reachability policy, not auth). This plugin fills that gap as an in-process gateway: the gateway exclusively owns the external port, the bundle patch pins the internal webserver to the loopback address, and the gateway is the only way in.
Features
- Password auth: on first deployment an initial password is auto-generated and printed to the console (one-time credential); after login you are guided to set a personal password (scrypt-hashed), and every subsequent visit requires login;
- Two-factor authentication (TOTP): optional; works with Google Authenticator, Authy, 1Password and other mainstream authenticators; ships with one-time backup codes (scrypt-hashed, single-use) for recovery when a device is lost;
- Real request interception: unauthenticated
/api/*returns 401, page paths 302 to the login page, WebSocket upgrades are rejected outright; authenticated traffic is forwarded transparently (Host/Origin normalization, compatible with the internal trust fence); - Layered brute-force protection: per-source lockout on password failures (default 5 failures / 5 min) + global rate limit (default 60 attempts/min) + per-source OTP/backup-code limit (default 10/min); scrypt runs asynchronously on the libuv thread pool, so login floods never block the event loop;
- Session management: in-memory 256-bit tokens (30 days), HttpOnly + SameSite=Strict cookies; changing the password or disabling OTP revokes all sessions;
- Security events: lockouts and exhausted rate-limit windows log warnings and broadcast a
dsh-auth-gateway/brute-forceCordis event (JSON payload) for monitoring and automation; - Bilingual (zh/en): the settings panel follows the dsh UI language (Settings → Language); the login / onboarding / OTP pages render in your preferred language (
locale.preferencein$DSH_HOME/settings.yaml), falling back to the browser language (Accept-Language) when no preference was set — a change applies on the next page load; the first-run console notice prints both languages; - Compliant shape: a host-only plugin (zero build, zero runtime dependencies) plus an optional client half (settings panel, source-built), all through official dsh extension points (
ctx.effect,webServer.tapIndex,ctx.slots).
Installation
Published on npm (dsh-auth-gateway); can also be installed from GitHub or a local directory:
# Option 1: npm (published registry package)
dsh plugin --profile web add dsh-auth-gateway
# Option 2: GitHub repository (default branch; pin a ref with #main, #v0.2.0, ...)
dsh plugin --profile web add github:xbzbing/dsh-auth-gateway
# Option 3: local dev directory (file: is an install-time snapshot — re-run
# `dsh plugin --profile web remove dsh-auth-gateway` + `add` after editing the source)
dsh plugin --profile web add file:/path/to/dsh-auth-gateway
dsh web --dump-config # verify webserver is 127.0.0.1:<internal port> and the dsh-auth-gateway row exists
dsh web # open http://<host>:<external port>
The package ships a dsh.bundle patch (loopback webserver + plugin row) — no hand-written composition required.
How it works
Browser ──> dsh-auth-gateway gateway (external port, inside the dsh process)
│ every request passes the auth check first (O(1) session table)
├─ unauthenticated ─> /api/*: 401 | pages: 302 /login | WS: rejected
├─ 2FA not passed ─> /otp/verify
└─ authenticated ─> forward (Host/Origin rewritten to loopback) ──> dsh webserver (127.0.0.1:internal port)
- The gateway's lifecycle is bound to dsh: it starts/stops with dsh, no separate process;
- The bundle patch moves the webserver to a loopback port (external =
--port, internal = external + 1), so remote clients cannot bypass the gateway and reach the backend directly; - Auth state machine:
first deploy → initial-password login → onboarding (set a personal password) → login → (optional) OTP verification → session; sessions that have not finished onboarding or 2FA can only reach their verification endpoints.
Screenshots
Quick start
- Start
dsh web: on first deployment an initial password is auto-generated and printed to the console (a prominent notice block); copy it and keep it safe; - Open the Web UI and log in with the initial password — you land on the onboarding page: set your own access password (at least 8 characters, mixed case or a special character; mandatory — all functionality stays locked until it is set). The initial password is one-time and invalidated once set;
- After login you can visit
/otp/setupto enable TOTP (scan the QR code or enter the secret manually, confirm with a verification code; backup codes are generated at the same time — store them safely); - Once OTP is enabled, login requires password + verification code (or a backup code);
- Change password: visit
/login(shows the change form when logged in), or use the "Auth Settings" panel.
Configuration
The fields below are the config of the dsh-auth-gateway row in the bundle patch / profile patch (validated by Standard Schema):
| Field | Default | Meaning |
|---|---|---|
listenHost / listenPort |
0.0.0.0 / 3080 |
Gateway external listen address and port |
upstreamHost / upstreamPort |
127.0.0.1 / 3081 |
Internal webserver address and port |
minPasswordLength |
8 |
Minimum password length (4–128) |
requireMixedCase / requireSpecial |
true / true |
Password complexity: mixed case OR special character |
maxLoginFailures / lockMinutes |
5 / 5 |
Password-failure lockout threshold and duration |
maxGlobalAuthAttemptsPerMinute |
60 |
Global auth-attempt rate cap |
maxOtpAttemptsPerMinute |
10 |
Per-source OTP/backup-code verification rate cap |
otpEnabled (deprecated) |
false |
No longer a switch — 2FA is bound and activated by the user from the Auth Settings panel; kept only for config compatibility |
otpRequired |
false |
Require verification at every login once 2FA is active (no config needed) |
otpIssuer / otpPeriod / otpDigits / otpWindow |
dsh-auth-gateway / 30 / 6 / 1 |
TOTP parameters (display name, period, digits, window) |
backupCodeCount / backupCodeLength |
10 / 8 |
Backup-code count and length |
Security model
Authentication-state changes (enable/disable OTP, change password) always require a fully verified session: disabling OTP while 2FA is active additionally requires the current password plus a verification code or an unused backup code; sessions that have not completed 2FA cannot reach sensitive endpoints. OTP verification is replay-protected (accepted time-steps are recorded) and spoof-resistant (x-forwarded-for never counts toward the source). The full threat model, known limitations and recovery paths are in docs/SECURITY.md (Chinese).
Uninstall & reset
dsh plugin --profile web remove dsh-auth-gateway # removes the composition; webserver reverts to default
~/.dsh/profiles/web/node_modules/.bin/dsh-auth-gateway-uninstall # removes credential data ($DSH_HOME/auth-gate/)
Forgot the password? Run dsh-auth-gateway-reset (removes only password.json; $DSH_HOME defaults to ~/.dsh), then restart dsh web — a fresh initial password is printed to the console and the onboarding flow lets you set a new one. The two bundled commands are linked into the profile's node_modules/.bin, which is not on PATH by default: use the full path or export PATH="$HOME/.dsh/profiles/web/node_modules/.bin:$PATH" first. See docs/DEPLOYMENT.md (Chinese) for details.
Documentation
| Doc | Content |
|---|---|
| docs/SECURITY.md | Threat model, OTP security design, known limitations, recovery paths |
| docs/DEPLOYMENT.md | Ports & listening, LAN deployment, HTTPS advice, troubleshooting |
| docs/TESTING.md | Unit tests, end-to-end (Playwright), API/WebSocket gate verification |
| docs/DEVELOPMENT.md | Architecture, build, development stats |
The detailed docs above are in Chinese.
Verification overview
- Unit & contract tests:
npm test(88 tests, incl. OTP security regressions, client contract, patch port derivation) - End-to-end against a real instance:
node scripts/e2e.mjs(Playwright; login/2FA/password-change flows) - Gate verification:
./scripts/verify.sh(curl; 401/302/WS rejection/lockout)
See docs/TESTING.md for details.
Model Experience
None — this package is an authentication carrier between the browser and the internal dsh webserver; it never enters any model request.
KV Cache effect
None — this package neither assembles nor sends provider requests.
License
MIT
Links
More in this category
strukto-ai/mirage#dsh★ 3495
Swaps the filesystem and bash providers for a mirage virtual workspace: file tools and shell commands run over mounted resources (RAM, S3, Redis, Slack, Gmail, Notion, Postgres) instead of the host disk, with per-mount read/write/exec modes, per-command sandbox routing (monty, pyodide, quickjs in process; docker, e2b, daytona remote), and installed CLIs (git, gh, slack, linear, ntn, gws, or one you register) as head words in the virtual terminal.
hust-open-atom-club/oh-dsh★ 237
Community distribution: TUI, desktop, and Web UI as one bundle with layered installation.
Jayden-X-L/forkprobe★ 66
Compare multiple skills on the same task and pick the winner.
vlln/plugin-registry★ 53
Ecosystem infrastructure: a thin browser console for managing official repository plugins (zero patches) plus a make-dsh-plugin skill for guided plugin development.
forrestchang/dsh-multica-runtime★ 45
Run the dsh runtime on Multica.
omdsh-dev/dsh-plugin-check★ 24
Plugin health checks: manifest protocol / patch format / build traps, zero-dependency and read-only.