Files
portfolio-hub/docs/srs/Interface/req-012_service-token-rotate-cli.md
T
genichin e6b6a8715b feat: implement daemon model with registry and single-asset assignment (BAT-010, REQ-023/028)
Core Implementation (BAT-010):
- Add daemon registry schema and CRUD operations (req-023)
- Implement single-daemon asset assignment constraint (one asset per daemon)
- Add daemon_id field to service_token for daemon identification
- Add daemon.py CLI module for daemon management commands
- Add 20260513_0016_daemon_model.py migration for daemon tables

API Changes:
- GET /v1/assets: include daemon_id in AssetResponse
- POST /v1/assets/{asset_id}/ledger/trades: require daemon token binding
- GET /v1/user_info: include daemon metadata
- Enhanced service-token rotate to preserve daemon bindings

CLI Commands:
- pfhub daemon register --name <name> --runtime-id <runtime-id>
- pfhub daemon list
- pfhub daemon get <daemon-id>
- pfhub daemon revoke <daemon-id>

Requirements (REQ-023, REQ-028):
- REQ-023: Daemon Registry and Service Token Daemon Binding (Interface, Approved)
  * AC-1: daemon CRUD operations
  * AC-2: service token daemon binding for POST /v1/assets/{asset_id}/ledger/trades
  * AC-3: single daemon per asset (exact-one assignment model)
- REQ-028: README.md Daemon Provisioning Runbook update (Documentation, Approved)

Documentation:
- Add dcy-006 Discovery for daemon-id and single-daemon asset assignment
- Update README.md with Daemon Provisioning Runbook section
- Add BAT-010 batch documentation with planning, implementation, verification
- Update rel-006 release document (daemon model implementation)
- Update data-model.md with daemon and daemon_asset_assignment tables
- Update interface-contract.md with daemon registry APIs
- Update runtime-flows.md with daemon lifecycle flows

Breaking Changes:
- REQ-005~009, REQ-011~012 status reverted to Approved due to revised contract
  * daemon_id field addition and exact-one asset assignment requirement
  * Existing Implemented evidence invalidated by contract revision
  * BAT-001/002/008 re-verification required

Testing:
- Add test_daemon_registry.py for daemon CRUD operations
- Add test_daemon_assignment.py for single-asset assignment constraints
- Update test_auth.py, test_cli.py, test_v1.py for daemon support
- Verify daemon token binding in trade endpoint authorization

Impact:
- Database: 2 new tables (daemons, daemon_asset_assignments)
- Migration: 20260513_0016 creates daemon model schema
- CLI: 4 new daemon subcommands
- API: daemon_id propagation through v1 endpoints
- Verification: BAT-001/002/008 re-verification gates next release
2026-05-13 23:07:24 +00:00

10 KiB

req-012: service-token rotate CLI 명령 구현

  • Status: Approved
  • Type: Interface
  • Priority: Medium
  • Owner: repository-maintainers

Intent

  • 발급된 service-token의 원문이 노출되었거나 주기적 교체가 필요한 경우, token을 revoke 없이 in-place로 갱신할 수 있는 CLI 명령이 필요하다.
  • 기존 revoke → re-issue 방식은 token_id, user_id, permission, expires_at, created_at 등 메타데이터가 변경되므로, 운영 시스템에서 동일한 이름으로 참조하는 통합 구성을 다시 설정해야 하는 불편이 있다.
  • rotate는 token_hash, token_prefix, runtime_id만 교체하고 daemon_id를 포함한 나머지 메타데이터를 유지하여, 동일한 token 이름과 daemon binding을 유지한 채 새 token 원문만 배포할 수 있게 한다. 단, runtime_id를 소비하는 시스템은 rotate 후 새 토큰 기준으로 runtime_id를 재조회해야 한다.

Requirement

  • pfhub service-token rotate <name> [--force] CLI 명령을 구현한다.
  • rotate는 기존 auth_token 행의 token_hash, token_prefix, runtime_id를 새 값으로 UPDATE하고, daemon_id는 유지해야 한다.
  • id, user_id, name, permission, token_type, expires_at, created_at, daemon_id는 변경하지 않는다.
  • rotate 대상 token이 존재하지 않으면 오류를 반환한다.
  • rotate 대상 token이 이미 revoked 상태이면 오류를 반환한다.
  • rotate 대상 token의 expires_at이 NULL이 아닌 경우(만료일이 설정된 토큰) 오류를 반환한다. rotate는 --no-expiry로 발급된 영구 토큰에만 허용된다.
  • --force 없이 실행하면 확인 프롬프트를 표시한다.
  • rotate 성공 시 새 token 원문을 1회 출력하고, 이후 list/조회에서는 token_prefix만 표시된다. rotate 출력 메타데이터에는 daemon_id가 포함되어야 한다.

