Skip to content

Add opt-in diagnostic logging behind -v/--verbose flag#125

Merged
alexkroman merged 1 commit into
mainfrom
claude/pensive-pasteur-6wrfny
Jun 12, 2026
Merged

Add opt-in diagnostic logging behind -v/--verbose flag#125
alexkroman merged 1 commit into
mainfrom
claude/pensive-pasteur-6wrfny

Conversation

@alexkroman

Copy link
Copy Markdown
Collaborator

Adds a new debuglog module that provides opt-in diagnostic logging to stderr, controlled by the root -v/--verbose flag. This allows users to diagnose auth/connectivity issues without guessing.

Summary

Introduces a diagnostic logging system that:

  • Remains silent by default (no logging output)
  • Activates with -v to show request-level diagnostics (HTTP requests, statuses)
  • Activates with -vv to show wire-level detail (WebSocket frames, connection events)
  • Automatically redacts sensitive values (API key, session JWT) from all output

Key Changes

  • New aai_cli/debuglog.py: Core module providing:

    • enable(verbosity) to install stderr handler at startup
    • register_secret(value) to record sensitive values for redaction
    • _RedactingFormatter that masks secrets in every log record
    • active() to check if verbose mode is on
  • Updated aai_cli/main.py:

    • Added --verbose/-v option (count-based, supports -v and -vv)
    • Calls debuglog.enable(verbose) before any other initialization
    • Logs environment info at DEBUG level when verbose is active
  • Updated aai_cli/config.py:

    • Refactored resolve_api_key() to register the key with debuglog before returning
    • Extracted internal logic to _resolve_api_key() to keep registration at the public API boundary
  • Updated aai_cli/context.py:

    • AppState.resolve_session() now registers the JWT with debuglog for redaction
  • Updated logging silencers (aai_cli/ws.py, aai_cli/streaming/diagnostics.py):

    • Both now check debuglog.active() and skip silencing when verbose mode is on
    • Allows library logs to surface when -v/-vv is requested
  • Comprehensive test coverage (tests/test_debuglog.py):

    • Tests for enable/disable behavior at different verbosity levels
    • Tests for secret redaction in log output
    • Tests for API key and JWT registration
    • Tests for CLI integration with -v and -vv flags
    • Fixture to reset global state between tests
  • Documentation updates:

    • Updated aai_cli/skills/aai-cli/SKILL.md with diagnostics usage guide
    • Updated AGENTS.md with debuglog module description

Implementation Details

  • Secrets are redacted at the formatter level (not call sites) because leaks come from library logs that the CLI doesn't control
  • The module is stdlib-only to avoid pulling in Rich, since config (a Rich-free library layer) registers secrets here
  • Verbosity and secrets are process-global state (like environments.py), with a test fixture to snapshot/restore between tests
  • The silencers stand down under verbose mode so -vv can show the wire-level frames it exists to display

https://claude.ai/code/session_01Q6KZYc7FPWhyJkbQLUtq36

@alexkroman alexkroman enabled auto-merge June 12, 2026 20:18
Comment thread aai_cli/debuglog.py
_MASK = "[redacted]"

_verbosity = 0
_secrets: set[str] = set()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_secrets is a module-level mutable set that stores per-session/API secrets; it persists for the process lifetime and can leak sensitive values between requests or sessions.

Details

✨ AI Reasoning
​A mutable module-level set is being used to hold sensitive values that are registered during normal command execution. In long-running processes or servers, that state will persist across independent requests or sessions and can accumulate or leak secrets between contexts, causing data leakage and cross-request coupling. The concern focuses on request/session-scoped secrets being stored in process-global mutable state.

🔧 How do I fix it?
Avoid storing request-specific data in module-level variables. Use request-scoped variables or explicitly mark shared caches as intentional.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AikidoSec ignore: This is a short-lived single-user CLI process, not a long-running server — there are no cross-request or cross-session contexts to leak between. The set holds only the current invocation's own credentials (API key, session JWT), registered precisely so the logging formatter can redact them from -v/--verbose diagnostic output, and it dies with the process. The process-global scope is intentional and mirrors the existing environments.py pattern in this codebase.


Generated by Claude Code

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Based on your feedback, we ignored this issue because of the following reason:

This is a short-lived single-user CLI process, not a long-running server — there are no cross-request or cross-session contexts to leak between. The set holds only the current invocation's own credentials (API key, session JWT), registered precisely so the logging formatter can redact them from -v/--verbose diagnostic output, and it dies with the process. The process-global scope is intentional and mirrors the existing environments.py pattern in this codebase.


Generated by Claude Code

The CLI had no way to see what it was doing on the wire: library loggers
were actively silenced so stderr stayed clean next to normalized errors,
and diagnosing an auth or connectivity problem meant reading code.

A new root flag closes that gap (counted, gh/speechmatics style):
- `-v` surfaces request-level logs (httpx and friends at INFO)
- `-vv` adds wire-level detail (websockets frames, httpcore events at DEBUG)

aai_cli/debuglog.py installs one stderr handler with a redacting
formatter; config.resolve_api_key and AppState.resolve_session register
the API key and session JWT at their resolution choke points so verbose
output can never print them in clear (websockets logs the raw
Authorization header at DEBUG during the handshake). The realtime
silencers (aai_cli.ws, streaming/diagnostics) stand down while verbose
mode is active, and the root callback logs the resolved environment.

https://claude.ai/code/session_01Q6KZYc7FPWhyJkbQLUtq36
@alexkroman alexkroman force-pushed the claude/pensive-pasteur-6wrfny branch from 149bf02 to fafed7b Compare June 12, 2026 20:27
@alexkroman alexkroman added this pull request to the merge queue Jun 12, 2026
Merged via the queue into main with commit b5fc14b Jun 12, 2026
16 checks passed
@alexkroman alexkroman deleted the claude/pensive-pasteur-6wrfny branch June 12, 2026 20:34
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