Move TOTP subprocess calls off the event loop#127
Conversation
Wrap all six totp.py call sites in bot.py with asyncio.to_thread() so the synchronous subprocess.run() calls (sudo cat/tee to root-owned files) run in the thread pool instead of blocking the event loop. A single verify_code() invocation can block for up to 15 seconds (three sudo calls with 5-second timeouts). With to_thread, the event loop remains responsive to other users during verification. totp.py itself is unchanged - it stays synchronous for CLI usage. Fixes #123
Review by KaiPR Review: Move TOTP subprocess calls off the event loopOverall: The core change is correct and cleanly implemented. All six call sites use proper WarningNo exception handling on threaded subprocess calls ( If any subprocess fails (TOTP binary missing, permission error, timeout), the exception propagates unhandled through the async handler. The critical path is if not await asyncio.to_thread(is_totp_configured):
return True # allow accessIf Minimum fix: wrap each SuggestionTOCTOU window slightly widened (
Clean
|
Summary
totp.pycall sites inbot.pywithasyncio.to_thread()so synchronoussubprocess.run()calls run in the thread pool instead of blocking the event loopverify_code()invocation can block for up to 15 seconds (threesudocalls with 5-second timeouts each), freezing message processing for all userstotp.pyitself is unchanged - it stays synchronous, which is correct for its CLI usage (python -m kai totp setup/status/reset)Call sites wrapped
is_totp_configured()_check_totpis_totp_configured()handle_messageget_lockout_remaining()handle_message(pre-verify)verify_code()handle_messageget_lockout_remaining()handle_message(post-verify failure)get_failure_count()handle_messageTest plan
to_thread- it callsmock()in the thread pool, returningmock.return_value)make checkclean (ruff lint + format)grep -c "to_thread" src/kai/bot.pyreturns 6grep "verify_code\|get_lockout_remaining\|get_failure_count\|is_totp_configured" src/kai/bot.py | grep -v "to_thread\|import\|def \|#"returns emptyFixes #123