Published

Runbook · Fase 1 — sacar la llave del warehouse de ml-runner

Connect any source, model it as an ontology, transform it, and operationalize it, analytics, automation and machine learning, under one governed, self-hostable roof. --- Most teams stitch the...

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)

PythonSERVICE_PROFILE decide qué routers monta el proceso:

PerfilMontaCredenciales que necesita
full (defecto)todolas de siempre
warehousesólo lakehouseR2 · catálogo · ninguna de LLM
computetodo menos lakehouseLLM/ML · ninguna de R2

El defecto es full: un despliegue que no configure nada se comporta exactamente como antes.

NodewarehouseWriterConfig() 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/sync404 (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-smoke en verde contra producción
  • Un pipeline real escribe y se lee de vuelta

Paso 4 · ⭐ Quitarle las llaves a ml-runneraquí 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-writer las referencian con ${{ml-runner.…}}. Antes
de borrarlas de ml-runner, pásalas a literales en warehouse-writer
— o la
referencia quedará colgando. Es el momento natural de hacerlo: a partir de aquí el dueño
del secreto es el writer, no ml-runner.

Orden seguro: literales en warehouse-writer → verificar → borrar en ml-runner.

  • warehouse-writer tiene las S3 como valores literales
  • ml-runner ya no tiene ninguna LAKEHOUSE_S3_*
  • Log de ml-runner: SERVICE_PROFILE=compute (compute=True, warehouse=False)
  • GET https://<ml-runner>/lakehouse/health404 ← la prueba de que la puerta se cerró
  • facade-smoke sigue 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 3quitar WAREHOUSE_WRITER_URL de Next → todo vuelve a ml-runner
Paso 4SERVICE_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.
  • LegacyLAKEHOUSE_CATALOG_TYPE / LAKEHOUSE_CATALOG_URI son la config del SqlCatalog, que writer.py documenta como retirado. El writer nuevo debería nacer sólo con rest; comprobar si CATALOG_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.