From 291bd85c78c6d4dd0f994a50ee7ebd115faf754a Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Mon, 20 Apr 2026 16:43:43 -0700 Subject: [PATCH] Better env var/config knob exposure --- app/routers/debug.py | 39 ++++++++++++++++++++++++++++++++++----- tests/test_api.py | 26 ++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) diff --git a/app/routers/debug.py b/app/routers/debug.py index ac67b67..ca5bb20 100644 --- a/app/routers/debug.py +++ b/app/routers/debug.py @@ -64,7 +64,6 @@ class DebugRuntimeInfo(BaseModel): path_hash_mode_supported: bool channel_slot_reuse_enabled: bool channel_send_cache_capacity: int - remediation_flags: dict[str, bool] class DebugContactAudit(BaseModel): @@ -110,6 +109,21 @@ class DebugHealthSummary(BaseModel): basic_auth_enabled: bool = False +class DebugEnvironment(BaseModel): + connection_type: str + serial_port: str + serial_baudrate: int + tcp_host: str + tcp_port: int + ble_address: str + log_level: str + database_path: str + disable_bots: bool + enable_message_poll_fallback: bool + force_channel_slot_reconfigure: bool + load_with_autoevict: bool + + class DebugAppSettings(BaseModel): max_radio_contacts: int auto_decrypt_dm_on_advert: bool @@ -123,6 +137,7 @@ class DebugSnapshotResponse(BaseModel): captured_at: str system: DebugSystemInfo application: DebugApplicationInfo + environment: DebugEnvironment health: DebugHealthSummary settings: DebugAppSettings runtime: DebugRuntimeInfo @@ -203,6 +218,23 @@ def _coerce_live_max_channels(device_info: dict[str, Any] | None) -> int | None: return None +def _build_environment() -> DebugEnvironment: + return DebugEnvironment( + connection_type=settings.connection_type, + serial_port=settings.serial_port, + serial_baudrate=settings.serial_baudrate, + tcp_host=settings.tcp_host, + tcp_port=settings.tcp_port, + ble_address=settings.ble_address, + log_level=settings.log_level, + database_path=settings.database_path, + disable_bots=settings.disable_bots, + enable_message_poll_fallback=settings.enable_message_poll_fallback, + force_channel_slot_reconfigure=settings.force_channel_slot_reconfigure, + load_with_autoevict=settings.load_with_autoevict, + ) + + def _build_debug_app_settings(app_settings: AppSettings) -> DebugAppSettings: return DebugAppSettings( max_radio_contacts=app_settings.max_radio_contacts, @@ -393,6 +425,7 @@ async def debug_support_snapshot() -> DebugSnapshotResponse: captured_at=datetime.now(UTC).isoformat(), system=_build_system_info(), application=_build_application_info(), + environment=_build_environment(), health=_build_debug_health_summary(health_data, radio_state=radio_state), settings=_build_debug_app_settings(app_settings), runtime=DebugRuntimeInfo( @@ -404,10 +437,6 @@ async def debug_support_snapshot() -> DebugSnapshotResponse: path_hash_mode_supported=radio_runtime.path_hash_mode_supported, channel_slot_reuse_enabled=radio_runtime.channel_slot_reuse_enabled(), channel_send_cache_capacity=radio_runtime.get_channel_send_cache_capacity(), - remediation_flags={ - "enable_message_poll_fallback": settings.enable_message_poll_fallback, - "force_channel_slot_reconfigure": settings.force_channel_slot_reconfigure, - }, ), database=DebugDatabaseInfo( total_dms=message_totals["total_dms"], diff --git a/tests/test_api.py b/tests/test_api.py index bf60aa2..d3799be 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -203,6 +203,30 @@ class TestHealthEndpoint: class TestDebugEndpoint: """Test the debug support snapshot endpoint.""" + def test_build_environment_exposes_env_settings(self): + """_build_environment should expose env config without secrets.""" + from app.config import Settings + from app.routers.debug import _build_environment + + with patch( + "app.routers.debug.settings", + Settings( + serial_port="/dev/ttyUSB0", + serial_baudrate=115200, + log_level="DEBUG", + database_path="data/test.db", + ), + ): + env = _build_environment() + + assert env.connection_type == "serial" + assert env.serial_port == "/dev/ttyUSB0" + assert env.log_level == "DEBUG" + assert env.database_path == "data/test.db" + assert not hasattr(env, "ble_pin") + assert not hasattr(env, "basic_auth_password") + assert not hasattr(env, "basic_auth_username") + def test_support_snapshot_sanitizes_radio_probe_location_fields(self): """Debug radio probe should redact advertised lat/lon from self_info.""" from app.routers.debug import _sanitize_radio_probe_self_info @@ -300,6 +324,8 @@ class TestDebugEndpoint: assert "multi_acks_enabled" not in payload["radio_probe"] assert "max_channels" not in payload["runtime"] assert "path_hash_mode" not in payload["runtime"] + assert "environment" in payload + assert payload["environment"]["connection_type"] in ("serial", "tcp", "ble") assert payload["runtime"]["channels_with_incoming_messages"] == 0 assert payload["database"]["total_dms"] == 0 assert payload["database"]["total_channel_messages"] == 0