docs(dcy-011): confirm-discovery — 데몬 admin CRUD + 사용자 self-service 인증정보 관리 API (소유스코프·쓰기전용·secret 미반환)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
2026-06-28 13:44:31 +09:00
co-authored by Claude Opus 4.8
parent 29cd86f0e4
commit a1bde2cf67
2 changed files with 194 additions and 0 deletions
@@ -0,0 +1,193 @@
# 0. 문서 상태
이 문서의 현재 상태와 식별 정보를 기록한다. 문서 추적과 승인 흐름에 필요한 최소 메타데이터를 적는다.
- 상태: confirmed
- 문서 ID: dcy-011_20260628_daemon-crud-and-self-service-credential-api
- 이슈명: 데몬 admin CRUD API + 사용자 self-service 브로커 인증정보 관리 API — 소유 스코프 write/rotate/메타데이터(복호화 secret 미반환), 데몬 생성·할당·(비)활성화는 admin. trex-web dcy-009 백엔드 (교차 발원)
- 작성 시각(KST): 2026-06-28
- 마지막 갱신 시각(KST): 2026-06-28
- 대체됨: 없음
- 후속 Discovery 참조: 없음
- 생성된 REQ 참조: 없음
# 1. 계획 상태 요약
현재 Discovery가 어떤 배경에서 작성되고 있는지, 연계 문서가 있는지, 지금 시점의 해석이 무엇인지 짧게 요약한다.
- 연계 문서 현황:
- 관련 점수 파일: 미생성
- 관련 TODO/메모: GitHub org 보드 fourblocks2020/trex #7 — "portfolio-hub 데몬 레지스트리/관리 API", "서비스 토큰/데몬 발급·관리". **교차 발원 = trex-web `dcy-009`**(사용자 self-service 인증정보 관리 + admin 데몬 provisioning UI). 선행 = `dcy-010`(daemon-scoped 브로커 인증정보 모델/보안), `dcy-004`(토큰·권한 모델)
- 해석:
- trex-web dcy-009가 정의한 기능(사용자가 자기 데몬 KIS 인증정보를 self-service 관리, admin이 데몬 provisioning)을 받쳐줄 portfolio-hub 백엔드 API가 없다. 현재 데몬은 read-only(`/v1/daemons`), 인증정보는 CLI(`pfhub credential`) 전용이며 복호화 read는 service-token `/v1/user_info`뿐이다.
- 핵심 판단: ① **사용자 소유 스코프** 인증정보 관리 API(write/rotate/delete + 메타데이터, **복호화 secret 절대 미반환**), ② **admin 데몬 CRUD** API(생성·할당·(비)활성화)를 dcy-004 권한 모델 위에 어떻게 설계하고, service-token decrypt 경로(REQ-051)와 어떻게 분리·불변 유지할지.
# 2. 요구사항 판정 결과
이번 요청이 기존 요구사항과 비교해 신규인지, 유사한지, 중복인지 판단하고 그 근거를 남긴다.
- 판정: 신규
- 근거
- 기존 `/v1/daemons`는 read 전용이고, 인증정보 관리는 CLI 전용이다. **HTTP 기반 소유 스코프 인증정보 관리 API**와 **admin 데몬 CRUD API**는 정의된 바 없다.
- dcy-010은 인증정보 *모델/저장*(CLI·service-token read)을 다뤘고, 본 건은 *사용자/admin HTTP 관리 표면*을 신설하므로 신규 주기로 추적한다.
# 3. 문제점의 요약
문제의 현재 상태, 기대 상태, 관련 이해관계자, 제약사항을 정리한다.
- 현재 상태
- `BrokerCredential`(dcy-010): PK `(daemon_id, broker)`, `user_id` 보유, Fernet 암호화 `{app_key, app_secret}`. 관리=CLI(`pfhub credential set/list/remove --daemon-id`). 복호화 read=**service-token 전용 `GET /v1/user_info`**(REQ-051, web/user-token 403).
- `Daemon`(id, owner_user_id, daemon_slug, display_name, is_active): read API `GET /v1/daemons`만. 생성·변경·(비)활성화 HTTP API 없음.
- 권한 모델(dcy-004): user-token(권한 read/write), admin role, service-token(daemon_id 바인딩). 소유 스코프 인가 선례 = dcy-009 trex-web 포트폴리오/REQ-049 `managed_by_daemon_id` 일치.
- 암호화 유틸(`app/core/encryption.py` Fernet, `CREDENTIAL_ENCRYPTION_KEY`) 존재.
- 기대 상태
- **사용자(소유 스코프)**: HTTP로 자기 소유 데몬의 인증정보를 등록·회전·삭제하고 메타데이터(broker, account_ref, version, last_rotated, 보유 여부)를 조회. **자기 것만**.
- **admin**: HTTP로 데몬을 생성·사용자 할당·변경·(비)활성화.
- **복호화된 app_secret은 user-token/admin 어떤 응답에도 포함되지 않는다.** service-token decrypt 경로(REQ-051)는 불변.
- 이해관계자
- 사장님(보안·권한·결재), 팀장(거버넌스), portfolio-hub-dev(구현), 일반 사용자(self-service 소비, trex-web 경유), admin/운영자(데몬 provisioning), trex-web 개발(dcy-009 소비측)
- 제약사항
- **최민감 시크릿(KIS app_secret = 실계좌·실자금)** 복호화 평문이 user-token/admin 응답·로그에 노출 금지. service-token `/v1/user_info`(REQ-051)가 유일 decrypt-return 경로 — 불변.
- **소유 스코프 인가 엄격**: 사용자는 자기 소유 데몬(owner_user_id==요청자)의 인증정보만. 타인 접근 403.
- 데몬은 운영 런타임 식별자 → 생성·(비)활성화 영향 큼. 삭제는 dcy-009 결정대로 제외(비활성화).
- CLI(`pfhub credential`)와 신규 API의 동작 정합(동일 repository·암호화 자산 재사용).
## 주요 사용자 시나리오
- admin이 `POST /v1/daemons`(또는 동등)로 데몬 D를 생성하고 owner=사용자 U로 할당한다.
- 사용자 U가 (trex-web 경유, user-token) 자기 데몬 D의 인증정보를 등록(app_key/app_secret) → 서버 Fernet 암호화 저장. 응답엔 secret 미포함, account_ref·version만.
- 사용자 V가 사용자 U의 데몬 D 인증정보 조회/수정 요청 → 403(소유 스코프).
- 데몬 D 인스턴스는 service-token으로 `/v1/user_info`에서 복호화 인증정보 fetch(기존 경로 불변).
# 4. 이번에 정의할 변경
이번 Discovery에서 다루는 핵심 변경 항목.
- (C1) **사용자 소유 스코프 인증정보 관리 API** — 자기 소유 데몬 인증정보 set/rotate/delete + 메타데이터 GET. user-token + ownership 게이트. **복호화 secret 미반환**. 암호화·저장은 dcy-010 자산 재사용.
- (C2) **admin 데몬 CRUD API** — 데몬 생성·owner 할당·변경·(비)활성화. admin role. (삭제 제외 — 비활성화.)
- (C3) **repository 계층 확장** — credential ownership/메타데이터 조회·daemon CRUD 메서드. CLI와 동일 자산 공유.
## 영향 범위
- 코드 영향:
- `app/api/` — 신규 인증정보 관리 라우트(소유 스코프) + `v1_daemons.py` write(admin) 확장
- `app/repositories/*` — credential 메타데이터/ownership 조회, daemon create/update/deactivate
- `app/core/auth.py` — ownership 게이트(요청자==owner) + admin 게이트 재사용
- `app/schemas/*` — 관리 요청/응답 스키마(**secret 미포함** 메타데이터 응답)
- 암호화: `app/core/encryption.py`(dcy-010) 재사용
- 문서 영향:
- `docs/srs/`(신규 REQ), `docs/discovery/index.md`(본 문서), `docs/interface-contract.md`(신규 엔드포인트)
- 교차: trex-web dcy-009(소비자 UI)
- 운영 영향:
- 사용자 self-service 등록으로 CLI 부담 감소(CLI는 fallback 유지). admin 데몬 provisioning HTTP 경로.
# 5. 요구사항 목록
## 기능 요구사항
- FR-1: 사용자가 **자기 소유 데몬**의 인증정보를 등록·회전·삭제한다(user-token). app_key/app_secret 입력 → Fernet 암호화 저장(dcy-010). 회전 시 credential_version++/last_rotated_at 갱신.
- FR-2: 사용자가 자기 인증정보 **메타데이터**(broker, account_ref, credential_version, last_rotated_at, 데몬별 보유 여부)를 조회한다. **복호화된 app_key/app_secret은 응답에 포함되지 않는다.**
- FR-3: 인증정보 관리 경로는 **소유 스코프 인가**를 강제한다 — 요청자 user_id != 대상 데몬 owner_user_id → HTTP 403(타인 데몬·계좌 접근 차단).
- FR-4: admin이 데몬을 생성·owner 할당·변경(display_name 등)·(비)활성화한다(admin role). **하드 삭제는 제공하지 않는다(비활성화로 대체).**
## 비기능 요구사항
- NFR-1 (보안 — 최우선): 복호화된 app_secret은 **user-token/admin 어떤 응답·로그에도 포함되지 않는다**(쓰기 전용 관리 + 메타데이터). service-token `GET /v1/user_info`(REQ-051)가 유일 decrypt-return 경로 — 본 건으로 변경하지 않는다. at-rest Fernet 불변.
- NFR-2 (권한): 인증정보 관리 = **소유 스코프**(user-token, 요청자==데몬 owner). 데몬 CRUD = **admin role**. dcy-004 토큰/권한 모델 재사용.
- NFR-3 (일관성): 신규 API·CLI(`pfhub credential`)·service-token read가 동일 `BrokerCredential`/`Daemon` 모델·암호화 자산에서 정합 동작. daemon_id·owner_user_id 바인딩 정합(dcy-010).
## 범위 경계
- In Scope
- 소유 스코프 인증정보 관리 API(set/rotate/delete + 메타데이터, secret 미반환).
- admin 데몬 CRUD API(생성·할당·변경·(비)활성화, 삭제 제외).
- repository·스키마·권한 게이트.
- Out of Scope
- service-token 복호화 read 경로(`/v1/user_info`, dcy-010/REQ-051) 변경 — **불변**.
- trex-web UI(= dcy-009).
- 서비스 토큰 발급·폐기(별 Discovery, dcy-009 OQ-3).
- 외부 시크릿 매니저 외부화.
- 데몬 하드 삭제.
# 6. 리스크/가정 목록
## 리스크
- R-1 (High): 복호화 app_secret이 user-token/admin 관리 응답·로그로 유출(실계좌 탈취).
- 완화 방안: 관리 API는 set/rotate/delete + 메타데이터만, 복호화 payload 절대 미반환. 응답 스키마에 secret 필드 부재. 로그 secret 미포함(REQ-051 패턴). service-token 경로와 라우트·권한 분리. 음성 테스트.
- R-2 (High): ownership 인가 결함 → 사용자가 타인 데몬 인증정보 조회·수정(횡적 권한 상승).
- 완화 방안: 모든 관리 경로 **엄격 ownership 검사**(요청자==owner_user_id, 불일치 403). dcy-009 trex-web 포트폴리오/REQ-049 daemon-scope 인가 재사용. 음성 테스트(타인 자원→403) 필수.
- R-3 (Medium): 데몬 (비)활성화·변경 blast radius — 운영 인스턴스가 daemon_id로 fetch 중.
- 완화 방안: 삭제 미제공(비활성화), 참조 무결성·활성 데몬 보호, 인증정보 보유 데몬 비활성화 경고.
- R-4 (Medium): CLI·신규 API 이중 관리 경로 불일치.
- 완화 방안: 동일 repository 메서드·암호화 자산 재사용, PK `(daemon_id, broker)` 정합.
## 가정
- A-1: dcy-010 `BrokerCredential`(PK `(daemon_id, broker)`, user_id, Fernet, `CREDENTIAL_ENCRYPTION_KEY`) 완료. (확인됨, Implemented)
- A-2: `Daemon.owner_user_id` 존재 → 소유 스코프 인가가 데이터상 가능. (확인됨)
- A-3: dcy-004 토큰/권한(user-token, admin role, service-token) 재사용.
- A-4: 교차 발원 trex-web `dcy-009`(confirmed)가 소비자. UI는 dcy-009 범위.
- A-5: service-token `/v1/user_info`(REQ-051) decrypt-return 경로 불변(유일 decrypt 경로).
## 오픈 질문
- OQ-1: 인증정보 관리 엔드포인트 경로·형태 — daemon-scoped(`/v1/daemons/{id}/credential`) vs user-credential.
- 상태: **Deferred (2026-06-28)** → draft-batch-design 단계 위임.
- 처리 방안: design에서 REST 형태 확정. 팀장 권고 = daemon-scoped 경로(소유 스코프 자연). dcy-009 UI 흐름·v1 컨벤션 정합.
- 종료 조건: draft-batch-design 전 경로·메서드 확정.
- OQ-2: admin 데몬 CRUD 경로 — `/v1/daemons` 확장 vs `/v1/admin/daemons`.
- 상태: **Deferred (2026-06-28)** → draft-batch-design 단계 위임.
- 처리 방안: design에서 확정. dcy-006 admin 경로 컨벤션 참고.
- 종료 조건: draft-batch-design 전 확정.
- OQ-3: 데몬 owner 재할당 허용 여부 + 재할당 시 인증정보 처리.
- 상태: **Answered (2026-06-28)**
- 결정: **재할당 허용(admin), 단 owner 변경 시 해당 데몬의 기존 인증정보 자동 무효화**(이전 owner의 KIS 계좌 이관 금지 — 새 owner가 재등록). §5 FR-4/범위·R-2 반영.
# 7. 초기 성공 기준
- S-1: 사용자가 자기 데몬 인증정보를 API로 등록하면, 그 데몬이 service-token `/v1/user_info`에서 동일 계좌를 fetch함을 검증할 수 있다.
- S-2: 인증정보 관리/메타데이터·데몬 CRUD 어떤 user-token/admin 응답에도 복호화 app_secret/app_key가 포함되지 않음이 테스트로 확인된다.
- S-3: 사용자 A가 사용자 B 소유 데몬의 인증정보 조회·수정 시 403(소유 스코프)이 테스트로 확인된다.
- S-4: admin이 데몬 생성·owner 할당·(비)활성화를 수행하고, 비-admin은 거부(403)됨이 확인된다.
## 측정 방식 및 데이터 출처
- S-1 측정 방식: API 등록 → service-token `/v1/user_info` fetch 일치 / 데이터 출처: 통합테스트, broker_credentials 행
- S-2 측정 방식: 관리·CRUD 응답 스키마/실응답에서 secret 필드 부재 + 로그 미노출 / 데이터 출처: API·로그 테스트
- S-3 측정 방식: 타 사용자 자원 접근 → 403 음성 테스트 / 데이터 출처: API 테스트
- S-4 측정 방식: admin/비-admin 권한 경로 테스트 / 데이터 출처: API 테스트
# 8. REQ로 넘기기 전 확인 체크
- [x] 범위(In Scope/Out of Scope) 확정
- 기준: 데몬 owner 재할당 정책(OQ-3 재할당+인증정보 무효화) 결정 완료
- [x] 성공 지표의 측정 방식/데이터 출처 확정
- 기준: S-1~S-4 측정 가능성 검토 완료
- [x] High 영향 리스크 우선순위 및 해소 책임자 지정
- 기준: R-1(secret 노출 — 쓰기전용)·R-2(소유 스코프 인가 — 엄격 ownership) 완화 모델 확정. 구현 책임=portfolio-hub-dev
- [x] 모든 오픈 질문(OQ) `Open` 상태 없음 확인
- 기준: OQ-3 Answered, OQ-1/OQ-2 Deferred(design 위임)
- [x] Freeze 확정 담당자(CONFIRMED_BY) 지정
- 기준: 사장님+팀장 confirm-discovery 완료
# 9. 파일 처리 결과
- 처리 결과: 생성
- 생성 경로: docs/discovery/dcy-011_20260628_daemon-crud-and-self-service-credential-api.md
- 참조 문서:
- trex-web docs/discovery/dcy-009_20260628_daemon-and-broker-credential-admin-management.md (교차 발원 — 소비자 UI/요건)
- docs/discovery/dcy-010_20260627_daemon-scoped-broker-credential-provisioning.md (선행 — 인증정보 모델/보안/REQ-051)
- docs/discovery/dcy-004_20260520_token-issuance-and-lifecycle-overhaul.md (권한/토큰 모델)
# 10. 사용자 결정 필요 항목 요약
## DECIDE
- ~~데몬 owner 재할당 (OQ-3)~~ → **RESOLVED 2026-06-28: 재할당 허용(admin), owner 변경 시 기존 인증정보 자동 무효화**
## CONFIRM
- ~~쓰기 전용·secret 미반환·service-token 유일 decrypt 불변~~ → **RESOLVED 2026-06-28: 확정(NFR-1)**
- ~~엄격 소유 스코프 인가 + 데몬 CRUD admin~~ → **RESOLVED 2026-06-28: 확정(NFR-2)**
## DATA
- {{DATA_V1_CONVENTIONS:기존 v1 API 경로·권한 컨벤션(/v1/daemons, /v1/portfolios, dcy-006 admin) — 엔드포인트 형태(OQ-1/OQ-2 design) 설계 입력}}
# 11. Discovery Freeze
REQ 초안 작성 인계 시점의 확정 플래그와 섹션 참조만 기록한다.
## 섹션 참조
- 범위: [# 5. 요구사항 목록 > 범위 경계](#5-요구사항-목록)
- 리스크/오픈 질문: [# 6. 리스크/가정 목록](#6-리스크가정-목록)
- 성공 기준: [# 7. 초기 성공 기준](#7-초기-성공-기준)
- 인계 전 확인: [# 8. REQ로 넘기기 전 확인 체크](#8-req로-넘기기-전-확인-체크)
- 사용자 결정 필요: [# 10. 사용자 결정 필요 항목 요약](#10-사용자-결정-필요-항목-요약)
## Freeze 플래그
- Handoff Decision: REQ Drafting 진행 가능
- Handoff Rationale: confirm-discovery 완료(2026-06-28). CONFIRM 2건(쓰기전용·secret 미반환·service-token 유일 decrypt / 엄격 소유 스코프+데몬 CRUD admin) + DECIDE 1건(OQ-3 owner 재할당 허용+인증정보 자동 무효화) 확정. OQ-1/OQ-2(엔드포인트 경로)는 draft-batch-design 위임 Deferred. 잔여 Open 0건 → REQ 인계 가능. trex-web dcy-009가 이 계약 소비.
- Ready for REQ Drafting: true
- Confirmed By: 팀장(Claude)
- Confirmed At (KST): 2026-06-28
+1
View File
@@ -16,3 +16,4 @@
| dcy-008_20260625_current-holding-wac-cost-basis-read | 20260625 | 현재 보유 종목별 평단(WAC) 조회 API — trex-core 중장기 cold-start 슬롯 평단 seed 의존(교차 발원 trex-core dcy-034 FR-7). GET /v1/portfolios/{id}/holdings/cost-basis | [Discovery](./dcy-008_20260625_current-holding-wac-cost-basis-read.md) | confirmed |
| dcy-009_20260627_user-read-asset-catalog-and-daemons | 20260627 | 사용자용 read 2종 — GET /v1/assets require_read 완화 + 사용자 가용 데몬 목록 신규(교차 발원 trex-web 포트폴리오 관리 dcy-007) | [Discovery](./dcy-009_20260627_user-read-asset-catalog-and-daemons.md) | confirmed |
| dcy-010_20260627_daemon-scoped-broker-credential-provisioning | 20260627 | daemon-scoped 브로커(KIS) 인증정보 소유·전달 — 다중 인스턴스 trex-core가 daemon별 다른 계좌로 거래 (교차 발원 trex-core 중장기 멀티 인스턴스) | [Discovery](./dcy-010_20260627_daemon-scoped-broker-credential-provisioning.md) | confirmed |
| dcy-011_20260628_daemon-crud-and-self-service-credential-api | 20260628 | 데몬 admin CRUD API + 사용자 self-service 브로커 인증정보 관리 API — 소유 스코프 write/rotate/메타데이터(복호화 secret 미반환), 데몬 생성·할당·(비)활성화는 admin (교차 발원 trex-web dcy-009) | [Discovery](./dcy-011_20260628_daemon-crud-and-self-service-credential-api.md) | confirmed |