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>
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# 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 (이 배치에서 변경 없음)
|
||||
Reference in New Issue
Block a user