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>
323 lines
45 KiB
Markdown
323 lines
45 KiB
Markdown
# 0. 문서 상태
|
||
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
|
||
|
||
- 상태: confirmed
|
||
- 문서 ID: dcy-004_20260520_token-issuance-and-lifecycle-overhaul
|
||
- 이슈명: 토큰 발급 및 관리 전면 정비 — client token 폐지, service-token rotate 정상화, 발급 권한 매트릭스, 만료·회전·자동 revoke 정책
|
||
- 작성 시각(KST): 2026-05-20T07:00+09:00
|
||
- 마지막 갱신 시각(KST): 2026-05-20T14:30+09:00 (draft-req 완료 — REQ-016~024 9건 생성. FR-1/2/3/4+10/5/6/7/8/9 분해, NFR-1~6 흡수, docs/srs/index.md 갱신)
|
||
- 대체됨: 없음
|
||
- 후속 Discovery 참조: 없음
|
||
- 생성된 REQ 참조: REQ-016, REQ-017, REQ-018, REQ-019, REQ-020, REQ-021, REQ-022, REQ-023, REQ-024
|
||
|
||
# 1. 계획 상태 요약
|
||
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
|
||
|
||
- 연계 문서 현황:
|
||
- 관련 점수 파일: 미생성
|
||
- 관련 TODO/메모: temp/2026-05-18_todo.md (운영 결함 inventory — service-token rotate 실패, HTTP daemon_id forward 누락, rebind/AuthContext 문서 누락), temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md (rotate verification 실패 기록 + Feedback Handoff에 NULL daemon_id 마이그레이션·daemon deactivation 자동 revoke 항목 잔존)
|
||
- 해석:
|
||
- 현행 토큰 surface는 4종(session/client/service/env)이 발급 경로·만료·회전·문서화에서 부분 정합성 결함을 누적해 왔고, 작은 결함들(client v1 mutation 진입, service-token rotate 실 DB 실패, HTTP body daemon_id silently drop, rebind/AuthContext 문서 누락)이 운영 단계에서 노출되었다. dcy-003에서 client token 폐지를 후속 Discovery에 명시 위임하면서 토큰 surface 정비를 단일 batch로 묶을 시점이 됐다.
|
||
- 본 Discovery는 토큰 surface 전면 정비 — client token 발급 경로 폐지, rotate 정상화, HTTP/CLI 일관성, 발급 권한 매트릭스, 만료·회전 정책, daemon deactivation 자동 revoke, NULL daemon_id legacy token 마이그레이션, env admin token 운영 SOP — 를 단일 주기로 처리한다.
|
||
|
||
# 2. 요구사항 판정 결과
|
||
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
|
||
|
||
- 판정: 신규
|
||
- 근거
|
||
- 활성 SDLC(dcy-001 asset catalog, dcy-002 tag 정책, dcy-003 portfolio v1)는 모두 도메인 모델·portfolio API에 집중하며 토큰 surface 정비를 다루는 active Discovery는 없다. dcy-003 §10 DATA_CLIENT_TOKEN_DEPRECATION_DISCOVERY가 본 Discovery의 명시 예고에 해당한다.
|
||
- 폐기된 옛 SDLC의 dcy-005(production-admin-bootstrap)는 운영 admin 토큰 절차의 일부만 다루며 본 Discovery의 토큰 surface 전반 정비 범위와는 부분 겹침에 그친다. 새 SDLC 리셋(2026-05-19) 이후 토큰 surface 정비는 미수용 backlog로 남아 있었다.
|
||
|
||
# 3. 문제점의 요약
|
||
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다. Discovery의 핵심 맥락을 가장 먼저 이해할 수 있어야 한다.
|
||
|
||
- 현재 상태
|
||
- 토큰 surface 4종: `app/core/auth.py`의 `AuthContext`가 `token_type ∈ {session, client, service, None(env)}` × `token_source ∈ {db, env}` 두 축으로 분류. 발급 경로는 `app/services/auth_service.py`의 `login`(session), `issue_client_token`(client), `issue_service_token`(service)에 분리. env token은 `settings.admin_tokens`/`trade_write_tokens`/`read_tokens`로 환경 변수에 박힌 형태.
|
||
- **결함 1 — client token 발급 경로 잔존**: `POST /auth/tokens` HTTP API([app/api/auth_routes.py:100-109](app/api/auth_routes.py#L100-L109))로 user가 본인 명의 발급 가능. dcy-003에서 v1 portfolio mutation 진입을 차단하기로 결정(token_type 게이트)했으나 발급 경로·`issue_client_token`·`CreateClientTokenRequest` 코드는 잔존.
|
||
- **결함 2 — service-token rotate 실 DB 실패**: `app/repositories/postgres.py:161-167`의 `idx_auth_tokens_service_name_unique` partial index가 `WHERE token_type='service'` 조건만 적용해 revoked row와 새 row가 같은 name으로 unique 충돌. [REL-006 verification](temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md#L74)에 "rotate insert 막힘" 검증 실패 기록.
|
||
- **결함 3 — POST /auth/service-tokens body `daemon_id` silently drop**: `app/schemas/auth.py:74-80`의 `IssueServiceTokenRequest.daemon_id` 필드는 노출되나 [app/api/auth_routes.py:131-137](app/api/auth_routes.py#L131-L137)에서 `service.issue_service_token` 호출 시 forward 하지 않음. CLI `--daemon-id`는 정상 forward하나 HTTP 경로만 daemon binding 불가.
|
||
- **결함 4 — `pfhub service-token rebind` CLI 문서화 누락**: [app/cli/commands/service_tokens.py:119-141](app/cli/commands/service_tokens.py#L119-L141)에 구현 완료, repository protocol/postgres 모두 존재하나 `docs/interface-contract.md` CLI surface 열거에 명시 없음.
|
||
- **결함 5 — runtime-flows.md AuthContext 필드 목록 daemon_id 누락**: `docs/runtime-flows.md:76`이 `user_id, token_type, permission, runtime_id`로 `daemon_id` 누락. 실제 코드는 보유.
|
||
- **정책 공백 1 — 발급 권한 매트릭스 부재**: 누가 어떤 token type을 발급/회전/폐기할 수 있는가가 코드에 흩어진 dependency(require_admin/require_read)로만 표현되고 baseline에 명시 없음.
|
||
- **정책 공백 2 — 만료·회전 정책 부재**: session은 `settings.session_token_ttl_hours` 강제, client는 발급 시 ttl 인자, service는 `--no-expiry` 허용. 일관 기준·강제 회전 주기 미정의.
|
||
- **정책 공백 3 — daemon deactivation 자동 revoke 부재**: `pfhub daemon update --deactivate`가 daemon `is_active=false`만 설정하고 바인딩 service token에 영향 없음 (REL-006 Feedback Handoff에 후속 항목으로 기록).
|
||
- **정책 공백 4 — NULL daemon_id legacy token 마이그레이션 자동화 부재**: REL-006 Feedback Handoff에 "rotate + rebind 자동화" 후보로 기록만 됨.
|
||
- **정책 공백 5 — env admin token 운영 SOP 부재**: 환경 변수 갱신·회전·폐기 절차가 baseline에 없음.
|
||
- 기대 상태
|
||
- 토큰 surface 3종(session/service/env)으로 정리, client token 발급 경로 코드 폐지
|
||
- service-token rotate가 실 DB에서 정상 작동
|
||
- HTTP/CLI 토큰 발급 surface가 일관 (daemon_id forward 누락 해소, rebind/AuthContext 문서 반영)
|
||
- 발급 권한 매트릭스·만료·회전 정책이 baseline에 명시
|
||
- daemon deactivation 시 바인딩 service token 자동 revoke (또는 grace period)
|
||
- NULL daemon_id legacy token 마이그레이션 자동화(CLI 서브커맨드 또는 스크립트, dry-run 포함)
|
||
- env admin token 회전·폐기 운영 SOP 정의
|
||
- 이해관계자
|
||
- admin/운영자: 토큰 발급·회전·폐기 SOP의 일관성 확보, env admin token 회전 책임
|
||
- 로그인 user: client 폐지 후 자동화 use case의 대체 경로 명확화
|
||
- daemon 운영 주체 (trex-basic 등): rotate/rebind 정상 작동, daemon deactivation 동기화
|
||
- repository-maintainers: 토큰 surface 정합성 유지, baseline 문서 일관성
|
||
- 제약사항
|
||
- dcy-003(draft, 본 Discovery 결정 완료 후 진행 재개 예정)이 client token v1 portfolio mutation 진입 차단을 이미 결정(OQ-5 Answered 2026-05-20) → 본 Discovery는 그 결정 위에서 발급 경로 자체 폐지로 진행. 진행 순서: 본 Discovery 결정·confirm 완료 → dcy-003 잔여 항목(CONFIRM 2건, DATA 1건, dcy-001 REQ Approved 선행 조건) 처리 재개
|
||
- 기존 발급된 client token이 운영 중일 수 있어 폐지 마이그레이션 절차 필요 (DB inventory 사전 조사)
|
||
- service-token rotate 정상화는 unique index 변경을 포함 → Alembic migration 추가
|
||
- env admin token 회전은 코드 변경과 환경 변수 갱신을 동시 수행해야 운영 차단 회피
|
||
- 본 Discovery 범위는 토큰 surface 정비에 한정. 신규 토큰 타입 도입(OAuth, PAT 등), 외부 SSO 통합, 감사 로그 정책은 별도 Discovery로 분리
|
||
|
||
## 주요 사용자 시나리오
|
||
사용자 또는 운영자가 실제로 어떤 상황에서 이 변경을 필요로 하는지 대표 시나리오를 적는다.
|
||
|
||
- admin이 alice의 trex-basic daemon용 service-token을 발급한다. `pfhub service-token issue NAME --user alice --daemon-id DID` (CLI) 또는 `POST /auth/service-tokens` body `{"name": "...", "username": "alice", "daemon_id": "DID", ...}` (HTTP) 두 경로 모두 동일하게 daemon_id binding이 작동한다.
|
||
- admin이 alice의 service-token을 회전한다. `pfhub service-token rotate NAME --force` 실행 시 새 raw token이 발급되고 unique index 충돌 없이 성공한다. 새 토큰의 `runtime_id`는 새로 발급, `daemon_id`는 유지(invariant).
|
||
- admin이 alice의 daemon을 비활성화한다(`pfhub daemon update DID --deactivate`). 해당 daemon에 바인딩된 service token이 자동 revoke되어 후속 v1 호출은 HTTP 401/403.
|
||
- user가 본인 명의 client token 발급을 시도하면 HTTP 404 또는 405 (path 자체 제거)를 받는다. 자동화 use case는 admin이 발급한 service token으로 흡수 또는 OQ-8 결정에 따른 새 경로.
|
||
- 운영자가 NULL daemon_id legacy service token을 일괄 마이그레이션한다. `pfhub service-token migrate-null-daemon --dry-run`으로 inventory 확인 후 매핑 결정, 실제 실행으로 rebind 또는 revoke 처리.
|
||
- env admin token 회전 시점에 운영자가 SOP에 따라 신규 admin token을 환경 변수에 추가 → service rolling restart → 구 admin token 환경 변수 제거 절차를 단계적으로 수행한다.
|
||
|
||
# 4. 이번에 정의할 변경
|
||
이번 Discovery에서 다루는 핵심 변경 항목을 목록으로 적는다. 기능, 구조, 규칙 변경을 함께 포함해도 된다.
|
||
|
||
- client token 발급 경로 코드 폐지: `POST /auth/tokens` HTTP route, `issue_client_token` service 메서드, `CreateClientTokenRequest`/`IssuedClientTokenResponse` schemas, 관련 테스트
|
||
- service-token rotate unique index 보강: `idx_auth_tokens_service_name_unique`에 `AND revoked_at IS NULL` 조건 추가 또는 동등 효과 정책 채택
|
||
- `POST /auth/service-tokens` HTTP body의 `daemon_id` forward 정상화: `auth_routes.py:131-137` 호출 시 `payload.daemon_id` 전달
|
||
- 토큰 발급 권한 매트릭스·만료·회전 정책을 baseline 문서(`docs/interface-contract.md`, `docs/runtime-flows.md`)에 명시
|
||
- daemon deactivation 시 바인딩 service token 자동 revoke 동작 추가 (`pfhub daemon update --deactivate` 흐름)
|
||
- NULL daemon_id legacy token 마이그레이션 자동화: 신규 CLI 서브커맨드(`pfhub service-token migrate-null-daemon`) 또는 일괄 스크립트, dry-run + report 포함
|
||
- env admin token 운영 SOP 정의: `docs/runtime-flows.md` 또는 `README.md`에 회전·폐기 절차 추가
|
||
- baseline 문서 정합성 회복: `pfhub service-token rebind` CLI 명세 추가, `AuthContext` 필드 목록에 `daemon_id` 추가, 토큰 surface 전체 명세 갱신
|
||
|
||
## 영향 범위
|
||
변경이 미칠 코드/문서/운영 범위를 명시해 REQ 정제와 batch planning에서 추정 누락을 줄인다.
|
||
|
||
- 코드 영향:
|
||
- `app/api/auth_routes.py`: `POST /auth/tokens` 라우트 + 관련 dependency 제거; `POST /auth/service-tokens`에서 `payload.daemon_id` forward 보정
|
||
- `app/services/auth_service.py`: `issue_client_token` 메서드 제거; `revoke_tokens_for_daemon(daemon_id)` 등 신규 헬퍼 도입 검토
|
||
- `app/schemas/auth.py`: `CreateClientTokenRequest`, `IssuedClientTokenResponse` 제거
|
||
- `app/repositories/postgres.py`: `idx_auth_tokens_service_name_unique` 조건 보강(`ensure_schema` + Alembic migration), daemon deactivation 시 바인딩 token 일괄 revoke를 위한 repository 메서드 추가
|
||
- `app/repositories/portfolio_repository.py`: Protocol에 신규 메서드 선언
|
||
- `app/cli/commands/daemon.py`: `update --deactivate` 동작에 자동 revoke 호출 추가
|
||
- `app/cli/commands/service_tokens.py`: `migrate-null-daemon` 신규 서브커맨드(또는 별도 모듈) 추가
|
||
- `migrations/versions/`: 신규 Alembic revision — partial index 조건 보강(CONCURRENTLY 적용 권장)
|
||
- `tests/`: client 폐지 후 호출 경로 회귀 테스트, rotate 정상화 통합 테스트, daemon deactivation 자동 revoke 회귀 테스트, NULL daemon_id 마이그레이션 dry-run 테스트
|
||
- 문서 영향:
|
||
- `docs/interface-contract.md`: client 발급 경로 제거, `pfhub service-token rebind` 명세 추가, 토큰 발급 권한 매트릭스 표 추가, 만료·회전 정책 명시
|
||
- `docs/runtime-flows.md`: 토큰 발급/회전/리바인드/자동 revoke flow, `AuthContext` 필드 목록에 `daemon_id` 추가, env admin token 운영 SOP flow
|
||
- `docs/data-model.md`: `auth_tokens` 인덱스 변경 반영, 토큰 surface 3종 정리
|
||
- `docs/project-structure.md`: 신규 마이그레이션 스크립트/CLI 서브커맨드 추가에 따른 패키지 책임 갱신(필요 시)
|
||
- `README.md`: daemon provisioning runbook 갱신 (auto-revoke 정책 반영), env admin token 회전 SOP 섹션 추가
|
||
- 운영 영향:
|
||
- 기존 발급된 client token 보유자 영향: 폐지 release 전 inventory 조사 + 보유자 통보 필요 (R-1)
|
||
- service-token rotate migration이 partial index recreate를 포함하므로 운영 시간대·CONCURRENTLY 적용 검토 필요 (R-2)
|
||
- daemon deactivation 자동 revoke가 in-flight 거래에 영향 가능 — graceful drain 절차 검토 (R-3)
|
||
- env admin token 회전 SOP가 잘못 적용되면 운영 차단 발생 — 다단계 절차 + rollback 명시 (R-4)
|
||
|
||
# 5. 요구사항 목록
|
||
실제로 구현과 검증으로 이어질 요구사항을 기능/비기능으로 나누어 정리하고, 범위 경계를 명확히 적는다.
|
||
|
||
## 기능 요구사항
|
||
- FR-1: client token 발급 경로 코드 폐지 — `POST /auth/tokens` HTTP route, `app/services/auth_service.py:issue_client_token`, `CreateClientTokenRequest`, `IssuedClientTokenResponse`, 관련 테스트 모두 제거. 기존 발급된 client token 처리 정책(OQ-1 Answered 2026-05-20): **즉시 코드 폐지로 진행**. DATA-1 inventory(2026-05-20)에서 active client token 0건 확인됐으므로 별도 revoke 스크립트 실행이나 grace period 운영 불필요. 폐지 release 직전 동일 쿼리로 재확인하는 절차만 release plan에 포함한다.
|
||
- FR-2: `idx_auth_tokens_service_name_unique` partial index 조건 보강 (OQ-2 Answered 2026-05-20) — 현행 `WHERE token_type='service'`에 `AND revoked_at IS NULL` 추가. `ensure_schema()`(app/repositories/postgres.py)와 신규 Alembic migration 양쪽 갱신. 기존 active service token 행에는 영향 없음. migration은 `CREATE INDEX CONCURRENTLY` 적용으로 R-2 lock 영향 최소화(autocommit 외부 트랜잭션 필요).
|
||
- FR-3: `POST /auth/service-tokens` HTTP body `daemon_id` forward 정상화 — `app/api/auth_routes.py:131-137`이 `payload.daemon_id`를 `service.issue_service_token` 호출에 전달. CLI `--daemon-id`와 동등 동작 보장. 비존재/비활성 daemon 지정 시 동일한 ValueError → HTTP 4xx 변환.
|
||
- FR-4: baseline 문서 정합성 회복 — `docs/interface-contract.md` CLI surface에 `service-token rebind` 추가, `docs/runtime-flows.md` `AuthContext` 필드 목록에 `daemon_id` 추가, 토큰 surface 전체(env vs DB, session/service 만료 정책, rotate runtime_id/daemon_id invariant) 명세 갱신.
|
||
- FR-5: 토큰 발급 권한 매트릭스 정의 — type(session/service/env) × 발급자 권한(admin/trade-write/read) × 발급 가능 여부의 표를 `docs/interface-contract.md`에 명시. (client는 폐지 후 표에서 제거.) 매트릭스 위반 호출은 HTTP 403 fail-closed. **admin이 자기 자신에게 service token을 발급하는 경로는 허용**(OQ-4 Answered 2026-05-20)하며 admin은 본인 user_id를 대상으로 daemon-bound 또는 unbound service token을 발급할 수 있다. 발급된 self-service token의 권한 한도는 admin이 명시한 permission이지만, 관행상 자기 자동화에 필요한 최소 권한(예: trade-write)으로 발급할 것을 매트릭스 주석에 권장.
|
||
- FR-6: 토큰 만료·회전 정책 정의 (OQ-5 Answered 2026-05-20 — 현행 유지) — type별 기본 TTL을 `docs/runtime-flows.md`에 명시: session = `settings.session_token_ttl_hours`(현행 값 유지), service = 365일 default, env = 만료 없음. `no_expiry` 허용 범위는 **service 한정**(session/client 발급 경로 모두 거부). 강제 회전 주기는 본 정책에 두지 않으며 env admin token 회전 주기만 OQ-6에서 별도 결정한다. 정책 위반 발급(예: `no_expiry`를 session에 요청)은 HTTP 422 또는 ValueError로 거부.
|
||
- FR-7: daemon deactivation 자동 revoke (OQ-3 Answered 2026-05-20 — 즉시 revoke) — `pfhub daemon update <id> --deactivate` 실행 시 해당 daemon에 바인딩된 service token(`auth_tokens.daemon_id == <id>` AND `revoked_at IS NULL`)을 **동일 트랜잭션 내에서 즉시 revoke**. graceful drain은 daemon 측 책임이며 trex-basic 등 daemon 구현이 종료 전 in-flight 처리를 수행할 것을 release plan에 명시. CLI에 `--keep-tokens` 같은 회피 옵션은 노출하지 않는다(정책 일관성). 동작 evidence는 revoked_at 갱신 timestamp로 추적.
|
||
- FR-8: NULL daemon_id legacy token 마이그레이션 (OQ-7 Answered 2026-05-20 — 운영자 수동 매핑) — 신규 CLI 서브커맨드 `pfhub service-token migrate-null-daemon` 또는 동등 진단 스크립트. dry-run(`--dry-run`) 옵션으로 inventory + 매핑 후보(같은 `user_id`의 active daemon 목록) 출력. 실제 적용은 운영자가 token별로 `pfhub service-token rebind NAME --daemon-id ID`(매핑) 또는 `pfhub service-token revoke NAME`(폐기)을 수동 실행한다. 자동 매핑/auto-create daemon 경로는 노출하지 않는다(권한 escalation 위험). idempotent 작동(재실행 안전) 필수. 본 batch release 시점에는 DATA-2 inventory에서 발견된 admin 테스트 fixture 1건을 운영자가 직접 revoke 처리(별도 운영 작업).
|
||
- FR-9: env admin token 운영 SOP 정의 (OQ-6 Answered 2026-05-20 — 분기 권장 주기, 강제 없음) — `docs/runtime-flows.md` 또는 `README.md`에 명시할 항목: (1) **권장 회전 주기**: 분기(3개월). 강제 회전·만료 알람 없음. (2) **운영자 권한 매트릭스**: env admin token 추가/제거는 인프라 운영자 권한. (3) **다단계 회전 절차**: 신규 token 생성(`python -c "import secrets; print(secrets.token_urlsafe(48))"` 권장) → `PORTFOLIOHUB_ADMIN_TOKENS`에 신규 token 추가(콤마 분리) → service rolling restart → 운영 검증 → 구 token 제거 → service rolling restart. (4) **회전 트리거 조건**: 정기(분기) / token leak 의심 / 운영자 교체·퇴사 / 보안 사고 대응. (5) **rollback 절차**: 회전 실패 시 환경 변수에 구 token 재추가 후 rolling restart. (6) **token 강도 가이드**: 최소 48-byte URL-safe 무작위. 평문 단어·예측 가능한 패턴 금지.
|
||
- FR-10: `docs/data-model.md`, `docs/project-structure.md` baseline 갱신 — `auth_tokens` 인덱스 변경 반영, 토큰 surface 3종 정리, 신규 CLI/migration 파일 책임 반영(필요 시).
|
||
|
||
## 비기능 요구사항
|
||
성능, 운영성, 일관성, 자동화 안정성 등 기능 외 요구사항을 적는다.
|
||
|
||
- NFR-1: client token 폐지 후 dcy-003 v1 portfolio mutation 동작에 회귀 없음. `token_type='client'` 자체가 생성 불가하므로 dcy-003 FR-1/3/4/5의 게이트 로직은 유지하되 실 트래픽에서는 자연 사라짐.
|
||
- NFR-2: service-token rotate는 `docker-compose.local-test.yml` 실 DB 환경에서 unique 위반 없이 성공해야 한다. staging 환경 통합 검증 필수.
|
||
- NFR-3: 기존 service-token의 daemon_id invariant(rotate 시 daemon_id 유지)는 본 Discovery 변경 후에도 보존. runtime_id는 매 rotate마다 갱신.
|
||
- NFR-4: 토큰 발급 권한 매트릭스의 위반 호출은 HTTP 403 fail-closed로 통일.
|
||
- NFR-5: NULL daemon_id 마이그레이션은 idempotent. 재실행 시 안전, 부분 실패 시 보고된 예외 목록과 일치.
|
||
- NFR-6: `idx_auth_tokens_service_name_unique` 변경 migration은 운영 환경에서 long-running lock 회피를 위해 CONCURRENTLY 옵션 적용 또는 운영 시간대 적용 권장.
|
||
|
||
## 범위 경계
|
||
이번 반복에서 반드시 포함할 범위와 명시적으로 제외할 범위를 구분한다.
|
||
|
||
- In Scope
|
||
- client token 발급 경로 코드 폐지 + 기존 토큰 처리 절차
|
||
- service-token rotate unique index 보강 + migration
|
||
- HTTP `POST /auth/service-tokens` body daemon_id forward 정상화
|
||
- 토큰 발급 권한 매트릭스 정의 (baseline 명시)
|
||
- 토큰 만료·회전 정책 정의 (baseline 명시)
|
||
- daemon deactivation 자동 revoke
|
||
- NULL daemon_id legacy token 마이그레이션 자동화 (CLI + dry-run)
|
||
- env admin token 운영 SOP 정의
|
||
- baseline 문서 정합성 회복 (rebind, AuthContext daemon_id, 토큰 surface 전체)
|
||
- Out of Scope
|
||
- 신규 토큰 타입 도입 (Personal Access Token, OAuth, API key 서비스) — 별도 Discovery
|
||
- user 신원/SSO 통합 — 별도 Discovery
|
||
- 토큰 사용 감사 로그·audit trail 정책 — 별도 Discovery (단, daemon deactivation 자동 revoke의 logging은 본 Discovery 범위)
|
||
- token 시각 동기화·시간대 정책 — 별도 운영 Discovery
|
||
- service-token 발급량/rate-limit 정책 — 별도 Discovery
|
||
|
||
# 6. 리스크/가정 목록
|
||
불확실성과 전제 조건을 분리해서 적는다. 리스크는 영향과 완화 방안을, 가정은 현재 판단의 전제를 기록한다.
|
||
|
||
## 리스크
|
||
- R-1 (해소됨, 잔여 모니터링 Low): DATA-1 inventory(2026-05-20)에서 active client token 0건 확인됐으므로 폐지 release 시 보유자 영향 없음. 잔여 리스크는 release 직전 새로 발급된 client token이 0건이 아닐 가능성뿐이다.
|
||
- 완화 방안: 폐지 release 직전 `SELECT COUNT(*) FROM auth_tokens WHERE token_type='client' AND revoked_at IS NULL` 재확인을 release plan에 포함. >0이면 발급 사유 조사 후 즉시 revoke 또는 release 보류.
|
||
- R-2 (High): `idx_auth_tokens_service_name_unique` partial index 재생성이 long-running lock을 유발하면 운영 중 auth_tokens 쓰기가 블로킹된다.
|
||
- 완화 방안: Alembic migration에 `CREATE INDEX CONCURRENTLY` 적용(단, autocommit 외부 트랜잭션 필요), 운영 시간대 적용, staging 환경 dry-run으로 lock 시간 사전 측정. partial index 변경이 어려우면 OQ-2의 대안(name suffix 부여)으로 전환.
|
||
- R-3 (Medium): daemon deactivation 즉시 revoke(OQ-3 Answered)는 정책상 단순하나 in-flight ledger trade가 진행 중인 시점에 deactivation이 실행되면 거래 기록이 누락될 수 있다.
|
||
- 완화 방안: graceful drain은 daemon 구현 측 책임으로 분리(trex-basic이 종료 신호 수신 시 in-flight 마무리 후 종료). release plan에 deactivation SOP를 "(1) daemon 프로세스 종료/drain 완료 확인 → (2) `pfhub daemon update --deactivate` 실행 → (3) 자동 revoke" 순서로 명시. deactivation 호출 전 daemon process health check 권장.
|
||
- R-4 (Medium): env admin token 회전 절차가 잘못 적용되면 운영자가 admin HTTP/CLI 접근을 잃어 회복 자체가 불가.
|
||
- 완화 방안: env admin token 회전 SOP에 다단계 절차(신규 token 추가 → 동시 운영 기간 → 구 token 제거) + rollback 절차 명시. 회전 중 fallback admin 접근 경로(예: DB 직접 접근으로 토큰 재생성) 보장.
|
||
- R-5 (해소됨, 잔여 모니터링 Low): OQ-7에서 자동 매핑/auto-create daemon 경로를 채택하지 않기로 결정(2026-05-20)했으므로 권한 escalation 리스크는 본 Discovery 범위에서 발생하지 않는다. DATA-2 inventory(2026-05-20)에서 NULL daemon service token 1건만 발견됐고 운영자 확인 결과 테스트 fixture로 분류돼 본 batch 시점 release에 운영자가 직접 revoke 처리한다.
|
||
- 완화 방안: release 직전 동일 쿼리로 NULL daemon token 0건 재확인. >0이면 운영자가 token별 `rebind` 또는 `revoke` 수동 실행. `pfhub service-token migrate-null-daemon --dry-run` 보고에 `token.user_id`와 매핑 후보 daemon의 `owner_user_id` 일치 여부를 명시 표시(자동 매핑은 하지 않더라도 진단 데이터 제공).
|
||
- R-6 (Low): 토큰 발급 권한 매트릭스 정의가 현행 dependency 체계(require_admin/require_read)와 불일치하면 구현 단계에서 광범위 회귀 가능.
|
||
- 완화 방안: 매트릭스 정의 단계에서 코드 grep으로 현행 dependency 사용처와 1:1 매핑 확인, 불일치 시 매트릭스나 dependency 중 한쪽 우선 조정.
|
||
- R-7 (High, 본 Discovery 범위 밖 — 별도 hotfix 권장): DATA-3 수집(2026-05-20) 결과 **prod 환경에 env admin token이 약한 placeholder 값(`admin-token-789` — 예측 가능한 영문+숫자 패턴)으로 운영 중**. 환경 변수 leak 또는 단순 brute-force 시 즉시 admin 권한 escalation 가능. 본 Discovery는 토큰 surface 정비 batch이므로 단일 token 값 교체는 본 Discovery 외부 hotfix로 분리하며, 본 Discovery FR-9의 SOP가 이 hotfix의 표준 절차를 제공한다.
|
||
- 완화 방안: 본 Discovery confirm-discovery 이전이라도 보안상 즉시 hotfix 권장. 절차(FR-9 SOP 미리 적용): (1) 강한 무작위 token 생성, (2) `PORTFOLIOHUB_ADMIN_TOKENS`에 신규 token 추가, (3) service rolling restart, (4) 운영 검증, (5) 구 token(`admin-token-789`) 제거, (6) service rolling restart, (7) 회전 evidence 기록. hotfix 추적은 §10 DATA_PROD_ADMIN_TOKEN_HOTFIX 항목으로 관리.
|
||
|
||
## 가정
|
||
아직 확정되지 않았지만 현재 문서 작성의 기반이 되는 전제를 적는다.
|
||
|
||
- A-1: 본 Discovery 후 토큰 surface는 4종(session/client/service/env)에서 3종(session/service/env)으로 정리된다. user의 자동화 use case는 admin이 발급한 service token으로 흡수한다(OQ-8 Answered 2026-05-20 — admin 발급 service token으로 충분). PAT 등 새 토큰 타입 도입은 본 batch 후 운영 피드백을 보고 별도 Discovery에서 판단한다.
|
||
- A-2: dcy-003 FR-1/3/4/5의 token_type 게이트(`token_type ∈ {service, client}` 거부)는 client 폐지 후 "client는 더 이상 발급 불가"로 자연 강화. 게이트 자체는 env token 외부 침투 방어로 유지.
|
||
- A-3: `idx_auth_tokens_service_name_unique` 보강은 단일 Alembic migration으로 처리 가능. 기존 active service token 행 데이터에는 변경 없음.
|
||
- A-4: daemon deactivation 자동 revoke는 기존 daemon registry CRUD(dcy-006 모델 — `daemons` 테이블, `is_active` 플래그) 위에 얹는 동작이며 daemon 모델 자체에는 변경 없음.
|
||
- A-5: env admin token 회전 SOP는 환경 변수 다중 등록(예: `PORTFOLIOHUB_ADMIN_TOKENS` 콤마 분리)이 가능한 현행 구조([app/core/auth.py:43-50](app/core/auth.py#L43-L50)의 `_resolve_permission`이 set 기반)를 활용해 다단계 운영이 가능하다.
|
||
- A-6: 본 Discovery는 기존 service-token의 정상 운영 중 daemon binding을 변경하지 않는다. NULL daemon_id token만 마이그레이션 대상이며, 이미 daemon-bound token은 그대로 유지.
|
||
|
||
## 오픈 질문
|
||
답을 아직 모르는 항목을 적는다. 추정으로 덮지 말고 REQ 초안 작성 이전에 확인할 질문으로 남긴다.
|
||
|
||
- OQ-1: 기존 발급된 client token 처리 정책 — 폐지 release에서 즉시 일괄 revoke vs grace period(기간 명시) vs 자연 만료 대기?
|
||
- 상태: Answered
|
||
- 처리 방안: 즉시 코드 폐지로 결정(2026-05-20). DATA-1 inventory에서 active client token 0건 확인됐으므로 grace period/revoke 스크립트 불필요. 폐지 release 직전 동일 쿼리로 재확인하는 절차만 release plan에 포함.
|
||
- 반영 위치: FR-1 본문 갱신 완료, R-1 완화 방안 갱신 완료, §10 DECIDE에서 DECIDE_CLIENT_TOKEN_DEPRECATION_POLICY 처리 완료, §10 DATA에서 DATA_CLIENT_TOKEN_INVENTORY 수집 완료(0건).
|
||
- OQ-2: `idx_auth_tokens_service_name_unique` 보강 방식 — `AND revoked_at IS NULL` partial 조건 추가 vs rotate 시 revoked row의 name suffix 부여(예: `<name>__revoked_<ts>`) vs unique 제약을 active row로만 한정하는 별도 모델 재설계?
|
||
- 상태: Answered
|
||
- 처리 방안: partial 조건 추가로 결정(2026-05-20). `WHERE token_type='service' AND revoked_at IS NULL`. 단일 Alembic migration + CONCURRENTLY 적용으로 R-2 lock 영향 완화. ensure_schema와 migration 양쪽 갱신.
|
||
- 반영 위치: FR-2 본문 갱신 완료, §10 DECIDE에서 DECIDE_ROTATE_UNIQUE_INDEX_STRATEGY 처리 완료.
|
||
- OQ-3: daemon deactivation 자동 revoke 동작 — 즉시 revoke vs grace period(예: 30분 drain) vs 운영자 별도 확정 명령?
|
||
- 상태: Answered
|
||
- 처리 방안: 즉시 revoke로 결정(2026-05-20). `pfhub daemon update --deactivate` 실행 시 동일 트랜잭션 내에서 바인딩 service token 모두 revoke. graceful drain은 daemon 측 책임으로 분리. release plan에 deactivation SOP(drain 완료 확인 → deactivate 실행 → 자동 revoke) 명시.
|
||
- 반영 위치: FR-7 본문 갱신 완료, R-3 완화 방안 갱신 완료, §10 DECIDE에서 DECIDE_DAEMON_DEACTIVATION_REVOKE_TIMING 처리 완료.
|
||
- OQ-4: 토큰 발급 권한 매트릭스에서 admin이 자기 자신에게 service token을 발급할 수 있는가? (admin이 본인 user_id로 service token 발급해 운영 자동화에 사용하는 시나리오)
|
||
- 상태: Answered
|
||
- 처리 방안: 허용으로 결정(2026-05-20). admin이 본인 user_id 대상으로 daemon-bound 또는 unbound service token 발급 가능. 권한 한도는 admin이 명시한 permission이며, 관행상 최소 권한(예: trade-write) 발급을 매트릭스 주석에 권장.
|
||
- 반영 위치: FR-5 본문 갱신 완료, §10 DECIDE에서 DECIDE_ADMIN_SELF_SERVICE_TOKEN 처리 완료.
|
||
- OQ-5: 토큰 type별 기본 TTL — session(현행 `session_token_ttl_hours`), service(현행 365일 default), env(만료 없음) 유지 vs 신규 정책?
|
||
- 상태: Answered
|
||
- 처리 방안: 현행 유지로 결정(2026-05-20). session/service/env 각 정책이 운영상 안정. `no_expiry` 옵션은 service 한정. 강제 회전 주기는 OQ-6 env admin 결정으로 흡수.
|
||
- 반영 위치: FR-6 본문 갱신 완료, §10 DECIDE에서 DECIDE_TOKEN_TTL_POLICY 처리 완료.
|
||
- OQ-6: env admin token 회전 주기 — 권장 주기(예: 분기/반기/연간), 강제 회전 여부?
|
||
- 상태: Answered
|
||
- 처리 방안: 분기(3개월) 권장 주기, 강제 회전 없음으로 결정(2026-05-20). 만료 알람 없음. SOP에 회전 트리거 조건(정기·leak 의심·운영자 교체·보안 사고)과 다단계 절차·rollback·token 강도 가이드를 명시.
|
||
- 반영 위치: FR-9 본문 갱신 완료, §10 DECIDE에서 DECIDE_ENV_ADMIN_ROTATION_INTERVAL 처리 완료, §10 DATA에서 DATA_ENV_ADMIN_TOKEN_ROTATION_HISTORY 수집 완료. 추가로 R-7(prod 약한 token hotfix) 신규 추가 및 §10 DATA_PROD_ADMIN_TOKEN_HOTFIX 추적 항목 추가.
|
||
- OQ-7: NULL daemon_id legacy token 마이그레이션 매핑 전략 — 운영자 수동 매핑(token name 기반 추정) vs daemon auto-create 후 매핑 vs 폐기 후 재발급?
|
||
- 상태: Answered
|
||
- 처리 방안: 운영자 수동 매핑으로 결정(2026-05-20). CLI는 dry-run으로 inventory + 매핑 후보(같은 user_id의 active daemon) 진단만 제공하고 실제 적용은 운영자가 `rebind` 또는 `revoke`를 명시적으로 실행. 자동 매핑/auto-create daemon은 권한 escalation 리스크로 채택하지 않음. DATA-2 inventory(2026-05-20)에서 발견된 admin 테스트 fixture 1건은 운영자가 직접 revoke 처리.
|
||
- 반영 위치: FR-8 본문 갱신 완료, R-5 완화 방안 갱신 완료, §10 DECIDE에서 DECIDE_NULL_DAEMON_MIGRATION_STRATEGY 처리 완료, §10 DATA에서 DATA_NULL_DAEMON_TOKEN_INVENTORY 수집 완료(1건, 테스트 fixture).
|
||
- OQ-8: client token 폐지 후 user 자동화 경로 — admin 발급 service token으로 충분한가, user가 본인 명의로 발급 가능한 새 토큰 타입(예: Personal Access Token)을 후속 Discovery에서 도입할지?
|
||
- 상태: Answered
|
||
- 처리 방안: admin 발급 service token으로 충분으로 결정(2026-05-20). 본 batch 후 토큰 surface 3종(session/service/env)으로 종료. PAT 등 새 토큰 타입은 본 batch 후 운영 피드백을 확인한 뒤 필요 시 별도 Discovery에서 검토.
|
||
- 반영 위치: A-1 갱신 완료, §10 DECIDE에서 DECIDE_USER_AUTOMATION_PATH_AFTER_CLIENT 처리 완료.
|
||
|
||
### 오픈 질문 처리 기준
|
||
- `Open`: 아직 답이 없고 다음 단계 진행에 영향이 있는 상태다. 문서에 유지한다.
|
||
- `Answered`: 답이 확정된 상태다. 관련 본문/요구사항에 반영한 뒤 오픈 질문 목록에서는 제거하거나 resolved 기록으로 이동한다.
|
||
- `Deferred`: 지금 단계에서 결정하지 않고 다음 단계 또는 후속 이슈로 넘기는 상태다. 넘길 대상과 이유를 반드시 적는다.
|
||
- `Dropped`: 더 이상 유효하지 않은 질문이다. 삭제 사유를 짧게 남기고 목록에서 제거한다.
|
||
- 처리 원칙 1: 질문만 남기지 말고 반드시 `상태`, `처리 방안`, `종료 조건`을 함께 적는다.
|
||
- 처리 원칙 2: 답이 나온 질문은 본문 반영 없이 단순 삭제하지 않는다. 반영 위치를 먼저 갱신한다.
|
||
- 처리 원칙 3: High 영향 질문은 `Deferred` 처리 후 처리 방안에 REQ 인계 추적 방식을 명시한다.
|
||
|
||
# 7. 초기 성공 기준
|
||
이 변경이 성공했다고 판단할 기준을 측정 가능하게 정의한다. 가능하면 데이터 출처까지 연결한다.
|
||
|
||
- S-1: codebase에서 client token 관련 코드 모두 제거 — `POST /auth/tokens` 라우트, `issue_client_token` 메서드, `CreateClientTokenRequest`/`IssuedClientTokenResponse` schemas grep 결과 0건.
|
||
- S-2: 실 DB 환경에서 `pfhub service-token issue NAME --user U --no-expiry` → `pfhub service-token rotate NAME --force` 시퀀스가 unique 위반 없이 성공. 새 token의 `runtime_id`는 갱신, `daemon_id`는 유지.
|
||
- S-3: `POST /auth/service-tokens` body `{"daemon_id": "<id>", "name": "...", "username": "..."}`로 호출 시 issued token의 `auth_token.daemon_id == <id>` 반환.
|
||
- S-4: `pfhub daemon update <id> --deactivate` 실행 후 해당 daemon에 바인딩된 service token이 모두 `revoked_at IS NOT NULL` 상태로 전환 (또는 OQ-3 결정 방식대로 grace period 후 전환).
|
||
- S-5: `pfhub service-token migrate-null-daemon --dry-run` 실행 시 NULL daemon_id token inventory + 매핑 후보가 보고로 출력. 실 실행 후 NULL daemon_id token이 0건 또는 예외 보고 목록과 일치.
|
||
- S-6: baseline 문서(`docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/data-model.md`)에 토큰 surface 변경(client 제거, rebind 추가, AuthContext daemon_id, 발급 권한 매트릭스, 만료·회전 정책, 자동 revoke flow, env admin SOP) 모두 반영. baseline grep으로 누락 없음 확인.
|
||
- S-7: env admin token 회전 SOP가 `docs/runtime-flows.md` 또는 `README.md`에 명시되어 있고, staging 환경에서 dry-run으로 회전 절차 실행 가능.
|
||
|
||
## 측정 방식 및 데이터 출처
|
||
각 성공 기준을 어떻게 측정할지와 어떤 데이터나 문서를 근거로 쓸지 적는다.
|
||
|
||
- S-1 측정 방식: codebase grep(`grep -rn "issue_client_token\|CreateClientTokenRequest\|POST /auth/tokens"`) + pytest로 폐지된 endpoint 호출 시 HTTP 404/405 확인 / 데이터 출처: codebase grep 결과 + 신규 `tests/test_auth_surface_after_client_deprecation.py`
|
||
- S-2 측정 방식: 실 DB(`docker-compose.local-test.yml`) 환경 통합 테스트 / 데이터 출처: 신규 `tests/test_service_token_rotate_integration.py` (DB 의존 마킹)
|
||
- S-3 측정 방식: pytest로 HTTP request 시뮬레이션 / 데이터 출처: 갱신 `tests/test_auth.py`
|
||
- S-4 측정 방식: pytest로 daemon deactivation 후 바인딩 token 상태 검증 / 데이터 출처: 갱신 `tests/test_daemon_registry.py`
|
||
- S-5 측정 방식: CLI smoke + 마이그레이션 스크립트 단위 테스트 (dry-run 출력 검증 + idempotency 회귀) / 데이터 출처: 신규 `tests/test_null_daemon_migration.py`
|
||
- S-6 측정 방식: baseline 문서 grep + 사람 리뷰 (`grep -n "service-token rebind\|AuthContext.daemon_id\|발급 권한 매트릭스\|만료·회전 정책" docs/`) / 데이터 출처: 갱신된 baseline 문서
|
||
- S-7 측정 방식: SOP 문서 + 운영 환경 dry-run 실행 결과 / 데이터 출처: 갱신 `README.md` 또는 `docs/runtime-flows.md`의 env admin SOP 섹션
|
||
|
||
# 8. REQ로 넘기기 전 확인 체크
|
||
REQ 초안 작성 단계로 넘기기 전에 무엇이 확정되어야 하는지 확인한다. 미완료 항목은 후속 결정이 필요함을 뜻한다.
|
||
|
||
- [x] 범위(In Scope/Out of Scope) 확정
|
||
- 기준: OQ-1~8 전체 Answered 완료(2026-05-20). In/Out of Scope 경계 최종화 완료. CONFIRM 2건 모두 Confirmed. DATA-1/2/3 Collected. R-7(prod 약한 token hotfix)는 본 Discovery 범위 밖 별도 운영 작업으로 분리.
|
||
- [x] 성공 지표의 측정 방식/데이터 출처 확정
|
||
- 기준: S-1~7 측정 방식이 §7 측정 방식 섹션에 각각 신규/갱신 pytest 시나리오(`tests/test_auth_surface_after_client_deprecation.py`, `tests/test_service_token_rotate_integration.py`, `tests/test_auth.py`, `tests/test_daemon_registry.py`, `tests/test_null_daemon_migration.py`) 또는 SOP 문서(`README.md`/`docs/runtime-flows.md`)로 명시 연결됨.
|
||
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
|
||
- 기준: R-1(client token 보유자 식별)은 DATA-1 0건 확인으로 해소(잔여 Low), R-2(migration lock)는 CONCURRENTLY 적용으로 완화. R-7(prod 약한 admin token)은 본 Discovery 외부 hotfix로 분리. 책임자 = repository-maintainers(본 Discovery 단계). REQ 작성 시 개별 작업 책임자가 REQ Impacted Area에 반영될 예정.
|
||
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
|
||
- 기준: OQ-1~8 전체 Answered 완료(2026-05-20). Open 상태 OQ 0건.
|
||
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
|
||
- 기준: §11 Confirmed By에 Justin 지정 완료(2026-05-20).
|
||
- [x] dcy-003 의존성 확인
|
||
- 기준: dcy-003(draft)의 client token 폐지 결정이 본 Discovery로 이관됐음을 §3 제약사항·§9 참조에 반영 완료. 진행 순서: 본 Discovery 결정·confirm 완료 → dcy-003 잔여 항목 처리 재개(사용자 결정 2026-05-20).
|
||
- [x] 운영 inventory 수집
|
||
- 기준: DATA-1(client token, 0건) / DATA-2(NULL daemon service token, 1건 테스트 fixture) / DATA-3(env admin token 1개, 약한 placeholder, 회전 이력 없음) 모두 Collected(2026-05-20). DATA_PROD_ADMIN_TOKEN_HOTFIX는 본 Discovery 외부 hotfix 추적 항목으로 잔존.
|
||
|
||
# 9. 파일 처리 결과
|
||
이 Discovery 문서 생성/갱신 결과와 참조 문서를 기록한다. 이후 추적과 리뷰 시 근거로 사용한다.
|
||
|
||
- 처리 결과: 생성
|
||
- 생성 경로: docs/discovery/dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md
|
||
- 참조 문서:
|
||
- docs/discovery/dcy-003_20260520_user-portfolio-mutation-v1-api-migration.md (선행 Discovery — client token 폐지 결정 위임, DATA_CLIENT_TOKEN_DEPRECATION_DISCOVERY 명시 예고)
|
||
- temp/2026-05-18_todo.md (운영 결함 inventory — service-token rotate 실패, HTTP body daemon_id 무시, rebind/AuthContext 문서 누락)
|
||
- temp/20260519_old_sdlc/releases/rel-006_20260513_daemon-model-implementation.md (rotate verification 실패 기록 + Feedback Handoff에 NULL daemon_id 마이그레이션·daemon deactivation 자동 revoke 잔존)
|
||
- app/core/auth.py, app/services/auth_service.py, app/api/auth_routes.py, app/schemas/auth.py, app/repositories/postgres.py, app/cli/commands/service_tokens.py, app/cli/commands/daemon.py (저장소 근거)
|
||
- docs/interface-contract.md, docs/runtime-flows.md, docs/data-model.md, docs/project-structure.md (본 Discovery로 갱신 예정)
|
||
|
||
# 10. 사용자 결정 필요 항목 요약
|
||
사용자나 승인자가 결정해야 하는 항목만 모아 정리한다. 결정, 확인, 데이터 요청을 구분해서 적는다.
|
||
|
||
## DECIDE
|
||
- ~~{{DECIDE_CLIENT_TOKEN_DEPRECATION_POLICY}}~~: **Decided** — 즉시 코드 폐지. DATA-1 inventory(2026-05-20) active client token 0건 확인, grace period/revoke 스크립트 불필요. release 직전 재확인만 release plan에 포함. FR-1·R-1·OQ-1 본문 반영 완료. (OQ-1 Answered 2026-05-20)
|
||
- ~~{{DECIDE_ROTATE_UNIQUE_INDEX_STRATEGY}}~~: **Decided** — `idx_auth_tokens_service_name_unique`에 `AND revoked_at IS NULL` partial 조건 추가. CONCURRENTLY migration으로 R-2 lock 완화. FR-2·OQ-2 본문 반영 완료. (OQ-2 Answered 2026-05-20)
|
||
- ~~{{DECIDE_DAEMON_DEACTIVATION_REVOKE_TIMING}}~~: **Decided** — daemon deactivation 즉시 revoke. graceful drain은 daemon 측 책임 분리. release plan에 SOP 명시. FR-7·R-3·OQ-3 본문 반영 완료. (OQ-3 Answered 2026-05-20)
|
||
- ~~{{DECIDE_ADMIN_SELF_SERVICE_TOKEN}}~~: **Decided** — admin이 본인 user_id 대상으로 service token 발급 허용. 권한 한도는 명시 permission, 최소 권한 발급 권장. FR-5·OQ-4 본문 반영 완료. (OQ-4 Answered 2026-05-20)
|
||
- ~~{{DECIDE_TOKEN_TTL_POLICY}}~~: **Decided** — 현행 TTL 정책 유지(session/service/env 각 정책). `no_expiry`는 service 한정. 강제 회전 주기는 env admin(OQ-6)에서 별도 결정. FR-6·OQ-5 본문 반영 완료. (OQ-5 Answered 2026-05-20)
|
||
- ~~{{DECIDE_ENV_ADMIN_ROTATION_INTERVAL}}~~: **Decided** — 분기(3개월) 권장 주기, 강제 회전 없음. SOP에 다단계 절차·rollback·token 강도 가이드 명시. FR-9·OQ-6 본문 반영 완료. (OQ-6 Answered 2026-05-20)
|
||
- ~~{{DECIDE_NULL_DAEMON_MIGRATION_STRATEGY}}~~: **Decided** — 운영자 수동 매핑. CLI는 dry-run 진단(inventory + 매핑 후보 user_id 일치 표시)만 제공, 실제 rebind/revoke는 운영자가 명시적으로 실행. 자동 매핑/auto-create 미채택(권한 escalation 리스크). DATA-2 inventory(2026-05-20) 1건(admin 테스트 fixture)은 운영자가 직접 revoke 처리. FR-8·R-5·OQ-7 본문 반영 완료. (OQ-7 Answered 2026-05-20)
|
||
- ~~{{DECIDE_USER_AUTOMATION_PATH_AFTER_CLIENT}}~~: **Decided** — admin 발급 service token으로 충분. PAT 등 새 토큰 타입은 본 batch 후 운영 피드백 확인 후 별도 Discovery에서 검토. A-1·OQ-8 본문 반영 완료. (OQ-8 Answered 2026-05-20)
|
||
|
||
## CONFIRM
|
||
- ~~{{CONFIRM_DISCOVERY_SCOPE_OPTION_C}}~~: **Confirmed** — 옵션 C(Wide — 토큰 발급/관리 전면 정비) 범위로 9개 변경 항목을 단일 batch로 다루는 결정 사용자 동의 완료. (2026-05-20)
|
||
- ~~{{CONFIRM_DCY003_DEPENDENCY}}~~: **Confirmed** — dcy-003(draft)에서 결정된 client token v1 mutation 진입 차단 정책이 본 Discovery로 이관됐다는 의존성 관계 사용자 동의. 진행 순서: 본 Discovery 결정·confirm 완료 → dcy-003 잔여 항목 처리 재개. §3 제약사항·§8 체크리스트·§11 Handoff Rationale 반영 완료. (2026-05-20)
|
||
|
||
## DATA
|
||
- ~~{{DATA_CLIENT_TOKEN_INVENTORY}}~~: **Collected (2026-05-20)** — active client token 0건. `SELECT ... WHERE token_type='client' AND revoked_at IS NULL` 결과 0행. OQ-1 즉시 폐지 결정의 evidence.
|
||
- ~~{{DATA_NULL_DAEMON_TOKEN_INVENTORY}}~~: **Collected (2026-05-20)** — NULL daemon_id active service token 1건. token name `admin`(user `500e336a-...`, created 2026-05-11, no_expiry). 운영자 확인 결과 **테스트 fixture, 운영 용도 없음**. release 시점에 운영자가 `pfhub service-token revoke admin` 수동 실행으로 정리. OQ-7 수동 매핑 결정의 evidence.
|
||
- ~~{{DATA_ENV_ADMIN_TOKEN_ROTATION_HISTORY}}~~: **Collected (2026-05-20)** — prod 환경 `PORTFOLIOHUB_ADMIN_TOKENS=admin-token-789` 1개 운영 중. **token 강도: 약함**(예측 가능 영문+숫자 placeholder, dev default 추정). 회전 이력 없음(최초 발급 후 미회전). SOP 부재. OQ-6 회전 주기 결정 및 R-7 hotfix 트리거의 evidence.
|
||
- ~~{{DATA_PROD_ADMIN_TOKEN_HOTFIX}}~~: **External tracking (본 Discovery 외부 hotfix)** — prod 환경 `admin-token-789` 교체는 §6 R-7로 추적. 본 Discovery는 SOP(FR-9)를 정의하고 hotfix는 그 SOP를 적용하는 운영 작업이라 본 Discovery confirm-discovery 게이트 차단 사유는 아님. 실행 evidence는 운영 노트에 별도 기록.
|
||
|
||
# 11. Discovery Freeze
|
||
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다. 내용 재작성 없이 원본 섹션을 링크로 대신한다.
|
||
|
||
## 섹션 참조
|
||
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
|
||
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
|
||
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
|
||
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
|
||
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
|
||
|
||
## Freeze 플래그
|
||
- Handoff Decision: 승인
|
||
- Handoff Rationale: OQ-1~8 전체 Answered + CONFIRM 2건 모두 Confirmed + DATA-1/2/3 모두 Collected + §8 체크리스트 7건 모두 [x] 완료(2026-05-20). confirm-discovery 게이트 통과로 본 Discovery는 `confirmed` 상태로 승급되어 REQ 초안 작성 가능. **외부 의존 항목**: R-7(prod 약한 admin token) hotfix는 본 Discovery 외부 운영 작업이라 별도 처리, dcy-003 잔여 처리 + dcy-001 REQ Approved는 batch 통합 시점 의존성으로 §3 제약사항에 명시됨.
|
||
- Ready for REQ Drafting: true
|
||
- Confirmed By: Justin
|
||
- Confirmed At (KST): 2026-05-20T11:00+09:00
|