Commit Graph
459 Commits
Author SHA1 Message Date
Gitea CI 7f56a6ad2e ci: update roswell-corpus image to d8154523 [skip ci] 2026-07-28 12:28:32 +00:00
Gitea CI 6c9c529986 ci: update roswell-corpus image to 8181cca6 [skip ci] 2026-07-28 12:20:39 +00:00
Gitea CI 878ae4855a ci: update roswell-corpus image to 4b02af00 [skip ci] 2026-07-28 12:01:03 +00:00
Gitea CI b66deb9766 ci: update researchowl image to a43bbc75 [skip ci] 2026-07-28 05:39:50 +00:00
Gitea CI 939b535134 ci: update roswell-corpus image to 4b757c08 [skip ci] 2026-07-28 05:39:44 +00:00
chemavx 58cf9cdf8a roswell: volver a FEED_CHECK_HOURS=24, el atraso está drenado
El pase de las 04:58 UTC dejó todos los feeds a 0 pendientes, así que la
bajada temporal a 3 h del commit 134449e ya no hace falta. En régimen
normal entra ~1 episodio al día: revisar cada 3 h solo repetiría la
descarga del RSS ocho veces para no encontrar nada.

El comentario deja de ser un aviso pendiente y pasa a explicar por qué 24
es lo correcto y cuándo conviene volver a bajarlo.
2026-07-28 05:37:23 +00:00
chemavxandClaude Opus 5 d9f6a61c6a backup: quitar el --exclude de reddit-intel, ya no protege nada
Las copias remotas se purgaron a mano el 2026-07-27 (8 objetos en Mega, 3 en
B2, con --mega-hard-delete y --b2-hard-delete para que no se queden en papelera
ni como versiones ocultas). Sin subdirectorio remoto que preservar, el exclude
solo era ruido en la orden.

Verificado: el ConfigMap parsea y los cuatro scripts pasan `sh -n`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 20:17:48 +00:00
chemavxandClaude Opus 5 b19bb4d414 auditoría: quitar la excepción de reddit-intel
Los manifiestos que amparaba ya no existen, así que la excepción solo podía
envejecer. La auditoría queda en 0 hallazgos nuevos y 4 esperados (antes 13:
los otros 8 eran precisamente reddit-intel inactivo).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 20:12:45 +00:00
chemavxandClaude Opus 5 6f8d699ac0 backup: retirar reddit-intel del sistema de copias
El servicio se decomisó (Reddit denegó el acceso a la API), así que su CronJob
de n97 estaría empaquetando cada noche un directorio que ya nadie escribe, y
backup-verify seguiría exigiendo una copia fresca que nunca va a llegar: en
tres días habría empezado a sumar ERRORS y a disparar la alerta por una avería
que no existe.

Se van el CronJob, el script reddit-intel.sh y su comprobación en verify.sh.
El `--exclude /reddit-intel/**` de rclone-mega SE QUEDA a propósito: sync es un
espejo exacto y quitarlo borraría de un tirón las copias que aún viven en Mega.
Se retirará cuando se decida purgar ese remoto a mano.

Verificado: el ConfigMap sigue parseando y los cuatro scripts pasan `sh -n`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 20:11:38 +00:00
chemavxandClaude Opus 5 e4e387d360 reddit-intel: retirar los manifiestos, el proyecto queda decomisado
Reddit nos ha denegado el acceso a la API —la misma respuesta que a
roswell-corpus—, así que la promoción a k3s que describía PROMOTION.md no va a
ocurrir nunca. Estos manifiestos nunca llegaron a aplicarse (el app-of-apps no
es recursivo y no había Application), así que quitarlos no cambia nada en el
clúster; el namespace vacío se ha borrado a mano.

El contenedor de n97 se paró el 2026-07-27 tras once días devolviendo 401, con
la base de datos vacía: nunca registró un solo post. Copia final en
~/decommissioned/reddit-intel-final-2026-07-27.tar.gz y el código sigue en su
repo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 20:09:40 +00:00
chemavxandClaude Opus 5 134449e6dc roswell: revisar feeds cada 3h mientras drena el atraso (TEMPORAL)
45 episodios pendientes = 33,8 h de audio ≈ 20 h de CPU, pero a una pasada
cada 24 h con tope de 5 el pod transcribía ~4 h y descansaba ~20: el atraso
tardaba 5 días. Con 3 h encadena pasadas y se va en algo más de un día.

No cambia el pico de memoria (sigue transcribiendo de una en una, y el
troceado en ventanas ya lo acotó). Revertir a 24 al terminar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 20:44:45 +00:00
chemavxandClaude Opus 5 e4b1d28da7 roswell: explicar por qué 8Gi ya no es una cifra perseguida
El límite subió 4→6→8Gi persiguiendo episodios largos porque el pico de
whisper escalaba con la duración. Con el troceado en ventanas (roswell-corpus
c869b61) el pico dejó de depender del episodio, así que 8Gi pasa de ser el
siguiente escalón a tener margen de sobra. Se deja escrito para que nadie lo
suba por inercia sin mirar antes si el troceado sigue vivo.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-26 20:38:21 +00:00
Gitea CI c75d6d5491 ci: update roswell-corpus image to 993f54b0 [skip ci] 2026-07-26 20:33:17 +00:00
Gitea CI b6616a6fa7 ci: update roswell-corpus image to 74731e14 [skip ci] 2026-07-26 15:48:24 +00:00
chemavxandClaude Opus 5 4adbcbd4af vaultwarden y gitea pasan a ArgoCD
Los dos estaban en git sin ninguna Application que los gestionara: editar el
repo no desplegaba nada. De ahi que un pin de imagen de abril nunca llegara
al cluster y gitea se subiera solo de 1.25.5 a 1.27.0, y que vaultwarden se
parcheara con kubectl replace. A partir de ahora manda git.

