mirror of
https://github.com/NomaDamas/k-skill.git
synced 2026-06-24 02:04:11 +00:00
Replace sops+age encryption with plain dotenv and agent-native credential resolution
Agent environments (OpenClaw, Claude Code, Codex) assume users delegate credentials to the agent. sops+age added setup friction without real security benefit since the agent decrypts on every call anyway. New model: skills declare required env var names; how they are supplied is up to the agent (own vault, shell env, or ~/.config/k-skill/secrets.env as the default fallback with 0600 permissions). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
249b56e2b1
commit
ef2c69b81c
20 changed files with 203 additions and 486 deletions
|
|
@ -38,8 +38,8 @@ Claude code, codex, opencode 등 각종 코딩 에이전트 지원합니다.
|
|||
## 처음 시작하는 순서
|
||||
|
||||
1. [설치 방법](docs/install.md)을 따라 `k-skill` 전체 스킬을 먼저 설치합니다.
|
||||
2. 설치가 끝나면 `k-skill-setup` 스킬을 사용해 `sops + age`, 공통 secrets 파일, 런타임 주입 확인까지 진행합니다.
|
||||
3. 시크릿이 비어 있으면 값을 채팅에 붙여 넣지 말고, [공통 설정 가이드](docs/setup.md)와 [보안/시크릿 정책](docs/security-and-secrets.md)에 따라 로컬에 안전하게 등록합니다.
|
||||
2. 설치가 끝나면 `k-skill-setup` 스킬을 사용해 credential 확보와 환경변수 확인을 진행합니다.
|
||||
3. 시크릿이 비어 있으면 [공통 설정 가이드](docs/setup.md)와 [보안/시크릿 정책](docs/security-and-secrets.md)에 따라 credential resolution order로 확보합니다.
|
||||
4. Node/Python 패키지가 없으면 먼저 전역 설치를 기본으로 진행합니다.
|
||||
5. 각 기능 문서를 열어 입력값, 예시, 제한사항을 확인합니다.
|
||||
|
||||
|
|
@ -48,7 +48,7 @@ Claude code, codex, opencode 등 각종 코딩 에이전트 지원합니다.
|
|||
| 문서 | 설명 |
|
||||
| --- | --- |
|
||||
| [설치 방법](docs/install.md) | 패키지 설치, 선택 설치, 로컬 테스트 방법 |
|
||||
| [공통 설정 가이드](docs/setup.md) | `sops + age` 설치, age key 생성, 공통 secrets 파일 준비 |
|
||||
| [공통 설정 가이드](docs/setup.md) | credential resolution order, 기본 secrets 파일 준비 |
|
||||
| [보안/시크릿 정책](docs/security-and-secrets.md) | 인증 정보 저장 원칙, 금지 패턴, 표준 환경변수 이름 |
|
||||
| [k-skill 프록시 서버 가이드](docs/features/k-skill-proxy.md) | 무료 API를 프록시 서버로 바로 호출하는 방법 |
|
||||
| [릴리스/배포 가이드](docs/releasing.md) | npm Changesets, Python release-please, trusted publishing 운영 규칙 |
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@
|
|||
- [보안/시크릿 정책](../security-and-secrets.md) 확인
|
||||
- `k-skill-proxy` 또는 에어코리아 OpenAPI key
|
||||
|
||||
## 필요한 시크릿
|
||||
## 필요한 환경변수
|
||||
|
||||
클라이언트 기본값:
|
||||
|
||||
|
|
@ -24,6 +24,13 @@
|
|||
|
||||
- `AIR_KOREA_OPEN_API_KEY`
|
||||
|
||||
### Credential resolution order
|
||||
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
## 입력값
|
||||
|
||||
- 기본: 지역명/행정구역 힌트(`regionHint`)
|
||||
|
|
@ -41,9 +48,7 @@
|
|||
프록시 예시:
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/fine_dust.py report --region-hint "서울 강남구" --json'
|
||||
python3 scripts/fine_dust.py report --region-hint "서울 강남구" --json
|
||||
```
|
||||
|
||||
후보 반환 예시:
|
||||
|
|
@ -77,29 +82,25 @@ curl -fsS --get 'https://k-skill-proxy.nomadamas.org/B552584/ArpltnInforInqireSv
|
|||
지역 기반 direct fallback:
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'curl -sG "http://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList" \
|
||||
--data-urlencode "serviceKey=${AIR_KOREA_OPEN_API_KEY}" \
|
||||
--data-urlencode "returnType=json" \
|
||||
--data-urlencode "numOfRows=50" \
|
||||
--data-urlencode "pageNo=1" \
|
||||
--data-urlencode "addr=서울 강남구"'
|
||||
curl -sG "http://apis.data.go.kr/B552584/MsrstnInfoInqireSvc/getMsrstnList" \
|
||||
--data-urlencode "serviceKey=${AIR_KOREA_OPEN_API_KEY}" \
|
||||
--data-urlencode "returnType=json" \
|
||||
--data-urlencode "numOfRows=50" \
|
||||
--data-urlencode "pageNo=1" \
|
||||
--data-urlencode "addr=서울 강남구"
|
||||
```
|
||||
|
||||
실시간 측정값:
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'curl -sG "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty" \
|
||||
--data-urlencode "serviceKey=${AIR_KOREA_OPEN_API_KEY}" \
|
||||
--data-urlencode "returnType=json" \
|
||||
--data-urlencode "numOfRows=100" \
|
||||
--data-urlencode "pageNo=1" \
|
||||
--data-urlencode "stationName=중구" \
|
||||
--data-urlencode "dataTerm=DAILY" \
|
||||
--data-urlencode "ver=1.4"'
|
||||
curl -sG "http://apis.data.go.kr/B552584/ArpltnInforInqireSvc/getMsrstnAcctoRltmMesureDnsty" \
|
||||
--data-urlencode "serviceKey=${AIR_KOREA_OPEN_API_KEY}" \
|
||||
--data-urlencode "returnType=json" \
|
||||
--data-urlencode "numOfRows=100" \
|
||||
--data-urlencode "pageNo=1" \
|
||||
--data-urlencode "stationName=중구" \
|
||||
--data-urlencode "dataTerm=DAILY" \
|
||||
--data-urlencode "ver=1.4"
|
||||
```
|
||||
|
||||
helper script 반복 검증:
|
||||
|
|
|
|||
|
|
@ -15,11 +15,18 @@
|
|||
- [공통 설정 가이드](../setup.md) 완료
|
||||
- [보안/시크릿 정책](../security-and-secrets.md) 확인
|
||||
|
||||
## 필요한 시크릿
|
||||
## 필요한 환경변수
|
||||
|
||||
- `KSKILL_KTX_ID`
|
||||
- `KSKILL_KTX_PASSWORD`
|
||||
|
||||
### Credential resolution order
|
||||
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
## 입력값
|
||||
|
||||
- 출발역
|
||||
|
|
@ -44,7 +51,7 @@
|
|||
## 기본 흐름
|
||||
|
||||
1. `korail2` 또는 `pycryptodome` 패키지가 없으면 다른 방법으로 우회하지 말고 먼저 전역 설치한다.
|
||||
2. `KSKILL_KTX_ID`, `KSKILL_KTX_PASSWORD` 가 없으면 채팅에 붙여 넣게 하지 말고 로컬 secrets 등록 절차를 안내한다.
|
||||
2. `KSKILL_KTX_ID`, `KSKILL_KTX_PASSWORD` 가 없으면 credential resolution order에 따라 확보한다.
|
||||
3. helper 로 먼저 열차를 조회한다.
|
||||
4. 후보 열차의 `index`, `train_id`, 출발/도착 시각, KTX 여부, 좌석 여부를 보여준다.
|
||||
5. 대상 열차가 명확할 때만 예약한다.
|
||||
|
|
@ -55,9 +62,7 @@
|
|||
조회:
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py search 서울 부산 20260328 090000 --limit 5'
|
||||
python3 scripts/ktx_booking.py search 서울 부산 20260328 090000 --limit 5
|
||||
```
|
||||
|
||||
좌석이 없는 열차까지 같이 보고 싶으면 `--include-no-seats`, 예약 대기 가능 열차도 같이 보고 싶으면 `--include-waiting-list` 를 붙인다.
|
||||
|
|
@ -67,9 +72,7 @@ sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
|||
예약:
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py reserve 서울 부산 20260328 090000 --train-id <train_id> --seat-option general-first'
|
||||
python3 scripts/ktx_booking.py reserve 서울 부산 20260328 090000 --train-id <train_id> --seat-option general-first
|
||||
```
|
||||
|
||||
좌석이 없을 때 예약 대기까지 시도하려면 조회 단계에서도 `--include-waiting-list` 를 켜고, 예약 단계에서 `--try-waiting` 을 추가한다.
|
||||
|
|
@ -77,17 +80,13 @@ sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
|||
예약 확인:
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py reservations'
|
||||
python3 scripts/ktx_booking.py reservations
|
||||
```
|
||||
|
||||
취소:
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py cancel <reservation_id>'
|
||||
python3 scripts/ktx_booking.py cancel <reservation_id>
|
||||
```
|
||||
|
||||
응답은 JSON 으로 나오며 예약번호, 구입기한, 운임 확인에 바로 쓸 수 있다. **결제는 제외** 하고 예약까지만 자동화한다.
|
||||
|
|
@ -95,6 +94,6 @@ sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
|||
## 주의할 점
|
||||
|
||||
- SRT 예매와는 별도 표면이므로 혼용하지 않는다.
|
||||
- 평문 비밀번호 전달은 금지한다.
|
||||
- credential은 환경변수로 주입한다.
|
||||
- 결제 완료까지 자동화하는 범위는 아니다.
|
||||
- Korail anti-bot 규칙이 다시 바뀌면 helper 도 함께 점검해야 한다.
|
||||
|
|
|
|||
|
|
@ -12,10 +12,17 @@
|
|||
- [보안/시크릿 정책](../security-and-secrets.md) 확인
|
||||
- 서울 열린데이터 광장 API key
|
||||
|
||||
## 필요한 시크릿
|
||||
## 필요한 환경변수
|
||||
|
||||
- `SEOUL_OPEN_API_KEY`
|
||||
|
||||
### Credential resolution order
|
||||
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
## 입력값
|
||||
|
||||
- 역명
|
||||
|
|
@ -23,17 +30,14 @@
|
|||
|
||||
## 기본 흐름
|
||||
|
||||
1. `SEOUL_OPEN_API_KEY` 가 없으면 채팅에 붙여 넣게 하지 말고 로컬 secrets 등록 절차를 안내합니다.
|
||||
2. API key가 안전하게 주입되는지 확인합니다.
|
||||
1. `SEOUL_OPEN_API_KEY` 가 없으면 credential resolution order에 따라 확보합니다.
|
||||
3. 역명 기준으로 실시간 도착정보를 조회합니다.
|
||||
4. 호선, 진행 방향, 도착 메시지, 조회 시점을 함께 요약합니다.
|
||||
|
||||
## 예시
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'curl -s "http://swopenAPI.seoul.go.kr/api/subway/${SEOUL_OPEN_API_KEY}/json/realtimeStationArrival/0/8/강남"'
|
||||
curl -s "http://swopenAPI.seoul.go.kr/api/subway/${SEOUL_OPEN_API_KEY}/json/realtimeStationArrival/0/8/강남"
|
||||
```
|
||||
|
||||
## 주의할 점
|
||||
|
|
|
|||
|
|
@ -15,11 +15,18 @@
|
|||
- [공통 설정 가이드](../setup.md) 완료
|
||||
- [보안/시크릿 정책](../security-and-secrets.md) 확인
|
||||
|
||||
## 필요한 시크릿
|
||||
## 필요한 환경변수
|
||||
|
||||
- `KSKILL_SRT_ID`
|
||||
- `KSKILL_SRT_PASSWORD`
|
||||
|
||||
### Credential resolution order
|
||||
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
## 입력값
|
||||
|
||||
- 출발역
|
||||
|
|
@ -32,7 +39,7 @@
|
|||
## 기본 흐름
|
||||
|
||||
1. `SRTrain` 패키지가 없으면 다른 방법으로 우회하지 말고 먼저 전역 설치합니다.
|
||||
2. `KSKILL_SRT_ID`, `KSKILL_SRT_PASSWORD` 가 없으면 채팅에 붙여 넣게 하지 말고 로컬 secrets 등록 절차를 안내합니다.
|
||||
2. `KSKILL_SRT_ID`, `KSKILL_SRT_PASSWORD` 가 없으면 credential resolution order에 따라 확보합니다.
|
||||
3. 먼저 열차를 조회합니다.
|
||||
4. 후보 열차의 출발/도착 시각, 좌석 여부, 운임을 보여줍니다.
|
||||
5. 대상 열차가 명확할 때만 예약합니다.
|
||||
|
|
@ -41,8 +48,7 @@
|
|||
## 예시
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" 'python3 - <<'"'"'PY'"'"'
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from SRT import SRT
|
||||
|
||||
|
|
@ -52,11 +58,10 @@ trains = srt.search_train("수서", "부산", "20260328", "080000", time_limit="
|
|||
for idx, train in enumerate(trains[:5], start=1):
|
||||
print(idx, train)
|
||||
PY
|
||||
'
|
||||
```
|
||||
|
||||
## 주의할 점
|
||||
|
||||
- 평문 비밀번호를 채팅이나 `.env` 평문 파일에 두지 않습니다.
|
||||
- credential은 환경변수로 주입합니다.
|
||||
- 결제 완료까지 자동화하는 문서는 아닙니다.
|
||||
- 매진 시 공격적인 재시도 루프는 피합니다.
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
Codex나 Claude Code에 아래 문장을 그대로 붙여 넣으면 된다.
|
||||
|
||||
```text
|
||||
이 레포의 설치 문서를 읽고 k-skill 전체 스킬을 먼저 설치해줘. 설치가 끝나면 k-skill-setup 스킬을 사용해서 sops + age, 공통 secrets 파일, 런타임 주입 확인까지 이어서 진행해줘. 끝나면 설치된 스킬과 다음 단계만 짧게 정리해.
|
||||
이 레포의 설치 문서를 읽고 k-skill 전체 스킬을 먼저 설치해줘. 설치가 끝나면 k-skill-setup 스킬을 사용해서 credential 확보와 환경변수 확인까지 이어서 진행해줘. 끝나면 설치된 스킬과 다음 단계만 짧게 정리해.
|
||||
```
|
||||
|
||||
## 직접 설치
|
||||
|
|
|
|||
|
|
@ -1,38 +1,23 @@
|
|||
# Security And Secrets
|
||||
|
||||
`k-skill`은 인증이 필요한 스킬에서 비밀번호나 토큰을 채팅창에 직접 붙여 넣는 방식을 허용하지 않는다. 기본 원칙은 "비밀값은 암호화된 파일로 보관하고, 런타임에만 주입"이다.
|
||||
`k-skill`은 **필요한 환경변수 이름만 선언**하고, 그 값을 어떻게 공급하느냐는 에이전트의 자유에 맡긴다.
|
||||
|
||||
## Missing secret handling policy
|
||||
## Credential resolution order
|
||||
|
||||
인증이 필요한 스킬에서 필요한 값이 없으면 우회하지 않는다.
|
||||
모든 credential-bearing 스킬은 아래 우선순위를 따른다.
|
||||
|
||||
- 어떤 값이 비어 있는지 정확한 환경변수 이름으로 사용자에게 알려준다
|
||||
- 그 값을 채팅창에 붙여 넣으라고 하지 않는다
|
||||
- 대체 사이트, 대체 API, 하드코딩, 임시 평문 `.env` 파일 같은 우회 경로를 찾지 않는다
|
||||
- 사용자가 직접 로컬에 안전하게 등록하도록 안내한 뒤 다시 진행한다
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
안내 기본형:
|
||||
기본 경로에 저장하는 것은 fallback일 뿐, 강제가 아니다.
|
||||
|
||||
1. 필요한 값 이름을 짚는다. 예: `KSKILL_SRT_ID`, `KSKILL_SRT_PASSWORD`
|
||||
2. `~/.config/k-skill/secrets.env.plain` 에 값을 적고
|
||||
3. `sops`로 `~/.config/k-skill/secrets.env` 로 암호화한 뒤
|
||||
4. plaintext 파일을 지우고
|
||||
5. `bash scripts/check-setup.sh` 로 다시 확인하게 한다
|
||||
## Default secrets file
|
||||
|
||||
즉, "시크릿이 없으면 사용자에게 필요한 정보를 요청하고, 안전한 등록 절차를 안내한 뒤 멈춘다"가 기본 동작이다.
|
||||
|
||||
## Required
|
||||
|
||||
- `sops`
|
||||
- `age`
|
||||
- local age private key
|
||||
- encrypted dotenv file for `k-skill`
|
||||
|
||||
## Allowed patterns
|
||||
|
||||
### 1. `sops exec-env` with an encrypted dotenv file
|
||||
|
||||
평문 예시는 한 번만 작성하고, 바로 암호화해서 지운다.
|
||||
- 경로: `~/.config/k-skill/secrets.env`
|
||||
- 형식: plain dotenv (`KEY=value`, 한 줄에 하나)
|
||||
- 퍼미션: `0600` (owner-only read/write)
|
||||
|
||||
```dotenv
|
||||
KSKILL_SRT_ID=replace-me
|
||||
|
|
@ -44,52 +29,31 @@ AIR_KOREA_OPEN_API_KEY=replace-me
|
|||
KSKILL_PROXY_BASE_URL=https://k-skill-proxy.nomadamas.org
|
||||
```
|
||||
|
||||
실행은 항상 다음 패턴으로 한다.
|
||||
## Missing secret handling policy
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" '<command>'
|
||||
```
|
||||
인증이 필요한 스킬에서 필요한 값이 없으면 우회하지 않는다.
|
||||
|
||||
### 2. `sops` encrypted file with repo-external storage
|
||||
|
||||
권장 기본 위치:
|
||||
|
||||
- encrypted secrets file: `~/.config/k-skill/secrets.env`
|
||||
- age private key: `~/.config/k-skill/age/keys.txt`
|
||||
|
||||
이렇게 하면 저장소 안에 평문 비밀 파일을 둘 필요가 없다.
|
||||
|
||||
### 3. Wrapper commands that consume secrets internally
|
||||
|
||||
가장 강한 모델은 에이전트에게 secret 값을 직접 주지 않고, 내부에서만 secret을 소비하는 래퍼 명령을 두는 것이다.
|
||||
|
||||
예:
|
||||
|
||||
- `kskill-run srt-search ...`
|
||||
- `kskill-run ktx-book ...`
|
||||
|
||||
이 경우 비밀값은 helper process 내부에서만 쓰인다.
|
||||
- 어떤 값이 비어 있는지 정확한 환경변수 이름으로 사용자에게 알려준다
|
||||
- credential resolution order에 따라 확보한다
|
||||
- 대체 사이트, 대체 API, 하드코딩 같은 우회 경로를 찾지 않는다
|
||||
- 시크릿이 없다는 이유로 다른 서비스나 비공식 우회 수단을 자동 채택하지 않는다
|
||||
|
||||
## Forbidden patterns
|
||||
|
||||
- 채팅 메시지에 비밀번호/토큰을 직접 붙여 넣기
|
||||
- 실제 비밀값이 들어있는 plaintext `.env` 파일을 git에 두기
|
||||
- 셸 히스토리에 남는 `export PASSWORD=...`
|
||||
- 실제 비밀값이 들어있는 파일을 git에 두기
|
||||
- 스킬 문서 안에 예시용 실비밀번호를 쓰기
|
||||
- 시크릿이 없다는 이유로 다른 서비스나 비공식 우회 수단을 자동 채택하기
|
||||
|
||||
## Threat model notes
|
||||
## Threat model
|
||||
|
||||
- `sops + age`는 저장 시점과 git 저장소에서의 노출을 줄여준다
|
||||
- 하지만 `sops exec-env`로 실행된 프로세스는 복호화된 env var를 사용할 수 있다
|
||||
- 즉 "에이전트가 쓸 수는 있지만 절대로 읽을 수는 없는" 구조는 아니다
|
||||
- 그 수준이 필요하면 secret을 직접 주입하지 말고 capability wrapper를 둬야 한다
|
||||
- `~/.config/k-skill/secrets.env`는 plain dotenv 파일이다
|
||||
- 파일 퍼미션 `0600`으로 같은 머신의 다른 유저로부터 보호한다
|
||||
- `.gitignore`로 git 노출을 방지한다
|
||||
- 에이전트는 이 파일을 읽고 쓸 수 있다 — 이것은 의도된 동작이다
|
||||
- OpenClaw/에이전트 환경에서 유저는 에이전트에게 credential을 위임하는 것을 전제로 사용한다
|
||||
|
||||
## Standard variable names
|
||||
|
||||
실제 환경변수 이름은 현재 다음을 사용한다.
|
||||
|
||||
- `KSKILL_SRT_ID`
|
||||
- `KSKILL_SRT_PASSWORD`
|
||||
- `KSKILL_KTX_ID`
|
||||
|
|
@ -98,11 +62,4 @@ sops exec-env "$HOME/.config/k-skill/secrets.env" '<command>'
|
|||
- `AIR_KOREA_OPEN_API_KEY`
|
||||
- `KSKILL_PROXY_BASE_URL`
|
||||
|
||||
## Why sops plus age
|
||||
|
||||
- 가입과 클라우드 로그인이 필요 없다
|
||||
- macOS, Linux, Windows 모두 가능하다
|
||||
- dotenv 파일을 그대로 암호화할 수 있다
|
||||
- `sops exec-env`로 런타임 주입 패턴이 단순하다
|
||||
|
||||
이 레포의 credential-bearing skill은 전부 이 정책을 전제로 작성한다. 자세한 공통 설치 절차는 [공통 설정 가이드](setup.md)를 본다.
|
||||
|
|
|
|||
121
docs/setup.md
121
docs/setup.md
|
|
@ -1,73 +1,25 @@
|
|||
# 공통 설정 가이드
|
||||
|
||||
`k-skill` 전체 스킬을 설치한 뒤, `k-skill-setup` 스킬이 실제로 수행해야 하는 공통 설정 절차를 이 문서에 정리한다.
|
||||
`k-skill` 전체 스킬을 설치한 뒤, 인증 정보가 필요한 기능(SRT 예매, KTX 예매, 서울 지하철 도착정보 조회, 미세먼지 조회)을 사용하려면 이 절차를 진행하면 된다.
|
||||
|
||||
SRT 예매, KTX 예매, 서울 지하철 도착정보 조회, 사용자 위치 미세먼지 조회처럼 인증 정보가 필요한 기능은 설치 직후 이 절차를 진행하면 된다.
|
||||
## Credential resolution order
|
||||
|
||||
## 이 설정으로 해결하는 것
|
||||
모든 credential-bearing 스킬은 아래 우선순위를 따른다.
|
||||
|
||||
- `sops + age` 설치
|
||||
- age key 생성
|
||||
- 공통 secrets 파일 생성
|
||||
- 암호화 확인
|
||||
- 런타임 주입 확인
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
## 기본 경로
|
||||
에이전트가 자체 vault를 사용 중이라면 기본 경로 설정을 건너뛰어도 된다.
|
||||
|
||||
- age key: `~/.config/k-skill/age/keys.txt`
|
||||
- encrypted secrets file: `~/.config/k-skill/secrets.env`
|
||||
## 기본 경로로 설정하기
|
||||
|
||||
## 1) 필요한 도구 설치
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
brew install sops age
|
||||
```
|
||||
|
||||
### Ubuntu / Debian
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y sops age
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
```bash
|
||||
sudo pacman -S sops age
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
winget install Mozilla.SOPS FiloSottile.age
|
||||
```
|
||||
|
||||
도구가 없으면 다른 비밀 관리 방식으로 우회하지 말고, 이 도구들을 먼저 설치하는 것을 기본으로 합니다.
|
||||
|
||||
## 2) age key 만들기
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/k-skill/age
|
||||
age-keygen -o ~/.config/k-skill/age/keys.txt
|
||||
```
|
||||
|
||||
출력된 public key를 복사해 둡니다.
|
||||
|
||||
## 3) `.sops.yaml` 만들기
|
||||
|
||||
```yaml
|
||||
creation_rules:
|
||||
- path_regex: .*secrets\.env(\.plain)?$
|
||||
age: age1replace-with-your-public-key
|
||||
```
|
||||
|
||||
## 4) 공통 secrets 파일 만들기
|
||||
에이전트가 별도 vault를 쓰지 않는 경우, 기본 fallback 파일을 만든다.
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/k-skill
|
||||
cat > ~/.config/k-skill/secrets.env.plain <<'EOF'
|
||||
cat > ~/.config/k-skill/secrets.env <<'EOF'
|
||||
KSKILL_SRT_ID=replace-me
|
||||
KSKILL_SRT_PASSWORD=replace-me
|
||||
KSKILL_KTX_ID=replace-me
|
||||
|
|
@ -76,56 +28,23 @@ SEOUL_OPEN_API_KEY=replace-me
|
|||
AIR_KOREA_OPEN_API_KEY=replace-me
|
||||
KSKILL_PROXY_BASE_URL=https://k-skill-proxy.nomadamas.org
|
||||
EOF
|
||||
chmod 0600 ~/.config/k-skill/secrets.env
|
||||
```
|
||||
|
||||
실제 값을 채운 뒤 바로 암호화합니다.
|
||||
실제 값을 채운다.
|
||||
|
||||
```bash
|
||||
cd ~/.config/k-skill
|
||||
sops --encrypt --input-type dotenv --output-type dotenv \
|
||||
secrets.env.plain > secrets.env
|
||||
rm secrets.env.plain
|
||||
```
|
||||
|
||||
## 시크릿이 없을 때의 기본 응답
|
||||
|
||||
인증이 필요한 스킬에서 값이 비어 있으면 다음 식으로 안내하는 것을 기본으로 합니다.
|
||||
|
||||
- 어떤 값이 필요한지 정확한 변수 이름으로 알려주기
|
||||
- 그 값을 채팅에 보내지 말라고 안내하기
|
||||
- 아래 절차로 로컬에 직접 등록하게 하기
|
||||
|
||||
예:
|
||||
|
||||
```text
|
||||
이 작업에는 KSKILL_SRT_ID, KSKILL_SRT_PASSWORD 가 필요합니다.
|
||||
값을 채팅창에 붙여 넣지 말고, ~/.config/k-skill/secrets.env.plain 에 채운 뒤
|
||||
sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
||||
끝나면 plaintext 파일은 지우고 bash scripts/check-setup.sh 로 다시 확인해 주세요.
|
||||
```
|
||||
|
||||
## 5) 런타임 주입 확인
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'test -n "$KSKILL_SRT_ID" || test -n "$KSKILL_KTX_ID" || test -n "$SEOUL_OPEN_API_KEY" || test -n "$AIR_KOREA_OPEN_API_KEY"'
|
||||
```
|
||||
|
||||
또는:
|
||||
## 확인
|
||||
|
||||
```bash
|
||||
bash scripts/check-setup.sh
|
||||
```
|
||||
|
||||
## 6) 실행 래퍼 두기
|
||||
## 시크릿이 없을 때의 기본 응답
|
||||
|
||||
```bash
|
||||
kskill-run() {
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" "$@"
|
||||
}
|
||||
```
|
||||
인증이 필요한 스킬에서 값이 비어 있으면 credential resolution order에 따라 확보한다.
|
||||
|
||||
- 어떤 값이 필요한지 정확한 변수 이름으로 알려주기
|
||||
- resolution order에 따라 유저에게 확보 방법 안내하기
|
||||
|
||||
## 기능별로 필요한 값
|
||||
|
||||
|
|
@ -144,4 +63,4 @@ kskill-run() {
|
|||
- [사용자 위치 미세먼지 조회 가이드](features/fine-dust-location.md)
|
||||
- [보안/시크릿 정책](security-and-secrets.md)
|
||||
|
||||
설치 기본 흐름은 "전체 스킬 설치 → `k-skill-setup` 실행 → 개별 기능 사용" 입니다.
|
||||
설치 기본 흐름은 "전체 스킬 설치 → 개별 기능 사용" 이다.
|
||||
|
|
|
|||
|
|
@ -37,5 +37,3 @@
|
|||
- CJ대한통운 배송상세 JSON: https://www.cjlogistics.com/ko/tool/parcel/tracking-detail
|
||||
- 우체국 배송조회: https://service.epost.go.kr/trace.RetrieveRegiPrclDeliv.postal?sid1=
|
||||
- 우체국 배송상세 HTML: https://service.epost.go.kr/trace.RetrieveDomRigiTraceList.comm
|
||||
- SOPS docs: https://getsops.io/docs/
|
||||
- age: https://github.com/FiloSottile/age
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
creation_rules:
|
||||
- path_regex: .*secrets\.env(\.plain)?$
|
||||
age: age1replace-with-your-public-key
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
name: k-skill-setup
|
||||
description: After installing the full k-skill bundle, configure and verify the shared cross-platform setup with sops plus age, then optionally wire update checks and GitHub starring with explicit user consent.
|
||||
description: After installing the full k-skill bundle, configure and verify the shared cross-platform setup, then optionally wire update checks and GitHub starring with explicit user consent.
|
||||
license: MIT
|
||||
metadata:
|
||||
category: setup
|
||||
|
|
@ -14,47 +14,33 @@ metadata:
|
|||
|
||||
전체 `k-skill` 설치가 끝난 뒤, 공통 후속 작업을 처리한다.
|
||||
|
||||
- `sops + age` 설치
|
||||
- age key 생성
|
||||
- 공통 secrets 파일 생성
|
||||
- 암호화 확인
|
||||
- 런타임 주입 확인
|
||||
- credential 확보 (에이전트 vault 또는 기본 secrets.env)
|
||||
- 런타임 환경변수 확인
|
||||
- 선택 사항: 주기적인 업데이트 확인 자동화
|
||||
- 선택 사항: GitHub star 여부 확인 및 동의 시 실행
|
||||
|
||||
이 스킬의 기본 정책:
|
||||
|
||||
- 시크릿이 없으면 필요한 값 이름을 사용자에게 정확히 알려준다
|
||||
- 값을 채팅창에 붙여 넣으라고 하지 않는다
|
||||
- 로컬에 안전하게 등록하는 절차를 안내한 뒤 다시 진행한다
|
||||
- credential resolution order에 따라 확보한다
|
||||
- 필요한 패키지가 없으면 대체 구현을 찾기보다 전역 설치를 먼저 시도한다
|
||||
- `cron`, `launchd`, `schtasks`, `gh` 같은 지속성/외부 상태 변경은 자동으로 하지 말고 먼저 사용자 동의를 받는다
|
||||
- GitHub star는 사용자가 명시적으로 동의했을 때만 실행한다
|
||||
|
||||
## Why this is the default setup path
|
||||
## Credential resolution order
|
||||
|
||||
- 계정 가입이 필요 없다
|
||||
- macOS, Linux, Windows 모두 가능하다
|
||||
- 스킬은 비밀값 위치를 몰라도 되고, 표준 환경변수 이름만 보면 된다
|
||||
- 비밀값은 저장소에 평문으로 두지 않아도 된다
|
||||
- 설치 단계와 시크릿/운영 자동화 단계를 분리할 수 있다
|
||||
모든 credential-bearing 스킬은 아래 우선순위를 따른다.
|
||||
|
||||
## Security model
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
중요한 한계:
|
||||
기본 경로에 저장하는 것은 fallback일 뿐, 강제가 아니다.
|
||||
|
||||
- 암호화된 파일은 안전하게 저장할 수 있다
|
||||
- 하지만 `sops exec-env ...` 로 실행된 프로세스는 복호화된 환경변수를 사용할 수 있다
|
||||
- 즉, 에이전트가 "쓸 수는 있지만 절대로 읽을 수는 없는" 구조는 아니다
|
||||
## Standard file location
|
||||
|
||||
더 강한 모델이 필요하면 비밀값 자체를 넘기지 말고, 비밀값을 내부에서 소비하는 래퍼 명령만 노출해야 한다.
|
||||
|
||||
## Standard file locations
|
||||
|
||||
- age key: `~/.config/k-skill/age/keys.txt`
|
||||
- encrypted secrets file: `~/.config/k-skill/secrets.env`
|
||||
|
||||
원하면 다른 위치를 써도 되지만, 기본 문서는 이 경로를 기준으로 한다.
|
||||
- secrets file (기본 fallback): `~/.config/k-skill/secrets.env`
|
||||
|
||||
## Install
|
||||
|
||||
|
|
@ -68,59 +54,15 @@ npx --yes skills add <owner/repo> --all -g
|
|||
|
||||
설치가 끝나면 이 스킬을 호출해 아래 setup 단계를 이어간다.
|
||||
|
||||
### macOS
|
||||
|
||||
```bash
|
||||
brew install sops age
|
||||
```
|
||||
|
||||
### Ubuntu / Debian
|
||||
|
||||
```bash
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y sops age
|
||||
```
|
||||
|
||||
### Arch Linux
|
||||
|
||||
```bash
|
||||
sudo pacman -S sops age
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```powershell
|
||||
winget install Mozilla.SOPS FiloSottile.age
|
||||
```
|
||||
|
||||
패키지 이름은 배포 채널에 따라 바뀔 수 있으니, 실패하면 공식 releases 페이지를 확인한다.
|
||||
|
||||
## Setup steps
|
||||
|
||||
### 1. Create an age key
|
||||
### 1. Create the default secrets file (if no vault is in use)
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/k-skill/age
|
||||
age-keygen -o ~/.config/k-skill/age/keys.txt
|
||||
```
|
||||
|
||||
출력에 보이는 public key를 복사한다.
|
||||
|
||||
### 2. Create `.sops.yaml`
|
||||
|
||||
작업 디렉터리나 secrets 파일이 있는 디렉터리에 생성한다.
|
||||
|
||||
```yaml
|
||||
creation_rules:
|
||||
- path_regex: .*secrets\.env(\.plain)?$
|
||||
age: age1replace-with-your-public-key
|
||||
```
|
||||
|
||||
### 3. Create the plaintext env file once
|
||||
에이전트가 자체 vault를 쓰지 않는 경우, 기본 fallback 파일을 만든다.
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/k-skill
|
||||
cat > ~/.config/k-skill/secrets.env.plain <<'EOF'
|
||||
cat > ~/.config/k-skill/secrets.env <<'EOF'
|
||||
KSKILL_SRT_ID=replace-me
|
||||
KSKILL_SRT_PASSWORD=replace-me
|
||||
KSKILL_KTX_ID=replace-me
|
||||
|
|
@ -128,31 +70,16 @@ KSKILL_KTX_PASSWORD=replace-me
|
|||
SEOUL_OPEN_API_KEY=replace-me
|
||||
AIR_KOREA_OPEN_API_KEY=replace-me
|
||||
EOF
|
||||
chmod 0600 ~/.config/k-skill/secrets.env
|
||||
```
|
||||
|
||||
실제 값을 채운다.
|
||||
|
||||
### 4. Encrypt it
|
||||
|
||||
```bash
|
||||
cd ~/.config/k-skill
|
||||
sops --encrypt --input-type dotenv --output-type dotenv \
|
||||
secrets.env.plain > secrets.env
|
||||
rm secrets.env.plain
|
||||
```
|
||||
유저에게 물어서 실제 값을 채운다.
|
||||
|
||||
### Missing secret response template
|
||||
|
||||
인증 스킬에서 값이 빠졌을 때는 다음 식으로 안내한다.
|
||||
인증 스킬에서 값이 빠졌을 때는 credential resolution order에 따라 확보한다.
|
||||
|
||||
```text
|
||||
이 작업에는 <REQUIRED_SECRET_NAMES> 이 필요합니다.
|
||||
값을 채팅창에 보내지 말고 ~/.config/k-skill/secrets.env.plain 에 직접 채운 뒤
|
||||
sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
||||
암호화가 끝나면 plaintext 파일은 지우고 bash scripts/check-setup.sh 로 다시 확인해 주세요.
|
||||
```
|
||||
|
||||
예를 들면:
|
||||
필요한 값 예:
|
||||
|
||||
- SRT: `KSKILL_SRT_ID`, `KSKILL_SRT_PASSWORD`
|
||||
- KTX: `KSKILL_KTX_ID`, `KSKILL_KTX_PASSWORD`
|
||||
|
|
@ -161,28 +88,13 @@ sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
|||
|
||||
시크릿이 비어 있다는 이유로 다른 서비스나 비공식 우회 경로를 자동 선택하지 않는다.
|
||||
|
||||
### 5. Verify runtime injection
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'test -n "$KSKILL_SRT_ID" || test -n "$KSKILL_KTX_ID" || test -n "$SEOUL_OPEN_API_KEY" || test -n "$AIR_KOREA_OPEN_API_KEY"'
|
||||
```
|
||||
|
||||
또는 저장소에 들어있는 점검 스크립트를 쓴다.
|
||||
### 2. Verify runtime environment
|
||||
|
||||
```bash
|
||||
bash scripts/check-setup.sh
|
||||
```
|
||||
|
||||
### 6. Run tools with the encrypted file
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" '<your command>'
|
||||
```
|
||||
|
||||
### 7. Offer scheduled update checks
|
||||
### 3. Offer scheduled update checks
|
||||
|
||||
setup이 끝나면 사용자에게 주기적인 업데이트 확인 자동화를 원하는지 먼저 묻는다. 원하지 않으면 건너뛴다.
|
||||
|
||||
|
|
@ -226,7 +138,7 @@ schtasks /Create /SC DAILY /TN "k-skill-update-check" /TR "\"$HOME/.config/k-ski
|
|||
|
||||
- `~/.config/k-skill/logs/skills-check.log`
|
||||
|
||||
### 8. Offer GitHub starring with explicit consent
|
||||
### 4. Offer GitHub starring with explicit consent
|
||||
|
||||
setup 마지막에는 다음처럼 짧게 묻는다.
|
||||
|
||||
|
|
@ -249,31 +161,13 @@ gh repo star NomaDamas/k-skill
|
|||
|
||||
성공하면 짧게 완료만 알린다.
|
||||
|
||||
## Recommended shell helper
|
||||
|
||||
```bash
|
||||
kskill-run() {
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" "$@"
|
||||
}
|
||||
```
|
||||
|
||||
예시:
|
||||
|
||||
```bash
|
||||
kskill-run python your-script.py
|
||||
```
|
||||
|
||||
## Completion checklist
|
||||
|
||||
- `sops --version` works
|
||||
- `age-keygen --version` or `age --version` works
|
||||
- `~/.config/k-skill/age/keys.txt` exists
|
||||
- `~/.config/k-skill/secrets.env` exists and is encrypted
|
||||
- `sops exec-env ...` can inject expected env vars
|
||||
- `~/.config/k-skill/secrets.env` exists with permission `0600` (또는 에이전트가 자체 vault로 credential을 관리 중)
|
||||
- 필요한 환경변수가 설정되어 있다
|
||||
- 사용자가 원한 경우에만 업데이트 확인 자동화 또는 GitHub star가 설정되었다
|
||||
|
||||
## Notes
|
||||
|
||||
- 기본 흐름은 "전체 스킬 설치 → 이 setup skill 실행 → 개별 기능 사용" 이다
|
||||
- 저장소 안에는 plaintext secret file을 두지 않는다
|
||||
- 저장소 안에는 secret file을 두지 않는다
|
||||
|
|
|
|||
|
|
@ -33,15 +33,21 @@ metadata:
|
|||
|
||||
- Python 3.10+
|
||||
- `python3 -m pip install korail2 pycryptodome`
|
||||
- `sops` and `age` installed
|
||||
- common setup reviewed in `../k-skill-setup/SKILL.md`
|
||||
- secret policy reviewed in `../docs/security-and-secrets.md`
|
||||
|
||||
## Required secrets
|
||||
## Required environment variables
|
||||
|
||||
- `KSKILL_KTX_ID`
|
||||
- `KSKILL_KTX_PASSWORD`
|
||||
|
||||
### Credential resolution order
|
||||
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
기본 경로에 저장하는 것은 fallback일 뿐, 강제가 아니다.
|
||||
|
||||
## Inputs
|
||||
|
||||
- 출발역
|
||||
|
|
@ -62,16 +68,9 @@ metadata:
|
|||
python3 -m pip install korail2 pycryptodome
|
||||
```
|
||||
|
||||
### 1. Stop for secure registration when secrets are missing
|
||||
### 1. Ensure credentials are available
|
||||
|
||||
`KSKILL_KTX_ID`, `KSKILL_KTX_PASSWORD`, `~/.config/k-skill/secrets.env`, `~/.config/k-skill/age/keys.txt` 중 하나라도 없으면 다음 식으로 안내하고 멈춘다.
|
||||
|
||||
```text
|
||||
이 작업에는 KSKILL_KTX_ID, KSKILL_KTX_PASSWORD 가 필요합니다.
|
||||
값을 채팅창에 붙여 넣지 말고 ~/.config/k-skill/secrets.env.plain 에 직접 채운 뒤
|
||||
sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
||||
암호화가 끝나면 plaintext 파일은 지우고 bash scripts/check-setup.sh 로 다시 확인해 주세요.
|
||||
```
|
||||
`KSKILL_KTX_ID`, `KSKILL_KTX_PASSWORD` 환경변수가 설정되어 있는지 확인한다. 없으면 위 credential resolution order에 따라 확보한다.
|
||||
|
||||
시크릿이 없다는 이유로 웹사이트를 직접 긁거나 다른 비공식 경로를 찾지 않는다.
|
||||
|
||||
|
|
@ -80,9 +79,7 @@ sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
|||
항상 helper 를 통해 조회한다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py search 서울 부산 20260328 090000 --limit 5'
|
||||
python3 scripts/ktx_booking.py search 서울 부산 20260328 090000 --limit 5
|
||||
```
|
||||
|
||||
좌석이 없는 열차도 후보에 포함하려면 `--include-no-seats`, 예약 대기 가능한 열차도 같이 보고 싶으면 `--include-waiting-list` 를 붙인다.
|
||||
|
|
@ -103,9 +100,7 @@ sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
|||
조회 결과의 `train_id` 를 고른 뒤에만 예약한다. 이 값은 helper 가 열차 번호/운행일/시각/역 코드를 묶어 만든 stable selector 이므로, 재조회 시 같은 열차가 아직 있으면 그대로 잡고 없으면 실패한다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py reserve 서울 부산 20260328 090000 --train-id <train_id> --seat-option general-first'
|
||||
python3 scripts/ktx_booking.py reserve 서울 부산 20260328 090000 --train-id <train_id> --seat-option general-first
|
||||
```
|
||||
|
||||
응답에는 예약번호, 운임, 구입기한이 포함된다. **결제는 자동화하지 않는다.**
|
||||
|
|
@ -116,15 +111,11 @@ sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
|||
취소는 대상 예약을 다시 조회해 식별한 뒤에만 진행한다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py reservations'
|
||||
python3 scripts/ktx_booking.py reservations
|
||||
```
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'python3 scripts/ktx_booking.py cancel <reservation_id>'
|
||||
python3 scripts/ktx_booking.py cancel <reservation_id>
|
||||
```
|
||||
|
||||
## Done when
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@
|
|||
## 로컬 실행
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'node packages/k-skill-proxy/src/server.js'
|
||||
node packages/k-skill-proxy/src/server.js
|
||||
```
|
||||
|
||||
환경변수(`AIR_KOREA_OPEN_API_KEY` 등)가 이미 설정되어 있거나 `~/.config/k-skill/secrets.env`를 source한 상태에서 실행한다.
|
||||
|
||||
## PM2 실행
|
||||
|
||||
루트의 `ecosystem.config.cjs` + `scripts/run-k-skill-proxy.sh` 조합을 사용하면 재부팅 이후에도 같은 encrypted secrets 경로로 다시 올라옵니다.
|
||||
루트의 `ecosystem.config.cjs` + `scripts/run-k-skill-proxy.sh` 조합을 사용하면 재부팅 이후에도 같은 환경변수로 다시 올라옵니다.
|
||||
|
|
|
|||
|
|
@ -2,44 +2,28 @@
|
|||
set -euo pipefail
|
||||
|
||||
secrets_file="${1:-$HOME/.config/k-skill/secrets.env}"
|
||||
age_key_file="${SOPS_AGE_KEY_FILE:-$HOME/.config/k-skill/age/keys.txt}"
|
||||
|
||||
missing=0
|
||||
|
||||
check_cmd() {
|
||||
if ! command -v "$1" >/dev/null 2>&1; then
|
||||
echo "missing command: $1"
|
||||
if [[ ! -f "$secrets_file" ]]; then
|
||||
echo "missing secrets file: $secrets_file"
|
||||
missing=1
|
||||
else
|
||||
perms=$(stat -f '%Lp' "$secrets_file" 2>/dev/null || stat -c '%a' "$secrets_file" 2>/dev/null)
|
||||
if [[ "$perms" != "600" ]]; then
|
||||
echo "insecure permissions on $secrets_file: $perms (expected 600)"
|
||||
missing=1
|
||||
fi
|
||||
}
|
||||
|
||||
check_cmd sops
|
||||
check_cmd age
|
||||
check_cmd age-keygen
|
||||
|
||||
if [[ ! -f "$age_key_file" ]]; then
|
||||
echo "missing age key file: $age_key_file"
|
||||
missing=1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$secrets_file" ]]; then
|
||||
echo "missing encrypted secrets file: $secrets_file"
|
||||
missing=1
|
||||
fi
|
||||
|
||||
if [[ "$missing" -ne 0 ]]; then
|
||||
cat <<EOF
|
||||
next steps:
|
||||
1. follow k-skill-setup / docs/setup.md
|
||||
2. register required secrets in ~/.config/k-skill/secrets.env.plain
|
||||
3. encrypt it to ~/.config/k-skill/secrets.env with sops
|
||||
4. delete the plaintext file
|
||||
5. run this check again
|
||||
1. create ~/.config/k-skill/secrets.env with your credentials
|
||||
2. chmod 0600 ~/.config/k-skill/secrets.env
|
||||
3. run this check again
|
||||
EOF
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SOPS_AGE_KEY_FILE="$age_key_file" \
|
||||
sops exec-env "$secrets_file" 'true'
|
||||
|
||||
echo "k-skill setup looks usable"
|
||||
|
|
|
|||
|
|
@ -337,10 +337,9 @@ def build_report(
|
|||
|
||||
def build_missing_secret_message() -> str:
|
||||
return (
|
||||
f"이 작업에는 {SECRET_NAME} 가 필요합니다.\n"
|
||||
"값을 채팅창에 붙여 넣지 말고 ~/.config/k-skill/secrets.env.plain 에 직접 채운 뒤\n"
|
||||
"sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.\n"
|
||||
"암호화가 끝나면 plaintext 파일은 지우고 bash scripts/check-setup.sh 로 다시 확인해 주세요."
|
||||
f"이 작업에는 {SECRET_NAME} 환경변수가 필요합니다.\n"
|
||||
"환경변수가 설정되어 있지 않으면 ~/.config/k-skill/secrets.env 에 추가하거나\n"
|
||||
"에이전트의 secret vault에서 주입해 주세요."
|
||||
)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -625,9 +625,9 @@ def build_client() -> PatchedKorail:
|
|||
korail_pw = os.environ.get("KSKILL_KTX_PASSWORD")
|
||||
if not korail_id or not korail_pw:
|
||||
raise SystemExit(
|
||||
"이 작업에는 KSKILL_KTX_ID, KSKILL_KTX_PASSWORD 가 필요합니다. "
|
||||
"값을 채팅창에 붙여 넣지 말고 ~/.config/k-skill/secrets.env.plain 에 직접 채운 뒤 "
|
||||
"sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요."
|
||||
"이 작업에는 KSKILL_KTX_ID, KSKILL_KTX_PASSWORD 환경변수가 필요합니다. "
|
||||
"환경변수가 설정되어 있지 않으면 ~/.config/k-skill/secrets.env 에 추가하거나 "
|
||||
"에이전트의 secret vault에서 주입해 주세요."
|
||||
)
|
||||
client = PatchedKorail(korail_id, korail_pw)
|
||||
if not client.logined:
|
||||
|
|
|
|||
|
|
@ -3,23 +3,13 @@ set -euo pipefail
|
|||
|
||||
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
SECRETS_FILE="${KSKILL_SECRETS_FILE:-$HOME/.config/k-skill/secrets.env}"
|
||||
AGE_KEY_FILE="${SOPS_AGE_KEY_FILE:-$HOME/.config/k-skill/age/keys.txt}"
|
||||
|
||||
if ! command -v sops >/dev/null 2>&1; then
|
||||
echo "missing command: sops" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$SECRETS_FILE" ]]; then
|
||||
echo "missing encrypted secrets file: $SECRETS_FILE" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! -f "$AGE_KEY_FILE" ]]; then
|
||||
echo "missing age key file: $AGE_KEY_FILE" >&2
|
||||
exit 1
|
||||
if [[ -f "$SECRETS_FILE" ]]; then
|
||||
set -a
|
||||
# shellcheck disable=SC1090
|
||||
source "$SECRETS_FILE"
|
||||
set +a
|
||||
fi
|
||||
|
||||
cd "$ROOT_DIR"
|
||||
exec env SOPS_AGE_KEY_FILE="$AGE_KEY_FILE" \
|
||||
sops exec-env "$SECRETS_FILE" 'node packages/k-skill-proxy/src/server.js'
|
||||
exec node packages/k-skill-proxy/src/server.js
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ test("ktx-booking docs document the helper-based live Korail workflow", () => {
|
|||
assert.match(doc, /--include-no-seats/);
|
||||
assert.match(doc, /--include-waiting-list/);
|
||||
assert.match(doc, /--try-waiting/);
|
||||
assert.match(doc, /sops exec-env/);
|
||||
assert.match(doc, /credential resolution order|KSKILL_KTX_ID/);
|
||||
assert.match(doc, /anti-bot|Dynapath|x-dynapath-m-token/i);
|
||||
assert.match(doc, /결제(까지)?는 자동화하지 않는다|결제는 제외/);
|
||||
assert.doesNotMatch(doc, /예약 시 선택할 `--train-index`/);
|
||||
|
|
|
|||
|
|
@ -23,15 +23,21 @@ metadata:
|
|||
## Prerequisites
|
||||
|
||||
- 서울 열린데이터 광장 API key
|
||||
- `sops` and `age` installed
|
||||
- common setup reviewed in `../k-skill-setup/SKILL.md`
|
||||
- secret policy reviewed in `../docs/security-and-secrets.md`
|
||||
- optional: `jq`
|
||||
|
||||
## Required secrets
|
||||
## Required environment variables
|
||||
|
||||
- `SEOUL_OPEN_API_KEY`
|
||||
|
||||
### Credential resolution order
|
||||
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
기본 경로에 저장하는 것은 fallback일 뿐, 강제가 아니다.
|
||||
|
||||
## Inputs
|
||||
|
||||
- 역명
|
||||
|
|
@ -39,34 +45,18 @@ metadata:
|
|||
|
||||
## Workflow
|
||||
|
||||
### 1. Stop for secure registration when the API key is missing
|
||||
### 1. Ensure credentials are available
|
||||
|
||||
평문 key를 붙여 넣지 않는다.
|
||||
|
||||
`SEOUL_OPEN_API_KEY`, `~/.config/k-skill/secrets.env`, `~/.config/k-skill/age/keys.txt` 중 하나라도 없으면 다음 식으로 안내하고 멈춘다.
|
||||
|
||||
```text
|
||||
이 작업에는 SEOUL_OPEN_API_KEY 가 필요합니다.
|
||||
값을 채팅창에 붙여 넣지 말고 ~/.config/k-skill/secrets.env.plain 에 직접 채운 뒤
|
||||
sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
||||
암호화가 끝나면 plaintext 파일은 지우고 bash scripts/check-setup.sh 로 다시 확인해 주세요.
|
||||
```
|
||||
`SEOUL_OPEN_API_KEY` 환경변수가 설정되어 있는지 확인한다. 없으면 위 credential resolution order에 따라 확보한다.
|
||||
|
||||
시크릿이 없다는 이유로 비공식 미러 API나 다른 출처로 자동 우회하지 않는다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" 'test -n "$SEOUL_OPEN_API_KEY"'
|
||||
```
|
||||
|
||||
### 2. Query the official station arrival endpoint
|
||||
|
||||
서울 실시간 지하철 API는 역명 기준 실시간 도착 정보를 JSON/XML로 제공한다. 기본 질의 예시는 다음 패턴을 쓴다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" \
|
||||
'curl -s "http://swopenAPI.seoul.go.kr/api/subway/${SEOUL_OPEN_API_KEY}/json/realtimeStationArrival/0/8/강남"'
|
||||
curl -s "http://swopenAPI.seoul.go.kr/api/subway/${SEOUL_OPEN_API_KEY}/json/realtimeStationArrival/0/8/강남"
|
||||
```
|
||||
|
||||
### 3. Summarize the response
|
||||
|
|
|
|||
|
|
@ -31,16 +31,20 @@ metadata:
|
|||
|
||||
- Python 3.10+
|
||||
- `python3 -m pip install SRTrain`
|
||||
- `sops` and `age` installed
|
||||
- common setup reviewed in `../k-skill-setup/SKILL.md`
|
||||
- secret policy reviewed in `../docs/security-and-secrets.md`
|
||||
|
||||
## Required secrets
|
||||
## Required environment variables
|
||||
|
||||
- `KSKILL_SRT_ID`
|
||||
- `KSKILL_SRT_PASSWORD`
|
||||
|
||||
평문 비밀번호는 금지한다. 항상 `sops exec-env ...` 패턴을 사용한다.
|
||||
### Credential resolution order
|
||||
|
||||
1. **이미 환경변수에 있으면** 그대로 사용한다.
|
||||
2. **에이전트가 자체 secret vault(1Password CLI, Bitwarden CLI, macOS Keychain 등)를 사용 중이면** 거기서 꺼내 환경변수로 주입해도 된다.
|
||||
3. **`~/.config/k-skill/secrets.env`** (기본 fallback) — plain dotenv 파일, 퍼미션 `0600`.
|
||||
4. **아무것도 없으면** 유저에게 물어서 2 또는 3에 저장한다.
|
||||
|
||||
기본 경로에 저장하는 것은 fallback일 뿐, 강제가 아니다.
|
||||
|
||||
## Inputs
|
||||
|
||||
|
|
@ -61,18 +65,9 @@ metadata:
|
|||
python3 -m pip install SRTrain
|
||||
```
|
||||
|
||||
### 1. Validate secrets path and stop for secure registration when missing
|
||||
### 1. Ensure credentials are available
|
||||
|
||||
비밀번호를 직접 받지 않는다. 필요한 경우 encrypted secrets file 경로와 변수 이름만 확인한다.
|
||||
|
||||
`KSKILL_SRT_ID`, `KSKILL_SRT_PASSWORD`, `~/.config/k-skill/secrets.env`, `~/.config/k-skill/age/keys.txt` 중 하나라도 없으면 다음 식으로 안내하고 멈춘다.
|
||||
|
||||
```text
|
||||
이 작업에는 KSKILL_SRT_ID, KSKILL_SRT_PASSWORD 가 필요합니다.
|
||||
값을 채팅창에 붙여 넣지 말고 ~/.config/k-skill/secrets.env.plain 에 직접 채운 뒤
|
||||
sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
||||
암호화가 끝나면 plaintext 파일은 지우고 bash scripts/check-setup.sh 로 다시 확인해 주세요.
|
||||
```
|
||||
`KSKILL_SRT_ID`, `KSKILL_SRT_PASSWORD` 환경변수가 설정되어 있는지 확인한다. 없으면 위 credential resolution order에 따라 확보한다.
|
||||
|
||||
시크릿이 없다는 이유로 웹사이트를 직접 긁거나 다른 비공식 경로를 찾지 않는다.
|
||||
|
||||
|
|
@ -81,8 +76,7 @@ sops 로 ~/.config/k-skill/secrets.env 로 암호화해 주세요.
|
|||
먼저 조회해서 후보를 요약한다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" 'python3 - <<'"'"'PY'"'"'
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from SRT import SRT
|
||||
|
||||
|
|
@ -92,7 +86,6 @@ trains = srt.search_train("수서", "부산", "20260328", "080000", time_limit="
|
|||
for idx, train in enumerate(trains[:5], start=1):
|
||||
print(idx, train)
|
||||
PY
|
||||
'
|
||||
```
|
||||
|
||||
### 3. Summarize options before side effects
|
||||
|
|
@ -108,8 +101,7 @@ PY
|
|||
예약은 부작용이 있으므로 정확한 열차를 고른 뒤에만 진행한다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" 'python3 - <<'"'"'PY'"'"'
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from SRT import Adult, SRT, SeatType
|
||||
|
||||
|
|
@ -122,16 +114,14 @@ reservation = srt.reserve(
|
|||
)
|
||||
print(reservation)
|
||||
PY
|
||||
'
|
||||
```
|
||||
|
||||
### 5. Inspect or cancel
|
||||
|
||||
예약 확인이나 취소도 같은 credential path를 유지한다. 취소 전에는 대상 예약을 다시 식별한다.
|
||||
취소 전에는 대상 예약을 다시 식별한다.
|
||||
|
||||
```bash
|
||||
SOPS_AGE_KEY_FILE="$HOME/.config/k-skill/age/keys.txt" \
|
||||
sops exec-env "$HOME/.config/k-skill/secrets.env" 'python3 - <<'"'"'PY'"'"'
|
||||
python3 - <<'PY'
|
||||
import os
|
||||
from SRT import SRT
|
||||
|
||||
|
|
@ -139,7 +129,6 @@ srt = SRT(os.environ["KSKILL_SRT_ID"], os.environ["KSKILL_SRT_PASSWORD"])
|
|||
reservations = srt.get_reservations()
|
||||
print(reservations)
|
||||
PY
|
||||
'
|
||||
```
|
||||
|
||||
## Done when
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue