---
title: |-
  Playbook *P0*
  Prerrequisitos E2E
description: "Verificación de prerequisites para todos los playbooks: broker público vivo, credenciales OIDC, workspace container con gateway-server + a2a-mcp, proxy funcionando."
kicker: Playbook · P0 · Prerrequisitos
subtitle: |-
  Antes de ejecutar cualquier use case, el ecosistema base debe estar verde.
  Este playbook verifica: **broker**, **OIDC**, **workspace container**, **gateway-server**,
  **a2a-mcp**, **proxy** y conectividad entre todos los componentes.
strip:
  - { k: PLAYBOOK, v: P0-PREREQUISITES }
  - { k: REV, v: "1.0" }
  - { k: FECHA, v: 21 JUN 2026 }
  - { k: CLASIFICACIÓN, v: USO INTERNO }
meta:
  - { k: Esfuerzo, v: 30 min }
  - { k: Depende de, v: nada }
  - { k: Bloquea, v: UC1 · UC2 · UC3 · UC4 · UC5 }
  - { k: Veredicto, v: ⏳ pendiente }
stamps:
  - { text: Broker prod vivo, tone: ok }
  - { text: Workspace configurado, tone: ok }
  - { text: CI imagen Docker rota, tone: warn }
toc_legend:
  - { text: verificado, tone: ok }
  - { text: requiere fix, tone: warn }
  - { text: roto, tone: bad }
footer:
  left: |-
    **P0-PREREQUISITES · REV 1.0**
    Playbooks de verificación E2E para mks-agentics
    docs/jarvis/playbooks/PREREQUISITES.dossier.md
  right: |-
    **BROKER** público · **WORKSPACE** container · **OIDC** credenciales
    Tipografía: Archivo · Instrument Serif · IBM Plex Mono
---

## Arquitectura del *ecosistema* {#arquitectura}

El ecosistema mks-agentics tiene 5 componentes que deben estar vivos para los playbooks. Este diagrama muestra las conexiones entre ellos y por dónde fluye cada use case. {.lead}

```dossier:diagram
engine: d2
source: |
  direction: right

  # ── Nodos externos ──
  mac: "Mac (waxin)" {
    icon: https://raw.githubusercontent.com/github/explore/main/topics/macos/macos.png
    style: {
      fill: "#1a1a2e"
      stroke: "#e94560"
    }
  }

  pocketid: "Pocket-ID" {
    shape: hexagon
    style: {
      fill: "#16213e"
      stroke: "#0f3460"
    }
  }

  # ── Nube pública ──
  cloud: {
    broker: "Broker\nbroker.gateway.mks2508.systems" {
      style: {
        fill: "#0f3460"
        stroke: "#e94560"
      }
    }

    auth: "auth-provider.mks2508.systems" {
      shape: hexagon
      style: {
        fill: "#16213e"
        stroke: "#0f3460"
      }
    }

    workspace: "Workspace Container\nlab1-helsinki.api.mks2508.systems" {
      style: {
        fill: "#1a1a2e"
        stroke: "#533483"
      }
      gateway: "gateway-server\n:4105" {
        style: {
          fill: "#16213e"
          stroke: "#533483"
        }
      }
      a2a: "a2a-mcp" {
        style: {
          fill: "#16213e"
          stroke: "#533483"
        }
      }
    }

    proxy: "gateway-proxy\nproxy.gateway.mks2508.systems" {
      style: {
        fill: "#1a1a2e"
        stroke: "#e94560"
      }
    }
  }

  # ── Conexiones ──
  mac -> broker: "WS channels + MCP tools\n(OIDC JWT)" {
    style.stroke-dash: 3
  }
  mac -> proxy: "ANTHROPIC_BASE_URL\n(virtual key sk-mks-*)" {
    style.stroke-dash: 3
  }
  mac -> workspace: "gateway-web UI\n(HTTPS + WS)" {
    style.stroke-dash: 3
  }

  broker -> workspace.gateway: "spawn_order WS\n+ peer messages" {
    style.stroke-dash: 3
  }
  broker -> workspace.a2a: "channel-push WS\n+ MCP tools REST" {
    style.stroke-dash: 3
  }

  broker -> auth: "OIDC validation\nJWT verify" {
    style.stroke-dash: 3
  }

  workspace.gateway -> workspace.a2a: "Bun.spawn(claude --sdk-url)\nvia cli-launcher" {
    style.stroke-dash: 3
  }

  workspace.gateway -> broker: "peer register\n+ watchPeerInbox" {
    style.stroke-dash: 3
  }

  proxy -> auth: "OIDC session\n(control-plane)" {
    style.stroke-dash: 3
  }
```