Verificado en frio antes de adoptar: los 17 manifiestos coincidian campo a
campo con lo vivo, asi que la adopcion no revierte nada. Con
ServerSideApply/ServerSideDiff, igual que el kube-prometheus-stack: estos
objetos arrastran un last-applied viejo y el merge clasico intentaria borrar
lo que ese blob declaraba y git ya no; en un PVC eso es un campo inmutable y
el sync falla.

Limpieza que iba en el mismo paquete:

- Fuera la basura de export de 5 ficheros: last-applied-configuration, la
  revision del Deployment y las anotaciones que pone el provisionador en los
  PVC (bind-completed, selected-node...). Es lo que dejaba los recursos
  OutOfSync para siempre. Verificado que el resto del manifiesto no cambia.
- Fuera serviceaccount-default y kube-root-ca.crt de los dos: los crea k8s
  solo en cada namespace.
- RESCATADO configmap-gitea-runner-config: config del act_runner escrita a
  mano en abril, montada por el runner y que NO estaba en git. Reconstruir
  gitea desde el repo levantaba el runner sin configuracion.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:42:38 +00:00
chemavxandClaude Opus 5 9719a6179a monitoring: avisar de que el alerting no se recarga solo
Las dos alertas de B2 llevaban desde esta manana en el ConfigMap y MUERTAS:
Grafana lee el provisioning de alerting solo al arrancar y el pod era del
dia 22. El sidecar de kiwigrid recarga dashboards y datasources, pero el
alerting entra por extraConfigmapMounts y ese camino no lo toca nadie.

Reiniciado Grafana: 7 reglas cargadas -> 9. Queda el aviso en la cabecera
del fichero, con el comando de reinicio y el de comprobacion contra la API,
que es la unica fuente de verdad aqui (el ConfigMap mentia).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:27:04 +00:00
chemavxandClaude Opus 5 64d306d1e3 monitoring: fuera de git los 49 manifiestos que no escribimos nosotros
43 eran el render del kube-prometheus-stack, volcados con kubectl get -o yaml
y sin que nadie los aplicara: decoracion que solo servia para creerse que git
describia el cluster. Ahora los genera el chart desde la Application de la
Fase 2, que es su unico dueño.

Los 6 restantes los fabrica un controlador y en git no pintan nada:
rulefiles-0 (154KB que genera el prometheus-operator desde los
PrometheusRule), el StatefulSet, el service prometheus-operated y el PVC de
datos (los tres nacen del CR Prometheus / su volumeClaimTemplate), mas
serviceaccount-default y kube-root-ca.crt, que k8s crea solo en cada
namespace.

Ninguno estaba en la lista blanca del app de monitoring, asi que borrarlos de
git no toca el cluster. Clasificados uno a uno antes de borrar (render del
chart vs ownerReferences vivas), no a bulto.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:22:27 +00:00
chemavxandClaude Opus 5 40205eddc3 monitoring: Fase 2, el kube-prometheus-stack pasa a ArgoCD
El chart era un release Helm gestionado a mano (helm upgrade) desde abril.
Ahora lo gestiona su propia Application con source Helm, version clavada a
83.2.0 (la que ya corria: esto es una adopcion, no una actualizacion).

Los valores se mudan de values-kube-prometheus-stack.yaml al valuesObject de
la Application, con sus comentarios: ahi esta el historial de los retoques a
mano que se recuperaron en julio (alerting, SSO de Authentik, token de
Telegram), y perderlo seria perder el mapa. Inline y no multi-source $values
porque ese montaje se auto-podo la Application de infisical el 2026-06-18, y
tener los valores en dos sitios es la deriva que veniamos a matar.

La adopcion se verifico ANTES de activar el auto-sync: aplicada sin
syncPolicy, ArgoCD daba 96/97 Synced sin haber sincronizado nunca. El que
faltaba era el Deployment de Grafana, y no por deriva: el render del subchart
no declara managed-by: Helm ni el restartedAt del podTemplate, asi que el
merge a 3 bandas del cliente los habria BORRADO (reinicio de Grafana + Helm
perdiendo la propiedad del objeto). Con ServerSideApply/ServerSideDiff ArgoCD
solo toca lo que declara: 97/97 Synced, cero operaciones de sync, cero pods
reiniciados. selfHeal probado con replicas 1->2 en kube-state-metrics,
revertido en 11s.

