Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
3.3 KiB
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
LogBufferring ALWAYS exists; stdout respectslogging.levelviaLevelFilterWriter. New leveled lines useinternal/logx(Debugf/Infof/Warnf/Errorf). LegacyisDebug()-gated call sites are grandfathered — their DEBUG does not exist atinfo. - Agent:
applog.Newfans out — stderr/journald at the configured level, theapplog.Ringfixed at DEBUG. Served byGET /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)
- Pick the level from the table — flow detail is DEBUG even if it feels important (the ring holds it).
- English, keys-never-values, include duration on outcomes.
- Controller: use
logx.*; agent: the injected*slog.Logger. - 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").