Las tres secretKeyRef van optional: true: mientras no existan en Infisical
el pod arranca igual y el comando contesta 'no configurado'. Sin optional,
una clave que aún no está deja el Deployment en CreateContainerConfigError
y tira el bot entero por una función sin estrenar.
YOUTUBE_PRIVACY=private documenta el candado: los vídeos por API de un
proyecto sin auditar quedan privados los pidas como los pidas.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Renovate seguia escaneandolo 6 veces al dia y empujando ramas a un repo
muerto: renovate/uvicorn-0.x se actualizo el 2-ago, 16 dias despues del
decomiso. Misma familia que el bloque de Polymarket del Resumen Diario:
decomisar un servicio no lo saca de las cosas que lo miran.
Quedan 11 ramas renovate/* en el repo; se limpian aparte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El primer render real murio OOMKilled con el limite de 1Gi que salia de medir en
la maquina de desarrollo. El host lleva ffmpeg 4.4.2 y la imagen 7.1.5, y 7.0
reescribio la transcodificacion sobre un planificador con colas de fotogramas
decodificados entre componentes: pasarle el WAV como segunda entrada dejaba al
decodificador de video correr por delante y llenar una cola con fotogramas de
6,2 MB. Arreglado partiendo el encode en dos pasos (repo shortsmith), y aqui van
las cifras nuevas, medidas dentro del pod.
requests 768Mi (pico anon 640 MB), limits 1536Mi. El limite no es 1Gi por el
caso techo de 180 s: ahi el pico era 940 MB de 1024 y sobrevivia solo porque
quedaba cache reclamable que soltar. A 1.5Gi memory.events da max 0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Renderizador determinista JSON->MP4 para Shorts. API interna sin Ingress, en
shortsmith-svc:8080; la cola es por proceso y la SQLite va en RWO, de ahí la
replica unica con estrategia Recreate.
Los recursos van medidos, no estimados, y la tabla con las cifras queda en el
propio manifiesto: el pico son 522 MB de anon y lo pone entero el encode, no el
render. Ojo al comentario sobre por que los workers salen del limite de CPU y no
del request.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Un barrido hecho con cp dijo que un cambio recien escrito no estaba, media
hora despues de escribirlo. La copia no tiene los ultimos commits y no da
ningun error.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El GET de comprobacion no miraba su propio codigo HTTP. Un 403 devuelve un
objeto Status, que parsea como JSON perfectamente y luego revienta en
dep.spec.replicas ("Cannot read properties of undefined (reading 'replicas')").
El aviso resultante decia "reinicio fallido, intervencion manual necesaria"
cuando el reinicio habia ido BIEN y lo unico roto era la verificacion.
Falso negativo, mucho menos grave que el falso positivo de ayer -- pero te
levanta de la cama para nada, y estando Jose fuera eso importa.
Ahora el nodo marca verificable:false cuando no ha podido comprobar (HTTP no
2xx, cuerpo ilegible o error de red) y el aviso lo dice con esas palabras,
con el codigo de la comprobacion aparte del codigo del PATCH.
Reproducido en produccion: POST legitimo a las 08:43:22 (PATCH ok, restartedAt
cambia), permiso retirado a los 15 s, GET a los 60 s con 403. Ejecucion de
60,394 s, o sea el camino completo.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Anade tambien como leer por que rama fue una ejecucion cuando
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none ya la ha vaciado: por la duracion.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tres agujeros que encontro una review adversarial de Codex sobre el trabajo
de ayer. Los tres verificados contra el fichero antes de tocar nada.
1. El webhook publico fallaba en ABIERTO. Con `optional: true` y un
`if (secreto && ...)`, la ausencia del secreto equivalia a autorizacion:
cualquier POST con heartbeat.status=0 y un monitor.name del mapa reiniciaba
n8n, Gitea, ArgoCD, Vaultwarden o los blogs. El argumento para dejarlo asi
era "que un despiste no te deje sin avisos", y era falso: el aviso de caida
lo manda Kuma por su notificacion 1, directa, no por este webhook.
Ahora el secreto es obligatorio (sin `optional`) y la puerta rechaza si
falta. Secreto ausente da error ruidoso; cabecera que no casa, vacio.
2. El ClusterRole permitia patch sobre CUALQUIER deployment de 10 namespaces,
y la SA va montada en el pod entero de n8n: cualquier workflow con un nodo
Code heredaba eso. Ahora son Roles por namespace con resourceNames sobre
los 12 workloads exactos del SERVICE_MAP.
3. El flujo daba exito aunque el PATCH fallara. `restartOk` se calculaba y se
tiraba: `K8s API Check Status` construia un objeto nuevo sin el, y el IF
final solo miraba `statusOk`. Un 403 sobre un servicio ya sano mandaba
"reiniciado correctamente". Ahora hay rama de fallo tras el PATCH y el
exito exige observedGeneration >= la generation que devolvio el PATCH.
Se compara con >= porque selfHeal de ArgoCD revierte la anotacion y vuelve
a subir la generation; con == daria un fallo falso.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Este directorio es el WorkingDirectory de hermes-bot, o sea el sitio donde un
agente lee el contexto del cluster, y no tenia ninguno.
Se ha notado hoy: tras poner EXECUTIONS_DATA_SAVE_ON_SUCCESS=none en n8n, las
ejecuciones exitosas quedan como status='running' con deletedAt puesta hasta que
pasa el barrido. Hermes las vio, las tomo por ejecuciones colgadas y se gasto un
turno entero de quince minutos diagnosticando una averia inexistente en un
workflow que corria clavado cada cinco minutos.
Solo van aqui las cosas que enganan: lo que parece roto y no lo esta, y lo que
parece inofensivo y no lo es. El resto sigue en el skill k8s-infra.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
/webhook/uptime-kuma-restart es publico y sin autenticar. Mientras no reiniciaba
nada daba igual; desde hoy reinicia doce servicios, los dos blogs incluidos, asi
que cualquiera que diera con la ruta podia zarandear el cluster.
Uptime Kuma manda ahora la cabecera X-Kuma-Token (webhookAdditionalHeaders de la
notificacion "n8n Auto-Restart") y el primer nodo Code la compara con
KUMA_WEBHOOK_TOKEN antes de hacer nada.
La comprobacion falla en ABIERTO si el secreto no esta configurado en n8n: un
despiste de configuracion degrada al comportamiento de antes en vez de dejar de
avisar en silencio, que es el fallo que no se ve venir. Si esta configurado y no
coincide, se devuelve vacio: ni reinicio, ni Telegram, ni fila de error que se
pueda llenar a base de peticiones.
El secret n8n-kuma-webhook se crea con kubectl y NO esta en git; el env va con
optional: true para que la ausencia no impida arrancar el pod.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Una Application de tipo directorio lee *.yaml, *.yml y *.json indistintamente,
asi que la copia del workflow que acabo de meter en n8n/ entraba como si fuera
un manifiesto y dejaba la app en ComparisonError: "Object 'Kind' is missing".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Los workflows viven solo en la SQLite, que ya se revirtio sola una vez (16-jul,
a un snapshot de abril) y se llevo por delante mes y medio de trabajo. Este en
concreto es el que reacciona a las caidas, asi que conviene tenerlo fuera.
Ya no hay nada secreto que ocultar: los tres nodos de Telegram leen el token de
$env.TELEGRAM_BOT_TOKEN en vez de llevarlo incrustado.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Ghost EN y ES son lo unico del mapa que da la cara al publico y lo que peor se
lleva con dos semanas sin nadie mirando. Entran en el SERVICE_MAP con la clave
igual al nombre exacto del monitor de Kuma.
El monitor "www.zonadeexclusion.com (301->apex)" se queda fuera a proposito:
que ese 301 falle es cosa de Traefik o del DNS, no de Ghost.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
El workflow "Uptime Kuma -> K8s Auto-Restart" lleva desde siempre sin poder
reiniciar nada: hacia el PATCH con el token de la ServiceAccount "default" del
namespace, que no tiene ni un permiso, y el API contestaba 403. Se iba entonces
por la rama de "reinicio fallido" y mandaba el aviso por Telegram -- que se
enviaba bien, y por eso los 23 "exitos" de workflow_statistics. La integracion
parecia viva y no lo estaba.
SA propia (n8n-restarter) en vez de la default, y un RoleBinding por namespace
en lugar de un ClusterRoleBinding: el webhook que dispara esto es publico y sin
autenticar, asi que el alcance se limita al SERVICE_MAP del workflow.
El ClusterRole incluye get ademas de patch porque el workflow relee el objeto a
los 60 s para comprobar readyReplicas (sin get: "Cannot read properties of
undefined"), y statefulsets ademas de deployments porque Gitea es lo primero.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tres workflows ACTIVOS fallaban con "Module 'https' is disallowed" sin que
saltara ninguna alerta, porque estaba NODE_FUNCTION_ALLOW_EXTERNAL (paquetes
npm) pero no NODE_FUNCTION_ALLOW_BUILTIN (modulos internos):
- Uptime Kuma -> K8s Auto-Restart ultimo exito 2026-07-23 07:51
- TLS Certs -> Alerta Telegram ultimo exito 2026-04-13
- Resumen Diario - Metricas K8s 30 fallos, 0 exitos
Es decir: el auto-reinicio que dispara Uptime Kuma llevaba una semana
muerto. Se rompio al reiniciarse el pod la tarde del 23-jul, cuando el task
runner empezo a bloquear require() de modulos internos.
Lista explicita (fs,http,https) en vez de '*': es justo lo que piden los
nodos Code de esos workflows, y asi un modulo nuevo falla a la vista en vez
de estar concedido de antemano.
La poda por edad ya estaba activa de serie (n8n 2.15.1, prune=true, 336 h):
los IDs de ejecucion empiezan en 22.634 con 7.052 filas, o sea que ya habia
borrado ~22.600 el solo. El tar diario crecia 0,4 MB/dia y estaba llegando a
meseta, asi que no habia desbocamiento: la premisa era falsa.
El problema real era la composicion. De 322 MB de execution_data, 246 MB
(76%) eran las 3.436 ejecuciones EXITOSAS de 'Real Madrid RSS -> Twitter',
que corre cada 5 min y guarda 73 KB por vuelta. Sus errores -lo unico que
sirve para depurar- ocupaban 2 MB.
Y no es un problema de disco (21%, 699 GB libres) sino de backup: n8n esta
en CROWN, asi que ese .tar.gz de 101 MB viajaba entero a MEGA cada dia y a
B2 en las dos ultimas copias. Es justo el gasto que revento la cuota el
2026-07-25.
Nada depende de ese historico: el monitor de reversion solo lee
workflow_entity, el panel de Grafana mira replicas del deployment y
'Resumen Diario' saca metricas de k8s por un nodo Code.
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.
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
- 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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
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>
El ConfigMap grafana-alerting (reglas provisionadas + contact points + política
de notificación → Telegram) se monta en el Deployment de Grafana, pero el chart
NO generaba ese volumen: se había añadido a mano en algún momento y sobrevivía
únicamente por el three-way merge de Helm, que preserva lo que no aparece ni en
el render viejo ni en el nuevo.
O sea que todo el alerting de Grafana dependía de un accidente afortunado. Un
`helm upgrade --force`, un `kubectl replace` desde la salida del chart, un
cambio del chart que reestructurase `volumes`, o reconstruir el release desde el
fichero de valores que acabo de añadir en el commit anterior — cualquiera de las
cuatro se lo habría llevado por delante. Y en silencio: los paneles seguirían
pintando igual, solo dejarían de llegar los avisos.
Se declara con grafana.extraConfigmapMounts. Comprobado antes de aplicar que el
spec renderizado es equivalente al vivo (subPath/readOnly a null y defaultMode
420 son los valores por defecto), así que el pod NO se ha reiniciado: mismo
nombre antes y después. Y verificado lo que de verdad importaba: renderizando
SOLO desde values-kube-prometheus-stack.yaml, el volumen aparece — el fichero ya
basta para reconstruir el release entero sin perder el alerting.
Encontrado haciendo el chequeo posterior al upgrade de las alertas de ruido, no
por el upgrade en sí: llevaba así desde que se montó.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Tres eran falsos positivos de k3s: KubeControllerManagerDown, KubeSchedulerDown
y KubeProxyDown llevaban 92 DÍAS disparadas en severidad critical. En k3s esos
tres componentes van embebidos en el proceso del servidor y no exponen las
métricas que espera el chart, así que sus ServiceMonitors nunca conectaban: la
alerta no medía la salud de nada, solo la ausencia de un target imposible.
La cuarta, PrometheusNotConnectedToAlertmanagers, llevaba disparada desde el
reinicio del máster del 16-jul. La causa es de diseño: alertmanager.enabled es
false y los avisos van por Grafana→Telegram y Kuma→Telegram. Ya se había
desactivado el grupo de reglas `alertmanager`, pero esta alerta vive en el
grupo `prometheus`, así que sobrevivió; se quita por nombre con
defaultRules.disabled.
Tres "critical" permanentes no son un aviso, son entrenamiento para ignorar el
panel: cuando algo se rompa de verdad, se perderá entre el ruido.
Hecho por Helm y no a mano (rev 1 → 2, mismo chart 83.2.0), que es lo único que
sobrevive a un futuro upgrade. Verificado ANTES de aplicar renderizando el chart
con y sin los valores nuevos: el diff quita exactamente esas 4 alertas (141 →
137) y ningún recurso nuevo aparece. Comprobado además que el render reproduce
el manifiesto ya desplegado, o sea que no había ediciones a mano que el upgrade
fuese a aplastar.
Después: 1 sola alerta disparada (Watchdog, el latido intencionado del chart,
severity none) y los 15 targets de scrape en up — antes 3 fallaban siempre.
Grafana sano (api/health ok, 12.4.2) y su contraseña rotada intacta, porque
GF_SECURITY_ADMIN_PASSWORD solo aplica en el primer arranque.
Se añade values-kube-prometheus-stack.yaml: este namespace no está bajo ArgoCD y
hasta hoy los valores del release solo vivían dentro de Helm, sin forma de saber
cómo estaba configurado sin interrogarlo. Se refresca el export del ConfigMap de
reglas y se borran 3 dashboards que el upgrade eliminó del cluster.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cierra el cambio de orden del flujo editorial. Con 'on', ResearchOwl escribía
el SEO en el borrador nada más generarlo — pero el SEO es DERIVADO del
artículo, y la revisión editorial posterior cambia el texto: la meta acababa
describiendo algo que ya no existía y había que rehacerla a mano en cada post.
Caso real del 2026-07-20: la meta del artículo belga decía 'A declassified
case unsolved' cuando el artículo argumenta que los datos crudos siguen
clasificados.
Con 'dryrun' el borrador se crea sin SEO y autofill solo lo PROPONE por
Telegram. El SEO se deriva del texto FINAL en el remate (seo_finish.py), que
además escribe el feature_image_alt — imposible en generación porque la imagen
destacada aún no se ha elegido (de ahí el 'human adds later' de autofill.py,
un paso humano que no ocurría: 9 posts publicados sin alt entre el 29-jun y
el 16-jul).
Contrapartida asumida: si un post se publica SIN pasar por el remate, saldrá
sin SEO. Red de seguridad: seo_watch marca meta_description.missing como HIGH,
y seo-check sigue disponible como puerta pre-publicación.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Elimina manifests openclaw/ y argocd/application-openclaw.yaml
- backup-system: fuera del backup diario, crown-jewels, expected y retain;
fuera el mount hostPath del cronjob y la fila de RESTORE.md
- monitoring: eliminado panel openclaw del dashboard homelab-overview
- n8n: openclaw y polymarket fuera del health-check (espejo del workflow vivo)
- cluster-wide/namespaces.yaml: fuera openclaw + añadidos separadores '---'
que faltaban (el archivo era un único doc YAML donde ganaba el último ns)
Cluster ya limpio: app ArgoCD, namespace, PVC/PV, RBAC cluster-scoped y
monitor de Uptime Kuma. Backup final: ~/decommissioned/openclaw-final-2026-07-18.tar.gz
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- fix: card ze apunta a /ze/ (el redirect de nginx a http:// caía en 404 de Traefik)
- fuera Open WebUI (chat.chemavx.xyz no existe) y card duplicada de polymarket en Services
- nuevos servicios: Uptime Kuma, Infisical, Trilium, Files, Umami, Ollama
- Projects: blogs The Exclusion Zone (EN) y Zona de Exclusión (ES) + ResearchOwl
- badges de infra bajo el header y subtítulos en las cards; Status en el footer
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fase final de la decomisión: namespace y Application borrados del cluster,
manifests fuera de git. Dump definitivo verificado y offsite en
/data/backups/backups/polymarket-decommission/ (espejo en mega:k3s-backups/).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
El bloque [b2] ya vive en RCLONE_CONF (Infisical /backup-system); los 3
CronJobs vuelven a rclone-conf-infisical y se elimina el secret
out-of-band rclone-conf-b2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- b2-crown-jewels.sh: sync diario 04:00 de las 2 copias mas recientes de
cada servicio irremplazable (todo menos uptime-kuma) a
b2:chemavx-k3s-backups/crown-jewels/ (~2GB, tier gratuito).
- reddit-intel.sh sube tambien a B2 (retencion 3d).
- verify.sh comprueba Mega (completo) + B2 (crown jewels).
- Config via Secret out-of-band rclone-conf-b2 (mega+b2, kubectl, NO git);
consolidar en Infisical RCLONE_CONF cuando se anada [b2] por UI (la
identity del operator es read-only por API).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
El k3s.yaml del host apunta a 127.0.0.1:6443 (inalcanzable desde el pod).
El script antiguo solo funcionaba porque su KUBECONFIG apuntaba a un
fichero inexistente y kubectl caia en fallback al SA. Se hace explicito:
unset KUBECONFIG + RBAC con apps/deployments,statefulsets get (para
kubectl exec deploy/...) y se retira el mount del kubeconfig.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- backup.sh: fix openclaw (tar-eaba un hostPath stub vacio, 104 bytes/dia
desde hace meses); ahora resuelve el PVC real via local-path.
- Nuevos servicios: ghost-en/zona-exclusion (VACUUM INTO consistente via
node del pod + tar del content), gitea (sqlite .backup + repos + conf,
sin packages 6.4G reconstruibles), researchowl (sqlite backup API via
python3), roswell-pg, umami-pg, infisical-pg, trilium, vaultwarden,
uptime-kuma (sqlite3 .backup), filebrowser-db, home-master (sin ~/.ssh
ni caches), k3s token + /etc/rancher/k3s (cadena de restauracion).
- Validacion por artefacto: gzip -t + tamano minimo; el job sale 1 si
falta algo -> alerta Grafana->Telegram existente.
- rclone-mega: de semanal a diario (03:30); --exclude reddit-intel/.
- Nuevo CronJob reddit-intel-backup en n97 (unico dato del worker fuera
de k3s; su cron local escribia en el mismo disco).
- verify.sh: lista completa (20 servicios + reddit-intel), diario,
ventana de frescura 3 dias (BusyBox-safe).
- backoffLimit 1 (retry recrearia artefactos y romperia la retencion).
- RESTORE.md: runbook con la cadena token->state.db->infisical-secrets.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
El pod fue OOMKilled el 2026-07-10 durante un research: 20 fuentes
concurrentes con PDFs grandes y pdfplumber superaron 1Gi. El scraper
se endurece en paralelo (cap PDF 15MB, contenido 300k chars), pero el
margen extra evita que un pico legítimo mate la tarea en memoria.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Regla homelab-backup-job-failed (kube_job_status_failed{namespace="backup-system"} > 0)
en el grupo homelab-infra; cubre backup, rclone-mega-backup y backup-verify.
Aplicado con kubectl replace + rollout restart de Grafana (namespace manual).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
App corre en docker compose en n97 (semana de calibración). El app-of-apps
no es recursivo: nada de este directorio se sincroniza hasta aplicar a mano
argocd-app.yaml. Checklist de activación en PROMOTION.md (imagen construida,
secrets en Infisical /reddit-intel, copia de la BD al PVC).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Merge de feat/portfolio-polymarket-archived con las cifras finales de
cierre (12 trades, +$247.78 realized, 239k evaluaciones, 81 días).
El widget deja de consultar la API (apagada) y pasa a tarjeta estática
con enlace al case study.
Primer paso del apagado ordenado: se detiene el ciclo de evaluación con
postgres aún vivo, para que el dump final sea la foto exacta del cierre
sin ciclos a medias. Postgres y cronjobs se apagan en commits siguientes.
Se retiran las suscripciones on-sync-failed / on-health-degraded: durante
el apagado (F3) generarían ruido sin valor. No hay trigger 'informativo'
viable: escalar a 0 vía git deja la app Synced/Healthy. El resto de apps
conservan sus notificaciones.
Sustituye el widget live (fetch a polymarket.chemavx.xyz/api/summary) por
una tarjeta estática con las cifras finales del paper trading y enlace al
case study en el repo. La tarjeta de servicio apunta al repo de Gitea en
lugar del dominio, que desaparecerá con el apagado.
NO MERGEAR HASTA F2/F3: mientras el bot siga vivo, main mantiene el
widget live. Aplicar esta rama cuando se apague el bot (Fase 3 del plan
de decomisión).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Online-safe sqlite3 .backup at 03:00 Europe/Madrid, integrity check,
7-day retention. Data PVC mounted RW (WAL -shm requirement) but only read.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Tras el plan F1-F4 del scraper, el seed descubre hasta ~285 fuentes en
topics documentados y el cap de 150 descartaba la mitad (incluida la
recursión que encuentra los PDFs de archive.org). 200 cubre casi todos
los resultados directos + margen de recursión. Implica +2-4 min por
research de topic rico y céntimos más de scoring.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extension login failed with 'No Bitwarden-Client-Version header provided'
after the Mac browser extension auto-updated. 1.36.0 supports the new
client login flow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Flip del flag que deja operativo el monitor F0-F2 ya desplegado en
b1c5bc73. Defaults: poll cada 6h, digest al primer TELEGRAM_ALLOWED_USERS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Corre python -m bot.outcomes a las 00:30 UTC (tras metrics-retention a
las 00:10): resoluciones UMA-finales de Gamma -> market_outcomes + reporte
de calibración. Solo-análisis: no toca tablas de trading. Idempotente por
market_id, seguro relanzarlo a mano. Misma imagen del bot; CI bumpa el tag
(cambio correspondiente en polymarket-bot/.gitea/workflows/ci.yml).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The theexclusionzone.com (Ghost EN) zone was never in DOMAINS, so favonia
never updated its origin A records. When the ISP rotated the public IP, the
EN records went stale and Cloudflare returned 522 (apex+www are proxied).
Add all three EN records to DOMAINS and use favonia 1.16.2's per-domain
PROXIED expression so apex+www stay orange while the wildcard and the
chemavx/zona zones stay grey, all from one DDNS instance.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds www.zonadeexclusion.com to the Ingress (rules + tls hosts) so cert-manager
re-issues zona-exclusion-tls covering apex + www via HTTP-01, and a Traefik
redirectRegex Middleware that 301s www -> apex (ES canonical = apex). Mirrors the
EN redirect-to-www-https middleware, inverted. DNS for www was fixed in Phase 1
(stale explicit A removed; now resolves via the favonia wildcard).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3c: n8n now sources the encryption key from n8n-secret-infisical (Infisical).
Remove the empty CreateOnly stub manifest and the now-dead ignoreDifferences
/data stanza so ArgoCD prunes the old out-of-band n8n-secret.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cut n8n over to the Infisical-synced encryption key (byte-identical, key never
changes -> credentials stay decryptable) and switch RollingUpdate -> Recreate
(485MB SQLite + WAL on RWO must not have two writers during a roll).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Out-of-band Secret -> Infisical homelab/prod /n8n. Parallel-name target
n8n-secret-infisical (Owner), passthrough of the single key encryption-key
(n8n's irrecoverable credentials-encryption root, lifted byte-identical).
Old secret stays live until decommission. ArgoCD-managed -> git->sync.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>