#!/bin/bash set -euo pipefail # NOTE: this container runs as uid 1000 at runtime on HF Spaces, not root — # every path this script touches was already created and chowned to `user` # in the Dockerfile. Nothing here uses sudo/gosu/chown, on purpose. log() { echo "[entrypoint] $*"; } log "starting up as $(id -un) (uid $(id -u))" # --------------------------------------------------------------------- # 1. Basic auth in front of noVNC — creds come from HF Spaces secrets, # never baked into the image. # --------------------------------------------------------------------- : "${BASIC_AUTH_USER:?Set BASIC_AUTH_USER as an HF Space secret}" : "${BASIC_AUTH_PASS:?Set BASIC_AUTH_PASS as an HF Space secret}" htpasswd -bc /etc/nginx/.htpasswd "$BASIC_AUTH_USER" "$BASIC_AUTH_PASS" unset BASIC_AUTH_PASS # scrub from the environment once consumed # --------------------------------------------------------------------- # 2. Ephemeral, tmpfs-backed Firefox profile — nothing survives a restart, # and nothing ever touches the container's writable layer on disk. # /dev/shm and /tmp are world-writable (sticky 1777), so no chown needed. # --------------------------------------------------------------------- PROFILE_DIR=/dev/shm/tbprofile rm -rf "$PROFILE_DIR" mkdir -p "$PROFILE_DIR" /tmp/tb-downloads # --------------------------------------------------------------------- # 3. Bring nginx up on :7860 IMMEDIATELY with a "connecting" holding page, # before tor has bootstrapped. HF Spaces needs to see the container # answering HTTP quickly — gating nginx's startup on tor's bootstrap # (which can take minutes across bridge retries) makes the container # look dead/unresponsive. # --------------------------------------------------------------------- cat > /var/www/wait/index.html <<'HTML'

Starting up…

Connecting to Tor. This page refreshes automatically.

HTML sed -i 's#proxy_pass http://127.0.0.1:6080;#root /var/www/wait;#' /etc/nginx/nginx.conf log "starting nginx on :7860 (holding page)" nginx -g "daemon off;" & # --------------------------------------------------------------------- # 4. Start tor and wait for 100% bootstrap, retrying across the # configured bridge lines if the first one is blocked/dead. # See bootstrap-test.sh for the retry/fallback logic itself. # --------------------------------------------------------------------- log "starting tor..." tor -f /etc/tor/torrc & if /usr/local/bin/bootstrap-test.sh; then log "tor bootstrapped successfully — switching nginx over to noVNC" # --------------------------------------------------------------- # 5. Virtual display + browser, single VNC session, no clipboard bridging # --------------------------------------------------------------- # Xvfb refuses to create /tmp/.X11-unix itself unless running as root # (a hardcoded security check) — but it's happy to use it if it already # exists, and /tmp is world-writable regardless of our uid. mkdir -p /tmp/.X11-unix chmod 1777 /tmp/.X11-unix Xvfb "$DISPLAY" -screen 0 "$SCREEN_GEOMETRY" -nolisten tcp & sleep 1 /opt/tor-browser/Browser/firefox --profile "$PROFILE_DIR" --no-remote & # -once: exit after first client disconnects (forces a clean re-auth per # session instead of leaving a stale desktop up for the next visitor) # -noxdamage / clipboard disabled to avoid host<->guest clipboard leaks x11vnc -display "$DISPLAY" -forever -shared -once -noxdamage \ -nopw -rfbport 5900 -quiet -nosel -noprimary & websockify --web=/usr/share/novnc 6080 localhost:5900 & # Idle auto-disconnect: 15 minutes of no keyboard/mouse input tears the # session down (see idle-watcher.sh). Restarting the container after this # also gives the next visitor a guaranteed-clean tmpfs profile. IDLE_MINUTES=15 /usr/local/bin/idle-watcher.sh & sed -i 's#root /var/www/wait;#proxy_pass http://127.0.0.1:6080;#' /etc/nginx/nginx.conf nginx -s reload else log "tor failed to bootstrap on any configured bridge." log "Serving a diagnostic page instead of crash-looping the container." touch /tmp/tor-bootstrap-failed cat > /var/www/error/index.html <<'EOF2'

Tor bootstrap failed

All configured Bridge lines in torrc failed to connect within the retry window. This is usually because the bridge addresses are still the placeholders from the template, or the configured bridges have been blocklisted.

Fix: get fresh bridges from https://bridges.torproject.org/ or by emailing [email protected] (body: "get transport obfs4" or "get transport snowflake"), update torrc, and restart the Space.

Check the Space's Logs tab for the full [bootstrap-test] output.

EOF2 sed -i 's#root /var/www/wait;#root /var/www/error;#' /etc/nginx/nginx.conf nginx -s reload fi wait -n