```dossier:arch
no: "1"
title: Broker público
sub: broker.gateway.mks2508.systems · OIDC auth · SQLite peers/teams/inbox
stamp: { text: prod vivo, tone: ok }
diagram:
  top: API REST + WebSocket
  bus: "/api/peers · /api/teams · /api/spawn · /api/runners · /ws/runner/:id · /ws/broker/peer/:id"
  nodes:
    - name: Auth
      sub: OIDC JWT + BROKER_ALLOWED_CLIENTS
      items: []
    - name: Peers
      sub: register · unregister · message · inbox · status
      items: []
    - name: Spawn
      sub: spawn_order → runner → cliLauncher.launch()
      items: []
    - name: Events
      sub: ring buffer 500 · metrics · healthz
      items: []
  link: https://broker.gateway.mks2508.systems/healthz
  workloads:
    - Channels WS
    - MCP tools REST
    - Spawn-plane routing
  stats:
    - { v: "3457", k: puerto dev, tone: ok }
    - { v: "4200", k: puerto prod, tone: ok }
    - { v: SQLite, k: persistencia, tone: ok }
  pros:
    - OIDC client_credentials para servicios
    - Channel-push realtime sin polling
    - Spawn-plane con runner registry
  cons:
    - Sin HA (single node SQLite)
    - Runner heartbeat timeout 120s (lento para detectar caídas)
```

```dossier:arch
no: "2"
title: Workspace container
sub: lab1-helsinki.api.mks2508.systems · Coolify project code-env
stamp: { text: configurado, tone: ok }
diagram:
  top: Container Docker con supervisord
  bus: entrypoint.sh → 17 setup functions → supervisord (gateway + codeserver + sshd + desktop)
  nodes:
    - name: Gateway
      sub: pull-on-startup desde GitHub Releases
      items:
        - gateway-server.js (bundle)
        - update-gateway.sh (baked)
        - supervisord autorestart=true
    - name: a2a-mcp
      sub: pull-on-startup desde GitHub Releases
      items:
        - a2a-mcp.js (bundle)
        - update-a2a-mcp.sh (baked)
        - wrapper /usr/local/bin/a2a-mcp
    - name: Claude Code
      sub: baked en imagen · multi-provider API keys
      items: []
  link: https://lab1-helsinki.api.mks2508.systems
  workloads:
    - code-server :8443
    - gateway-server :3456
    - SSH :2222
    - RDP :3389
  stats:
    - { v: "3456", k: gateway puerto, tone: ok }
    - { v: "8443", k: code-server puerto, tone: ok }
    - { v: GITHUB_TOKEN, k: requerido para bundles, tone: warn }
  pros:
    - entrypoint idempotente
    - bundles cacheados en /var/cache/
    - gateway + a2a-mcp auto-descarga
  cons:
    - CI roto (symlink mesh-drop huérfano)
    - Sin health check del gateway en Docker HEALTHCHECK
    - GITHUB_TOKEN obligatorio para bundles privados
```

```dossier:arch
no: "3"
title: Gateway-proxy público
sub: proxy.gateway.mks2508.systems · multi-provider · OIDC auth
stamp: { text: prod vivo, tone: ok }
diagram:
  top: Proxy HTTP multi-provider
  bus: Data-plane /v1/messages + Control-plane /v1/keys · /v1/usage · /auth/*
  nodes:
    - name: Providers
      sub: GLM (z.ai) · MiniMax · DeepSeek
      items:
        - anthropic-native protocol
        - routing por model name + prefix
    - name: Auth
      sub: Virtual keys sk-mks-* + OIDC admin session
      items:
        - SHA-256 hash storage
        - atomic writes chmod 600
    - name: Usage
      sub: tracking tokens · quotas · rate limiting
      items: []
  link: https://proxy.gateway.mks2508.systems/health
  workloads:
    - Tier-1 (usuarios auth)
    - Tier-2 (público con rate limit)
  stats:
    - { v: "3200", k: puerto, tone: ok }
    - { v: "3", k: providers, tone: ok }
    - { v: OIDC, k: control-plane, tone: ok }
  pros:
    - Virtual keys sin provider keys en cliente
    - Hot-reload de config (fs.watch)
    - Per-session route pinning
  cons:
    - Channel-block daemon (:3201) legacy
    - Hot-switch per-session roto (no implementado)
```

## F1 — Verificar *broker* público {#f1-broker}