skipCrds: true a proposito: ArgoCD pasa --include-crds por defecto y los 10
CRD de monitoring.coreos.com no los gestiona nadie hoy. Contrapartida
documentada en el fichero: al subir de chart hay que aplicarlos a mano.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 20:22:27 +00:00
chemavxandClaude Opus 4.8 e26b8b95ad monitoring: quitar last-applied-configuration de los manifiestos propios
Basura del volcado con kubectl get -o yaml. Al estar versionada se muerde la
cola: ArgoCD la aplica, k8s genera una nueva que ya incluye el tracking-id, y
git y cluster no coinciden nunca -> los 4 recursos se quedaban OutOfSync para
siempre. Verificado que el resto del manifiesto queda byte a byte igual.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:40:01 +00:00
chemavxandClaude Opus 4.8 9d903775ac monitoring: Fase 1 bajo ArgoCD (solo los manifiestos propios)
Hasta ahora las alertas de Grafana se aplicaban a mano con kubectl apply,
asi que dependian de que alguien se acordara (se vio hoy al anadir las de B2).

Lista blanca explicita en vez del directorio entero: monitoring/ mezcla tres
duenos y solo uno es nuestro. Fuera quedan los ~40 renderizados del chart
kube-prometheus-stack (release Helm viva -> dos duenos del mismo objeto) y los
generados por controladores (rulefiles-0 del prometheus-operator, etc).
Adoptar el chart via source Helm queda como Fase 2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:36:13 +00:00
chemavxandClaude Opus 4.8 d45e0a7cad monitoring: alertas de cuota y versiones ocultas de B2
Gemelas de las de MEGA: avisan si B2 factura >80% de los 10GB gratis
(b2_billed_bytes) o si las versiones ocultas pasan de 2GB (b2_hidden_bytes,
lo que indicaria que algun borrado no usa --b2-hard-delete).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:17:14 +00:00
chemavxandClaude Opus 4.8 dab1a72e1c backup-system: --b2-hard-delete + exporter de uso real de B2
B2 tiene el mismo fallo que la papelera de MEGA con otro nombre: al
borrar/espejar no elimina, oculta la version vieja y la sigue facturando.
Las versiones ocultas llegaron al ~88% del tramo gratis de 10GB el
2026-07-25 (8.78 GiB facturados vs 1.75 GiB visibles).

- --b2-hard-delete en el sync de crown-jewels y en el delete de reddit-intel
- mega-quota-exporter ahora tambien exporta b2_visible/billed/hidden_bytes

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 15:17:14 +00:00
chemavxandClaude Opus 5 c33913e481 monitoring: alertar sobre la cuota y la papelera de MEGA
El incidente del 2026-07-25 (MEGA lleno -> dos CronJobs de backup caídos)
solo se detectó por el síntoma. La causa llevaba meses creciendo sin que
nada la mirase.

- backup-system/mega-quota-exporter: Deployment que sondea `rclone about`
  y `rclone size` cada 15 min y los sirve a Prometheus vía ServiceMonitor.
  mega_trash_bytes (usado - visible) es la métrica que habría cazado esto.
- monitoring: dos reglas de Grafana, cuota > 80% y papelera > 2GB.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 08:33:29 +00:00
chemavxandClaude Opus 5 cd30504eeb backup-system: --mega-hard-delete en las rotaciones a MEGA
La papelera de MEGA cuenta cuota. Los `rclone sync`/`rclone delete` de
rotación diaria enviaban ahí las copias antiguas, acumulando 16.6 GiB de
basura hasta llenar los 20 GiB de la cuenta. El 2026-07-25 eso tumbó los
jobs rclone-mega-backup y reddit-intel-backup con "Request over quota".

Papelera vaciada a mano (375 objetos); este flag evita que se repita.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-25 08:11:36 +00:00
chemavxandClaude Fable 5 f6b027dd5e roswell: subir límite de memoria a 8Gi (OOMKill transcribiendo episodio de 80 min)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 16:01:23 +00:00
chemavxandClaude Fable 5 8c6a061829 blogs: servir key de IndexNow en la raíz vía replacePath de Traefik
La key de IndexNow solo autoriza URLs bajo su directorio, así que debe
vivir en /{key}.txt. Ghost no sirve ficheros arbitrarios en la raíz: el
middleware reescribe la ruta al fichero real en content/files/indexnow/
(PVC de Ghost, colocado con kubectl cp). El envío de URLs lo hace
~/seo-tools/indexnow_watch.py (timer horario en el master).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 09:23:15 +00:00
chemavxandClaude Opus 4.8 ef75792c18 n8n: vigilante contra reversiones de BD (alerta Telegram)
CronJob externo a n8n que lee la SQLite en solo-lectura por el hostPath y avisa
si el workflow del autopost desaparece (firma de la reversión del 2026-07-16) o
si el total de workflows se desploma. La reversión SUBE el nº de activos, así que
un umbral de activos no la cazaría; el disparador es la desaparición del autopost.
Cada 6h. Reutiliza telegram-notify-infisical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 16:45:20 +00:00
Gitea CI a11e2f9a5f ci: update roswell-corpus image to 75eb5ac5 [skip ci] 2026-07-23 10:27:25 +00:00
Gitea CI 93d32a6600 ci: update roswell-corpus image to f24350d1 [skip ci] 2026-07-23 09:55:25 +00:00
Gitea CI 00f1f61c5c ci: update researchowl image to 96538983 [skip ci] 2026-07-23 09:55:15 +00:00
chemavxandClaude Opus 4.8 b40790155e ollama: el arranque descarga qwen2.5:7b + bge-m3 (los que se usan), no qwen2.5:3b
Borrados qwen2.5:3b y nomic-embed-text (sin consumidores tras subir a 7b/bge-m3).
El script de arranque solo traía 3b: tras perder el PVC, un reinicio dejaba a
researchowl y roswell sin el modelo de embeddings. Ahora trae los dos reales.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 09:47:52 +00:00
chemavxandClaude Opus 4.8 5a05f26fd5 researchowl: generación 3B→7B y embeddings nomic→bge-m3 (iGPU activa)
Los 3990+ chunks existentes se re-embebieron a 1024d in situ antes de este
cambio; sin eso el coseno daría 0.0 por dimensión distinta y rompería la
recuperación. bge-m3 alinea researchowl con roswell (mismo modelo, multilingüe).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 09:37:49 +00:00
Gitea CI fae3a3c7f6 ci: update roswell-corpus image to 94aa8e87 [skip ci] 2026-07-23 08:55:46 +00:00
chemavxandClaude Opus 4.8 6227a8a44c ollama: el dominio del recurso es devic.es, no squat.ai
La versión actual del generic-device-plugin registra devic.es/gpu por
defecto; con squat.ai/gpu el pod se quedaba Pending.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 07:50:58 +00:00
chemavxandClaude Opus 4.8 dbfd5f7c58 ollama: cerrar exposición pública y activar la iGPU vía device plugin
- Ingress con forward-auth de Authentik (la API estaba abierta a internet)
- generic-device-plugin entrega card0+renderD128 como squat.ai/gpu con el
  cgroup de dispositivos bien configurado — lo que el hostPath nunca dio
