deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
INSTALL REFERENCE
dsh plugin --profile web add github:vocsong/deepseek-harness-portal
该命令指向仓库当前默认分支;尚无绑定当前 commit 的完整验证结果。
PROJECT README
A multi-tenant platform that provisions and routes isolated DeepSeek Harness (dsh) instances — one container per user — behind a single Cloudflare Tunnel.
Internet
│ <your-domain> (portal: login, admin, user dashboard)
│ <slug>.<instance-domain> (one instance per user)
▼
Cloudflare Tunnel (cloudflared, one named tunnel)
│
▼
Portal (Node + Fastify + SQLite) ── auth + reverse proxy + orchestrator
│
├─► container dsh-<slug> (dsh web on :3000 inside, published 127.0.0.1:18xxx)
├─► container dsh-<slug2> ...
└─► ...
The portal is the only authentication entry point. It owns registration, login, roles, and per-instance access control.
OTP delivery uses SMTP (nodemailer), which is required in production. Code logging is available only with the explicit localhost-only development combination NODE_ENV=development, DOMAIN=localhost, and OTP_DEV_MODE=true; OTP values are never returned by the API.
| Component | Location | Role |
|---|---|---|
| Portal app | portal/ |
Fastify server: auth (OTP + password, bcrypt, session cookie), admin/user JSON API, static dashboard, subdomain reverse proxy |
| Orchestrator | portal/src/orchestrator.js |
podman CLI wrappers: run/start/stop/rm/logs, port allocation, health polling, background provisioning |
| Storage | portal/data/portal.db |
SQLite (better-sqlite3): users, otps, settings, instances, sessions |
| dsh image | image/Dockerfile |
Builds the reviewed dsh image from an approved upstream commit, a dependency-security patch, and two documented source patches; tagged dsh:47f9438-node24 (Node 24 LTS on Debian 13 trixie) and deployed by its sha256: digest |
| dsh clone | dsh/ |
Fresh deepseek-harness checkout (build context, gitignored) |
Each user gets exactly one instance (1 user : 1 instance):
dsh-<slug>, CPU/memory/swap/PID limits, read-only root + bounded tmpfs/logs, no capabilities, no-new-privileges, explicit isolated pasta networking, --restart unless-stopped<name>-home (mounted at /home/dsh — the user's writable home, including $DSH_HOME=/home/dsh/.dsh for sessions, settings, and credentials) and <name>-workspace (mounted at /workspace — the agent's persistent cwd)127.0.0.1:<port> (never exposed beyond the host); the portal proxies to itchangeOrigin rewrites Host to 127.0.0.1:<port> and the browser's Origin is dropped) so dsh's loopback-only settings/credentials methods work; TRUSTED_HOST=<slug>.<instanceDomain> is still passed as a fallbackimage/Dockerfile)image/dsh-security.patch applies reviewed transitive dependency floors and a matching frozen lockfile (production audit: zero known advisories at review time)."You are an AI agent." (branding).--host 0.0.0.0 → required so the published port reaches dsh inside the container (its CLI refuses 0.0.0.0 by default for LAN safety; inside the container only the loopback-published port is reachable).The build script requires the exact approved upstream commit and fails if source/patch checks drift.
Instances use one-level subdomains (<slug>.<instance-domain>), not <your-domain>/<slug>. Two reasons:
/api and loads /assets with absolute paths baked at build time — path-based routing would require rewriting HTML and intercepting the SPA's absolute /api calls (fragile with multiple tabs).*.example.com), so <slug>.<sub>.<your-domain> has no valid TLS certificate. A -deepseek slug suffix keeps the brand while staying one label.podman machine start <machine-name>cloudflared tunnel --version)git clone --depth 1 https://github.com/deepseek-ai/deepseek-harness.git dsh
git -C dsh fetch --depth 1 origin 47f943859bef60e4160492346772ded9b24f765a
git -C dsh checkout --detach 47f943859bef60e4160492346772ded9b24f765a
./build-image.sh
Always use the script: it verifies the approved commit and patch, validates the context policy, and builds from a clean git archive. A direct podman build bypasses those controls. Copy the script's printed DSH_IMAGE=sha256:... line into .env; production rejects mutable image tags.
First build is slow (pnpm install + full harness build); result is ~2.5 GB.
cd portal && npm install && cd ..
cp .env.example .env # then edit .env (DOMAIN, INSTANCE_DOMAIN, ADMIN_*, SMTP_*)
cloudflared tunnel route dns <tunnel-name> '*.<instance-domain>'
cloudflared tunnel route dns <tunnel-name> <your-domain>
Tunnel config (~/.cloudflared/<tunnel-name>.yml):
tunnel: <tunnel-id>
credentials-file: '~/.cloudflared/<tunnel-id>.json'
ingress:
- hostname: <your-domain>
service: http://127.0.0.1:8080
- hostname: '*.<instance-domain>'
service: http://127.0.0.1:8080
- service: http_status:404
cloudflared tunnel --config ~/.cloudflared/<tunnel-name>.yml run <tunnel-name>
./run-portal.sh
Seeds an admin account on first boot from ADMIN_EMAIL / ADMIN_NAME / ADMIN_PASSWORD. The password must be explicitly set, non-placeholder, and at least 16 characters; startup refuses an unsafe bootstrap.
run-portal.sh first applies the tenant egress firewall (firewall/apply.sh) and fails closed if the Podman machine is not running.
See .env.example. The important ones:
| Var | Default | Meaning |
|---|---|---|
NODE_ENV |
production |
Use development only for localhost testing |
DOMAIN |
example.com |
Portal apex domain |
PORTAL_ORIGIN |
https://<DOMAIN> |
Exact trusted browser origin for mutation/CSRF checks |
INSTANCE_DOMAIN |
example.com |
Base for <slug>.<instanceDomain> |
INSTANCE_SLUG_SUFFIX |
-deepseek |
Appended to the slug |
COOKIE_DOMAIN |
(empty) | Session cookie domain (must cover apex + instances) |
SESSION_ABSOLUTE_TTL_MS / SESSION_IDLE_TTL_MS |
7 days / 24 hours | Server-enforced session lifetime and idle expiry |
PORT |
8080 |
Portal listen port (cloudflared connects here) |
ADMIN_EMAIL / ADMIN_NAME / ADMIN_PASSWORD |
(empty) | First-boot admin; password must be explicit and at least 16 characters |
SMTP_HOST / SMTP_PORT / SMTP_USER / SMTP_PASS / SMTP_FROM |
(empty) | Production OTP delivery; host/from required, auth values paired |
OTP_TTL_MS / OTP_MAX_ATTEMPTS |
10 min / 5 | OTP lifetime and per-code attempt cap |
AUTH_RATE_WINDOW_MS / AUTH_RATE_BLOCK_MS |
15 min / 15 min | Persistent authentication throttling window/block |
OTP_RESEND_COOLDOWN_MS |
60000 |
Minimum interval between OTP requests per address/purpose |
PORT_RANGE_START / END |
18000 / 18100 |
Host loopback port pool |
DSH_IMAGE |
required | Immutable sha256:... image ID printed by ./build-image.sh; mutable tags are rejected in production |
PODMAN_COMMAND_TIMEOUT_MS |
60000 |
Per-subprocess timeout for every podman call |
INSTANCE_CPUS / INSTANCE_MEMORY / INSTANCE_MEMORY_SWAP |
2 / 2g / 2g |
Per-instance compute limits |
INSTANCE_PIDS_LIMIT |
512 |
Per-container process limit |
INSTANCE_NETWORK |
pasta |
Required isolated rootless network mode |
INSTANCE_LOG_SIZE / INSTANCE_TMPFS_SIZE |
10mb / 64m |
Bounded runtime log and temporary storage |
INSTANCE_READ_ONLY_ROOT |
true |
Run tenant containers with a read-only root filesystem |
cd portal && npm test
Runs the isolated transactional regression suite (email-change proof binding, attempt accounting, and uniqueness/session rollback) against a temporary SQLite database in portal/test/. It does not touch live data, send email, or require Podman.
https://<slug>.<instance-domain> — the portal authenticates + authorizes, then proxies to the container.podman machine start <machine-name> # 1) container runtime
cloudflared tunnel --config ~/.cloudflared/<tunnel-name>.yml run <tunnel-name>
./run-portal.sh # 2) portal (foreground; wrap in nohup/& for background)
Containers carry --restart unless-stopped; platform behavior after a WSL machine restart can still leave them stopped. The portal auto-starts the authorized user's container on launch and re-queues any instance left provisioning at boot. run-portal.sh re-applies the tenant egress firewall on every start (idempotent); if you restart the Podman machine while the portal is already running, run firewall/apply.sh manually.
HttpOnly, SameSite=Lax, Secure when COOKIE_DOMAIN is set; bearer tokens are SHA-256-digested in SQLite with server-enforced 7-day absolute and 24-hour idle expiry.10/8, 172.16/12); only public internet egress is allowed. Enforced by nftables on the Podman machine (firewall/tenant-egress.nft), applied automatically by run-portal.sh.deepseek-portal/
portal/ Fastify app + static dashboard
src/ backend modules (auth, db, otp, mailer, proxy, orchestrator, email-change)
test/ transactional regression suite (npm test)
image/ Dockerfile + start.sh + dsh-security.patch (dsh image)
firewall/ tenant egress firewall (nftables) + apply.sh
dsh/ fresh upstream clone (build context, gitignored)
build-image.sh
run-portal.sh applies the egress firewall, then starts the portal
.env.example
README.md
SECURITY_AUDIT.md full audit, findings, and remediation status
SECURITY.md vulnerability disclosure policy CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。