Files
felhom-controller/REPORT.md
T

5.0 KiB

REPORT — v0.111.0: remote app-log diagnostics (error context + on-demand log tails)

Date: 2026-07-10 · Version: v0.111.0 (from v0.110.0) · Pairs with: hub v0.43.0 Spec: TASK — remote app-log diagnostics (Parts B, D-controller, E)

What shipped

Part B — error context capture (internal/metrics/logscanner.go)

  • The scan loop was extracted into the pure analyzeLogLines(lines []string) (first-ever unit tests for the scanner). On the FIRST occurrence of an error-severity fingerprint in a scrape window, captureContext attaches LogIssue.Context: up to 5 raw lines before + the line + up to 5 after, ordered as emitted. Repeats never re-capture; warns never carry context.
  • Caps: ≤11 lines; each line ANSI-stripped + rune-truncated to 400 chars with ; and a HARD 16KB per-report budget in internal/report/telemetry.go (enforceContextBudget — context dropped from the lowest-count issues first, across all apps).
  • Additive context field (omitempty) on the report's issue objects — old hubs ignore it.

Part E — sanitization (internal/metrics/redact.go)

  • RedactLine: case-insensitive password|passwd|secret|token|api[_-]?key|authorization|bearer followed by [=: ]\S+ → value [REDACTED] (the value group also swallows a Bearer prefix so Authorization: Bearer <tok> redacts fully in one pass); any 64-hex string → [REDACTED-HEX64]. Applied to EVERY context line and tail line before it leaves the box — controller-side, authoritative.

Part D (controller half) — on-demand log tails (internal/report/logtail.go)

  • PushResponse gains log_tail_requests: [app…] (same additive ACK-flag pull pattern as escrow/config-refresh — the guest listens to no one). OnPushResponseSetPendingLogTails; the NEXT BuildReport drains (consume-once) and ships log_tails: [{app, collected_at, lines[]}].
  • Collection reuses existing plumbing: stacks.GetLogs(app, 200) (compose logs, ordered) for stacks; new metrics.FetchContainerLogTail (docker logs --tail=200, 15s timeout) for the felhom-controller container. Caps: ≤400 chars/line, ≤64KB/app HEAD-truncated (newest lines kept), every line redacted.
  • Fail-safe: fetch or push failure leaves the hub's request pending → the next ACK re-arms it.

Tests + red-proofs (all executed; failures verbatim)

  • B red-proof — capture call dropped → TestAnalyzeLogLines_ErrorContextWindow FAILED: context has 0 lines, want 11: []string(nil). Restored → green.
  • E red-proofRedactLine gutted (return s) → all redact tests FAILED with the secrets visible, e.g. RedactLine("password=hunter2 token: abc Bearer xyz") = "password=hunter2 token: abc Bearer xyz" and 64-hex secret shipped: "repo unlock with ab12…". Restored → green.
  • D red-proof — consume-once clear removed from drainPendingLogTailsTestPendingLogTails_ConsumeOnce FAILED: second drain = [gokapi cwa] — tails would ship on EVERY report. Restored → green.
  • Non-hollow coverage: exact ±5 ordered window; repeat keeps the FIRST occurrence's context; warn has no context; 400-char truncation (); budget drops lowest-count first / under-budget untouched; window clamps at buffer edges; 10-issue cap + count ordering intact; tail order + redaction + line-cap + 64KB newest-kept + fetch-error skip + empty-ACK clears stale pending.
  • Full suite green (go build && go vet && go test ./...). One PRE-EXISTING flake observed once in internal/web (TestBackupTier2Restore_DoubleClickRefused, async timing) — passed 4/4 on re-run, unrelated to this diff.

Deploy + live validation (demo 9201)

  • Image 0.111.0 built on 180, deployed via the bootstrap mechanism → gitea.dooplex.hu/admin/felhom-controller:0.111.0 Up (healthy), clean startup, channel up.
  • Synthetic error injected into the controller container's own log stream (the controller rides the same telemetry pipeline as apps): neighbor lines + password=hunter2 + an ERROR: line — hub-side landing verified in the hub REPORT (felhom.eu) including LIVE redaction of the secret.
  • Method note: server-side pipeline exercised end-to-end (report cycle → hub ingest → hub DB); the hub UI click-path is render-tested (the hub operator password is Viktor-only — CC cannot log in).

Live tail round-trip — COMPLETED (Viktor logged the browser in; CC drove the hub UI)

Request clicked (14:35:20Z) → "tail pending" badge + transparency event → 14:37:34Z report's ACK armed the controller → 14:52:34Z report: log-tail collected for felhom-controller: 200 lines (push 2,545 B → 18,849 B) → hub stored tail #1, pending badge cleared (consume-once), ordered 200-line viewer + Download .log verified in the browser. Details in felhom.eu/REPORT.md.

Residuals / notes

  • The live tail exercised the FetchContainerLogTail path (no customer app deployed on the demo right now); the stacks.GetLogs path is unit-covered via the fetch seam.
  • Do NOT request tails from Peti's box as part of this task — Viktor's call from the UI (per spec).