@asafarim/envage 0.1.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.md +367 -0
- package/bin/envage.js +11 -0
- package/dist/cli/commands/decrypt.d.ts +12 -0
- package/dist/cli/commands/decrypt.d.ts.map +1 -0
- package/dist/cli/commands/decrypt.js +91 -0
- package/dist/cli/commands/decrypt.js.map +1 -0
- package/dist/cli/commands/encrypt.d.ts +11 -0
- package/dist/cli/commands/encrypt.d.ts.map +1 -0
- package/dist/cli/commands/encrypt.js +95 -0
- package/dist/cli/commands/encrypt.js.map +1 -0
- package/dist/cli/commands/init-key.d.ts +13 -0
- package/dist/cli/commands/init-key.d.ts.map +1 -0
- package/dist/cli/commands/init-key.js +32 -0
- package/dist/cli/commands/init-key.js.map +1 -0
- package/dist/cli/commands/status.d.ts +11 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +73 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +44 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/prompt.d.ts +9 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +62 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/config.d.ts +16 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +41 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/decrypt.d.ts +10 -0
- package/dist/lib/decrypt.d.ts.map +1 -0
- package/dist/lib/decrypt.js +60 -0
- package/dist/lib/decrypt.js.map +1 -0
- package/dist/lib/encrypt.d.ts +20 -0
- package/dist/lib/encrypt.d.ts.map +1 -0
- package/dist/lib/encrypt.js +75 -0
- package/dist/lib/encrypt.js.map +1 -0
- package/dist/lib/gitignore.d.ts +19 -0
- package/dist/lib/gitignore.d.ts.map +1 -0
- package/dist/lib/gitignore.js +82 -0
- package/dist/lib/gitignore.js.map +1 -0
- package/dist/lib/keygen.d.ts +30 -0
- package/dist/lib/keygen.d.ts.map +1 -0
- package/dist/lib/keygen.js +66 -0
- package/dist/lib/keygen.js.map +1 -0
- package/dist/lib/logger.d.ts +21 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +52 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/status.d.ts +14 -0
- package/dist/lib/status.d.ts.map +1 -0
- package/dist/lib/status.js +50 -0
- package/dist/lib/status.js.map +1 -0
- package/dist/types.d.ts +40 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# @asafarim/envage
|
|
2
|
+
|
|
3
|
+
> **Secure, age-based encryption for `.env` files across monorepo environments.**
|
|
4
|
+
|
|
5
|
+
`envage` lets you safely commit encrypted `.env` files to Git while keeping the
|
|
6
|
+
decrypted secrets out of version control. It uses the battle-tested
|
|
7
|
+
[age](https://age-encryption.org) encryption format — the same tool used by
|
|
8
|
+
Go, Rust, and DevSecOps toolchains worldwide.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Features
|
|
13
|
+
|
|
14
|
+
- 🔐 **age encryption** — uses `age-encryption` (pure TypeScript, no native deps)
|
|
15
|
+
- 🌍 **Multi-environment** — `dev`, `staging`, `prod` and any custom names
|
|
16
|
+
- 📁 **Monorepo-aware** — encrypt/decrypt all apps at once with `--all`
|
|
17
|
+
- 🔑 **Key or passphrase** — supports X25519 keypairs and passphrase-based encryption
|
|
18
|
+
- 🛡 **Git-safe** — auto-updates `.gitignore`; warns if you stage a decrypted file
|
|
19
|
+
- ✅ **Production guard** — interactive confirmation required to decrypt `prod` files
|
|
20
|
+
- 🧩 **Programmatic API** — use as a library in your own scripts
|
|
21
|
+
- 📦 **pnpm / monorepo friendly** — works seamlessly with pnpm workspaces
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
# In your monorepo root
|
|
29
|
+
pnpm add -D @asafarim/envage
|
|
30
|
+
|
|
31
|
+
# Or globally
|
|
32
|
+
pnpm add -g @asafarim/envage
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
### 1. Generate a keypair
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npx envage init-key
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This creates:
|
|
46
|
+
```
|
|
47
|
+
.age/key.txt ← private key (mode 0600, gitignored automatically)
|
|
48
|
+
.age/key.pub ← public key (safe to share with teammates)
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 2. Create your config
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# envage.config.json (at the monorepo root)
|
|
55
|
+
{
|
|
56
|
+
"apps": ["apps/web", "apps/admin", "packages/api"],
|
|
57
|
+
"envs": ["dev", "staging", "prod"],
|
|
58
|
+
"keyFile": ".age/key.txt"
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Encrypt your env files
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# Encrypt a single app/env
|
|
66
|
+
npx envage encrypt apps/web --env dev
|
|
67
|
+
|
|
68
|
+
# Encrypt all apps at once
|
|
69
|
+
npx envage encrypt --all --env prod
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### 4. Check status
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npx envage status
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
apps/web
|
|
80
|
+
dev 🔒 encrypted
|
|
81
|
+
staging 🔒 encrypted
|
|
82
|
+
prod 🔒 encrypted
|
|
83
|
+
|
|
84
|
+
apps/admin
|
|
85
|
+
dev 🔓 decrypted (not yet encrypted)
|
|
86
|
+
prod 🔒 encrypted
|
|
87
|
+
|
|
88
|
+
packages/api
|
|
89
|
+
dev 🔒 encrypted
|
|
90
|
+
prod 🔒 encrypted
|
|
91
|
+
|
|
92
|
+
9 entries — 8 encrypted 1 decrypted
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 5. Decrypt on a new machine
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
# Copy your private key to .age/key.txt (never commit it!)
|
|
99
|
+
npx envage decrypt apps/web --env dev
|
|
100
|
+
|
|
101
|
+
# Decrypt all at once
|
|
102
|
+
npx envage decrypt --all --env dev
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## CLI Reference
|
|
108
|
+
|
|
109
|
+
### `envage init-key`
|
|
110
|
+
|
|
111
|
+
Generate a new age X25519 keypair.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
envage init-key [--output <folder>]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Option | Default | Description |
|
|
118
|
+
|--------|---------|-------------|
|
|
119
|
+
| `--output` | `.age` | Output folder for `key.txt` and `key.pub` |
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
### `envage encrypt`
|
|
124
|
+
|
|
125
|
+
Encrypt `.env.<env>` → `.env.<env>.age`.
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
envage encrypt [path] [options]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
| Option | Default | Description |
|
|
132
|
+
|--------|---------|-------------|
|
|
133
|
+
| `--env` | `dev` | Environment name |
|
|
134
|
+
| `--key` | auto-detected | Path to age key file |
|
|
135
|
+
| `--passphrase` | — | Passphrase (prompted if omitted) |
|
|
136
|
+
| `--all` | — | Encrypt all apps in `envage.config.json` |
|
|
137
|
+
|
|
138
|
+
**Examples:**
|
|
139
|
+
```bash
|
|
140
|
+
envage encrypt apps/web --env dev
|
|
141
|
+
envage encrypt apps/web --env prod --key .age/key.txt
|
|
142
|
+
envage encrypt --all --env staging
|
|
143
|
+
envage encrypt apps/web --env dev --passphrase "my secret"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### `envage decrypt`
|
|
149
|
+
|
|
150
|
+
Decrypt `.env.<env>.age` → `.env.<env>`.
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
envage decrypt [path] [options]
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
| Option | Default | Description |
|
|
157
|
+
|--------|---------|-------------|
|
|
158
|
+
| `--env` | `dev` | Environment name |
|
|
159
|
+
| `--key` | auto-detected | Path to age private key file |
|
|
160
|
+
| `--passphrase` | — | Passphrase |
|
|
161
|
+
| `--all` | — | Decrypt all apps in `envage.config.json` |
|
|
162
|
+
|
|
163
|
+
> ⚠️ Decrypting `prod` or `production` environments always prompts for
|
|
164
|
+
> manual confirmation.
|
|
165
|
+
|
|
166
|
+
**Examples:**
|
|
167
|
+
```bash
|
|
168
|
+
envage decrypt apps/web --env dev
|
|
169
|
+
envage decrypt --all --env prod
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
### `envage status`
|
|
175
|
+
|
|
176
|
+
Show encryption status for all configured apps and environments.
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
envage status [path]
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
| Option | Description |
|
|
183
|
+
|--------|-------------|
|
|
184
|
+
| `--env` | Filter output to a single environment |
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Configuration — `envage.config.json`
|
|
189
|
+
|
|
190
|
+
Place this file at the root of your monorepo:
|
|
191
|
+
|
|
192
|
+
```json
|
|
193
|
+
{
|
|
194
|
+
"apps": [
|
|
195
|
+
"apps/web",
|
|
196
|
+
"apps/admin",
|
|
197
|
+
"packages/api"
|
|
198
|
+
],
|
|
199
|
+
"envs": ["dev", "staging", "prod"],
|
|
200
|
+
"keyFile": ".age/key.txt",
|
|
201
|
+
"keyPubFile": ".age/key.pub"
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
| Field | Type | Default | Description |
|
|
206
|
+
|-------|------|---------|-------------|
|
|
207
|
+
| `apps` | `string[]` | `[]` | App folder paths (relative to cwd) |
|
|
208
|
+
| `envs` | `string[]` | `["dev","staging","prod"]` | Environment names to manage |
|
|
209
|
+
| `keyFile` | `string` | `.age/key.txt` | Path to private key |
|
|
210
|
+
| `keyPubFile` | `string` | `.age/key.pub` | Path to public key |
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Programmatic API
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
import {
|
|
218
|
+
encryptEnv,
|
|
219
|
+
decryptEnv,
|
|
220
|
+
generateKeyPair,
|
|
221
|
+
generateKeyPairToFolder,
|
|
222
|
+
getEnvStatus,
|
|
223
|
+
loadConfig,
|
|
224
|
+
ensureGitignore,
|
|
225
|
+
} from "@asafarim/envage";
|
|
226
|
+
|
|
227
|
+
// Generate a keypair
|
|
228
|
+
const { identity, recipient } = await generateKeyPair();
|
|
229
|
+
|
|
230
|
+
// Write keypair to disk
|
|
231
|
+
await generateKeyPairToFolder(".age");
|
|
232
|
+
|
|
233
|
+
// Encrypt a file (key-based)
|
|
234
|
+
await encryptEnv({
|
|
235
|
+
folder: "apps/web",
|
|
236
|
+
env: "dev",
|
|
237
|
+
keyFile: ".age/key.txt",
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Encrypt a file (passphrase-based)
|
|
241
|
+
await encryptEnv({
|
|
242
|
+
folder: "apps/web",
|
|
243
|
+
env: "dev",
|
|
244
|
+
passphrase: "my-secret-passphrase",
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Decrypt a file
|
|
248
|
+
await decryptEnv({
|
|
249
|
+
folder: "apps/web",
|
|
250
|
+
env: "dev",
|
|
251
|
+
keyFile: ".age/key.txt",
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Check status
|
|
255
|
+
const config = await loadConfig();
|
|
256
|
+
const statuses = await getEnvStatus(config);
|
|
257
|
+
statuses.forEach((s) => {
|
|
258
|
+
console.log(`${s.folder} [${s.env}]: encrypted=${s.encrypted} decrypted=${s.decrypted}`);
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// Ensure .gitignore rules are present
|
|
262
|
+
await ensureGitignore();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Git Integration
|
|
268
|
+
|
|
269
|
+
### `.gitignore` rules
|
|
270
|
+
|
|
271
|
+
Running `envage init-key` automatically adds these rules to your `.gitignore`:
|
|
272
|
+
|
|
273
|
+
```gitignore
|
|
274
|
+
# --- envage managed ---
|
|
275
|
+
# Decrypted env files — never commit
|
|
276
|
+
.env*
|
|
277
|
+
!.env*.age
|
|
278
|
+
!.env.example
|
|
279
|
+
!.env.template
|
|
280
|
+
# Age private key
|
|
281
|
+
.age/key.txt
|
|
282
|
+
# --- end envage ---
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
### Staged file warning
|
|
286
|
+
|
|
287
|
+
If you accidentally `git add` a decrypted env file before encrypting, the
|
|
288
|
+
`encrypt` command will catch it and exit with:
|
|
289
|
+
|
|
290
|
+
```
|
|
291
|
+
✖ You are trying to commit a decrypted env file.
|
|
292
|
+
✖ Only encrypted .age files are allowed.
|
|
293
|
+
apps/web/.env.prod
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Optional: pre-commit hook
|
|
297
|
+
|
|
298
|
+
Add this to `.git/hooks/pre-commit` to prevent accidental commits entirely:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
#!/usr/bin/env bash
|
|
302
|
+
npx envage status 2>/dev/null | grep -q "decrypted" && \
|
|
303
|
+
echo "❌ You have decrypted env files. Run: npx envage encrypt --all" && \
|
|
304
|
+
exit 1
|
|
305
|
+
exit 0
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Monorepo Structure
|
|
311
|
+
|
|
312
|
+
```
|
|
313
|
+
monorepo/
|
|
314
|
+
├── .age/
|
|
315
|
+
│ ├── key.txt ← private key (gitignored)
|
|
316
|
+
│ └── key.pub ← public key (commit this)
|
|
317
|
+
├── apps/
|
|
318
|
+
│ ├── web/
|
|
319
|
+
│ │ ├── .env.dev ← decrypted (gitignored)
|
|
320
|
+
│ │ ├── .env.dev.age ← encrypted (committed ✅)
|
|
321
|
+
│ │ ├── .env.prod ← decrypted (gitignored)
|
|
322
|
+
│ │ └── .env.prod.age← encrypted (committed ✅)
|
|
323
|
+
│ └── admin/
|
|
324
|
+
│ ├── .env.dev.age
|
|
325
|
+
│ └── .env.prod.age
|
|
326
|
+
├── packages/
|
|
327
|
+
│ └── api/
|
|
328
|
+
│ ├── .env.dev.age
|
|
329
|
+
│ └── .env.prod.age
|
|
330
|
+
├── envage.config.json ← committed ✅
|
|
331
|
+
└── .gitignore ← auto-updated by envage
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
---
|
|
335
|
+
|
|
336
|
+
## Security Notes
|
|
337
|
+
|
|
338
|
+
- **Never commit `.age/key.txt`** — it is your private key. Share it with
|
|
339
|
+
teammates via a secrets manager (1Password, Vault, AWS SSM, etc.)
|
|
340
|
+
- **Decrypted values are never logged** — not in verbose mode, not in errors
|
|
341
|
+
- **Production decryption is gated** — always requires explicit `y` confirmation
|
|
342
|
+
- **Encrypted files are binary** — they cannot be accidentally read as text
|
|
343
|
+
- The `age` format provides authenticated encryption; tampering is detectable
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## Development
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Install dependencies
|
|
351
|
+
pnpm install
|
|
352
|
+
|
|
353
|
+
# Build
|
|
354
|
+
pnpm build
|
|
355
|
+
|
|
356
|
+
# Run tests
|
|
357
|
+
pnpm test
|
|
358
|
+
|
|
359
|
+
# Type-check only
|
|
360
|
+
pnpm lint
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
---
|
|
364
|
+
|
|
365
|
+
## License
|
|
366
|
+
|
|
367
|
+
MIT © [Ali Safarim](https://github.com/asafarim)
|
package/bin/envage.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @asafarim/envage CLI entrypoint
|
|
4
|
+
* Compiled output lives in dist/cli/index.js
|
|
5
|
+
*/
|
|
6
|
+
import { runCLI } from "../dist/cli/index.js";
|
|
7
|
+
|
|
8
|
+
runCLI().catch((err) => {
|
|
9
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
10
|
+
process.exit(1);
|
|
11
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI `decrypt` command.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* envage decrypt [path] [--env prod] [--key .age/key.txt]
|
|
6
|
+
* envage decrypt --all [--env prod]
|
|
7
|
+
*
|
|
8
|
+
* Production decryption always prompts for manual confirmation.
|
|
9
|
+
*/
|
|
10
|
+
import type { Command } from "commander";
|
|
11
|
+
export declare function registerDecrypt(program: Command): void;
|
|
12
|
+
//# sourceMappingURL=decrypt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/decrypt.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBzC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6CtD"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { decryptEnv } from "../../lib/decrypt.js";
|
|
3
|
+
import { loadConfig } from "../../lib/config.js";
|
|
4
|
+
import { logger } from "../../lib/logger.js";
|
|
5
|
+
import { confirm, promptPassphrase } from "../prompt.js";
|
|
6
|
+
const PROD_ENVS = new Set(["prod", "production"]);
|
|
7
|
+
export function registerDecrypt(program) {
|
|
8
|
+
program
|
|
9
|
+
.command("decrypt [path]")
|
|
10
|
+
.description("Decrypt .env.<env>.age → .env.<env>")
|
|
11
|
+
.option("-e, --env <env>", "environment name (e.g. dev, prod)", "dev")
|
|
12
|
+
.option("-k, --key <keyFile>", "path to age private key file (.age/key.txt)")
|
|
13
|
+
.option("-p, --passphrase <pass>", "passphrase for decryption")
|
|
14
|
+
.option("--all", "decrypt all apps defined in envage.config.json")
|
|
15
|
+
.action(async (folderArg, opts) => {
|
|
16
|
+
const envName = opts.env ?? "dev";
|
|
17
|
+
let keyFile = opts.key;
|
|
18
|
+
let passphrase = opts.passphrase;
|
|
19
|
+
// Production guard — always require explicit confirmation
|
|
20
|
+
if (PROD_ENVS.has(envName)) {
|
|
21
|
+
logger.warn(`You are about to decrypt PRODUCTION environment files.`);
|
|
22
|
+
const ok = await confirm("Are you sure you want to continue?");
|
|
23
|
+
if (!ok) {
|
|
24
|
+
logger.info("Aborted.");
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Resolve credential
|
|
29
|
+
if (!passphrase && !keyFile) {
|
|
30
|
+
const config = await loadConfig();
|
|
31
|
+
const defaultKey = config.keyFile;
|
|
32
|
+
try {
|
|
33
|
+
const { readIdentityFromFile } = await import("../../lib/keygen.js");
|
|
34
|
+
await readIdentityFromFile(path.resolve(process.cwd(), defaultKey));
|
|
35
|
+
keyFile = path.resolve(process.cwd(), defaultKey);
|
|
36
|
+
logger.detail(`Using key file: ${keyFile}`);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
logger.info("No key file found. Enter the passphrase for decryption:");
|
|
40
|
+
passphrase = await promptPassphrase();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (opts.all) {
|
|
44
|
+
await decryptAll(envName, keyFile, passphrase);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const folder = folderArg ? path.resolve(process.cwd(), folderArg) : process.cwd();
|
|
48
|
+
await decryptOne(folder, envName, keyFile, passphrase);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
async function decryptOne(folder, env, keyFile, passphrase) {
|
|
53
|
+
const fromFile = path.join(folder, `.env.${env}.age`);
|
|
54
|
+
const toFile = path.join(folder, `.env.${env}`);
|
|
55
|
+
logger.decrypting(fromFile, toFile);
|
|
56
|
+
try {
|
|
57
|
+
await decryptEnv({ folder, env, keyFile, passphrase });
|
|
58
|
+
logger.success("Done");
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
62
|
+
logger.error(msg);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function decryptAll(env, keyFile, passphrase) {
|
|
67
|
+
const config = await loadConfig();
|
|
68
|
+
if (config.apps.length === 0) {
|
|
69
|
+
logger.warn("No apps defined in envage.config.json. Nothing to decrypt.");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
let anyFailed = false;
|
|
73
|
+
for (const app of config.apps) {
|
|
74
|
+
const folder = path.resolve(process.cwd(), app);
|
|
75
|
+
const fromFile = path.join(folder, `.env.${env}.age`);
|
|
76
|
+
const toFile = path.join(folder, `.env.${env}`);
|
|
77
|
+
logger.decrypting(fromFile, toFile);
|
|
78
|
+
try {
|
|
79
|
+
await decryptEnv({ folder, env, keyFile, passphrase });
|
|
80
|
+
logger.success("Done");
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
84
|
+
logger.error(msg);
|
|
85
|
+
anyFailed = true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (anyFailed)
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=decrypt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decrypt.js","sourceRoot":"","sources":["../../../src/cli/commands/decrypt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI `encrypt` command.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* envage encrypt [path] [--env dev] [--key .age/key.txt] [--passphrase ...]
|
|
6
|
+
* envage encrypt --all [--env dev] [--key .age/key.txt]
|
|
7
|
+
*/
|
|
8
|
+
import type { Command } from "commander";
|
|
9
|
+
export declare function registerEncrypt(program: Command): void;
|
|
10
|
+
export declare function runEncryptAll(env: string, keyFile?: string, passphrase?: string): Promise<void>;
|
|
11
|
+
//# sourceMappingURL=encrypt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypt.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/encrypt.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAezC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA+CtD;AAqDD,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAErG"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { encryptEnv } from "../../lib/encrypt.js";
|
|
3
|
+
import { loadConfig } from "../../lib/config.js";
|
|
4
|
+
import { checkStagedEnvFiles } from "../../lib/gitignore.js";
|
|
5
|
+
import { logger } from "../../lib/logger.js";
|
|
6
|
+
import { promptPassphrase } from "../prompt.js";
|
|
7
|
+
export function registerEncrypt(program) {
|
|
8
|
+
program
|
|
9
|
+
.command("encrypt [path]")
|
|
10
|
+
.description("Encrypt .env.<env> → .env.<env>.age")
|
|
11
|
+
.option("-e, --env <env>", "environment name (e.g. dev, prod)", "dev")
|
|
12
|
+
.option("-k, --key <keyFile>", "path to age key file (.age/key.txt or .age/key.pub)")
|
|
13
|
+
.option("-p, --passphrase <pass>", "passphrase for encryption (not recommended via CLI arg — use without value to be prompted)")
|
|
14
|
+
.option("--all", "encrypt all apps defined in envage.config.json")
|
|
15
|
+
.action(async (folderArg, opts) => {
|
|
16
|
+
// Warn about staged decrypted files first
|
|
17
|
+
const staged = checkStagedEnvFiles();
|
|
18
|
+
if (staged.length > 0) {
|
|
19
|
+
logger.error("You are trying to commit a decrypted env file.");
|
|
20
|
+
logger.error("Only encrypted .age files are allowed.");
|
|
21
|
+
staged.forEach((f) => logger.detail(f));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
// Resolve encryption credential
|
|
25
|
+
let passphrase = opts.passphrase;
|
|
26
|
+
let keyFile = opts.key;
|
|
27
|
+
if (!passphrase && !keyFile) {
|
|
28
|
+
// Auto-detect default key file
|
|
29
|
+
const config = await loadConfig();
|
|
30
|
+
const defaultKey = config.keyFile;
|
|
31
|
+
try {
|
|
32
|
+
const { readIdentityFromFile } = await import("../../lib/keygen.js");
|
|
33
|
+
await readIdentityFromFile(path.resolve(process.cwd(), defaultKey));
|
|
34
|
+
keyFile = path.resolve(process.cwd(), defaultKey);
|
|
35
|
+
logger.detail(`Using key file: ${keyFile}`);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// No key file found — prompt for passphrase
|
|
39
|
+
logger.info("No key file found. Enter a passphrase for encryption:");
|
|
40
|
+
passphrase = await promptPassphrase();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const envName = opts.env ?? "dev";
|
|
44
|
+
if (opts.all) {
|
|
45
|
+
await encryptAll(envName, keyFile, passphrase);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
const folder = folderArg ? path.resolve(process.cwd(), folderArg) : process.cwd();
|
|
49
|
+
await encryptOne(folder, envName, keyFile, passphrase);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async function encryptOne(folder, env, keyFile, passphrase) {
|
|
54
|
+
const fromFile = path.join(folder, `.env.${env}`);
|
|
55
|
+
const toFile = path.join(folder, `.env.${env}.age`);
|
|
56
|
+
logger.encrypting(fromFile, toFile);
|
|
57
|
+
try {
|
|
58
|
+
await encryptEnv({ folder, env, keyFile, passphrase });
|
|
59
|
+
logger.success("Done");
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
63
|
+
logger.error(msg);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async function encryptAll(env, keyFile, passphrase) {
|
|
68
|
+
const config = await loadConfig();
|
|
69
|
+
if (config.apps.length === 0) {
|
|
70
|
+
logger.warn("No apps defined in envage.config.json. Nothing to encrypt.");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let anyFailed = false;
|
|
74
|
+
for (const app of config.apps) {
|
|
75
|
+
const folder = path.resolve(process.cwd(), app);
|
|
76
|
+
const fromFile = path.join(folder, `.env.${env}`);
|
|
77
|
+
const toFile = path.join(folder, `.env.${env}.age`);
|
|
78
|
+
logger.encrypting(fromFile, toFile);
|
|
79
|
+
try {
|
|
80
|
+
await encryptEnv({ folder, env, keyFile, passphrase });
|
|
81
|
+
logger.success("Done");
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
85
|
+
logger.error(msg);
|
|
86
|
+
anyFailed = true;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (anyFailed)
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
export async function runEncryptAll(env, keyFile, passphrase) {
|
|
93
|
+
await encryptAll(env, keyFile, passphrase);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=encrypt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypt.js","sourceRoot":"","sources":["../../../src/cli/commands/encrypt.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI `init-key` command.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* envage init-key [--output .age]
|
|
6
|
+
*
|
|
7
|
+
* Generates a new age X25519 keypair and writes:
|
|
8
|
+
* .age/key.txt (private key, mode 0600)
|
|
9
|
+
* .age/key.pub (public key)
|
|
10
|
+
*/
|
|
11
|
+
import type { Command } from "commander";
|
|
12
|
+
export declare function registerInitKey(program: Command): void;
|
|
13
|
+
//# sourceMappingURL=init-key.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-key.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/init-key.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASzC,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAkCtD"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { generateKeyPairToFolder } from "../../lib/keygen.js";
|
|
2
|
+
import { ensureGitignore } from "../../lib/gitignore.js";
|
|
3
|
+
import { logger } from "../../lib/logger.js";
|
|
4
|
+
export function registerInitKey(program) {
|
|
5
|
+
program
|
|
6
|
+
.command("init-key")
|
|
7
|
+
.description("Generate a new age keypair (.age/key.txt and .age/key.pub)")
|
|
8
|
+
.option("-o, --output <folder>", "output folder for the keypair", ".age")
|
|
9
|
+
.action(async (opts) => {
|
|
10
|
+
const outputFolder = opts.output ?? ".age";
|
|
11
|
+
logger.info(`Generating age X25519 keypair in ${outputFolder}/`);
|
|
12
|
+
try {
|
|
13
|
+
const { keyFile, pubFile } = await generateKeyPairToFolder(outputFolder);
|
|
14
|
+
logger.success(`Private key written to: ${keyFile}`);
|
|
15
|
+
logger.success(`Public key written to: ${pubFile}`);
|
|
16
|
+
logger.info("Updating .gitignore to protect the private key…");
|
|
17
|
+
await ensureGitignore();
|
|
18
|
+
logger.success(".gitignore updated.");
|
|
19
|
+
logger.warn("IMPORTANT: Never commit your private key (.age/key.txt).");
|
|
20
|
+
logger.warn("Share .age/key.pub with teammates who need to encrypt files.");
|
|
21
|
+
logger.raw("");
|
|
22
|
+
logger.raw(" To encrypt: envage encrypt apps/web --env dev");
|
|
23
|
+
logger.raw(" To decrypt: envage decrypt apps/web --env dev");
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
27
|
+
logger.error(`Failed to generate keypair: ${msg}`);
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=init-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-key.js","sourceRoot":"","sources":["../../../src/cli/commands/init-key.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAM7C,MAAM,UAAU,eAAe,CAAC,OAAgB;IAC9C,OAAO;SACJ,OAAO,CAAC,UAAU,CAAC;SACnB,WAAW,CAAC,4DAA4D,CAAC;SACzE,MAAM,CAAC,uBAAuB,EAAE,+BAA+B,EAAE,MAAM,CAAC;SACxE,MAAM,CAAC,KAAK,EAAE,IAAoB,EAAE,EAAE;QACrC,MAAM,YAAY,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;QAE3C,MAAM,CAAC,IAAI,CAAC,oCAAoC,YAAY,GAAG,CAAC,CAAC;QAEjE,IAAI,CAAC;YACH,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,uBAAuB,CAAC,YAAY,CAAC,CAAC;YACzE,MAAM,CAAC,OAAO,CAAC,2BAA2B,OAAO,EAAE,CAAC,CAAC;YACrD,MAAM,CAAC,OAAO,CAAC,2BAA2B,OAAO,EAAE,CAAC,CAAC;YAErD,MAAM,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;YAC/D,MAAM,eAAe,EAAE,CAAC;YACxB,MAAM,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;YAEtC,MAAM,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;YACxE,MAAM,CAAC,IAAI,CAAC,8DAA8D,CAAC,CAAC;YAC5E,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACf,MAAM,CAAC,GAAG,CACR,kDAAkD,CACnD,CAAC;YACF,MAAM,CAAC,GAAG,CACR,kDAAkD,CACnD,CAAC;QACJ,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,MAAM,CAAC,KAAK,CAAC,+BAA+B,GAAG,EAAE,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI `status` command.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* envage status [path]
|
|
6
|
+
*
|
|
7
|
+
* Shows which env files are encrypted/decrypted for all configured apps.
|
|
8
|
+
*/
|
|
9
|
+
import type { Command } from "commander";
|
|
10
|
+
export declare function registerStatus(program: Command): void;
|
|
11
|
+
//# sourceMappingURL=status.d.ts.map
|