Skip to content

Tech debt y workarounds activos

Last updated: 2026-07-12

Workarounds vivos en el homelab. Cada uno tiene Estado: (aceptado / bloqueado). Los resuelto se mueven a agent-memory/proxmox_issues.md como histórico. Revisar al menos cada 30 días o cuando algo huela mal.


1. Loki 3.7.1 — count_over_time + |~ regex infla counts (bug upstream)

Síntoma: count_over_time({...} |~ "regex"[24h]) reporta cifras hasta ×3-4 vs líneas reales. Streams directos con misma query devuelven totalPostFilterLines correctos.

Causa raíz (verificada en código de Loki por agente, 2026-04-26): - Issue upstream grafana/loki#18760, abierto 2025-08-07 por slim-bean (Ed Welch, maintainer). Status: OPEN, sin PR, sin assignee. - Dedup en pkg/iter/entry_iterator.go:133-135 compara streamHash que viene de Stream.Hash, calculado con labels.StableHash(lbls) incluyendo __stream_shard__ y __time_shard__. - Cuando Promtail/Alloy reintenta un push y la copia cae en otro shard, ambas sobreviven el dedup heap-merge. - PR #7005 (2022-09-01, owen-d) ya intentó arreglarlo. Revertido el mismo día en #7031 porque "tenían un design dramatically simpler" que nunca llegó al repo.

Workarounds activos: 1. limits_config.shard_streams.enabled: false en /home/monxas/appdata/loki-config/loki-config.yaml — impide nuevos chunks sharded. Solo afecta ingesta nueva. Chunks ya escritos siguen inflando hasta retention (30d) o que salgan de la ventana de la query. 2. morning-report.py función verify_real(): cross-check con stream query real para cada top-N entry. Descarta si total>50 AND real<total/10. 3. Reglas Grafana error-spike + queries del morning report excluyen container_name!~"loki|grafana" y filtros != "query=" != "caller=metrics" para evitar self-loop (queries de Grafana logueadas por Loki contienen las palabras buscadas).

Tracking: comentado en #18760 (2026-04-26) preguntando a slim-bean/owen-d por el "simpler design" antes de implementar PR. Esperando respuesta.