- fuera los 3 hostPath de dispositivos (ya no hacen falta)
- OLLAMA_NUM_CTX→OLLAMA_CONTEXT_LENGTH (la real); fuera OLLAMA_METRICS (no existe)

Sonda temporal confirmó: con cgroup, Vulkan enumera la Radeon 780M (17 GiB).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 07:39:44 +00:00
chemavxandClaude Fable 5 155d4ffb13 ollama: fuera las variables ROCm, tapaban el descubrimiento de la GPU
Con HSA_OVERRIDE_GFX_VERSION y HIP_VISIBLE_DEVICES puestas, ollama
arranca avisando 'user overrode visible devices / if GPUs are not
correctly discovered, unset and try again' y solo encuentra la CPU.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:50:17 +00:00
chemavxandClaude Fable 5 2a5717e683 ollama: activa el backend Vulkan y declara el paso de la iGPU
La GPU nunca se usaba: ollama/ollama:0.20.7 no trae ROCm, asi que
HSA_OVERRIDE_GFX_VERSION/HIP_VISIBLE_DEVICES eran inertes. La imagen si
trae el backend Vulkan y el driver RADV, que cubre las iGPU AMD.

De paso se declara la deriva: todo el paso de dispositivos vivia solo en
el objeto del cluster, no en git, y sobrevivia por el merge de tres vias.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 20:47:52 +00:00
chemavxandClaude Opus 4.8 ee539e572d homarr: de una build de main a la release v1.71.0
Iba fijado por digest porque su tag "latest" no es una release sino una build
de la rama main (org.opencontainers.image.version=main). Al mirarlo de cerca,
ese digest resultó ser EXACTAMENTE v1.70.0: el commit 4b351bb es su propio
release commit. O sea, ya corría el código de una release, sólo que sin poder
saberlo desde el manifiesto.

Se sube a v1.71.0 (17-jul), una menor por delante, en vez de quedarse en la
v1.70.0 equivalente: es la última publicada y arregla justo algo de base de
datos — "restore widget_secret migration journal entries accidentally removed".
Sin cambios rompedores en las notas.

Homarr guarda una SQLite en homarr-db-pvc, así que esto aplica migraciones y no
es un pin inocuo como los otros seis. Copia consistente antes de tocar, con el
pod parado para no copiar una escritura a medias:
/data/backups/backups/homarr_pre-v1.71.0_2026-07-22_10-02-23.tar.gz
(además del backup diario, que ya cubría homarr).

Verificado tras arrancar: "Migration complete" en el log, 200 en
home.chemavx.xyz, OIDC contra Authentik intacto, y los datos en su sitio —
consultada la SQLite en solo lectura: el tablero Dashboard_ChemaVX con sus 7
items, 7 apps, 1 integración y los 2 usuarios.

Los dos "Board not found" del log llevan la hora exacta de mis curl anónimos:
un visitante sin sesión no tiene tablero. No es un fallo.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 10:06:55 +00:00
chemavxandClaude Opus 4.8 33f429e412 monitoring: cierra el formulario local de Grafana
Con el SSO ya funcionando (379af6d), a la web sólo se entra por Authentik.
Ayer se dejó abierto a propósito porque el SSO acababa de estar 15 días roto en
silencio y no quería quedarme sin puerta; hoy, verificado que funciona, se
cierra.

Lo que NO cierra, y es lo que importa: la auth básica de la API sigue abierta.
De ella cuelgan los dos sidecars —que es exactamente lo que se rompió mudo el
21 y dejó de recargar dashboards y datasources— y la puerta de emergencia con
la contraseña del secret grafana-admin.

