@himorogy/enclave-env 0.2.0 → 0.2.1

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.md CHANGED
@@ -4,119 +4,77 @@
4
4
 
5
5
  [dotenvx](https://dotenvx.com/) による暗号化と実行時ガードを組み合わせ、LLM が動く devcontainer から prod キーを構造的に隔離します。
6
6
 
7
- ## このパッケージが提供するもの
8
-
9
- | 種別 | 内容 |
10
- |---|---|
11
- | **CLI** | `encrypt` / `decrypt` — env ファイルの暗号化・復号 |
12
- | **CLI** | `check` — 平文 `.env*` のコミットをブロック(pre-commit hook 用) |
13
- | **シェルスクリプト** | `scripts/init-check-dev.sh` — dev コンテナ起動時のセキュリティチェック(`initializeCommand` 用) |
14
- | **シェルスクリプト** | `scripts/init-check-prod.sh` — prod コンテナ起動時の相互排他チェック(`initializeCommand` 用) |
15
- | **テンプレート** | `templates/` — prod-shell スクリプト・devcontainer 構成の参照実装 |
16
-
17
- **このパッケージが担わないもの:** devcontainer.json の作成・管理、prod 環境の構築。これらはプロジェクト側の責務です。テンプレートはその参照実装として提供します。
18
-
19
- # セキュリティ思想
7
+ ---
20
8
 
21
- ## なぜ必要か
9
+ # できること
22
10
 
23
- Claude Code などの LLM は devcontainer 内から bind mount 経由でワークスペース全体にアクセスできます。`.env.production` または復号キーを平文で置いておくと、LLM が意図せず本番秘密情報を参照・出力するリスクがあります。
24
- prod用復号キーをワークスペース外に置くことで devcontainer への同期を防ぎ、 `.env.production` が平文の状態で devcontainer に同期されないように複数層でブロックします。
11
+ ## 平文 env のコミットをブロックする
25
12
 
26
- `.env.production` に対する単一変数の追加・更新は下記の通り `dotenvx set` で対応できます。
13
+ ステージ済みの `.env*` ファイルに暗号化されていないものがあればコミットを中断します。
27
14
 
28
- ```sh
29
- dotenvx set DATABASE_URL "postgres://..." -f .env.production
15
+ ```json
16
+ {
17
+ "simple-git-hooks": {
18
+ "pre-commit": "sh node_modules/@himorogy/enclave-env/scripts/check.sh"
19
+ }
20
+ }
30
21
  ```
31
22
 
32
- - `.env.production` に含まれる public key だけで暗号化するため private key 不要
33
- - 平文ファイルが一切ディスクに書き出されない
34
- - dev コンテナ内でも実行可能(ただしコマンドヒストリーを削除しないと、LLMがセットした値を読み取ることが可能である点に留意)
35
-
36
- ## prod キー隔離の弊害
37
-
38
- prod の private key を dev コンテナの外に置くと、dev コンテナ内から `.env.production` を復号できなくなります。
39
- これは意図した動作ですが、複数変数を一覧で確認しながら編集したい場合や、prod deploy・DB メンテナンスを伴う作業には対応できません。そうしたワークフローのために prod 環境を用意します。
40
-
41
- ## 守るべき原則
42
-
43
- prod 環境の実現手段は問いませんが、以下の 2 つを守ることがこのツールの前提です:
44
-
45
- | 原則 | 目的 |
46
- |---|---|
47
- | **prod の private key は常にワークスペース外に置く** | bind mount 経由で LLM に渡らない |
48
- | **prod 操作は dev コンテナが停止した状態で行う** | 復号した平文を LLM から守る |
49
-
50
- ---
51
-
52
- # prod 環境の用意
53
-
54
- ## prod 環境の活用シナリオ
55
-
56
- ### 全復号して操作する
23
+ 使用する機能:`scripts/check.sh`(内部で `enclave-env check` を呼び出し)
57
24
 
58
- 複数変数をまとめて確認・変更したい場合:
25
+ ## env ファイルを暗号化・復号する
59
26
 
60
27
  ```sh
61
- # 1. 復号
62
- enclave-env decrypt --env prod
63
-
64
- # 2. エディタで編集
65
- vim .env.production
66
-
67
- # 3. 再暗号化
68
- enclave-env encrypt --env prod
28
+ enclave-env encrypt --env local # .env を in-place 暗号化
29
+ enclave-env decrypt --env local # .env を in-place 復号
69
30
  ```
70
31
 
71
- `protected: true` の環境では、手順 1 が dev コンテナ稼働中にブロックされます。
32
+ `--env` に指定する名前は `enclave-env` 設定ファイルの `ENV_<NAME>_FILE` に対応します。
33
+
34
+ ## prod secrets を LLM から守る
72
35
 
73
- ### prod 環境の DB 整備や deploy
36
+ ### 単一変数を追加・更新する
74
37
 
75
- prod deploy・DB マイグレーションなど、prod の認証情報を使った操作も prod 環境内で完結できます。
38
+ `dotenvx set` を使えば private key 不要で暗号化したまま書き込めます:
76
39
 
77
40
  ```sh
78
- pnpm deploy
79
- pnpm db:migrate
41
+ dotenvx set DATABASE_URL "postgres://..." -f .env.production
80
42
  ```
81
43
 
82
- ## 運用上の注意点と 2 層の防御
83
-
84
- 全復号フロー中は `.env.production` が平文でディスクに存在します。この間に dev コンテナが起動すると、bind mount 経由で LLM が平文にアクセスできてしまいます。
44
+ - `.env.production` public key だけで暗号化するため private key 不要
45
+ - 平文ファイルが一切ディスクに書き出されない
46
+ - dev コンテナ内でも実行可能(コマンドヒストリー経由で LLM が値を読み取れる点に留意)
85
47
 
86
- enclave-env はこれを 2 つのレイヤーで防ぎます:
48
+ ### prod キーをワークスペース外に置く
87
49
 
88
- | レイヤー | タイミング | 担う側 | 実装 |
89
- |---|---|---|---|
90
- | 起動時 | devcontainer 起動 | **プロジェクト** が `initializeCommand` に登録 | `scripts/init-check-dev.sh` / `scripts/init-check-prod.sh` |
91
- | 実行時 | `decrypt` 実行時 | **ライブラリ** が自動で実行 | `protected` フラグが付いた環境で dev コンテナの稼働を確認しブロック |
50
+ | 環境 | キーの保管場所 |
51
+ |---|---|
52
+ | local / dev | `.devcontainer/dev/.env.container`(gitignore 対象) |
53
+ | prod | `~/.config/<your-project>/.env.container`(ワークスペース外) |
92
54
 
93
- 実行時ガードは `DEVCONTAINER=true`(コンテナ内からの実行)の場合はスキップされます。起動時ガードで保証済みのためです。
55
+ prod キーをワークスペース外に置くことで bind mount 経由で LLM に渡るリスクをなくします。`ENV_PROD_PROTECTED=true` を設定すると、`decrypt` 実行時に dev コンテナが稼働中かどうかを確認しブロックします。
94
56
 
95
- ## 運用案
57
+ ## prod 操作環境を用意する(全復号・deploy・DB)
96
58
 
97
- prod キーが使える実行環境の用意方法です。
59
+ prod の private key を dev コンテナ外に置くと、コンテナ内から `.env.production` を復号できなくなります。これは意図した動作ですが、複数変数の編集や prod deploy には prod 専用環境が必要です。
98
60
 
99
61
  ### 選択肢 1:ホスト直実行
100
62
 
101
- 最もシンプル。dev コンテナを停止した状態でホストから実行します。
63
+ 最もシンプル。dev コンテナを停止した状態でホストから実行します。ホストに Node.js と `enclave-env` のインストールが必要です。
102
64
 
103
65
  ```sh
104
66
  # DOTENV_PRIVATE_KEY_PRODUCTION が使える状態で
105
67
  pnpm decrypt-env:prod
106
68
  ```
107
69
 
108
- ホストに Node.js と `enclave-env` のインストールが必要です。
109
-
110
70
  ### 選択肢 2:prod-shell スクリプト(推奨)
111
71
 
112
- Docker だけで prod 操作環境を再現するスクリプトを用意します。相互排他チェック・コンテナ起動・prod キー注入を 1 コマンドで行います。
72
+ 相互排他チェック・コンテナ起動・prod キー注入を 1 コマンドで行います。コンテナ内で `enclave-env`・`dotenvx`・各種デプロイツールが使えます。
113
73
 
114
74
  ```sh
115
75
  sh scripts/prod-shell.sh
116
76
  ```
117
77
 
118
- コンテナ内で `enclave-env`・`dotenvx`・各種デプロイツールが使えます。macOS / Linux 間の環境差が出ないため、CI/CD 安定前の prod deploy や DB メンテナンスも同じコンテナ内で完結します。
119
-
120
78
  テンプレート:[`templates/prod-shell.sh`](./templates/prod-shell.sh)
121
79
 
122
80
  ### 選択肢 3:2 層 devcontainer
@@ -138,6 +96,23 @@ VS Code の「Reopen in Container」で prod devcontainer に切り替える構
138
96
 
139
97
  テンプレート:[`templates/devcontainer/`](./templates/devcontainer/)
140
98
 
99
+ ## devcontainer 起動時に安全性を検証する
100
+
101
+ `initializeCommand` にシェルスクリプトを登録することで、コンテナ起動前に安全性を確認します。
102
+
103
+ | スクリプト | 登録先 | チェック内容 |
104
+ |---|---|---|
105
+ | `scripts/init-check-dev.sh` | dev コンテナの `initializeCommand` | prod コンテナ稼働中なら起動をブロック(2 層 devcontainer 使用時) |
106
+ | `scripts/init-check-prod.sh` | prod コンテナの `initializeCommand` | dev コンテナ稼働中なら起動をブロック |
107
+
108
+ ```json
109
+ {
110
+ "initializeCommand": "source enclave-env && sh node_modules/@himorogy/enclave-env/scripts/init-check-dev.sh"
111
+ }
112
+ ```
113
+
114
+ > 実行時ガード(`protected` フラグ)と起動時ガードの 2 層で平文露出を防ぎます。詳細は「セキュリティ設計」を参照してください。
115
+
141
116
  ---
142
117
 
143
118
  # インストール
@@ -192,7 +167,37 @@ pnpm encrypt-env
192
167
 
193
168
  生成された `DOTENV_PUBLIC_KEY` はリポジトリにコミットします。`.env.keys` はコミットしないでください。
194
169
 
195
- # CLI リファレンス
170
+ ---
171
+
172
+ # セキュリティ設計
173
+
174
+ ## 守るべき原則
175
+
176
+ prod 環境の実現手段は問いませんが、以下の 2 つを守ることがこのツールの前提です:
177
+
178
+ | 原則 | 目的 |
179
+ |---|---|
180
+ | **prod の private key は常にワークスペース外に置く** | bind mount 経由で LLM に渡らない |
181
+ | **prod 操作は dev コンテナが停止した状態で行う** | 復号した平文を LLM から守る |
182
+
183
+ ## 2 層の防御
184
+
185
+ 全復号フロー中は `.env.production` が平文でディスクに存在します。この間に dev コンテナが起動すると、bind mount 経由で LLM が平文にアクセスできてしまいます。
186
+
187
+ enclave-env はこれを 2 つのレイヤーで防ぎます:
188
+
189
+ | レイヤー | タイミング | 担う側 | 実装 |
190
+ |---|---|---|---|
191
+ | 起動時 | devcontainer 起動 | **プロジェクト** が `initializeCommand` に登録 | `scripts/init-check-dev.sh` / `scripts/init-check-prod.sh` |
192
+ | 実行時 | `decrypt` 実行時 | **ライブラリ** が自動で実行 | `protected` フラグが付いた環境で dev コンテナの稼働を確認しブロック |
193
+
194
+ 実行時ガードは `DEVCONTAINER=true`(コンテナ内からの実行)の場合はスキップされます。起動時ガードで保証済みのためです。
195
+
196
+ ---
197
+
198
+ # リファレンス
199
+
200
+ ## CLI
196
201
 
197
202
  ```
198
203
  enclave-env encrypt --env <environment> # 指定環境の env ファイルを in-place 暗号化
@@ -209,7 +214,7 @@ scripts/init-check-dev.sh # dev コンテナ起動時(enclave-env を sourc
209
214
  scripts/init-check-prod.sh # prod コンテナ起動時(enclave-env を source して実行)
210
215
  ```
211
216
 
212
- # `enclave-env` 設定リファレンス
217
+ ## `enclave-env` 設定キー
213
218
 
214
219
  | キー | 説明 |
215
220
  |---|---|
@@ -221,16 +226,7 @@ scripts/init-check-prod.sh # prod コンテナ起動時(enclave-env を sour
221
226
 
222
227
  `<NAME>` は大文字・数字・アンダースコアで構成し、`--env` オプションでは小文字で参照します。例:`ENV_PROD_FILE` → `--env prod`
223
228
 
224
- # キー管理の推奨構成
225
-
226
- | 環境 | キーの保管場所 |
227
- |---|---|
228
- | local / dev | `.devcontainer/dev/.env.container`(gitignore 対象) |
229
- | prod | `~/.config/<your-project>/.env.container`(ワークスペース外) |
230
-
231
- prod キーをワークスペース外に置くことで、bind mount 経由で LLM に渡るリスクをなくします。
232
-
233
- # Git 管理ポリシー(推奨)
229
+ ## Git 管理ポリシー(推奨)
234
230
 
235
231
  | ファイル | Git 管理 |
236
232
  |---|---|
@@ -239,6 +235,8 @@ prod キーをワークスペース外に置くことで、bind mount 経由で
239
235
  | `.env.keys*` | ❌ 必ず gitignore |
240
236
  | `enclave-env` | ✅ |
241
237
 
238
+ ---
239
+
242
240
  # 機能一覧とテスト状況
243
241
 
244
242
  | 機能 | 種別 | テスト |
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@himorogy/enclave-env",
9
- version: "0.2.0",
9
+ version: "0.2.1",
10
10
  description: "Encrypted env management that keeps secrets out of LLM reach",
11
11
  packageManager: "pnpm@10.33.2",
12
12
  type: "module",
@@ -20,8 +20,8 @@ var package_default = {
20
20
  format: "biome check --write .",
21
21
  lint: "biome ci .",
22
22
  test: "pnpm test:ts && pnpm test:sh",
23
- "test:ts": "tsc -p tsconfig.test.json && node --test dist/test/config.test.js",
24
- "test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh",
23
+ "test:ts": "tsc -p tsconfig.test.json && node --test dist/test/tests/config.test.js dist/test/tests/security.test.js",
24
+ "test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh && sh tests/init-check-prod.test.sh",
25
25
  release: "sh release.sh"
26
26
  },
27
27
  dependencies: {
@@ -87,6 +87,8 @@ function check() {
87
87
 
88
88
  // src/commands/decrypt.ts
89
89
  import { execSync as execSync3 } from "child_process";
90
+ import { createRequire } from "module";
91
+ import path from "path";
90
92
 
91
93
  // src/config.ts
92
94
  import { readFileSync as readFileSync2 } from "fs";
@@ -176,6 +178,11 @@ function checkDevContainerNotRunning(containerName) {
176
178
  }
177
179
 
178
180
  // src/commands/decrypt.ts
181
+ var require2 = createRequire(import.meta.url);
182
+ var dotenvxBin = path.join(
183
+ path.dirname(require2.resolve("@dotenvx/dotenvx/package.json")),
184
+ "src/cli/dotenvx.js"
185
+ );
179
186
  function decryptEnv(config, env) {
180
187
  if (config.mode !== "single") {
181
188
  throw new Error(`Mode "${config.mode}" is not yet supported`);
@@ -185,17 +192,28 @@ function decryptEnv(config, env) {
185
192
  checkDevContainerNotRunning(config.security.devContainerName);
186
193
  }
187
194
  const filePath = resolveEnvFile(config, env);
188
- execSync3(`dotenvx decrypt -f "${filePath}"`, { stdio: "inherit" });
195
+ execSync3(`node "${dotenvxBin}" decrypt -f "${filePath}"`, {
196
+ stdio: "inherit"
197
+ });
189
198
  }
190
199
 
191
200
  // src/commands/encrypt.ts
192
201
  import { execSync as execSync4 } from "child_process";
202
+ import { createRequire as createRequire2 } from "module";
203
+ import path2 from "path";
204
+ var require3 = createRequire2(import.meta.url);
205
+ var dotenvxBin2 = path2.join(
206
+ path2.dirname(require3.resolve("@dotenvx/dotenvx/package.json")),
207
+ "src/cli/dotenvx.js"
208
+ );
193
209
  function encryptEnv(config, env) {
194
210
  if (config.mode !== "single") {
195
211
  throw new Error(`Mode "${config.mode}" is not yet supported`);
196
212
  }
197
213
  const filePath = resolveEnvFile(config, env);
198
- execSync4(`dotenvx encrypt -f "${filePath}"`, { stdio: "inherit" });
214
+ execSync4(`node "${dotenvxBin2}" encrypt -f "${filePath}"`, {
215
+ stdio: "inherit"
216
+ });
199
217
  }
200
218
 
201
219
  // src/cli.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@himorogy/enclave-env",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Encrypted env management that keeps secrets out of LLM reach",
5
5
  "packageManager": "pnpm@10.33.2",
6
6
  "type": "module",
@@ -14,8 +14,8 @@
14
14
  "format": "biome check --write .",
15
15
  "lint": "biome ci .",
16
16
  "test": "pnpm test:ts && pnpm test:sh",
17
- "test:ts": "tsc -p tsconfig.test.json && node --test dist/test/config.test.js",
18
- "test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh",
17
+ "test:ts": "tsc -p tsconfig.test.json && node --test dist/test/tests/config.test.js dist/test/tests/security.test.js",
18
+ "test:sh": "sh tests/check.test.sh && sh tests/init-check-dev.test.sh && sh tests/init-check-prod.test.sh",
19
19
  "release": "sh release.sh"
20
20
  },
21
21
  "dependencies": {
package/scripts/check.sh CHANGED
@@ -16,7 +16,7 @@ for file in $(git diff --cached --name-only | grep -E '(^|/)\.env|(^|/)secret\.e
16
16
  ''|'#'*|DOTENV_PUBLIC_KEY*) continue ;;
17
17
  esac
18
18
 
19
- if ! echo "$line" | grep -qE '^[A-Z0-9_]+=encrypted:'; then
19
+ if ! echo "$line" | grep -qE '^[A-Z0-9_]+="?encrypted:'; then
20
20
  echo "❌ Error: $file contains unencrypted value:"
21
21
  echo " $line"
22
22
  echo " Run: pnpm dotenvx encrypt"
@@ -32,7 +32,7 @@ find . -type f \
32
32
  case "$line" in
33
33
  ''|'#'*|DOTENV_PUBLIC_KEY*) continue ;;
34
34
  esac
35
- if ! echo "$line" | grep -qE '^[A-Z0-9_]+=encrypted:'; then
35
+ if ! echo "$line" | grep -qE '^[A-Z0-9_]+="?encrypted:'; then
36
36
  if [ "$FILE_HEADER_SHOWN" = "0" ]; then
37
37
  echo "❌ ERROR: $FILE contains unencrypted values:"
38
38
  FILE_HEADER_SHOWN=1