Despliegue de open-design (nexu-io) en Docker + runtime DeepSeek Harness (dsh) — documentación completa: arquitectura, decisiones, pasos, pitfalls y verificación. 2026-08-19.
Find a file
2026-08-19 22:30:45 +00:00
deploy pnpm-workspace.yaml — sharp multi-libc (glibc + musl) para contenedor Alpine 2026-08-19 19:00:15 +00:00
PLAN-LXC-NATIVO.md Actualizar estado: migración completada, corte realizado, fase 6 pendiente 2026-08-19 22:30:45 +00:00
README.md Documentación completa del despliegue open-design + dsh en Docker 2026-08-19 19:00:04 +00:00

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: truecorepack 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)

  1. Detecta dsh en el PATH del contenedor (dsh --version → 0.1.0-rc.7).
  2. Busca el bundle en /app/agent-runtimes/deepseek-harness/manifest.json (montado). Valida sha256.
  3. Ejecuta dsh plugin --profile open-design add <tgz> (con pnpm ya disponible).
  4. Probe de compatibilidad: dsh --profile open-design --probe → debe devolver plugin_version.
  5. 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)

  1. ERR_PNPM_Unknown system error -122 en pnpm install = EDQUOT (disco/cuota del LXC lleno), no error de pnpm. Fix: agrandar disco + rm -rf node_modules parcial + reinstalar (el store global se reutiliza).
  2. JavaScript heap out of memory en tsc -b durante pnpm run build de deepseek-harness. Fix: NODE_OPTIONS="--max-old-space-size=6144".
  3. El build raíz NO compila apps/cli — el binario queda en apps/cli/lib/bin.js. pnpm dsh es modo dev (tsx sobre src/bin.ts); para runtime usar lib/bin.js (JS puro).
  4. npm view @deepseek-ai/dsh-cmdline da 0.0.1-rc.1 (viejo) — las rc nuevas están publicadas como @deepseek-ai/dsh (0.1.0-rc.7, bin dsh).
  5. docker exec <ct> command -v X falla con "executable file not found" — command es builtin del shell; usar docker exec <ct> sh -c 'command -v X'.
  6. corepack enable pnpm falla con EROFS en contenedor read-only. Fix: npm install -g pnpm --prefix /app/.od/pnpm (volumen persistente) + PATH en compose.
  7. 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.yaml con supportedArchitectures.libc: [glibc, musl] y reinstalar.
  8. 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.tgz en /root/od-dsh-install.
  9. 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.
  10. POST /repos/migrate en Forgejo hace timeout en la respuesta para repos >1GB (el servidor sigue trabajando — poll de GET /repos/{o}/{r} hasta empty=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/.od persiste 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.