haechi 1.1.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.ko.md +46 -11
  2. package/README.md +46 -11
  3. package/SECURITY.md +7 -1
  4. package/docs/README.md +2 -0
  5. package/docs/current/compliance-mapping.ko.md +53 -0
  6. package/docs/current/compliance-mapping.md +53 -0
  7. package/docs/current/config-version.ko.md +30 -0
  8. package/docs/current/config-version.md +51 -0
  9. package/docs/current/configuration.ko.md +165 -9
  10. package/docs/current/configuration.md +165 -9
  11. package/docs/current/operations-runbook.ko.md +155 -0
  12. package/docs/current/operations-runbook.md +241 -0
  13. package/docs/current/release-process.ko.md +5 -1
  14. package/docs/current/release-process.md +5 -1
  15. package/docs/current/risk-register-release-gate.ko.md +5 -3
  16. package/docs/current/risk-register-release-gate.md +13 -3
  17. package/docs/current/security-whitepaper.ko.md +102 -0
  18. package/docs/current/security-whitepaper.md +102 -0
  19. package/docs/current/shared-responsibility.ko.md +2 -2
  20. package/docs/current/shared-responsibility.md +2 -2
  21. package/docs/current/threat-model.ko.md +4 -2
  22. package/docs/current/threat-model.md +4 -2
  23. package/examples/local-proxy-demo/README.md +51 -0
  24. package/examples/local-proxy-demo/demo.mjs +144 -0
  25. package/examples/local-proxy-demo/demo.tape +19 -0
  26. package/examples/local-proxy-demo/live-demo.mjs +121 -0
  27. package/examples/local-proxy-demo/live-demo.tape +25 -0
  28. package/haechi.config.example.json +20 -3
  29. package/package.json +7 -2
  30. package/packages/audit/index.mjs +26 -2
  31. package/packages/cli/bin/haechi.mjs +57 -10
  32. package/packages/cli/runtime.mjs +402 -10
  33. package/packages/core/index.mjs +143 -8
  34. package/packages/filter/index.mjs +975 -12
  35. package/packages/metrics/index.mjs +181 -0
  36. package/packages/privacy-profiles/index.mjs +72 -3
  37. package/packages/protocol-adapters/index.mjs +99 -1
  38. package/packages/proxy/index.mjs +525 -40
  39. package/packages/stream-filter/index.mjs +69 -7
