dsh-relay

August 19, 2026 · View on GitHub

English | 简体中文

DeepSeek Harness plugin infrastructure: this Relay supports the DSH Mobile plugin submitted to the official DeepSeek Harness plugin repository and published in the official community plugin showcase.

Cloud Relay for the DSH mobile remote-control MVP.

Public production endpoint: https://relay.dshmobile.online

Browser access

The Relay root redirects to /app/, a mobile-first web client for account access, pairing, device selection, and the original DSH Web UI. It implements sealed-tunnel-v1 with Web Crypto. Device keys remain in the browser's IndexedDB and active Service Worker memory; the Relay still sees only routing metadata, ciphertext sizes, and timing.

New Companion QR codes are HTTPS links shaped as https://<relay>/app/#/pair?code=...&key=.... iPhone Camera can open them directly, and URL fragments are not sent to the Relay. Production requires HTTPS and PUBLIC_RELAY_URL=https://your-relay.example with ALLOW_LEGACY_WEB_PROXY=0.

Administration

/admin/ is disabled until both credentials are configured as deployment secrets:

ADMIN_USERNAME=relay-admin
ADMIN_PASSWORD=a-unique-password-from-your-password-manager

The isolated HttpOnly admin session exposes account, device, and access-session aggregates only. It never exposes DSH paths, task content, HTTP bodies, WebSocket frames, IP addresses, tokens, or encryption keys.

Local

cp .env.example .env
npm install
npm run build
npm start

The default listener is http://127.0.0.1:8787. Set DATABASE_PATH to a persistent location in production. The Relay expects HTTPS/WSS in any public deployment and routes 0.1.9 sealed tunnel frames without receiving DSH plaintext.

Releases

Every pull request and branch push installs locked dependencies, builds, tests, and audits the Relay in GitHub Actions. A tag that exactly matches the version in both package.json and package-lock.json (for example, v0.1.9) creates a GitHub Release automatically.

Each release contains .tar.gz and .zip archives with the compiled dist/ output, deployment files, and production dependency manifests, plus SHA256SUMS. The same workflow publishes ghcr.io/april-jk/dsh-relay:<version> and :latest. Only the highest stable SemVer tag may update latest; existing version images and release assets cannot be replaced with different content. After extracting an archive, run npm ci --omit=dev before npm start; native dependencies are installed for the target platform instead of being bundled from CI.

Railway

Create a Railway service from this directory, set JWT_SECRET to a random value of at least 32 bytes, and attach a persistent volume mounted at /data. Production startup fails when this secret is missing, too short, or still set to a documented placeholder. Set DATABASE_PATH=/data/relay.sqlite. Add an HTTPS custom domain such as relay.dshmobile.online and use it as the Relay base URL in the mobile app and Companion. The Companion automatically converts an https:// Relay URL to wss:// for its device connection.

Set TRUST_PROXY=1 on Railway so rate limiting uses the first address supplied by Railway's trusted proxy. Leave it disabled when exposing the Node process directly.

Set PUBLIC_RELAY_URL=https://relay.dshmobile.online so ticket responses return the canonical wss:// client tunnel. Keep ALLOW_LEGACY_WEB_PROXY=0 in production; enabling it re-opens the deprecated plaintext /s proxy for local migration tests only.

The MVP is intentionally single-instance. SQLite volume persistence and a single Relay replica are required until a shared store is introduced.

Private deployment with Docker

cp .env.example .env

Set a random JWT_SECRET of at least 32 bytes, then start the single-instance Relay:

docker compose up -d --build
curl http://127.0.0.1:8787/health

Put an HTTPS reverse proxy in front of port 8787. Keep TRUST_PROXY=0 unless that proxy overwrites client forwarding headers. The relay-data volume contains SQLite state and must be backed up. In the mobile app, choose Relay 服务器 and enter the public HTTPS origin; start DSH with the same origin in DSH_RELAY.

Resource limits

The Relay bounds secure-session count, ciphertext bytes, frame rate, handshake time, and idle time. Defaults are suitable for an MVP deployment and can be adjusted through environment variables:

VariableDefaultScope
MAX_API_BODY_BYTES65,536JSON API request body
MAX_WS_PAYLOAD_BYTES4,194,304One incoming WebSocket frame
API_RATE_LIMIT_PER_MINUTE300API requests per client address
AUTH_RATE_LIMIT_PER_MINUTE20Additional authentication limit per client address
PAIR_RATE_LIMIT_PER_MINUTE30Additional pairing limit per client address
WS_UPGRADE_RATE_LIMIT_PER_MINUTE120WebSocket upgrades per client address
MAX_SECURE_TUNNELS_GLOBAL512Concurrent sealed sessions per Relay instance
MAX_SECURE_TUNNELS_PER_DEVICE8Concurrent sealed sessions per computer
MAX_SECURE_TUNNEL_BYTES536,870,912Total ciphertext bytes per sealed session
SECURE_FRAME_RATE_LIMIT_PER_MINUTE2,400Sealed frames per access session
SECURE_TUNNEL_IDLE_TIMEOUT_MS300,000Sealed-session idle timeout
SECURE_HANDSHAKE_TIMEOUT_MS10,000E2EE handshake timeout

The following limits apply only when the deprecated plaintext proxy is explicitly enabled with ALLOW_LEGACY_WEB_PROXY=1:

VariableDefaultScope
TUNNEL_RATE_LIMIT_PER_MINUTE600Forwarded HTTP requests per client address
MAX_TUNNEL_BODY_BYTES2,097,152One legacy HTTP request
MAX_TUNNEL_RESPONSE_BYTES33,554,432One legacy HTTP response
MAX_PENDING_HTTP_PER_DEVICE32Legacy concurrent HTTP tunnels per computer
MAX_TUNNEL_WS_PER_DEVICE16Legacy concurrent WebSocket tunnels per computer
MAX_PENDING_HTTP_GLOBAL512Legacy concurrent HTTP tunnels per Relay instance
MAX_TUNNEL_WS_GLOBAL256Legacy concurrent WebSocket tunnels per Relay instance

Rate counters are intentionally instance-local, matching the single-instance MVP architecture. Expired refresh tokens, pairing sessions, access sessions, and events are cleaned at startup and every 15 minutes.

Mobile release policy

The public GET /app/version?platform=android|ios endpoint drives update prompts in the mobile app. Configure each platform independently:

APP_ANDROID_LATEST_VERSION=0.2.0
APP_ANDROID_MINIMUM_VERSION=0.1.3
APP_ANDROID_DOWNLOAD_URL=https://play.google.com/store/apps/details?id=io.github.apriljk.dshremote
APP_ANDROID_RELEASE_NOTES=Improved remote session stability.
APP_IOS_LATEST_VERSION=0.2.0
APP_IOS_MINIMUM_VERSION=0.1.3
APP_IOS_DOWNLOAD_URL=https://apps.apple.com/app/id0000000000
APP_IOS_RELEASE_NOTES=Improved remote session stability.

Raise LATEST_VERSION for a dismissible prompt. Raise MINIMUM_VERSION only when older builds must be blocked. Always configure a valid platform download URL before raising either version.

License

MIT