Acceptance Criteria

  1. Given expires_at IS NULL인 유효한 service-token 이름, When pfhub service-token rotate <name> --force를 실행, Then DB 행의 token_hash, token_prefix, runtime_id가 새 값으로 변경되고 id, permission, expires_at(NULL), daemon_id는 기존과 동일하다.
  2. Given rotate 성공, When CLI 출력, Then 새 token 원문이 화면에 1회 출력되고 "이 토큰은 지금 1회만 표시" 안내 메시지와 함께 daemon_id 메타데이터가 표시되어야 한다.
  3. Given 존재하지 않는 token 이름, When rotate, Then 오류 메시지를 출력하고 종료 코드 1을 반환한다.
  4. Given revoked 상태의 token, When rotate, Then 오류 메시지를 출력하고 종료 코드 1을 반환한다.
  5. Given expires_at이 NULL이 아닌(만료일이 설정된) token, When rotate, Then "만료일이 설정된 토큰은 rotate할 수 없습니다" 오류를 출력하고 종료 코드 1을 반환한다.
  6. Given --force 없이 실행, When CLI, Then 확인 프롬프트를 표시하고 거절 시 변경 없이 종료한다.

Impacted Area

  • Docs: docs/project-structure.md (변경 없음), docs/runtime-flows.md (변경 없음)
  • Modules:
    • app/repositories/portfolio_repository.py — rotate_service_token Protocol stub 추가
    • app/repositories/postgres.py — rotate_service_token UPDATE 구현 추가
    • app/services/auth_service.py — rotate_service_token(name) 서비스 메서드 추가
    • app/cli/commands/service_tokens.py — rotate 명령 및 daemon_id 출력 추가
    • tests/test_cli.py — stub repo에 rotate_service_token 추가 및 테스트 케이스 추가
  • Tests:
    • tests/test_cli.py: rotate 성공, 없는 이름, revoked 상태, daemon_id 유지/출력 케이스

Notes

  • expires_at은 rotate 시 변경하지 않는다. 만료일 변경이 필요하면 revoke + re-issue를 사용한다.
  • rotate는 --no-expiry로 발급된 영구 토큰(expires_at IS NULL)에만 허용된다. 만료일이 설정된 토큰은 rotate 대상이 아니다. 해당 토큰은 만료 후 re-issue하는 것이 권장 패턴이다.
  • rotate는 runtime_id도 새 값으로 갱신하지만 daemon_id는 유지한다. trex-basic 등 runtime_id를 소비하는 시스템은 rotate 후 새 토큰으로 다시 인증하고 GET /v1/user_info를 재호출해 새 runtime_id를 재인식해야 한다.
  • 기존 runtime_id를 캐싱한 상태로 계속 사용하면 consumer 식별 불일치가 발생할 수 있다. rotate는 "토큰 원문 교체 시 token name과 메타데이터를 유지한다"는 의미이지, runtime_id까지 불변이라는 뜻은 아니다.
  • rotate는 daemon provisioning 경로가 아니며, unknown daemon 자동 생성/업서트를 수행하면 안 된다.
  • DB 스키마 변경(migration)은 없다. 기존 auth_tokens 테이블의 기존 컬럼만 UPDATE한다.
  • 구조/인터페이스/런타임 흐름 변경 없음 → minor-change fast path 적용.

Change Log

CHG-20260513-01

  • Date: 2026-05-13
  • Author: repository-maintainers

Change Summary

  • rotate REQ에 daemon_id 불변식과 CLI diagnostic 출력 요구를 추가하고, 기존 Implemented 상태를 Approved로 되돌림

Intent

  • dcy-006은 rotate 이후에도 daemon_id가 stable identity로 유지되어야 하고, 운영자는 rotate 출력과 provenance 절차에서 daemon_id를 확인할 수 있어야 한다. BAT-002/REL-002 구현은 runtime_id lifecycle만 다루므로 revised REQ를 충족하지 못한다.

Acceptance Criteria Delta

  • Added:
    • none
  • Updated:
    • AC-1: daemon_id 유지 불변식 추가
    • AC-2: CLI output에 daemon_id metadata 추가
  • Removed:
    • none

Impacted Area Delta

  • Docs:
    • README.md, token provenance 문서, docs/runtime-flows.md
  • Modules:
    • app/cli/commands/service_tokens.py, app/services/auth_service.py, app/repositories/postgres.py
  • Tests:
    • tests/test_cli.py, 필요한 경우 auth/v1 provenance 회귀 테스트

