dsh-auth-lock
English | 中文
Host-side password authentication for DeepSeek Harness Web deployments. It applies one authentication policy to the DSH API, SSE, WebSockets, and server routes registered by third-party plugins.
dsh-auth-lock is a dual-face DSH plugin: the Host entry owns password verification, Cookie sessions, and WebServer policy; the browser entry owns first-run setup, login, password changes, automatic locking, and responsive presentation.
[!IMPORTANT] This plugin requires the DSH WebServer Cordis waterfalls
webserver/requestandwebserver/upgrade. On an older DSH build, Auth Lock stays inactive and registers no authentication policy, while DSH continues to start normally. The page and Host log explicitly state that no authentication is active and show the uninstall commanddsh plugin --profile web remove dsh-auth-lock.
Features
- First-run setup is loopback-only by default, preventing remote takeover of a new instance.
- 4–16 character passwords with no complexity rule; numeric PINs are accepted.
- Node
scryptpassword derivation withN=32768,r=8,p=1, and a random 128-bit salt. - Constant-time verifier comparison through
timingSafeEqual; plaintext passwords are never stored. - Random 256-bit session tokens; Host memory retains only SHA-256 token keys.
- HttpOnly, SameSite=Strict cookies;
Secureis automatic on TLS sockets. - Ten-minute default idle expiration, configurable from 1 to 1440 minutes.
- A 24-hour absolute session lifetime.
- Eight failed password attempts per remote address in a 15-minute window; expensive derivations for one address run serially.
- Same-origin
Originchecks and a 16 KiB request-body limit. - Current-password verification for password changes, followed by revocation of all old sessions.
- A DSH Profile administrator command for forgotten-password reset.
- Responsive desktop, tablet, and phone login UI.
- Automatic reload or return to the original page after login so API, SSE, and WebSocket clients reconnect with the new cookie.
- No dependency on mobile, remote-control, pairing, or other business plugins; policy applies generically at the WebServer layer.
Security model
Browser / Mobile / Third-party route
│
▼
webserver/request / upgrade
│
dsh-auth-lock policy
│ │
unauthenticated authenticated
401/302 │
▼
DSH / plugin route handler
Auth Lock depends only on the generic DSH WebServer policy extension point. It does not import or configure another UI, mobile, remote-control, or pairing plugin. HTTP, SSE, and upgrade routes independently registered on the same WebServer receive the same authentication policy automatically.
Compatibility
- Node.js:
^22.19.0 || >=24.0.0 - DSH: authentication requires a build containing the WebServer policy waterfalls; older builds still start, but the plugin stays inactive and recommends uninstalling
- Browser: modern JavaScript, cookies, and CSS
env(safe-area-inset-*) - Host platforms: macOS, Linux, and other Node hosts supported by DSH
DeepSeek Harness is evolving rapidly and may introduce compatibility-breaking extension-point changes. Pin a Git commit or use an explicit release for long-lived deployments.
Installation
GitHub
dsh plugin --profile web add github:imchenmin/dsh-auth-lock
Pin a commit for production or long-lived deployments:
dsh plugin --profile web add github:imchenmin/dsh-auth-lock#COMMIT_SHA
The repository contains prebuilt lib/ files, so GitHub installation does not run prepare and does not require pnpm allowBuilds configuration.
npm
After the package is published to npm:
dsh plugin --profile web add dsh-auth-lock
Local checkout
dsh plugin --profile web add /absolute/path/to/dsh-auth-lock
Tarball
npm pack
dsh plugin --profile web add ./dsh-auth-lock-0.1.0.tgz
Verify and start
dsh --profile web --dump-config
dsh web
Confirm that the composition contains:
- id: auth-lock
name: dsh-auth-lock
If the page reports an incompatible DSH version, continue using DSH without Auth Lock protection and uninstall the plugin:
dsh plugin --profile web remove dsh-auth-lock
Restart dsh web. Do not interpret the incompatible state as active password protection.
On a compatible build, restart dsh web, open http://127.0.0.1:<port> on the computer running DSH, and complete first-run setup locally. Use phone, tunnel, or reverse-proxy addresses only after setup succeeds.
Usage
Login and lock
- First visit: create a 4–16 character password.
- Later visits: enter the password to log in.
- The DSH Settings panel contains a standalone Access Lock section for timeout and password management.
- The right-edge control rail keeps only the immediate lock action.
- After login the page reloads automatically; if authentication started from another protected HTML page, the browser returns to that page.
Change the password
Open Settings → Access Lock and enter:
- the current password;
- the new password;
- the new password again;
- Save.
The Host creates a new salt and verifier, revokes all old sessions, and issues a fresh cookie to the current browser.
Forgotten password
The verifier is irreversible, so the original password cannot be recovered. Reset Auth Lock on the computer running DSH.
Stop dsh web, then run:
dsh plugin --profile web exec dsh-auth-lock-reset -- --yes
Restart DSH:
dsh web
The next visit starts first-run password setup again.
For a custom state path:
dsh plugin --profile web exec dsh-auth-lock-reset -- \
--path /absolute/path/auth-lock.json \
--yes
The command deletes only the Auth Lock state file. It does not remove the DSH Profile, sessions, credentials, projects, or other plugin data. It refuses to run without --yes.
Configuration
The bundle inserts this Cordis row:
- id: auth-lock
name: dsh-auth-lock
config:
language: zh
Override it in the Profile cordis.patch.yml:
- id: auth-lock
config:
# Auth Lock UI language: zh (default) or en
language: en
# Custom Host state file
path: /absolute/path/auth-lock.json
# Force Secure cookies when TLS terminates at a reverse proxy
secureCookie: true
language supports exactly zh and en; the default is zh (Chinese). The value above is the first-run default. After creating the password, choose 中文 or English directly in Settings → Access Lock and save. Auth Lock persists the choice in its state file and reloads the browser so the login screen, lock guidance, quick-lock label, navigation label, and Settings section all switch together.
The default state file is:
$DSH_HOME/auth-lock.json
Without DSH_HOME, the default is:
~/.dsh/auth-lock.json
First-run setup accepts loopback requests only. A deployment that cannot use a Host-local browser may temporarily enable remote setup for one bootstrap run:
DSH_AUTH_LOCK_ALLOW_REMOTE_SETUP=1 dsh web
Stop that process immediately after setup and restart without the variable. Do not leave remote setup enabled in a long-running deployment.
Host API
| Method | Path | Purpose |
|---|---|---|
GET |
/auth-lock/status |
Read compatibility, setup, login, and idle-timeout status |
POST |
/auth-lock/setup |
Create the first password |
POST |
/auth-lock/login |
Log in with the password |
POST |
/auth-lock/logout |
Revoke the current session |
POST |
/auth-lock/password |
Change the password after verifying the current password |
POST |
/auth-lock/settings |
Change the idle timeout |
All state-changing requests require a same-origin Origin header. JSON API responses use Cache-Control: no-store.
Public boot resources
The following resources remain public so an unauthenticated browser can render the login UI:
- SPA root
/ /assets/*/plugins/<plugin-id>/client.js/plugins/<plugin-id>/client.js.map- favicon and manifest
/auth-lock/*authentication routes
Other unauthenticated HTML pages redirect to the root login page with a safe same-origin return path. API requests receive 401 and HTTP upgrades receive 401. Dynamic plugin endpoints such as /plugins/events are not public.
Auth Lock protects DSH data and server-side operations, but it does not conceal public JavaScript and CSS resources. Deployments that require static-resource confidentiality need an authenticated HTTPS reverse proxy in front of DSH.
Data and sessions
The state file stores only:
{
"version": 1,
"salt": "...",
"verifier": "...",
"idleMinutes": 10
}
The parent directory is created with mode 0700; the file is created with mode 0600 and replaced through a randomly named temporary file.
Login sessions exist only in the current DSH process:
- restarting
dsh weblogs out every device; - changing the password invalidates every old session;
- the current release does not support clustering or shared session storage.
Security boundaries
- Auth Lock does not encrypt project files, conversations, credentials, or network traffic.
- Remote HTTP access exposes passwords and cookies. Use HTTPS for LAN, tunnel, or public deployments.
- Set
secureCookie: truewhen TLS terminates at a reverse proxy. - A four-character PIN has a small search space. Although
scryptand failure limiting apply, use a longer password on untrusted networks. - Failure-limit state is process-local and resets on restart.
- Static application and plugin resources are intentionally public to render the login UI.
- This is a single-user plugin. It does not provide usernames, roles, project ACLs, OAuth, OIDC, or clustered sessions.
See SECURITY.md for vulnerability reporting. Do not publish exploitable details in a public Issue.
Uninstall
dsh plugin --profile web remove dsh-auth-lock
Optionally remove the state file after uninstalling:
rm "${DSH_HOME:-$HOME/.dsh}/auth-lock.json"
Development
git clone https://github.com/imchenmin/dsh-auth-lock.git
cd dsh-auth-lock
npm test
npm run check
Inspect the release payload before publishing:
npm pack --dry-run
The project ships prebuilt native ESM Host and Client bundles and has no runtime dependencies.
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md for development and test requirements.
No comments yet. Be the first to write one.