⭐ If you like this plugin, please star it on GitHub — it shows me that the plugin is useful to you and motivates me to keep developing it.
🐛 If you find a bug or would like to request a feature, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version.
⚡ Why DSH Fails Over Local Network (LAN)
By default, modern web browsers and the DeepSeek Harness frontend deliberately restrict access when opened from non-localhost IP addresses (e.g. 192.168.x.x or 10.x.x.x) over plain HTTP:
🔒 Locked Settings & Models Tabs: The Web UI evaluates the hostname via isLoopbackHostname. If accessed over LAN, the settings service falls back to in-memory mode: all plugin configuration cards render empty, section states become , mutations are discarded before transmission, and the page displays .
"unavailable"
Models
"settings are unavailable in this browser"
💥 Fatal UUID Generation Crash: crypto.randomUUID() only exists in browser Secure Contexts (HTTPS or localhost). On plain HTTP across LAN, file uploads, tool calls, and session initializations crash instantly.
📋 Broken Clipboard Copying: navigator.clipboard is completely disabled by browsers on non-secure origins, breaking all code snippet "Copy" buttons.
🎙️ Microphone & Voice Input Blockade: Browser security engines block navigator.mediaDevices.getUserMedia on plain HTTP, making voice input via dsh-voice impossible on remote mobile phones and tablets.
🛡️ Loopback-Only Core API Fencing: Core DSH methods (/api/settings.*, /api/credentials.*, /api/models.*) strictly reject requests not originating from loopback 127.0.0.1.
dsh-lanmode completely resolves all these limitations through non-invasive webServer.tapIndex HTML shims, a smart direct bridge, mDNS, Root CA generation, and an interactive settings card.
Registers tool /mobileqr: generates a clean SVG QR code with the active LAN URL and session token (https://dsh.local:3088/?token=...). Point your phone camera at the screen to connect immediately.
QR codes are also accessible in the Settings card and on /dsh-lanmode/health.
2. 📲 PWA & Mobile Standalone Mode
Route /dsh-lanmode/manifest.json and meta tags viewport-fit=cover, apple-mobile-web-app-capable, theme-color.
Adding DSH to your Home Screen on iOS/Android launches it as a standalone app without browser URL bars and with notch-aware safe areas.
3. 🌐 Automatic mDNS (dsh.local)
Built-in lightweight UDP 5353 responder: announces dsh.local across your local network. No need to memorize shifting IP addresses.
4. 🔐 Local Root CA for Permanent Trusted HTTPS
Generates a two-tier certificate structure: dsh-lanmode Local Root CA (10-year validity) $\rightarrow$ Server Certificate (with SAN for dsh.local, LAN IPs, and localhost).
Download GET /dsh-lanmode/ca.crt: install the profile once on your iPhone, iPad, or Android to enjoy persistent trusted HTTPS. Voice input via dsh-voice works flawlessly.
5. 🔔 Background Web Notifications (turn/end)
Hooks into turn/end and approval/asked session events.
When the tab or phone is inactive (document.hidden), dispatches a native push notification. Tapping the notification immediately refocuses the chat window.
6. 🎨 Settings Card in «Settings → Plugins» (lib/client.js)
Interactive plugin card following DSH design guidelines:
Connection status & active mode;
One-click LAN URL copying;
In-card QR code toggle;
One-click background notification toggle;
Download Root CA link (ca.crt).
7. 🛡️ Access Control & Optional LAN PIN
unlockPrivileged: Master gate for settings & credentials mutation from LAN.
lanPin: Optional PIN code (disabled by default). When set, LAN guests can chat freely, but changing system settings or API keys requires PIN authentication.
CIDR Subnet Filtering: Restrict access to trusted subnets (allow: ["192.168.77.0/24"]).
Administrative Endpoints Protection (v0.7.18+): Internal plugin routes (/dsh-lanmode/devices, /dsh-lanmode/devices/revoke, /dsh-lanmode/devices/kill-all, /dsh-lanmode/tunnel/toggle) feature built-in fail-closed defense-in-depth authorization. Bypassing the local bridge or accessing from untrusted networks requires valid admin credentials or trusted loopback origins.
Guest Role Quarantine: Subnets designated under guestAllow are strictly prohibited from mutating system settings, revoking sessions, or toggling WAN tunnels (403 Forbidden).
CSRF Mitigation: Mutating POST requests reject cross-site invocations (Sec-Fetch-Site: cross-site) and validate origin headers.
📦 Quick Installation
dsh plugin --profile web add @goodandready/dsh-lanmode
⚙️ Configuration Reference (settings.yaml)
dsh-lanmode:
mode: direct # 'direct', 'proxy', or 'auto'
directHost: 0.0.0.0
directPort: 3088
mdns: true # Announce dsh.local in LAN
pwa: true # PWA manifest, splash screen & mobile viewport
mobileEnterSends: false # When false (default), Enter adds newline on mobile touch
tls: self-signed # 'self-signed' (with Root CA), 'files', or 'off'
unlockPrivileged: true # Permit settings & credentials from LAN
lanPinRef: "" # Credential reference name or ENV var for LAN PIN
lanPin: "" # (Deprecated) Plain PIN string for backwards compatibility
tunnel: off # Cloudflare WAN tunnel: 'off', 'quick', or 'named'
tunnelTokenRef: "" # Credential reference name or ENV var for Cloudflare tunnel token
tunnelToken: "" # (Deprecated) Plain tunnel token string for backwards compatibility
tunnelPin: true # Require PIN for requests from WAN
allow:
- 192.168.0.0/16
- 10.0.0.0/8
🚀 What is New in 0.7.15 (Issue #123 Evolution)
📱 Connected Devices & Session Management: Live client presence tracking, device OS/browser discovery, per-device token revocation, and emergency "Revoke All Others" kill switch.
🍏 1-Click Apple Configuration Profile: Native .mobileconfig payload for Safari on iOS, iPadOS, and macOS to trust DeepSeek Harness Local Root CA in one tap.
🛡️ Subnet Role Separation (Admin vs Guest): Distinct adminAllow and guestAllow CIDR rules. Guests can chat and interact with agents while access to harness configuration, plugins, and settings is protected with 403 Forbidden.
🌐 Multi-Interface & Mesh Detection: Automatic identification of local LAN, Tailscale (100.x.y.z), WireGuard, and VPN adapters with quick-select UI pills.
⚡ Live Network Telemetry: Compact real-time telemetry widget displaying RTT ping latency, active concurrent connections, and streaming data volume.
Cloudflare WAN Tunnels: Built-in zero-config Quick Tunnels and Named Tunnels, public URL auto-parsing, dynamic start/stop toggle, mandatory WAN PIN protection.
In direct bridge mode, upstream connections to DeepSeek Harness are segregated into two independent pools:
Standard HTTP Pool: Keep-alive enabled with up to 100 reusable sockets for rapid loading of WebUI assets, static scripts, and REST endpoints. Protected by a queue timeout (15s default) returning HTTP 503 rather than stalling indefinitely if saturated.
Dedicated Streaming Pool: Independent unpooled socket handling for long-lived Server-Sent Events (SSE), token streaming (/api/chat/stream), and live notifications. 100+ concurrent streaming clients can run without exhausting or starving the WebUI static and API traffic.
In-App One-Click Plugin Updates (v0.7.19+)
The plugin provides a built-in one-click updater service and settings card UI (/api/dsh-lanmode/update):
Version Awareness: Real-time display of the currently installed version and availability of new releases from the npm registry.
Security Perimeter: Checks loopback origin or admin session credentials, origin/host match, anti-CSRF headers, and the mandatory x-dsh-plugin-update: 1 verification header.
In-App Upgrades: Upgrade @goodandready/dsh-lanmode directly from the DSH settings card with zero terminal commands required.