Sí desaparece la otra: el POST /login pasa a devolver 400. Comprobado antes y
después, porque era la salida que uno supondría disponible y ya no lo está.
El procedimiento de rescate queda escrito junto al valor: la API con
grafana-admin, y disable_login_form: false + helm upgrade para recuperar el
formulario en un minuto.

Verificado: SSO entra como akadmin; la auth básica entra como admin;
disableLoginForm=true y authProxyEnabled=true en /api/frontend/settings; desde
fuera sigue el 302 a Authentik; y los DOS sidecars probados con ConfigMaps
reales, con 200 OK y el dashboard de prueba llegando de verdad a Grafana (y
desapareciendo al retirar el ConfigMap). 0 errores en el arranque.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:56:48 +00:00
chemavxandClaude Opus 4.8 72395ecc0e monitoring: panel para el runner, Prometheus y Uptime Kuma
Los tres puntos ciegos que quedaban. El gitea-runner se dejó fuera del panel de
gitea a propósito, porque si cae se paran los builds pero Gitea sigue sirviendo
y pintar "gitea: Down" sería una falsa alarma; pero su caída es de las mudas y
merecía el suyo. Prometheus y Uptime Kuma se quedaron sin representar cuando el
panel "grafana" pasó a mirar sólo a su deployment en vez de a todo el namespace
monitoring (5d0078c).

Misma forma que los otros doce, con el selector acotado al workload concreto;
para Prometheus, que es un StatefulSet, la rama de deployment sale vacía y
resuelve la de statefulset. La fila de servicios estaba llena, así que se abre
una tercera y todo lo de debajo baja 4 filas de rejilla.

Verificado antes de escribirlo: las tres consultas dan 1 ahora y 0 con escasez
de réplicas simulada. Y después, los 15 estados a través del proxy de datasource
de Grafana, no consultando yo a Prometheus por otro lado.

Nota conocida, no un fallo: si kube-state-metrics cae, los 15 paneles se ponen
en rojo a la vez, porque todos dependen de sus métricas. Un tablero entero en
rojo hay que leerlo como "se ha caído el que mide", no como quince caídas.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:51:59 +00:00
chemavxandClaude Opus 4.8 7b50e4f382 tools: la auditoría de manifiestos, en un temporizador semanal
Sábados 07:00 UTC como servicio de host en chemavx-k8, fuera del cluster, igual
que seo-watch y el canario de hermes. Separado a propósito de esos dos (domingo
06:00 y lunes 05:30): si algo falla, se sabe cuál sin desenredar tres avisos de
la misma mañana.

Callado por diseño: sólo escribe a Telegram si hay hallazgos nuevos Y cambian
respecto a la semana anterior. Si la deriva sigue igual porque aún no se ha
arreglado, no insiste — un aviso semanal que siempre llega deja de leerse, que
es el fallo que se ha estado corrigiendo estos días.

Rompen el silencio cuatro cosas: deriva nueva; el repo local por detrás de
origin (se habría comparado contra ficheros viejos); excepciones que ya no se
disparan; y que el propio auditor haya fallado, porque un vigilante roto no es
un "todo bien".

Probado viéndolo hacer las cuatro, no sólo la buena, y por el camino real
(systemctl start, con el entorno del unit, no desde mi shell). Dos cosas que
salieron de probarlo:

- La primera versión avisaba de "cambios sin commitear". Es estado de trabajo
  normal y la copia local es justo la intención que hay que comparar, así que
  habría mandado ruido cada semana. Ahora eso es contexto, no motivo.
- Un error de sintaxis en el auditor hace que Python salga con código 1, EL
  MISMO que "hay deriva": el aviso habría dicho "los ficheros no coinciden" con
  el volcado de una traza. Ya no basta el código de salida, se exige además que
  el informe traiga su línea de resumen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:46:46 +00:00
chemavxandClaude Opus 4.8 07c872e847 tools: auditor de exports contra el cluster
Deja en el repo lo que hoy destapó que argocd-redis, el daemon.json de gitea y
siete imágenes llevaban meses diciendo una cosa mientras el cluster hacía otra.

Compara cada manifiesto con el objeto vivo, en las dos direcciones (el fichero
afirma algo falso / hay algo desplegado que el fichero no menciona / el objeto no
existe). Sólo mira los directorios fuera de ArgoCD, que es donde no hay nadie
comprobando: en los demás ArgoCD ya marca OutOfSync cada 3 minutos. La lista de
directorios se deduce preguntando a ArgoCD por los paths de sus Applications, así
que si mañana un namespace entra en GitOps el script se ajusta solo.

Las excepciones conocidas van en excepciones-auditoria.yaml CON su motivo, para
que lo normal sea "hallazgos NUEVOS: 0" y cualquier cosa que asome merezca una
mirada. Un informe que siempre saca ruido deja de leerse — que es justo el fallo
que se ha estado corrigiendo estos días. El script también avisa cuando una
excepción ya no se dispara: se arregló y la entrada sobra.

Probado viéndolo fallar, no sólo pasar: inyectadas a propósito una avería de cada
tipo (imagen cambiada, env borrada del fichero, objeto inexistente) y las caza
las tres saliendo con código 1. Al probarlo apareció un fallo propio: con -d
marcaba como sobrantes las excepciones de los directorios no barridos; ahora sólo
lo afirma tras un barrido completo.