```dossier:phases
items:
  - dur: "5 min"
    title: Broker health + autenticación
    checks:
      - text: "`curl -s https://broker.gateway.mks2508.systems/healthz` → 200 OK"
        sub: Health check público sin auth
      - text: "`curl -s https://broker.gateway.mks2508.systems/api/peers` → 401"
        sub: Sin token JWT → rechazado
      - text: "OIDC client_credentials → JWT válido → `GET /api/peers` → 200 + JSON array"
        sub: Con token JWT → autorizado
      - text: "`GET /api/runners` → lista de runners conectados"
        sub: Verificar que hay al menos un runner (workspace container)
```

### Verificación broker

```dossier:term
title: Verificación broker público
content: |
  # Health check público
  $ curl -s https://broker.gateway.mks2508.systems/healthz | jq .
  → {"status":"ok","uptime":3600}

  # Auth requerido para API
  $ curl -s https://broker.gateway.mks2508.systems/api/peers
  → {"error":"MISSING_TOKEN","message":"Authorization header required"}

  # Con JWT válido (client_credentials)
  $ curl -s https://broker.gateway.mks2508.systems/api/peers \
      -H "Authorization: Bearer $JWT_TOKEN" | jq .
  → [
  →   {"id":"...","kind":"operator","name":"...","status":"running"},
  →   ...
  → ]
```

```dossier:note
tone: accent
tag: Credenciales OIDC necesarias
body: |
  Para generar el JWT, se necesita:
  - `OIDC_CLIENT_ID` — cliente OIDC registrado en Pocket-ID
  - `OIDC_CLIENT_SECRET` — secret del cliente
  - `OIDC_TOKEN_ENDPOINT` — `https://auth-provider.mks2508.systems/api/oidc/token`

  El cliente `gateway-server-workspace-helsinki` (`46c6e32c-75c0-4428-b358-9104c688ebfc`) existe en Pocket-ID.
  Sus credenciales están en Coolify como env vars del servicio `mks-backend`.

  Para smoke local, usar las credenciales del cliente `mcp-standalone-mac-waxin` en `.env.brk-extract.local`.
```

## F2 — Verificar *workspace container* {#f2-workspace}

```dossier:phases
items:
  - dur: "10 min"
    title: Workspace container vivo + servicios
    checks:
      - text: "`curl -s https://lab1-helsinki.api.mks2508.systems/health` → 200 OK"
        sub: Health del gateway-server dentro del workspace
      - text: "`curl -s https://lab1-helsinki.api.mks2508.systems/health` → body contiene 'gateway'"
        sub: Verificar que es el gateway-server, no code-server
      - text: "`docker exec <container> which a2a-mcp` → `/usr/local/bin/a2a-mcp`"
        sub: a2a-mcp wrapper existe en PATH
      - text: "`docker exec <container> cat /usr/local/bin/a2a-mcp` → script bash válido"
        sub: El wrapper apunta a `bun /opt/mks-agentics/agent2agent-mcp/a2a-mcp.js`
      - text: "`docker exec <container> test -f /opt/mks-agentics/agent2agent-mcp/a2a-mcp.js` → existe"
        sub: El bundle a2a-mcp.js fue descargado en startup
      - text: "`docker exec <container> supervisorctl status gateway` → RUNNING"
        sub: Gateway server está corriendo como servicio supervisado
      - text: "`docker exec <container> env | grep GATEWAY_BROKER_URL` → `https://broker.gateway.mks2508.systems`"
        sub: Env var GATEWAY_BROKER_URL seteada en el container
      - text: "`docker exec <container> env | grep OIDC_CLIENT_ID` → valor presente"
        sub: Credenciales OIDC inyectadas vía Coolify env vars
```

### Variables de entorno requeridas en el workspace

```dossier:table
caption: Env vars requeridas en el workspace container (Coolify)
columns:
  - { h: Variable }
  - { h: Valor }
  - { h: Necesaria para }
  - { h: Estado }
rows:
  - cells:
      - GATEWAY_BROKER_URL
      - https://broker.gateway.mks2508.systems
      - a2a-mcp + cli-launcher
      - "[[ok:seteada]]"
  - cells:
      - OIDC_ISSUER
      - https://auth-provider.mks2508.systems
      - OIDC token generation
      - "[[ok:seteada]]"
  - cells:
      - OIDC_TOKEN_ENDPOINT
      - https://auth-provider.mks2508.systems/api/oidc/token
      - OIDC client_credentials
      - "[[ok:seteada]]"
  - cells:
      - OIDC_CLIENT_ID
      - 46c6e32c-...
      - JWT para broker
      - "[[ok:seteada]]"
  - cells:
      - OIDC_CLIENT_SECRET
      - (secret)
      - JWT para broker
      - "[[ok:seteada]]"
  - cells:
      - GITHUB_TOKEN
      - ghp_...
      - Descargar bundles de GitHub Releases
      - "[[ok:seteada]]"
  - cells:
      - MINIMAX_API_KEY
      - (secret)
      - Provider MiniMax
      - "[[ok:seteada]]"
  - cells:
      - GLM_API_KEY
      - (secret)
      - Provider GLM
      - "[[ok:seteada]]"
  - cells:
      - DEEPSEEK_API_KEY
      - (secret)
      - Provider DeepSeek
      - "[[ok:seteada]]"
