diff --git a/CLAUDE.md b/CLAUDE.md index 471d364..470ae81 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -49,6 +49,9 @@ UI. Package map, helpers, seams, extension points: **`REUSE.md`** (e.g. new even - If you need more input or troubleshooting output, **ask first — don't guess**. - Testing doctrine (non-hollow tests, red-proofs, seams): use the `felhom-testing` skill. - UI/design work (tokens, gates, copy rules): use the `felhom-ui-design` skill. +- **Logging**: levels/English/no-secrets rules per `documentation/runbooks/logging-conventions.md` + (DEBUG = flow detail, INFO = state change + duration; logs are operator-tier English; keys never + values — the hub's bundle secret-gate blocks violating pulls fail-closed). ## Workflow & artifacts diff --git a/documentation/runbooks/logging-conventions.md b/documentation/runbooks/logging-conventions.md new file mode 100644 index 0000000..6392656 --- /dev/null +++ b/documentation/runbooks/logging-conventions.md @@ -0,0 +1,57 @@ +# Logging conventions (all Felhom components) + +> Codified by the 2026-07-11 observability pass (agent v0.83.0 / controller v0.116.0 / hub v0.46.0). +> The capture layers below make DEBUG remotely readable WITHOUT config flips — these rules are what +> make that safe and useful. + +## Levels + +| Level | Meaning | Examples | +|---|---|---| +| **DEBUG** | Flow detail: phase steps, per-call traces, parsed values, verdicts with their inputs | `netverify: /proc/mounts verdict mounted=false`, `[agentapi] GET /storage -> 200 (12ms)` | +| **INFO** | State changes and operations **with durations** ("X done in Yms") | `network storage added + verified: … in 4200ms`, `operator log pull served` | +| **WARN** | Degraded-but-continuing | `journal unavailable — degraded classification`, `status persist failed` | +| **ERROR** | A failed operation — **always with the underlying error** | `rollback RemoveNetworkMount failed: …` | + +Minimum standard per surface: **entry, decisions, outcome + duration, errors.** Every background +job logs start → outcome → duration; every refusal logs the category it returned. + +## Language + +**Logs are operator-tier: English.** The UI is Hungarian; logs are not UI. (Customer-facing +messages derived FROM log categories are mapped to Hungarian at the UI layer, never in the log.) + +## Secrets + +- **Keys, never values.** Log `password=[REDACTED]`, a creds-file *path*, a token's *length* — never + the credential, token, or key material itself. +- Journal-line content only as the bounded classification tail already established (netverify). +- Never log signatures, nonces, or one-time secrets — op class/host/expiry only. +- Belt-and-braces: the hub's bundle store runs a token-pattern grep gate before storing a pulled + log bundle; a hit stores NOTHING and flags `blocked: possible secret`. `[REDACTED]` shapes and + public checksums/fingerprints pass by design. + +## Capture layers (how DEBUG reaches the operator) + +- **Controller**: the 1000-entry `LogBuffer` ring ALWAYS exists; stdout respects `logging.level` + via `LevelFilterWriter`. New leveled lines use `internal/logx` (`Debugf/Infof/Warnf/Errorf`). + Legacy `isDebug()`-gated call sites are grandfathered — their DEBUG does not exist at `info`. +- **Agent**: `applog.New` fans out — stderr/journald at the configured level, the `applog.Ring` + fixed at DEBUG. Served by `GET /debug/logs` (local API) and the heartbeat log pull. +- **Hub**: plain leveled Printf; the hub is directly operable, no ring needed. + +## Remote pulls (sovereignty posture) + +Pull-only, box-pushed on its own cycle: controller ring via the report ACK +(`controller_log_requested` → `controller_log_tail`), agent ring via the heartbeat envelope +(`log_tail_requested` → `log_tail`). Consume-once on both ends; a failed push re-arms. Serving a +pull logs the customer-visible `operator log pull served` INFO line — a pull is never silent. +Bundles live 72 h on the hub, then purge. + +## Adding log lines (checklist) + +1. Pick the level from the table — flow detail is DEBUG even if it feels important (the ring holds it). +2. English, keys-never-values, include duration on outcomes. +3. Controller: use `logx.*`; agent: the injected `*slog.Logger`. +4. For a NEW flow: add/extend the S7-style log-sequence smoke test (the ordered phase markers are + the executable definition of "an operator can reconstruct this flow from the debug view"). diff --git a/manifests/hub.yaml b/manifests/hub.yaml index 6ac45ed..949d91c 100644 --- a/manifests/hub.yaml +++ b/manifests/hub.yaml @@ -126,7 +126,7 @@ spec: spec: containers: - name: hub - image: gitea.dooplex.hu/admin/felhom-hub:0.45.0 + image: gitea.dooplex.hu/admin/felhom-hub:0.46.0 ports: - containerPort: 8080 name: http