No toca nada (sólo lectura), nunca compara valores de Secret —sólo nombres de
clave— y se salta lo que ignora .gitignore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:37:05 +00:00
chemavxandClaude Opus 4.8 55d43f1565 remata la auditoría: exports que mentían y restos de polymarket
Tres ficheros describían algo distinto de lo desplegado, y aplicarlos rompía
cosas. Ahora coinciden campo por campo (verificado con kubectl diff vacío).

argocd/deployment-argocd-redis.yaml decía `resources: {}` y el nodeSelector por
defecto. Lo vivo lleva el anclaje a chemavx-k8 y límites de 200m/128Mi, sin los
cuales este pod muere con Exit Code 255 tras días de uptime. El arreglo sólo
constaba en argocd-patches/redis-patch.yaml, así que el fichero que uno
aplicaría primero era justo el que deshacía el arreglo. Queda el comentario
explicando por qué el anclaje está ahí, y el parche lleva cabecera diciendo que
es parcial y que no debe compararse con el cluster.

gitea/configmap-daemon-json.yaml le faltaban dos insecure-registries que sí
están vivos (git.chemavx.xyz y gitea.gitea.svc.cluster.local:3000). Comprobado
que lo vivo es lo correcto: el buildkitd.toml declara ese registro como
http/insecure y los workflows de CI empujan ahí. Aplicar el fichero viejo habría
dejado a la CI sin poder subir imágenes.

gitea/configmap-buildkitd-config.yaml es nuevo: ese ConfigMap llevaba 90 días
vivo sin manifiesto ninguno.

Restos de polymarket, decomisado el 2026-07-17: fuera su Namespace de
cluster-wide/namespaces.yaml (11 quedan) y borrado el directorio
polymarket-bot/, que resultó no estar en git siquiera — sus dos ficheros los
descarta .gitignore por autogenerados, así que eran basura local del export de
abril. De paso, namespaces.yaml pierde las anotaciones de último apply y gana
una cabecera: no es la lista completa del cluster (16 apps de ArgoCD crean su
propio namespace) y aplicarlo no lo reconstruye.

La auditoría baja de 26 hallazgos a 13, y ninguno de los 13 es deriva real:
8 son reddit-intel, inactivo a propósito (ver reddit-intel/PROMOTION.md), 3 son
el fichero de parche parcial, uno es un `group: ''` equivalente a estar ausente
y el último era una anotación del controlador, ya fuera del export.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:24:31 +00:00
chemavxandClaude Opus 4.8 b8b53467be fija las imágenes flotantes de los namespaces manuales
El commit 22ae5d7 (15-abr) fijó estos tags EN GIT pero nunca se aplicó al
cluster: estos namespaces no están en ArgoCD, así que un cambio en git no llega
solo. Tres meses después git decía 1.25.5 y gitea corría 1.27.0, subido en algún
reinicio sin que nadie lo decidiera. Ahora se fija a los dos lados.

  gitea            gitea/gitea:latest        -> gitea/gitea:1.27.0
  gitea (init)     busybox                   -> busybox:1.38.0
  gitea-runner     gitea/act_runner:latest   -> gitea/act_runner:0.6.1
  gitea-runner     docker:24-dind            -> docker:24.0.9-dind
  authentik-redis  redis:alpine              -> redis:8.8.0-alpine
  uptime-kuma      louislam/uptime-kuma:1    -> louislam/uptime-kuma:1.23.17
  homarr           homarr:latest             -> homarr@sha256:80ee593c...

Los seis primeros se fijan a la MISMA imagen que ya corría: verificado
comparando el repoDigest del tag flotante con el del tag candidato, así que el
reinicio no cambió de versión. Homarr va por digest porque su "latest" es una
build de la rama main (org.opencontainers.image.version=main): no existe un tag
de release que describa lo que corre. Moverlo a una release es otra decisión.

Trampa que casi me come, anotada para la próxima: el imageID de un pod NO
siempre es el digest del registro. Si viene como "repo@sha256:..." lo es; si es
un "sha256:..." pelado es el id local de containerd y NO se puede descargar.
Fijar uptime-kuma a ese id dio ImagePullBackOff; el pod viejo aguantó sirviendo
(maxUnavailable 0 para 1 réplica) y se revirtió sin corte. La comparación buena
es repoDigest contra repoDigest.

De paso, al regenerar gitea-runner desde lo vivo quedan documentados el volumen
buildkitd-config y su montaje, que existían en el cluster y no en git.

Verificado tras cada cambio: rollout completo, versión de la app dentro del pod,
y los cinco servicios respondiendo desde fuera (git 200, home 200, status/auth/
grafana 302 por Authentik). El runner se ha vuelto a registrar en Gitea. Sigue
sin haber pods fuera de Running y las 18 apps de ArgoCD Synced+Healthy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 09:11:10 +00:00
chemavxandClaude Opus 4.8 379af6d0cc monitoring: recupera el SSO de Grafana con Authentik
Grafana llevaba ~15 días sin SSO: ignoraba la cabecera X-authentik-username, de
modo que Authentik te dejaba pasar y Grafana te pedía credenciales igual. El
último acceso de akadmin (el usuario que crea auto_sign_up) es de hace 15 días.

