Skip to content

FIX-13: local opt-in, metadata-only structured logging for CLI/app server#36

Open
ChelseaKR wants to merge 1 commit into
mainfrom
roadmap/fix-13-local-opt-in-structured-logging-f
Open

FIX-13: local opt-in, metadata-only structured logging for CLI/app server#36
ChelseaKR wants to merge 1 commit into
mainfrom
roadmap/fix-13-local-opt-in-structured-logging-f

Conversation

@ChelseaKR

Copy link
Copy Markdown
Owner

Add on-device, opt-in structured JSON logging for the two local surfaces
(the habitable CLI and the loopback app server), mirroring the optional
relay's metadata-only discipline (src/habitable/relay.py). It exists so an
operator debugging their own device can trace what happened without ever
emitting anything that must stay on-device.

  • New src/habitable/obslog.py: a shared _JsonFormatter (one compact JSON
    object per line: ts/level/msg + an explicit event_fields map),
    configure_logging() installing it idempotently on a dedicated habitable
    logger (propagate=False), enabled_from_env() (only HABITABLE_LOG=json
    opts in), and log_event() — the privacy gate: it is a no-op until logging
    is configured and refuses any non-scalar field, so a dict/list/bytes/Path
    payload can never ride onto a log line.
  • CLI: top-level --log-format json flag; logging enabled by the flag or
    HABITABLE_LOG=json; a metadata-only command-boundary event (name, ok,
    duration_ms) — never arguments, paths, ids, or secrets. Off by default,
    emitted to stderr so stdout stays clean.
  • App server: honors HABITABLE_LOG=json; per-request access lines use a
    redacted _route_label (e.g. /api/issues/{issue}/timeline) so no issue
    id, static path, or query value is logged, and never a body.
  • capture.py / sync.py: emit metadata-only events (capture result booleans,
    redundant-authority counts, sync ciphertext sizes and merge counts).
  • tests/test_obslog.py: formatter shape, env gating, the scalar-only guard,
    CLI end-to-end JSON to stderr, off-by-default silence, app-server redacted
    routes, and a no-plaintext guard paralleling tests/test_relay.py — a real
    init/capture/export flow with sentinel passphrase, filename, media bytes,
    and case text asserts none of them (nor the device fingerprint) appear in
    the log stream.
  • ROADMAP.md: mark the observability item shipped (FIX-13).

Co-Authored-By: Claude Fable 5 noreply@anthropic.com


Backfill PR for a completed roadmap item (roadmap/fix-13-local-opt-in-structured-logging-f, 1 commit(s) over main). Part of the portfolio roadmap batch.

…rver

Add on-device, opt-in structured JSON logging for the two local surfaces
(the `habitable` CLI and the loopback app server), mirroring the optional
relay's metadata-only discipline (`src/habitable/relay.py`). It exists so an
operator debugging their own device can trace *what happened* without ever
emitting anything that must stay on-device.

- New `src/habitable/obslog.py`: a shared `_JsonFormatter` (one compact JSON
  object per line: ts/level/msg + an explicit `event_fields` map),
  `configure_logging()` installing it idempotently on a dedicated `habitable`
  logger (propagate=False), `enabled_from_env()` (only `HABITABLE_LOG=json`
  opts in), and `log_event()` — the privacy gate: it is a no-op until logging
  is configured and refuses any non-scalar field, so a dict/list/bytes/Path
  payload can never ride onto a log line.
- CLI: top-level `--log-format json` flag; logging enabled by the flag or
  `HABITABLE_LOG=json`; a metadata-only command-boundary event (name, ok,
  duration_ms) — never arguments, paths, ids, or secrets. Off by default,
  emitted to stderr so stdout stays clean.
- App server: honors `HABITABLE_LOG=json`; per-request access lines use a
  redacted `_route_label` (e.g. `/api/issues/{issue}/timeline`) so no issue
  id, static path, or query value is logged, and never a body.
- capture.py / sync.py: emit metadata-only events (capture result booleans,
  redundant-authority counts, sync ciphertext sizes and merge counts).
- tests/test_obslog.py: formatter shape, env gating, the scalar-only guard,
  CLI end-to-end JSON to stderr, off-by-default silence, app-server redacted
  routes, and a no-plaintext guard paralleling tests/test_relay.py — a real
  init/capture/export flow with sentinel passphrase, filename, media bytes,
  and case text asserts none of them (nor the device fingerprint) appear in
  the log stream.
- ROADMAP.md: mark the observability item shipped (FIX-13).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants