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
6.4 KiB
6.4 KiB
BAT-010 Implementation
- Status: completed
- Batch: BAT-010
Plan Summary
Implemented the full daemon model: Daemon entity (registry CRUD), daemon_id binding on service-token issuance, daemon_id rotate-invariant on token rotation, daemon-scoped authorization on GET /v1/assets and POST /v1/assets/{asset_id}/ledger/trades (fail-closed for NULL daemon_id), daemon_id diagnostic on GET /v1/user_info (NULL → 200+null), CLI daemon subcommand (create/list/update), CLI service-token rotate command, Alembic migration 20260513_0016, and docs updates across data-model.md, interface-contract.md, runtime-flows.md, and README.md.
Changed Files
New Files
migrations/versions/20260513_0016_daemon_model.py— Alembic revision; creates daemons table, adds auth_tokens.daemon_id and portfolios.managed_by_daemon_idapp/cli/commands/daemon.py— CLI daemon subcommand (create, list, update)tests/test_daemon_registry.py— 12 unit tests (REQ-023, REQ-005, REQ-012)tests/test_daemon_assignment.py— 8 integration tests (REQ-006, REQ-007, REQ-008)
Modified Files
app/domain/models.py— Added Daemon dataclass; added daemon_id to AuthToken, TokenPrincipal, Portfolioapp/core/auth.py— Added daemon_id to AuthContext; propagated from principalapp/schemas/v1.py— UserInfoResponse: runtime_id optional, daemon_id addedapp/schemas/auth.py— IssueServiceTokenRequest/ApiResponse/CliResponse: daemon_id addedapp/repositories/portfolio_repository.py— Protocol: create_auth_token(daemon_id), list_portfolios(managed_by_daemon_id), list_portfolios_with_strategies, rotate_service_token, daemon CRUD methodsapp/repositories/postgres.py— _SELECT_AUTH_TOKEN/TOKEN_PRINCIPAL/PORTFOLIO queries updated; create_auth_token(daemon_id), get_token_principal(daemon_id), list_portfolios(managed_by_daemon_id), list_portfolios_with_strategies, rotate_service_token, ensure_schema (daemons table + daemon_id columns), _row_to_auth_token/portfolio/daemon, daemon CRUDapp/services/auth_service.py— issue_service_token(daemon_id with validation), rotate_service_tokenapp/api/v1_assets.py— NULL daemon_id guard (HTTP 403); daemon-scoped list_portfolios_with_strategiesapp/api/v1_user_info.py— Removed 404 for null runtime_id; added daemon_id to responseapp/api/v1_ledger.py— 3-step daemon assignment authorization (NULL token, unassigned asset, mismatch)app/cli/commands/service_tokens.py— issue: added --daemon-id option; added rotate commandapp/cli/main.py— Registered daemon_app typertests/test_auth.py— FakeRepo.create_auth_token: added daemon_id paramtests/test_v1.py— Mock repos: added daemon_id; all asset/ledger TokenPrincipal: added daemon_id="d-1"; Portfolio mocks: added managed_by_daemon_id="d-1"; user_info null-runtime_id tests updated (404→200+null)tests/test_cli.py— FakeRepo.create_auth_token: added daemon_id paramdocs/data-model.md— Added Daemon entity; updated Portfolio/AuthToken fields; updated Change Logdocs/interface-contract.md— Added Daemon Authorization Model; updated all v1 endpoints; updated CLI surface; updated Change Logdocs/runtime-flows.md— Added daemon registry and rotate invariant flows; updated v1 endpoint flows; updated Alembic boundary; updated Change Logdocs/batches/bat-010_20260513_daemon-model-implementation/design.md— Status: confirmed
Execution Log
- Added
Daemondataclass toapp/domain/models.py; addeddaemon_idfields toAuthToken,TokenPrincipal,Portfolio. - Updated
AuthContextinapp/core/auth.pyto carrydaemon_id; propagated from_resolve_auth_context. - Extended
UserInfoResponseinapp/schemas/v1.py:runtime_idoptional,daemon_idadded. - Extended auth schemas in
app/schemas/auth.py:daemon_idon issue request and responses. - Extended
PortfolioRepositoryprotocol with daemon CRUD,rotate_service_token, and updated signatures. - Implemented full daemon support in
app/repositories/postgres.py: updated SELECT queries, ensure_schema DDL, row mappers, create_auth_token, get_token_principal, list_portfolios, list_portfolios_with_strategies, rotate_service_token, and all daemon CRUD methods. - Updated
AuthService.issue_service_tokento accept and validatedaemon_id; addedrotate_service_token. - Updated
v1_assets.py: NULL daemon_id → 403;list_portfolios_with_strategieswithmanaged_by_daemon_id=auth.daemon_id. - Updated
v1_user_info.py: removed 404 for null runtime_id; addeddaemon_id=auth.daemon_idto response. - Updated
v1_ledger.py: 3-step daemon assignment authorization beforeinsert_ledger_trade. - Created
app/cli/commands/daemon.pywithcreate,list,updatesubcommands. - Updated
app/cli/commands/service_tokens.py:--daemon-idoption on issue; addedrotatecommand. - Registered
daemon_appinapp/cli/main.py. - Created
migrations/versions/20260513_0016_daemon_model.pywith idempotent DDL guards. - Fixed
tests/test_auth.pyandtests/test_cli.pyFakeRepo.create_auth_token signatures. - Fixed
tests/test_v1.py: all asset/ledger TokenPrincipal + Portfolio mocks updated; null-runtime_id tests renamed and corrected. - Created
tests/test_daemon_registry.py(12 tests: daemon CRUD + token binding + rotate invariant). - Created
tests/test_daemon_assignment.py(8 tests: v1 daemon-scoped auth). - Updated
docs/data-model.md,docs/interface-contract.md,docs/runtime-flows.md,README.md.
Validation
pytestrun: 128 passed, 7 failed (7 failures are pre-existing in test_cli.py; confirmed via git stash baseline check before BAT-010 changes).- All 20 new daemon tests pass (test_daemon_registry.py: 12, test_daemon_assignment.py: 8).
- All existing tests in test_auth.py, test_v1.py, test_portfolios.py, test_strategy_sets.py continue to pass.
Remaining Risks
- Pre-existing 7 test failures in test_cli.py (out of BAT-010 scope; tracked separately).
- Production enforcement gate requires manual operator action: NULL token inventory + dry-run + release approver approval before deployment (documented in README Daemon Provisioning Runbook).
managed_by_daemon_idassignment to existing portfolios requires DB migration or manual update; no CLI path yet for daemon assignment on existing portfolios.- Real DB integration test against TimescaleDB with migration 20260513_0016 not yet run (env not available in this batch).