From cb8bdd347049791108582bc0ab660c4c9e13b27b Mon Sep 17 00:00:00 2001 From: toki Date: Tue, 2 Jun 2026 15:01:11 +0900 Subject: [PATCH] docs: add KIS Live secret guide and SOPS configuration - Add .sops.yaml for SOPS encryption configuration - Add kis-live-secret-guide.md with setup instructions - Add kis-live-secret-handoff.md for secret handoff procedure - Add secrets/kis.live.sops.yaml with encrypted credentials - Update kis-live-data-collection-pipeline milestone document --- .sops.yaml | 3 + .../kis-live-data-collection-pipeline.md | 2 + docs/kis-live-secret-guide.md | 169 ++++++++++++++++++ docs/kis-live-secret-handoff.md | 66 +++++++ secrets/kis.live.sops.yaml | 19 ++ 5 files changed, 259 insertions(+) create mode 100644 .sops.yaml create mode 100644 docs/kis-live-secret-guide.md create mode 100644 docs/kis-live-secret-handoff.md create mode 100644 secrets/kis.live.sops.yaml diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..a01f04e --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,3 @@ +creation_rules: + - path_regex: ^secrets/kis\.live\.sops\.yaml$ + age: age1fwqdkmqh3ykq7cnchcrr5nfwr77qsdt4p79lpa49g6q5k88cluvqkfw8d3 diff --git a/agent-roadmap/phase/operator-surface/milestones/kis-live-data-collection-pipeline.md b/agent-roadmap/phase/operator-surface/milestones/kis-live-data-collection-pipeline.md index 55ddf19..0443498 100644 --- a/agent-roadmap/phase/operator-surface/milestones/kis-live-data-collection-pipeline.md +++ b/agent-roadmap/phase/operator-surface/milestones/kis-live-data-collection-pipeline.md @@ -72,6 +72,8 @@ fixture/mock 기반 adapter를 실제 KIS provider 호출과 worker import pipel - 표준선(선택): 기존 provider-neutral importer/storage/backtest 입력 경계를 유지하고, KIS 특수성은 provider adapter와 runtime config에 격리한다. - 표준선(선택): KIS 공식 샘플은 tracked source dependency가 아니라 local cache 참조로 사용한다. private rule의 `.agent-cache/koreainvestment/open-trading-api` 경계를 따른다. - 표준선(선택): live smoke는 credential이 준비된 환경에서만 실행하며, credential 미설정 상태는 실패가 아니라 `unavailable` evidence로 남긴다. +- 표준선(선택): 원격 field secret boundary는 Vaultwarden container와 host-only `sops`/`age`로 둔다. code-server 컨테이너에는 age private key나 평문 secret 파일을 두지 않고, host wrapper가 `sops exec-env`로 필요한 환경변수만 주입해 smoke를 실행한다. +- 표준선(선택): Vaultwarden 접속 경로, host 경로, age key 위치 같은 환경 세부는 private testing rule에만 두고 tracked roadmap에는 raw secret, 계좌번호, item/vault 이름을 기록하지 않는다. - 선행 작업: Korea Daily Data Foundation, Backtest Engine Baseline, Operator Client API/Core Integration Validation - 후속 작업: Flutter Operator Console MVP - 확인 필요: diff --git a/docs/kis-live-secret-guide.md b/docs/kis-live-secret-guide.md new file mode 100644 index 0000000..a31026f --- /dev/null +++ b/docs/kis-live-secret-guide.md @@ -0,0 +1,169 @@ +# KIS Live Secret Guide + +이 문서는 KIS live smoke를 준비할 때 사용자가 어떤 값을 어디에 넣고, 에이전트에게 무엇만 알려주면 되는지 정리한다. secret 원문, 계좌번호, 비밀번호, Vaultwarden item 이름, vault 이름은 이 문서와 task/roadmap/log에 기록하지 않는다. + +## 원칙 + +- Vaultwarden은 원격 host의 code-server와 동레벨 container로 둔다. +- `sops`와 `age`는 원격 host에만 설치한다. +- code-server container 안에는 age private key, 평문 secret 파일, KIS credential 원문을 두지 않는다. +- KIS smoke는 원격 host wrapper가 `sops exec-env`로 필요한 환경변수만 주입해서 code-server container 안의 명령을 실행한다. +- 원격 dev/test credential은 조회/smoke 최소 권한을 원칙으로 한다. +- 주문, 잔고, 계좌 비밀번호는 KIS live data collection smoke 범위에 넣지 않는다. + +## 사용자가 준비할 것 + +Vaultwarden에 KIS 관련 원본 secret을 직접 저장한다. 채팅이나 tracked 파일에 원문을 붙여넣지 않는다. + +SOPS 암호화 파일에는 KIS live smoke에 필요한 값만 환경변수 이름으로 저장한다. 국내주식 일봉 조회 smoke의 기본 후보는 아래와 같다. + +```text +KIS_APP_KEY +KIS_APP_SECRET +KIS_BASE_URL +KIS_IS_PAPER +``` + +계좌번호나 계좌 상품코드는 일봉 시세 조회에 필요할 때만 별도 범위로 추가한다. + +```text +KIS_ACCOUNT_NO +KIS_ACCOUNT_PRODUCT +``` + +## Vaultwarden에 원본 저장 + +원격 Vaultwarden은 공개 포트가 아니라 원격 host loopback에만 열린다. 먼저 SSH tunnel을 연다. + +```bash +ssh -L 18088:127.0.0.1:18088 toki@192.168.0.97 +``` + +브라우저에서 아래 주소로 접속한다. + +```text +http://127.0.0.1:18088 +``` + +처음 사용하는 경우: + +1. Vaultwarden 계정을 만든다. +2. KIS 원본 credential을 저장할 항목을 만든다. +3. 항목 이름, vault 이름, 계좌번호, 비밀번호, app secret 원문은 이 repo 문서나 채팅에 쓰지 않는다. +4. 첫 계정을 만든 뒤에는 원격 host의 Vaultwarden compose `.env`에서 `SIGNUPS_ALLOWED=false`로 바꾸고 Vaultwarden을 재시작한다. + +KIS 항목에는 실제 원본 값을 사람이 알아볼 수 있게 저장한다. 예시는 항목 구성 방식만 보여주며 값은 쓰지 않는다. + +```text +KIS app key +KIS app secret +KIS base URL +KIS paper/live 구분 +``` + +일봉 조회 smoke에 계좌 정보가 필요하지 않으면 계좌번호, 계좌 상품코드, 계좌 비밀번호, 주문 비밀번호는 넣지 않거나 별도 항목으로 분리한다. + +## SOPS 파일에 smoke용 env 저장 + +SOPS 파일은 에이전트와 테스트 명령이 읽을 수 있는 암호화된 env 파일이다. Vaultwarden은 원본 금고이고, SOPS 파일은 smoke 실행에 필요한 최소 값만 담는다. + +원격 host에서 ALT repo로 이동한다. + +```bash +cd /Users/toki/docker/services/code-server/data/volume/workspace/alt +``` + +아래 명령으로 암호화된 파일을 연다. + +```bash +SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt" \ +sops secrets/kis.live.sops.yaml +``` + +편집기가 열리면 아래 키들의 오른쪽 값을 채운다. + +```yaml +KIS_APP_KEY: "..." +KIS_APP_SECRET: "..." +KIS_BASE_URL: "..." +KIS_IS_PAPER: "true" +``` + +입력 기준: + +- `KIS_APP_KEY`: KIS에서 발급받은 app key +- `KIS_APP_SECRET`: KIS에서 발급받은 app secret +- `KIS_BASE_URL`: KIS 실전 또는 모의투자 API base URL +- `KIS_IS_PAPER`: 모의투자면 `"true"`, 실전이면 `"false"` + +저장 후 종료하면 SOPS가 파일을 다시 암호화한다. 저장된 파일에서 실제 값이 `ENC[...]` 형태로 보이면 정상이다. +아직 값을 넣지 않은 빈 placeholder는 `""`로 보일 수 있다. 실제 credential을 넣은 뒤에도 값이 평문으로 보이면 저장이 잘못된 것이다. + +```bash +sed -n '1,80p' secrets/kis.live.sops.yaml +``` + +복호화 확인이 필요하면 원격 host에서만 아래 명령을 실행한다. 출력에는 secret 원문이 나오므로 화면 공유, 로그 저장, 채팅 복사를 하지 않는다. + +```bash +SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt" \ +sops --decrypt secrets/kis.live.sops.yaml +``` + +## 에이전트에게 알려줄 것 + +에이전트에게 secret 원문을 알려주지 않는다. 아래 메타 정보만 알려준다. + +```text +1. Vaultwarden에 KIS 항목을 만들었다. +2. SOPS 파일에 다음 env 이름으로 저장했다: + - KIS_APP_KEY + - KIS_APP_SECRET + - KIS_BASE_URL + - KIS_IS_PAPER +3. 첫 smoke 대상: + - market: KR + - symbol: 005930 + - date range: 20240501~20240531 +``` + +## 실행 흐름 + +에이전트가 준비할 수 있는 항목: + +- `.sops.yaml` +- `secrets/kis.live.sops.yaml` 템플릿 +- KIS smoke wrapper script +- code-server container에 env만 주입하는 실행 명령 + +현재 repo에는 `.sops.yaml`과 암호화된 `secrets/kis.live.sops.yaml` 템플릿이 준비되어 있다. 현재 설정 요약과 다음 에이전트 handoff는 `docs/kis-live-secret-handoff.md`를 본다. + +사용자는 원격 host에서 `sops` 편집기로 secret 값을 직접 입력한다. + +```bash +SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt" \ +sops secrets/kis.live.sops.yaml +``` + +KIS live smoke는 host-only age key를 사용해 실행한다. + +```bash +SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt" \ +sops exec-env secrets/kis.live.sops.yaml -- \ +docker exec \ + -e KIS_APP_KEY \ + -e KIS_APP_SECRET \ + -e KIS_BASE_URL \ + -e KIS_IS_PAPER \ + code-server bash -lc 'cd /config/workspace/alt && bin/kis-live-smoke' +``` + +## 완료 조건 + +KIS live smoke가 완료되었다고 보려면 다음 evidence가 필요하다. + +- 실제 KIS 일봉 조회가 credential 준비 환경에서 실행되었다. +- raw secret 없이 sanitized response 또는 요약 evidence가 남았다. +- 실패 시 auth, quota, unavailable, malformed response 중 어떤 상태인지 grep 가능한 출력으로 남았다. +- 수집된 normalized daily bars가 worker import/storage 경계를 통과했다. +- 저장된 bars가 backtest 입력 조회 경계에서 사용 가능함을 확인했다. diff --git a/docs/kis-live-secret-handoff.md b/docs/kis-live-secret-handoff.md new file mode 100644 index 0000000..c8627e5 --- /dev/null +++ b/docs/kis-live-secret-handoff.md @@ -0,0 +1,66 @@ +# KIS Live Secret Handoff + +## Current Setup + +- Remote field host owns the secret boundary. +- Vaultwarden runs outside the code-server container as a sibling container. +- `sops` and `age` are installed on the remote host only. +- The remote host age recipient for KIS live smoke is: + +```text +age1fwqdkmqh3ykq7cnchcrr5nfwr77qsdt4p79lpa49g6q5k88cluvqkfw8d3 +``` + +## Repo Files + +- `.sops.yaml`: SOPS rule for `secrets/kis.live.sops.yaml`. +- `secrets/kis.live.sops.yaml`: encrypted KIS live smoke env template. +- `docs/kis-live-secret-guide.md`: user-facing guide for how to fill and use the secret. + +## User Fill-In + +Do not paste secret values into chat, roadmap, docs, task logs, or command output. + +Open the Vaultwarden SSH tunnel from the local machine: + +```bash +ssh -L 18088:127.0.0.1:18088 toki@192.168.0.97 +``` + +Then open Vaultwarden in the browser: + +```text +http://127.0.0.1:18088 +``` + +On the remote host, edit the encrypted file: + +```bash +cd /Users/toki/docker/services/code-server/data/volume/workspace/alt +SOPS_AGE_KEY_FILE="$HOME/.config/sops/age/keys.txt" sops secrets/kis.live.sops.yaml +``` + +Fill only the env names required for first KIS daily-bar smoke: + +```text +KIS_APP_KEY +KIS_APP_SECRET +KIS_BASE_URL +KIS_IS_PAPER +``` + +Account number, account product, account password, order password, balance, and order scopes are not part of the first daily-bar smoke unless a later task explicitly expands the scope. + +## Agent Handoff + +After the user fills the encrypted file, tell the agent only: + +```text +SOPS file is filled. +First smoke target: +- market: KR +- symbol: 005930 +- date range: ~ +``` + +The agent should then implement or run a host-wrapper flow that decrypts on the remote host and passes env vars into the code-server container only for the command lifetime. diff --git a/secrets/kis.live.sops.yaml b/secrets/kis.live.sops.yaml new file mode 100644 index 0000000..03e22bd --- /dev/null +++ b/secrets/kis.live.sops.yaml @@ -0,0 +1,19 @@ +KIS_APP_KEY: "" +KIS_APP_SECRET: "" +KIS_BASE_URL: "" +KIS_IS_PAPER: ENC[AES256_GCM,data:BqMakQ==,iv:AJEP2eaQeYSQfWP8BhfcDR946tSQHUT6EPWV8EE4Txc=,tag:nsjz0h9Eh2H2/lru3qwmyQ==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSA5cVJCUlNwcHcrN2lOM1g3 + U2NHL3VMdkhnQ1V6Y3NlNFdGOW5HdTlnZFdvCmtyREUySFp0VXFHUk5tQmE0L1Yz + VTBXQUFoWWRmSDQyYlE2QTVtRzk1R3cKLS0tIDNTS3ljYjI0OFZVeWJhb1ZuTUNQ + U2c4Z0tRNHFSVkVja2dMZnBtM3pQeEEKJbdpCZ3XAWQaRqe3GbqsdIXDEjjEuLkT + HQUSiPEpFJcj8nunztDM6YQZS/QDJLNv2nWOxuts5AHoWaQEVAKkCw== + -----END AGE ENCRYPTED FILE----- + recipient: age1fwqdkmqh3ykq7cnchcrr5nfwr77qsdt4p79lpa49g6q5k88cluvqkfw8d3 + lastmodified: "2026-06-02T05:31:11Z" + mac: ENC[AES256_GCM,data:QSdh4ZoAE97c9TzQoe1baZefZ5Rk2vaRw97lKnR2h/iMRjTYbHgkP75Werd8T5oDH20q1o4IkjmOjyEk1XpHDZ9HThKsYmMI3xDFh/64Gz6rW8oCs8dEKW+j7AZ11DleGDkWHu4bIXk5HEeP2GNb7WJufpeIad0Ci3SyBGfJ+aA=,iv:5/BretO0xOrJ4o3jSSRlH0tIccM2wxaYg2TgiXj+gFk=,tag:8hxUFjbL/Q1UN74k1upYgQ==,type:str] + unencrypted_suffix: _unencrypted + version: 3.13.1