El bloque [auth.proxy] estaba en el ConfigMap vivo en abril — así consta en el
export de git de aquel momento — pero el chart NUNCA lo ha generado en ninguna
de sus 7 revisiones, así que era un retoque a mano que se perdió por el camino.
Ahora va declarado en values y sobrevive a una reconstrucción.

Aparecida al arreglarlo, otra pieza del mismo tipo: la anotación
router.middlewares que engancha el forward-auth de Authentik al Ingress. Vivía
sólo por el three-way merge de Helm, igual que el montaje de alerting (0501aab)
y el token de Telegram (28f1631). Reconstruir el release habría dejado Grafana
en internet SIN autenticación de Authentik, con sólo su formulario delante.

NO se restaura disable_login_form, que sí traía la config de abril: el
formulario local se queda como puerta de emergencia, con la contraseña del
secret grafana-admin. Que el SSO fallara en silencio es justo lo que pasó aquí.

Verificado: con la cabecera, autentica como akadmin; sin ella sigue dando 401;
una petición externa a grafana.chemavx.xyz redirige (302) al authorize de
Authentik; el formulario local responde 200 y la contraseña de emergencia entra;
0 errores en el arranque. Exports regenerados: el auditor deja monitoring en 2
hallazgos, ambos conocidos (una anotación del controlador y el tag de
uptime-kuma, pendiente de decidir con el resto de tags flotantes).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 08:55:01 +00:00
chemavxandClaude Opus 4.8 af162bf34d cloudflare-ddns: desactiva IPv6, que era el 100% de los errores
Todos los fallos del log eran el mismo: cada ciclo de 5 min intentaba detectar
la IP v6 contra api.cloudflare.com/cdn-cgi/trace, agotaba los 5s de
DETECTION_TIMEOUT y terminaba en "No valid IPv6 addresses were detected".
34 de 34 errores en las últimas 400 líneas; ni uno solo de IPv4.

Aquí no hay IPv6: la única dirección v6 global de los nodos es la ULA de
Tailscale (fd7a:115c:a1e0::/48) y no hay ruta a internet por v6. IP6_PROVIDER
no estaba puesto y cae por defecto en cloudflare.trace, así que lo intentaba
igual. Ahora va explícito a "none".

No se pierde ningún registro: no hay AAAA de origen que mantener. Los AAAA que
publica theexclusionzone.com son de Cloudflare (2606:4700::/32), sintetizados
por ser proxied, y el DDNS nunca los tocó.

Importa más de lo que parece: ese ruido constante es lo que haría invisible un
fallo de verdad en el servicio del que dependen los tres dominios.

De paso, fuera del fichero dos anotaciones que no son configuración deseada
sino estado del controlador: last-applied-configuration (además describía la
config de una sola zona, anterior al alta de los dos blogs) y
deployment.kubernetes.io/revision, que el fichero fijaba en 1 cuando la viva
iba por la 8.

Verificado con kubectl diff antes de aplicar (único cambio: +IP6_PROVIDER), y
después: el arranque ya no anuncia proveedor IPv6, comprueba los 7 registros A
y lleva 0 errores.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 08:33:56 +00:00
chemavxandClaude Opus 4.8 5d0078c8e8 monitoring: los 12 estados de servicio miden el servicio, no el namespace
Los paneles heredados preguntaban "cuántos pods hay Running en el namespace".
Para n8n u ollama da igual, pero el panel llamado "grafana" contaba los 7 pods
de monitoring y el de "argocd" los suyos: se habrían quedado en verde con
Grafana muerta mientras Prometheus siguiera en pie. Lo mismo en "authentik" y
"gitea", que sumaban su Postgres, su Redis y el runner.

Ahora los 12 usan la misma forma: ¿están en pie TODOS los componentes de los
que depende el servicio?

  min((kube_deployment_status_replicas_available{SEL} >= bool 1)
      or (kube_statefulset_status_replicas_ready{SEL} >= bool 1)) or vector(0)

El selector define qué es el servicio: el namespace entero cuando el namespace
ES el servicio (argocd, authentik); acotado cuando alberga varias cosas
(grafana dentro de monitoring, researchowl/searxng); y en gitea sólo el
StatefulSet, porque si cae el runner se paran los builds pero Gitea sigue
sirviendo — pintarlo "Down" sería una falsa alarma.

Cambio invisible salvo cuando algo falla: los 12 mapean 0 -> "Down" y >=1 ->
"Running", así que en pantalla nunca se vio el número. Y además exige réplicas
*disponibles*, no pods en fase Running: un pod arrancado pero que no pasa el
readiness ya no cuenta como sano.

Verificado los 12 en verde y los 12 en rojo (simulando escasez de réplicas), y
el caso que de verdad importa: UN solo componente caído entre sanos arrastra el
panel a 0 (probado con dex-server en argocd y con el Postgres de authentik).
Después, los 12 otra vez a través del proxy de datasource de Grafana.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 08:21:03 +00:00
chemavxandClaude Opus 4.8 886fee9292 monitoring: los blogs y ResearchOwl entran en el dashboard
Faltaban justo los servicios de cara al público. Se añaden cuatro estados de
pod (ghost-en, zona-exclusion, researchowl, searxng), que completan la fila de
servicios, y una fila nueva con memoria, CPU y la antigüedad de la última copia
correcta de researchowl.db (CronJob diario de las 03:00 con integrity_check).

