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únbloqueadopuede 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-DDcon detalle del cambio enproxmox_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 ansiblecaddy_lxc(Caddyfile.full-routing,Caddyfile.passthrough) y en vivo en LXC 270/271; ambos Caddy reiniciados. Verificado::2019desde LAN devuelve 000. dash.rp.monxas.casa:forward_authmuerto 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 snippetrp-dashboard.caddy(conforward_auth https://id.monxas.casainexistente) no está desplegado; el routing vive inline sin auth. ✅ Snippet muerto limpiado (2026-07-12):forward_autheliminado derp-dashboard.caddy. dash.rp queda LAN-only + accesible por Tailscale (tailscale serveen 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 (antes249/253decomisionados).sopsinstalado en.208(v3.9.1). Snapshot fantasma de VM 208 eliminado (4→3).