package/README.ko.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![CI](https://github.com/raeseoklee/haechi/actions/workflows/ci.yml/badge.svg)](https://github.com/raeseoklee/haechi/actions/workflows/ci.yml)
9
9
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
10
10
  [![node](https://img.shields.io/node/v/haechi)](https://nodejs.org)
11
- [![status](https://img.shields.io/badge/status-stable%201.1-brightgreen)](docs/current/api-stability.md)
11
+ [![status](https://img.shields.io/badge/status-stable%201.2-brightgreen)](docs/current/api-stability.md)
12
12
 
13
13
  [English](README.md) | **한국어**
14
14
 
@@ -18,7 +18,7 @@ Haechi는 LLM·MCP·vLLM·Ollama 및 에이전트 payload가 모델, 도구, 로
18
18
 
19
19
  이 저장소는 로컬 개발, 보안 설계 검토, 자체 호스팅 통합 실험을 위한 것입니다. 컴플라이언스를 보장하지는 않습니다.
20
20
 
21
- **1.0.0이 첫 stable 릴리스입니다.** 1.0부터 public API는 strict semver 하의 frozen 계약입니다. `package.json`의 `exports` 표면, CLI의 기계 판독 동작, audit event schema, config key shape이 모두 major 버전 계약의 일부이며, 문서화된 deprecation 정책과 in-minor 보안 예외 하나가 함께 적용됩니다. [`docs/current/api-stability.md`](docs/current/api-stability.md)를 참고하세요. 네 개의 `haechi-*` 위성은 pre-1.0으로 유지되며 core와 독립적으로 버저닝됩니다.
21
+ **1.0.0이 첫 stable 릴리스입니다.** 1.0부터 public API는 strict semver 하의 frozen 계약입니다. `package.json`의 `exports` 표면, CLI의 기계 판독 동작, audit event schema, config key shape이 모두 major 버전 계약의 일부이며, 문서화된 deprecation 정책과 in-minor 보안 예외 하나가 함께 적용됩니다. [`docs/current/api-stability.md`](docs/current/api-stability.md)를 참고하세요. `haechi-*` 위성은 pre-1.0으로 유지되며 core와 독립적으로 버저닝됩니다([위성 패키지](#위성-패키지) 참고).
22
22
 
23
23
  현재 범위는 로컬 도입에 초점을 맞춥니다.
24
24
 
@@ -30,6 +30,28 @@ Haechi는 LLM·MCP·vLLM·Ollama 및 에이전트 payload가 모델, 도구, 로
30
30
  - `haechi audit-verify`: audit hash chain을 검증하고 head hash를 출력합니다
31
31
  - `haechi mcp-wrap -- <command>`: MCP 서버를 양방향 stdio 보호로 감쌉니다
32
32
 
33
+ ## 데모
34
+
35
+ <p align="center">
36
+ <img src="https://raw.githubusercontent.com/raeseoklee/haechi/main/docs/assets/haechi-demo.gif" alt="Haechi 라이브 end-to-end 데모(실제 모델): 탐지 후 tokenize/mask/redact, 모델은 마스킹된 전화만 반복, 무평문 감사, 라이브 readiness + Prometheus metrics, 카드 차단" width="900">
37
+ </p>
38
+
39
+ 위 녹화는 실제 self-hosted 모델(vLLM의 Qwen3.6-35B)에 붙인 **라이브** end-to-end 실행입니다(`enforce` 모드). 모델에게 받은 전화번호를 그대로 반복하라고 시키면 — 진짜 번호는 모델에 도달조차 하지 않았으므로 **마스킹된** 형태만 돌려줄 수 있습니다. 무평문 감사, 라이브 `/__haechi/ready` + `/__haechi/metrics`, upstream 호출 전에 fail-closed로 차단되는 카드도 함께 보여줍니다.
40
+
41
+ 직접 실행해 보십시오 — 백엔드 없이 재현 가능한 스텁 버전:
42
+
43
+ ```bash
44
+ npm run demo
45
+ ```
46
+
47
+ …또는 본인의 OpenAI-호환 서버 상대로:
48
+
49
+ ```bash
50
+ HAECHI_LIVE_UPSTREAM=http://127.0.0.1:8000 node examples/local-proxy-demo/live-demo.mjs
51
+ ```
52
+
53
+ [`examples/local-proxy-demo/`](examples/local-proxy-demo/)를 참고하십시오.
54
+
33
55
  ## 설치
34
56
 
35
57
  ```bash
@@ -76,7 +98,7 @@ upstream 요청은 `limits.upstreamTimeoutMs`(기본값 120000) 이후 타임아
76
98
 
77
99
  ## Local Inference Servers
78
100
 
79
- Haechi는 OpenAI 호환 서버, vLLM, Ollama, llama.cpp용 프로토콜 adapter 프리셋을 제공합니다.
101
+ Haechi는 OpenAI 호환 서버, vLLM, Ollama, llama.cpp, Anthropic Messages API, 그리고 Google Gemini API용 프로토콜 adapter 프리셋을 제공합니다.
80
102
 
81
103
  ```json
82
104
  {
@@ -96,7 +118,7 @@ Haechi는 OpenAI 호환 서버, vLLM, Ollama, llama.cpp용 프로토콜 adapter
96
118
  }
97
119
  ```
98
120
 
99
- 그런 다음 OpenAI 호환 클라이언트를 `http://127.0.0.1:11016/v1`으로 향하게 합니다. Ollama 네이티브 API는 `target.adapter: "ollama"`를 사용하고 proxy를 통해 `/api/chat` 또는 `/api/generate`를 호출하세요.
121
+ 그런 다음 OpenAI 호환 클라이언트를 `http://127.0.0.1:11016/v1`으로 향하게 합니다. Ollama 네이티브 API는 `target.adapter: "ollama"`를 사용하고 proxy를 통해 `/api/chat` 또는 `/api/generate`를 호출하세요. Claude는 `target.type: "anthropic"`을 설정하고 `/v1/messages`(또는 `/v1/messages/count_tokens`, `/v1/complete`)를 호출하세요. 클라이언트의 `x-api-key`/`anthropic-version` 헤더는 upstream으로 그대로 전달됩니다. Gemini는 `target.type: "gemini"`를 설정하고 모델이 경로에 포함된 엔드포인트 `/v1beta/models/{model}:generateContent`(또는 `:streamGenerateContent`, `:countTokens`, `:embedContent`, `:batchEmbedContents`)를 호출하세요. 클라이언트의 `x-goog-api-key`(또는 `?key=`)는 upstream으로 그대로 전달됩니다.
100
122
 
101
123
  ## 토큰 왕복
102
124
 
@@ -173,14 +195,27 @@ haechi auth revoke <id>
173
195
  - **Rate limit**: identity별 분당 요청 수 → `429`(인메모리, 프로세스별).
174
196
  - Audit 이벤트에는 **PII-safe** `identity`(keyed-HMAC subject/issuer이며 원시 값이 아닙니다)와 매핑된 `profile`이 들어가고, `auth_denied`/`model_not_allowed`/`rate_limited` 결정에는 credential이 포함되지 않습니다. `/__haechi/health`는 인증 없이 접근할 수 있습니다.
175
197
 
176
- JWT/JWKS 인증과 KMS 기반 key custody는 `haechi-*` 위성 패키지로 제공되며, 각각 core와 독립적으로 버저닝·발행됩니다. 위성은 pre-1.0으로 유지되며 `haechi` peer 범위를 `>=0.8.0 <2.0.0`으로 선언합니다(상한이 core major따라가므로 core 1.0.0이 위성 설치를 깨뜨리지 않습니다).
198
+ JWT/JWKS 인증과 KMS 기반 key custody(및 기타 선택 기능)**`haechi-*` 위성 패키지**로 제공됩니다 아래 [위성 패키지](#위성-패키지)참고하세요.
199
+
200
+ ## 위성 패키지
201
+
202
+ 선택 기능은 **npm에 독립 발행되는 `haechi-*` 패키지**로 제공됩니다 — 각각 core와 별도로 버저닝되고, 기본적으로 `node:` 전용이며(KMS나 Redis 클라이언트 같은 무거운 SDK는 optional peer), `haechi` peer 범위를 `>=0.8.0 <2.0.0`으로 선언합니다(상한이 core major를 따라가므로 core minor가 위성 설치를 깨뜨리지 않습니다).
203
+
204
+ **위성과 함께 core를 반드시 설치하세요** — `haechi`는 **번들되지 않은 peer dependency**이므로, 위성만으로는 동작하지 않습니다:
205
+
206
+ ```bash
207
+ npm install haechi haechi-<satellite>
208
+ ```
177
209
 
178
- - [`haechi-auth-jwt`](satellites/auth-jwt/)(0.2.1) 헤드리스 JWKS bearer 검증. 재사용 가능한 JWS 검증기(`createJwtVerifier`)를 추가로 export합니다.
179
- - [`haechi-crypto-kms`](satellites/crypto-kms/)(0.2.1) — 실제 KMS 클라이언트 기반 envelope 암호화. AWS에 더해 GCP(`./gcp`), Azure(`./azure`), HashiCorp Vault Transit(`./vault`, `node:` 전용) 백엔드를 지원합니다.
180
- - [`haechi-dashboard`](satellites/dashboard/)(0.1.2) audit 로그와 hash chain 상태를 보는 zero-dependency 읽기 전용 audit 뷰어(`node:http`)입니다.
181
- - [`haechi-auth-oidc`](satellites/auth-oidc/)(0.1.2) 대시보드의 사람 로그인을 담당하는 대화형 OIDC 세션 브로커(authorization-code + PKCE)입니다.
210
+ | 패키지 | 추가하는 기능 |
211
+ |---|---|
212
+ | [`haechi-auth-jwt`](satellites/auth-jwt/) | 헤드리스 JWKS bearer(JWT) `authProvider`. 재사용 가능한 JWS 검증기(`createJwtVerifier`)를 추가로 export합니다. |
213
+ | [`haechi-auth-oidc`](satellites/auth-oidc/) | 대화형 OIDC 세션 브로커(authorization-code + PKCE) — 대시보드의 사람 로그인. `haechi-auth-jwt`를 재사용합니다. |
214
+ | [`haechi-crypto-kms`](satellites/crypto-kms/) | `keys.provider: external`용 envelope 암호화 `cryptoProvider` — AWS, GCP(`./gcp`), Azure(`./azure`), HashiCorp Vault Transit(`./vault`, `node:` 전용) 백엔드. |
215
+ | [`haechi-dashboard`](satellites/dashboard/) | audit 로그와 hash chain 상태를 보는 zero-dependency 읽기 전용 audit 뷰어(`node:http`). |
216
+ | [`haechi-ratelimit-redis`](satellites/ratelimit-redis/) | `providers.rateLimiter` 주입 시임을 통한 다중 복제용 공유 저장소(Redis 기반) `rateLimiter`. |
182
217
 
183
- 위성은 기본적으로 `node:` 전용이며(무거운 SDK는 optional peer), core zero-dependency로 유지합니다.
218
+ 패키지의 README가 사용법과 정확한 peer 요구사항을 다룹니다. 위성의 무거운 SDK는 해당 백엔드를 쓸 때만 설치되는 optional peer core zero-dependency로 유지됩니다.
184
219
 
185
220
  ## 설정
186
221
 
@@ -189,7 +224,7 @@ JWT/JWKS 인증과 KMS 기반 key custody는 `haechi-*` 위성 패키지로 제
189
224
  | 키 | 기본값 | 설명 |
190
225
  |---|---|---|
191
226
  | `mode` / `policy.mode` | `dry-run` | `dry-run`과 `report-only`는 탐지와 audit만 하고, `enforce`는 변환/차단을 적용합니다. `policy.mode`가 `mode`보다 우선합니다 |
192
- | `target.type` / `target.adapter` | `llm-http` / `openai-compatible` | upstream 프로토콜: `openai-compatible`, `vllm-openai`, `ollama`, `llama-cpp`. 알 수 없는 type은 fail-closed로 처리됩니다 |
227
+ | `target.type` / `target.adapter` | `llm-http` / `openai-compatible` | upstream 프로토콜: `openai-compatible`, `vllm-openai`, `ollama`, `llama-cpp`, `anthropic`, `gemini`. 알 수 없는 type은 fail-closed로 처리됩니다 |
193
228
  | `target.upstream` | `http://127.0.0.1:9999` | proxy가 요청을 전달하는 유일한 upstream(절대 URL 요청 대상은 거부됩니다) |
194
229
  | `proxy.host` / `proxy.port` | `127.0.0.1` / `11016` | proxy 바인드 주소. 아래 remote 바인딩 참고 |
195
230
  | `responseProtection.enabled` | `false` | upstream JSON 응답을 검사합니다. `failureMode: fail-closed`는 비JSON/압축/대용량 응답을 거부합니다 |
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  [![CI](https://github.com/raeseoklee/haechi/actions/workflows/ci.yml/badge.svg)](https://github.com/raeseoklee/haechi/actions/workflows/ci.yml)
9
9
  [![license](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
10
10
  [![node](https://img.shields.io/node/v/haechi)](https://nodejs.org)
11
- [![status](https://img.shields.io/badge/status-stable%201.1-brightgreen)](docs/current/api-stability.md)
11
+ [![status](https://img.shields.io/badge/status-stable%201.2-brightgreen)](docs/current/api-stability.md)
12
12
 
13
13
  **English** | [한국어](README.ko.md)
14
14
 
@@ -18,7 +18,7 @@ The name comes from Haechi, a Korean guardian figure associated with discernment
18
18
 
19
19
  This repository is intended for local development, security design review, and self-hosted integration experiments. It is not a compliance guarantee.
20
20
 
21
- **1.0.0 is the first stable release.** From 1.0 the public API is a frozen contract under strict semver: the `package.json` `exports` surface, the CLI's machine-readable behavior, the audit event schema, and the config key shape are all part of the major-versioned contract, with a documented deprecation policy and a one in-minor security exception. See [`docs/current/api-stability.md`](docs/current/api-stability.md). The four `haechi-*` satellites stay pre-1.0 and version independently of core.
21
+ **1.0.0 is the first stable release.** From 1.0 the public API is a frozen contract under strict semver: the `package.json` `exports` surface, the CLI's machine-readable behavior, the audit event schema, and the config key shape are all part of the major-versioned contract, with a documented deprecation policy and a one in-minor security exception. See [`docs/current/api-stability.md`](docs/current/api-stability.md). The `haechi-*` satellites stay pre-1.0 and version independently of core (see [Satellite packages](#satellite-packages)).
22
22
 
23
23
  The current scope focuses on local adoption:
24
24
 
@@ -30,6 +30,28 @@ The current scope focuses on local adoption:
30
30
  - `haechi audit-verify`: verify the audit hash chain and print its head hash
31
31
  - `haechi mcp-wrap -- <command>`: wrap an MCP server with bidirectional stdio protection
32
32
 
33
+ ## Demo
34
+
35
+ <p align="center">
36
+ <img src="https://raw.githubusercontent.com/raeseoklee/haechi/main/docs/assets/haechi-demo.gif" alt="Haechi live end-to-end demo against a real model: detection then tokenize/mask/redact, the masked phone the model can only repeat, a no-plaintext audit, live readiness + Prometheus metrics, and a blocked card" width="900">
37
+ </p>
38
+
39
+ The recording above is a **live** end-to-end run against a real self-hosted model (Qwen3.6-35B on vLLM) in `enforce` mode. The model is asked to repeat the phone number it was given — and it can only return the **masked** form, because the real number never reached it. It also shows the no-plaintext audit, the live `/__haechi/ready` + `/__haechi/metrics` surface, and a card blocked fail-closed before any upstream call.
40
+
41
+ Run it yourself — a no-backend, reproducible version with a stub upstream:
42
+
43
+ ```bash
44
+ npm run demo
45
+ ```
46
+
47
+ …or against your own OpenAI-compatible server:
48
+
49
+ ```bash
50
+ HAECHI_LIVE_UPSTREAM=http://127.0.0.1:8000 node examples/local-proxy-demo/live-demo.mjs
51
+ ```
52
+
53
+ See [`examples/local-proxy-demo/`](examples/local-proxy-demo/).
54
+
33
55
  ## Install
34
56
 
35
57
  ```bash
@@ -76,7 +98,7 @@ Upstream requests time out after `limits.upstreamTimeoutMs` (default 120000) and
76
98
 
77
99
  ## Local Inference Servers
78
100
 
79
- Haechi includes protocol adapter presets for OpenAI-compatible servers, vLLM, Ollama, and llama.cpp.
101
+ Haechi includes protocol adapter presets for OpenAI-compatible servers, vLLM, Ollama, llama.cpp, the Anthropic Messages API, and the Google Gemini API.
80
102
 
81
103
  ```json
82
104
  {
@@ -96,7 +118,7 @@ Haechi includes protocol adapter presets for OpenAI-compatible servers, vLLM, Ol
96
118
  }
97
119
  ```
98
120
 
99
- Then point an OpenAI-compatible client at `http://127.0.0.1:11016/v1`. For Ollama native APIs, use `target.adapter: "ollama"` and call `/api/chat` or `/api/generate` through the proxy.
121
+ Then point an OpenAI-compatible client at `http://127.0.0.1:11016/v1`. For Ollama native APIs, use `target.adapter: "ollama"` and call `/api/chat` or `/api/generate` through the proxy. For Claude, set `target.type: "anthropic"` and call `/v1/messages` (or `/v1/messages/count_tokens`, `/v1/complete`); the client's `x-api-key`/`anthropic-version` headers are forwarded to the upstream unchanged. For Gemini, set `target.type: "gemini"` and call the model-in-path endpoints `/v1beta/models/{model}:generateContent` (or `:streamGenerateContent`, `:countTokens`, `:embedContent`, `:batchEmbedContents`); the client's `x-goog-api-key` (or `?key=`) is forwarded to the upstream unchanged.
100
122
 
101
123
  ## Token Round-Trip
102
124
 
@@ -173,14 +195,27 @@ haechi auth revoke <id>
173
195
  - **Rate limit**: per-identity requests-per-minute → `429` (in-memory, per-process).
174
196
  - Audit events carry the **PII-safe** `identity` (keyed-HMAC subject/issuer, never raw values) and the resolved `profile`; `auth_denied` / `model_not_allowed` / `rate_limited` decisions never include credentials. `/__haechi/health` stays unauthenticated.
175
197
 
176
- JWT/JWKS auth and KMS-backed key custody ship as `haechi-*` satellite packages, each versioned and published independently of core. They remain pre-1.0 and declare a `haechi` peer range of `>=0.8.0 <2.0.0` (the upper bound tracks the core major, so core 1.0.0 does not break satellite installs):
198
+ JWT/JWKS auth and KMS-backed key custody (and other optional capabilities) ship as the **`haechi-*` satellite packages** see [Satellite packages](#satellite-packages) below.
199
+
200
+ ## Satellite packages
201
+
202
+ Optional capabilities ship as independently-published **`haechi-*` packages on npm** — each versioned separately from core, `node:`-only by default (heavy SDKs like a KMS or Redis client are optional peers), and each declaring a `haechi` peer range of `>=0.8.0 <2.0.0` (the upper bound tracks the core major, so a core minor never breaks a satellite install).
203
+
204
+ **Install the core alongside any satellite** — `haechi` is a **peer dependency, not bundled**, so a satellite does nothing on its own:
205
+
206
+ ```bash
207
+ npm install haechi haechi-<satellite>
208
+ ```
177
209
 
178
- - [`haechi-auth-jwt`](satellites/auth-jwt/) (0.2.1) headless JWKS bearer verification; additively exports a reusable JWS verifier (`createJwtVerifier`).
179
- - [`haechi-crypto-kms`](satellites/crypto-kms/) (0.2.1) — envelope encryption with a real KMS client; AWS plus GCP (`./gcp`), Azure (`./azure`), and HashiCorp Vault Transit (`./vault`, `node:`-only) backends.
180
- - [`haechi-dashboard`](satellites/dashboard/) (0.1.2) a zero-dependency, read-only audit viewer (`node:http`) over the audit log and its hash-chain status.
181
- - [`haechi-auth-oidc`](satellites/auth-oidc/) (0.1.2) an interactive OIDC session broker (authorization-code + PKCE) that provides the dashboard's human login.
210
+ | Package | What it adds |
211
+ |---|---|
212
+ | [`haechi-auth-jwt`](satellites/auth-jwt/) | Headless JWKS bearer (JWT) `authProvider`; additively exports a reusable JWS verifier (`createJwtVerifier`). |
213
+ | [`haechi-auth-oidc`](satellites/auth-oidc/) | Interactive OIDC session broker (authorization-code + PKCE) the dashboard's human login. Reuses `haechi-auth-jwt`. |
214
+ | [`haechi-crypto-kms`](satellites/crypto-kms/) | Envelope-encryption `cryptoProvider` for `keys.provider: external` — AWS, GCP (`./gcp`), Azure (`./azure`), and HashiCorp Vault Transit (`./vault`, `node:`-only) backends. |
215
+ | [`haechi-dashboard`](satellites/dashboard/) | Zero-dependency, read-only audit viewer (`node:http`) over the audit log and its hash-chain status. |
216
+ | [`haechi-ratelimit-redis`](satellites/ratelimit-redis/) | Shared-store (Redis-backed) `rateLimiter` for multi-replica deployments, via the `providers.rateLimiter` injection seam. |
182
217
 
183
- The satellites are `node:`-only by default (heavy SDKs are optional peers) and keep core zero-dependency.
218
+ Each package's README covers its usage and exact peer requirements. The satellites keep core zero-dependency: their heavy SDKs are optional peers installed only when you use that backend.
184
219
 
185
220
  ## Configuration
186
221
 
@@ -189,7 +224,7 @@ The satellites are `node:`-only by default (heavy SDKs are optional peers) and k
189
224
  | Key | Default | Meaning |
190
225
  |---|---|---|
191
226
  | `mode` / `policy.mode` | `dry-run` | `dry-run` and `report-only` detect + audit only; `enforce` transforms/blocks. `policy.mode` wins over `mode` |
192
- | `target.type` / `target.adapter` | `llm-http` / `openai-compatible` | Upstream protocol: `openai-compatible`, `vllm-openai`, `ollama`, `llama-cpp`. Unknown types fail closed |
227
+ | `target.type` / `target.adapter` | `llm-http` / `openai-compatible` | Upstream protocol: `openai-compatible`, `vllm-openai`, `ollama`, `llama-cpp`, `anthropic`, `gemini`. Unknown types fail closed |
193
228
  | `target.upstream` | `http://127.0.0.1:9999` | The only upstream the proxy will forward to (absolute-URL request targets are rejected) |
194
229
  | `proxy.host` / `proxy.port` | `127.0.0.1` / `11016` | Proxy bind address. See remote binding below |
195
230
  | `responseProtection.enabled` | `false` | Inspect upstream JSON responses. `failureMode: fail-closed` rejects non-JSON/compressed/oversized responses |
package/SECURITY.md CHANGED
@@ -12,7 +12,13 @@ Only the current `1.x` stable line is considered in scope. From 1.0 the public A
12
12
 
13
13
  ## Reporting
14
14
 
15
- Report suspected vulnerabilities privately to the repository maintainer. Do not include real secrets, production prompts, customer data, or personal information in reports.
15
+ **Preferred channel: GitHub private vulnerability reporting.** Open a private report through the repository's Security Advisories at <https://github.com/raeseoklee/haechi/security/advisories> (the "Report a vulnerability" button). This keeps the report confidential until a fix is coordinated. The machine-readable disclosure metadata is published at [`/.well-known/security.txt`](.well-known/security.txt) (RFC 9116; mirrored at the repo root).
16
+
17
+ Do not include real secrets, production prompts, customer data, or personal information in reports — describe the issue and a minimal, sanitized reproduction instead.
18
+
19
+ **Triage target (best-effort, not an SLA for a pre-1.0/OSS project):** we aim to **acknowledge a report within 3 business days** and to share an initial assessment (accepted / needs-info / out-of-scope) within **10 business days**. A disclosed, in-scope vulnerability is eligible for an in-minor security fix under the `1.x` stability exception (`docs/current/api-stability.md`).
20
+
21
+ A control mapping (OWASP LLM Top 10 2025 / NIST AI RMF) and a structured self-pentest are documented in [`docs/current/security-whitepaper.md`](docs/current/security-whitepaper.md). This is a self-assessment, not an independent audit or certification.
16
22
 
17
23
  ## Security Invariants
18
24
 
package/docs/README.md CHANGED
@@ -21,6 +21,8 @@ English is the primary documentation language. Korean translations are maintaine
21
21
  - `docs/current/configuration.md`: full configuration reference (every key, defaults, validation, presets, common setups)
22
22
  - `docs/current/risk-register-release-gate.md`: release-blocking risks, security/operational risk status, npm release gates (0.3.2 baseline)
23
23
  - `docs/current/threat-model.md`: Haechi trust boundaries, protected assets, key threats and controls
24
+ - `docs/current/security-whitepaper.md`: shipped-control mapping to OWASP LLM Top 10 (2025) + NIST AI RMF, with a structured self-pentest (a self-assessment, not a certification)
25
+ - `docs/current/compliance-mapping.md`: control-to-obligation-category mapping + DSAR/retention operational workflow (a mapping, not a certification)
24
26
  - `docs/current/shared-responsibility.md`: responsibility split between Haechi and users/operators in self-hosted deployments
25
27
  - `docs/current/api-stability.md`: developer preview API stability and migration note criteria
26
28
  - `docs/current/release-process.md`: release preflight, SBOM, npm provenance publish procedure
@@ -0,0 +1,53 @@
1
+ # 컴플라이언스 통제 매핑 & DSAR/Retention 워크플로
2
+
3
+ - 문서 상태: Living document (WS6 — reliability-hardening-track §WS6)
4
+ - 이 문서의 성격: **통제 매핑**이며 컴플라이언스 **인증**이 아닙니다. reliability-hardening-track §5는 인증을 명시적 비목표로 두며, `SECURITY.md` Scope는 이 저장소가 컴플라이언스 인증·법률 의견·보증 보고서가 아님을 밝힙니다. 이 문서는 Haechi 통제를, 운영자가 충족하도록 돕는 *의무 범주*에 매핑합니다 — Haechi를 배포하면 어떤 규제를 준수하게 된다고 주장하지 않습니다.
5
+
6
+ ## 0. 읽는 법
7
+
8
+ 규제 의무(예: "데이터 최소화", "접근 로깅", "정보주체 권리")는 *프로그램* — 사람·프로세스·기술 — 으로 충족됩니다. Haechi는 운영자가 그 프로그램의 LLM/MCP 게이트웨이 경계에 배선하는 하나의 **기술 통제**입니다. 아래에서는 각 의무 **범주**를 이를 지원하는 Haechi 통제에 매핑하며, Haechi가 하는 일과 하지 않는 일의 경계를 기록합니다. 권위 있는 통제 정의는 코드와 `docs/current/threat-model.md`에 있으며, 이 문서는 그것을 매핑할 뿐 재서술하지 않습니다.
9
+
10
+ ## 1. 통제 → 의무 범주 매핑
11
+
12
+ | 의무 범주 | Haechi 통제 | Haechi의 기여 | 운영자가 여전히 소유 |
13
+ |---|---|---|---|
14
+ | **데이터 최소화** | 탐지 + redact/mask/tokenize/encrypt/block 파이프라인(`packages/core`, `packages/filter`, `packages/policy`); privacy profile(`kr-pipa`/`eu-gdpr`/`us-general`) | PII/비밀이 모델·도구·로그에 도달하기 전에 제거하거나 가명화하여 최소 필요 데이터만 하류로 흐르게 합니다. tokenization은 값을 vault에만 보관되는 가역 참조로 대체합니다. | 적법 근거 정책, 지역 profile, 어떤 필드가 실제로 필요한지의 선택. |
15
+ | **접근 로깅 / 감사성** | SHA-256 hash chain + head anchoring을 가진 audit JSONL(`packages/audit`); request별 `correlationId`; PII 없는 이벤트(`FORBIDDEN_KEYS`) | *어떤 범주*가 탐지되었고, *어떤 action*이 집행되었으며, *어떤*(keyed-hashed) identity인지, *언제*인지를 — 민감 값 자체는 저장하지 않고 — 변조 증거와 함께 기록합니다. | append-only/불변 저장 매체, 로그 전송, retention 일정(§3). |
16
+ | **목적 제한 / 접근 통제** | body 읽기 전 auth gate; named policy profile; model allowlist; identity별 rate limit(`packages/proxy`, `packages/auth`) | 누가 게이트웨이를 사용할 수 있고 각 identity가 어떤 모델/연산/쿼터를 받는지를, payload를 읽기 전에 제약합니다. | identity 수명주기, token 발급/폐기 정책, 게이트웨이 너머의 인가 모델. |
17
+ | **보관 제한 / retention** | token-vault retention(`tokenVault.retentionDays`, mutation 시 만료 정리); chain-aware audit rotation/retention 절차(`operations-runbook.md` §6) | bounded token 수명과, hash-chain을 보존하는 문서화된 audit rotation/retention 절차. | 법적 요구에 따른 retention 윈도 설정과 rotation 일정 운영. |
18
+ | **정보주체 권리(열람 / 삭제)** | token-vault reveal 거버넌스(`revealPolicy`) + purge, 둘 다 token id 기준 audit; §2의 DSAR 워크플로 | reveal/purge 프리미티브와 그 거버넌스/audit이 DSAR 대응의 기술적 빌딩 블록입니다(§2 참고). | 각 요청의 법적 접수, 신원 확인, 결정, 기록 보존. |
19
+ | **전송 중 기밀성** | proxy TLS / remote-bind 강화(`proxy.tls` / `proxy.trustForwardedProto`); 기본 loopback(`packages/proxy`) | remote bind는 bearer token + payload를 평문으로 제공할 수 없습니다 — TLS를 종단하거나 검증된 `X-Forwarded-Proto: https` hop 뒤에 있어야 하며, 아니면 기동 시 fail-closed. | 인증서 발급/회전과 네트워크 경계. |
20
+ | **무결성 & 변조 증거** | audit hash chain + anchoring; canonical-AAD 결합 암호화(`packages/crypto`); 강화-전용 정책(`ACTION_STRENGTH`) | 변조 증거 audit, AEAD 결합 ciphertext, 조용히 약화될 수 없는 정책 격자. | 키 보관(프로덕션 KMS/HSM은 주입된 `cryptoProvider`이며 절대 코어 아님)과 사고 대응. |
21
+ | **처리의 안전성 / 복원력** | fail-closed 집행; depth/byte/encoding 가드; readiness(`/__haechi/ready`) + backpressure(`packages/proxy`, `packages/core`) | 인라인 집행과 fail-closed 가용성 통제가 미보호 payload나 unbounded-consumption 사건의 가능성을 줄입니다. | 용량 계획, 모니터링, 더 넓은 보안 프로그램. |
22
+
23
+ ## 2. DSAR / retention 운영 워크플로
24
+
25
+ **정보주체 열람/삭제 요청(DSAR)**은 법적/프로세스 워크플로이며, Haechi는 그것이 귀결되는 기술 연산을 제공합니다. 아래 흐름은 요청을 구체적 Haechi 프리미티브에 매핑합니다. **모든 reveal/purge 연산은 token id 기준으로 audit되며(평문 아님)**, `tokenVault.revealPolicy`로 거버넌스됩니다.
26
+
27
+ ### 2.1 열람 요청 (정보주체가 "내 데이터를 무엇을 보유/처리하는가?"라고 물을 때)
28
+ 1. **위치 파악.** audit 로그로 정보주체에 관련된 이벤트를 찾습니다 — keyed-HMAC `subjectHash`(audit는 원문 subject를 저장하지 않음), `correlationId`, 시간 윈도, 탐지 summary로 매칭합니다. audit는 *어떤 범주*가 처리되고 *어떤 action*이 취해졌는지를 값 없이 알려줍니다.
29
+ 2. **거버넌스될 때에 한해 token 해석.** 값이 **tokenize**되었다면 가역 참조가 token vault에 있습니다. reveal은 `tokenVault.revealPolicy`로 게이트됩니다:
30
+ - `disabled`(기본): reveal 거부. 이것이 프로덕션 안전 자세입니다 — DSAR 열람 응답은 live reveal이 아니라 audit 메타데이터 + 운영자의 upstream 기록으로 구성합니다.
31
+ - `local-dev`: 명시적 로컬 개발 워크플로에서만 reveal 허용(`haechi token-reveal <token> --allow-dev-reveal`). `--allow-dev-reveal`을 프로덕션 DSAR 절차로 **사용하지 마십시오**(`shared-responsibility.md` §2 참고).
32
+ 모든 reveal 결정은 token id 기준으로 audit 로그에 기록됩니다.
33
+ 3. **응답.** 법적/프로세스 채널을 통해 응답합니다. Haechi는 기술 증거를 제공하고, 운영자가 신원 확인과 응답을 소유합니다.
34
+
35
+ ### 2.2 삭제 요청 (정보주체가 "내 데이터를 삭제하라"고 할 때)
36
+ 1. **token 매핑 purge.** `haechi token-purge`가 vault 매핑을 제거해 tokenize된 값을 더 이상 reveal할 수 없게 합니다; 만료된 token도 vault mutation 시 자동 정리됩니다. purge 결정은 token id 기준으로 audit됩니다.
37
+ 2. **retention 윈도 밖의 audit 세그먼트 만료.** `operations-runbook.md` §6에 따라 audit 로그는 세그먼트로 rotation되며, retention은 **세그먼트 전체를 만료**합니다(부분 라인은 hash chain을 깨므로 절대 아님). audit는 의도적으로 **평문 PII를 보유하지 않으므로** — *내용*에 대한 삭제 의무는 대체로 upstream/운영자 저장소에서 충족되며, audit는 keyed-hashed 식별자와 범주 메타데이터만 보유합니다.
38
+ 3. **upstream 복사본 삭제.** 모델 제공자의 로그, 애플리케이션 DB, 백업은 **Haechi 밖**입니다 — 운영자가 자신의 데이터 맵에 따라 삭제해야 합니다.
39
+
40
+ ### 2.3 retention 운영 (상시)
41
+ - **token vault:** `tokenVault.retentionDays` 설정; 만료는 vault mutation 시 정리됩니다.
42
+ - **audit 로그:** `operations-runbook.md` §6의 chain-aware rotation 운영 — 유지보수 경계에서 rotation하고, 각 rotation된 세그먼트 **와 그 anchor**를 retention 윈도 동안 보관해 이력이 여전히 검증되게 한 뒤, 세그먼트 전체를 만료합니다. token-vault retention과 audit retention은 독립이며, audit rotation이 token을 purge하지 않습니다.
43
+
44
+ ## 3. 경계 & 비목표 (정직하게)
45
+ - 이것은 **매핑**이며, 인증이나 법률 자문이 아닙니다. Haechi 배포가 시스템을 "GDPR/PIPA 등 준수"로 만들지 않습니다.
46
+ - Haechi는 **게이트웨이 경계**만 통제합니다. 모델 제공자의 retention, 애플리케이션 저장소, 백업은 운영자 책임입니다(`shared-responsibility.md`).
47
+ - 탐지는 regex + validator(ML 없음)이며 문서화된 제외는 유효합니다(`threat-model.md` §4). DSAR/삭제 프로그램은 Haechi가 어떤 값의 *모든* 인스턴스를 잡았다고 가정해서는 안 됩니다.
48
+
49
+ ## 4. 상호 참조
50
+ - `docs/current/shared-responsibility.md` — Haechi 대 운영자 책임 매트릭스(DSAR/retention 구분이 거기에 명시됨).
51
+ - `docs/current/operations-runbook.md` — §6 chain-aware audit rotation & retention.
52
+ - `docs/current/security-whitepaper.md` — OWASP-LLM / NIST-AI-RMF 통제 매핑 + self-pentest.
53
+ - `docs/current/threat-model.md` — 제외 항목과 수용된 잔여 위험.
@@ -0,0 +1,53 @@
1
+ # Compliance Control Mapping & DSAR/Retention Workflow
2
+
3
+ - Status: Living document (WS6 — reliability-hardening-track §WS6)
4
+ - Nature of this document: a **control mapping**, NOT a compliance **certification**. The reliability-hardening-track §5 lists a certification as an explicit non-goal, and `SECURITY.md` Scope states this repository is not a compliance certification, legal opinion, or assurance report. This document maps Haechi controls to the *obligation categories* they help an operator satisfy — it does not assert that deploying Haechi makes a system compliant with any regulation.
5
+
6
+ ## 0. How to read this
7
+
8
+ A regulatory obligation (e.g. "data minimization", "access logging", "subject rights") is satisfied by a *program* — people, process, and technology. Haechi is one **technical control** an operator wires into that program at the LLM/MCP gateway boundary. Below, each obligation **category** is mapped to the Haechi control(s) that support it, with the boundary of what Haechi does and does not do. The authoritative control definitions are in the code and `docs/current/threat-model.md`; this maps them, it does not restate them.
9
+
10
+ ## 1. Control → obligation-category mapping
11
+
12
+ | Obligation category | Haechi control(s) | What Haechi contributes | Operator still owns |
13
+ |---|---|---|---|
14
+ | **Data minimization** | Detection + redact/mask/tokenize/encrypt/block pipeline (`packages/core`, `packages/filter`, `packages/policy`); privacy profiles (`kr-pipa`/`eu-gdpr`/`us-general`) | Strips or pseudonymizes PII/secrets before they reach the model, tools, or logs — so the minimum necessary data flows downstream. Tokenization replaces a value with a reversible reference held only in the vault. | Choosing the lawful-basis policy, the regional profile, and which fields are truly necessary. |
15
+ | **Access logging / auditability** | Audit JSONL with SHA-256 hash chain + head anchoring (`packages/audit`); per-request `correlationId`; PII-free events (`FORBIDDEN_KEYS`) | A tamper-evident record of *what category* was detected, *what action* was enforced, *which* (keyed-hashed) identity, and *when* — without storing the sensitive value itself. | Append-only/immutable storage media, log shipping, and the retention schedule (see §3). |
16
+ | **Purpose limitation / access control** | Auth gate before body-read; named policy profiles; model allowlist; per-identity rate limit (`packages/proxy`, `packages/auth`) | Constrains who may use the gateway and which models/operations/quotas each identity gets, enforced before any payload is read. | Identity lifecycle, token issuance/revocation policy, and the authorization model beyond the gateway. |
17
+ | **Storage limitation / retention** | Token-vault retention (`tokenVault.retentionDays`, expiry pruned on mutation); chain-aware audit rotation/retention procedure (`operations-runbook.md` §6) | Bounded token lifetime and a documented, hash-chain-preserving rotation/retention procedure for the audit log. | Setting the retention window per legal requirement and operating the rotation schedule. |
18
+ | **Subject rights (access / erasure)** | Token-vault reveal governance (`revealPolicy`) + purge, both audited by token id; the DSAR workflow in §2 | The reveal/purge primitives and their governance/audit are the technical building blocks of a DSAR response (see §2). | The legal intake, identity verification, decision, and recordkeeping of each request. |
19
+ | **Confidentiality in transit** | Proxy TLS / remote-bind hardening (`proxy.tls` / `proxy.trustForwardedProto`); loopback-by-default (`packages/proxy`) | A remote bind cannot serve bearer tokens + payloads in plaintext — it must terminate TLS or sit behind a verified `X-Forwarded-Proto: https` hop, else it fails closed at startup. | Certificate issuance/rotation and the network perimeter. |
20
+ | **Integrity & tamper evidence** | Audit hash chain + anchoring; canonical-AAD-bound encryption (`packages/crypto`); policies-only-get-stronger (`ACTION_STRENGTH`) | Tamper-evident audit, AEAD-bound ciphertext, and a policy lattice that cannot silently weaken. | Key custody (a production KMS/HSM is an injected `cryptoProvider`, never core), and incident response. |
21
+ | **Security of processing / resilience** | Fail-closed enforcement; depth/byte/encoding guards; readiness (`/__haechi/ready`) + backpressure (`packages/proxy`, `packages/core`) | Inline enforcement and fail-closed availability controls reduce the chance of an unprotected payload or an unbounded-consumption event. | Capacity planning, monitoring, and the broader security program. |
22
+
23
+ ## 2. DSAR / retention operational workflow
24
+
25
+ A **Data Subject Access/erasure Request (DSAR)** is a legal/process workflow; Haechi provides the technical operations it bottoms out on. The flow below maps a request to concrete Haechi primitives. **All reveal/purge operations are audited by token id (never plaintext)** and are governed by `tokenVault.revealPolicy`.
26
+
27
+ ### 2.1 Access request (the subject asks "what data of mine do you hold / process?")
28
+ 1. **Locate.** Use the audit log to find the events touching the subject — match on the keyed-HMAC `subjectHash` (the audit never stores a raw subject), the `correlationId`, the time window, and the detection summary. The audit tells you *that* a category was processed and *which action* was taken, without the value.
29
+ 2. **Resolve tokens, if and only if governed.** If a value was **tokenized**, the reversible reference lives in the token vault. Revealing it is gated by `tokenVault.revealPolicy`:
30
+ - `disabled` (the default): reveal is refused. This is the production-safe posture — a DSAR access response is assembled from the audit metadata + the operator's upstream records, not from a live reveal.
31
+ - `local-dev`: reveal is permitted only for explicit local-development workflows (`haechi token-reveal <token> --allow-dev-reveal`). **Do not** use `--allow-dev-reveal` as a production DSAR procedure (see `shared-responsibility.md` §2).
32
+ Every reveal decision is written to the audit log by token id.
33
+ 3. **Respond** through your legal/process channel. Haechi supplies the technical evidence; the operator owns identity verification and the response.
34
+
35
+ ### 2.2 Erasure request (the subject asks "delete my data")
36
+ 1. **Purge the token mapping.** `haechi token-purge` removes the vault mapping so the tokenized value can no longer be revealed; expired tokens are also pruned automatically on vault mutations. The purge decision is audited by token id.
37
+ 2. **Expire the audit segments** that fall outside the retention window. Per `operations-runbook.md` §6, the audit log rotates into segments; retention **expires whole segments** (never partial lines, which would break the hash chain). The audit deliberately holds **no plaintext PII** — so an erasure obligation against the *content* is largely satisfied by the upstream/operator store, while the audit holds only keyed-hashed identifiers and category metadata.
38
+ 3. **Erase upstream copies.** The model provider's logs, your application database, and any backups are **outside Haechi** — the operator must erase those per their own data map.
39
+
40
+ ### 2.3 Retention operation (ongoing)
41
+ - **Token vault:** set `tokenVault.retentionDays`; expiry is pruned on vault mutations.
42
+ - **Audit log:** operate the chain-aware rotation in `operations-runbook.md` §6 — rotate at a maintenance boundary, keep each rotated segment **and its anchor** for the retention window so the history still verifies, then expire whole segments. Token-vault retention and audit retention are independent; rotating the audit does not purge tokens.
43
+
44
+ ## 3. Boundaries & non-goals (honest)
45
+ - This is a **mapping**, not a certification or legal advice. Deploying Haechi does not make a system "GDPR/PIPA/etc. compliant."
46
+ - Haechi controls the **gateway boundary** only. The model provider's retention, your application store, and backups are the operator's responsibility (`shared-responsibility.md`).
47
+ - Detection is regex + validators (no ML); documented exclusions stand (`threat-model.md` §4). A DSAR/erasure program must not assume Haechi caught *every* instance of a value.
48
+
49
+ ## 4. Cross-references
50
+ - `docs/current/shared-responsibility.md` — the Haechi-vs-operator responsibility matrix (the DSAR/retention split is called out there).
51
+ - `docs/current/operations-runbook.md` — §6 chain-aware audit rotation & retention.
52
+ - `docs/current/security-whitepaper.md` — the OWASP-LLM / NIST-AI-RMF control mapping + self-pentest.
53
+ - `docs/current/threat-model.md` — exclusions and accepted residuals.
@@ -0,0 +1,30 @@
1
+ # Haechi `configVersion` & 업그레이드 노트
2
+
3
+ - 상태: Living document (코어 1.3.x 추적)
4
+
5
+ `configVersion`는 `haechi.config.json`(및 `haechi.config.example.json`) 최상위에 찍히는 단일 정수입니다. 향후 호환성을 깨는 설정 스키마 변경이 구체적으로 게이트할 수 있는 **버전 앵커**로서, 다른 Haechi 빌드가 쓴 설정을 조용히 잘못 읽는 일을 막습니다.
6
+
7
+ ## 동작
8
+
9
+ - **기본값 / 없음:** `configVersion`를 생략한 설정(예: 스탬프가 생기기 전의 1.1 파일)은 **현재** 버전으로 간주합니다. 필드 추가는 기존 설정에 아무 영향이 없었습니다.
10
+ - **현재 버전:** `1`.
11
+ - **더 높은/알 수 없는 값은 fail-closed:** 빌드가 이해하는 값보다 **큰** `configVersion`는 로드 시 throw합니다 — *더 새로운* Haechi가 쓴 설정은 이 빌드가 구현하지 않은 의미에 의존할 수 있으므로, 추측 대신 거부합니다. Haechi를 업그레이드하거나, 호환성을 확인한 뒤 스탬프를 낮추십시오.
12
+ - **형식이 잘못되면 fail-closed:** 양수 정수가 아닌 `configVersion`는 throw합니다(`configVersion must be a positive integer`).
13
+
14
+ 이는 `normalizeConfig`의 나머지와 동일한 fail-closed 자세입니다: 모호하거나 미래 시점의 설정은 게이트웨이를 약화시키는 대신 멈춥니다.
15
+
16
+ ## 더 높은 버전에 fail-closed인 이유
17
+
18
+ 알 수 없는 설정을 조용히 실행하는 보안 게이트웨이는, 예를 들어 인식하지 못한 미래의 집행 키를 무시하고 운영자 의도보다 약하게 동작할 수 있습니다. 기동을 거부하면 불일치가 즉시 드러나며 "정책은 더 강해질 뿐 / fail closed" 불변식이 유지됩니다.
19
+
20
+ ## 버전 맵
21
+
22
+ | `configVersion` | 코어 라인 | 노트 |
23
+ |---|---|---|
24
+ | `1` | 1.0 – 1.3.x | 최초 스탬프. 모든 키는 1.0 frozen 설정 표면(`api-stability.md` §2.4)에 대해 additive입니다. 1.1.x의 additive 키(`logging`, `metrics`, WS4-B의 `limits.maxInFlight` / `limits.shutdownGraceMs` / `limits.requestTimeoutMs` / `limits.headersTimeoutMs`, 그리고 `configVersion` 자체)와 1.2.0 신뢰성 강화 키(`filters.minConfidence` / `filters.allowlist`, `proxy.tls` / `proxy.trustForwardedProto`)는 모두 이전 동작을 기본값으로 합니다. 1.3.0의 추가는 새 키가 아니라 새 *값*입니다 — `target.type`의 `anthropic`/`gemini`, 추가 탐지 타입, `asia-pdpa`/`jp-appi` `privacy.profile` 값 — 따라서 설정 스키마(및 `configVersion`)는 변경되지 않습니다. 마이그레이션 불필요. |
25
+
26
+ ## 업그레이드
27
+
28
+ 향후 마이너가 설정 키를 추가할 때, 그 키들은 **additive**(이전 동작 기본값)로 유지되고 `configVersion`는 `1`에 머뭅니다 — 조치 불필요. `configVersion`는 의도적인 호환성 파괴 스키마 변경과 함께서만 **올라가며**, 그 변경은 `api-stability.md` §2.2에 따라 메이저 버전 상승과 deprecation 노트도 동반합니다. 그 시점에 이 표에 마이그레이션을 설명하는 행이 추가되며, 더 낮은 버전으로 찍힌 설정은 조용히가 아니라 명시적으로 마이그레이션(또는 호환 규칙으로 읽기)됩니다.
29
+
30
+ 핀 고정: 설정 최상위에 `"configVersion": 1`을 설정하십시오(예제 설정은 이미 그렇게 합니다). 향후 스키마 상승을 넘어 Haechi를 업그레이드하려면, 스탬프를 올리기 전에 대상 버전의 마이그레이션 행을 따르십시오.
@@ -0,0 +1,51 @@
1
+ # Haechi `configVersion` & Upgrade Notes
2
+
3
+ - Status: Living document (tracks core 1.3.x)
4
+
5
+ `configVersion` is a single integer stamped at the top of `haechi.config.json`
6
+ (and `haechi.config.example.json`). It is a **versioned anchor** so a future
7
+ breaking config-schema change has something concrete to gate on, rather than
8
+ silently mis-reading a config written by a different Haechi build.
9
+
10
+ ## Behavior
11
+
12
+ - **Default / absent:** a config that omits `configVersion` (e.g. a 1.1 file
13
+ written before the stamp existed) is treated as the **current** version. Adding
14
+ the field changed nothing for existing configs.
15
+ - **Current version:** `1`.
16
+ - **Fail-closed on newer/unknown:** a `configVersion` **greater** than the build
17
+ understands throws at load — a config a *newer* Haechi wrote may rely on
18
+ semantics this build does not implement, so Haechi refuses rather than guessing.
19
+ Upgrade Haechi, or lower the stamp once you have confirmed compatibility.
20
+ - **Fail-closed on malformed:** a non-positive or non-integer `configVersion`
21
+ throws (`configVersion must be a positive integer`).
22
+
23
+ This is the same fail-closed posture as the rest of `normalizeConfig`: an
24
+ ambiguous or forward-dated config stops the gateway rather than degrading it.
25
+
26
+ ## Why fail-closed on a newer version
27
+
28
+ A security gateway that silently runs an unfamiliar config could, for example,
29
+ ignore a future enforcement key it does not recognize and run weaker than the
30
+ operator intended. Refusing to start surfaces the mismatch immediately and keeps
31
+ the "policies only get stronger / fail closed" invariant intact.
32
+
33
+ ## Version map
34
+
35
+ | `configVersion` | Core line | Notes |
36
+ |---|---|---|
37
+ | `1` | 1.0 – 1.3.x | Initial stamp. All keys are additive over the 1.0 frozen config surface (`api-stability.md` §2.4). The 1.1.x additive keys (`logging`, `metrics`, the WS4-B `limits.maxInFlight` / `limits.shutdownGraceMs` / `limits.requestTimeoutMs` / `limits.headersTimeoutMs`, `configVersion` itself) and the 1.2.0 Reliability-Hardening keys (`filters.minConfidence` / `filters.allowlist`, `proxy.tls` / `proxy.trustForwardedProto`) all default to prior behavior. The 1.3.0 additions are new *values*, not new keys — `target.type` `anthropic`/`gemini`, additional detection types, and the `asia-pdpa`/`jp-appi` `privacy.profile` values — so the config schema (and `configVersion`) is unchanged. No migration needed. |
38
+
39
+ ## Upgrading
40
+
41
+ When a future minor adds config keys, they remain **additive** (default to prior
42
+ behavior) and `configVersion` stays `1` — no action required. `configVersion`
43
+ will only be **bumped** alongside a deliberate breaking schema change, which would
44
+ also carry a major version bump and a deprecation note per `api-stability.md`
45
+ §2.2. At that point this table gains a row describing the migration, and a config
46
+ stamped with the older version is migrated (or read under compatibility rules)
47
+ explicitly — never silently.
48
+
49
+ To pin: set `"configVersion": 1` at the top of your config (the example config
50
+ already does). To upgrade Haechi past a future schema bump, follow the migration
51
+ row for the target version before raising the stamp.