Los stat nuevos preguntan por kube_deployment_status_replicas_available del
deployment concreto, no por "todos los pods del namespace" como los antiguos:
esa consulta heredada hace que el panel llamado "grafana" cuente en realidad
los 7 pods de monitoring, y el de "argocd" todos los suyos. No los toco aquí.

Descartado a propósito un panel de uso de PVC: con local-path todos los
volúmenes de un nodo reportan el disco entero (ghost-en, zona-exclusion y
researchowl marcan los mismos 78 GB), así que habría mentido.

La primera versión de los cuatro stat estaba mal: "métrica{...} or vector(0)"
sobre una métrica CON etiquetas no sustituye, añade — devolvían 2 series y el
panel podía pintar un Down falso. Los paneles viejos se libran porque su sum()
deja la serie sin etiquetas. Corregido envolviendo en sum().

Verificado consulta a consulta contra Prometheus, incluido el caso "deployment
inexistente" (da 0 -> Down en rojo), y luego las 7 a través del proxy de
datasource de Grafana, no por fuera.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 08:14:07 +00:00
chemavxandClaude Opus 4.8 28f16314d8 monitoring: fuera los restos de polymarket y la datasource Alertmanager
El dashboard «ChemaVX Homelab Overview» tenía 4 paneles Infinity apuntando a
api.polymarket-bot.svc.cluster.local:8000, decomisado el 2026-07-17, más dos
stat de servicios cuyos namespaces ya no existen (polymarket-bot, open-webui)
que se veían como un 0 sospechoso en vez de como ausencia. 24 -> 17 paneles, y
la rejilla de servicios reempaquetada (arrastraba huecos de borrados viejos).

La datasource Alertmanager apuntaba a un servicio inexistente (alertmanager
está desactivado a propósito) y devolvía 500. Dos sorpresas: no la apaga
alertmanager.enabled, sino grafana.sidecar.datasources.alertmanager.enabled; y
quitarla del provisioning NO la borra de grafana.db — hace falta una directiva
deleteDatasources de un solo uso. Igual para la Infinity, creada por UI.

Auditando el Deployment contra el render aparecieron dos campos puestos a mano
que el chart no genera y que sólo seguían vivos por el three-way merge de Helm,
el mismo agujero que 0501aab: GF_INSTALL_PLUGINS (ya sin uso, retirado junto al
plugin de 48 MB) y TELEGRAM_BOT_TOKEN/CHAT_ID, que sí son críticos — el contact
point usa ${TELEGRAM_BOT_TOKEN}, así que reconstruir el release desde cero
habría dejado las alertas mudas. Declarados ya en values.

Verificado: render == desplegado salvo defaults del API server; Grafana
reiniciada y arrancando sin errores, 1 datasource, 25 dashboards, 5 reglas y el
contact point de Telegram con token resuelto.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 08:02:48 +00:00
chemavxandClaude Opus 4.8 f5bcb76738 monitoring: la contraseña de Grafana en git no abría nada
Los sidecars de dashboards y datasources autentican contra la API de Grafana con
las credenciales del secret del chart, y recibían 401 en cada recarga. El valor
en values era adminPassword: admin, que no es la contraseña de nadie.

Lo que apareció al investigarlo cambia el diagnóstico que traíamos anotado. La
cuenta admin local se configuró el 26-abr y NO se ha usado desde entonces
(last_seen_at y updated, ambos de esa fecha): no hubo ninguna rotación reciente
en la UI. El acceso real a Grafana es por Authentik — de ahí el usuario akadmin
—, así que la contraseña de esa cuenta no la sabía nadie y era irrecuperable,
por ser un hash bcrypt.

Conservar la contraseña existente era por tanto imposible: no había ninguna que
conservar. Se genera una aleatoria de 28 caracteres, se escribe en el Secret
grafana-admin y se resetea la de Grafana para que coincidan (leyendo del secret
por stdin: nunca pasa por argv ni se imprime). La cuenta queda como acceso de
emergencia; nadie tiene que memorizarla. Para leerla:

  kubectl get secret grafana-admin -n monitoring \
    -o jsonpath='{.data.admin-password}' | base64 -d; echo

El secret se crea FUERA del chart y se referencia con grafana.admin.existingSecret,
en vez de parchear el secret que genera Helm: con adminPassword en values, el
siguiente upgrade lo habría revertido a "admin". Es la misma trampa que el
montaje del alerting de hace un rato, y por eso adminPassword desaparece de git.

Verificado por el camino real y no por deducción: creando un ConfigMap de prueba
con la etiqueta grafana_dashboard, el sidecar escribe el fichero y la recarga
responde 200 OK "Dashboards config reloaded" — antes 401. ConfigMap de prueba
eliminado. Grafana sano tras el rollout (database ok, 12.4.2), las 5 reglas de
alerta provisionadas siguen activas, 25 dashboards y los 3 ficheros de alerting
en su sitio.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 17:24:02 +00:00