haechi 0.6.0 → 0.8.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.
- package/README.ko.md +8 -1
- package/README.md +8 -1
- package/docs/README.md +1 -0
- package/docs/current/api-stability.ko.md +14 -2
- package/docs/current/api-stability.md +14 -2
- package/docs/current/release-0.6-implementation-scope.ko.md +4 -4
- package/docs/current/release-0.6-implementation-scope.md +4 -4
- package/docs/current/release-0.7-implementation-scope.ko.md +91 -0
- package/docs/current/release-0.7-implementation-scope.md +92 -0
- package/docs/current/release-0.8-implementation-scope.ko.md +145 -0
- package/docs/current/release-0.8-implementation-scope.md +145 -0
- package/docs/current/release-process.ko.md +60 -6
- package/docs/current/release-process.md +60 -6
- package/docs/current/risk-register-release-gate.ko.md +4 -2
- package/docs/current/risk-register-release-gate.md +4 -3
- package/docs/current/threat-model.ko.md +5 -2
- package/docs/current/threat-model.md +5 -2
- package/examples/crypto-kms-reference/README.md +13 -0
- package/haechi.config.example.json +6 -1
- package/package.json +9 -1
- package/packages/audit/index.mjs +99 -6
- package/packages/auth/index.mjs +45 -0
- package/packages/cli/bin/haechi.mjs +40 -8
- package/packages/cli/runtime.mjs +33 -3
- package/packages/crypto/index.mjs +107 -0
- package/scripts/release-checksums.mjs +100 -0
package/README.ko.md
CHANGED
|
@@ -167,7 +167,7 @@ haechi auth revoke <id>
|
|
|
167
167
|
- **Rate limit**: identity별 분당 요청 수 → `429` (인메모리, 프로세스별).
|
|
168
168
|
- Audit 이벤트는 **PII-safe** `identity`(keyed-HMAC subject/issuer, 원시 값 아님)와 resolve된 `profile`을 포함하며, `auth_denied` / `model_not_allowed` / `rate_limited` 결정에는 credentials가 포함되지 않는다. `/__haechi/health`는 인증 없이 접근 가능하다.
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
JWT/JWKS 인증과 KMS 기반 key custody는 `haechi-*` 위성 패키지로 제공된다(0.8): [`haechi-auth-jwt`](satellites/auth-jwt/)(헤드리스 JWKS bearer 검증)와 [`haechi-crypto-kms`](satellites/crypto-kms/)(실제 AWS KMS 클라이언트 기반 envelope 암호화). 둘 다 기본 `node:` 전용이며 core를 zero-dependency로 유지한다; 대화형 OIDC와 대시보드는 0.9.
|
|
171
171
|
|
|
172
172
|
## 설정
|
|
173
173
|
|
|
@@ -243,6 +243,9 @@ Haechi는 로컬 정책 부트스트래핑을 위한 기본 지역별 Privacy Pr
|
|
|
243
243
|
- `haechi init --force`는 로컬 키를 교체한다: 기존 키는 `retired` 상태로 보관되어 기존 암호문과 token vault 레코드를 `kid`로 복호화할 수 있다.
|
|
244
244
|
- Privacy profile은 명시적으로 더 엄격한 사용자 action을 강화할 수는 있지만 약화할 수는 없다.
|
|
245
245
|
- 탐지는 문자열 값, JSON 숫자(예: 카드 번호), 객체 키 이름을 검사한다. Base64/URL 인코딩된 값과 URL 쿼리 스트링은 검사되지 않는다.
|
|
246
|
+
- Audit tail truncation: `audit.anchor.mode: file`을 설정하면(추가 전용/별도 미디어에서) `haechi audit-verify --anchor`가 마지막 anchor 이후 꼬리 레코드 삭제를 탐지한다. 동일한 쓰기 가능 파일시스템에서는 공격자가 두 파일을 함께 잘라낼 수 있다.
|
|
247
|
+
- Key custody: `keys.provider: external`은 주입된 `cryptoProvider`를 허용한다; `assertCryptoProviderConformance`로 adapter를 검증한다. envelope 암호화 KMS adapter는 `haechi-crypto-kms` satellite(`satellites/crypto-kms/`)가 제공한다.
|
|
248
|
+
- Release integrity: 배포된 tarball에는 npm provenance attestation이 포함되며, GitHub release asset에는 sigstore attestation과 `SHA256SUMS`가 추가된다(`gh attestation verify`와 `node scripts/release-checksums.mjs --check`로 검증한다).
|
|
246
249
|
- 이 패키지는 개발자 프리뷰이다. 인터넷에 노출된 운영 LLM 게이트웨이로 사용하지 않는다.
|
|
247
250
|
|
|
248
251
|
## 현재 범위
|
|
@@ -262,3 +265,7 @@ Haechi는 로컬 정책 부트스트래핑을 위한 기본 지역별 Privacy Pr
|
|
|
262
265
|
0.5.0은 SSE/NDJSON 스트리밍 응답 검사를 추가한다: `streaming.requestMode: "inspect"`가 bounded sliding buffer로 응답을 stream-filter하여 프레임에 걸쳐 쪼개진 PII도 잡는다(`streaming.maxMatchBytes`). `docs/current/release-0.5-implementation-scope.md` 참고.
|
|
263
266
|
|
|
264
267
|
0.6.0은 인증과 클라이언트별 통제를 추가한다: 해시 기반 token 저장소와 `haechi auth` CLI를 갖춘 내장 bearer auth, identity scope/label로 바인딩되는 named policy profile, model allowlisting, 그리고 identity별 rate limiting — audit 로그에는 PII-safe identity가 기록된다. `docs/current/release-0.6-implementation-scope.md` 참고.
|
|
268
|
+
|
|
269
|
+
0.7.0은 운영 강화(ops-hardening) 릴리스이다: 꼬리 절단을 탐지하는 audit head-hash anchoring(`audit.anchor`), `assertCryptoProviderConformance`와 reference KMS adapter를 포함한 강화된 외부 `cryptoProvider` 계약, 그리고 서명/체크섬된 GitHub release artifact. `docs/current/release-0.7-implementation-scope.md` 참고.
|
|
270
|
+
|
|
271
|
+
0.8.0은 `haechi-*` 에코시스템을 세운다: npm workspaces 모노레포(core는 unscoped `haechi` 유지, zero runtime dependency, 패킹 매니페스트 CI 게이트로 강제) + 첫 두 위성 — [`haechi-crypto-kms`](satellites/crypto-kms/)(실제 AWS KMS 클라이언트 기반 envelope 암호화; AWS SDK는 optional peer)와 [`haechi-auth-jwt`](satellites/auth-jwt/)(헤드리스 JWKS bearer 검증, `node:` 전용). 각각 자체 provenance + sigstore attest 워크플로로 독립 발행한다. `docs/current/release-0.8-implementation-scope.md` 참고.
|
package/README.md
CHANGED
|
@@ -167,7 +167,7 @@ haechi auth revoke <id>
|
|
|
167
167
|
- **Rate limit**: per-identity requests-per-minute → `429` (in-memory, per-process).
|
|
168
168
|
- 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.
|
|
169
169
|
|
|
170
|
-
|
|
170
|
+
JWT/JWKS auth and KMS-backed key custody ship as `haechi-*` satellite packages (0.8): [`haechi-auth-jwt`](satellites/auth-jwt/) (headless JWKS bearer verification) and [`haechi-crypto-kms`](satellites/crypto-kms/) (envelope encryption with a real AWS KMS client). Both are `node:`-only by default and keep core zero-dependency; interactive OIDC and a dashboard are 0.9.
|
|
171
171
|
|
|
172
172
|
## Configuration
|
|
173
173
|
|
|
@@ -243,6 +243,9 @@ Set `privacy.profile` in `haechi.config.json` to apply the profile's default act
|
|
|
243
243
|
- `haechi init --force` rotates the local key: prior keys are kept as `retired` so existing envelopes and token vault records stay decryptable by `kid`.
|
|
244
244
|
- Privacy profiles can strengthen but never weaken an explicitly stricter user action.
|
|
245
245
|
- Detection scans string values, JSON numbers (e.g. card numbers), and object key names. Base64/URL-encoded values and URL query strings are NOT inspected.
|
|
246
|
+
- Audit tail truncation: set `audit.anchor.mode: file` (on append-only/separate media) so `haechi audit-verify --anchor` detects deletion of trailing records back to the last anchor. On the same writable filesystem an attacker can truncate both files together.
|
|
247
|
+
- Key custody: `keys.provider: external` accepts an injected `cryptoProvider`; validate adapters with `assertCryptoProviderConformance`. The `haechi-crypto-kms` satellite (`satellites/crypto-kms/`) provides an envelope-encryption KMS adapter.
|
|
248
|
+
- Release integrity: published tarballs carry an npm provenance attestation; GitHub release assets add a sigstore attestation and `SHA256SUMS` (verify with `gh attestation verify` and `node scripts/release-checksums.mjs --check`).
|
|
246
249
|
- The package is a developer preview. Do not expose it as an internet-facing production LLM gateway.
|
|
247
250
|
|
|
248
251
|
## Current Scope
|
|
@@ -262,3 +265,7 @@ Set `privacy.profile` in `haechi.config.json` to apply the profile's default act
|
|
|
262
265
|
0.5.0 adds SSE/NDJSON streaming response inspection: `streaming.requestMode: "inspect"` stream-filters responses with a bounded sliding buffer that catches PII split across frames (`streaming.maxMatchBytes`). See `docs/current/release-0.5-implementation-scope.md`.
|
|
263
266
|
|
|
264
267
|
0.6.0 adds authentication and per-client controls: built-in bearer auth with a hashed token store and `haechi auth` CLI, named policy profiles bound by identity scope/label, model allowlisting, and per-identity rate limiting — with PII-safe identity in the audit log. See `docs/current/release-0.6-implementation-scope.md`.
|
|
268
|
+
|
|
269
|
+
0.7.0 is operational hardening: audit head-hash anchoring (`audit.anchor`) that detects tail truncation, a hardened external `cryptoProvider` contract with `assertCryptoProviderConformance` and a reference KMS adapter, and signed/checksummed GitHub release artifacts. See `docs/current/release-0.7-implementation-scope.md`.
|
|
270
|
+
|
|
271
|
+
0.8.0 stands up the `haechi-*` ecosystem: an npm workspaces monorepo (core stays the unscoped `haechi`, zero runtime dependency, gated by a packed-manifest CI check) plus the first two satellites — [`haechi-crypto-kms`](satellites/crypto-kms/) (envelope encryption with a real AWS KMS client; the AWS SDK is an optional peer) and [`haechi-auth-jwt`](satellites/auth-jwt/) (headless JWKS bearer verification, `node:`-only). Each publishes independently with its own provenance + sigstore-attested workflow. See `docs/current/release-0.8-implementation-scope.md`.
|
package/docs/README.md
CHANGED
|
@@ -17,6 +17,7 @@ English is the primary documentation language. Korean translations are maintaine
|
|
|
17
17
|
- `docs/current/release-0.4-implementation-scope.md`: 0.4 token round-trip, mcp-wrap, audit-verify/status, identity/authProvider contract reservation
|
|
18
18
|
- `docs/current/release-0.5-implementation-scope.md`: 0.5 SSE/NDJSON streaming response inspection with bounded cross-frame buffer
|
|
19
19
|
- `docs/current/release-0.6-implementation-scope.md`: 0.6 bearer auth, named policy profiles, model allowlist, rate limiting
|
|
20
|
+
- `docs/current/release-0.7-implementation-scope.md`: 0.7 audit anchoring, cryptoProvider contract + reference KMS adapter, signed release artifacts
|
|
20
21
|
- `docs/current/configuration.md`: full configuration reference (every key, defaults, validation, presets, common setups)
|
|
21
22
|
- `docs/current/risk-register-release-gate.md`: release-blocking risks, security/operational risk status, npm release gates (0.3.2 baseline)
|
|
22
23
|
- `docs/current/threat-model.md`: Haechi 0.3.2 trust boundaries, protected assets, key threats and controls
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
- 문서 상태: Draft 0.1
|
|
4
4
|
- 작성일: 2026-06-10
|
|
5
|
-
- 기준 버전: 0.
|
|
5
|
+
- 기준 버전: 0.7.0
|
|
6
6
|
|
|
7
7
|
## 1. 버전 해석
|
|
8
8
|
|
|
@@ -42,8 +42,11 @@
|
|
|
42
42
|
- `identity` audit 필드와 `authProvider` 계약 (0.4 예약, 0.6 구현 — 그 전까지 형태 변경 가능)
|
|
43
43
|
- `status` / `audit-verify` CLI 출력 형태
|
|
44
44
|
- `haechi/stream-filter` (`inspectResponseStream`, path helpers) 및 `createStreamProtector` (스트리밍 검사 내부 구현)
|
|
45
|
-
- `haechi/auth` (`createBearerAuthProvider`, token store, `buildIdentity`) 및 `authProvider` 계약
|
|
45
|
+
- `haechi/auth` (`createBearerAuthProvider`, token store, `buildIdentity`, `buildExternalIdentity`) 및 `authProvider` 계약
|
|
46
46
|
- `policy.profiles`/`policy.profileBinding`/`modelAllowlist`/`rate` 및 `identity`/`profile` audit 필드
|
|
47
|
+
- `assertCryptoProviderConformance` 및 강화된 cryptoProvider 계약 (envelope base shape + provider 확장)
|
|
48
|
+
- `audit.anchor` 설정 및 `verifyAuditChain(path, { anchorPath })`
|
|
49
|
+
- `scripts/release-checksums.mjs` (SHA256SUMS 생성/검증)
|
|
47
50
|
|
|
48
51
|
## 4. Migration note 기준
|
|
49
52
|
|
|
@@ -55,3 +58,12 @@
|
|
|
55
58
|
- audit event 필드 변경
|
|
56
59
|
- token format 변경
|
|
57
60
|
- plugin manifest schema 변경
|
|
61
|
+
|
|
62
|
+
## 5. Satellite 패키지 (`haechi-*`)
|
|
63
|
+
|
|
64
|
+
위성(예: `haechi-crypto-kms`, `haechi-auth-jwt`)은 core와 **독립적으로** 버저닝한다 — 위성 릴리스가 `haechi`를 bump하지 않고, 그 반대도 마찬가지다.
|
|
65
|
+
|
|
66
|
+
- **pre-1.0:** 위성은 npm semver를 따르며 `0.x` **minor** bump가 breaking change를 담을 수 있다; `major.minor`로 핀한다(예: `haechi-crypto-kms@~0.1`). 각자 자체 `1.0.0`까지 pre-stable.
|
|
67
|
+
- **core 호환성**은 `peerDependencies` 범위(`"haechi": ">=0.8.0 <1.0.0"`)로 표현한다 — 위성은 소비자가 설치한 단일 `haechi`를 재사용하므로 crypto/identity 표면이 하나다.
|
|
68
|
+
- **무거운 백엔드는 optional peer다.** `haechi-crypto-kms`는 `@aws-sdk/client-kms`를 `peerDependencies` + `peerDependenciesMeta.optional`로 선언하고 lazy import하므로, AWS 경로를 쓰지 않는 소비자는 설치하지 않고 core는 zero-dependency를 유지한다. 위성의 배포 tarball은 항상 **runtime `dependencies` 0**을 선언한다(CI `check-satellite-packaging`로 강제).
|
|
69
|
+
- 위성 export(`createKmsCryptoProvider`, `createAwsKmsClient`, `createJwtAuthProvider`)는 0.8에서 preview이며 각 위성의 `1.0.0` 전에 변경될 수 있다.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
- Status: Draft 0.1
|
|
4
4
|
- Date: 2026-06-10
|
|
5
|
-
- Target version: 0.
|
|
5
|
+
- Target version: 0.7.0
|
|
6
6
|
|
|
7
7
|
## 1. Version Interpretation
|
|
8
8
|
|
|
@@ -42,7 +42,10 @@ The following exports are treated as preview in 0.4.0.
|
|
|
42
42
|
- `identity` audit field and the `authProvider` contract (reserved in 0.4, implemented in 0.6 — shape may change until then)
|
|
43
43
|
- `status` / `audit-verify` CLI output shapes
|
|
44
44
|
- `haechi/stream-filter` (`inspectResponseStream`, path helpers) and `createStreamProtector` (streaming inspection internals)
|
|
45
|
-
- `haechi/auth` (`createBearerAuthProvider`, token store, `buildIdentity`) and the `authProvider` contract
|
|
45
|
+
- `haechi/auth` (`createBearerAuthProvider`, token store, `buildIdentity`, `buildExternalIdentity`) and the `authProvider` contract
|
|
46
|
+
- `assertCryptoProviderConformance` and the hardened cryptoProvider contract (envelope base shape + provider extensions)
|
|
47
|
+
- `audit.anchor` config and `verifyAuditChain(path, { anchorPath })`
|
|
48
|
+
- `scripts/release-checksums.mjs` (SHA256SUMS generate/verify)
|
|
46
49
|
- `policy.profiles`/`policy.profileBinding`/`modelAllowlist`/`rate` and the `identity`/`profile` audit fields
|
|
47
50
|
|
|
48
51
|
## 4. Migration note criteria
|
|
@@ -55,3 +58,12 @@ A migration note is added to `docs/current/release-*.md` or the README whenever
|
|
|
55
58
|
- Changing an audit event field
|
|
56
59
|
- Changing the token format
|
|
57
60
|
- Changing the plugin manifest schema
|
|
61
|
+
|
|
62
|
+
## 5. Satellite packages (`haechi-*`)
|
|
63
|
+
|
|
64
|
+
Satellites (e.g. `haechi-crypto-kms`, `haechi-auth-jwt`) version **independently** of core — a satellite release never bumps `haechi`, and vice versa.
|
|
65
|
+
|
|
66
|
+
- **Pre-1.0:** satellites follow npm semver where a `0.x` **minor** bump may carry breaking changes; pin `major.minor` (e.g. `haechi-crypto-kms@~0.1`). Each is pre-stable until its own `1.0.0`.
|
|
67
|
+
- **Core compatibility** is expressed as a `peerDependencies` range (`"haechi": ">=0.8.0 <1.0.0"`) — a satellite reuses the consumer's single installed `haechi`, so there is one crypto/identity surface.
|
|
68
|
+
- **Heavy backends are optional peers.** `haechi-crypto-kms` declares `@aws-sdk/client-kms` under `peerDependencies` + `peerDependenciesMeta.optional` and imports it lazily, so consumers who do not use the AWS path never install it and core stays zero-dependency. A satellite's published tarball always declares **zero runtime `dependencies`** (CI-gated by `check-satellite-packaging`).
|
|
69
|
+
- Satellite exports (`createKmsCryptoProvider`, `createAwsKmsClient`, `createJwtAuthProvider`) are preview in 0.8 and may change before each satellite's `1.0.0`.
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
0.4에서 예약해 둔 `authProvider`/`identity` 계약을 구현하고, identity를 실질적인 per-client 제어로 전환한다: 내장 bearer 인증, 명명된 per-client policy profile, model allowlist, request rate limiting. 이로써 Haechi를 단일 호스트에서 복수의 클라이언트/에이전트 앞에 안전하게 배치할 수 있게 된다.
|
|
12
12
|
|
|
13
|
-
**범위 결정 (2026-06-10):** 0.6은 auth 핵심에 집중한다. 원래 0.6으로 묶였던 무거운 운영 항목들 — Vault/AWS KMS 레퍼런스 어댑터, 외부 append-only audit 싱크, 서명된 릴리스 아티팩트,
|
|
13
|
+
**범위 결정 (2026-06-10):** 0.6은 auth 핵심에 집중한다. 원래 0.6으로 묶였던 무거운 운영 항목들 — Vault/AWS KMS 레퍼런스 어댑터, 외부 append-only audit 싱크, 서명된 릴리스 아티팩트, `haechi-*` 패키지 패밀리 — 은 각각 별도의 보안 설계를 받을 수 있도록 **0.7**로 이월한다.
|
|
14
14
|
|
|
15
15
|
## 2. 범위
|
|
16
16
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
- `auth.provider`로 선택한다:
|
|
22
22
|
- `none` (기본값) — 인증 없음; `identity`는 `null`로 유지 (0.5와 byte 단위로 동일한 audit 형태). Per-client policy는 default profile / base policy로 결정된다.
|
|
23
23
|
- `bearer` — 내장 token auth (§2.2).
|
|
24
|
-
- `external` — 주입된 `authProvider` 필요; 없으면 fail-closed (`keys.provider: external`과 동일한 방식). OIDC/JWT provider는 **0.7+ 위성 패키지** (
|
|
24
|
+
- `external` — 주입된 `authProvider` 필요; 없으면 fail-closed (`keys.provider: external`과 동일한 방식). OIDC/JWT provider는 **0.7+ 위성 패키지** (`haechi-auth-oidc`)로 남긴다; 0.6은 네트워크 IdP 코드를 포함하지 않는다.
|
|
25
25
|
|
|
26
26
|
### 2.2 내장 bearer auth + token store
|
|
27
27
|
|
|
@@ -123,8 +123,8 @@ forward
|
|
|
123
123
|
|
|
124
124
|
## 4. 명시적 비범위 (0.7+로 이월)
|
|
125
125
|
|
|
126
|
-
- OIDC/JWT provider (
|
|
127
|
-
- Vault/AWS KMS 레퍼런스 어댑터; 외부 append-only audit 싱크; 서명된 릴리스 아티팩트;
|
|
126
|
+
- OIDC/JWT provider (`haechi-auth-oidc`, `haechi-auth-jwt`) — 0.6은 bearer + external 주입만 포함.
|
|
127
|
+
- Vault/AWS KMS 레퍼런스 어댑터; 외부 append-only audit 싱크; 서명된 릴리스 아티팩트; the `haechi-*` package family.
|
|
128
128
|
- LLM token-budget limiting; 분산/공유 rate 상태.
|
|
129
129
|
- auth provider의 동적 npm 로딩 (1.0 plugin sandbox).
|
|
130
130
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
Implement the `authProvider`/`identity` contracts reserved in 0.4 and turn identity into real per-client controls: built-in bearer authentication, named per-client policy profiles, model allowlisting, and request rate limiting. This makes Haechi safe(r) to put in front of multiple clients/agents on one host.
|
|
12
12
|
|
|
13
|
-
**Scope decision (2026-06-10):** 0.6 is focused on the auth core. The heavier operational items originally grouped under 0.6 — Vault/AWS KMS reference adapter, external append-only audit sink, signed release artifacts,
|
|
13
|
+
**Scope decision (2026-06-10):** 0.6 is focused on the auth core. The heavier operational items originally grouped under 0.6 — Vault/AWS KMS reference adapter, external append-only audit sink, signed release artifacts, the `haechi-*` package family — move to **0.7** so each gets its own security design instead of bloating one release.
|
|
14
14
|
|
|
15
15
|
## 2. Scope
|
|
16
16
|
|
|
@@ -21,7 +21,7 @@ Implement the `authProvider`/`identity` contracts reserved in 0.4 and turn ident
|
|
|
21
21
|
- Selected by `auth.provider`:
|
|
22
22
|
- `none` (default) — no authentication; `identity` stays `null` (byte-identical audit shape to 0.5). Per-client policy resolves to the default profile / base policy.
|
|
23
23
|
- `bearer` — built-in token auth (§2.2).
|
|
24
|
-
- `external` — requires an injected `authProvider`; fail-closed if absent (mirrors `keys.provider: external`). The OIDC/JWT providers remain **0.7+ satellite packages** (
|
|
24
|
+
- `external` — requires an injected `authProvider`; fail-closed if absent (mirrors `keys.provider: external`). The OIDC/JWT providers remain **0.7+ satellite packages** (`haechi-auth-oidc`); 0.6 ships no network IdP code.
|
|
25
25
|
|
|
26
26
|
### 2.2 Built-in bearer auth + token store
|
|
27
27
|
|
|
@@ -123,8 +123,8 @@ Plus `policy.profiles`, `policy.profileBinding`, `policy.modelAllowlist`, `polic
|
|
|
123
123
|
|
|
124
124
|
## 4. Explicit non-scope (deferred to 0.7+)
|
|
125
125
|
|
|
126
|
-
- OIDC/JWT providers (
|
|
127
|
-
- Vault/AWS KMS reference adapter; external append-only audit sink; signed release artifacts;
|
|
126
|
+
- OIDC/JWT providers (`haechi-auth-oidc`, `haechi-auth-jwt`) — 0.6 ships bearer + external-injection only.
|
|
127
|
+
- Vault/AWS KMS reference adapter; external append-only audit sink; signed release artifacts; the `haechi-*` package family.
|
|
128
128
|
- LLM token-budget limiting; distributed/shared rate state.
|
|
129
129
|
- Dynamic npm loading of auth providers (1.0 plugin sandbox).
|
|
130
130
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Haechi 0.7 Implementation Scope
|
|
2
|
+
|
|
3
|
+
- 문서 상태: Final
|
|
4
|
+
- 작성일: 2026-06-10
|
|
5
|
+
- 기준 버전: 0.7.0 (0.6.0 이후)
|
|
6
|
+
- 성격: ops hardening
|
|
7
|
+
- 구현 완료: 2026-06-10 — PR #22 (audit anchoring), #23 (cryptoProvider 계약 + reference KMS), #24 (signed release artifacts)
|
|
8
|
+
|
|
9
|
+
## 1. 릴리스 목표
|
|
10
|
+
|
|
11
|
+
1.0("stable", developer-preview 레이블 제거)이 차단하는 운영 스토리를 강화한다: 단일 로컬 파일을 넘어선 audit 무결성, 외부 key custody, 검증 가능한 릴리스 아티팩트. 두 번의 1.0 차단 릴리스 중 첫 번째다.
|
|
12
|
+
|
|
13
|
+
**범위 결정 (2026-06-10):** 0.7은 **ops hardening** — audit 무결성, key custody 계약, 서명된 아티팩트에 집중한다. 이전에 여기에 묶였던 **생태계** 항목들(the `haechi-*` package family, `haechi-crypto-kms` / `haechi-auth-oidc` 게시, `haechi-dashboard`, npm workspaces)은 **0.8**로 이월하며, 0.8에서 중복된 0.7 로드맵 행도 제거한다.
|
|
14
|
+
|
|
15
|
+
Core의 **zero runtime dependency** 기조는 협상 불가: 0.7의 모든 것은 `node:` 빌트인만으로 제공된다. 무거운 어댑터(AWS KMS, Vault)는 satellite/example이며, 절대 core에 포함되지 않는다.
|
|
16
|
+
|
|
17
|
+
## 2. 범위
|
|
18
|
+
|
|
19
|
+
### 2.1 Audit tail-truncation 방어: head-hash anchoring (내장, zero-dep)
|
|
20
|
+
|
|
21
|
+
audit hash chain은 변조와 재정렬은 탐지하지만, 마지막 N개 레코드의 **삭제**는 탐지하지 못한다 — 단축된 chain도 여전히 검증을 통과하기 때문이다. 0.7은 주기적 anchoring으로 일반적인 경우를 해결한다.
|
|
22
|
+
|
|
23
|
+
- 추가 이후 JSONL sink는 현재 chain head를 별도의 **append-only anchor 스트림**에 기록한다: JSON 한 줄 `{ sequence, eventHash, timestamp }`.
|
|
24
|
+
- Config `audit.anchor`:
|
|
25
|
+
- `mode`: `none` (기본값 — 현재 동작) | `file` | `stdout`.
|
|
26
|
+
- `path`: `mode: file`일 때의 anchor 파일 (다른 매체 / append-only 플래그가 설정된 경로 권장).
|
|
27
|
+
- `everyRecords`: anchor 주기 (기본값 `1` — 레코드마다 anchor; 배치 처리 시 값 증가). Anchor 라인은 매우 작다.
|
|
28
|
+
- `verifyAuditChain(path, { anchorPath })`은 교차 검증한다: 최신 anchor의 `sequence`가 chain 길이를 초과해서는 안 되며, 앵커된 `sequence`의 chain 레코드는 앵커된 `eventHash`로 해시되어야 한다. 최신 anchor보다 짧은 chain → **truncation 탐지** (마지막 anchor 이후의 레코드가 제거된 것).
|
|
29
|
+
- `haechi audit-verify --anchor <path>`로 확인하며; `haechi status`는 anchor 모드 + 마지막으로 앵커된 sequence를 보고한다.
|
|
30
|
+
- **보장 범위의 한계:** truncation은 **마지막 anchor** 이전까지만 탐지된다; 마지막 anchor 이후, truncation 이전에 기록된 레코드는 여전히 조용히 손실될 수 있다. `everyRecords: 1`이면 그 범위는 레코드 하나다. 문서화되어 있다.
|
|
31
|
+
|
|
32
|
+
### 2.2 외부 append-only audit sink 계약
|
|
33
|
+
|
|
34
|
+
- 주입된 `auditSink` 계약을 공식화한다 (이미 `createRuntime(config, { auditSink })`를 통해 지원됨): `record(event)`는 append-only이며 순서를 보존한다; 외부 sink는 hash chain을 직접 구현하거나 내장 sink를 래핑한다. 기능 플래그(`writesAudit`, `integrity`, `appendOnly`)가 문서화된다.
|
|
35
|
+
- HTTP/syslog/object-lock 전달 레퍼런스 sink는 **0.8 satellite/example**이다; 0.7은 계약 + 내장 anchoring을 zero-dep 해답으로 제공한다.
|
|
36
|
+
|
|
37
|
+
### 2.3 cryptoProvider 계약 강화 + 레퍼런스 KMS 어댑터
|
|
38
|
+
|
|
39
|
+
- `keys.provider: external`에 대한 `cryptoProvider` 계약을 강화하고 문서화한다: 외부 provider는 `encrypt`, `decrypt`, **그리고 `hmac`** (토큰/identity를 위해 0.4에서 추가됨)을 구현해야 하며, envelope 형태(`{ v, alg, kid, iv, ct, tag, aadHash }`)를 보존하고, canonical AAD를 바인딩하며, `kid`로 키를 선택해야 한다.
|
|
40
|
+
- `assertCryptoProviderConformance(provider)` (익스포트된 테스트 헬퍼)를 제공한다: encrypt→decrypt 왕복, AAD 불일치 거부, `hmac` 결정론 + domain separation. Satellite 어댑터는 이를 통해 자체 테스트한다.
|
|
41
|
+
- `examples/crypto-kms-reference/` 아래에 **레퍼런스 어댑터**를 제공한다 (자체 `package.json`, AWS/Vault SDK는 *optional/peer* 의존성으로, core의 `files`에 포함하지 않음): 주입 방법을 시연한다. 이것이 0.8에서(npm org 취득 후) 게시되는 **`haechi-crypto-kms`** satellite의 소스가 된다.
|
|
42
|
+
|
|
43
|
+
### 2.4 서명된 릴리스 아티팩트
|
|
44
|
+
|
|
45
|
+
- npm provenance (SLSA attestation)는 신뢰할 수 있는 퍼블리싱을 통해 이미 제공된다 (0.4부터). 0.7은 **GitHub 릴리스 에셋 무결성**을 추가한다: 릴리스 워크플로가 `npm pack`을 실행하고, `SHA256SUMS`를 생성하며, 타볼 + 체크섬 (그리고 가능한 경우 sigstore/cosign 서명)을 각 GitHub 릴리스에 첨부한다.
|
|
46
|
+
- 사용자가 설치 전 다운로드한 타볼을 검증할 수 있게 하고, 릴리스 에셋에 레지스트리 외의 변조 방지 매니페스트를 제공한다.
|
|
47
|
+
|
|
48
|
+
## 3. 명시적 비범위 (0.8로 이월)
|
|
49
|
+
|
|
50
|
+
- the `haechi-*` package family 생성; `haechi-crypto-kms`, `haechi-auth-oidc`, `haechi-auth-jwt` 게시.
|
|
51
|
+
- `haechi-dashboard` (읽기 전용 audit 뷰어) 및 npm workspaces 전환.
|
|
52
|
+
- 게시된 패키지로서의 실제 AWS KMS / HashiCorp Vault SDK 연동 (0.7은 계약 + 레퍼런스 example만 제공).
|
|
53
|
+
- 분산/공유 audit 또는 rate 상태.
|
|
54
|
+
|
|
55
|
+
## 4. Config 스키마 요약
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
"audit": {
|
|
59
|
+
"sink": "jsonl",
|
|
60
|
+
"path": ".haechi/audit.jsonl",
|
|
61
|
+
"anchor": { "mode": "none", "path": ".haechi/audit.anchor.jsonl", "everyRecords": 1 }
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
Fail-closed 검증: 알 수 없는 `anchor.mode`; `path` 없이 `mode: file`; 비양수 `everyRecords`.
|
|
65
|
+
|
|
66
|
+
## 5. 1.0 졸업 기준 진행
|
|
67
|
+
|
|
68
|
+
0.7은 다섯 개의 1.0("developer-preview 레이블 제거") 차단 조건 중 세 개를 진전시킨다:
|
|
69
|
+
|
|
70
|
+
| 1.0 차단 조건 | 0.7 기여 |
|
|
71
|
+
|---|---|
|
|
72
|
+
| 운영 key custody | cryptoProvider 계약 강화 + conformance 테스트 + 레퍼런스 어댑터 (게시 패키지는 0.8) |
|
|
73
|
+
| 외부 / tamper-evident audit | 내장 anchoring으로 tail-truncation 해결; 외부 sink 계약 문서화 |
|
|
74
|
+
| 검증 가능한 릴리스 아티팩트 | 서명/체크섬된 GitHub 릴리스 에셋 |
|
|
75
|
+
| API stability freeze | (1.0) |
|
|
76
|
+
| Plugin sandbox + 실환경 검증 | (1.0) |
|
|
77
|
+
|
|
78
|
+
## 6. 테스트 기준 (구현 시)
|
|
79
|
+
|
|
80
|
+
- Anchoring: `everyRecords`에 따라 anchor 라인이 기록됨; anchor가 포함된 `verifyAuditChain`이 truncation(최신 anchor보다 짧은 chain)을 탐지하고 온전한 chain은 통과시킴; `mode: none`이면 0.6 동작과 byte 단위로 동일.
|
|
81
|
+
- `audit-verify --anchor` 종료 코드 + 출력; `status`가 anchor 모드/마지막 sequence를 보고.
|
|
82
|
+
- cryptoProvider conformance 헬퍼가 로컬 provider를 통과시키고, `hmac` 누락 / AAD 불일치 provider를 실패시킴.
|
|
83
|
+
- `audit.anchor` 블록에 대한 Config 검증.
|
|
84
|
+
- 릴리스 워크플로가 팩된 타볼과 일치하는 `SHA256SUMS`를 생성함 (CI 검증 가능).
|
|
85
|
+
|
|
86
|
+
## 7. 권장 PR 분할 (스택)
|
|
87
|
+
|
|
88
|
+
1. Audit anchoring (sink가 anchor 기록) + `verifyAuditChain` anchor 교차 검증 + config + `audit-verify --anchor` / `status`.
|
|
89
|
+
2. cryptoProvider 계약 문서 + `assertCryptoProviderConformance` + `examples/crypto-kms-reference/`.
|
|
90
|
+
3. 서명된 릴리스 아티팩트 (릴리스 워크플로 + 검증 문서).
|
|
91
|
+
4. 0.7.0 릴리스 컷 (버전, 문서 EN/KO, threat-model/risk-register/api-stability, wiki).
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Haechi 0.7 Implementation Scope
|
|
2
|
+
|
|
3
|
+
- Status: Final
|
|
4
|
+
- Date: 2026-06-10
|
|
5
|
+
- Target version: 0.7.0 (after 0.6.0)
|
|
6
|
+
- Type: ops hardening
|
|
7
|
+
- Shipped: 2026-06-10 — PRs #22 (audit anchoring), #23 (cryptoProvider contract + reference KMS), #24 (signed release artifacts)
|
|
8
|
+
|
|
9
|
+
## 1. Release Goal
|
|
10
|
+
|
|
11
|
+
Harden the operational story that 1.0 ("stable", developer-preview label removed) blocks on: audit integrity beyond a single local file, external key custody, and verifiable release artifacts. This is the first of the two 1.0-blocker releases.
|
|
12
|
+
|
|
13
|
+
**Scope decision (2026-06-10):** 0.7 is focused on **ops hardening** — audit integrity, key custody contract, signed artifacts. The **ecosystem** items previously grouped here (the `haechi-*` package family, publishing `haechi-crypto-kms` / `haechi-auth-oidc`, `haechi-dashboard`, npm workspaces) move to **0.8**, which also removes the duplicate 0.7 roadmap row.
|
|
14
|
+
|
|
15
|
+
Core's **zero runtime dependency** posture is non-negotiable: everything in 0.7 ships with `node:` builtins only. Heavy adapters (AWS KMS, Vault) are satellites/examples, never core.
|
|
16
|
+
|
|
17
|
+
## 2. Scope
|
|
18
|
+
|
|
19
|
+
### 2.1 Audit tail-truncation defense: head-hash anchoring (built-in, zero-dep)
|
|
20
|
+
|
|
21
|
+
The audit hash chain detects tampering and reordering but **not** deletion of the last N records — the shortened chain still verifies. 0.7 closes this for the common case with periodic anchoring.
|
|
22
|
+
|
|
23
|
+
- After appending, the JSONL sink writes the current chain head to a separate **append-only anchor stream**: one JSON line `{ sequence, eventHash, timestamp }`.
|
|
24
|
+
- Config `audit.anchor`:
|
|
25
|
+
- `mode`: `none` (default — current behavior) | `file` | `stdout`.
|
|
26
|
+
- `path`: anchor file when `mode: file` (created `0600`). `stdout` writes anchor lines to stdout for capture by a long-running command's supervisor — not for JSON-emitting commands, whose output it would interleave.
|
|
27
|
+
- `everyRecords`: anchor cadence (default `1` — anchor every record; raise to batch). Anchor lines are tiny.
|
|
28
|
+
- `verifyAuditChain(path, { anchorPath })` cross-checks: the latest anchor's `sequence` must not exceed the chain length, and the chain record at the anchored `sequence` must hash to the anchored `eventHash`. A chain shorter than the latest anchor → **truncation detected** (records after the last anchor were removed). A partial trailing anchor line (from a crash) is tolerated.
|
|
29
|
+
- `haechi audit-verify --anchor <path>` surfaces this; `haechi status` reports anchor mode + last anchored sequence.
|
|
30
|
+
- **Threat-model boundary (required, not optional):** the anchor adds tamper-evidence **only when it lives on append-only or physically separate media** (append-only-flagged FS, S3/GCS object-lock, syslog, a different host). On the **same writable filesystem** an attacker who truncates `audit.jsonl` can truncate `audit.anchor.jsonl` to match and verification passes — so file-mode on the same disk is a convenience, not a guarantee. The CLI (`status`, `audit-verify`, `config`) states this explicitly.
|
|
31
|
+
- **Bounded guarantee:** even on proper media, truncation is detected only back to the **last anchor**; records written after the last anchor and before truncation can still be lost silently. With `everyRecords: 1` that window is one record. Documented.
|
|
32
|
+
|
|
33
|
+
### 2.2 External append-only audit sink contract
|
|
34
|
+
|
|
35
|
+
- Formalize the injected `auditSink` contract (already supported via `createRuntime(config, { auditSink })`): `record(event)` is append-only and order-preserving; an external sink either implements the hash chain itself or wraps the built-in sink. Capability flags (`writesAudit`, `integrity`, `appendOnly`) are documented.
|
|
36
|
+
- A reference HTTP/syslog/object-lock forwarding sink is a **0.8 satellite/example**; 0.7 ships the contract + the built-in anchoring as the zero-dep answer.
|
|
37
|
+
|
|
38
|
+
### 2.3 cryptoProvider contract hardening + reference KMS adapter
|
|
39
|
+
|
|
40
|
+
- Tighten and document the `cryptoProvider` contract for `keys.provider: external`: a provider always implements `encrypt`/`decrypt`, binds canonical AAD, and selects keys by `kid`; the envelope **base shape** is `{ v, alg, kid, iv, ct, tag, aadHash }` and adapters **may add provider-specific fields** (e.g. a KMS adapter's `wrappedKey`). `hmac` is required **only by features that use it** — bearer auth and deterministic tokenization — and `createRuntime` fails closed at construction when one of those is configured without `hmac` (an encrypt-only provider is otherwise valid). Policy-bundle signing uses the local key file directly via the CLI, not the injected provider.
|
|
41
|
+
- Ship `assertCryptoProviderConformance(provider, { requireHmac = true })` (an exported test helper): encrypt→decrypt round-trip (distinct plaintexts), AAD-mismatch rejection, **tampered-ciphertext rejection (real AEAD authentication)**, and `hmac` determinism + data-dependency + domain separation + invalid-domain rejection. Satellite adapters self-test against it; pass `requireHmac: false` for an encrypt-only provider.
|
|
42
|
+
- Ship a **reference adapter** under `examples/crypto-kms-reference/` (its own `package.json`, AWS/Vault SDK as an *optional* dependency; the in-process `createInMemoryKms` is explicitly non-production) demonstrating envelope-encryption injection. It is the source that becomes the published **`haechi-crypto-kms`** satellite in 0.8 (gated on the npm org).
|
|
43
|
+
|
|
44
|
+
### 2.4 Signed release artifacts
|
|
45
|
+
|
|
46
|
+
- npm provenance (SLSA attestation) already ships via trusted publishing (since 0.4). 0.7 adds **GitHub release asset integrity**: the release workflow runs `npm pack`, emits `SHA256SUMS`, and attaches the tarball + checksums (and, where available, a sigstore/cosign signature) to each GitHub release.
|
|
47
|
+
- Lets users verify a downloaded tarball before install and gives the release assets a tamper-evident manifest beyond the registry.
|
|
48
|
+
|
|
49
|
+
## 3. Explicit non-scope (deferred to 0.8)
|
|
50
|
+
|
|
51
|
+
- Publish the `haechi-*` family: publish `haechi-crypto-kms`, `haechi-auth-oidc`, `haechi-auth-jwt`.
|
|
52
|
+
- `haechi-dashboard` (read-only audit viewer) and npm workspaces conversion.
|
|
53
|
+
- Real AWS KMS / HashiCorp Vault SDK integration as a published package (0.7 ships the contract + reference example only).
|
|
54
|
+
- Distributed/shared audit or rate state.
|
|
55
|
+
|
|
56
|
+
## 4. Config schema summary
|
|
57
|
+
|
|
58
|
+
```json
|
|
59
|
+
"audit": {
|
|
60
|
+
"sink": "jsonl",
|
|
61
|
+
"path": ".haechi/audit.jsonl",
|
|
62
|
+
"anchor": { "mode": "none", "path": ".haechi/audit.anchor.jsonl", "everyRecords": 1 }
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
Fail-closed validation: unknown `anchor.mode`; `mode: file` without a `path`; non-positive `everyRecords`.
|
|
66
|
+
|
|
67
|
+
## 5. 1.0 exit-criteria progress
|
|
68
|
+
|
|
69
|
+
0.7 advances three of the five 1.0 ("remove developer-preview label") blockers:
|
|
70
|
+
|
|
71
|
+
| 1.0 blocker | 0.7 contribution |
|
|
72
|
+
|---|---|
|
|
73
|
+
| Operational key custody | cryptoProvider contract hardened + conformance test + reference adapter (published package in 0.8) |
|
|
74
|
+
| External / tamper-evident audit | Built-in anchoring closes tail-truncation; external sink contract documented |
|
|
75
|
+
| Verifiable release artifacts | Signed/checksummed GitHub release assets |
|
|
76
|
+
| API stability freeze | (1.0) |
|
|
77
|
+
| Plugin sandbox + real-environment validation | (1.0) |
|
|
78
|
+
|
|
79
|
+
## 6. Test criteria (for implementation)
|
|
80
|
+
|
|
81
|
+
- Anchoring: anchor lines written per `everyRecords`; `verifyAuditChain` with an anchor detects truncation (chain shorter than last anchor) and passes an intact chain; `mode: none` keeps 0.6 behavior byte-for-byte.
|
|
82
|
+
- `audit-verify --anchor` exit code + output; `status` reports anchor mode/last sequence.
|
|
83
|
+
- cryptoProvider conformance helper passes the local provider and fails a provider missing `hmac` / mismatching AAD.
|
|
84
|
+
- Config validation for the `audit.anchor` block.
|
|
85
|
+
- Release workflow produces `SHA256SUMS` matching the packed tarball (CI-verifiable).
|
|
86
|
+
|
|
87
|
+
## 7. Suggested PR breakdown (stacked)
|
|
88
|
+
|
|
89
|
+
1. Audit anchoring (sink writes anchors) + `verifyAuditChain` anchor cross-check + config + `audit-verify --anchor` / `status`.
|
|
90
|
+
2. cryptoProvider contract doc + `assertCryptoProviderConformance` + `examples/crypto-kms-reference/`.
|
|
91
|
+
3. Signed release artifacts (release workflow + verification doc).
|
|
92
|
+
4. 0.7.0 release cut (version, docs EN/KO, threat-model/risk-register/api-stability, wiki).
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Haechi 0.8 구현 범위
|
|
2
|
+
|
|
3
|
+
- 상태: Draft 0.3 (설계 — 아직 미구현)
|
|
4
|
+
- 작성일: 2026-06-10
|
|
5
|
+
- 대상 버전: 0.8.0 (0.7.0 이후)
|
|
6
|
+
- 유형: 에코시스템 기반
|
|
7
|
+
|
|
8
|
+
## 1. 릴리스 목표
|
|
9
|
+
|
|
10
|
+
`haechi-*` 패키지 에코시스템을 띄운다: 저장소를 npm workspaces 모노레포로 전환하고 첫 두 satellite를 배포한다 — `haechi-crypto-kms`(0.7 reference 승격)와 `haechi-auth-jwt`(JWKS bearer 검증), 둘 다 unscoped(`@haechi` scope는 점유됨; org 불필요). 이로써 운영 키 custody를 설치 가능한 패키지로 실현하고, core의 zero-dependency 자세를 건드리지 않으면서 auth 에코시스템을 키운다.
|
|
11
|
+
|
|
12
|
+
**범위 결정(2026-06-10):** 0.8은 **패키징 기반 + satellite**다. `haechi-dashboard` 읽기 전용 audit 뷰어(UI 빌드)와 완전한 대화형 `haechi-auth-oidc`는 **0.9**로 이동하여, 0.8은 코드가 가볍고 모노레포 + 두 개의 헤드리스 친화적 어댑터에 집중한다.
|
|
13
|
+
|
|
14
|
+
Core(`haechi`, unscoped)는 **zero runtime dependency**를 유지한다. satellite 의존성(예: AWS SDK)은 해당 satellite의 `package.json`에만 존재하며 core의 tarball이나 SBOM에 절대 들어가지 않는다.
|
|
15
|
+
|
|
16
|
+
## 2. 범위
|
|
17
|
+
|
|
18
|
+
### 2.1 npm workspaces 모노레포 (검증된 resolution 메커니즘)
|
|
19
|
+
|
|
20
|
+
순진한 레이아웃(`"workspaces": ["satellites/*"]` + satellite peer 범위 `"haechi": ">=0.8.0"`)은 **동작하지 않으며** 경험적 테스트 후 기각했다: npm은 충족되지 않은 peer 범위를 레지스트리 조회로 취급하고(`ETARGET: no matching version for haechi@>=0.8.0`), 루트 프로젝트를 `node_modules/haechi`로 심링크하지 않으며, satellite의 `import "haechi/crypto"`가 `ERR_MODULE_NOT_FOUND`로 던진다. 루트 프로젝트는 기본적으로 workspace 멤버가 **아니므로** npm이 절대 링크하지 않는다.
|
|
21
|
+
|
|
22
|
+
검증된 동작 레이아웃:
|
|
23
|
+
|
|
24
|
+
- **루트가 자기 자신을 workspace 멤버로 등록한다:** `"workspaces": [".", "satellites/*"]`. `"."` 항목이 npm으로 하여금 `node_modules/haechi → ..` 심링크를 만들게 해 satellite가 core를 resolve한다. 이게 없으면 satellite는 레지스트리로 fallback한다.
|
|
25
|
+
- **저장소 루트는 배포되는 `haechi` 패키지로 그대로 남는다** — `exports`, `bin`, `files` 허용목록은 변하지 않는다. `package.json` 변경은 추가된 `workspaces` 필드와 버전 bump뿐이다. satellite는 core의 `files`에 없으므로 `haechi` tarball 안으로 절대 실리지 않는다(검증됨: 루트 `npm pack --dry-run`은 `files` 허용목록만 나열하고 `satellites/`는 제외된다).
|
|
26
|
+
- **satellite는 core에 대한 이중 의존성을 선언한다:**
|
|
27
|
+
- `"peerDependencies": { "haechi": ">=0.8.0 <1.0.0" }` — *소비자*가 설치할 때의 계약. satellite는 소비자의 단일 `haechi` 인스턴스를 재사용한다(crypto/identity 표면 하나, 중복 사본 없음).
|
|
28
|
+
- `"devDependencies": { "haechi": "*" }` — 모노레포 개발/CI 중에 npm이 peer 범위를 레지스트리에서 resolve하지 않고 **로컬 workspace**를 링크하게 만드는 메커니즘. `npm pack`은 배포 tarball에서 devDependencies를 제거하므로 소비자 매니페스트에는 peer 범위만 남고 `*` devDep은 다운스트림에서 보이지 않는다. (`satellites/*/package.json`을 읽는 저장소/소스 스캐너는 여전히 그것을 본다. 이 source-vs-artifact 차이는 예상된 것이고 무해하다.)
|
|
29
|
+
- **소비자 peer-mismatch 동작:** 배포된 satellite를 *비호환* `haechi`(예: 이미 설치된 `haechi@0.7.0`)에 설치하면 hard failure가 아니라 npm `ERESOLVE` **경고**가 난다; 소비자는 `haechi`를 올리거나(자기 책임으로 `--legacy-peer-deps`) 해야 한다. satellite는 범위를 벗어난 core에서 올바르게 동작하지 않는다.
|
|
30
|
+
- satellite는 core를 subpath로 import한다(`haechi/crypto`, `haechi/auth`, `haechi/runtime`). 개발 중에는 workspace 심링크로, 프로덕션에서는 소비자가 설치한 `haechi`로 resolve된다.
|
|
31
|
+
- `examples/crypto-kms-reference/`는 `satellites/crypto-kms/`로 **승격**한다. reference 예제는 workspaces 이전에 중첩 `package.json`이 `haechi/crypto`를 self-resolve하지 못해 `canonicalize`를 인라인했었다; **workspaces 하에서는 그 import가 resolve되므로** satellite는 사본을 들고 다니지 않고 `haechi/crypto`에서 `canonicalize`를 import한다(core와 satellite 간 AAD 정규화 drift 방지). 기존 `examples/` 디렉터리는 배포된 패키지를 가리키는 짧은 README만 남긴다. conformance 테스트가 satellite의 AAD 정규화가 `haechi/crypto`와 **byte-for-byte 동일**함을 검증한다(의미적 동등이 아니라).
|
|
32
|
+
- **lock 파일:** workspaces 전환은 `package-lock.json`을 workspace-resolve된 항목(루트 자기 멤버 포함)으로 재생성한다. 재생성된 lock 파일을 커밋한다; CI는 `npm ci`(stale/누락 lock이면 실패)를 쓰므로, 전환 PR이 새 lock을 커밋해야 한다.
|
|
33
|
+
|
|
34
|
+
**CI 전략(중복 실행 방지):** 루트 CI는 루트에서 `node --test`를 직접 돌리며, 이는 `satellites/**/*.test.mjs`를 자동 발견한다(workspace 심링크 덕에 그들의 `haechi/*` import가 resolve됨). CI는 `npm test --workspaces`를 **쓰지 않는다**(루트 자기 멤버로 재귀해 스위트를 다시 돌릴 것이다). 각 satellite는 로컬 단독 실행용(`npm test -w haechi-crypto-kms`) 자체 `test` 스크립트만 유지한다. 검증됨: 루트 `node --test`가 core + satellite 테스트를 한 번 실행하고, `node_modules/haechi → ..` 심링크 순환에도 runner가 hang하지 않는다(node는 `node_modules`를 건너뜀).
|
|
35
|
+
|
|
36
|
+
**정직한 패키징 노트(기존 "byte-stable" 주장 대체):** 루트 tarball은 0.7.0과 byte-identical이 **아니다** — `package.json`이 `workspaces` 필드를 얻고 버전이 bump되며, 이는 어떤 릴리스에서도 예상되는 일이다. 방어 가능하고 테스트된 더 좁은 주장이며 CI 게이트(§6.1)로 강제한다: **(a) satellite 파일이 `haechi` tarball에 나타나지 않고, (b) `haechi` tarball 자체의 `package.json`이 runtime `dependencies`를 0으로 선언한다.** 게이트는 **패킹된 매니페스트**를 검사한다(`npm pack` 출력에서 `package.json`을 추출해 `dependencies`가 비어있음/undefined임을 단언) — 오늘은 공허하게 통과하고 미래의 runtime-dep 누수를 놓칠 설치된 `node_modules` SBOM이 아니라.
|
|
37
|
+
|
|
38
|
+
### 2.2 unscoped `haechi-*` 이름 + 패키지별 trusted publishing
|
|
39
|
+
|
|
40
|
+
- **네이밍(2026-06-10 결정):** `@haechi` npm org/scope는 제3자가 이미 점유했으므로, satellite는 **unscoped `haechi-*`** 이름으로 발행한다 — `haechi-crypto-kms`, `haechi-auth-jwt`(둘 다 npm에 비어있음 확인). **npm org 불필요**, unscoped core `haechi`와 일관되며, 각 이름을 개별 예약 + Trusted Publisher 바인딩한다. (scope 대비 트레이드오프: 네임스페이스 그룹핑/방어 없음; `haechi-` 접두사가 관례.)
|
|
41
|
+
- 각 satellite는 0.7에서 증명한 **동일한 OIDC trusted-publishing + sigstore + SHA256SUMS** 경로로 배포한다 — 자체 npmjs.com Trusted Publisher 링크와 태그 트리거 배포 워크플로.
|
|
42
|
+
- **satellite `package.json` 요구사항(루트에서 상속 안 됨):** 각 satellite는 여전히 자체 `"publishConfig": { "access": "public", "provenance": true }`를 설정한다. unscoped 패키지는 기본이 public이라 `access: public`은 보조적이고, `provenance: true`와 루트 `publishConfig` 비상속이 핵심이다. 배포 후 런북은 `npm view haechi-<pkg> access`가 `public`을 보고하는지 검증한다.
|
|
43
|
+
- **태그 네임스페이싱 + 워크플로 가드(오트리거·충돌 방지):**
|
|
44
|
+
- core 릴리스 태그: `v<semver>`(예: `v0.8.0`). 루트 배포 워크플로는 `push: tags: ['v[0-9]*.[0-9]*.[0-9]*']`에서 트리거된다. GitHub 태그 glob은 `.`을 리터럴로, `[0-9]*`를 느슨하게 취급하므로(`v1.2.3.4`나 `v1a.2.3`도 매칭됨), 워크플로는 pre-publish 단계에서 엄격한 `^v[0-9]+\.[0-9]+\.[0-9]+$` 정규식으로 **재검증**하고 불일치 시 fail-closed한다.
|
|
45
|
+
- satellite 태그는 **접두사**가 붙는다: `crypto-kms-v<semver>`, `auth-jwt-v<semver>`. 각 satellite 워크플로는 자기 접두사 glob에서만 트리거되고 마찬가지로 `^<prefix>-v[0-9]+\.[0-9]+\.[0-9]+$`로 재검증한다.
|
|
46
|
+
- 각 워크플로는 배포할 패키지 디렉터리를 재확인한다(`npm publish -w <dir>`)므로 잘못 태깅된 push가 엉뚱한 패키지를 배포할 수 없다. npmjs.com의 Trusted Publisher는 **특정 워크플로 파일명**에 바인딩된다 — npm 설정 갱신 없이 워크플로를 rename하면 OIDC auth가 깨진다(런북에 패키지→워크플로-파일명→태그-glob 매핑 표와 함께 실패 모드로 문서화).
|
|
47
|
+
- satellite별 **독립 semver**(satellite patch가 core를 bump하지 않음). satellite는 `0.1.0`에서 시작한다. **pre-1.0 계약:** satellite는 표준 npm semver를 따르며 `0.x` **minor** bump가 breaking change를 담을 수 있다; 소비자는 `major.minor`로 핀해야 한다(예: `haechi-crypto-kms@~0.1`). satellite는 자체 `1.0.0`까지 pre-stable이다.
|
|
48
|
+
- **첫 배포 부트스트랩(org 불필요):** satellite별 순서 — (1) npmjs.com에서 (아직 미발행) unscoped 이름에 대해 repo + 정확한 워크플로 파일명을 연결하는 **Trusted Publisher 설정**; (2) satellite 첫 태그 push → 워크플로의 OIDC 배포가 provenance와 함께 `0.1.0`을 생성하고 첫 발행 시 **이름을 확보**. 노트북에서의 수동 `npm publish`는 필요 없다(0.7 trusted-publishing 자세와 동일). 이름이 unscoped이고 현재 비어있으므로 org-membership 선행 요건이 없다.
|
|
49
|
+
|
|
50
|
+
### 2.3 `haechi-crypto-kms` (배포 + 실제 KMS 클라이언트)
|
|
51
|
+
|
|
52
|
+
- 0.7 reference(`createKmsCryptoProvider` envelope 암호화 + `createInMemoryKms`)를 배포 패키지로 승격하며, 인라인 `canonicalize`를 `import { canonicalize } from "haechi/crypto"`(§2.1)로 전환한다. 기존 `kms` 클라이언트 인터페이스(`keyId`/`wrap`/`unwrap`/`deriveHmacKey`)는 **변경하지 않으므로** 승격된 provider와 in-memory 클라이언트는 byte-for-byte 동일하고 0.7 테스트가 그대로 넘어온다.
|
|
53
|
+
- **실제 AWS KMS 클라이언트**를 `haechi-crypto-kms/aws`에 추가한다: `createAwsKmsClient({ keyId, region, client, hmacRootCiphertext })`. 동일한 `kms` 인터페이스를 구현한다: `wrap` = CSPRNG로 생성한 32바이트 data key를 KMS `Encrypt`, `unwrap` = KMS `Decrypt`(envelope 암호화 — master key는 KMS를 떠나지 않음); `deriveHmacKey(domain)` = 단일 KMS-`Decrypt`된 32바이트 root(`hmacRootCiphertext`, 캐시)에 대한 **HKDF-SHA256**, domain-separated — 결정적이고 토큰당 네트워크 호출 없음. `hmacRootCiphertext`가 없으면 `deriveHmacKey`는 throw하고 provider는 encrypt-only가 된다(`requireHmac:false`로 유효).
|
|
54
|
+
- **`@aws-sdk/client-kms`는 hard dependency가 아니라 OPTIONAL peer dependency다**(2026-06-10 결정, 기존 "satellite 자체 의존성" 표현을 수정). `client` 미주입 시에만 **lazy import**되므로: 모노레포 `npm ci`/CI는 (대용량) AWS SDK를 절대 받지 않고; in-memory 또는 주입형 클라이언트를 쓰는 소비자는 설치하지 않으며; core는 자명하게 영향받지 않는다. 배포 satellite는 `peerDependencies` + `peerDependenciesMeta.optional`로 선언한다. 이로써 실제 백엔드를 제공하면서도 satellite를 의존성 가볍게 유지한다.
|
|
55
|
+
- satellite CI는 in-memory 클라이언트 **그리고** KMS `encrypt`/`decrypt` ops의 **주입된 mock**(SDK·네트워크 없음)으로 구동되는 AWS 클라이언트에 대해 `assertCryptoProviderConformance`(workspace 심링크로 `haechi/crypto`에서 import)를 실행한다. mock은 충실한 envelope(per-mock master key의 AES-256-GCM)여야 한다: `Decrypt`는 이 키가 wrap한 blob에 대해서만 plaintext를 반환하고, 다른 키가 wrap한 blob(cross-key 격리)과 손상된 blob은 **거부**한다. 항상 성공하는 trivial stub은 불충분하며 스위트가 이 거부 경로와 HMAC 결정성/domain-separation을 실행한다. sandbox KMS 키에 대한 실제 `createAwsKmsClient` 검증은 **CI 밖 통합 테스트**다(문서화, 게이팅 아님).
|
|
56
|
+
|
|
57
|
+
### 2.4 `haechi-auth-jwt` (JWKS bearer 검증, 의존성 최소)
|
|
58
|
+
|
|
59
|
+
`createJwtAuthProvider({ issuer, audience, jwksUri, cryptoProvider, algorithms, clockSkewSeconds, claimMappings })`는 **헤드리스** 게이트웨이를 위한 `authProvider` 계약을 구현한다. `node:` 빌트인만으로 구현 가능하다(`jose` 없음): JWKS는 전역 `fetch`로, JWK→키는 `crypto.createPublicKey({ key: jwk, format: "jwk" })`, 서명은 `crypto.verify`로 검증한다.
|
|
60
|
+
|
|
61
|
+
**구현 노트 — ES256 서명 인코딩(검증됨):** JWS ES256 서명은 raw `R‖S`(IEEE-P1363, P-256은 64바이트)이지만, `node:crypto.verify`는 EC 키에 대해 기본이 **DER**이고 raw 서명에 `false`를 반환한다 — 유효한 ES256 토큰을 전부 조용히 거부한다. 검증기는 EC 알고리즘에 대해 반드시 `dsaEncoding: "ieee-p1363"`을 넘겨야 한다. (경험적 확인: 기본 DER ⇒ `false`; `ieee-p1363` ⇒ `true`.) 이는 옵션이 아니라 수용 기준이다.
|
|
62
|
+
|
|
63
|
+
**보안 명세(필수 — 옵션이 아니라 수용 기준).** 아래 구체 상수는 구현 재량이 아니라 *결정*이다.
|
|
64
|
+
|
|
65
|
+
- **알고리즘 선택은 서버 측이며, 토큰에서 가져오지 않는다.** 검증기는 설정된 `algorithms` 허용목록(기본 `["RS256","ES256"]`)과 JWK의 `kty`/`crv`에서 알고리즘을 고른다. 토큰의 `alg` 헤더는 키 선택 **전에** 허용목록 *멤버십*만 확인하고, 검증 루틴을 선택하지 않는다.
|
|
66
|
+
- **`alg: "none"`을 무조건 거부**한다.
|
|
67
|
+
- **alg-confusion 차단:** RSA 공개키를 HMAC verify에 절대 넣지 않는다. HMAC 계열(`HS*`)은 기본적으로 **허용하지 않는다**; JWKS에서 온 공개키는 오직 그에 맞는 비대칭 알고리즘과만 쓰인다.
|
|
68
|
+
- **`kid`는 필수**다; 서명 키는 JWKS에서 `kid`로 선택하며, 모든 키를 시도하지 않는다.
|
|
69
|
+
- **RSA 키 강도 하한:** modulus `< 2048` 비트인 RSA JWK는 invalid로 거부한다.
|
|
70
|
+
- **JWK 사용 의도:** JWK에 `use`가 있으면 `sig`여야 하고, `key_ops`가 있으면 `verify`/`sign`을 포함하고 `encrypt`/`decrypt`를 포함하지 않아야 한다. 아니면 거부.
|
|
71
|
+
- **헤더 `typ` / JWE 금지:** `typ`가 있으면 `JWT`여야 한다; 암호화된(JWE) 토큰은 무조건 거부 — JWS만 수용.
|
|
72
|
+
- **클레임은 필수이며 완전 검증한다:**
|
|
73
|
+
- `iss`는 설정된 `issuer`와 같아야 한다.
|
|
74
|
+
- `aud`(토큰)는 문자열 또는 문자열 배열일 수 있다(RFC 7519); 설정된 `audience`는 그 문자열과 같거나 배열의 멤버여야 한다 — 정확, 대소문자 구분 일치.
|
|
75
|
+
- `sub`는 필수이며 비어있지 않은 문자열이어야 한다(`subjectHash`의 입력).
|
|
76
|
+
- `exp`와 `nbf`는 **필수**다. `exp`: `now > exp + clockSkewSeconds`이면 거부. `nbf`: `now < nbf - clockSkewSeconds`이면 거부. `exp`가 없는 토큰은 거부. `iat`가 있으면 sanity-check.
|
|
77
|
+
- **`clockSkewSeconds`** 기본 `60`, **최대 `300`** — `> 300` 값은 생성 시 거부(더 큰 skew는 만료 검증을 무력화).
|
|
78
|
+
- **JWKS fetch는 SSRF-하드닝:**
|
|
79
|
+
- `issuer`는 유효한 **HTTPS URL**이어야 하고; `jwksUri`는 HTTPS이며 그 **hostname이 `issuer` hostname과 정확히 일치**해야 한다(포트 제외). 0.8은 **단일 origin issuer만** 지원한다 — issuer 식별자와 다른 host에서 JWKS를 서빙하는 IdP(일부 CDN-fronted 구성)는 0.8 범위 밖이며 생성 시 거부한다. 비-URL issuer(URN 형태)는 명확한 오류로 생성 시 거부한다.
|
|
80
|
+
- private/loopback/link-local 대역과 클라우드 메타데이터 엔드포인트로의 요청은 거부한다: `127.0.0.0/8`, `::1`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16`(`169.254.169.254` 포함), `fe80::/10`. 거부는 DNS resolve 후 fetch 시점에 한다(rebinding 방어), fetch 타임아웃 포함.
|
|
81
|
+
- **JWKS 응답 경계:** 응답 본문 `> 1 MiB`는 거부; `JSON.parse`는 병적 중첩(depth 한계)에 대해 가드한다. JWT 세그먼트는 `JSON.parse` 전에 **엄격한 base64url**(`[A-Za-z0-9_-]`, 패딩 없음)로 디코드한다.
|
|
82
|
+
- **JWKS 캐시는 경계가 있고 DoS 저항적:** 키는 TTL로 캐시한다; 알 수 없는 `kid`가 **무제한 refetch를 트리거하지 않는다** — **60초 cooldown**당 최대 1회 전체 JWKS 갱신이므로, 위조된 `kid` 폭주가 IdP에 대한 fetch storm으로 바뀌지 않는다.
|
|
83
|
+
- **identity는 PII-safe(fail-closed):** `cryptoProvider`는 **필수**이며 `hmac`을 노출해야 한다; 없으면 provider 생성자가 throw한다(PII-safe identity를 만들 수 없음). `subjectHash`/`issuerHash`는 keyed **HMAC-SHA-256**(`haechi:identity:hash:v1`), hex 인코딩(64자) — raw `sub`/`iss`는 절대 저장·로깅하지 않는다. `scopes`는 설정된 scope 클레임(`scp`/`scope`)에서, `labels`는 허용목록 클레임 매핑에서만 온다.
|
|
84
|
+
- **모든 곳에서 fail-closed:** 검증 오류 → `authenticate`는 `null`(거부)을 반환하고 요청 경로로 throw하지 않으며, 토큰 세부정보를 클라이언트로 echo하지 않는다.
|
|
85
|
+
|
|
86
|
+
**주입**으로 연결한다(`createRuntime(config, { authProvider: createJwtAuthProvider(...) })`); `auth.provider: external`. 동적 로딩은 1.0 plugin sandbox까지 금지를 유지한다.
|
|
87
|
+
|
|
88
|
+
### 2.5 모노레포 릴리스 프로세스
|
|
89
|
+
|
|
90
|
+
- 기존 루트 워크플로가 `haechi`를 계속 배포한다(`v*` 태그 glob + 엄격한 정규식 재검증으로 가드).
|
|
91
|
+
- 각 satellite는 0.7의 서명 아티팩트 단계(pack → checksum → attest → publish → upload)를 자기 디렉터리로 스코프하여 재사용하는 자체 접두사 태그 배포 워크플로를 가지며, 각각 자체 Trusted Publisher(특정 워크플로 파일명)에 바인딩된다.
|
|
92
|
+
- 패키지별 릴리스 런북(`release-process.md`)은 다음을 문서화한다: 태그 규칙 + 패키지→워크플로-파일명→태그-glob 매핑 표, Trusted Publisher 부트스트랩 순서(예약 → 설정 → 태그), 워크플로-rename 실패 모드, 배포 후 검증(provenance, `npm view ... access`).
|
|
93
|
+
|
|
94
|
+
## 3. 명시적 비범위 (0.9+로 이월)
|
|
95
|
+
|
|
96
|
+
- `haechi-dashboard` 읽기 전용 audit 뷰어(UI 빌드, 자체 기술스택 결정).
|
|
97
|
+
- `haechi-auth-oidc` 완전 대화형 OIDC(authorization-code flow) — `haechi-auth-jwt`가 헤드리스 케이스를 먼저 다룬다.
|
|
98
|
+
- `haechi-auth-jwt` multi-origin/CDN-fronted JWKS(issuer host ≠ JWKS host).
|
|
99
|
+
- `haechi-classifier-*` ML/휴리스틱 classifier 플러그인.
|
|
100
|
+
- `haechi-crypto-kms` Vault/GCP/Azure 백엔드(0.8은 AWS만).
|
|
101
|
+
- satellite의 동적 로딩(1.0 plugin sandbox).
|
|
102
|
+
|
|
103
|
+
risk-register와 로드맵은 `haechi-auth-oidc`와 `haechi-dashboard`를 0.8 행에서 빼 새 **0.9** 행으로 옮겨, 공개 문서가 이 범위와 일치하도록 갱신한다.
|
|
104
|
+
|
|
105
|
+
## 4. 하위 호환성
|
|
106
|
+
|
|
107
|
+
Core 동작은 불변이다: 루트 패키지의 `exports`, `bin`, `files`, zero-dep runtime 자세는 동일하다. 루트 `package.json`에 `workspaces`(`"."` 자기 항목 포함)를 추가하는 것은 `haechi`를 단일 의존성으로 설치하는 누구에게도 무해(inert)하다. 기존 config와 API는 손대지 않으며, satellite는 순수하게 부가적이고 opt-in이다.
|
|
108
|
+
|
|
109
|
+
## 5. 1.0 관계
|
|
110
|
+
|
|
111
|
+
0.8 자체는 1.0 blocker를 닫지 않지만, **운영 키 custody를 설치 가능하고 attest된 패키지로 실현**하고(`haechi-crypto-kms`) satellite 모델을 end-to-end로 증명한다. 남은 1.0 게이트는 유지된다: API 안정성 freeze와 plugin sandbox + 실환경 검증.
|
|
112
|
+
|
|
113
|
+
## 6. 테스트 기준 (PR 분해에 매핑)
|
|
114
|
+
|
|
115
|
+
### 6.1 PR1 — workspaces 전환 (새 배포 패키지 없음)
|
|
116
|
+
|
|
117
|
+
- 루트 `npm install`이 **ERESOLVE/ETARGET 없이** exit 0; `node_modules/haechi`가 workspace 심링크; 커밋된 `package-lock.json`으로 fresh checkout에서 `npm ci` 성공.
|
|
118
|
+
- `import { ... } from "haechi/crypto"`를 하는 satellite 테스트가 루트 `node --test`에서 green.
|
|
119
|
+
- **no-leak + zero-dep 게이트:** core `npm pack --dry-run`에 **`satellites/` 경로 없음**; **패킹된** `haechi` `package.json`(tarball에서 추출)의 `dependencies`가 비어있음/undefined. 게이트는 **음성 테스트**된다: core의 `files`에 `satellites/`를 임시로 추가하거나 core `package.json`에 runtime dep를 추가하면 게이트가 명확한 오류로 실패한다(공허한 통과 방지).
|
|
120
|
+
- in-memory crypto provider(승격된 0.7 코드)가 workspace 심링크로 `assertCryptoProviderConformance`를 통과하며, `haechi/crypto` 대비 byte-for-byte `canonicalize` parity 검사를 포함한다.
|
|
121
|
+
|
|
122
|
+
### 6.2 PR2 — `haechi-crypto-kms` (실제 AWS 클라이언트)
|
|
123
|
+
|
|
124
|
+
- in-memory **및 AWS** 클라이언트(AWS는 KMS `encrypt`/`decrypt` ops의 **주입된 mock**으로 구동 — SDK·네트워크 없음)가 cross-key/손상-blob **거부** 경로와 HMAC 결정성/domain-separation을 포함해 `assertCryptoProviderConformance`를 통과; `createRuntime`을 통한 end-to-end(암호화 + 토큰화 round-trip).
|
|
125
|
+
- `createAwsKmsClient`는 `keyId` 없으면 throw; `hmacRootCiphertext` 없으면 `deriveHmacKey`가 throw하고 provider는 encrypt-only로 conformance 통과(`requireHmac:false`).
|
|
126
|
+
- 배포 매니페스트가 `publishConfig.access: public`을 설정하고 `@aws-sdk/client-kms`를 `peerDependencies` + `peerDependenciesMeta.optional`로 선언(runtime `dependency` 아님); 배포 satellite tarball은 `dependencies: {}`이고 core tarball은 zero-dep 유지(§6.1 게이트 계속 통과).
|
|
127
|
+
- satellite publish 워크플로(`crypto-kms-v<semver>`)가 0.7 서명 아티팩트 경로로 존재; core 워크플로는 satellite 릴리스 태그가 `haechi`를 발행하지 않도록 가드.
|
|
128
|
+
|
|
129
|
+
### 6.3 PR3 — `haechi-auth-jwt` (보안 게이트)
|
|
130
|
+
|
|
131
|
+
- 유효한 RS256/ES256 JWT(테스트 키 서명, stub JWKS)가 **audit에 raw `sub` 없이** PII-safe identity로 인증; `subjectHash`/`issuerHash`는 64-hex자 HMAC-SHA-256.
|
|
132
|
+
- 다음이 각각 **거부**됨: `alg:"none"`; RSA 공개키로 위조한 `HS256` 토큰(alg-confusion); JWE/`typ` 불일치; 만료(`exp`); 아직 유효하지 않음(`nbf`); `exp` 누락; `sub` 누락/빈 값; 잘못된 `aud`(문자열·배열 형태); 잘못된 `iss`; 알 수 없는 `kid`; 잘못된 서명; `< 2048` 비트 RSA JWK; `use:"enc"`/`key_ops:["encrypt"]` JWK.
|
|
133
|
+
- 생성이 거부함: non-HTTPS 또는 cross-origin `jwksUri`; 비-URL `issuer`; `clockSkewSeconds > 300`; `cryptoProvider.hmac` 누락. `127.0.0.1`, `169.254.169.254`, `::1`, 또는 RFC1918 CIDR로 resolve되는 `jwksUri`는 거부.
|
|
134
|
+
- 알 수 없는 `kid` 폭주가 60초 cooldown 내 **정확히 1회** JWKS refetch를 트리거; `> 1 MiB` JWKS 응답은 거부.
|
|
135
|
+
|
|
136
|
+
### 6.4 모든 satellite
|
|
137
|
+
|
|
138
|
+
- 각각 provenance + sigstore attestation으로 배포(0.7처럼 배포 후 검증).
|
|
139
|
+
|
|
140
|
+
## 7. 제안 PR 분해 (스택)
|
|
141
|
+
|
|
142
|
+
1. **Workspaces 전환**(새 배포 패키지 없음): 루트 `workspaces: [".", "satellites/*"]`, 루트를 **0.8.0**으로 bump, `crypto-kms`를 `satellites/crypto-kms/`로 이동(core `peer + dev` 의존성), 인라인 `canonicalize`를 `haechi/crypto`로 전환(+ parity 테스트), 테스트 재지정, 재생성된 `package-lock.json` 커밋, **no-leak + zero-dep CI 게이트** 추가(음성 테스트 포함), 루트 CI가 루트 `node --test`로 모든 workspace 테스트 실행. → §6.1
|
|
143
|
+
2. **`haechi-crypto-kms`:** 실제 AWS KMS 클라이언트(satellite만의 `@aws-sdk/client-kms` 의존성) + 충실한 mocked-AWS conformance CI + `publishConfig` + 접두사 태그 배포 워크플로(엄격한 정규식 가드) + Trusted Publisher 부트스트랩. → §6.2
|
|
144
|
+
3. **`haechi-auth-jwt`:** §2.4 전체 보안 명세를 구현하는 JWKS 검증 provider + identity 매핑 + §6.3 보안 게이트 테스트 + `publishConfig` + 접두사 태그 배포 워크플로. → §6.3
|
|
145
|
+
4. **0.8.0 릴리스 컷:** EN/KO 문서, packaging/roadmap/risk-register(OIDC+dashboard를 0.9로 이동)/api-stability, wiki, npm org / Trusted Publisher 런북(매핑 표 + 부트스트랩 순서 + 실패 모드).
|