Runbook · OpenFGA — el grafo de permisos
F4·0 · COMPLETADO 2026-08-06/07. El sustrato ReBAC del que penden la autorización de
Trino (vía el bridge OPA) y, más adelante, el filtrado del grafo semántico.El porqué está en catalogo-como-origen-del-grafo.md
y ontology-projection.md. Aquí está el cómo.⚠️ F4·0 despliega OpenFGA y NADA MÁS.
LAKEKEEPER__AUTHZ_BACKENDsigue en
allowalla propósito: encender el authorizer es F4·1, y va después de poblar los
permisos. Invertir el orden deja el catálogo deny-by-default y vacío — o sea, todo
roto a la vez.
🏁 Estado
| Pieza | Valor |
|---|---|
| Servicio | openfga en Railway · proyecto cozy-comfort |
| Imagen | openfga/openfga:v1.14.0 (Lakekeeper exige ≥ v1.11; v1.14 es la que ellos prueban) |
| Datastore | openfga, una BD propia en el Postgres que ya existía (el mismo servidor que usa Lakekeeper — un servicio menos, aislamiento lógico suficiente) |
| Puertos | HTTP 8080 · gRPC 8081 · métricas 2112 |
| Autenticación | preshared ✅ |
| Exposición | ⭐ NINGUNA — cero dominios públicos, sólo openfga.railway.internal |
| Deployment | SUCCESS |
Los logs que lo cierran:
[INFO] running all migrations → migration done
[INFO] using 'preshared' authentication
[INFO] 🚀 starting gRPC server on '[::]:8081'
[INFO] 🚀 starting HTTP server on '0.0.0.0:8080'
1 · Cómo se levantó, y las tres cosas que no son obvias
① La imagen no arranca sola: necesita subcomando
openfga/openfga sin argumentos imprime la ayuda y sale — el servicio queda EXITED
sin ningún error que lo explique. Hay que fijar el arranque explícitamente, igual que
Lakekeeper (/home/nonroot/lakekeeper serve):
startCommand = /openfga run
preDeployCommand = /openfga migrate
② La migración va en preDeployCommand, no encadenada
openfga migrate es un one-shot que crea las tablas e índices. No se puede hacer
migrate && run: la imagen es mínima y no hay shell donde encadenar. Railway tiene
preDeployCommand justo para esto, y lo corre antes de cada despliegue —
migrate es idempotente, así que repetirlo es gratis.
③ La CLI no expone el startCommand — se pone por la API
railway add --image crea el servicio, pero no hay flag para el comando de arranque.
Se fija con la API GraphQL:
mutation($e:String!,$s:String!,$i:ServiceInstanceUpdateInput!){
serviceInstanceUpdate(environmentId:$e, serviceId:$s, input:$i)
}
# input: { startCommand: "/openfga run", preDeployCommand: ["/openfga migrate"] }
⚠️ Y después hay que forzar un DESPLIEGUE NUEVO, no un redeploy. railway redeploy
reusa el deployment anterior con su configuración vieja — se queda EXITED otra vez y
parece que el cambio no se guardó. El que vale es serviceInstanceDeployV2.
El token de la API está en
~/.railway/config.json→user.accessToken.
2 · Seguridad — dos capas, y la segunda no era opcional
| ① Sin exposición pública | cero dominios. Sólo alcanzable en openfga.railway.internal |
② preshared authentication | ⭐ el primer arranque logueó [WARN] authentication is disabled — OpenFGA por defecto no autentica nada. En una red privada es tolerable; como única capa, no |
La clave vive en las variables del servicio (OPENFGA_AUTHN_PRESHARED_KEYS), y es la
que Lakekeeper necesitará en F4·1. Se lee con
railway variables --service openfga --kv; no está en git ni debe estarlo.
⚠️ Si algún día se le pone dominio público, la preshared key deja de bastar — un
token estático en internet es una llave sin caducidad. Ese día toca OIDC
(OPENFGA_AUTHN_METHOD=oidc), que OpenFGA soporta y Keycloak ya puede emitir.
3 · Verificación
Lo que está verificado: deployment SUCCESS, migración hecha, autenticación
preshared activa y los dos servidores escuchando.
Lo que NO está verificado todavía, y es a propósito: que alguien le hable. Como no
tiene dominio público, la prueba real es Lakekeeper conectando en F4·1 — y ése es el
gate honesto. Un /healthz desde fuera no se puede hacer sin exponerlo, y exponerlo para
probarlo sería empeorar lo que se quiere comprobar.
Para diagnóstico interno hay SSH (railway ssh --service <x>), con la clave ya
registrada. [confirmar] el known_hosts: la primera conexión falla con
Host key verification failed.
4 · Lo que viene, y el ORDEN importa
| Fase | Qué | Por qué en este orden |
|---|---|---|
| ✅ F4·0 | desplegar OpenFGA | inerte: nadie lo usa todavía |
| F4·1 | conectar Lakekeeper (LAKEKEEPER__OPENFGA__*) y poblar los permisos desde principal_grants | ⚠️ poblar ANTES de flipear. El flip a openfga es deny-by-default: con el store vacío, todo deja de funcionar a la vez |
| F4·2 | LAKEKEEPER__AUTHZ_BACKEND=openfga | el flip. Rollback = volver a allowall, una variable |
| F4·3 | bridge OPA + Trino | los mismos 403 de F2, ahora decididos por el motor |
| F4·4 | 🔬 el experimento de la proyección: un tipo propio relacionado con Table, y ver si check hereda | es el que decide si el grafo hereda nativamente |
⚠️ Y una limpieza pendiente que no depende de nada
Hoy conviven LAKEKEEPER__AUTHZ_BACKEND=allowall y
LAKEKEEPER__CEDAR__POLICY_SOURCES__LOCAL_FILES apuntando a un fichero de políticas.
Cedar no está en el build OSS y nadie lee ese fichero: es «una configuración que
parece aplicada y no lo está», que es la trampa que ya costó una tarde. Se quita al
flipear a openfga.
5 · Rollback
| Nivel | Cómo |
|---|---|
| El flip de F4·2 | LAKEKEEPER__AUTHZ_BACKEND=allowall — una variable |
| El servicio entero | borrar openfga en Railway. La BD openfga sobrevive y se puede reusar; para limpiarla, DROP DATABASE openfga |
Nada de esto toca el dato. OpenFGA guarda tuplas de permisos, no filas del Warehouse.
Cruza con: catalogo-como-origen-del-grafo.md ·
ontology-projection.md ·
f4-opa-reconocimiento.md (el terreno medido) ·
infra/lakekeeper/policies/README.md (Cedar, y por qué no).