Delivery Trace Delta

  • Batches:
    • BAT-002 (release-candidate; REL-002 released input) — rotate verification evidence는 daemon_id 유지/출력 기준으로 재검증 필요
  • Releases:
    • REL-002 (released) — rotate 운영 메모는 runtime_id 재조회만 다루며 daemon-aware provenance는 미반영

Revalidation Impact

  • Reverification Needed: yes
  • Existing Implementation Invalidated: yes
  • Status Recommendation: rollback-to-approved
  • Follow-up Action: dcy-006 후속 batch에서 rotate response metadata와 daemon binding invariants를 구현하고, REL-002 후속 tooling release evidence를 갱신한다.

Validation Plan

  • current CLI/service/repository 구현이 daemon_id를 보존·출력하지 않는지 코드 리뷰로 확인한다.
  • 구현 후 rotate 전후 daemon_id 불변, runtime_id 갱신, CLI output metadata를 targeted CLI 테스트로 검증한다.

CHG-20260511-02

  • Date: 2026-05-11
  • Author: repository-maintainers

Change Summary

  • Implemented 전환 — BAT-002 verification passed, confirm-req-implemented 게이트 통과

Intent

  • BAT-002 verification.md에서 REQ-012 AC-1~6이 모두 PASS이고 blocking issue가 없음을 근거로 Implemented로 승격한다.

Acceptance Criteria Delta

  • Added: none
  • Updated: none
  • Removed: none

Impacted Area Delta

  • Docs: none
  • Modules: none
  • Tests: none

Delivery Trace Delta

  • Batches:
    • BAT-002 (release-candidate)
  • Releases:
    • none

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: implemented
  • Follow-up Action: tooling release에 포함 시 rotate 후 runtime_id 재조회 운영 메모 반영

Validation Plan

  • BAT-002 verification.md의 REQ-012 AC-1~6 evidence와 Ready for Release: true 확인

CHG-20260511-01

  • Date: 2026-05-11
  • Author: repository-maintainers

Change Summary

  • rotate 후 runtime_id lifecycle 명시 추가 — downstream consumer는 새 토큰 배포 후 runtime_id를 재조회해야 함

Intent

  • CONFLICT-3 분석 결과를 반영해 runtime_id가 rotate 시 변경된다는 사실과, trex-basic 같은 consumer가 이를 어떻게 재인식해야 하는지 REQ-012 문서에 명시한다.

Acceptance Criteria Delta

  • Added:
    • none
  • Updated:
    • none
  • Removed:
    • none

Impacted Area Delta

  • Docs:
    • docs/srs/Interface/req-012_service-token-rotate-cli.md: Intent/Notes에 rotate 후 runtime_id 재조회 lifecycle 추가
  • Modules:
    • none
  • Tests:
    • none

Delivery Trace Delta

  • Batches:
    • BAT-002 (release-candidate) — verification evidence는 유지 가능하나, rotate 후 consumer 재인식 절차를 운영 메모로 함께 검토해야 한다.
  • Releases:
    • none

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: keep-current
  • Follow-up Action: trex-basic 연동 runbook 또는 후속 REQ에서 rotate 후 runtime_id 재조회 절차를 운영 문서에 반영 검토

Validation Plan

  • REQ-005, REQ-007, REQ-012 문구를 대조해 rotate 시 runtime_id 변경과 GET /v1/user_info 재조회 필요성이 일관되게 설명되는지 코드 리뷰로 확인

CHG-20260502-01

  • Date: 2026-05-02
  • Author: repository-maintainers

Change Summary

  • REQ-012 신규 작성 (minor-change fast path, Approved 직행)
  • rotate 허용 대상을 expires_at IS NULL(no-expiry) 토큰으로 제한

Intent

  • service-token rotate CLI 명령 필요성 확인 후 즉시 구현 진행

Acceptance Criteria Delta

  • Added:
    • 6개 기준 신규 정의 (AC-5: expires_at 설정 토큰 rotate 거부 추가)

Impacted Area Delta

  • Modules:
    • postgres.py, auth_service.py, service_tokens.py, test_cli.py 추가
  • Tests:
    • test_cli.py 3개 케이스 신규

Delivery Trace Delta

  • Batches:
    • BAT-002 (batch-lite)
  • Releases:
    • none (미정)

Revalidation Impact

  • Reverification Needed: no
  • Existing Implementation Invalidated: no
  • Status Recommendation: keep-current
  • Follow-up Action: BAT-002 planning → implementation → verification 진행

Validation Plan

  • test_cli.py 3개 케이스 통과 + 기존 service-token 테스트 회귀 없음으로 완료 판단