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:
@@ -108,8 +108,13 @@ pfhub user delete <username> --force
|
||||
pfhub service-token list --user alice
|
||||
pfhub service-token issue nightly --user alice --ttl-days 30
|
||||
pfhub service-token issue permanent --user alice --no-expiry
|
||||
pfhub service-token issue my-daemon-token --user alice --daemon-id <daemon_id>
|
||||
pfhub service-token revoke nightly --force
|
||||
pfhub service-token rotate permanent --force
|
||||
pfhub daemon create alpha-bot --user alice --display-name "Alpha Trading Bot"
|
||||
pfhub daemon list --user alice
|
||||
pfhub daemon update <daemon_id> --display-name "Updated Name"
|
||||
pfhub daemon update <daemon_id> --deactivate
|
||||
pfhub portfolio list --user <username>
|
||||
pfhub portfolio status 005930:tv --recent 10
|
||||
pfhub portfolio create 005930:tv --symbol 005930 --user <username> --cash-balance 10000000 --force
|
||||
@@ -134,6 +139,9 @@ Supported commands:
|
||||
- `service-token issue`
|
||||
- `service-token revoke`
|
||||
- `service-token rotate`
|
||||
- `daemon create`
|
||||
- `daemon list`
|
||||
- `daemon update`
|
||||
- `portfolio list`
|
||||
- `portfolio status`
|
||||
- `portfolio create`
|
||||
@@ -155,6 +163,15 @@ Service token issue policy:
|
||||
- `--no-expiry`는 관리자 권한에서만 허용된다.
|
||||
- 토큰 원문은 발급 응답 1회 이후 마스킹된 값으로만 출력/기록된다.
|
||||
|
||||
### runtime_id vs daemon_id
|
||||
|
||||
Two stable identifiers are attached to service tokens:
|
||||
|
||||
- **`runtime_id`**: per-token opaque ID, regenerated on every `service-token rotate`. Use to track which token generation is currently active (`GET /v1/user_info` returns it for live verification).
|
||||
- **`daemon_id`**: stable logical identity for a trading daemon process. Bound at token issuance and preserved across rotates (rotate invariant). Use to scope daemon-owned assets and authorize ledger writes.
|
||||
|
||||
A token with `daemon_id=null` is a legacy/unbound token. `GET /v1/assets` and `POST /v1/assets/{asset_id}/ledger/trades` return HTTP 403 for null-daemon tokens (fail-closed). `GET /v1/user_info` returns 200 with `daemon_id: null` as a diagnostic (not an error).
|
||||
|
||||
The CLI uses the same repository and settings layer as the HTTP API. To target the external TimescaleDB, set `pfhub_DB_ENABLED=true` and provide `TIMESCALEDB_*` values in the environment or `.env`.
|
||||
|
||||
Initialize a new database explicitly before the first use:
|
||||
@@ -179,6 +196,72 @@ pfhub_DB_ENABLED=true pfhub portfolio list
|
||||
|
||||
This repository now uses Alembic for versioned schema migrations. Future schema changes should be added under `migrations/versions`.
|
||||
|
||||
## Daemon Provisioning Runbook
|
||||
|
||||
이 섹션은 daemon registry 등록, service-token 바인딩, asset 할당, 그리고 daemon-scoped enforcement 활성화 절차를 정의한다.
|
||||
|
||||
### 1. Daemon 등록
|
||||
|
||||
```bash
|
||||
# 1. daemon 생성 (daemon_slug는 per-user unique)
|
||||
pfhub_DB_ENABLED=true pfhub daemon create alpha-bot --user alice --display-name "Alpha Trading Bot"
|
||||
# 출력: daemon_id (UUID), daemon_slug, is_active=True
|
||||
|
||||
# 2. daemon 목록 확인
|
||||
pfhub_DB_ENABLED=true pfhub daemon list --user alice
|
||||
```
|
||||
|
||||
### 2. Service Token을 Daemon에 바인딩
|
||||
|
||||
```bash
|
||||
# daemon_id는 위 create 출력에서 확인
|
||||
pfhub_DB_ENABLED=true pfhub service-token issue alpha-bot-token --user alice \
|
||||
--daemon-id <daemon_id> --permission trade-write --no-expiry
|
||||
|
||||
# 발급된 토큰 원문은 1회만 표시됨. daemon_id와 runtime_id를 기록해 둘 것.
|
||||
```
|
||||
|
||||
바인딩 규칙:
|
||||
- daemon은 존재하고 `is_active=True`여야 한다.
|
||||
- daemon의 `owner_user_id`가 토큰 발급 대상 user와 일치해야 한다.
|
||||
- 비활성 daemon에 대한 token 발급은 즉시 거부된다.
|
||||
|
||||
### 3. Asset을 Daemon에 할당
|
||||
|
||||
현재 `managed_by_daemon_id` 할당은 DB 직접 수정 또는 배포 시 마이그레이션으로 수행한다(CLI portfolio update를 통한 daemon 할당은 향후 추가 예정).
|
||||
|
||||
```sql
|
||||
UPDATE portfolios SET managed_by_daemon_id = '<daemon_id>' WHERE name = 'my-portfolio' AND owner_user_id = '<user_id>';
|
||||
```
|
||||
|
||||
### 4. Daemon-Scoped Enforcement 활성화 게이트
|
||||
|
||||
daemon-scoped enforcement(NULL token → 403)는 코드 레벨에서 이미 활성화되어 있다. 프로덕션 전환 전 아래 4가지 증거를 확보해야 한다:
|
||||
|
||||
| # | 항목 | 충족 기준 |
|
||||
|---|------|-----------|
|
||||
| 1 | Active NULL daemon_id token inventory 완료 | `SELECT COUNT(*) FROM auth_tokens WHERE daemon_id IS NULL AND token_type='service' AND revoked_at IS NULL` = 0 (또는 의도된 레거시 토큰만 남음) |
|
||||
| 2 | Dry-run 결과 zero active-NULL | 대상 환경에서 위 쿼리 실행, 결과 스크린샷 또는 텍스트 캡처 |
|
||||
| 3 | Dry-run 결과 zero mismatch | `SELECT COUNT(*) FROM portfolios p JOIN auth_tokens t ON p.managed_by_daemon_id != t.daemon_id WHERE t.token_type='service' AND t.revoked_at IS NULL` = 0 |
|
||||
| 4 | Release approver 승인 | 위 증거 3가지를 release 문서에 첨부하고 release approver 서명 확보 |
|
||||
|
||||
마이그레이션 20260513_0016이 적용된 환경에서만 enforcement가 정상 동작한다.
|
||||
|
||||
### 5. Service Token Rotate (daemon_id invariant)
|
||||
|
||||
```bash
|
||||
pfhub_DB_ENABLED=true pfhub service-token rotate alpha-bot-token --force
|
||||
# runtime_id는 새로 발급됨; daemon_id는 유지됨
|
||||
```
|
||||
|
||||
rotate 후 `GET /v1/user_info`로 새 `runtime_id`와 기존 `daemon_id`를 교차 확인한다:
|
||||
|
||||
```bash
|
||||
curl -X GET "<BASE_URL>/v1/user_info" \
|
||||
-H "Authorization: Bearer <NEW_TOKEN>"
|
||||
# 응답: runtime_id=<new_id>, daemon_id=<same_daemon_id>
|
||||
```
|
||||
|
||||
## Local DB Validation Runbook
|
||||
|
||||
이 섹션은 로컬 docker compose 환경에서 DB 초기화, 마이그레이션, health-check, smoke flow를 반복 검증하기 위한 절차를 정의한다.
|
||||
|
||||
Reference in New Issue
Block a user