From ba762f6d60047a54ea1275c606cbc32053ae748d Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Fri, 10 Jul 2026 16:25:54 +0200 Subject: [PATCH] REPORT: hub v0.43.0 deployed + live-validated (context leg proven live incl. redaction; tail click = 1-step operator follow-up) Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6 --- REPORT.md | 107 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 63 insertions(+), 44 deletions(-) diff --git a/REPORT.md b/REPORT.md index 51165c5..ccaa39a 100644 --- a/REPORT.md +++ b/REPORT.md @@ -2,55 +2,74 @@ > **Overwrite** this file with a summary of the most recent task only (uniform with the other repos; not cumulative). The cumulative hub history lives in [hub/CHANGELOG.md](hub/CHANGELOG.md); the scripts history lives in [scripts/CHANGELOG.md](scripts/CHANGELOG.md). -## TASK — hub v0.42.0: remote "Debug mód" toggle on the customer config editor — 2026-07-10 +## TASK — hub v0.43.0: remote app-log diagnostics (copyable issues + context + on-demand log tails) — 2026-07-10 -**Scope:** hub only (`felhom.eu/hub`). Agent/controller unchanged — the controller already honours -`Logging.Level=="debug"` (`isDebug()` → `/debug` menu + verbose log). Version 0.41.0 → **0.42.0**. +**Pairs with controller v0.111.0.** Deployed via GitOps (image 0.43.0, manifest bump, ArgoCD hard-refresh ++ sync): **Synced/Healthy**, rollout complete, clean startup log. Motivated live: Peti's CWA issue was +tooltip-only unreadable and context-free, with no remote way to see the app's logs. -### Goal -Let an operator enable the controller's debug mode **remotely, without SSH**, from the hub's customer -config editor — the support workflow (today: Peti's box). Enabling on any specific customer is a Viktor -decision/click; this task does not turn it on for anyone. +### What shipped (Parts A, C, D-hub, F, G, H of the spec) -### Why a form field, not raw-JSON injection -`handleConfigUpdate` rebuilds `ConfigJSON` from the form on every save (`configs.go` → `buildConfigJSON`), -so a foreign key poked straight into the stored JSON is dropped on the next save. The toggle is therefore -a first-class **form field**, which by construction survives every save. The offsite descriptor survives -independently, via the post-save provision-merge — untouched by this change. +- **A — readable/copyable issues** (`app_detail.html`): Known Issues rows click-to-expand — full message + in a wrapping monospace `
` + Copy button (clipboard API + execCommand fallback); fingerprint,
+  severity, first/last seen, and the explicit affected-customers list (linked) in the body. Tooltip
+  truncation killed.
+- **C — context store/render** (`store/telemetry.go`): additive `context` + `context_customer` columns;
+  `upsertAppIssue` stores context on INSERT, adopts a later one ONLY while the stored one is empty
+  (**first capture wins**). Rendered as "Context around first occurrence — from ", copyable.
+  Nil-safe with pre-v0.111 reports.
+- **D — on-demand log tail (pull-based, hub half)**: per-app **Request log tail** button on the customer
+  page (+ "tail pending" badge) → `log_tail_requests` (one per (customer,app), re-click refreshes) + a
+  customer-visible `log_tail_requested` event (transparency). Report ACK advertises
+  `log_tail_requests: [app…]` (same additive omit-when-empty pattern as escrow); the arriving
+  `log_tails` are stored in `app_log_tails` (transient, last 2 per app), request **cleared in the same
+  tx (consume-once)**. Ordered viewer with line numbers (`log_tail.html`) + Download .log; reads are
+  customer-scoped (cross-customer → 404). NO hub→controller push channel.
+- **F — bug fix**: the 24h/7d/30d selector now filters Known Issues too — `GetAppIssues` gained the same
+  `since` cutoff the Memory Trend uses (it previously had NO time filter at all).
+- **G — deletion → dismissal.** **Diagnosis recorded (spec asked a/b):** the delete handler was NOT
+  broken — `DeleteAppIssues`/`ByIDs` execute correct SQL. Deletion is futile because the controller
+  re-scans a rolling 15-minute log window every report and re-upserts any still-occurring fingerprint
+  with a fresh scan-time `last_seen` minutes later → cause **(b)**. Implemented `dismissed_at`:
+  Dismiss Selected/All, dismissed rows out of the default view ("Show dismissed" toggle shows them
+  dimmed + badged), and un-dismiss ONLY on `excluded.last_seen > dismissed_at` (a re-sent old window
+  stays hidden; a genuinely new occurrence resurfaces — recurrence is never silently swallowed).
+- **H — per-customer scoping**: `?customer=` filters Known Issues by `affected_customers`
+  membership (header: "filtered: "; period links carry the filter); the customer page's App
+  Telemetry rows link to that filtered view (the drill-down). Fleet view unchanged; count column
+  labeled "Occurrences (all customers)" (per-customer counts noted as a future refinement, not built).
 
-### Changes
-- `internal/web/configs.go` — `buildConfigJSON`: `debug_mode` checked → `"logging":{"level":"debug"}`;
-  unchecked → the `logging` key is omitted entirely (controller.yaml default stands; no needless `"info"`).
-- `internal/web/templates/config_form.html` — new collapsible **"Hibakeresési mód (fejlesztői)"** section
-  with the `debug_mode` checkbox + a Hungarian hint (restart-next-cycle + the `/debug` URL + "turn it off
-  after"). Render state parsed from `ConfigJSON`. No emoji, no BOM (consistent with the hub templates).
-- `internal/web/configs_debug_test.go` — new tests (below).
-- Untouched, as required: configgen, the offsite provision-merge, the controller. No generic raw-JSON editor.
+### Tests + red-proofs (all four failed exactly as designed; restored green)
 
-### Tests (non-hollow) — all green
-- `TestBuildConfigJSON_DebugMode` — checked emits `logging.level=debug`; unchecked omits the `logging` key.
-- `TestConfigUpdate_DebugSurvivesRebuild_OffsiteUntouched` — full path through `handleConfigUpdate`:
-  (a) the debug key lands, (b) the offsite descriptor (`host/user/repo_path/host_fingerprint/quota_gb`) is
-  unchanged across save + idempotent re-provision, and the **red-proof** that a hand-injected foreign key
-  (`foo`) is gone after one save — the concrete proof of why the switch had to be a form field.
-- `TestConfigForm_DebugRenderState` — debug-on ConfigJSON renders the checkbox `checked`; plain renders it unchecked.
-- **Red-proof exercised:** with the `logging` block removed from `buildConfigJSON`, the survival + form tests fail.
+- **G:** dismissal guard replaced with unconditional clear → `old-window re-report RESURRECTED the
+  dismissed issue` → FAIL.
+- **F:** range predicate neutered → `10d-old issue visible in the 24h view` → FAIL.
+- **C:** first-capture-wins CASE replaced with overwrite → `empty-context upsert clobbered the stored
+  context: []string(nil)` → FAIL.
+- **D consume-once:** request-DELETE removed from `SaveAppLogTail` → store test AND the API ACK
+  round-trip both FAILED (`request survived fulfillment — the controller would ship tails every cycle`).
+- Non-hollow coverage: late-context adoption; warn-no-context; occurrence counting; tail
+  request/refresh/fulfill/prune-to-2/cross-customer-scoping; ACK omit-when-empty baseline + full
+  round-trip; render tests (expanded row incl. full message + context + provenance + dismiss controls +
+  "filtered:" header; customer page request button/pending badge/tails section via the REAL handler;
+  ordered tail view + text/plain attachment download + cross-customer 404).
 
-Green gate: `go build ./... && go vet ./... && go test ./...` — all pass.
+### Live validation (demo, operator-free legs)
 
-### Deploy — DONE
-GitOps: image `felhom-hub:0.42.0` built on 180 → `manifests/hub.yaml` tag bumped (0.41.0→0.42.0) → ArgoCD
-hard-refresh + sync → **Synced/Healthy**, live image 0.42.0, pod rolled out (2026-07-10 15:17 CEST).
+Synthetic error injected into the demo controller container's log stream (neighbor lines +
+`password=hunter2` + an `ERROR: … LC0111 …` line). On the next report cycle (14:22:34Z, 3.7KB vs the
+2.5KB baseline) the hub DB showed the row in `app_log_issues`: severity `error`,
+`context_customer=demo-felhom`, and an **exactly-ordered 11-line context** containing
+`diag: connecting with password=[REDACTED] to fake backend` — **live proof the secret never left the
+box** and that capture/wire/store all work end-to-end. (Method: report-cycle → `/api/v1/report` ingest →
+hub DB read via kubectl cp; DB copy deleted after. The UI click-path is render-tested — the hub operator
+password is Viktor-only, CC cannot log in.)
 
-### Live validation — done except the one operator-gated click
-- **Off-baseline (live, operator-free):** the demo controller's generated `controller.yaml` carries
-  `logging.level: info` and its `/debug` route returns **404** — the exact observable the toggle flips, and
-  proof the `logging.level` merge target exists in the real generated config.
-- **Propagation leg (green test):** `configgen.TestGenerate_DebugLevelOverride` pins that
-  `logging.level=debug` in ConfigJSON deep-merges over the `info` default into the generated YAML (the merge
-  target shape taken from the live box). configgen itself was not modified.
-- **The one operator step:** ticking "Debug mód" on the demo customer and saving goes through the hub's
-  operator-password-gated UI — a credential CC does not have (no logged-in browser session available this
-  run). That single save (→ demo controller restarts next cycle → `/debug` ≠ 404 → untick → 404) is the last
-  hands-on confirmation; Viktor can click it. **Peti-box use (enabling on his config) is likewise Viktor's
-  click — not part of this task.**
+### Operator follow-up (1 click)
+
+- **Live tail round-trip:** on `hub.felhom.eu/customers/demo-felhom` → App Telemetry →
+  **Request log tail** on the `felhom-controller` row; the ordered tail + Download .log appears within
+  ≤2 report cycles (≤30 min), and the "tail pending" badge clears (consume-once). The mechanism is
+  fully red-proof-tested on both repos; only the click itself is operator-gated.
+- Per spec: do NOT request tails from Peti's box in this task — Viktor's call once he's read this.
+  Peti's CWA issues are now fully readable (full message + context) on the next report cycles.