Skip to content

fix(privacy): keep verbatim transcript text off INFO logs - #44

Merged
WingedGuardian merged 2 commits into
mainfrom
fix/s2s-transcript-log-privacy
Aug 13, 2026
Merged

fix(privacy): keep verbatim transcript text off INFO logs#44
WingedGuardian merged 2 commits into
mainfrom
fix/s2s-transcript-log-privacy

Conversation

@WingedGuardian

Copy link
Copy Markdown
Owner

What

Both the s2s and ambient bridges logged full conversation/utterance text at INFO, landing plaintext in the systemd journal. This moves the verbatim text to DEBUG and gates verbosity behind an env (default INFO).

  • s2s: genesis_tool_service per-turn text → logger.debug (count summary stays INFO); main.py honors S2S_LOG_LEVEL.
  • ambient: pipeline utterance text → DEBUG, keeping source/duration at INFO; server.py honors AMBIENT_LOG_LEVEL. Mirrors the existing MEETING_LOG_LEVEL/OMI_LOG_LEVEL.

The level env is guarded (getattr fallback to INFO) so a bad value can't crash the bridge at startup.

Why

Captured voice content in journald plaintext is a privacy risk. Verbatim text stays recoverable on demand via *_LOG_LEVEL=DEBUG, and the persist-failure local fallback file already preserves transcripts — so nothing is lost.

Testing

  • pytest bridges/s2s_bridge/tests/test_genesis_tool_service.py — 15 passed. New tests pin: text absent at INFO + count summary still logged; text present at DEBUG. Verify-RED confirmed.
  • ruff clean; py_compile OK. Leak closure verified by enumerating every INFO log site across both bridges (the two text sites are the only ones; file-path/control-frame/count logs are not transcript).
  • Known gap: the ambient pipeline.py change has no unit test (sherpa_onnx unavailable locally) — symmetric to the tested s2s change; covered once the pytest CI job (separate PR) installs the deps.

Both the s2s and ambient bridges logged full conversation/utterance text at
INFO, landing plaintext in journald. Move the verbatim text to DEBUG and gate
verbosity behind an env (default INFO):

- s2s: genesis_tool_service per-turn text -> logger.debug (count summary stays
  INFO); main.py basicConfig honors S2S_LOG_LEVEL.
- ambient: pipeline utterance text -> DEBUG, keeping source/duration at INFO;
  server.py basicConfig honors AMBIENT_LOG_LEVEL. Mirrors MEETING/OMI_LOG_LEVEL.

Level env is guarded (getattr fallback to INFO) so a bad value can't crash the
bridge at startup. Verbatim text is recoverable via *_LOG_LEVEL=DEBUG; the
persist-failure local fallback file already preserves transcripts, so nothing
is lost. Tests pin text-absent-at-INFO + present-at-DEBUG + count-summary-logged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9b18258705

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread bridges/s2s_bridge/app/main.py Outdated
# Mirrors MEETING_LOG_LEVEL / OMI_LOG_LEVEL in the sibling bridges.
logging.basicConfig(
level=logging.INFO,
level=getattr(logging, os.environ.get("S2S_LOG_LEVEL", "INFO").upper(), logging.INFO),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate the resolved log level before configuring logging

If either environment variable contains an invalid value that happens to match another uppercase attribute of logging—for example, S2S_LOG_LEVEL=BASIC_FORMATgetattr returns that non-level value and logging.basicConfig() raises during import, preventing the bridge from starting instead of falling back to INFO as intended. The identical expression in bridges/ambient_bridge/server.py has the same failure mode; restrict the lookup to recognized level names or verify that the resolved value is an integer.

Useful? React with 👍 / 👎.

Comment thread bridges/s2s_bridge/app/main.py Outdated
# Mirrors MEETING_LOG_LEVEL / OMI_LOG_LEVEL in the sibling bridges.
logging.basicConfig(
level=logging.INFO,
level=getattr(logging, os.environ.get("S2S_LOG_LEVEL", "INFO").upper(), logging.INFO),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Stop overriding the configured level for the main module

When the supported python -m app.main entry point runs with S2S_LOG_LEVEL=WARNING or a stricter level, logger is named __main__, but the existing logging.getLogger("__main__").setLevel(logging.INFO) immediately lowers that logger's threshold back to INFO. As a result, the numerous INFO messages in main.py continue reaching journald despite the new environment setting; remove that override or derive it from the configured level.

Useful? React with 👍 / 👎.

Comment thread bridges/s2s_bridge/app/main.py Outdated
# Mirrors MEETING_LOG_LEVEL / OMI_LOG_LEVEL in the sibling bridges.
logging.basicConfig(
level=logging.INFO,
level=getattr(logging, os.environ.get("S2S_LOG_LEVEL", "INFO").upper(), logging.INFO),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Load dotenv before reading the new log-level setting

When the bridge is launched directly with configuration supplied through its built-in python-dotenv support, S2S_LOG_LEVEL exists only in .env, but this expression reads the process environment before dotenv.load_dotenv() runs at line 40. The bridge therefore remains at INFO and the advertised DEBUG transcript logging cannot be enabled this way, even though the other .env settings are read later during initialization; load dotenv before configuring logging.

Useful? React with 👍 / 👎.

- Load dotenv BEFORE basicConfig so S2S_LOG_LEVEL from .env is honored.
- Validate the resolved level (isinstance int) in both s2s + ambient, so a
  bad value that resolves to a non-level attr (e.g. logging.BASIC_FORMAT)
  falls back to INFO instead of crashing the bridge at startup.
- Drop the __main__->INFO override so S2S_LOG_LEVEL governs main's own logs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@WingedGuardian

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. More of your lovely PRs please.

Reviewed commit: 9b4ac2bdbb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@WingedGuardian
WingedGuardian merged commit 34a2a29 into main Aug 13, 2026
4 checks passed
@WingedGuardian
WingedGuardian deleted the fix/s2s-transcript-log-privacy branch August 13, 2026 22:13
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.

1 participant