```

```dossier:note
tone: warn
tag: CI de imagen Docker roto
body: |
  El pipeline `build-push-base-image.yml` (#27881812954) falla en `build-ubuntu-amd64`
  y `build-arch` por un symlink huérfano en `mks-terminal/bin/mesh-drop`:

  ```
  chmod: cannot operate on dangling symlink '/home/developer/dotfiles/mks-terminal/bin/mesh-drop'
  ```

  El fix es cambiar `chmod +x .../bin/*` por `find .../bin -type f -exec chmod +x {} \;`
  en el `Dockerfile.unified:649`.

  **Impacto en playbooks**: mientras el CI esté roto, la imagen Docker no se actualiza.
  La imagen actual (pre-fix) ya tiene `update-a2a-mcp.sh` baked (commit `b0acfb7` sí entró
  en el último build verde). Los playbooks **no están bloqueados** — el workspace container
  actual ya tiene a2a-mcp funcional.
```

## F3 — Verificar *proxy* público {#f3-proxy}

```dossier:phases
items:
  - dur: "5 min"
    title: Proxy health + virtual key
    checks:
      - text: "`curl -s https://proxy.gateway.mks2508.systems/health` → 200 OK"
        sub: Health del proxy público
      - text: "`curl -s https://proxy.gateway.mks2508.systems/v1/providers` → 200 + provider list"
        sub: Lista de providers disponibles (data-plane, sin auth)
      - text: "`curl -s https://proxy.gateway.mks2508.systems/v1/keys` → 401 (sin OIDC session)"
        sub: Control-plane requiere OIDC admin session
```

### Virtual key para desarrollo local

```dossier:term
title: Proxy local vs proxy público
content: |
  # El proxy tiene dos modos de acceso:
  #
  # 1. Proxy público (proxy.gateway.mks2508.systems:443)
  #    → Requiere virtual key sk-mks-* en header x-api-key
  #    → Para producción / Tier-2
  #
  # 2. Proxy local (127.0.0.1:3200)
  #    → Sin auth si PROXY_AUTH_REQUIRED=false
  #    → Para desarrollo local con cl minimax proxy

  # Para crear una virtual key (requiere OIDC admin session):
  $ gateway-proxyctl key create --label playbook-test
  → sk-mks-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

  # Verificar que la key funciona:
  $ curl -s https://proxy.gateway.mks2508.systems/v1/models \
      -H "x-api-key: sk-mks-..." | jq .
```

## F4 — Verificar *claudio* en Mac {#f4-claudio}

```dossier:phases
items:
  - dur: "5 min"
    title: Claudio modos + channels
    checks:
      - text: "`claudio minimax --env --channels` → emite exports sin errores"
        sub: Verificar que mergea a2a MCP + dangerouslyLoadDevelopmentChannels
      - text: "`ls ~/.claude-minimax/settings.json` → existe"
        sub: Provider dir creado con settings.json
      - text: "`python3 -c \"import json; d=json.load(open('$HOME/.claude-minimax/settings.json')); print('agent2agent' in d.get('mcpServers',{}))\"` → True"
        sub: a2a MCP mergeado en settings.json del provider dir
      - text: "`python3 -c \"import json; d=json.load(open('$HOME/.claude-minimax/settings.json')); print(d.get('dangerouslyLoadDevelopmentChannels',[]))\"` → ['server:agent2agent']"
        sub: Channels habilitados en settings.json
      - text: "`cl minimax --proxy --channels` → lanza Claude Code sin errores"
        sub: "Smoke: Claude Code arranca con proxy + channels"
```

### Verificación del provider dir

```dossier:term
title: Inspeccionar settings.json del provider dir
content: |
  # Verificar que claudio --channels mergeó correctamente
  $ python3 -c "
  import json
  d = json.load(open('$HOME/.claude-minimax/settings.json'))
  print('mcpServers:', json.dumps(d.get('mcpServers', {}), indent=2))
  print('channels:', d.get('dangerouslyLoadDevelopmentChannels', []))
  print('tengu_harbor:', d.get('cachedGrowthBookFeatures', {}).get('tengu_harbor'))
  "
  → mcpServers: {
  →   "agent2agent": {
  →     "command": "a2a-mcp",
  →     "env": {
  →       "GATEWAY_BASE_URL": "https://broker.gateway.mks2508.systems",
  →       "A2A_CHANNEL_PUSH": "1"
  →     }
  →   }
  → }
  → channels: ['server:agent2agent']
  → tengu_harbor: true
```

## F5 — Verificación *final* {#f5-final}

```dossier:matrix
groups:
  - tone: v
    title: Verificado
    badge: "3 checks"
    items:
      - text: Broker público responde healthz
        sub: https://broker.gateway.mks2508.systems/healthz → 200
      - text: Workspace container gateway-server responde
        sub: https://lab1-helsinki.api.mks2508.systems/health → 200
      - text: Proxy público responde
        sub: https://proxy.gateway.mks2508.systems/health → 200
  - tone: r
    title: Research pendiente
    badge: "1 check"
    items:
      - text: a2a-mcp bundle existe en workspace
        sub: Verificar que update-a2a-mcp.sh se ejecutó en el último boot
  - tone: p
    title: Pendiente
    badge: "2 checks"
    items:
      - text: CI Docker image verde
        sub: Fix symlink mesh-drop en Dockerfile.unified:649
      - text: Runner daemon en Mac para spawn-plane
        sub: Necesario para UC4 (spawn local)
```

```dossier:sources
groups:
  - title: Configuración
    items:
      - label: Broker deploy Coolify
        url: https://coolify.mks2508.systems
        chip: { text: prod }
      - label: Workspace container Coolify
        url: https://coolify.mks2508.systems
        chip: { text: prod }
      - label: Pocket-ID admin
        url: https://auth-provider.mks2508.systems
        chip: { text: admin }
  - title: Código
    items:
      - label: cli-launcher.service.ts
        url: apps/gateway-server/src/services/cli-launcher.service.ts
        chip: { text: source }
      - label: entrypoint.sh
        url: ../mks-workspaces/docker/devenv-full/entrypoint.sh
        chip: { text: source }
      - label: claudio index.ts
        url: ~/dotfiles/claude-model/src/index.ts
        chip: { text: source }
      - label: Dockerfile.unified
        url: ../mks-workspaces/docker/base/Dockerfile.unified
        chip: { text: source }
```

```dossier:questions
items:
  - no: Q1
    title: "¿Está el broker respondiendo?"
    chip: { text: crítico, tone: hot }
    body: Verificar con `curl -s https://broker.gateway.mks2508.systems/healthz`
    branches:
      - tone: "yes"
        label: "Sí → 200 OK"
        text: Continuar a F2. El broker es el bus central.
      - tone: "no"
        label: "No → error"
        text: "Revisar deploy en Coolify. El broker es prerequisite para TODO."
  - no: Q2
    title: "¿Está el workspace container ejecutando gateway-server?"
    chip: { text: necesario, tone: ok }
    body: Verificar con `curl -s https://lab1-helsinki.api.mks2508.systems/health`
    branches:
      - tone: "yes"
        label: "Sí → 200 OK"
        text: Continuar a UC1/UC2. El workspace está listo.
      - tone: "no"
        label: "No → error o timeout"
        text: "Revisar `supervisorctl status gateway` dentro del container. Puede que GITHUB_TOKEN no esté seteado y el bundle no se descargó."
  - no: Q3
    title: "¿Está a2a-mcp funcional en el workspace?"
    chip: { text: UC3+, tone: warn }
    body: Verificar con `docker exec <container> which a2a-mcp`
    branches:
      - tone: "yes"
        label: "Sí → /usr/local/bin/a2a-mcp"
        text: Listo para UC3/UC5 (channels y spawn).
      - tone: "no"
        label: "No → not found"
        text: "Ejecutar update-a2a-mcp.sh dentro del container. Si GITHUB_TOKEN no está, el bundle no se descarga."
```

```dossier:note
tone: accent
tag: Siguiente paso
body: |
  Con P0 verde, se puede proceder a:
  - **UC1** — sesión básica con proxy (`cl minimax --proxy`)
  - **UC2** — gateway-web → workspace session
  - **UC3** — dos peers Mac con channels (requiere F4 verde)
  - **UC4** — spawn desde sesión Mac (requiere runner daemon)
  - **UC5** — E2E workspace completo (requiere todo lo anterior)
```
