| deploy | ||
| PLAN-LXC-NATIVO.md | ||
| README.md | ||
open-design-deploy
Despliegue de open-design (nexu-io) en Docker con runtime DeepSeek Harness (dsh) — documentación completa. 2026-08-19.
Contexto
- Upstream: https://github.com/nexu-io/open-design (Apache-2.0, ~89K★, TypeScript)
- Mirror Forgejo: https://forgejo.odoocoop.cl/proxymedia/open-design (pull mirror, sync cada 8h, ~1928 ramas)
- Qué es: alternativa open-source a Claude Design — app local-first que genera prototipos, landing pages, dashboards, slides, imágenes y video (export HTML/PDF/PPTX/MP4). Compatible con DeepSeek Harness, Claude Code, Codex, Cursor, OpenCode y 20+ CLIs vía BYOK.
Relación open-design ↔ dsh (de código, no de marketing)
- open-design = HOST — daemon + web UI en
:7456, skills, design systems, artefactos. - dsh = RUNTIME de agente que open-design spawna:
dsh --profile open-design --stdio. @open-design/dsh-runtime= conector (plugin de dsh) en~/.dsh/profiles/open-design/. El daemon lo instala automáticamente (installDeepSeekHarnessCompanion) cuando detecta dsh sin perfil.
Decisión de arquitectura
open-design se mantiene como servicio separado del dsh principal: instancia propia de dsh + API key DeepSeek propia (aislamiento de credenciales y propósitos). dsh está en 0.1.0-rc (pre-release) y open-design es hiperactivo — no acoplar ciclos de vida.
Unificación futura en 1 línea: dsh plugin --profile open-design add @open-design/dsh-runtime.
Infraestructura
| Componente | Detalle |
|---|---|
| LXC | 192.168.100.202 (host Debian 13, SSH root) |
| Contenedor | ghcr.io/nexu-io/od:latest — Alpine (node:24-alpine), read_only: true |
| Compose | deploy/docker-compose.yml + deploy/docker-compose.linux.yml |
| Puerto | 7456 (network_mode host, OD_BIND_HOST=127.0.0.1) |
| Auth HTTP | usuario open-design, password = OD_API_TOKEN del .env |
Mounts del compose Linux (docker-compose.linux.yml)
volumes:
- ${HOME}/.local/bin:/mnt/host-local-bin:ro # CLIs de agentes del host
- ${HOME}/.opencode/bin:/mnt/host-opencode:ro
- /root/od-dsh-bundle/agent-runtimes:/app/agent-runtimes:ro # bundle companion
- /root/od-dsh-install:/mnt/dsh-install:ro # paquete npm dsh completo
- /lib/x86_64-linux-gnu:/lib/x86_64-linux-gnu:ro # glibc (CLIs codex/claude/opencode)
- /lib64:/lib64:ro
environment:
PATH: /mnt/host-local-bin:/mnt/host-opencode:/app/.od/pnpm/bin:/usr/local/bin:/usr/bin:/bin
DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-}
Componentes instalados en el host (192.168.100.202)
1. Repo fuente dsh (para build y CLI)
cd /root
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
NODE_OPTIONS="--max-old-space-size=6144" pnpm run build # heap ampliado, ver pitfalls
pnpm dsh --version # 0.1.0-rc.7
2. Paquete npm dsh completo (runtime del contenedor) — /root/od-dsh-install
mkdir -p /root/od-dsh-install && cd /root/od-dsh-install
# pnpm-workspace.yaml con sharp para glibc + musl (clave para Alpine):
cat > pnpm-workspace.yaml << 'EOF'
supportedArchitectures:
os: [linux]
cpu: [x64]
libc: [glibc, musl]
EOF
NODE_OPTIONS=--dns-result-order=ipv4first pnpm add @deepseek-ai/dsh@0.1.0-rc.7
# + el companion instalado dentro del árbol para que el loader lo resuelva:
pnpm add file:/root/od-dsh-bundle/agent-runtimes/deepseek-harness/open-design-dsh-runtime-0.1.0.tgz
3. Bundle companion — /root/od-dsh-bundle/agent-runtimes/deepseek-harness/
La imagen Docker no trae packages/dsh-runtime (el Dockerfile no copia packages/) ni el bundle pre-empacado → el daemon falla con BUNDLED_COMPANION_INVALID ("This Open Design build does not contain the DeepSeek Harness connection component.").
Fix: buildear el companion en el host y montarlo como resource root:
cd /root/nexu-io/open-design
pnpm install --filter @open-design/dsh-runtime... # falla el postinstall del raíz (packages/release) pero instala deps
pnpm --filter @open-design/dsh-runtime build # genera packages/dsh-runtime/dist/
mkdir -p /root/od-dsh-bundle/agent-runtimes/deepseek-harness
pnpm -C packages/dsh-runtime pack --pack-destination /root/od-dsh-bundle/agent-runtimes/deepseek-harness
cd /root/od-dsh-bundle/agent-runtimes/deepseek-harness
TGZ=$(ls *.tgz); SHA=$(sha256sum "$TGZ" | awk '{print $1}'); VER=$(node -e "console.log(require('/root/nexu-io/open-design/packages/dsh-runtime/package.json').version)")
cat > manifest.json << EOF
{
"file": "$TGZ",
"packageName": "@open-design/dsh-runtime",
"schemaVersion": 1,
"sha256": "$SHA",
"version": "$VER"
}
EOF
4. pnpm dentro del contenedor (volumen persistente)
El contenedor es read_only: true → corepack enable pnpm falla con EROFS. dsh ejecuta pnpm internamente para instalar el plugin → sin pnpm, COMPANION_INSTALL_FAILED.
docker exec -u root open-design sh -c 'export HOME=/tmp npm_config_cache=/tmp/.npm; npm install -g pnpm@10 --prefix /app/.od/pnpm'
# + PATH agregado en el compose: /app/.od/pnpm/bin
5. Wrapper dsh — ~/.local/bin/dsh (montado en /mnt/host-local-bin)
cat > ~/.local/bin/dsh << 'EOF'
#!/bin/bash
exec node /mnt/dsh-install/node_modules/@deepseek-ai/dsh/lib/bin.js "$@"
EOF
chmod +x ~/.local/bin/dsh
Flujo de instalación del companion (lo que hace el daemon)
- Detecta
dshen el PATH del contenedor (dsh --version→ 0.1.0-rc.7). - Busca el bundle en
/app/agent-runtimes/deepseek-harness/manifest.json(montado). Valida sha256. - Ejecuta
dsh plugin --profile open-design add <tgz>(con pnpm ya disponible). - Probe de compatibilidad:
dsh --profile open-design --probe→ debe devolverplugin_version. - Agente disponible en UI → Settings → Agent → DeepSeek Harness → modelo.
Verificación
# En el host:
docker exec open-design sh -c 'command -v dsh' # /mnt/host-local-bin/dsh
docker exec open-design dsh --version # 0.1.0-rc.7
docker exec open-design ls /app/agent-runtimes/deepseek-harness/ # manifest.json + tgz
# Probe de compatibilidad (debe devolver plugin_version):
docker exec -u open-design open-design sh -c 'export PATH=/app/.od/pnpm/bin:$PATH; dsh --profile open-design --probe'
# → {"v":1,"type":"probe","runtime":"open-design","protocol_version":1,"plugin_version":"0.1.0",...}
Pitfalls (todos encontrados en este despliegue)
ERR_PNPM_Unknown system error -122enpnpm install= EDQUOT (disco/cuota del LXC lleno), no error de pnpm. Fix: agrandar disco +rm -rf node_modulesparcial + reinstalar (el store global se reutiliza).JavaScript heap out of memoryentsc -bdurantepnpm run buildde deepseek-harness. Fix:NODE_OPTIONS="--max-old-space-size=6144".- El build raíz NO compila apps/cli — el binario queda en
apps/cli/lib/bin.js.pnpm dshes modo dev (tsx sobresrc/bin.ts); para runtime usarlib/bin.js(JS puro). - npm view @deepseek-ai/dsh-cmdline da 0.0.1-rc.1 (viejo) — las rc nuevas sí están publicadas como
@deepseek-ai/dsh(0.1.0-rc.7, bindsh). docker exec <ct> command -v Xfalla con "executable file not found" —commandes builtin del shell; usardocker exec <ct> sh -c 'command -v X'.corepack enable pnpmfalla con EROFS en contenedor read-only. Fix:npm install -g pnpm --prefix /app/.od/pnpm(volumen persistente) + PATH en compose.Could not load the sharp module using linuxmusl-x64: sharp tiene bindings nativos por plataforma. El paquete instalado en host Debian (glibc) no incluye musl. Fix:pnpm-workspace.yamlconsupportedArchitectures.libc: [glibc, musl]y reinstalar.Cannot find package '@open-design/dsh-runtime'desde el loader: el companion debe estar dentro del árbol del paquete dsh (no solo en el perfil). Fix:pnpm add file:.../open-design-dsh-runtime-0.1.0.tgzen/root/od-dsh-install.vela binary not found= solo el login de cuenta cloud (device-auth). El daemon degrada sin vela (routes/vela.ts: /status sin campo account). NO bloquea BYOK. vela es CLI Go aparte, no viene en la imagen.- POST /repos/migrate en Forgejo hace timeout en la respuesta para repos >1GB (el servidor sigue trabajando — poll de
GET /repos/{o}/{r}hastaempty=false; el PATCH mirror_interval falla con 500 durante la migración, reintentar después).
Notas
- El perfil dsh (
~/.dsh/profiles/open-design/) vive en tmpfs del contenedor → se pierde al recrear; el daemon lo re-instala solo. - El volumen
open_design_data:/app/.odpersiste pnpm, config y datos. - El contenedor usa la imagen oficial;
deploy/Dockerfile.local(base + libc6-compat) existe para CLIs glibc (codex/claude/opencode) si se necesitan.