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
This commit is contained in:
@@ -1,24 +1,25 @@
|
||||
# req-011: trex-basic 계약 반영을 위한 baseline 문서 갱신
|
||||
|
||||
- Status: Implemented
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- dcy-002 구현 완료 후 interface-contract.md, data-model.md, runtime-flows.md를 실제 구현 결과와 동기화한다.
|
||||
- dcy-006의 daemon-aware 변경으로 baseline 문서는 runtime_id 중심 설명을 daemon binding/assignment 모델로 다시 정렬해야 한다.
|
||||
- 이 REQ는 구현이 완료된 시점에 수행하는 문서 갱신이며, 구현 전 선행 작업이 아니다.
|
||||
|
||||
## Requirement
|
||||
- REQ-004~010이 구현 완료(Implemented)된 시점에 다음 3개 baseline 문서를 갱신해야 한다.
|
||||
1. docs/interface-contract.md: GET /health 응답 스키마, GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 명세 및 service-token bearer 인증 방식 추가
|
||||
2. docs/data-model.md: broker_credentials 테이블, ledger_trades 테이블, portfolios.market 컬럼, auth_tokens.runtime_id 컬럼 추가
|
||||
3. docs/runtime-flows.md: /v1/* API 요청 처리 흐름(인증 → 서비스 → DB), service-token 발급 시 runtime_id 생성 흐름 추가
|
||||
- REQ-005~009, REQ-012의 daemon-aware 변경이 구현 완료(Implemented)된 시점에 다음 3개 baseline 문서를 갱신해야 한다.
|
||||
1. docs/interface-contract.md: `POST /auth/service-tokens`, `GET /v1/assets`, `GET /v1/user_info`, `POST /v1/assets/{asset_id}/ledger/trades`의 daemon binding / daemon-scoped contract, `403` mismatch 정책, service-token bearer 인증 방식을 반영
|
||||
2. docs/data-model.md: `daemons` 테이블, `auth_tokens.daemon_id`, `portfolios.managed_by_daemon_id`, `auth_tokens.runtime_id`의 역할 분리와 transition `NULL` backfill 정책 반영
|
||||
3. docs/runtime-flows.md: daemon registry binding, `/v1/*` daemon-scoped read/write 흐름, rotate 시 `daemon_id` 유지 + `runtime_id` 갱신 불변식 반영
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given docs/interface-contract.md를 검토하면, Then GET /health, GET /v1/assets, GET /v1/user_info, POST /v1/assets/{asset_id}/ledger/trades의 요청/응답 명세가 포함되어야 한다.
|
||||
2. Given docs/data-model.md를 검토하면, Then broker_credentials, ledger_trades 테이블과 portfolios.market, auth_tokens.runtime_id 컬럼이 문서화되어야 한다.
|
||||
3. Given docs/runtime-flows.md를 검토하면, Then v1 API 처리 흐름과 runtime_id 발급 흐름이 포함되어야 한다.
|
||||
1. Given docs/interface-contract.md를 검토하면, Then `POST /auth/service-tokens`, `GET /v1/assets`, `GET /v1/user_info`, `POST /v1/assets/{asset_id}/ledger/trades`의 daemon-aware 요청/응답 명세와 `403` mismatch 정책이 포함되어야 한다.
|
||||
2. Given docs/data-model.md를 검토하면, Then `daemons`, `broker_credentials`, `ledger_trades` 테이블과 `portfolios.market`, `portfolios.managed_by_daemon_id`, `auth_tokens.runtime_id`, `auth_tokens.daemon_id`가 문서화되어야 한다.
|
||||
3. Given docs/runtime-flows.md를 검토하면, Then daemon registry binding 흐름, daemon-scoped v1 API 처리 흐름, rotate 시 `runtime_id`/`daemon_id` 역할 차이가 포함되어야 한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/interface-contract.md, docs/data-model.md, docs/runtime-flows.md
|
||||
@@ -28,10 +29,53 @@
|
||||
## Notes
|
||||
- 이 REQ는 구현 완료 후 수행하므로 batch 설계 단계에서 별도 implementation task로 명시한다.
|
||||
- 문서 갱신 내용은 draft-batch-verification 단계에서 실제 구현과 비교해 검증한다.
|
||||
- Source Discovery: dcy-002 (전체 FR/NFR 구현 완료 후 documentation closure)
|
||||
- README.md 운영 provenance / release 절차 변경은 REQ-021 및 후속 dcy-006 REQ에서 다룬다.
|
||||
- Source Discovery: dcy-002 (전체 FR/NFR 구현 완료 후 documentation closure), dcy-006 (FR-5, FR-6, NFR-1)
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- baseline 문서 REQ를 daemon-aware v1 contract 반영 범위로 확장하고, 기존 Implemented 상태를 Approved로 되돌림
|
||||
|
||||
#### Intent
|
||||
- dcy-006은 interface-contract, data-model, runtime-flows가 모두 daemon binding/assignment와 runtime_id/daemon_id 역할 분리를 반영하도록 요구한다. BAT-008/REL-002 문서 evidence는 runtime_id 중심 baseline만 다뤘으므로 revised REQ를 충족하지 못한다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- none
|
||||
- Updated:
|
||||
- AC-1~3: daemon-aware contract, data model, runtime flow 기준으로 재정의
|
||||
- Removed:
|
||||
- none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/interface-contract.md`, `docs/data-model.md`, `docs/runtime-flows.md`
|
||||
- Modules:
|
||||
- none
|
||||
- Tests:
|
||||
- none (문서 리뷰 및 delivery verification evidence 중심)
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-008 (release-candidate; REL-002 released input) — 기존 verification evidence는 daemon-aware baseline을 포함하지 않아 재작성 필요
|
||||
- Releases:
|
||||
- REL-002 (released) — released baseline docs scope가 revised REQ 대비 불완전
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes
|
||||
- Existing Implementation Invalidated: yes
|
||||
- Status Recommendation: rollback-to-approved
|
||||
- Follow-up Action: dcy-006 후속 batch에서 daemon-aware baseline docs를 갱신하고 BAT-008에 대응하는 새 verification evidence를 작성한다.
|
||||
|
||||
#### Validation Plan
|
||||
- dcy-006 FR-5/FR-6와 현행 baseline 문서를 대조해 누락된 daemon contract 항목을 식별한다.
|
||||
- 구현 후 문서 리뷰와 batch verification에서 interface/data/runtime 3축이 모두 갱신됐는지 확인한다.
|
||||
|
||||
### CHG-20260511-01
|
||||
- Date: 2026-05-11
|
||||
- Author: repository-maintainers
|
||||
|
||||
+57
-12
@@ -7,36 +7,81 @@
|
||||
|
||||
## Intent
|
||||
- release 검증에 사용하는 credential의 용도, 발급 주체, 보관 위치, 폐기 절차가 정의되지 않으면 smoke 재현성이 없어지고 secret leakage 위험이 높아진다.
|
||||
- public/no-auth, read service-token, trade-write service-token, admin auth context 4개 credential 관점의 matrix를 문서화하고, 기존 CLI/API를 token provenance 확인 canonical path로 명시한다.
|
||||
- public/no-auth, read service-token, trade-write service-token, admin auth context 4개 credential 관점의 matrix를 문서화하고, daemon-aware CLI/API를 token provenance 확인 canonical path로 명시한다.
|
||||
|
||||
## Requirement
|
||||
- README.md Production Operator Runbook 또는 연결된 운영 문서는 아래 credential matrix를 명시해야 한다.
|
||||
- **public/no-auth**: `GET /health`, `/docs`, `/openapi.json`. 인증 불필요. 배포 직후 공개 smoke에 사용.
|
||||
- **read service-token**: `GET /v1/assets`, `GET /v1/user_info`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens`. 보관: `/opt/portfoliohub/.env` 또는 동일 접근 통제 하 운영 절차. 공동 소유자: release approver / repository-maintainers.
|
||||
- **trade-write service-token**: `POST /v1/assets/{asset_id}/ledger/trades`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens`. 보관: read token과 동일 정책. 공동 소유자: release approver / repository-maintainers.
|
||||
- **read service-token**: `GET /v1/assets`, `GET /v1/user_info`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens` 후 existing `daemon_id`에 binding. 보관: `/opt/portfoliohub/.env` 또는 동일 접근 통제 하 운영 절차. 공동 소유자: release approver / repository-maintainers.
|
||||
- **trade-write service-token**: `POST /v1/assets/{asset_id}/ledger/trades`. 환경 단위 운영 credential. 발급: admin context로 `POST /auth/service-tokens` 후 existing `daemon_id`에 binding. 보관: read token과 동일 정책. 공동 소유자: release approver / repository-maintainers.
|
||||
- **admin auth context**: `POST /auth/service-tokens`. 운영자 개인 소유. 공유 금지. 발급: steady-state는 `/auth/login` 경유 DB-issued session, break-glass는 env token.
|
||||
- release 문서에는 name, token_prefix, permission, runtime_id, issued_at, revoked_at만 기록하고 raw token값은 기록하지 않는다.
|
||||
- 운영자는 `pfhub service-token list`, `pfhub service-token issue`, `pfhub service-token rotate`, `pfhub user get`, `GET /v1/user_info`를 token provenance 확인 canonical support path로 사용한다. 각 명령의 출력 필드(name, token_prefix, token_id, user_id, permission, created_at, last_used_at, revoked_at, runtime_id)를 provenance 확인 기준으로 명시한다.
|
||||
- release 문서에는 name, token_prefix, permission, `daemon_id`, `runtime_id`, issued_at, revoked_at만 기록하고 raw token값은 기록하지 않는다.
|
||||
- 운영자는 `pfhub service-token list`, `pfhub service-token issue`, `pfhub service-token rotate`, `pfhub user get`, `GET /v1/user_info`를 token provenance 확인 canonical support path로 사용한다. 각 명령의 출력 필드(name, token_prefix, token_id, user_id, permission, `daemon_id`, created_at, last_used_at, revoked_at, `runtime_id`)를 provenance 확인 기준으로 명시한다.
|
||||
- rotate 또는 재발급 이후에는 `daemon_id` 불변과 `runtime_id` 갱신 여부를 함께 대조해야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given README.md 또는 연결된 운영 문서가 존재할 때, When 운영자가 credential matrix를 참조할 때, Then public/read/trade-write/admin 4개 credential의 용도, 발급 주체, 보관 위치, 공동 소유 역할이 구분되어 기술되어 있어야 한다.
|
||||
2. Given release 문서(docs/releases/*.md)가 존재할 때, When smoke evidence를 기록할 때, Then name, token_prefix, permission, runtime_id, issued_at, revoked_at 필드만 남기고 raw token 값은 포함되지 않아야 한다.
|
||||
3. Given 운영자가 service-token provenance를 확인할 때, When `pfhub service-token list`를 실행할 때, Then name, permission, created_at, last_used_at, revoked_at 필드가 출력되고, `GET /v1/user_info`의 runtime_id와 대조해 배포된 토큰의 provenance를 재현 가능하게 확인할 수 있어야 한다.
|
||||
1. Given README.md 또는 연결된 운영 문서가 존재할 때, When 운영자가 credential matrix를 참조할 때, Then public/read/trade-write/admin 4개 credential의 용도, 발급 주체, 보관 위치, 공동 소유 역할과 daemon binding 전제가 구분되어 기술되어 있어야 한다.
|
||||
2. Given release 문서(docs/releases/*.md)가 존재할 때, When smoke evidence를 기록할 때, Then name, token_prefix, permission, `daemon_id`, `runtime_id`, issued_at, revoked_at 필드만 남기고 raw token 값은 포함되지 않아야 한다.
|
||||
3. Given 운영자가 service-token provenance를 확인할 때, When `pfhub service-token list`를 실행할 때, Then name, permission, `daemon_id`, created_at, last_used_at, revoked_at 필드가 출력되고, `GET /v1/user_info`의 `runtime_id`, `daemon_id`와 대조해 배포된 토큰의 provenance를 재현 가능하게 확인할 수 있어야 한다.
|
||||
4. Given admin auth context가 필요한 smoke를 수행할 때, When 운영자가 credential matrix를 따를 때, Then admin context는 개인 소유로 취급되고 read/trade-write token은 release approver 역할과 공동 소유 형태로 관리되는 것이 문서에 명시되어 있어야 한다.
|
||||
5. Given 운영자가 rotate 또는 재발급 후 provenance를 재확인할 때, When `pfhub service-token rotate` 또는 issue 결과를 검토할 때, Then `daemon_id`는 유지 또는 요청한 binding 값으로 확인되고 `runtime_id`는 새 값으로 대조 가능해야 한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: `README.md` — credential matrix 항목 및 token provenance CLI 절차 추가/보강
|
||||
- Modules: `app/api/v1_user_info.py` (runtime_id 반환 확인), `app/cli/commands/service_token.py` (list/rotate 출력 필드 확인)
|
||||
- Docs: `README.md` — credential matrix 항목 및 daemon-aware token provenance CLI 절차 추가/보강
|
||||
- Modules: `app/api/v1_user_info.py` (`runtime_id`, `daemon_id` 반환 확인), `app/cli/commands/service_tokens.py` (issue/list/rotate 출력 필드 확인)
|
||||
- Tests: 없음 (문서화 중심 REQ)
|
||||
|
||||
## Notes
|
||||
- NFR-1 (raw credential 비노출): 이 REQ의 핵심 제약. AC-2에 명시.
|
||||
- FR-4와 FR-2를 통합한 REQ. token provenance 확인 수단과 credential matrix는 운영자가 같은 맥락에서 참조하므로 분리 시 중복이 발생한다.
|
||||
- `runtime_id`는 service-token rotate 후 변경되므로 rotate 후 consumer가 재조회해야 한다는 사실도 matrix 문서에 포함할 것을 권장 (REQ-012 CHG-20260511-01 참조).
|
||||
- Source Discovery: dcy-005 FR-2, FR-4
|
||||
- `runtime_id`는 service-token rotate 후 변경되고 `daemon_id`는 유지되므로, 문서에는 두 식별자의 역할 차이를 함께 명시해야 한다.
|
||||
- daemon provisioning 자체는 별도 관리 경로로 수행하고, provenance 절차는 binding 결과 확인에 집중한다.
|
||||
- Source Discovery: dcy-005 FR-2, FR-4; dcy-006 FR-5, FR-6
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- token provenance REQ를 daemon-aware support path와 `daemon_id` evidence 필드 기준으로 보강
|
||||
|
||||
#### Intent
|
||||
- dcy-006은 issue/list/rotate와 `/v1/user_info`가 모두 `daemon_id`를 노출하고, release provenance도 `runtime_id` 단독이 아니라 daemon binding을 함께 추적하도록 요구한다. 기존 Approved 본문은 runtime_id 중심이라 운영 절차 기준이 불완전하다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-5: rotate/re-issue 후 `daemon_id`/`runtime_id` 대조 절차
|
||||
- Updated:
|
||||
- AC-1~3: daemon binding 전제와 `daemon_id` evidence 필드 반영
|
||||
- Removed:
|
||||
- none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `README.md` credential matrix / provenance 절차
|
||||
- Modules:
|
||||
- `app/api/v1_user_info.py`, `app/cli/commands/service_tokens.py`
|
||||
- Tests:
|
||||
- none (문서화 중심 REQ)
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-009 (release-candidate; REL-005 confirmed input) — README provenance 절차와 release evidence field 정의를 daemon-aware 기준으로 재검토해야 함
|
||||
- Releases:
|
||||
- REL-005 (confirmed) — 배포 전 daemon-aware provenance field와 rotate invariant를 반영하도록 scope 재검토 필요
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: BAT-009 구현/verification 전에 README와 release evidence 템플릿을 daemon-aware 필드 기준으로 조정한다.
|
||||
|
||||
#### Validation Plan
|
||||
- README.md와 release evidence 예시가 `daemon_id`, `runtime_id`, rotate invariant를 모두 설명하는지 코드 리뷰로 확인한다.
|
||||
- REQ-005, REQ-007, REQ-012 문구와 provenance 절차가 식별자 역할 측면에서 일관적인지 대조한다.
|
||||
|
||||
### CHG-20260512-02
|
||||
- Date: 2026-05-12
|
||||
- Author: repository-maintainers
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# REQ-028: README.md Daemon Provisioning Runbook 반영
|
||||
|
||||
- Status: Approved
|
||||
- Type: Documentation
|
||||
- Priority: Medium
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- interface-contract, data-model, runtime-flows의 daemon-aware 반영은 REQ-011 CHG-20260513-01에서 이미 다룬다.
|
||||
- 이 REQ는 REQ-011이 다루지 않는 `README.md` 운영 runbook 영역에 한정해, daemon provisioning 절차와 enforcement enablement gate를 반영한다.
|
||||
|
||||
## Requirement
|
||||
- `README.md` Production Operator Runbook 섹션은 daemon provisioning 절차(daemon create → service-token issue/rotate binding 순서)를 반영해야 한다.
|
||||
- `README.md`는 daemon-scoped enforcement enablement gate의 required evidence(inventory 완료, dry-run audit zero-active-NULL/zero-mismatch, smoke 검증 통과, release approver 승인)를 명시해야 한다.
|
||||
- `README.md`는 `runtime_id`와 `daemon_id`의 역할 차이(runtime identity vs stable logical identity)를 운영 provenance 절차에서 구분해 설명해야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `README.md` Production Operator Runbook을 검토할 때, Then daemon create → service-token issue/rotate binding의 순서와 각 단계 조건이 명시되어 있다.
|
||||
2. Given `README.md` enforcement enablement gate 섹션을 확인할 때, Then inventory 완료, dry-run audit(zero-active-NULL/zero-mismatch), smoke 검증, release approver 승인의 4가지 required evidence가 모두 명시되어 있다.
|
||||
3. Given `README.md` 운영 provenance 절차를 확인할 때, Then `runtime_id`(rotate 시 갱신)와 `daemon_id`(rotate 후에도 유지)의 역할 차이가 구분되어 설명되어 있다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: `README.md` (Production Operator Runbook 섹션)
|
||||
- Modules: 없음
|
||||
- Tests: 없음 (문서 검토로 검증)
|
||||
|
||||
## Notes
|
||||
- interface-contract.md, data-model.md, runtime-flows.md의 daemon 반영은 REQ-011 CHG-20260513-01 범위이므로 이 REQ에서 중복 요구하지 않는다.
|
||||
- enforcement enablement gate의 실제 cutover evidence 수집은 release 단계에서 수행한다. 이 REQ는 절차 문서화만 대상으로 한다.
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-02
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- Status: Proposed → Approved (confirm-req 게이트 통과)
|
||||
|
||||
#### Intent
|
||||
- 필수 필드, AC, blocker 없음 확인 후 승인 전환
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added: 없음
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: 없음
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: 미정
|
||||
- Releases: 미정
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: 없음
|
||||
|
||||
#### Validation Plan
|
||||
- 없음 (상태 전환만)
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- 최초 생성 시 REQ-011과 중복되는 interface-contract/data-model/runtime-flows 항목을 제거하고, README.md 운영 runbook 한정으로 범위 축소
|
||||
|
||||
#### Intent
|
||||
- REQ-011이 다루지 않는 README.md daemon provisioning runbook과 enforcement gate 절차만 별도 REQ로 정의
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~3 전체 신규 추가
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: `README.md` 한정
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: 미정
|
||||
- Releases: 미정
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: 없음 (신규 REQ)
|
||||
|
||||
#### Validation Plan
|
||||
- README.md에서 AC별 확인 항목(daemon provisioning 순서, gate evidence 4종, runtime_id/daemon_id 역할 구분)이 존재하는지 코드 리뷰로 검증
|
||||
@@ -1,39 +1,85 @@
|
||||
# req-005: service-token 발급 시 runtime_id 동시 생성
|
||||
|
||||
- Status: Implemented
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- trex-basic 데몬은 GET /v1/user_info에서 runtime_id를 조회해 자신을 식별한다.
|
||||
- runtime_id는 service-token 발급 시 함께 생성되어 해당 토큰에 연결 저장되어야 한다.
|
||||
- CLI(pfhub service-token issue)와 API 엔드포인트 양쪽에서 발급 결과에 runtime_id가 포함되어야 한다.
|
||||
- trex-basic 계열 consumer는 `runtime_id`를 런타임 세대 식별자로 사용하되, asset assignment와 운영 provenance는 stable `daemon_id`를 기준으로 추적해야 한다.
|
||||
- service-token 발급 시 runtime_id를 새로 생성하면서도, 토큰은 기존 daemon registry에 존재하는 `daemon_id`에 바인딩되어 저장되어야 한다.
|
||||
- CLI(`pfhub service-token issue`)와 API 엔드포인트 양쪽에서 발급 결과에 `runtime_id`와 `daemon_id`가 함께 포함되어야 한다.
|
||||
|
||||
## Requirement
|
||||
- pfhub service-token issue CLI 커맨드와 대응 API 엔드포인트는 service-token 발급 시 고유 runtime_id를 동시 생성하고 auth_tokens 테이블에 연결 저장한 뒤 발급 결과(token + runtime_id)를 반환해야 한다.
|
||||
- runtime_id는 해당 service-token에 1:1 연결되며, 동일 사용자의 다른 service-token은 각기 다른 runtime_id를 가진다.
|
||||
- pfhub service-token issue CLI 커맨드와 대응 API 엔드포인트(`POST /auth/service-tokens`)는 service-token 발급 시 고유 `runtime_id`를 생성하고, 기존 daemon registry에 존재하는 `daemon_id`에 토큰을 바인딩해 auth_tokens에 함께 저장한 뒤 발급 결과(`token`, `runtime_id`, `daemon_id`)를 반환해야 한다.
|
||||
- 발급 경로는 unknown daemon 자동 생성 또는 upsert를 허용하면 안 되며, binding 대상은 사전에 생성된 daemon registry entry여야 한다.
|
||||
- `runtime_id`는 발급마다 새로 생성되는 런타임 식별자이고, `daemon_id`는 rotate/재기동과 무관하게 유지되는 stable logical identity다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `pfhub service-token issue <name> --user <username>` 실행 시, When 발급이 성공하면, Then 출력에 token 문자열과 runtime_id가 모두 포함되어야 한다.
|
||||
2. Given API 엔드포인트(POST /auth/service-tokens)로 service-token 발급 시, When 성공 응답을 받으면, Then 응답 JSON에 token과 runtime_id가 포함되어야 한다.
|
||||
3. Given runtime_id가 발급된 service-token으로 GET /v1/user_info를 호출하면, Then 응답의 runtime_id가 발급 시 생성된 값과 동일해야 한다.
|
||||
4. Given 동일 사용자에 대해 두 번의 service-token 발급을 수행하면, Then 각각 다른 runtime_id가 반환되어야 한다.
|
||||
5. Given auth_tokens 테이블을 직접 조회하면, Then 발급된 service-token 행에 runtime_id 값이 저장되어 있어야 한다.
|
||||
1. Given `pfhub service-token issue <name> --user <username> --daemon-id <daemon_id>` 실행 시, When 발급이 성공하면, Then 출력에 token 문자열, `runtime_id`, `daemon_id`가 모두 포함되어야 한다.
|
||||
2. Given API 엔드포인트(`POST /auth/service-tokens`)로 existing `daemon_id`를 지정해 service-token 발급 시, When 성공 응답을 받으면, Then 응답 JSON에 token, `runtime_id`, `daemon_id`가 포함되어야 한다.
|
||||
3. Given `runtime_id`와 `daemon_id`가 발급된 service-token으로 `GET /v1/user_info`를 호출하면, Then 응답의 `runtime_id`, `daemon_id`가 발급 시 저장된 값과 동일해야 한다.
|
||||
4. Given 동일 daemon에 대해 두 번의 service-token 발급을 수행하면, Then 각각 다른 `runtime_id`가 반환되어야 하고 `daemon_id`는 요청한 binding 값을 유지해야 한다.
|
||||
5. Given auth_tokens 테이블을 직접 조회하면, Then 발급된 service-token 행에 `runtime_id`와 `daemon_id` 값이 모두 저장되어 있어야 한다.
|
||||
6. Given 존재하지 않는 `daemon_id`로 service-token 발급을 요청하면, Then 명시적 validation 오류가 반환되어야 하고 auth_tokens 행은 생성되지 않아야 한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/runtime-flows.md (REQ-011에서 갱신 예정)
|
||||
- Modules: app/cli/commands/service_tokens.py, app/api/auth_routes.py, app/domain/models.py (AuthToken.runtime_id 필드 추가), app/services/auth_service.py, app/repositories/postgres.py
|
||||
- Tests: tests/test_auth.py 확장 또는 tests/test_service_tokens.py (신규)
|
||||
- Docs: docs/runtime-flows.md, docs/interface-contract.md (REQ-011에서 갱신 예정), README.md token provenance 절차 (REQ-021에서 갱신 예정)
|
||||
- Modules: app/cli/commands/service_tokens.py, app/schemas/auth.py, app/api/auth_routes.py, app/domain/models.py, app/services/auth_service.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py
|
||||
- Tests: tests/test_auth.py, tests/test_cli.py, tests/test_v1.py
|
||||
|
||||
## Notes
|
||||
- runtime_id 포맷은 UUID v4를 권장한다. 설계 단계에서 확정한다.
|
||||
- auth_tokens 테이블에 runtime_id 컬럼 추가는 REQ-009 migration에서 처리한다.
|
||||
- 기존 service-token(runtime_id 없음) 처리 방식은 R-4로 식별됨. GET /v1/user_info(REQ-007) 설계 단계에서 결정한다.
|
||||
- Source Discovery: dcy-002 (FR-2, A-2)
|
||||
- auth_tokens 테이블의 `runtime_id`, `daemon_id` 컬럼 및 daemon registry 스키마는 REQ-009 migration에서 처리한다.
|
||||
- daemon provisioning(create/update)은 별도 관리 경로에서 수행하며, issue 경로는 binding만 담당한다.
|
||||
- 기존 daemon-less service-token은 transition 구간에서 `daemon_id = NULL`로 남을 수 있고, daemon-scoped API에서는 fail-closed로 처리한다.
|
||||
- Source Discovery: dcy-002 (FR-2, A-2), dcy-006 (FR-1, FR-5, NFR-1)
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- runtime_id-only service-token 발급 요구사항을 existing daemon binding + `daemon_id` diagnostics 포함 구조로 확장하고, 기존 Implemented 상태를 Approved로 되돌림
|
||||
|
||||
#### Intent
|
||||
- dcy-006에서 `runtime_id`는 runtime identity, `daemon_id`는 stable logical identity로 역할이 분리됐다. 현재 BAT-001/REL-001 구현은 runtime_id만 저장·노출하므로 revised issuance contract와 daemon-scoped authorization 전제를 충족하지 못한다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-6: unknown `daemon_id` 발급 요청 거부
|
||||
- Updated:
|
||||
- AC-1~5: CLI/API issue, `/v1/user_info`, DB persistence 기준을 `daemon_id` binding 포함으로 재정의
|
||||
- Removed:
|
||||
- none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/runtime-flows.md`, `docs/interface-contract.md` — issue flow와 response contract에 `daemon_id` 추가 필요
|
||||
- `README.md` — daemon-aware provenance 절차 반영 필요
|
||||
- Modules:
|
||||
- `app/cli/commands/service_tokens.py`, `app/schemas/auth.py`, `app/api/auth_routes.py`, `app/services/auth_service.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`
|
||||
- Tests:
|
||||
- `tests/test_auth.py`, `tests/test_cli.py`, `tests/test_v1.py`
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-001 (release-candidate; REL-001 released input) — runtime_id-only issuance evidence는 daemon binding 요구사항 기준으로 재설계 및 재검증 필요
|
||||
- Releases:
|
||||
- REL-001 (released) — service-token issuance release evidence가 revised REQ를 충족하지 않으므로 후속 app-service release가 필요
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes
|
||||
- Existing Implementation Invalidated: yes
|
||||
- Status Recommendation: rollback-to-approved
|
||||
- Follow-up Action: dcy-006 후속 batch에서 daemon registry binding, issue API/CLI contract, `/v1/user_info` diagnostics를 함께 구현하고 재검증한다.
|
||||
|
||||
#### Validation Plan
|
||||
- dcy-006 FR-1/FR-5와 `app/services/auth_service.py`, `app/api/auth_routes.py`, `app/cli/commands/service_tokens.py`의 현재 구현 차이를 코드 리뷰로 대조한다.
|
||||
- 구현 후 `tests/test_auth.py`, `tests/test_cli.py`, `tests/test_v1.py`에 daemon-bound issuance/user_info 케이스를 추가해 targeted validation을 수행한다.
|
||||
|
||||
### CHG-20260511-02
|
||||
- Date: 2026-05-11
|
||||
- Author: repository-maintainers
|
||||
|
||||
@@ -1,23 +1,24 @@
|
||||
# req-006: GET /v1/assets 엔드포인트 구현
|
||||
|
||||
- Status: Implemented
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- trex-basic 데몬이 관리 자산 목록과 전략 파라미터를 조회할 수 있도록 portfolio 데이터를 asset 뷰로 변환해 제공한다.
|
||||
- asset_id는 기존 portfolio_id를 그대로 사용하며 별도 식별자 체계를 만들지 않는다(A-3).
|
||||
- trex-basic 데몬이 자신에게 배정된 자산 목록과 전략 파라미터만 조회할 수 있도록 portfolio 데이터를 daemon-scoped asset 뷰로 제공한다.
|
||||
- 동일 owner user 아래 여러 daemon이 존재해도, 다른 daemon에 배정된 asset은 응답에 섞이면 안 된다.
|
||||
|
||||
## Requirement
|
||||
- GET /v1/assets는 service-token bearer 인증으로 토큰 소유자의 portfolio 목록만 asset_id, symbol, market, status, cash, quantity, broker_type, strategy_id, strategy_type, strategy_params, updated_at 필드로 반환해야 한다. 다른 사용자 소유 portfolio는 응답에 포함되면 안 된다.
|
||||
- status=active 쿼리 파라미터로 필터링이 가능해야 한다.
|
||||
- GET /v1/assets는 service-token bearer 인증으로 토큰 소유 user 범위 안에서, 인증된 token의 `daemon_id`에 배정된 portfolio 목록만 `asset_id`, `symbol`, `market`, `status`, `cash`, `quantity`, `broker_type`, `strategy_id`, `strategy_type`, `strategy_params`, `updated_at` 필드로 반환해야 한다. 다른 사용자 소유 portfolio와 다른 daemon에 배정된 portfolio는 응답에 포함되면 안 된다.
|
||||
- `status=active` 쿼리 파라미터는 daemon-scoped 결과 집합 위에서 필터링되어야 한다.
|
||||
- strategy_params는 해당 portfolio의 active_strategy_id로 조회한 StrategySet.config에서 파생한다. strategy_id는 허용값 검증 없이 그대로 반환한다(OQ-1 Answered).
|
||||
- strategy_type은 해당 portfolio의 active_strategy_id로 조회한 StrategySet.name을 반환한다. active_strategy_id가 없거나 StrategySet 조회 실패 시 null을 반환한다.
|
||||
- `daemon_id`가 없는 legacy service-token은 transition 구간 동안 `GET /v1/assets`에서 fail-closed(`403 Forbidden`)로 처리되어야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given 유효한 service-token bearer 인증으로 GET /v1/assets 요청 시, When 토큰이 유효하면, Then HTTP 200과 해당 사용자 소유 portfolio 목록만 반환되어야 하며 다른 사용자 소유 portfolio는 포함되면 안 된다.
|
||||
2. Given GET /v1/assets?status=active 요청 시, Then status가 active인 항목만 반환되어야 한다.
|
||||
1. Given 유효한 daemon-bound service-token bearer 인증으로 `GET /v1/assets` 요청 시, When 토큰이 유효하면, Then HTTP 200과 해당 daemon에 배정된 portfolio 목록만 반환되어야 하며 다른 사용자 소유 portfolio나 다른 daemon에 배정된 portfolio는 포함되면 안 된다.
|
||||
2. Given `GET /v1/assets?status=active` 요청 시, Then status가 active인 항목만 daemon-scoped 결과 안에서 반환되어야 한다.
|
||||
3. Given 응답의 각 항목을 확인하면, Then asset_id, symbol, market, status, cash, quantity, broker_type, strategy_id, strategy_type, strategy_params, updated_at 필드가 모두 포함되어야 한다.
|
||||
4. Given portfolio에 연결된 StrategySet이 있을 때, When strategy_params를 확인하면, Then StrategySet.config 값에서 파생된 JSON 객체여야 한다.
|
||||
5. Given StrategySet config가 없거나 파싱 실패 시, Then strategy_params는 빈 객체({})를 반환하고 에러가 전파되지 않아야 한다.
|
||||
@@ -25,20 +26,64 @@
|
||||
7. Given 인증 없이 GET /v1/assets 요청 시, Then HTTP 401이 반환되어야 한다.
|
||||
8. Given portfolio에 active_strategy_id로 연결된 StrategySet이 있을 때, Then strategy_type은 해당 StrategySet.name 값을 반환해야 한다.
|
||||
9. Given portfolio에 active_strategy_id가 없거나 StrategySet 조회 실패 시, Then strategy_type은 null을 반환해야 한다.
|
||||
10. Given `daemon_id`가 없는 legacy service-token으로 `GET /v1/assets` 요청 시, Then HTTP 403이 반환되어야 하고 daemon-scoped asset 목록은 비노출이어야 한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/project-structure.md (신규 라우터 파일 반영 필요)
|
||||
- Modules: app/api/v1_assets.py (신규), app/services/ (assets 조회 서비스 메서드 추가), app/schemas/ (v1 assets 응답 스키마 추가), app/repositories/postgres.py (portfolio + strategy_set JOIN 쿼리 추가), app/main.py (/v1 라우터 등록)
|
||||
- Tests: tests/test_v1.py (GET /v1/assets 관련 케이스 및 owner scope 회귀 검증)
|
||||
- Modules: app/api/v1_assets.py, app/schemas/v1.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py, app/core/auth.py, app/main.py
|
||||
- Tests: tests/test_v1.py, tests/test_portfolio_ownership.py 또는 신규 daemon assignment 회귀 테스트
|
||||
|
||||
## Notes
|
||||
- broker_type은 portfolios.broker 컬럼을 그대로 매핑한다(m-2 finding 반영).
|
||||
- strategy_id는 portfolios.active_strategy_id를 그대로 반환한다.
|
||||
- GET /v1/assets의 소유권 스코프는 service-token 소유자(owner_user_id)를 기준으로 한다.
|
||||
- Source Discovery: dcy-002 (FR-3, A-3, OQ-1)
|
||||
- `GET /v1/assets`의 최종 authorization 스코프는 service-token 소유자 범위 위에 `managed_by_daemon_id = auth.daemon_id` 조건을 추가한 daemon scope다.
|
||||
- exact-one asset assignment는 `portfolios.managed_by_daemon_id`를 통해 표현되며 스키마 제약은 REQ-009 change-req에서 다룬다.
|
||||
- Source Discovery: dcy-002 (FR-3, A-3, OQ-1), dcy-006 (FR-2, FR-3, NFR-2)
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- owner-user scope 기반 `/v1/assets` 요구사항을 daemon-scoped asset visibility와 fail-closed legacy token 규칙으로 갱신하고, Implemented 상태를 Approved로 되돌림
|
||||
|
||||
#### Intent
|
||||
- dcy-006에서 각 asset은 정확히 1개 daemon에 배정되고, `GET /v1/assets`는 owner user 전체가 아니라 인증 daemon 범위만 반환해야 한다. 현재 구현은 `owner_user_id=auth.user_id`만 사용하므로 revised authorization model과 맞지 않는다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-10: daemon-less legacy token fail-closed(403)
|
||||
- Updated:
|
||||
- AC-1, AC-2: owner scope를 daemon scope 기준으로 재정의
|
||||
- Removed:
|
||||
- none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/interface-contract.md`, `docs/runtime-flows.md` — daemon-scoped asset list contract 반영 필요
|
||||
- Modules:
|
||||
- `app/api/v1_assets.py`, `app/schemas/v1.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`, `app/core/auth.py`
|
||||
- Tests:
|
||||
- `tests/test_v1.py`, daemon assignment 관련 신규 회귀 테스트
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-001 (release-candidate; REL-001 released input) — owner-user scope verification evidence는 daemon-scoped visibility 기준으로 전면 재검증 필요
|
||||
- Releases:
|
||||
- REL-001 (released) — released contract는 owner-user scope 기준이라 revised REQ와 불일치
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes
|
||||
- Existing Implementation Invalidated: yes
|
||||
- Status Recommendation: rollback-to-approved
|
||||
- Follow-up Action: dcy-006 후속 batch에서 daemon assignment query, 403 fail-closed path, daemon-scoped regression test를 구현한다.
|
||||
|
||||
#### Validation Plan
|
||||
- `app/api/v1_assets.py`가 현재 `owner_user_id=auth.user_id`만 적용하는지 확인하고, daemon-aware query 설계를 REQ-009와 함께 정리한다.
|
||||
- 구현 후 daemon A/B 분리 시나리오와 daemon-less token 403 시나리오를 targeted pytest로 검증한다.
|
||||
|
||||
### CHG-20260511-02
|
||||
- Date: 2026-05-11
|
||||
- Author: repository-maintainers
|
||||
|
||||
@@ -1,43 +1,88 @@
|
||||
# req-007: GET /v1/user_info 엔드포인트 구현
|
||||
|
||||
- Status: Implemented
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- trex-basic 데몬이 사용자 거래 가능 여부와 브로커 인증 정보를 조회할 수 있도록 한다.
|
||||
- runtime_id는 인증 service-token에 연결된 값을 반환하며, environment 필드는 포함하지 않는다(NFR-1).
|
||||
- `runtime_id`는 인증 service-token의 런타임 세대 식별자이고, `daemon_id`는 stable logical identity로 함께 반환되어야 한다.
|
||||
- credential_payload는 DB에서 Fernet 복호화 후 반환한다(NFR-3/REQ-010 의존).
|
||||
|
||||
## Requirement
|
||||
- GET /v1/user_info는 service-token bearer 인증으로 runtime_id, trading_enabled(users.is_active 매핑), disabled_reason, brokers[](broker, account_ref, credential_payload, credential_version, last_rotated_at)를 반환해야 한다.
|
||||
- GET /v1/user_info는 service-token bearer 인증으로 `runtime_id`, `daemon_id`, `trading_enabled`(users.is_active 매핑), `disabled_reason`, `brokers[]`(broker, account_ref, credential_payload, credential_version, last_rotated_at)를 반환해야 한다.
|
||||
- environment 필드를 응답에 포함하지 않는다.
|
||||
- credential_payload는 Fernet으로 복호화된 JSON을 반환한다.
|
||||
- transition 구간의 legacy token은 `daemon_id = null`을 진단용으로 반환할 수 있어야 하며, `runtime_id`가 없는 구형 token 처리 규칙은 기존 v1 contract를 유지한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given runtime_id가 연결된 service-token으로 GET /v1/user_info 호출 시, Then HTTP 200과 응답 JSON에 runtime_id, trading_enabled, brokers[] 필드가 포함되어야 한다.
|
||||
1. Given `runtime_id`가 연결된 service-token으로 `GET /v1/user_info` 호출 시, Then HTTP 200과 응답 JSON에 `runtime_id`, `daemon_id`, `trading_enabled`, `brokers[]` 필드가 포함되어야 한다.
|
||||
2. Given GET /v1/user_info 응답을 확인하면, Then environment 필드가 존재하지 않아야 한다.
|
||||
3. Given users.is_active가 true인 사용자의 service-token으로 호출 시, Then 응답의 trading_enabled가 true여야 한다.
|
||||
4. Given users.is_active가 false인 사용자의 service-token으로 호출 시, Then 응답의 trading_enabled가 false여야 한다.
|
||||
5. Given broker_credentials에 KIS credential이 저장된 사용자의 토큰으로 호출 시, When 응답의 brokers[] 항목을 확인하면, Then credential_payload가 복호화된 JSON({app_key, app_secret})으로 반환되어야 한다.
|
||||
6. Given runtime_id가 없는 구형 service-token으로 호출 시, Then 설계 단계에서 확정된 오류 응답이 반환되어야 한다(R-4, 404 또는 즉시 runtime_id 생성 중 선택).
|
||||
7. Given 인증 없이 요청 시, Then HTTP 401이 반환되어야 한다.
|
||||
8. Given `daemon_id = NULL`로 backfill된 legacy service-token으로 호출 시, Then HTTP 200 응답이 유지되고 `daemon_id` 필드는 `null`로 노출되어야 한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/data-model.md (REQ-011에서 갱신 예정)
|
||||
- Modules: app/api/v1_user_info.py (신규), app/domain/models.py (BrokerCredential 모델 추가), app/services/ (user_info 조회 서비스 메서드 추가), app/schemas/ (v1 user_info 응답 스키마 추가), app/repositories/postgres.py (broker_credentials 조회 추가), app/main.py (/v1 라우터 등록)
|
||||
- Tests: tests/test_v1_user_info.py (신규)
|
||||
- Modules: app/api/v1_user_info.py, app/schemas/v1.py, app/core/auth.py, app/domain/models.py, app/services/, app/repositories/postgres.py, app/main.py
|
||||
- Tests: tests/test_v1.py, tests/test_auth.py
|
||||
|
||||
## Notes
|
||||
- trading_enabled는 users.is_active 단순 매핑이다(A-4 Confirmed). 별도 trading 전용 상태 필드는 이번 반복 Out of Scope.
|
||||
- disabled_reason: trading_enabled가 false일 때 이유 문자열. 이번 반복에서는 is_active가 false인 경우 "account_inactive" 고정값을 사용하는 방안을 설계 단계에서 확정한다.
|
||||
- R-4(기존 service-token에 runtime_id 없는 경우 처리): 404 또는 즉시 생성 중 설계 단계에서 결정. 이 REQ의 Acceptance Criteria 6번은 설계 결정 후 구체화한다.
|
||||
- `daemon_id = NULL` legacy token은 transition diagnostics 대상으로 허용되지만, daemon-scoped read/write endpoint에서는 fail-closed가 적용된다.
|
||||
- R-4(기존 service-token에 runtime_id 없는 경우 처리): 404 또는 즉시 생성 중 설계 단계에서 결정. 이 REQ의 Acceptance Criteria 6번은 runtime_id 부재 처리 규칙에 한해 유지한다.
|
||||
- Fernet 복호화는 REQ-010에서 정의한 암호화 유틸리티를 사용한다.
|
||||
- Source Discovery: dcy-002 (FR-4, NFR-1, A-4)
|
||||
- Source Discovery: dcy-002 (FR-4, NFR-1, A-4), dcy-006 (FR-1, FR-5, NFR-1)
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- `/v1/user_info` 응답 요구사항에 stable `daemon_id` diagnostics를 추가하고, runtime_id-only 구현 증거를 무효화하여 Status를 Approved로 되돌림
|
||||
|
||||
#### Intent
|
||||
- dcy-006은 `/v1/user_info`를 runtime 세대 식별과 daemon binding diagnostics를 함께 제공하는 endpoint로 재정의한다. 현재 구현/verification은 `runtime_id`만 기준으로 삼아 revised contract를 충족하지 못한다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-8: legacy token의 `daemon_id = null` diagnostics
|
||||
- Updated:
|
||||
- AC-1: 응답 필수 필드에 `daemon_id` 추가
|
||||
- Removed:
|
||||
- none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/interface-contract.md`, `docs/data-model.md`, `docs/runtime-flows.md`
|
||||
- Modules:
|
||||
- `app/api/v1_user_info.py`, `app/schemas/v1.py`, `app/core/auth.py`, `app/repositories/postgres.py`
|
||||
- Tests:
|
||||
- `tests/test_v1.py`, `tests/test_auth.py`
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-001 (release-candidate; REL-001 released input) — `/v1/user_info` evidence를 daemon diagnostics 기준으로 재검증해야 함
|
||||
- Releases:
|
||||
- REL-001 (released) — released smoke는 `runtime_id`만 확인했으므로 revised REQ 충족 증거로 재사용 불가
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes
|
||||
- Existing Implementation Invalidated: yes
|
||||
- Status Recommendation: rollback-to-approved
|
||||
- Follow-up Action: dcy-006 후속 batch에서 `daemon_id` schema/API contract와 legacy diagnostic behavior를 구현한다.
|
||||
|
||||
#### Validation Plan
|
||||
- `app/api/v1_user_info.py`, `app/schemas/v1.py` 현재 구현에 `daemon_id`가 없는지 코드 리뷰로 확인한다.
|
||||
- 구현 후 daemon-bound token, legacy daemon-less token, runtime_id-missing token 시나리오를 분리해 targeted pytest를 추가한다.
|
||||
|
||||
### CHG-20260511-01
|
||||
- Date: 2026-05-11
|
||||
- Author: repository-maintainers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# req-008: POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 구현
|
||||
|
||||
- Status: Implemented
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: repository-maintainers
|
||||
@@ -8,34 +8,81 @@
|
||||
## Intent
|
||||
- trex-basic이 체결 내역을 portfolio-hub에 idempotent하게 전송할 수 있도록 한다.
|
||||
- 동일 (asset_id, order_id, execution_id) 조합의 중복 수신은 DB unique constraint로 감지하고 status=duplicate를 반환한다(NFR-2).
|
||||
- 동일 키에 다른 payload가 오면 충돌로 처리한다.
|
||||
- 동일 키에 다른 payload가 오면 충돌로 처리하며, daemon assignment mismatch 또는 미배정 asset write는 저장 전에 거부한다.
|
||||
|
||||
## Requirement
|
||||
- POST /v1/assets/{asset_id}/ledger/trades는 service-token bearer 인증으로 체결 내역(execution_id, order_id, side, quantity, price, fee_amount, tax_amount, executed_at)을 ledger_trades 테이블에 저장하고 HTTP 201을 반환해야 한다.
|
||||
- POST /v1/assets/{asset_id}/ledger/trades는 service-token bearer 인증으로, 인증된 token의 `daemon_id`에 배정된 asset에 대해서만 체결 내역(execution_id, order_id, side, quantity, price, fee_amount, tax_amount, executed_at)을 ledger_trades 테이블에 저장하고 HTTP 201을 반환해야 한다.
|
||||
- 동일 (asset_id, order_id, execution_id) 재수신은 신규 행 생성 없이 status=duplicate와 기존 ledger_entry_id를 HTTP 200으로 반환해야 한다.
|
||||
- 동일 키에 payload가 다른 경우 HTTP 409를 반환해야 한다.
|
||||
- idempotency는 DB unique constraint로 보장하며, 애플리케이션 로직만으로 보장하지 않는다.
|
||||
- daemon assignment mismatch, 미배정 asset, `daemon_id = NULL` legacy token은 모두 HTTP 403으로 fail-closed 처리해야 하며 ledger row가 생성되면 안 된다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given 유효한 service-token과 신규 체결 데이터로 POST /v1/assets/{asset_id}/ledger/trades 요청 시, Then HTTP 201과 생성된 ledger_entry_id가 반환되어야 한다.
|
||||
1. Given 유효한 daemon-bound service-token과 해당 daemon에 배정된 asset의 신규 체결 데이터로 POST /v1/assets/{asset_id}/ledger/trades 요청 시, Then HTTP 201과 생성된 ledger_entry_id가 반환되어야 한다.
|
||||
2. Given 동일 (asset_id, order_id, execution_id)와 동일 payload로 두 번째 요청 시, Then HTTP 200과 status="duplicate", 기존 ledger_entry_id가 반환되어야 하며 DB에 단일 행만 존재해야 한다.
|
||||
3. Given 동일 (asset_id, order_id, execution_id)에 다른 payload로 요청 시, Then HTTP 409가 반환되어야 한다.
|
||||
4. Given asset_id가 존재하지 않는 portfolio_id인 경우, Then HTTP 404가 반환되어야 한다.
|
||||
5. Given 인증 없이 요청 시, Then HTTP 401이 반환되어야 한다.
|
||||
6. Given asset이 다른 daemon에 배정되어 있는 경우, When 요청 daemon이 asset owner daemon과 다르면, Then HTTP 403이 반환되어야 하고 ledger row는 생성되지 않아야 한다.
|
||||
7. Given asset이 아직 daemon에 배정되지 않았거나 토큰의 `daemon_id`가 NULL인 경우, Then HTTP 403이 반환되어야 하고 ledger row는 생성되지 않아야 한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/interface-contract.md (REQ-011에서 갱신 예정), docs/data-model.md (REQ-011에서 갱신 예정)
|
||||
- Modules: app/api/v1_ledger.py (신규), app/domain/models.py (LedgerTrade 모델 추가), app/services/ (ledger 저장 서비스 메서드 추가), app/schemas/ (v1 ledger 요청/응답 스키마 추가), app/repositories/postgres.py (ledger_trades 저장 및 중복 감지 쿼리 추가), app/main.py (/v1 라우터 등록)
|
||||
- Tests: tests/test_v1_ledger.py (신규)
|
||||
- Modules: app/api/v1_ledger.py, app/domain/models.py, app/services/, app/schemas/, app/repositories/portfolio_repository.py, app/repositories/postgres.py, app/main.py
|
||||
- Tests: tests/test_v1.py 또는 신규 daemon assignment / ledger authorization 회귀 테스트
|
||||
|
||||
## Notes
|
||||
- NFR-2(idempotency DB unique constraint): (asset_id, order_id, execution_id) unique constraint는 REQ-009 migration에서 정의한다.
|
||||
- ledger_trades 테이블과 기존 portfolio_ledger 테이블은 별개다. portfolio_ledger는 내부 원장이고 ledger_trades는 trex-basic이 외부에서 기록하는 체결 내역이다.
|
||||
- 중복 감지 구현: DB unique constraint 위반(psycopg UniqueViolation) 캐치 → 기존 행 조회 → payload 동일 여부 비교 → duplicate 또는 409 응답.
|
||||
- Source Discovery: dcy-002 (FR-5, NFR-2)
|
||||
- daemon authorization은 기존 permission(`trade-write`) 위에 추가되는 두 번째 authorization 축이다.
|
||||
- Source Discovery: dcy-002 (FR-5, NFR-2), dcy-006 (FR-2, FR-4, NFR-2)
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- ledger write REQ에 daemon assignment authorization과 403 fail-closed 규칙을 추가하고, 기존 Implemented 상태를 Approved로 되돌림
|
||||
|
||||
#### Intent
|
||||
- dcy-006은 `trade-write` permission만으로는 충분하지 않고, asset이 인증 daemon에 배정됐는지까지 검증하도록 요구한다. 현재 구현은 asset 존재성만 확인하므로 revised authorization contract를 충족하지 못한다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-6: daemon mismatch 403
|
||||
- AC-7: 미배정 asset 또는 daemon-less token 403
|
||||
- Updated:
|
||||
- AC-1: 성공 조건을 daemon-assigned asset으로 제한
|
||||
- Removed:
|
||||
- none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/interface-contract.md`, `docs/runtime-flows.md`, `docs/data-model.md`
|
||||
- Modules:
|
||||
- `app/api/v1_ledger.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`
|
||||
- Tests:
|
||||
- `tests/test_v1.py` 또는 신규 daemon assignment ledger authorization 테스트
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-001 (release-candidate; REL-001 released input) — ledger verification evidence는 404/409/idempotency 중심이라 daemon authz 케이스를 추가해 재검증해야 함
|
||||
- Releases:
|
||||
- REL-001 (released) — released trade-write contract는 daemon mismatch 403 정책을 포함하지 않음
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes
|
||||
- Existing Implementation Invalidated: yes
|
||||
- Status Recommendation: rollback-to-approved
|
||||
- Follow-up Action: dcy-006 후속 batch에서 asset assignment lookup과 ledger authorization guard를 구현하고 duplicate/conflict 회귀와 함께 재검증한다.
|
||||
|
||||
#### Validation Plan
|
||||
- `app/api/v1_ledger.py`가 현재 asset 존재성만 확인하는지 코드 리뷰로 확인한다.
|
||||
- 구현 후 success/duplicate/conflict/404와 daemon mismatch·unassigned 403 시나리오를 함께 검증한다.
|
||||
|
||||
### CHG-20260511-01
|
||||
- Date: 2026-05-11
|
||||
- Author: repository-maintainers
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# req-012: service-token rotate CLI 명령 구현
|
||||
|
||||
- Status: Implemented
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: Medium
|
||||
- Owner: repository-maintainers
|
||||
@@ -9,23 +9,23 @@
|
||||
|
||||
- 발급된 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`만 교체하고 나머지 메타데이터를 유지하여, 동일한 token 이름과 메타데이터를 유지한 채 새 token 원문만 배포할 수 있게 한다. 단, `runtime_id`를 소비하는 시스템은 rotate 후 새 토큰 기준으로 runtime_id를 재조회해야 한다.
|
||||
- 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한다.
|
||||
- `id`, `user_id`, `name`, `permission`, `token_type`, `expires_at`, `created_at`은 변경하지 않는다.
|
||||
- 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 성공 시 새 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)은 기존과 동일하다.
|
||||
2. Given rotate 성공, When CLI 출력, Then 새 token 원문이 화면에 1회 출력되고 "이 토큰은 지금 1회만 표시" 안내 메시지가 함께 표시된다.
|
||||
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을 반환한다.
|
||||
@@ -38,22 +38,66 @@
|
||||
- `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` 명령 추가
|
||||
- `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 상태 3개 케이스
|
||||
- `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`도 새 값으로 갱신한다. trex-basic 등 `runtime_id`를 소비하는 시스템은 rotate 후 새 토큰으로 다시 인증하고 `GET /v1/user_info`를 재호출해 새 `runtime_id`를 재인식해야 한다.
|
||||
- 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
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# REQ-023: Daemon Registry 관리 경로
|
||||
|
||||
- Status: Approved
|
||||
- Type: Interface
|
||||
- Priority: High
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- service-token issue/rotate가 기존 daemon에만 binding할 수 있으려면, daemon을 사전에 생성·수정할 수 있는 별도 관리 경로(daemon registry CRUD)가 필요하다.
|
||||
- daemon provisioning은 service-token issuance와 분리된 경로에서 수행되어야 하며, REQ-005(issuance binding)와 REQ-012(rotate daemon_id 유지)의 전제 조건이 된다.
|
||||
|
||||
## Requirement
|
||||
- 시스템은 daemon을 생성·수정할 수 있는 별도 관리 경로를 제공해야 한다.
|
||||
- daemon은 owner user 내에서 unique한 `daemon_slug`(lowercase kebab-case)와 `daemon_display_name`(non-empty human-readable label)을 가진다.
|
||||
- 동일 owner user 내에서 중복 `daemon_slug`로 생성 시 명시적으로 거부한다.
|
||||
- daemon list 조회는 owner user 범위로 제한된다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given admin context에서 유효한 `daemon_slug`와 `daemon_display_name`으로 daemon create를 호출할 때, Then 새 daemon entry가 registry에 생성되고 `daemon_id`가 반환된다.
|
||||
2. Given 기존 daemon에 대해 `daemon_display_name` 수정을 요청할 때, Then daemon entry가 갱신된다.
|
||||
3. Given 동일 owner user 내에 이미 존재하는 `daemon_slug`로 daemon create를 요청할 때, Then 명시적 오류로 거부되고 중복 entry가 생성되지 않는다.
|
||||
4. Given daemon list 조회를 요청할 때, Then 인증된 user 소유의 daemon만 반환된다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: `docs/interface-contract.md`, `docs/runtime-flows.md`
|
||||
- Modules: `app/domain/models.py`, `app/repositories/postgres.py`, `app/api/auth_routes.py` 또는 별도 daemon management route, `app/cli/commands/` (daemon subcommand 신규)
|
||||
- Tests: 신규 `tests/test_daemon_registry.py`
|
||||
|
||||
## Notes
|
||||
- service-token issuance의 unknown daemon 거부(REQ-005 AC-6), rotate daemon_id 유지 불변식(REQ-012 CHG-20260513-01)은 이 REQ의 daemon registry가 사전 구축된 상태를 전제한다.
|
||||
- daemon registry CRUD의 구체적인 transport 형태(HTTP endpoint vs CLI subcommand)는 구현 시 결정한다.
|
||||
- canonical 운영 표기: `<username>/<daemon_slug>`
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-02
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- Status: Proposed → Approved (confirm-req 게이트 통과)
|
||||
|
||||
#### Intent
|
||||
- 필수 필드, AC, blocker 없음 확인 후 승인 전환
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added: 없음
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: 없음
|
||||
- Modules: 없음
|
||||
- Tests: 없음
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: 미정
|
||||
- Releases: 미정
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: 없음
|
||||
|
||||
#### Validation Plan
|
||||
- 없음 (상태 전환만)
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- dcy-006 FR-1의 daemon provisioning 관리 경로 부분만 추출해 신규 생성 (issuance binding·rotate invariant·fail-closed는 REQ-005/012/006/008에 이미 반영)
|
||||
|
||||
#### Intent
|
||||
- service-token issuance(REQ-005)와 rotate(REQ-012)의 전제가 되는 daemon registry CRUD 경로를 별도 REQ로 정의
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-1~4 전체 신규 추가
|
||||
- Updated: 없음
|
||||
- Removed: 없음
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: `docs/interface-contract.md`, `docs/runtime-flows.md`
|
||||
- Modules: daemon registry route, CLI subcommand
|
||||
- Tests: `tests/test_daemon_registry.py`
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: 미정
|
||||
- Releases: 미정
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: 없음 (신규 REQ)
|
||||
|
||||
#### Validation Plan
|
||||
- pytest로 daemon create/duplicate reject/update/list 시나리오 통과 확인
|
||||
@@ -1,12 +1,13 @@
|
||||
# req-009: v1 API 지원을 위한 DB 스키마 migration 추가
|
||||
|
||||
- Status: Implemented
|
||||
- Status: Approved
|
||||
- Type: Migration
|
||||
- Priority: High
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- dcy-002에서 요구하는 4개 DB 변경(portfolios.market 컬럼, auth_tokens.runtime_id 컬럼, broker_credentials 테이블, ledger_trades 테이블)을 Alembic revision으로 구현한다.
|
||||
- dcy-006의 daemon-aware authorization 모델을 위해 `daemons` registry, `auth_tokens.daemon_id`, `portfolios.managed_by_daemon_id`, transition backfill 및 관련 제약을 같은 migration ownership 규칙 안에서 확장한다.
|
||||
- IF NOT EXISTS 패턴을 유지해 REQ-002 정책을 준수한다(NFR-4).
|
||||
|
||||
## Requirement
|
||||
@@ -15,6 +16,9 @@
|
||||
2. auth_tokens 테이블에 runtime_id TEXT UNIQUE NULLABLE 컬럼 추가
|
||||
3. broker_credentials 테이블 신규 생성 (user_id, broker, account_ref, encrypted_payload, credential_version, last_rotated_at 포함)
|
||||
4. ledger_trades 테이블 신규 생성 (ledger_entry_id PK, asset_id, order_id, execution_id, side, quantity, price, fee_amount, tax_amount, executed_at, recorded_at 포함, (asset_id, order_id, execution_id) unique constraint 포함)
|
||||
5. daemons registry 테이블 신규 생성 (`daemon_id`, `owner_user_id`, stable slug/display metadata, 생성/갱신 시각 포함)
|
||||
6. auth_tokens 테이블에 `daemon_id` NULLABLE FK 컬럼 추가, portfolios 테이블에 `managed_by_daemon_id` NULLABLE FK 컬럼 추가
|
||||
7. transition migration은 기존 service-token과 portfolio의 daemon 관련 컬럼을 `NULL`로 backfill하고, post-cutover exact-one assignment로 수렴할 수 있도록 same-user consistency 및 invalid reference rejection을 위한 FK/check/unique 제약을 제공해야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given alembic upgrade head 실행 후, When portfolios 테이블을 조회하면, Then market TEXT NULLABLE 컬럼이 존재해야 한다.
|
||||
@@ -24,21 +28,69 @@
|
||||
5. Given (asset_id, order_id, execution_id) 동일 조합으로 ledger_trades에 두 번 삽입 시, Then UniqueViolation이 발생해야 한다.
|
||||
6. Given migration을 이미 실행한 DB에 동일 migration을 재실행하면, Then 에러 없이 완료되어야 한다(IF NOT EXISTS 보장).
|
||||
7. Given portfolios 테이블의 기존 행, When migration 실행 후 조회하면, Then market 값이 NULL이어야 한다(기존 데이터 보정 없음, DECIDE_MARKET_DEFAULT Answered).
|
||||
8. Given alembic upgrade head 실행 후, When schema를 조회하면, Then `daemons` 테이블과 `auth_tokens.daemon_id` 컬럼이 존재해야 한다.
|
||||
9. Given alembic upgrade head 실행 후, When portfolios 테이블을 조회하면, Then `managed_by_daemon_id` 컬럼이 존재하고 기존 행은 `NULL`로 backfill되어 있어야 한다.
|
||||
10. Given 존재하지 않는 `daemon_id`를 auth_tokens 또는 portfolios에 바인딩하려고 시도하면, Then FK 또는 동등 제약으로 거부되어야 하며, owner user 일관성을 깨는 binding/assignment는 DB 레벨 제약 또는 동등한 무결성 장치로 방지되어야 한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs: docs/data-model.md (REQ-011에서 갱신 예정)
|
||||
- Modules: migrations/versions/ (신규 migration 파일 추가), app/domain/models.py (Portfolio.market, AuthToken.runtime_id, BrokerCredential, LedgerTrade 모델 필드 추가), app/repositories/postgres.py (신규 테이블 쿼리 추가)
|
||||
- Tests: tests/test_app.py 또는 migration 검증 테스트
|
||||
- Modules: migrations/versions/, app/domain/models.py, app/repositories/portfolio_repository.py, app/repositories/postgres.py
|
||||
- Tests: tests/test_app.py 또는 migration 검증 테스트, daemon schema integrity 검증
|
||||
|
||||
## Notes
|
||||
- NFR-4(IF NOT EXISTS 패턴) 준수 — REQ-002 Alembic migration 정책 기반.
|
||||
- broker_credentials 테이블의 encrypted_payload 컬럼은 Fernet 암호화된 ciphertext를 TEXT로 저장한다. 암복호화 로직은 REQ-010 참조.
|
||||
- 컬럼 상세 타입(NUMERIC vs FLOAT, TIMESTAMPTZ 등)은 설계 단계(draft-batch-design)에서 확정한다.
|
||||
- migration revision ID는 구현 시 기존 chain의 마지막 revision(20260423_0013)을 down_revision으로 참조한다.
|
||||
- Source Discovery: dcy-002 (영향 범위, NFR-4, DECIDE_MARKET_DEFAULT)
|
||||
- daemon exact-one assignment의 steady-state `NOT NULL`/강제 cutover는 inventory/backfill 완료 이후 단계적으로 적용할 수 있다. transition migration은 `NULL` backfill을 허용하되 fail-open을 허용하면 안 된다.
|
||||
- migration revision ID는 구현 시 기존 revision chain의 마지막 head를 down_revision으로 참조한다.
|
||||
- Source Discovery: dcy-002 (영향 범위, NFR-4, DECIDE_MARKET_DEFAULT), dcy-006 (FR-1, FR-2, NFR-2, NFR-3)
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260513-01
|
||||
- Date: 2026-05-13
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- runtime_id 중심 v1 migration REQ를 daemon registry/binding/assignment 스키마까지 포함하도록 확장하고, 기존 Implemented 상태를 Approved로 되돌림
|
||||
|
||||
#### Intent
|
||||
- dcy-006의 daemon-aware model은 기존 REQ-009 구현 범위를 넘어서 `daemons`, `auth_tokens.daemon_id`, `portfolios.managed_by_daemon_id`, transition backfill, same-user consistency 제약을 추가로 요구한다. BAT-001/REL-001 migration evidence는 이 범위를 포함하지 않는다.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added:
|
||||
- AC-8: `daemons`, `auth_tokens.daemon_id`
|
||||
- AC-9: `portfolios.managed_by_daemon_id` + NULL backfill
|
||||
- AC-10: daemon FK / 무결성 제약
|
||||
- Updated:
|
||||
- Requirement 본문에 daemon registry 및 transition backfill 항목 추가
|
||||
- Removed:
|
||||
- none
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs:
|
||||
- `docs/data-model.md`, `docs/runtime-flows.md`
|
||||
- Modules:
|
||||
- `migrations/versions/`, `app/domain/models.py`, `app/repositories/portfolio_repository.py`, `app/repositories/postgres.py`
|
||||
- Tests:
|
||||
- migration integrity / daemon schema constraint 검증
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches:
|
||||
- BAT-001 (release-candidate; REL-001 released input) — 기존 migration verification은 daemon schema, backfill, constraint evidence를 포함하지 않음
|
||||
- Releases:
|
||||
- REL-001 (released) — release facts는 runtime_id/broker_credentials/ledger_trades까지만 다루므로 revised migration REQ를 충족하지 않음
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: yes
|
||||
- Existing Implementation Invalidated: yes
|
||||
- Status Recommendation: rollback-to-approved
|
||||
- Follow-up Action: dcy-006 후속 batch에서 신규 Alembic revision과 migration validation을 설계하고, cutover/backfill evidence를 release 단계에서 수집한다.
|
||||
|
||||
#### Validation Plan
|
||||
- 현재 migration chain과 `app/repositories/postgres.py` schema ownership을 재검토해 daemon schema 추가 위치를 설계한다.
|
||||
- 구현 후 alembic upgrade / re-run / invalid daemon reference 검증을 포함한 migration test plan을 수행한다.
|
||||
|
||||
### CHG-20260511-01
|
||||
- Date: 2026-05-11
|
||||
- Author: repository-maintainers
|
||||
|
||||
+15
-15
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-023`
|
||||
- Current: `REQ-029`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -85,14 +85,14 @@
|
||||
| REQ-002 | Alembic와 runtime schema ownership 경계 정리 | Migration | Implemented | High | repository-maintainers | [REQ-002](./Migration/req-002_alembic-runtime-schema-ownership.md) |
|
||||
| REQ-003 | 로컬 DB 운영 runbook 및 baseline 문서 갱신 | Documentation | Implemented | Medium | repository-maintainers | [REQ-003](./Documentation/req-003_local-db-operations-runbook-and-baseline-updates.md) |
|
||||
| REQ-004 | GET /health 응답 스키마 확장 | Interface | Implemented | High | repository-maintainers | [REQ-004](./Interface/req-004_health-response-schema-extension.md) |
|
||||
| REQ-005 | service-token 발급 시 runtime_id 동시 생성 | Interface | Implemented | High | repository-maintainers | [REQ-005](./Interface/req-005_service-token-runtime-id-issuance.md) |
|
||||
| REQ-006 | GET /v1/assets 엔드포인트 구현 | Interface | Implemented | High | repository-maintainers | [REQ-006](./Interface/req-006_v1-assets-endpoint.md) |
|
||||
| REQ-007 | GET /v1/user_info 엔드포인트 구현 | Interface | Implemented | High | repository-maintainers | [REQ-007](./Interface/req-007_v1-user-info-endpoint.md) |
|
||||
| REQ-008 | POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 구현 | Interface | Implemented | High | repository-maintainers | [REQ-008](./Interface/req-008_v1-ledger-trades-endpoint.md) |
|
||||
| REQ-009 | v1 API 지원을 위한 DB 스키마 migration 추가 | Migration | Implemented | High | repository-maintainers | [REQ-009](./Migration/req-009_v1-api-db-schema-migration.md) |
|
||||
| REQ-005 | service-token 발급 시 runtime_id 동시 생성 | Interface | Approved | High | repository-maintainers | [REQ-005](./Interface/req-005_service-token-runtime-id-issuance.md) |
|
||||
| REQ-006 | GET /v1/assets 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-006](./Interface/req-006_v1-assets-endpoint.md) |
|
||||
| REQ-007 | GET /v1/user_info 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-007](./Interface/req-007_v1-user-info-endpoint.md) |
|
||||
| REQ-008 | POST /v1/assets/{asset_id}/ledger/trades 엔드포인트 구현 | Interface | Approved | High | repository-maintainers | [REQ-008](./Interface/req-008_v1-ledger-trades-endpoint.md) |
|
||||
| REQ-009 | v1 API 지원을 위한 DB 스키마 migration 추가 | Migration | Approved | High | repository-maintainers | [REQ-009](./Migration/req-009_v1-api-db-schema-migration.md) |
|
||||
| REQ-010 | credential_payload Fernet 암호화 및 startup validation | Non-Functional | Implemented | High | repository-maintainers | [REQ-010](./Non-Functional/req-010_credential-payload-fernet-encryption.md) |
|
||||
| REQ-011 | trex-basic 계약 반영을 위한 baseline 문서 갱신 | Documentation | Implemented | Medium | repository-maintainers | [REQ-011](./Documentation/req-011_baseline-docs-update-for-trex-basic-contract.md) |
|
||||
| REQ-012 | service-token rotate CLI 명령 구현 | Interface | Implemented | Medium | repository-maintainers | [REQ-012](./Interface/req-012_service-token-rotate-cli.md) |
|
||||
| REQ-011 | trex-basic 계약 반영을 위한 baseline 문서 갱신 | Documentation | Approved | Medium | repository-maintainers | [REQ-011](./Documentation/req-011_baseline-docs-update-for-trex-basic-contract.md) |
|
||||
| REQ-012 | service-token rotate CLI 명령 구현 | Interface | Approved | Medium | repository-maintainers | [REQ-012](./Interface/req-012_service-token-rotate-cli.md) |
|
||||
| REQ-013 | CLI `portfolio list --user` 필수화 및 소유자 기반 조회 | Interface | Implemented | Medium | repository-maintainers | [REQ-013](./Interface/req-013_portfolio-list-user-required.md) |
|
||||
| REQ-014 | CLI `portfolio create --user` 필수화 및 소유자 설정 | Interface | Implemented | Medium | repository-maintainers | [REQ-014](./Interface/req-014_portfolio-create-user-required.md) |
|
||||
| REQ-015 | CLI `credential` 명령 그룹 구현 (브로커 인증 정보 관리) | Interface | Implemented | Medium | repository-maintainers | [REQ-015](./Interface/req-015_broker-credential-cli.md) |
|
||||
@@ -103,13 +103,13 @@
|
||||
| REQ-020 | 실배포 admin bootstrap 및 steady-state operator access runbook | Deployment | Approved | High | repository-maintainers | [REQ-020](./Deployment/req-020_production-admin-bootstrap-and-operator-access-runbook.md) |
|
||||
| REQ-021 | release 검증 credential matrix 및 token provenance 운영 절차 | Documentation | Approved | High | repository-maintainers | [REQ-021](./Documentation/req-021_release-credential-matrix-and-token-provenance-procedures.md) |
|
||||
| REQ-022 | 운영 로그 접근 및 release evidence 기록 절차 | Documentation | Approved | Medium | repository-maintainers | [REQ-022](./Documentation/req-022_operator-log-access-and-release-evidence-recording.md) |
|
||||
| REQ-023 | Daemon Registry 관리 경로 | Interface | Approved | High | repository-maintainers | [REQ-023](./Interface/req-023_daemon-registry-and-service-token-daemon-binding.md) |
|
||||
| REQ-028 | README.md Daemon Provisioning Runbook 반영 | Documentation | Approved | Medium | repository-maintainers | [REQ-028](./Documentation/req-028_daemon-model-baseline-docs-update.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-05-12: REQ-020~022 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음)
|
||||
- 2026-05-12: REQ-020~022 Proposed 생성 — dcy-005 (실배포 admin bootstrap 및 operator access) FR-1~3, NFR-1~3 기반
|
||||
- 2026-05-11: REQ-011, REQ-012, REQ-015 Implemented 전환 — BAT-008/BAT-002/BAT-005 verification evidence 확인
|
||||
- 2026-05-11: REQ-005 CHG-20260511-02 — service-token 발급 API 경로를 POST /auth/service-tokens로 명확화
|
||||
- 2026-05-11: REQ-012 CHG-20260511-01 — rotate 후 runtime_id lifecycle 및 consumer 재조회 필요성 명시
|
||||
- 2026-05-11: REQ-001~003 Implemented 전환 — BAT-007 verification passed, confirm-req-implemented 게이트 통과
|
||||
- 2026-05-11: REQ-006 CHG-20260511-02 — GET /v1/assets owner scope 회귀 수정 및 BAT-001 verification 정합화
|
||||
- 2026-05-13: REQ-023, REQ-028 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음)
|
||||
- 2026-05-13: REQ-023, REQ-028 Proposed 생성 — dcy-006 FR-1(daemon registry CRUD)·FR-6(README.md runbook); REQ-024~027은 REQ-005~009/011/012 CHG-20260513-01과 완전 중복으로 미생성
|
||||
- 2026-05-13: REQ-005~009, REQ-011~012 CHG-20260513-01 — dcy-006 daemon_id / exact-one asset assignment 반영으로 revised contract가 기존 Implemented evidence를 무효화해 Status를 Approved로 되돌림
|
||||
- 2026-05-13: REQ-021 CHG-20260513-01 — token provenance 절차에 `daemon_id` evidence 필드와 rotate invariant 반영, Status 유지
|
||||
- 2026-05-12: REQ-020~022 Approved 전환 — confirm-req 게이트 통과 (필수 필드, AC, blocker 없음)
|
||||
Reference in New Issue
Block a user