# OpenCode Web UI Este stack ejecuta OpenCode en un contenedor con aislamiento fuerte para exponerlo solo por HTTPS detras de Traefik + Authentik. ## Objetivo - Dominio: `https://ocode.sherlockhomeless.net` - Acceso: solo usuarios autorizados en Authentik - Aislamiento: sin privilegios, filesystem raiz de solo lectura y workspace dedicado ## Archivos - Compose: `opencode/docker-compose.yml` - Variables: `opencode/.env` (copiar desde `opencode/.env.example`) ## Despliegue en contenedor ```bash cp opencode/.env.example opencode/.env mkdir -p /opt/opencode/home /opt/opencode/config /opt/opencode/workspace docker compose --env-file opencode/.env -f opencode/docker-compose.yml up -d ``` El router activo se define en `/opt/traefik/dynamic/opencode.yml` y aplica Authentik junto con la cabecera Basic necesaria para el backend. ## Endurecimiento aplicado - `user: 1000:1000` - `cap_drop: [ALL]` - `security_opt: [no-new-privileges:true]` - `read_only: true` - `/tmp` en `tmpfs` con `exec`, necesario para que Bun/OpenCode cargue librerias temporales - Home persistente aislada en `/opt/opencode/home` - Workspace aislado en `/opt/opencode/workspace`, montado dentro de la home como `/home/opencode/workspace` - Sin `docker.sock` ni montajes del host fuera de los paths definidos ## Authentik (acceso solo a esta app) En Authentik, `ocode.sherlockhomeless.net` necesita su propio `Proxy Provider` publicado en el `authentik Embedded Outpost`. Para aislar el acceso, crea un grupo exclusivo, por ejemplo `opencode-ocode-users`, y una `Policy Binding` que permita solo ese grupo. El contenedor usa los modelos configurados mediante `DEEPSEEK_API_KEY` en su `.env`. ## Claude Sonnet 5 mediante LiteLLM OpenCode consume `claude-sonnet-5` desde el proveedor OpenAI-compatible de LiteLLM. La configuracion activa debe incluir el modelo con el identificador `litellm/claude-sonnet-5` y apuntar `options.baseURL` a `https://llm.sherlockhomeless.net/v1` (o a `http://litellm:4000/v1` si ambos servicios comparten la red Docker). El fichero `opencode/opencode.json` contiene la configuracion local efectiva y esta ignorado por Git porque incluye la clave maestra de LiteLLM. OpenCode solo necesita esa clave; el OAuth de Claude se mantiene y renueva exclusivamente en LiteLLM, por lo que no se debe montar `.claude/.credentials.json` dentro de OpenCode. OpenWebUI no requiere una entrada estatica equivalente: obtiene dinamicamente `claude-sonnet-5` de `/v1/models` usando su conexion OpenAI-compatible existente con LiteLLM.