# req-113: KISConfig 소스 우선순위 — portfolio-hub fetch 1차 + env break-glass + opt-in - Status: Implemented - Type: Configuration - Priority: High - Owner: trex-core-dev ## Intent - 현재 `DaemonConfig.from_env()`는 KIS 인증정보를 `TREX_KIS_BROKER_*` 환경변수에서만 읽는다. dcy-036 C1/FR-4/NFR-1에 따라 "fetch 1차, env는 명시적 break-glass 오버라이드"로 소스 우선순위를 변경하되, 기존 단기 인스턴스(M1/M2)의 회귀를 막기 위해 fetch는 **opt-in**(명시적 활성화 없으면 기존 env-only 경로 보존)으로 도입한다. - 이 REQ는 설정 계층(TOML/env/fetch 우선순위)과 `DaemonInstanceConfig`/`DaemonConfig` 로더의 변경만 다루며, 실제 fetch I/O는 REQ-112 어댑터에 위임한다. ## Requirement - `DaemonInstanceConfig`(per-instance TOML 로더)에 `credential_fetch_enabled: bool = False` 필드를 추가한다. TOML에 `credential_fetch_enabled = true`가 명시된 경우에만 활성화된다(opt-in, 부재 시 False = env-only 기존 경로 보존). - `credential_fetch_enabled = true`일 때 `DaemonConfig` 구성 단계에서 KISConfig 소스 우선순위는 다음과 같다: 1. **portfolio-hub fetch** (`PortfolioHubReadAdapter.fetch_broker_credential()` 호출) — 성공 시 반환된 `BrokerCredentialPayload`로 `KISConfig` 구성. 2. **env break-glass** (`TREX_KIS_BROKER_BREAK_GLASS=true` 환경변수가 명시적으로 설정된 경우) — portfolio-hub fetch를 시도하지 않고 기존 `TREX_KIS_BROKER_*` env로 KISConfig 구성. 운영자가 의도적으로 break-glass 경로를 활성화한 것으로 간주. - fetch와 break-glass가 모두 부재/비활성이면 env-only 기존 경로(`TREX_KIS_BROKER_*`) 유지. - `credential_fetch_enabled = false`(기본값)일 때: 기존 `TREX_KIS_BROKER_*` env-only 경로를 byte-for-byte 보존한다. portfolio-hub를 호출하지 않는다. - break-glass 활성화 여부를 기동 시 INFO 로그로 출력한다(운영자 인지). 단, app_secret은 마스킹(NFR-2). - KISConfig 구성에 사용된 소스(fetch / break-glass / env-only)를 기동 시 INFO 로그로 기록한다. ## Acceptance Criteria 1. Given TOML에 `credential_fetch_enabled = false`(부재 포함)이고 `TREX_KIS_BROKER_*` env 설정, When 데몬 기동, Then `KISConfig`가 `TREX_KIS_BROKER_*` env-only로 구성되고 portfolio-hub fetch 호출이 발생하지 않는다 (기존 회귀 없음). 2. Given TOML에 `credential_fetch_enabled = true`이고 portfolio-hub fetch가 유효한 `BrokerCredentialPayload`를 반환, When 데몬 기동, Then `KISConfig.app_key`, `KISConfig.app_secret`, `KISConfig.account_ref`가 fetch 결과로 구성된다. 3. Given TOML에 `credential_fetch_enabled = true`이고 `TREX_KIS_BROKER_BREAK_GLASS=true` 환경변수 설정, When 데몬 기동, Then portfolio-hub fetch를 시도하지 않고 `TREX_KIS_BROKER_*` env로 KISConfig가 구성된다 (break-glass 경로). 4. Given break-glass 활성화 상태, When 데몬 기동 로그 확인, Then break-glass 활성화 사실이 INFO로 기록된다. 5. Given 어떤 KISConfig 구성 경로든, When 기동 로그 확인, Then `app_secret` 값이 평문으로 출력되지 않는다 (마스킹). 6. Given `credential_fetch_enabled = true` TOML이고 `TREX_KIS_BROKER_BREAK_GLASS` 미설정, When 기동 로그 확인, Then KISConfig 소스가 "fetch" 임을 INFO로 기록한다. 7. Given `DaemonInstanceConfig.from_toml`에서 `credential_fetch_enabled` 파싱, When TOML에 해당 키 부재, Then `False`로 파싱된다 (opt-in 기본값). ## Impacted Area - Docs: 없음 (baseline 문서 갱신은 별도 scope) - Modules: - `trex_core/runtime/config.py` — `DaemonInstanceConfig`에 `credential_fetch_enabled: bool = False` 필드 추가; `from_toml()` 로더에서 파싱; `from_config()` 또는 `DaemonConfig` 조립 단계에서 fetch/break-glass/env-only 우선순위 로직 추가; `TREX_KIS_BROKER_BREAK_GLASS` 환경변수 인식 - `trex_core/runtime/daemon.py` (또는 조립 지점) — 기동 시 소스 INFO 로그 출력 - Tests: - `tests/runtime/test_config.py` — AC-1~7 케이스 추가: fetch opt-in off/on, break-glass, 마스킹, TOML 파싱 기본값 ## Notes - `TREX_KIS_BROKER_BASE_URL`은 fetch 경로에서도 여전히 env로 제공한다. portfolio-hub `/v1/user_info`는 `app_key`/`app_secret`/`account_ref`만 전달하며 `base_url`과 기타 KIS 설정(timeout, allow_live 등)은 env에서 읽는다. - `TREX_KIS_BROKER_BREAK_GLASS` 환경변수명은 의도적으로 명시적이다 — 운영자가 비상 경로임을 인지하고 설정해야 한다. - portfolio-hub fetch 실패(네트워크 오류 등) 시 처리는 REQ-114(그날-시작 로드 게이트)가 담당한다. 이 REQ는 설정 우선순위 계층만 다룬다. - OQ-4(알람 trex-notifier 연동): Deferred — REQ-114 Notes 참조. - Source: dcy-036 C1, FR-4, NFR-1, OQ-3 Answered (fetch 1차, env 명시적 break-glass). ## Change Log ### CHG-20260628-01 - Date: 2026-06-28 - Author: trex-core-dev #### Change Summary - 신규 생성 (Proposed) — dcy-036 C1/FR-4/NFR-1/OQ-3 산출물. #### Intent - dcy-036 FR-4(KISConfig 소스 우선순위 확장: fetch 1차 + env break-glass + opt-in)를 Configuration REQ로 정규화한다. REQ-112 어댑터의 소비자 배선 계층. #### Acceptance Criteria Delta - Added: - AC-1~7 전체 (신규 초안) - Updated: 없음 - Removed: 없음 #### Impacted Area Delta - Docs: 없음 - Modules: - `config.py` `DaemonInstanceConfig` 확장 (`credential_fetch_enabled`), `from_toml` 파싱, 조립 우선순위 로직 - `daemon.py` 기동 로그 - Tests: - `tests/runtime/test_config.py` 케이스 추가 #### Delivery Trace Delta - Batches: 미정 (suggest-batch-reqs 대기) - Releases: 미정 #### Revalidation Impact - Reverification Needed: no - Existing Implementation Invalidated: no - Status Recommendation: keep-current - Follow-up Action: confirm-req 후 REQ-112~115 일괄 batch 배정 검토. #### Validation Plan - pytest로 AC-1~7 각 케이스 확인. AC-1(opt-in off → env-only, fetch 미호출)가 회귀 방지의 핵심 케이스. ### CHG-20260628-02 - Date: 2026-06-28 - Author: 팀장(Claude) - confirm-req: `Proposed` → `Approved`. dcy-036 FR-4/OQ-3 추적. credential_fetch_enabled opt-in 기본 false로 M1/M2 byte-for-byte 보존(AC-1·NFR-1) 적절. fetch 1차→env break-glass(명시적 TREX_KIS_BROKER_BREAK_GLASS) 우선순위 정합. base_url 등은 env 유지 합리. blocker 없음. ### CHG-20260628-03 - Date: 2026-06-28 - Author: 팀장(Claude) - 부분 구현 — BAT-053 PR #34 머지(merge commit 01655ab). credential_fetch_enabled TOML 플래그 신규 필드, opt-in off 회귀보존(AC-1), break-glass 코드, TOML 기본 False(AC-7) 구현·머지 완료. **연기: AC-2(fetch 결과로 KISConfig 조립)는 KISBroker 런타임 재조립 필요 → M3 후속 batch.** AC-4 break-glass 로그 내용 assert 보강도 후속. Status `Approved` 유지(미충족 AC 존재). ### CHG-20260628-04 - Date: 2026-06-28 - Author: 팀장(Claude) — confirm-req-implemented: `Approved` → `Implemented` - 근거: BAT-054(dcy-036 KISBroker 양의 경로 주입), 커밋 11decaa. QC(opus) **PASS-WITH-NOTES, Blocker 0** — 독립 테스트 재실행 757 passed/회귀 0. **연기됐던 AC-2(fetch 결과로 KISConfig 조립 = KISBroker `reinitialize()` 런타임 재주입)** 및 **AC-4(break-glass 로그 'break-glass' 키워드 assert 보강)** 충족·검증. opt-in off 기본 경로 byte-for-byte 회귀보존(AC-1) 유지 확인. fail-closed 불변식 미파괴(reinit 실패가 entry_allowed 불변), secret 평문 로그 미노출, domain 순수·AMQP 무변경 QC 확인. - 후속(비차단): QC Minor #3(design D1의 L2 token_cache.invalidate 호출이 구현에선 키-miss 전략으로 대체 — 설계-구현 문서 정합) 정정. AC-4 로그 assert 강도는 키워드 수준(수용). - 게이트: 머지(PR)는 사장님 결재 게이트(실거래 주입=외부 비가역).