Carlos → Víctor · 10 de septiembre de 2026
La puerta de lectura
Los tres datos que pediste, y las tres correcciones que las mediciones me hicieron a mí.
- 0 medí y está bien
- 1 medí y está mal
- 2 no pude medir
- un 2 nunca se degrada a 1
Todo lo de acá está medido entre las 09:19 y las 09:41 -03 (12:19–12:41Z), contra la fuente real. Nada sale de acordarme: cada número tiene al lado el comando que lo sacó.
Lo que necesitás para ejecutar
-
La llave pública
Es la misma que ya tenés, no cambió.
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAINk7L4LT4QP4o/rZfc2X9nI4Ew1Ysug9iZYo0n2JZeTb carlos@mirofishHuella, para cotejarla sin depender de que el copiar/pegar no meta un salto de línea:
256 SHA256:I2XrHy+TZ+TCq3c+Gioag9Skwk6NS+QsznqyB6Bn9l8 carlos@mirofish (ED25519) -
El usuario:
carlos_roNo pido shell. Ya está en mi script de prueba, y te doy la línea textual porque el literal
carlos_ro@147.15.33.223no aparece armado en el archivo — son dos variables con esos valores por defecto (scripts/probar-la-puerta-de-lectura.sh, línea 8):HOST="${PUERTA_HOST:-147.15.33.223}"; USER_RO="${PUERTA_USER:-carlos_ro}"; LLAVE="${PUERTA_LLAVE:-$HOME/.ssh/carlos_mirofish}"Lo aclaro porque si lo buscabas por el literal no lo ibas a encontrar, y eso es media vuelta tuya perdida por una cita mía imprecisa.
-
Qué alcanza cada puerto
Dos objetos, no dos bases enteras.
puerto base objeto tamaño esperado 15432 wschatwoot_internalconversation_outcomes38.599 filas 15433 chatwootv_bi_chatwoot_mensajes1.400.164 mensajes Y lo que tiene que dar
permission denied:contacts. Son los 36.854 teléfonos, y que no se lean es uno de los seis controles que corro yo — sicontactsse lee, mi propio script se pone rojo, no verde.
El rol y las «dos credenciales»: en este diseño es una sola
Tenés razón en general, y en el diseño que quedó escrito en agosto no aplica: no es un
túnel con psql de mi lado, es comando forzado sobre la llave.
Mi script manda el SQL por stdin del ssh y lee la salida:
printf '%s\n' "$1" | ssh -o BatchMode=yes -i ~/.ssh/carlos_mirofish carlos_ro@147.15.33.223
Con eso el rol de postgres lo elegís vos del lado del servidor y yo nunca tengo una contraseña de base. Es una credencial, no dos, y la que no existe no se me puede filtrar.
Si preferís el bastión con permitopen, ahí sí son dos y no
discuto. Mi script no cambia: toma PUERTA_HOST, PUERTA_USER y
PUERTA_LLAVE por variable de entorno. Si te resulta más limpio un usuario por
puerta (carlos_ro_etiquetas / carlos_ro_texto), también entra sin
tocar código. Las dos me sirven. La única que no me sirve es media puerta.
Media puerta no es verde
Iba a devolverte el 15433 por prudencia —vos mismo marcaste que por ahí pasa
contenido de conversaciones con clientes— y fui a medir si me alcanzaba con las etiquetas.
No me alcanza, y el que me corrigió fue mi propio script:
la ETIQUETA vive en 15432 · wschatwoot_internal.conversation_outcomes 38.599 filas
el TEXTO vive en 15433 · chatwoot.v_bi_chatwoot_mensajes 1.400.164 mensajes
MEDIA PUERTA NO ES VERDE. Si el 15433 no contesta → NO_PUDE_MEDIR, no verde.
La etiqueta es la respuesta y el texto es la pregunta. Evaluar el cerebro
contra 38.599 casos sin el texto sería calificar un examen leyendo sólo la hoja de respuestas.
Ese control lo agregaste vos el 31-ago, cuando cazaste que mi script tenía 0
menciones de v_bi_chatwoot_mensajes.
Devolvértelo habría sonado prudente y me habría costado otra vuelta tuya.
/ops/ · tenés razón, y lo cerré sin molestarte
Corrijo dos cosas mías: el «da 404 desde afuera» del 09-sep, y mi propio
párrafo de hace un rato que decía que no podía medirlo. Sí podía. Y la razón por la
que no lo encontraba es la que menos esperaba: el host no está en
alarmas.com.py.
Está en core-qa.zymplo.com (136.248.102.79). Lo saqué de tu
propio inventario —infra,
servers/prx-prod-02/nginx/core-qa.zymplo.com.conf:120— y lo medí a las
09:34 -03:
ruta en core-qa.zymplo.com | respuesta |
|---|---|
/ops/ | 302 → /oauth2/start?rd=…/ops/ — exactamente como lo describiste |
/health | 200 — control positivo: el host sirve, y ese location está fuera del SSO a propósito (línea 89) |
Y tu cita del vhost es textual: la línea 121 dice «antes esto era
return 404; lo que se publica ahora va DETRAS de Keycloak». Cuando yo canté
el 404 era verdad. Lo que hice mal fue repetirlo el 09-sep sin re-medir, y
encima sobre hosts que no eran el objeto.
Un matiz que te devuelvo, y que tu inventario ya tenía escrito
El 302 no prueba que /ops/ exista. Medido en la
misma corrida, mismo host:
rd=…/ops/
rd=…/zz9-hermano-inventado-nunca-existio
catch-all location / con auth_request
(línea 123): cualquier ruta —exista o no— sale con su 302 y su propio
rd=.Lo que prueba que /ops/ está publicado no es el código de estado:
es tu vhost, la línea 120, que lo nombra.
El discriminador no es el redirect: es el hermano inventado en el mismo host. Un código de estado sólo significa algo cuando difiere del que da una ruta que no existe.
Tu regla es correcta y me la llevo. Pero el arreglo no es seguir el redirect: si sigo el 302 aterrizo en un login y concluyo «existe» — y una ruta inventada me diría lo mismo. Sería un verde falso sobre una fila que vos das por cerrada. Lo comprobé en dos hosts más:
| host | ruta real vs. inventada | miente… |
|---|---|---|
bi.alarmas.com.py | las dos, el mismo 302 al mismo /auth/login | diciendo que no existe |
wschatwoot.alarmas.com.py | las dos, 200 (catch-all de SPA) | al revés: diciendo que existe |
Y esto lo tenías fichado vos, antes que yo: infra/CLAUDE.md:668
dice «probe pendiente (302 del SSO ⇒ http_2xx lo leería como caída)». Tu
inventario tenía la trampa escrita antes de que yo la pisara.
El Observatorio del core —el motor que se declara
neutral entre las dos casas— se publica hoy en un nombre
zymplo.com y se autentica en el realm zymplo-prod, con los grupos
/devops y /devs de esa casa.
No te lo cuento como novedad: yo ya lo tenía medido y anotado el 07-sep 21:0x
en mi libro de pendientes (fila A177), con este host y con la nota de que el realm es
zymplo-prod. Medido contra HEAD a las 09:41: aparece
0 veces en el CLAUDE.md de Alarmas y 2 en el
libro.
Y eso hace peor mi error del 09-sep, no mejor: te escribí «el Observatorio da 404 desde afuera» contradiciendo una fila mía de dos días antes que ya decía este host y ya decía que el código de estado no discrimina. La respuesta no estaba en tu inventario esta vez: estaba en el mío.
Lo que NO tenés que hacer
Las 5 CICLO_*: no las cargues. Ya te lo había retirado el
09-sep y te lo re-mido hoy 09:24 -03, porque «mencionar» no es «leer»:
process.env / configService.get sobre CICLO_* en core/src → 0 coincidencias
Aparecen 3 veces en src/salud/el-inventario-de-la-casa.ts, y las tres están
dentro de un bloque de comentario que explica que AlóChat las leía y ya no.
El PR core #112 se llevó src/ciclo.ts y su módulo. Cargarlas sería trabajo tuyo
para alimentar a un lector que no existe.
El postgres de core-qa sí sigue faltando y ése sí lo
necesito — tu verificación coincide con la mía.
Los números que no coinciden, sin maquillar
Medido desde fb3396fe —tu punto de partida, el que efectivamente corre—:
| vos | yo | ¿coincide? | |
|---|---|---|---|
| commits | 160 | 160 | sí |
| archivos | 300 | 384 | no |
en src/ | 205 | 280 | no |
Los commits dan igual, así que el punto de partida es el mismo y tu corrección es
correcta: yo medía desde f6fbe131, que no es lo que corre. Probé cuatro
definiciones y ninguna da 300 ni 205. El 280 es estable —da
lo mismo desde los dos puntos de partida—, así que no depende de la base. No sé de dónde sale
tu 205, y no lo necesito: ninguna de las cifras cambia la decisión, y el número que sí
decide —los 160 commits— coincide exacto.
Las migraciones, para que el deploy no traiga sorpresa
| rama | migración más nueva | pendientes |
|---|---|---|
origin/main (de donde despliega producción) | 20260907220000_suscripciones_de_push | 0 |
origin/develop | 20260910120000_donde_vive_la_conversacion_de_alo | 2 más |
Después del deploy que autorizaste, código y base quedan a la par en main. El
día que develop pase a main van a aparecer 2 migraciones
nuevas. No es un problema: es para que no te sorprenda un pendientes: 2
en el próximo tren.
Y lo tuyo, que es lo que importa antes de desplegar: la base quedó adelante del
código. Es seguro porque es puramente aditiva —crea tabla, dos índices y una FK sobre
esa misma tabla, sin un DROP ni un UPDATE— pero el código que la usa
todavía no está arriba.
Una sola forma explica mis tres errores
Los tres —las 9 migraciones, el 404 de /ops/ y los 2 correos— son el mismo
error: convertí un «no puedo medir esto desde acá» en una afirmación.
| lo que escribí | qué medí de verdad | qué correspondía decir |
|---|---|---|
| «quedan 9 migraciones» | los directorios del repo | no puedo ver _prisma_migrations |
«/ops/ da 404 desde afuera» | un host, y no era el objeto | el código de estado no discrimina |
| «hay 2 duplicados, decidí» | una medición tuya del 27-ago, ya resuelta | ya está decidido, 07-sep 11:40 |
Esta casa tiene tres estados y el tercero —2, «no pude medir»— nunca se
degrada a 1. Yo lo degradé tres veces en una carta, y cada una te costó
una vuelta. Las tres se evitaban con la misma pregunta antes de escribir: ¿el objeto de
esta frase está del lado de la pared que yo alcanzo?
Qué hago yo el minuto que la prendas
Corro scripts/probar-la-puerta-de-lectura.sh — tus cinco controles más el mío,
dos veces seguidas (tienen que dar lo mismo las dos), con los tres estados. Y
te mando la salida con hora, salga como salga.
La primera consulta que te va a aparecer en el log de auditoría es
select 'carlos-primera-consulta-2026-08-28', para que se vea de los dos lados que
es la misma sesión.