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:
2026-05-21 01:03:39 +00:00
co-authored by Claude Sonnet 4.6
parent d4b841bc37
commit c815ce39b6
48 changed files with 3573 additions and 117 deletions
@@ -0,0 +1,100 @@
# REQ-017: service-token rotate unique index 보강
- Status: Approved
- Type: Migration
- Priority: High
- Owner: Justin Ko
## Intent
- 현행 `idx_auth_tokens_service_name_unique` partial index가 `WHERE token_type='service'` 조건만 적용해 revoked row와 새 row가 같은 name으로 unique 충돌을 일으켜 `pfhub service-token rotate`가 실 DB에서 insert 실패하는 결함을 정상화한다(REL-006 verification 실패 기록).
- index 조건에 `AND revoked_at IS NULL`을 추가해 active service token name만 unique 유지하도록 보강한다.
## Requirement
- Alembic migration 신규 작성: 기존 `idx_auth_tokens_service_name_unique`를 drop하고 `(name)` partial index를 `WHERE token_type='service' AND revoked_at IS NULL` 조건으로 재생성한다.
- migration은 `CREATE INDEX CONCURRENTLY` 적용으로 운영 lock 영향을 최소화한다(autocommit 외부 트랜잭션 필요 — Alembic op.execute로 직접 SQL 실행).
- `app/repositories/postgres.py:161-167` `ensure_schema()`의 `idx_auth_tokens_service_name_unique` 정의도 동일 조건으로 갱신한다.
- 기존 active service token 행 데이터에는 변경 없음. 데이터 backfill 불요.
- rotate 동작은 기존 코드(`app/services/auth_service.py:rotate_service_token`, `app/repositories/postgres.py:1502-1549`)를 변경하지 않으며, index 보강만으로 정상 작동해야 한다.
## Acceptance Criteria
1. Given 신규 Alembic revision 적용 후, When `pfhub service-token issue NAME --user U --no-expiry` → `pfhub service-token rotate NAME --force` 시퀀스 실행, Then 두 명령 모두 unique 위반 없이 성공한다(실 DB `docker-compose.local-test.yml` 환경).
2. Given AC-1 시퀀스 실행 후, When `SELECT name, token_type, revoked_at FROM auth_tokens WHERE name = '<NAME>'`, Then revoked row 1건 + active row 1건이 존재한다.
3. Given AC-1 rotate 결과, When 새 token의 `runtime_id`와 `daemon_id`를 확인, Then runtime_id는 갱신, daemon_id는 유지(rotate invariant 보존).
4. Given `ensure_schema()` 단독 실행, When 새 partial index 조건이 적용, Then 동일 조건(`WHERE token_type='service' AND revoked_at IS NULL`)이 PostgreSQL에 반영된다.
5. Given migration이 운영 환경에서 적용, When CREATE INDEX CONCURRENTLY가 실행, Then auth_tokens write 작업이 long-running lock 없이 진행된다(staging 환경 사전 검증).
## Impacted Area
- Docs:
- `docs/data-model.md` — auth_tokens 인덱스 변경 반영 (REQ-019에서 처리)
- Modules:
- `migrations/versions/` — 신규 Alembic revision (예: `20260520_NNNN_service_token_unique_index_fix.py`). `op.execute("DROP INDEX IF EXISTS ...")` + `op.execute("CREATE UNIQUE INDEX CONCURRENTLY ... WHERE token_type='service' AND revoked_at IS NULL")` 패턴
- `app/repositories/postgres.py:161-167` — `ensure_schema()`의 idx 정의 조건 갱신
- Tests:
- 신규 `tests/test_service_token_rotate_integration.py` (실 DB 의존 마킹) — AC-1~3 검증
- 기존 `tests/test_daemon_registry.py::test_rotate_*` — 신규 index 조건에서 통과 확인
## Notes
- 이 결함은 REL-006 verification에 "rotate insert 막힘" 검증 실패로 이미 기록됨([temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md:74](temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md#L74)).
- CREATE INDEX CONCURRENTLY는 transactional block 밖에서 실행 필요. Alembic revision에서 `autocommit_block`을 사용하거나 `op.execute()`로 직접 SQL 실행해야 한다.
- 기존 운영 환경에 revoked service token이 있다면 새 partial index 생성 시점에 자동 적용되며, 별도 데이터 정리 불요.
- Source: dcy-004 FR-2, dcy-004 NFR-2/NFR-3/NFR-6, dcy-004 OQ-2 결정(partial 조건 추가).
## Change Log
### CHG-20260520-02
- Date: 2026-05-20
- Author: Justin Ko
#### Change Summary
- Status: Proposed → Approved
#### Intent
- 승인 게이트 통과: 필수 필드(Status/Type/Priority/Owner/Intent/Requirement/AC/Impacted Area/Change Log) 모두 충족, AC 구체성 확인 완료, blocker 없음
#### Acceptance Criteria Delta
- No change
#### Impacted Area Delta
- No change
#### Delivery Trace Delta
- Batches: none (미정)
- Releases: none (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: promote-to-approved
- Follow-up Action: batch 계획 편입
### CHG-20260520-01
- Date: 2026-05-20
- Author: Justin Ko
#### Change Summary
- 최초 생성 — dcy-004 confirm-discovery 후 draft-req 실행. dcy-004 FR-2 (rotate unique index 보강) 산출물.
#### Intent
- rotate 실 DB 실패 결함 해소 + R-2(migration lock) 완화 적용.
#### Acceptance Criteria Delta
- Added:
- AC-1~5 최초 작성
#### Impacted Area Delta
- Docs: docs/data-model.md (REQ-019에서 처리)
- Modules: migrations/versions/, app/repositories/postgres.py — 최초 지정
- Tests: tests/test_service_token_rotate_integration.py (신규), tests/test_daemon_registry.py — 최초 지정
#### Delivery Trace Delta
- Batches: none (미정)
- Releases: none (미정)
#### Revalidation Impact
- Reverification Needed: no
- Existing Implementation Invalidated: no
- Status Recommendation: keep-current
- Follow-up Action: confirm-req 후 batch 계획 편입; staging 환경에서 CONCURRENTLY migration dry-run으로 lock 시간 측정
#### Validation Plan
- 실 DB(`docker-compose.local-test.yml`) 환경에서 AC-1~3 시퀀스 검증; staging 환경에서 AC-5 lock 측정