本卡屬 FR-166(母卡 CM-母卡號(建完補)),卡 P1-3,階段 P1 登入、批 2。只改 evidence-agent。依賴:P0-4。
Agent 端記住主系統下發的離線操作者名單,存本機管理員與離線密碼的雜湊,判斷誰能登、有沒有被鎖、有沒有到期、斷線超過 30 天沒有、有沒有同步過。
認證核心:argon2、鎖定單調時鐘、四種失敗同一回應;必含突變測試
extend:local_login.py 既有 LOCAL_ADMIN_SUB+_create_local_session 加密碼路徑,不新身分;session 走既有 SessionStore 加 revoke_by_sub;observer 照 _observe_allowed_targets 形狀。登記型檔集中:enroll.py 這張卡一次掛 offline_operators/restart_flag/outbox.drain 三個 observer(後兩支先放 no-op 檔),P2-9/P3-1 只填自己的檔、不碰 enroll.py
repo:~/Projects/Billows/Audit-Manager/evidence-agent
改既有:core/enroll.py 411(掛三個 observer)
改既有:core/console/session.py 75
改既有:core/console/local_login.py 188
改既有:core/heartbeat_status.py
新開:core/console/local_auth.py
新開:core/offline_operators.py
新開:core/local_credentials.py
新開:core/restart_flag.py(no-op 殼)
新開:core/outbox.py(no-op 殼)
細節在 design.md 對應段(~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-166-2610-agent-standalone-console/design.md §5,開 design.html 看導覽),本段只列本卡要做到的點:
core/offline_operators.py:observe(data) 整份覆蓋寫 SQLite;撤銷→SessionStore.revoke_by_sub;received_at。core/local_credentials.py:argon2id;failed_count/locked_until_mono(time.monotonic);12 碼。core/console/local_auth.py:verify(sub, pw) 判定順序照 design §5.1;四種失敗回同一個結果物件;local-admin 走既有 LOCAL_ADMIN_SUB+_create_local_session。enroll.py 本卡是 owner:一次掛 offline_operators.observe/restart_flag.observe/outbox.drain(後兩支先寫 no-op 檔)。allowed_targets.received and offline_operators.received_at;30 天:now - last_heartbeat_ok > 30d。local_auth.verify(sub, pw) 對名單外/密碼錯/到期/被鎖回同一個結果物件;5 次錯鎖 15 分(monotonic);撤銷→該 sub session 全消;30 天停用旗標對;未同步判定=allowed_targets.received and operators.received
對照雛形:~/Projects/Billows/Audit-Manager/compliance-manager-be/docs/features/FR-166-2610-agent-standalone-console/prototype/index.html(上方「跟著走」)。