# OpenCode Web UI Este stack ejecuta OpenCode en un contenedor con aislamiento fuerte para exponerlo solo por HTTPS detras de Traefik + Authentik. ## Objetivo - Dominio sandboxed: `https://ocode.sherlockhomeless.net` - Dominio clasico host/systemd: `https://opencode.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`) - Middleware Traefik (inyecta `Authorization`): `/opt/traefik/dynamic/opencode-auth.yml` - Config de legado host/systemd: `opencode/systemd/` y `opencode/traefik/opencode.yml` ## 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 ``` Para SSO directo (sin segundo login de OpenCode), crea tambien este middleware dinamico en Traefik: ```yaml http: middlewares: opencode-inject-auth: headers: customRequestHeaders: Authorization: "Basic " ``` El router sandboxed usa la cadena `ths-authentik@docker,opencode-inject-auth@file`, asi Authentik valida al usuario y Traefik pasa despues la cabecera Basic al 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, cada dominio necesita su propio `Proxy Provider` publicado en el `authentik Embedded Outpost`: 1. Provider/app para `ocode.sherlockhomeless.net` (sandboxed). 2. Provider/app independiente para `opencode.sherlockhomeless.net` (clasico). 3. Ambos providers deben estar anadidos al `authentik Embedded Outpost`. 4. Para aislar el acceso sandboxed, crea un grupo exclusivo, por ejemplo `opencode-ocode-users`. 5. Crea una `Policy Binding` para la app sandboxed que permita solo ese grupo. 6. Crea el usuario del colega y anadelo solo a `opencode-ocode-users`. 7. No lo anadas a grupos con acceso a otras aplicaciones. Si el clasico devuelve una pagina `Not Found` de Authentik, normalmente significa que el provider de `opencode.sherlockhomeless.net` no esta asociado al outpost o fue sustituido por el provider de `ocode.sherlockhomeless.net`. ## Arquitectura de modelos LLM Hay dos instancias de OpenCode con modelos distintos: | Instancia | Tipo | Modelos | |-------------------------------|-------------- |----------------------------| | `opencode.sherlockhomeless.net` | Host (systemd) | DeepSeek + **Gemma 4** (Ollama) | | `ocode.sherlockhomeless.net` | Contenedor | Solo DeepSeek (colega) | El contenedor sandboxed (`ocode`) **no** usa Ollama ni Gemma 4. Sus modelos ya estan configurados via `DEEPSEEK_API_KEY` en su `.env`. ### Modelos Gemma 4 via Ollama (para el host OpenCode) Ollama corre en contenedor expuesto en `127.0.0.1:11434` (solo localhost). ```bash cp ollama/.env.example ollama/.env mkdir -p /opt/ollama/data docker compose --env-file ollama/.env -f ollama/docker-compose.yml up -d ``` Pull de modelos: ```bash docker exec -it ollama ollama pull gemma4:e2b # modelo principal docker exec -it ollama ollama pull gemma4:e4b # opcional si e2b no basta ``` Activar provider en el OpenCode del host: ```bash sudo install -m 0644 opencode/opencode.json.example /home/felidae/.config/opencode/opencode.json sudo systemctl restart opencode-web ``` Modelos recomendados para este VPS (8 vCPU, 22 GiB RAM, sin GPU): | Modelo | RAM aprox | Uso | |------------- |---------- |--------------------- | | `gemma4:e2b` | ~3-4 GiB | Modelo principal | | `gemma4:e4b` | ~5-6 GiB | Solo si e2b no basta | | `gemma4:26b` | ~16 GiB | No recomendable | | `gemma4:31b` | ~20 GiB | No recomendable |