docs: REPORT + CONTEXT for v0.112.0 (deployed + live-proven credential-less on demo)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
+14
-1
@@ -7,7 +7,20 @@
|
||||
>
|
||||
> Ask Claude Code: "Please update CONTEXT.md with what we did today"
|
||||
|
||||
Last updated: 2026-07-10 (v0.111.0 — remote app-log diagnostics; pairs with hub v0.43.0)
|
||||
Last updated: 2026-07-10 (v0.112.0 — anonymous registry self-update; pairs with hub v0.43.1)
|
||||
|
||||
> **2026-07-10 — v0.112.0: self-update without credentials (LIVE on 9201, pairs with hub v0.43.1).**
|
||||
> Root cause on Peti's box: the updater refused without Git Sync creds, but the public package is
|
||||
> anonymously pullable. `queryRegistry` now does the Docker v2 anonymous token dance when both creds are
|
||||
> empty (realm/service parsed FROM the WWW-Authenticate header — never hardcoded); `pullImage` skips
|
||||
> `docker login` credential-less; creds path byte-unchanged (private catalogs); half-configured pair =
|
||||
> loud misconfig; denial = "registry denied anonymous access — a private registry requires Git Sync
|
||||
> credentials". Settings panel gains the mode line "Registry: nyilvános (hitelesítés nélkül) /
|
||||
> hitelesített" — credential-less is a supported mode, not an error. Red-proof green (old guard restored
|
||||
> → anonymous tests fail with the old message). LIVE-PROVEN on the credential-less demo (git creds are
|
||||
> quoted-empty): /api/selfupdate/check → ok, latest=0.112.0, no error; settings shows "nyilvános".
|
||||
> PENDING OPERATOR: floor-bump Peti to 0.112.0, then delete his temp Git Sync creds → clean "nyilvános"
|
||||
> check. runCommand/runCommandStdin are now package VARS (test seam).
|
||||
|
||||
> **2026-07-10 — v0.111.0: remote app-log diagnostics (LIVE on 9201, pairs with hub v0.43.0).** The
|
||||
> telemetry scraper now attaches `LogIssue.Context` (±5 raw lines around the FIRST occurrence of each
|
||||
|
||||
@@ -1,73 +1,68 @@
|
||||
# REPORT — v0.111.0: remote app-log diagnostics (error context + on-demand log tails)
|
||||
# REPORT — v0.112.0: self-update without credentials (anonymous registry mode)
|
||||
|
||||
**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)
|
||||
**Date:** 2026-07-10 · **Version:** v0.112.0 (from v0.111.0) · **Pairs with:** hub v0.43.1
|
||||
**Spec:** TASK — controller self-update without credentials (anonymous registry mode)
|
||||
|
||||
## Root cause (live on Peti's box)
|
||||
|
||||
The updater piggybacked on the Git Sync credentials and REFUSED when absent (`queryRegistry` +
|
||||
`pullImage` guards) — but the registry serves the public package anonymously (Docker v2 token dance,
|
||||
verified empirically 2026-07-10). A fresh customer without a private catalog silently lost version
|
||||
discovery + self-update for no reason. Credentials are now what they were meant to be: **optional,
|
||||
private-catalog only**.
|
||||
|
||||
## 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 1 — `queryRegistry` anonymous mode** (`internal/selfupdate/updater.go`): both creds empty →
|
||||
plain GET; on 401 parse the `WWW-Authenticate` Bearer challenge (realm + service **from the header**
|
||||
— never hardcoded; quoted/bare values, any order, commas-in-quotes handled); GET the realm with
|
||||
`service` + `repository:<image>:pull` scope and NO credentials; retry tags/list with the Bearer.
|
||||
Creds present → the BasicAuth path unchanged. Half-configured pair → loud
|
||||
"hiányos registry hitelesítő adatok". A genuinely-denying registry →
|
||||
**"registry denied anonymous access — a private registry requires Git Sync credentials"** (never the
|
||||
old "credentials missing"). Registry base URL now derives from the image ref (was a hardcoded host).
|
||||
- **Part 2 — `pullImage` without login:** no creds → the `docker login` step skipped entirely
|
||||
(docker's native anonymous flow covers public packages); creds → login/pull/logout unchanged
|
||||
(token still stdin-only). The old refuse-guard removed; a denied anonymous pull surfaces docker's
|
||||
own error.
|
||||
- **Part 3 — settings truthfulness:** "Verzió és frissítés" gains
|
||||
"Registry: nyilvános (hitelesítés nélkül)" / "Registry: hitelesített"; credential-less is no longer
|
||||
an error state; the Hiba row appears only on real failure. `DryRun.PullCapable` counts anonymous as
|
||||
capable (false only on a half-configured pair).
|
||||
- **Part 4 (hub v0.43.1):** Git Sync form hint — "Opcionális — csak privát alkalmazás-katalógushoz.
|
||||
A verziófrissítés enélkül is működik." (one template string).
|
||||
|
||||
### 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.
|
||||
## Tests + red-proof (executed; failures verbatim)
|
||||
|
||||
### 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). `OnPushResponse` → `SetPendingLogTails`;
|
||||
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.
|
||||
- **Red-proof** — the old creds-required guard restored in `queryRegistry` → all three anonymous tests
|
||||
FAILED with the old message visible:
|
||||
`anonymous queryRegistry failed: registry hitelesítő adatok hiányoznak`. Restored → green.
|
||||
- Non-hollow coverage (`registry_anon_test.go`, httptest fake registry + fake CLI runner): full token
|
||||
dance with ZERO creds (token request carries NO Authorization, correct
|
||||
`service`+`scope=repository:admin/felhom-controller:pull`, highest semver returned, non-semver tags
|
||||
skipped); creds path unchanged (BasicAuth on tags/list, token endpoint never hit); BOTH denial paths
|
||||
(token 401, tags-401-with-Bearer) → the new clear error and NOT the old one; `parseWWWAuthenticate`
|
||||
table (quoted/bare/order/comma-inside-quotes/missing-realm/non-Bearer/empty); pull with no creds →
|
||||
**no login/logout invocation recorded**, exactly one `docker pull`; creds → login→pull→logout order +
|
||||
token via stdin; partial creds refuse everywhere with zero docker commands run.
|
||||
- Green gate both repos (`go build && go vet && go test ./...`) + controller template gates
|
||||
(`template_id_gate.py`, `emoji_gate.py`) pass.
|
||||
|
||||
## Tests + red-proofs (all executed; failures verbatim)
|
||||
## Deploy + live validation (demo, credential-less for real)
|
||||
|
||||
- **B red-proof** — capture call dropped → `TestAnalyzeLogLines_ErrorContextWindow` FAILED:
|
||||
`context has 0 lines, want 11: []string(nil)`. Restored → green.
|
||||
- **E red-proof** — `RedactLine` 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 `drainPendingLogTails` →
|
||||
`TestPendingLogTails_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.
|
||||
- Controller `0.112.0` → 9201 (bootstrap mechanism), `Up (healthy)`. Hub `0.43.1` → GitOps sync,
|
||||
Synced/Healthy, rollout complete.
|
||||
- The demo box's `controller.yaml` has `git.username: ""` / `git.token: ""` (verified quoted-empty →
|
||||
parses to empty strings) — a genuinely credential-less box.
|
||||
- **Live anonymous check against the real registry:** `POST /api/selfupdate/check` (in-guest, via the
|
||||
container IP + Host header, hub-API-key bearer) →
|
||||
`{"ok":true, "current_version":"0.112.0", "latest_version":"0.112.0", "update_available":false}` —
|
||||
NO error. Pre-0.112.0 this exact call failed with "registry hitelesítő adatok hiányoznak".
|
||||
- **Settings page live:** renders `Registry — nyilvános (hitelesítés nélkül)` and
|
||||
`Legújabb verzió — 0.112.0`; no Hiba row.
|
||||
|
||||
## Deploy + live validation (demo 9201)
|
||||
## Pending operator step (per spec — Peti's box untouched by this task)
|
||||
|
||||
- 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).
|
||||
After Viktor's floor bump rolls Peti to 0.112.0: delete the temporary Git Sync creds from Peti's hub
|
||||
config → next config pull → Peti's settings page should show a clean "Registry: nyilvános
|
||||
(hitelesítés nélkül)" with zero credentials on the box.
|
||||
|
||||
Reference in New Issue
Block a user