Plan al recibir fix upstream (cuando grafana/loki:3.8+ exista con #18760 cerrado): 1. docker pull grafana/loki:NEW → verificar release notes incluyen #18760 2. docker compose up -d loki desde stacks/infra/ 3. Quitar bloque shard_streams: enabled: false del config Loki, restart 4. Quitar verify_real() de morning-report.py (opcional) 5. Quitar filtros != "query=" y container_name!~"loki|grafana" de regla error-spike y query SIGRTMIN+3 (opcional, defense-in-depth cuesta cero)

Estado: bloqueado — esperando upstream. Revisar quincenal en #18760.


2. Filtros belt+suspenders != "query=" y container_name!~"loki|grafana" redundantes

Qué hace: ambos filtros aplicados a la misma query consiguen el mismo objetivo (excluir self-loop de Loki/Grafana). En Loki 3.x con LogQL2, el label filter solo ya cubriría.

Por qué se mantienen: si el label container_name desaparece de un stream (promtail config rota, nuevo job sin docker_sd_configs, container fuera de Docker), el regex de string sigue protegiendo. Coste de mantener: cero.

Cuándo limpiar: cuando se cierre #1 — junto con quitar otros workarounds.

Estado: aceptado — defensa real con coste cero.


3. uptime-kuma + lazy services: gap del 1% conocido

Qué hace: lazy services (Paperless, Dockge, jellyseerr, gastosapp, comparador, etc. — 20 hostnames con sablier en Caddyfile) NO están en uptime-kuma. Su monitorización formal queda en caddy-upstream-down (fallo cuando se accede al hostname) y container-restarting.

Gap real: si un lazy service queda en estado "TCP up pero unhealthy" (DB corrupta, app responde 500), no hay alerta proactiva. Solo se descubre cuando alguien intenta usarlo (502 → caddy alerta) o por restart loop (container-restarting alerta).

Por qué no fix: Sablier no expone endpoint "is-ready-but-don't-wake" (su API /api/strategies/dynamic siempre despierta — es su contrato). Construir monitor on-demand custom = ~2h de scripting para cubrir un gap del 1%. No vale la pena.

Cuándo reconsiderar: si algún lazy critical empieza a fallar en este modo silenciosamente. Hoy ninguno lo es.

Estado: aceptado — gap conocido y proporcionado.


Cómo usar este documento

  • Antes de cada session de mantenimiento mensual: leer este doc + proxmox_issues.md. Revisar si algún bloqueado puede destrabarse.
  • Si algo huele raro en el homelab y no aparece en proxmox_issues.md: empezar buscando aquí.
  • Cuando se aplica un fix: marcar resuelto AAAA-MM-DD con detalle del cambio en proxmox_issues.md, y borrar el item de aquí.
  • Cuando aparece nueva chapuza: añadir aquí con misma estructura (Síntoma, Causa raíz, Workaround, Plan al fix, Estado).

Sesión 2026-07-12 — resueltos + nuevos residuales

Ver ../architecture/adr/ADR-0022-hardening-session-2026-07.md para el detalle completo.

Resueltos (movidos a histórico): hueco de backup CT100 (57 días), cobertura DR de caddy 270/271/280, pbs-host-backup fingerprint, apps lazy caídas de routing (host_port_for parcheado), 43 GB fantasma por falta de TRIM, logrotate duplicado, credenciales clawdbot2026/jobhunter2025 en claro.

N1. root SSH sin restringir (CT 100 / Vera)

Qué: PermitRootLogin yes, sin AllowUsers. El acceso de Vera es root a todo el homelab. Riesgo: superficie amplia; un fallo de restricción deja fuera de TODO el homelab (auto-lockout). Plan: usuario dedicado + forced-command keys, en sesión dedicada con consola de rescate a mano. Estado: bloqueado — diferido por riesgo de lockout.

N2. zfs-ha fragmentación ~73% (pmx)

Qué: pool zfs-ha al 80% CAP con 73% FRAG. ZFS no tiene defrag online. Mitigación: se alivia liberando espacio (~19 GB liberados 2026-07-12). El rendimiento degrada >80% CAP. Plan: vigilar CAP; si cruza 85% mover algún guest a zfs-ha de pmx2 (12% CAP) o ampliar. Un scrub no defragmenta. Estado: aceptado — vigilar mensual.

N3. gog OAuth scopes gmail/contacts flakean

Qué: gog gmail/contacts fallan por scopes; gog calendar OK vía gog-cal.sh (self-source + timeout). Plan: re-auth con browser (gog auth add … --force-consent --services=all) en sesión con Mon. Estado: bloqueado — requiere interacción de Mon.

Seguridad — hallazgos auditoría doc↔realidad (2026-07-12)

  • RESUELTO (2026-07-12) — Caddy admin API a 127.0.0.1:2019. Endurecido en el rol ansible caddy_lxc (Caddyfile.full-routing, Caddyfile.passthrough) y en vivo en LXC 270/271; ambos Caddy reiniciados. Verificado: :2019 desde LAN devuelve 000.
  • dash.rp.monxas.casa: forward_auth muerto pero LAN-only (riesgo bajo). No tiene registro DNS público en Cloudflare (verificado vía CF API 2026-07-12) → no expuesto a internet, solo LAN/WireGuard. El snippet rp-dashboard.caddy (con forward_auth https://id.monxas.casa inexistente) no está desplegado; el routing vive inline sin auth. ✅ Snippet muerto limpiado (2026-07-12): forward_auth eliminado de rp-dashboard.caddy. dash.rp queda LAN-only + accesible por Tailscale (tailscale serve en rp-server, autenticado por tailnet). Gate per-usuario adicional (OIDC de app) queda como opcional si algún día se expone.
  • pbs-drill pool corregido (2026-07-12): (100 123 155 200 251) en vivo + ansible (antes 249/253 decomisionados). sops instalado en .208 (v3.9.1). Snapshot fantasma de VM 208 eliminado (4→3).