Runbook · Fase 1 — sacar la llave del warehouse de ml-runner
Ejecuta la Fase 1 de
warehouse-writer-extraction.md:
el mismo código desplegado dos veces, con configuraciones distintas.El objetivo no es mover código: es mover la LLAVE. Al terminar, la credencial que
puede reescribir el warehouse ya no vive en el proceso que entrena modelos y habla con
LLMs externos — sin refactor y sin ventana de rotura.
Lo que el código ya trae (en main)
Python — SERVICE_PROFILE decide qué routers monta el proceso:
| Perfil | Monta | Credenciales que necesita |
|---|---|---|
full (defecto) | todo | las de siempre |
warehouse | sólo lakehouse | R2 · catálogo · ninguna de LLM |
compute | todo menos lakehouse | LLM/ML · ninguna de R2 |
El defecto es full: un despliegue que no configure nada se comporta exactamente como
antes.
Node — warehouseWriterConfig() en lib/lakehouse/runner-fetch.ts es el punto único
por el que pasan los 6 clientes del lakehouse. Resuelve:
WAREHOUSE_WRITER_URL ?? ML_RUNNER_URL
WAREHOUSE_WRITER_TOKEN ?? ML_RUNNER_TOKEN
Sin WAREHOUSE_WRITER_URL, todo va a ml-runner como hasta ahora.
Paso 1 · Crear el servicio warehouse-writer en Railway
Mismo repo, mismo Dockerfile (services/ml-runner). Variables:
SERVICE_PROFILE = warehouse
# Catálogo + R2 — referenciadas del ml-runner actual, que hoy es su dueño
LAKEHOUSE_REST_URI = ${{ml-runner.LAKEHOUSE_REST_URI}}
LAKEHOUSE_REST_WAREHOUSE = ${{ml-runner.LAKEHOUSE_REST_WAREHOUSE}}
LAKEHOUSE_CATALOG_TYPE = ${{ml-runner.LAKEHOUSE_CATALOG_TYPE}}
LAKEHOUSE_CATALOG_CREDENTIAL = ${{ml-runner.LAKEHOUSE_CATALOG_CREDENTIAL}}
LAKEHOUSE_CATALOG_OAUTH2_SERVER_URI = ${{ml-runner.LAKEHOUSE_CATALOG_OAUTH2_SERVER_URI}}
LAKEHOUSE_CATALOG_SCOPE = ${{ml-runner.LAKEHOUSE_CATALOG_SCOPE}}
LAKEHOUSE_NAMESPACE = ${{ml-runner.LAKEHOUSE_NAMESPACE}}
LAKEHOUSE_WAREHOUSE = ${{ml-runner.LAKEHOUSE_WAREHOUSE}}
LAKEHOUSE_S3_ENDPOINT = ${{ml-runner.LAKEHOUSE_S3_ENDPOINT}}
LAKEHOUSE_S3_REGION = ${{ml-runner.LAKEHOUSE_S3_REGION}}
LAKEHOUSE_S3_ACCESS_KEY_ID = ${{ml-runner.LAKEHOUSE_S3_ACCESS_KEY_ID}}
LAKEHOUSE_S3_SECRET_ACCESS_KEY = ${{ml-runner.LAKEHOUSE_S3_SECRET_ACCESS_KEY}}
ML_RUNNER_TOKEN = ${{ml-runner.ML_RUNNER_TOKEN}}
⚠️ NO le pongas ANTHROPIC_API_KEY, GEMINI_API_KEY, JUPYTERHUB_PLATFORM_TOKEN,
DATABASE_URL_READER ni NODE_RUNNER_BASE. Si el servicio arranca sin ellas, es la prueba
de que no las necesitaba.
- Servicio creado y Online
- En el log de arranque:
SERVICE_PROFILE=warehouse (compute=False, warehouse=True) -
GET /lakehouse/health→ 200 -
POST /lakehouse/sync→ 404 (el espejo no existe; confirma que es el build nuevo)
Paso 2 · Probar el servicio nuevo ANTES de reapuntar nada
Todavía nadie lo usa: es el momento de romperlo sin consecuencias.
WAREHOUSE_WRITER_URL=https://<host-del-nuevo> npx dotenv -e .env.local -- npx tsx scripts/dataspaces/facade-smoke.ts
- El e2e pasa contra el servicio nuevo (write nativo → read → gobernanza)
Si falla aquí, no has roto nada: ml-runner sigue sirviendo a todo el mundo.
Paso 3 · Reapuntar Node
En el servicio de Next / Vercel:
WAREHOUSE_WRITER_URL = https://<host-del-nuevo>
- Desplegado
-
facade-smokeen verde contra producción - Un pipeline real escribe y se lee de vuelta
Paso 4 · ⭐ Quitarle las llaves a ml-runner — aquí está el valor
Los tres pasos anteriores no mejoran la seguridad: sólo preparan. El valor se materializa en éste.
En ml-runner:
SERVICE_PROFILE = compute
Y borrar de sus variables:
LAKEHOUSE_S3_ACCESS_KEY_ID
LAKEHOUSE_S3_SECRET_ACCESS_KEY
⚠️ Las variables del
warehouse-writerlas referencian con${{ml-runner.…}}. Antes
de borrarlas deml-runner, pásalas a literales enwarehouse-writer— o la
referencia quedará colgando. Es el momento natural de hacerlo: a partir de aquí el dueño
del secreto es el writer, noml-runner.Orden seguro: literales en
warehouse-writer→ verificar → borrar enml-runner.
-
warehouse-writertiene las S3 como valores literales -
ml-runnerya no tiene ningunaLAKEHOUSE_S3_* - Log de
ml-runner:SERVICE_PROFILE=compute (compute=True, warehouse=False) -
GET https://<ml-runner>/lakehouse/health→ 404 ← la prueba de que la puerta se cerró -
facade-smokesigue en verde (ahora va al writer) - Entrenar/predecir un modelo sigue funcionando
Rollback
Cada paso se revierte solo, y ninguno depende del anterior para volver atrás:
| Si falla en… | Deshacer |
|---|---|
| Paso 3 | quitar WAREHOUSE_WRITER_URL de Next → todo vuelve a ml-runner |
| Paso 4 | SERVICE_PROFILE=full en ml-runner + restaurar sus LAKEHOUSE_S3_* |
Después
- Fase 2 — podar los 13 endpoints del router que Node no llama. Buena parte de la "extracción" va a ser borrado.
- Legacy —
LAKEHOUSE_CATALOG_TYPE/LAKEHOUSE_CATALOG_URIson la config delSqlCatalog, quewriter.pydocumenta como retirado. El writer nuevo debería nacer sólo conrest; comprobar siCATALOG_URI(un DSN de Postgres) se puede no copiar. - D · rotación — con la llave ya en un solo servicio, rotarla pasa a ser trivial.