Hemifield commited on
Commit
202cd7f
·
verified ·
1 Parent(s): 94b8897

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +63 -0
  2. README.md +112 -0
  3. entrypoint.sh +55 -0
  4. nginx.conf +41 -0
Dockerfile ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # syntax=docker/dockerfile:1
2
+ #
3
+ # DeepSeek Harness (dsh) on a Hugging Face Docker Space.
4
+ #
5
+ # Persistent user data:
6
+ # Hugging Face Storage Bucket -> /home/user
7
+ #
8
+ # DSH itself and nginx live in the image; DSH's user data, settings, credentials,
9
+ # sessions, plugins, auth state, and user-level caches live under /home/user.
10
+
11
+ FROM node:24-bookworm-slim
12
+
13
+ # Always build the current DSH release.
14
+ ARG DSH_VERSION=latest
15
+ ARG MOBILE_CSS_PLUGIN="dsh-mobile-css@latest"
16
+ ARG REMOTE_AUTH_PLUGIN="dsh-web-startup-auth@latest"
17
+
18
+ ENV HOME=/home/user
19
+ ENV DSH_HOME=/home/user/.dsh
20
+
21
+ # Keep user-level configuration/cache/package-manager state inside the persistent bucket.
22
+ ENV XDG_CONFIG_HOME=/home/user/.config
23
+ ENV XDG_DATA_HOME=/home/user/.local/share
24
+ ENV XDG_STATE_HOME=/home/user/.local/state
25
+ ENV XDG_CACHE_HOME=/home/user/.cache
26
+ ENV NPM_CONFIG_CACHE=/home/user/.cache/npm
27
+ ENV PNPM_HOME=/home/user/.local/share/pnpm
28
+
29
+ ENV MOBILE_CSS_PLUGIN=${MOBILE_CSS_PLUGIN}
30
+ ENV REMOTE_AUTH_PLUGIN=${REMOTE_AUTH_PLUGIN}
31
+
32
+ # DSH requires Node >=22.19 or >=24; Node 24 is the current LTS line.
33
+ RUN apt-get update && apt-get install -y --no-install-recommends \
34
+ nginx-light \
35
+ curl \
36
+ ca-certificates \
37
+ && rm -rf /var/lib/apt/lists/*
38
+
39
+ # Install DSH globally outside /home/user so the image contains the runtime.
40
+ ENV NPM_CONFIG_PREFIX=/opt/npm-global
41
+ ENV PATH=/opt/npm-global/bin:/home/user/.local/share/pnpm:$PATH
42
+ # DSH's plugin manager is a thin wrapper around pnpm, so pnpm must be
43
+ # available on PATH at runtime. Install both globally in the image; the
44
+ # actual DSH profile/plugin state remains under the persistent /home/user.
45
+ RUN npm install -g pnpm@latest @deepseek-ai/dsh@${DSH_VERSION}
46
+
47
+ # Hugging Face Docker Spaces convention: uid 1000, home = /home/user.
48
+ RUN if getent passwd 1000 >/dev/null; then userdel -r "$(getent passwd 1000 | cut -d: -f1)"; fi \
49
+ && useradd -m -u 1000 user
50
+
51
+ WORKDIR /app
52
+
53
+ COPY --chown=user:user nginx.conf /etc/nginx/nginx.conf
54
+ RUN mkdir -p /var/lib/nginx /var/log/nginx \
55
+ && chown -R user:user /var/lib/nginx /var/log/nginx /etc/nginx
56
+
57
+ COPY --chown=user:user entrypoint.sh /app/entrypoint.sh
58
+ RUN chmod +x /app/entrypoint.sh
59
+
60
+ USER user
61
+
62
+ EXPOSE 7860
63
+ CMD ["/app/entrypoint.sh"]
README.md ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: DeepSeek Harness
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: gray
6
+ sdk: docker
7
+ app_port: 7860
8
+ ---
9
+
10
+ # DeepSeek Harness (dsh) on a Hugging Face Space
11
+
12
+ This Space runs the latest DeepSeek Harness behind nginx with persistent Hugging
13
+ Face Storage and the `dsh-web-startup-auth` remote-access/auth plugin.
14
+
15
+ ## Storage: persist the whole user home
16
+
17
+ Attach a Hugging Face Storage Bucket to this Space and mount it at:
18
+
19
+ `/home/user`
20
+
21
+ Use read-write access.
22
+
23
+ This persists the entire user home, including:
24
+
25
+ - `$DSH_HOME` (`/home/user/.dsh`)
26
+ - DSH settings and provider configuration
27
+ - DSH credentials stored by the app
28
+ - sessions and workspace/user data
29
+ - installed DSH profile plugins
30
+ - web-auth state (`web-auth.json`)
31
+ - user-level `.config`, `.local`, and `.cache` data
32
+
33
+ System software installed into the Docker image (`/usr`, `/opt/npm-global`,
34
+ nginx, Node, and the DSH runtime itself) remains part of the image and is
35
+ recreated from the Dockerfile on rebuild. That is intentional.
36
+
37
+ ## Remote settings / Models fix
38
+
39
+ The stock DSH web startup keeps remote privileged configuration APIs loopback-only,
40
+ and the browser also disables settings on a non-loopback hostname. The current
41
+ deployment therefore installs `dsh-web-startup-auth@latest` into the web profile.
42
+
43
+ That plugin provides authenticated remote browser access, fixes the remote
44
+ browser settings gate, handles privileged loopback-authority APIs after
45
+ authentication, and supplies the browser crypto compatibility needed by remote
46
+ HTTP contexts.
47
+
48
+ The plugin is installed/updated from the npm `latest` tag at every container
49
+ start. If the registry is temporarily unavailable, an already-installed copy is
50
+ kept; a first boot fails rather than silently starting without the security and
51
+ settings layer.
52
+
53
+ See the plugin's current documentation for the authentication flow. On first
54
+ visit, use its registration/login page to create the administrator account.
55
+ The authentication state is stored under `/home/user/.dsh`, so it survives
56
+ restarts when the bucket is attached.
57
+
58
+ ## Latest packages
59
+
60
+ The Docker build installs:
61
+
62
+ - `@deepseek-ai/dsh@latest`
63
+ - `dsh-mobile-css@latest`
64
+ - `dsh-web-startup-auth@latest`
65
+
66
+ The two web plugins are also refreshed on every boot so a persistent profile
67
+ doesn't permanently pin an old plugin version.
68
+
69
+ ## Hugging Face visibility
70
+
71
+ Keep this Space **Private**. DSH has powerful filesystem/agent capabilities.
72
+ The authentication plugin is an additional application-level security layer,
73
+ not a reason to make a powerful agent public.
74
+
75
+ ## Secrets
76
+
77
+ Put API keys in Hugging Face Space **Settings → Secrets**, not in this repo.
78
+
79
+ ## Port
80
+
81
+ Hugging Face routes the Space to port `7860`. nginx listens on `7860` and
82
+ proxies to DSH on `0.0.0.0:3080` inside the container.
83
+
84
+ ## Why nginx no longer rewrites Host/Origin
85
+
86
+ The earlier configuration rewrote `Host`/`Origin` to localhost. That could make
87
+ the server-side loopback check pass, but it could not change the browser's
88
+ actual `location.hostname`, which is what caused:
89
+
90
+ `settings are unavailable in this browser`
91
+
92
+ The remote-auth plugin is designed to solve both sides: the browser-side remote
93
+ settings gate and the authenticated server-side privileged API boundary. nginx
94
+ therefore preserves the HF authority and forwards the real request metadata.
95
+
96
+
97
+ ## Plugin package manager
98
+
99
+ `dsh plugin --profile web ...` forwards plugin operations to pnpm. The image
100
+ therefore installs **pnpm@latest** globally as well as **@deepseek-ai/dsh@latest**.
101
+ The pnpm executable is outside `/home/user`; the profile, lockfile, installed
102
+ plugins, settings, auth state, credentials, sessions, and user-level caches are
103
+ inside `/home/user` and are persisted by the HF Storage Bucket.
104
+
105
+ On every container start the entrypoint runs:
106
+
107
+ - `dsh plugin --profile web add dsh-mobile-css@latest`
108
+ - `dsh plugin --profile web add dsh-web-startup-auth@latest`
109
+
110
+ A successful run updates the persistent profile to the current npm `latest`
111
+ versions. If npm is temporarily unreachable, an already-installed plugin is
112
+ kept so the Space can still start.
entrypoint.sh ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ # The HF Storage Bucket is mounted at /home/user. Keep all user-level state
5
+ # underneath it so DSH settings, credentials, sessions, plugins, auth state,
6
+ # and package-manager caches survive container restarts/rebuilds.
7
+ mkdir -p \
8
+ "$HOME/.dsh" \
9
+ "$HOME/.config" \
10
+ "$HOME/.local/share" \
11
+ "$HOME/.local/state" \
12
+ "$HOME/.cache/npm" \
13
+ "$HOME/.cache"
14
+
15
+ # dsh plugin is a pnpm forwarder. pnpm is installed globally in the image.
16
+ # Install/update both runtime UI plugins from the npm latest tag on every boot.
17
+ # If the registry is temporarily unavailable but an existing copy is present,
18
+ # keep the existing copy; otherwise fail rather than silently starting without
19
+ # the remote-settings/auth capability.
20
+ ensure_latest_plugin() {
21
+ local spec="$1"
22
+ local package_name="$2"
23
+
24
+ echo "[entrypoint] installing/updating ${spec}"
25
+ if dsh plugin --profile web add "${spec}"; then
26
+ echo "[entrypoint] ${package_name}: latest install/update succeeded"
27
+ return 0
28
+ fi
29
+
30
+ if [ -d "$DSH_HOME/profiles/web/node_modules/$package_name" ]; then
31
+ echo "[entrypoint] ${package_name}: registry update failed; keeping existing installed copy"
32
+ return 0
33
+ fi
34
+
35
+ echo "[entrypoint] ERROR: ${package_name} is not installed and could not be fetched"
36
+ exit 1
37
+ }
38
+
39
+ ensure_latest_plugin "${MOBILE_CSS_PLUGIN}" "dsh-mobile-css"
40
+ ensure_latest_plugin "${REMOTE_AUTH_PLUGIN}" "dsh-web-startup-auth"
41
+
42
+ # nginx is the HF-facing listener. DSH remains behind it.
43
+ nginx -g 'daemon off;' &
44
+ NGINX_PID=$!
45
+ trap 'kill "$NGINX_PID" 2>/dev/null || true' EXIT
46
+
47
+ # dsh-web-startup-auth replaces the stock startup guard and provides:
48
+ # - authenticated remote browser access
49
+ # - remote settings/provider/credentials support
50
+ # - crypto.randomUUID compatibility for non-loopback browser contexts
51
+ # - loopback-authority handling for privileged APIs after authentication
52
+ #
53
+ # Bind DSH to all interfaces inside the container; nginx is the only public
54
+ # HF-facing entry point. The auth plugin protects remote access.
55
+ exec dsh --profile web --host 0.0.0.0 --port 3080 --no-open
nginx.conf ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ worker_processes 1;
2
+ pid /tmp/nginx.pid;
3
+
4
+ events {
5
+ worker_connections 1024;
6
+ }
7
+
8
+ http {
9
+ include /etc/nginx/mime.types;
10
+ default_type application/octet-stream;
11
+
12
+ access_log /var/log/nginx/access.log;
13
+ error_log /var/log/nginx/error.log;
14
+
15
+ server {
16
+ listen 0.0.0.0:7860;
17
+
18
+ # DSH supports large file/workspace uploads.
19
+ client_max_body_size 160M;
20
+
21
+ location / {
22
+ proxy_pass http://127.0.0.1:3080;
23
+
24
+ proxy_http_version 1.1;
25
+ proxy_set_header Upgrade $http_upgrade;
26
+ proxy_set_header Connection "upgrade";
27
+
28
+ # Preserve the real HF authority. dsh-web-startup-auth performs
29
+ # the authenticated loopback-authority rewrite itself after login.
30
+ proxy_set_header Host $host;
31
+ proxy_set_header X-Real-IP $remote_addr;
32
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
33
+ proxy_set_header X-Forwarded-Proto $scheme;
34
+
35
+ # Required for live token/event streaming and terminal/WebSocket traffic.
36
+ proxy_read_timeout 3600s;
37
+ proxy_send_timeout 3600s;
38
+ proxy_buffering off;
39
+ }
40
+ }
41
+ }