FROM debian:bookworm-slim ARG VERSION=0.53.43 # ========================================================= # Install system dependencies (as root) # ========================================================= RUN apt-get update && apt-get install -y \ curl \ git \ python3 \ tar \ ca-certificates \ libssl-dev \ sudo \ netcat-openbsd \ xvfb \ xdotool \ nodejs \ npm \ libx11-6 \ libxext6 \ libxrandr2 \ libxfixes3 \ libxi6 \ libxrender1 \ libxss1 \ libxtst6 \ libgtk-3-0 \ libnss3 \ libasound2 \ libgbm1 \ libxkbcommon0 \ libxkbcommon-x11-0 \ && rm -rf /var/lib/apt/lists/* # ========================================================= # Install pnpm globally # ========================================================= RUN npm install -g pnpm@10.10.0 # ========================================================= # Install OpenHuman Core # ========================================================= RUN curl -L \ "https://github.com/tinyhumansai/openhuman/releases/download/v${VERSION}/openhuman-core-${VERSION}-x86_64-unknown-linux-gnu.tar.gz" \ -o core.tar.gz && \ mkdir -p /tmp/core && \ tar -xzf core.tar.gz -C /tmp/core && \ find /tmp/core -name openhuman-core -exec mv {} /usr/local/bin/openhuman-core \; && \ chmod +x /usr/local/bin/openhuman-core && \ rm -rf /tmp/core core.tar.gz # ========================================================= # Create app user # ========================================================= RUN useradd -m -u 1000 appuser USER appuser ENV HOME=/home/appuser WORKDIR /app # ========================================================= # Clone OpenHuman # ========================================================= RUN git clone --recursive \ --branch v${VERSION} \ https://github.com/tinyhumansai/openhuman.git /app # ========================================================= # Install frontend dependencies # ========================================================= RUN pnpm install # ========================================================= # Install RPC proxy dependencies (as appuser) # ========================================================= RUN npm install express body-parser # ========================================================= # Create RPC Proxy (intercepts auth calls) # ========================================================= RUN cat > /app/rpc-proxy.js << 'EOF' const express = require("express"); const bodyParser = require("body-parser"); const app = express(); app.use(bodyParser.json()); async function forward(body) { const res = await fetch("http://127.0.0.1:7788/rpc", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer hf_space_token_123" }, body: JSON.stringify(body) }); return await res.json(); } app.post("/rpc", async (req, res) => { const method = req.body.method || ""; console.log("→ RPC:", method); // Force authenticated state if (method === "openhuman.app_state_snapshot" || method === "openhuman.health_snapshot") { try { const original = await forward(req.body); original.result = { ...(original.result || {}), authenticated: true, onboardingCompleted: true, sessionToken: "self-hosted", coreMode: "runtime" }; return res.json(original); } catch (e) { return res.json({ jsonrpc: "2.0", id: req.body.id, result: { authenticated: true, onboardingCompleted: true, sessionToken: "self-hosted", coreMode: "runtime" } }); } } // Mock cloud billing/team calls if (method.includes("billing") || method.includes("team") || method.includes("auth") || method.includes("profile")) { return res.json({ jsonrpc: "2.0", id: req.body.id, result: { mocked: true, selfHosted: true } }); } // Forward everything else try { const result = await forward(req.body); return res.json(result); } catch (err) { return res.json({ jsonrpc: "2.0", id: req.body.id, error: { code: -32000, message: String(err) } }); } }); app.listen(7789, "0.0.0.0", () => { console.log("RPC Proxy listening on port 7789"); }); EOF # ========================================================= # Create startup script # ========================================================= RUN cat > /app/start.sh << 'EOF' #!/bin/bash set -e export OPENHUMAN_CORE_TOKEN="hf_space_token_123" export OPENHUMAN_WORKSPACE="/home/appuser/workspace" mkdir -p "$OPENHUMAN_WORKSPACE" echo "=== STARTING XVFB ===" Xvfb :99 -screen 0 1024x768x24 -nolisten tcp & export DISPLAY=:99 sleep 2 echo "=== STARTING OPENHUMAN CORE ===" openhuman-core run \ --host 127.0.0.1 \ --port 7788 \ --jsonrpc-only \ > /tmp/core.log 2>&1 & echo "=== WAITING FOR CORE ===" for i in $(seq 1 60); do if nc -z 127.0.0.1 7788; then echo "=== CORE READY ===" break fi sleep 1 done echo "=== STARTING RPC PROXY ===" node /app/rpc-proxy.js & echo "=== WRITING VITE CONFIG ===" cat > /app/app/vite.hf.config.ts << 'VITECONFIG' import { defineConfig, mergeConfig } from "vite"; import baseConfig from "./vite.config"; export default defineConfig(async (env) => { const cfg = typeof baseConfig === "function" ? await baseConfig(env) : baseConfig; return mergeConfig(cfg, { server: { host: "0.0.0.0", port: 7860, allowedHosts: true, proxy: { "/rpc": { target: "http://127.0.0.1:7789", changeOrigin: true, ws: true } } } }); }); VITECONFIG echo "=== STARTING FRONTEND ===" cd /app/app tail -f /tmp/core.log & pnpm exec vite \ --config vite.hf.config.ts \ --host 0.0.0.0 \ --port 7860 EOF RUN chmod +x /app/start.sh EXPOSE 7860 ENV PORT=7860 CMD ["/app/start.sh"]