☀ TORMENTA SOLAR

Cómo funciona, capa por capa

El juego es 100% estático (JS vanilla + HTML5 Canvas, sin build) servido por GitHub Pages. Lo único que sale a la red es el chat con los linyeras: un fetch a nuestro proxy. Y ese pedido atraviesa una infra Kubernetes self-hosted entera. Esto es el viaje completo de un mensaje, de punta a punta. Nada es mágico ni una nube ajena: es fierro propio, y todo se declara por API.

¿Por dónde entra el juego? — GitHub Pages vs. infra propia

Hay dos cosas distintas viajando: los estáticos del juego (HTML/JS/CSS) y el chat con IA. Los estáticos pueden servirse desde GitHub Pages (hoy) o desde tu propia infra (nginx en el cluster); el chat siempre sale por la infra self-hosted. Este es el mismo navegador, dos orígenes:

🖥️ Tu navegador
▼ pide dos cosas ▼

① El juego — estáticos

HTML · JS · CSS · assets (sin build)
MODO A — HOY · GitHub Pages CDN de GitHub · villadalmine.github.io/tormenta-solar
gratis, global, cero infra propia.
MODO B — self-host · ✅ LIVE nginx en tu cluster · tormenta-solar.cybercirujas.club
por HAProxy → Cilium Gateway → pod nginx. "Todo local".

② El chat — IA

fetch al proxy (en los dos modos)
SIEMPRE · tu infra self-hosted llm-tormenta-solar.cybercirujas.club
HAProxy (SNI) → Cilium Gateway (TLS) → proxy Node → LiteLLM → OpenRouter / GPU (HAMi+Ollama) / NPU RK1.
Da igual de dónde venga el juego: la IA, la key y el fierro son siempre tuyos. Por eso es gratis para el jugador.
▮ GitHub Pages = solo archivos estáticos (modo A) · ▮ Infra propia = el chat siempre, y los estáticos también en modo B.

El viaje de un mensaje del chat

