Files
portfolio-hub/docs/batches/bat-007_20260520_daemon-lifecycle-token-enforcement/design.md
T
genichinandClaude Sonnet 4.6 c815ce39b6 feat: dcy-003/004 토큰 surface 개편 및 daemon lifecycle 정책 구현 (BAT-006/007)
BAT-006 (REQ-016/017/018):
- client token 발급 경로 제거 (issue_client_token, POST /auth/tokens, 관련 스키마/테스트)
- service-token rotate unique index revoked_at IS NULL 조건 추가 (Alembic CONCURRENTLY 마이그레이션)
- POST /auth/service-tokens body daemon_id forward 누락 보정 (1-line fix)

BAT-007 (REQ-022/023):
- daemon deactivate 시 바인딩 service token 자동 revoke (CLI --force 옵션 + revoke_tokens_by_daemon_id)
- NULL daemon_id legacy token 진단 CLI (migrate-null-daemon --dry-run)

거버넌스 문서: dcy-003/004 Discovery, REQ-016~023/025~032 신규, BAT-006/007 전체 산출물

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 01:03:39 +00:00

3.8 KiB

BAT-007 Design

  • Status: confirmed
  • Batch: BAT-007

Architecture Summary

  • REQ-022: daemon CLI의 --deactivate 분기에 신규 service 메서드(revoke_tokens_for_daemon) 호출을 추가한다. 해당 메서드는 repository의 revoke_tokens_by_daemon_id를 통해 UPDATE auth_tokens SET revoked_at=NOW() WHERE daemon_id=? AND revoked_at IS NULL을 실행한다.
  • REQ-023: service_tokens CLI에 migrate_null_daemon 서브커맨드를 추가한다. --dry-run만 허용하며 실제 변경 없이 inventory + 매핑 후보를 출력한다.
  • 두 변경 모두 신규 repository 메서드 → service 헬퍼 → CLI 연결 패턴으로 일관된다.

Changed Areas

  • Docs:
    • 없음 (baseline 문서 갱신은 REQ-019~021/024 별도 배치)
  • Modules:
    • app/repositories/portfolio_repository.py — revoke_tokens_by_daemon_id(daemon_id: str) -> int 추상 메서드 (REQ-022); list_service_tokens_with_null_daemon() -> list[AuthToken] 추상 메서드 (REQ-023)
    • app/repositories/postgres.py — 두 메서드 구현
    • app/services/auth_service.py — revoke_tokens_for_daemon(daemon_id: str) -> int (REQ-022); list_null_daemon_service_tokens() -> list[AuthToken], find_candidate_daemons_for_user(user_id: str) -> list[Daemon] (REQ-023)
    • app/cli/commands/daemon.py — update_daemon --deactivate 분기에 revoke 호출 추가 (REQ-022)
    • app/cli/commands/service_tokens.py — migrate_null_daemon 서브커맨드 추가 (REQ-023)
  • Tests:
    • tests/test_daemon_registry.py — REQ-022 AC-1~4 회귀 추가 (_DaemonRepo에 revoke_tokens_by_daemon_id 추가)
    • tests/test_null_daemon_migration.py — 신규, FakeRepo 기반 REQ-023 AC-1~5

Key Decisions

  1. REQ-022 CLI 호출 패턴: daemon.py _with_service는 callback에 (repository, app_context)를 전달한다. repo.update_daemon(...) 후 repo.revoke_tokens_by_daemon_id(daemon_id)를 순차 호출하여 deactivate + revoke를 처리. AuthService.revoke_tokens_for_daemon은 서비스 계층 테스트 목적으로 추가.
  2. REQ-022 --force 옵션 추가: update_daemon 명령에 force: bool = typer.Option(False, "--force") 추가. --deactivate와 함께 사용 시 --force 없으면 확인 프롬프트 표시. 자동 revoke는 --force 여부와 무관하게 항상 실행.
  3. REQ-022 회피 옵션 없음: --keep-tokens 옵션 미노출 (dcy-004 OQ-3 결정 — 정책 일관성). deactivation이면 항상 revoke.
  4. REQ-022 재활성화 시 자동 복구 없음: --activate 분기에서는 revoke_tokens_by_daemon_id 호출 없음. 이미 revoke된 token은 운영자가 신규 발급해야 함.
  5. REQ-023 dry-run 전용 설계: --dry-run 없이 실행 시 typer.Exit(code=1) + 수동 rebind/revoke 안내 메시지. 자동 변경 코드 없음.
  6. REQ-023 매핑 후보 조회: service_tokens.py _with_service는 callback에 (service, app_context) 전달. service.list_null_daemon_service_tokens()와 service.find_candidate_daemons_for_user(user_id)를 사용해 출력 구성.
  7. revoke_tokens_by_daemon_id 반환값: revoke된 token 수(int) 반환 — AC-3(no-op, 0 반환) 검증에 사용.

Edge Cases

  • REQ-022: daemon에 바인딩된 token이 0건인 경우 → revoke_tokens_by_daemon_id 0 반환, 정상 종료 (AC-3)
  • REQ-022: --activate 재활성화 시 token revoke 호출 안 함 (분기 조건: deactivate=True일 때만)
  • REQ-023: NULL daemon active token이 0건인 경우 → "no NULL daemon tokens" 메시지 + exit 0 (AC-2)
  • REQ-023: 매핑 후보 daemon이 0건인 token → dry-run 출력에 별도 경고 표시

Architecture Impact

  • none (runtime-flows, project-structure 변경 없음 — REQ-019 배치에서 docs 반영)

Reference Doc Update Plan

  • project-structure.md: none (이 배치에서 변경 없음)
  • runtime-flows.md: none (이 배치에서 변경 없음)