🖥️Navegador / GitHub Pages
el juego estático · hace fetch al proxy
HTTPS
🌐DNS público + IP WAN
llm-tormenta-solar… → IP de casa · :443
🧱HAProxy — Mac mini G4 · OpenBSD
TCP · SNI passthrough (no termina TLS)
TCP crudo
🚪Cilium Gateway API · VIP .200
acá TERMINA el TLS (Let's Encrypt)
HTTPRoute + cilium-envoy
⚙️Proxy Node — tormenta-ai-proxy
CORS · personas · guardrails
POST /v1/chat/completions
🔀LiteLLM — router
pool de keys · fallback · ruteo por modelo
según el modelo
☁️OpenRouter
nube · free
🎮GPU NVIDIA
HAMi + Ollama
🔌NPU RK1 ×4
inferencia local
Ver el mismo recorrido en ASCII
   [ Navegador / GitHub Pages ]            el juego (estático)
              │  HTTPS  fetch(llm-tormenta-solar.cybercirujas.club)
              ▼
   [ DNS público + IP WAN ]                A → IP de casa
              │  :443
              ▼
   [ HAProxy  (borde, Mac mini) ]          modo TCP · SNI passthrough
              │  reenvía TCP por hostname (no termina TLS)
              ▼
   [ Cilium Gateway API  192.168.178.200 ] ← acá TERMINA el TLS (Let's Encrypt)
              │  cilium-envoy  +  HTTPRoute (hostname → Service)
              ▼
   [ Service → Pod: tormenta-ai-proxy ]    Node · CORS · personas · guardrails
              │  POST /v1/chat/completions
              ▼
   [ LiteLLM  (el centralizador) ]         pool de keys · fallback · ruteo
              ├──────────────┬──────────────┐
              ▼              ▼              ▼
       [ OpenRouter ]   [ GPU NVIDIA ]   [ NPUs RK1 ]
        nube · free      HAMi + Ollama    4× inferencia local
    
0

El cliente — el juego en tu navegador

Vanilla JS + Canvas, sin framework ni build, hosteado en GitHub Pages. El chat hace un fetch POST con el NPC, tu mensaje y un poco de contexto. Tu API key es opcional: por defecto pega contra nuestro proxy (gratis); si ponés una key de OpenRouter, queda solo en tu navegador como override. La key "buena" del servidor nunca toca el cliente.

JS vanillaHTML5 CanvasGitHub Pagesi18n ES/EN
1

DNS + TLS — entrar sin abrir puertos de más

El dominio llm-tormenta-solar.cybercirujas.club resuelve a la IP pública de casa. El certificado es de Let's Encrypt, emitido por cert-manager con desafío DNS-01 vía acme-dns — así no hace falta exponer el :80 ni validar por HTTP. El cert se renueva solo.

cert-managerLet's EncryptDNS-01 / acme-dns
2

HAProxy — el borde (fuera del cluster)

El borde es lo más cirujeado de todo: una Mac mini G4 (PowerPC) corriendo OpenBSD — sí, una máquina de ~2005 reciclada como router TLS. Ahí vive HAProxy en modo TCP con SNI passthrough: mira el req.ssl_sni del hello TLS y, según el hostname, manda el TCP crudo al backend que corresponde — sin desencriptar nada (el TLS lo termina el gateway, más adentro). Varios dominios comparten el mismo backend que apunta a la VIP del cluster. Acá ajustamos maxconn y timeout para que las respuestas largas del LLM no se corten.

HAProxyTCP / L4SNI routing
3

Cilium Gateway API — la puerta del cluster

El tráfico entra al cluster Kubernetes por el cluster-gateway (GatewayClass cilium), una VIP fija servida por LB-IPAM de Cilium. Acá termina el TLS: un listener HTTPS por hostname presenta el certificado (el Secret que llenó cert-manager). Es Gateway API, no Ingress: el routing es un recurso declarativo y estándar.

Cilium 1.19Gateway APIeBPFLB-IPAM
4

HTTPRoute + Envoy — routing L7

Un HTTPRoute matchea el hostname y enruta al Service del proxy. El plano de datos es cilium-envoy, que hace de reverse-proxy HTTP dentro del cluster. Sumar un dominio nuevo es, literalmente, agregar un HTTPRoute y un listener — sin tocar HAProxy más que la regla de SNI.

HTTPRoutecilium-envoyL7
5

El proxy del juego — tormenta-ai-proxy

Un servicio chico en Node (imagen propia, arm64). Hace tres cosas: pone los CORS para que GitHub Pages pueda llamarlo; guarda las personas (los system-prompts de cada linyera) del lado del servidor; aplica los guardrails (si el modelo tarda, devuelve el "la tormenta solar interfiere con el modelo" en vez de dejarte colgado). Después reenvía el pedido a LiteLLM con la key real, que jamás sale al navegador.

NodeCORSpersonas server-sideService ClusterIP
6

LiteLLM — el centralizador de modelos

Un único endpoint OpenAI-compatible (/v1/chat/completions) que es el cerebro del ruteo. Mantiene un pool de API keys, hace fallback entre modelos si uno falla o satura, y decide a dónde mandar cada request según el model_name. Cambiar de "nube" a "fierro propio" es cambiar un nombre de modelo — el juego no se entera de nada. Hoy el chat usa un modelo free (familia Gemma) por defecto.

LiteLLMOpenAI APIpool de keysfallbacks
7

La inferencia — nube y/o fierro propio

Detrás de LiteLLM hay tres destinos, intercambiables:

  • OpenRouter (nube): modelos free, costo $0 — el default actual del chat.
  • GPU NVIDIA self-hosted: un nodo con GPU, compartida por HAMi (vGPU slicing, varias cargas sobre la misma placa), corriendo Ollama.
  • NPUs RK1 self-hosted: 4 placas RK1 (una por nodo arm64) haciendo inferencia local, en round-robin.

La idea: empezar gratis en la nube y, cuando convenga, mover el chat al hardware propio sin tocar ni el juego ni el proxy.

OpenRouterNVIDIA + HAMiOllamaRK1 NPU ×4
8

Observabilidad — ver todo lo que pasa

Hubble (de Cilium) muestra cada flujo de red L3/L4/L7 del cluster: se puede ver, en vivo, el pedido saliendo del proxy hacia LiteLLM y de ahí a la inferencia. Prometheus junta las métricas (LiteLLM expone requests, latencia, gasto, fallbacks) y Grafana las grafica. Si algo va lento o falla, se ve en un tablero, no a ciegas.

HubblePrometheusGrafana
9

Build & deploy — sin Docker daemon, todo reproducible

La imagen del proxy se buildea dentro del cluster con Kaniko orquestado por Argo Workflows (no hace falta un Docker daemon ni una CI externa), en un nodo arm64, y se empuja a un registry interno. El deploy es un Helm chart que crea todo lo declarativo: el HTTPRoute, el Certificate, y hasta un hook idempotente que suma el listener HTTPS al gateway compartido. Volver a levantarlo en otro cluster es correr un comando.

KanikoArgo Workflowsregistry internoHelm

Todo es API / declarativo

Nada de esto se hizo "a mano y a ver si anda": Gateway y HTTPRoute (Gateway API), Certificate y ClusterIssuer (CRDs de cert-manager), el build (CRD de Argo), el deploy (Helm values). Todo es un objeto versionado que se reaplica igual siempre. Eso es lo que hace que una infra casera sea seria: es reproducible.

🔜

Lo que viene — Telegram + Hermes

Se va a agregar un bot de Telegram conectado a Hermes (un agente que ya corre en el mismo cluster) para manejar el juego desde el chat de Telegram: administrarlo, generar contenido nuevo y orquestar el mundo desde el celular. El juego pasa a tener un "panel de control" conversacional, sobre la misma infra.

Telegram BotHermes (agente)control conversacional