@chriscode/hush 4.2.0 → 5.0.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/dist/cli.js +58 -29
- package/dist/commands/check.d.ts +3 -3
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +30 -33
- package/dist/commands/decrypt.d.ts +2 -2
- package/dist/commands/decrypt.d.ts.map +1 -1
- package/dist/commands/decrypt.js +52 -55
- package/dist/commands/edit.d.ts +2 -2
- package/dist/commands/edit.d.ts.map +1 -1
- package/dist/commands/edit.js +10 -12
- package/dist/commands/encrypt.d.ts +2 -2
- package/dist/commands/encrypt.d.ts.map +1 -1
- package/dist/commands/encrypt.js +27 -29
- package/dist/commands/expansions.d.ts +2 -2
- package/dist/commands/expansions.d.ts.map +1 -1
- package/dist/commands/expansions.js +46 -44
- package/dist/commands/has.d.ts +2 -2
- package/dist/commands/has.d.ts.map +1 -1
- package/dist/commands/has.js +12 -15
- package/dist/commands/init.d.ts +2 -2
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +107 -87
- package/dist/commands/inspect.d.ts +2 -2
- package/dist/commands/inspect.d.ts.map +1 -1
- package/dist/commands/inspect.js +14 -16
- package/dist/commands/keys.d.ts +2 -1
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +47 -49
- package/dist/commands/list.d.ts +2 -2
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +11 -14
- package/dist/commands/migrate.d.ts +7 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +117 -0
- package/dist/commands/push.d.ts +2 -2
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +41 -45
- package/dist/commands/resolve.d.ts +2 -2
- package/dist/commands/resolve.d.ts.map +1 -1
- package/dist/commands/resolve.js +25 -28
- package/dist/commands/run.d.ts +2 -2
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +35 -39
- package/dist/commands/set.d.ts +2 -2
- package/dist/commands/set.d.ts.map +1 -1
- package/dist/commands/set.js +61 -70
- package/dist/commands/skill.d.ts +2 -2
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +186 -487
- package/dist/commands/status.d.ts +2 -2
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +52 -55
- package/dist/commands/template.d.ts +2 -2
- package/dist/commands/template.d.ts.map +1 -1
- package/dist/commands/template.js +36 -39
- package/dist/commands/trace.d.ts +2 -2
- package/dist/commands/trace.d.ts.map +1 -1
- package/dist/commands/trace.js +16 -19
- package/dist/config/loader.js +3 -3
- package/dist/context.d.ts +3 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +59 -0
- package/dist/core/parse.js +3 -3
- package/dist/core/sops.js +9 -9
- package/dist/core/template.d.ts +2 -2
- package/dist/core/template.d.ts.map +1 -1
- package/dist/core/template.js +11 -12
- package/dist/lib/age.js +9 -9
- package/dist/lib/fs.d.ts +25 -0
- package/dist/lib/fs.d.ts.map +1 -0
- package/dist/lib/fs.js +36 -0
- package/dist/lib/onepassword.d.ts.map +1 -1
- package/dist/lib/onepassword.js +41 -4
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +4 -4
- package/dist/utils/version-check.js +5 -5
- package/package.json +3 -2
package/dist/commands/skill.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
2
1
|
import { createInterface } from 'node:readline';
|
|
3
2
|
import { homedir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
3
|
import pc from 'picocolors';
|
|
6
4
|
const SKILL_FILES = {
|
|
7
5
|
'SKILL.md': `---
|
|
8
6
|
name: hush-secrets
|
|
9
|
-
description: Manage secrets safely using Hush CLI. Use when working with .
|
|
7
|
+
description: Manage secrets safely using Hush CLI. Use when working with .hush files, environment variables, secrets, API keys, database URLs, credentials, or configuration. NEVER read .hush files directly - always use hush commands instead to prevent exposing secrets to the LLM.
|
|
10
8
|
allowed-tools: Bash(hush:*), Bash(npx hush:*), Bash(brew:*), Bash(npm:*), Bash(pnpm:*), Bash(age-keygen:*), Read, Grep, Glob, Write, Bash(cat:*), Bash(grep:*)
|
|
11
9
|
---
|
|
12
10
|
|
|
13
11
|
# Hush - AI-Native Secrets Management
|
|
14
12
|
|
|
15
|
-
**CRITICAL: NEVER read
|
|
13
|
+
**CRITICAL: NEVER read .hush files directly.** Always use \`npx hush status\`, \`npx hush inspect\`, or \`npx hush has\` to check secrets.
|
|
16
14
|
|
|
17
|
-
Hush keeps secrets **encrypted at rest** at the project root. Subdirectory \`.
|
|
15
|
+
Hush keeps secrets **encrypted at rest** at the project root using \`.hush.encrypted\` files. Subdirectory \`.hush\` files are **templates** (safe to commit) that reference root secrets via \`\${VAR}\` syntax.
|
|
18
16
|
|
|
19
17
|
## First Step: Investigate Current State
|
|
20
18
|
|
|
@@ -35,13 +33,13 @@ This tells you:
|
|
|
35
33
|
|
|
36
34
|
| You See | What It Means | Action |
|
|
37
35
|
|---------|---------------|--------|
|
|
38
|
-
| \`SECURITY WARNING: Unencrypted .env files\` | Plaintext
|
|
36
|
+
| \`SECURITY WARNING: Unencrypted .env files\` | Plaintext .env files found (legacy or output) | Run \`npx hush migrate\` or delete them |
|
|
39
37
|
| \`No hush.yaml found\` | Hush not initialized | Run \`npx hush init\` |
|
|
40
38
|
| \`SOPS not installed\` | Missing prerequisite | \`brew install sops\` |
|
|
41
39
|
| \`age key not found\` | Missing encryption key | \`npx hush keys setup\` |
|
|
42
40
|
| \`Project: not set\` | Key management limited | Add \`project:\` to hush.yaml |
|
|
43
41
|
|
|
44
|
-
**Note:**
|
|
42
|
+
**Note:** Any \`.env\` file is suspect - Hush uses \`.hush\` files everywhere. Subdirectory \`.hush\` files are templates (safe to commit).
|
|
45
43
|
|
|
46
44
|
## Decision Tree: What Do I Do?
|
|
47
45
|
|
|
@@ -53,12 +51,12 @@ npx hush encrypt # Encrypts any existing .env files, deletes plaintext
|
|
|
53
51
|
npx hush inspect # Verify setup
|
|
54
52
|
\`\`\`
|
|
55
53
|
|
|
56
|
-
### Scenario 2: Existing .env Files Found
|
|
54
|
+
### Scenario 2: Existing .env Files Found (Migration from v4)
|
|
57
55
|
|
|
58
56
|
\`\`\`bash
|
|
59
57
|
npx hush status # Check what's there
|
|
60
|
-
npx hush
|
|
61
|
-
npx hush inspect # Confirm everything is
|
|
58
|
+
npx hush migrate # Migrate .env.encrypted to .hush.encrypted
|
|
59
|
+
npx hush inspect # Confirm everything is migrated
|
|
62
60
|
\`\`\`
|
|
63
61
|
|
|
64
62
|
### Scenario 3: Hush Already Set Up (Team Member Joining)
|
|
@@ -72,7 +70,8 @@ npx hush inspect # See what secrets exist
|
|
|
72
70
|
### Scenario 4: Need to Add/Modify Secrets
|
|
73
71
|
|
|
74
72
|
\`\`\`bash
|
|
75
|
-
npx hush set <KEY>
|
|
73
|
+
npx hush set <VALUE> <KEY> # Add inline (value and key provided)
|
|
74
|
+
npx hush set <KEY> # Add interactively (prompts for value)
|
|
76
75
|
npx hush edit # Edit all secrets in $EDITOR
|
|
77
76
|
npx hush inspect # Verify changes
|
|
78
77
|
\`\`\`
|
|
@@ -110,12 +109,12 @@ targets:
|
|
|
110
109
|
include: [NEXT_PUBLIC_*] # All matching vars auto-flow
|
|
111
110
|
\`\`\`
|
|
112
111
|
|
|
113
|
-
### Pull (subdirectory .
|
|
112
|
+
### Pull (subdirectory .hush templates)
|
|
114
113
|
|
|
115
114
|
Best for transformation, renaming, or explicit dependencies:
|
|
116
115
|
|
|
117
116
|
\`\`\`bash
|
|
118
|
-
# apps/mobile/.
|
|
117
|
+
# apps/mobile/.hush (committed - it's just a template)
|
|
119
118
|
EXPO_PUBLIC_API_URL=\${API_URL} # Rename from root
|
|
120
119
|
PORT=\${PORT:-8081} # Default value
|
|
121
120
|
\`\`\`
|
|
@@ -126,7 +125,7 @@ PORT=\${PORT:-8081} # Default value
|
|
|
126
125
|
|
|
127
126
|
## Subdirectory Templates (Pull-Based)
|
|
128
127
|
|
|
129
|
-
When a subdirectory needs to rename, transform, or add defaults to root secrets, create a \`.
|
|
128
|
+
When a subdirectory needs to rename, transform, or add defaults to root secrets, create a \`.hush\` template file in that subdirectory.
|
|
130
129
|
|
|
131
130
|
### Step-by-Step Setup
|
|
132
131
|
|
|
@@ -137,7 +136,7 @@ npx hush inspect # From repo root - verify secrets are configured
|
|
|
137
136
|
|
|
138
137
|
**2. Create subdirectory template (this file is committed to git):**
|
|
139
138
|
\`\`\`bash
|
|
140
|
-
# apps/mobile/.
|
|
139
|
+
# apps/mobile/.hush
|
|
141
140
|
EXPO_PUBLIC_API_URL=\${API_URL} # Pull API_URL from root, rename it
|
|
142
141
|
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_KEY} # Pull and rename
|
|
143
142
|
PORT=\${PORT:-8081} # Use root PORT, or default to 8081
|
|
@@ -153,7 +152,7 @@ npx hush run -- npm start
|
|
|
153
152
|
Hush automatically:
|
|
154
153
|
1. Finds the project root (where \`hush.yaml\` is)
|
|
155
154
|
2. Decrypts root secrets
|
|
156
|
-
3. Loads the local \`.
|
|
155
|
+
3. Loads the local \`.hush\` template
|
|
157
156
|
4. Resolves \`\${VAR}\` references against root secrets
|
|
158
157
|
5. **Filters root secrets based on target config (include/exclude)**
|
|
159
158
|
6. **Merges them (Template overrides Target)**
|
|
@@ -171,7 +170,7 @@ Hush automatically:
|
|
|
171
170
|
|
|
172
171
|
**Expo/React Native app:**
|
|
173
172
|
\`\`\`bash
|
|
174
|
-
# apps/mobile/.
|
|
173
|
+
# apps/mobile/.hush
|
|
175
174
|
EXPO_PUBLIC_API_URL=\${API_URL}
|
|
176
175
|
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
177
176
|
EXPO_PUBLIC_ENV=\${ENV:-development}
|
|
@@ -179,7 +178,7 @@ EXPO_PUBLIC_ENV=\${ENV:-development}
|
|
|
179
178
|
|
|
180
179
|
**Next.js app:**
|
|
181
180
|
\`\`\`bash
|
|
182
|
-
# apps/web/.
|
|
181
|
+
# apps/web/.hush
|
|
183
182
|
NEXT_PUBLIC_API_URL=\${API_URL}
|
|
184
183
|
NEXT_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
185
184
|
DATABASE_URL=\${DATABASE_URL}
|
|
@@ -187,20 +186,30 @@ DATABASE_URL=\${DATABASE_URL}
|
|
|
187
186
|
|
|
188
187
|
**API server with defaults:**
|
|
189
188
|
\`\`\`bash
|
|
190
|
-
# apps/api/.
|
|
189
|
+
# apps/api/.hush
|
|
191
190
|
DATABASE_URL=\${DATABASE_URL}
|
|
192
191
|
PORT=\${PORT:-8787}
|
|
193
192
|
LOG_LEVEL=\${LOG_LEVEL:-info}
|
|
194
193
|
\`\`\`
|
|
195
194
|
|
|
196
|
-
### Important Notes
|
|
195
|
+
### Important Notes: File Conventions
|
|
197
196
|
|
|
198
|
-
|
|
199
|
-
|
|
197
|
+
**All Hush files use \`.hush\` extension - never \`.env\`:**
|
|
198
|
+
|
|
199
|
+
| Location | File Type | Contains | Committed? |
|
|
200
|
+
|----------|-----------|----------|------------|
|
|
201
|
+
| **Root** | \`.hush\` | Actual secrets | NO (gitignored) |
|
|
202
|
+
| **Root** | \`.hush.encrypted\` | Encrypted secrets | YES |
|
|
203
|
+
| **Subdirectory** | \`.hush\` | Templates with \`\${VAR}\` | YES (safe) |
|
|
204
|
+
| **Subdirectory** | \`.hush.development\` | Dev-specific templates | YES (safe) |
|
|
205
|
+
| **Anywhere** | \`.env\` | ⚠️ LEGACY - delete! | NO |
|
|
206
|
+
|
|
207
|
+
**Key rules:**
|
|
208
|
+
- **Everything is \`.hush\`** - consistent naming throughout
|
|
209
|
+
- **Any \`.env\` file is wrong** - legacy file that should be deleted
|
|
210
|
+
- Subdirectory \`.hush\` templates are safe to commit (no actual secrets)
|
|
200
211
|
- **Run from the subdirectory** - \`hush run\` auto-detects the project root
|
|
201
|
-
- **Root secrets stay encrypted** - subdirectory templates just reference them
|
|
202
212
|
- **Self-reference works** - \`PORT=\${PORT:-3000}\` uses root PORT if set, else 3000
|
|
203
|
-
- **Security warnings only apply to root** - subdirectory .env files are always safe
|
|
204
213
|
|
|
205
214
|
---
|
|
206
215
|
|
|
@@ -211,7 +220,7 @@ LOG_LEVEL=\${LOG_LEVEL:-info}
|
|
|
211
220
|
| \`npx hush status\` | **Full diagnostic** | First step, always |
|
|
212
221
|
| \`npx hush inspect\` | See variables (masked) | Check what's configured |
|
|
213
222
|
| \`npx hush has <KEY>\` | Check specific variable | Verify a secret exists |
|
|
214
|
-
| \`npx hush set <KEY>\` | Add secret
|
|
223
|
+
| \`npx hush set [VALUE] <KEY>\` | Add secret (prompts if no value) | User needs to set a value |
|
|
215
224
|
| \`npx hush edit\` | Edit all secrets | Bulk editing |
|
|
216
225
|
| \`npx hush run -- <cmd>\` | Run with secrets in memory | Actually use the secrets |
|
|
217
226
|
| \`npx hush init\` | Initialize Hush | First-time setup |
|
|
@@ -304,25 +313,34 @@ npx hush has API_KEY -q # Quiet: exit code only (0=set, 1=missing)
|
|
|
304
313
|
|
|
305
314
|
## Adding/Modifying Secrets
|
|
306
315
|
|
|
307
|
-
### Add a single secret
|
|
316
|
+
### Add a single secret (three methods)
|
|
317
|
+
|
|
318
|
+
**Method 1: Inline value (recommended for AI agents)**
|
|
319
|
+
\`\`\`bash
|
|
320
|
+
npx hush set "postgres://user:pass@host/db" DATABASE_URL
|
|
321
|
+
npx hush set "sk_live_xxx" STRIPE_KEY -e production
|
|
322
|
+
\`\`\`
|
|
308
323
|
|
|
324
|
+
**Method 2: Interactive prompt (for users)**
|
|
309
325
|
\`\`\`bash
|
|
310
|
-
npx hush set DATABASE_URL #
|
|
326
|
+
npx hush set DATABASE_URL # Prompts user for value
|
|
311
327
|
npx hush set API_KEY -e production # Set in production secrets
|
|
312
328
|
npx hush set DEBUG --local # Set personal local override
|
|
313
329
|
\`\`\`
|
|
314
330
|
|
|
315
|
-
|
|
316
|
-
**You never see the actual secret - just invoke the command!**
|
|
317
|
-
|
|
318
|
-
### Add a secret via pipe (for scripts/automation)
|
|
319
|
-
|
|
331
|
+
**Method 3: Pipe (for scripts/automation)**
|
|
320
332
|
\`\`\`bash
|
|
321
333
|
echo "my-secret-value" | npx hush set MY_KEY
|
|
322
334
|
cat secret.txt | npx hush set CERT_CONTENT
|
|
323
335
|
\`\`\`
|
|
324
336
|
|
|
325
|
-
|
|
337
|
+
### GUI dialog for AI agents
|
|
338
|
+
|
|
339
|
+
When running in a non-TTY environment (like AI agents), use \`--gui\`:
|
|
340
|
+
\`\`\`bash
|
|
341
|
+
npx hush set API_KEY --gui # Opens visible dialog
|
|
342
|
+
\`\`\`
|
|
343
|
+
The dialog shows the pasted value for easy verification.
|
|
326
344
|
|
|
327
345
|
---
|
|
328
346
|
|
|
@@ -423,9 +441,9 @@ The generated config looks like:
|
|
|
423
441
|
|
|
424
442
|
\`\`\`yaml
|
|
425
443
|
sources:
|
|
426
|
-
shared: .
|
|
427
|
-
development: .
|
|
428
|
-
production: .
|
|
444
|
+
shared: .hush
|
|
445
|
+
development: .hush.development
|
|
446
|
+
production: .hush.production
|
|
429
447
|
|
|
430
448
|
targets:
|
|
431
449
|
- name: root
|
|
@@ -461,33 +479,79 @@ Customize targets for your monorepo. Common patterns:
|
|
|
461
479
|
format: yaml
|
|
462
480
|
\`\`\`
|
|
463
481
|
|
|
464
|
-
### Step 5: Create initial \`.
|
|
482
|
+
### Step 5: Create initial \`.hush\` files
|
|
465
483
|
|
|
466
|
-
Create \`.
|
|
484
|
+
Create \`.hush\` with shared secrets at the **repository root**:
|
|
467
485
|
|
|
468
486
|
\`\`\`bash
|
|
469
|
-
# .
|
|
487
|
+
# .hush (root level - contains actual secrets)
|
|
470
488
|
DATABASE_URL=postgres://localhost/mydb
|
|
489
|
+
SUPABASE_URL=https://xxx.supabase.co
|
|
490
|
+
SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
491
|
+
STRIPE_SECRET_KEY=sk_test_xxx
|
|
471
492
|
API_KEY=your_api_key_here
|
|
472
|
-
NEXT_PUBLIC_API_URL=http://localhost:3000
|
|
473
493
|
\`\`\`
|
|
474
494
|
|
|
475
|
-
Create \`.
|
|
495
|
+
Create \`.hush.development\` for dev-specific values:
|
|
476
496
|
|
|
477
497
|
\`\`\`bash
|
|
478
|
-
# .
|
|
498
|
+
# .hush.development
|
|
479
499
|
DEBUG=true
|
|
480
500
|
LOG_LEVEL=debug
|
|
481
501
|
\`\`\`
|
|
482
502
|
|
|
483
|
-
Create \`.
|
|
503
|
+
Create \`.hush.production\` for production values:
|
|
484
504
|
|
|
485
505
|
\`\`\`bash
|
|
486
|
-
# .
|
|
506
|
+
# .hush.production
|
|
487
507
|
DEBUG=false
|
|
488
508
|
LOG_LEVEL=error
|
|
489
509
|
\`\`\`
|
|
490
510
|
|
|
511
|
+
### Step 5b: Set up subdirectory templates (for monorepos)
|
|
512
|
+
|
|
513
|
+
For packages that need secrets with different prefixes, create a **template** \`.hush\` file in the subdirectory.
|
|
514
|
+
|
|
515
|
+
**Example: Expo app needs Supabase with EXPO_PUBLIC_ prefix**
|
|
516
|
+
|
|
517
|
+
\`\`\`bash
|
|
518
|
+
# apps/mobile/.hush (committed to git - template with variable references)
|
|
519
|
+
EXPO_PUBLIC_SUPABASE_URL=\${SUPABASE_URL}
|
|
520
|
+
EXPO_PUBLIC_SUPABASE_ANON_KEY=\${SUPABASE_ANON_KEY}
|
|
521
|
+
EXPO_PUBLIC_API_URL=\${API_URL:-http://localhost:3000}
|
|
522
|
+
\`\`\`
|
|
523
|
+
|
|
524
|
+
**Example: Next.js app needs different prefixes**
|
|
525
|
+
|
|
526
|
+
\`\`\`bash
|
|
527
|
+
# apps/web/.hush (committed to git - template)
|
|
528
|
+
NEXT_PUBLIC_SUPABASE_URL=\${SUPABASE_URL}
|
|
529
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=\${SUPABASE_ANON_KEY}
|
|
530
|
+
DATABASE_URL=\${DATABASE_URL}
|
|
531
|
+
\`\`\`
|
|
532
|
+
|
|
533
|
+
**File structure:**
|
|
534
|
+
\`\`\`
|
|
535
|
+
repo-root/
|
|
536
|
+
├── .hush # Actual secrets (gitignored)
|
|
537
|
+
├── .hush.encrypted # Encrypted secrets (committed)
|
|
538
|
+
├── apps/
|
|
539
|
+
│ ├── mobile/
|
|
540
|
+
│ │ └── .hush # Template with \${VAR} refs (committed)
|
|
541
|
+
│ └── web/
|
|
542
|
+
│ └── .hush # Template with \${VAR} refs (committed)
|
|
543
|
+
\`\`\`
|
|
544
|
+
|
|
545
|
+
**How it works:**
|
|
546
|
+
1. Root \`.hush\` contains actual secrets → encrypted to \`.hush.encrypted\`
|
|
547
|
+
2. Subdirectory \`.hush\` templates reference root secrets via \`\${VAR}\`
|
|
548
|
+
3. Run \`hush run\` from subdirectory - it resolves templates automatically
|
|
549
|
+
|
|
550
|
+
\`\`\`bash
|
|
551
|
+
cd apps/mobile
|
|
552
|
+
npx hush run -- expo start # Template vars resolved from root secrets
|
|
553
|
+
\`\`\`
|
|
554
|
+
|
|
491
555
|
### Step 6: Encrypt secrets
|
|
492
556
|
|
|
493
557
|
\`\`\`bash
|
|
@@ -495,9 +559,9 @@ npx hush encrypt
|
|
|
495
559
|
\`\`\`
|
|
496
560
|
|
|
497
561
|
This creates:
|
|
498
|
-
- \`.
|
|
499
|
-
- \`.
|
|
500
|
-
- \`.
|
|
562
|
+
- \`.hush.encrypted\`
|
|
563
|
+
- \`.hush.development.encrypted\`
|
|
564
|
+
- \`.hush.production.encrypted\`
|
|
501
565
|
|
|
502
566
|
### Step 7: Verify setup
|
|
503
567
|
|
|
@@ -511,22 +575,26 @@ npx hush inspect
|
|
|
511
575
|
Add these lines to \`.gitignore\`:
|
|
512
576
|
|
|
513
577
|
\`\`\`gitignore
|
|
514
|
-
# Hush - plaintext
|
|
578
|
+
# Hush - plaintext source files (encrypted versions are committed)
|
|
579
|
+
.hush
|
|
580
|
+
.hush.local
|
|
581
|
+
.hush.development
|
|
582
|
+
.hush.production
|
|
583
|
+
|
|
584
|
+
# Output files (generated by hush decrypt, not committed)
|
|
515
585
|
.env
|
|
516
|
-
.env
|
|
517
|
-
.env.development
|
|
518
|
-
.env.production
|
|
586
|
+
.env.*
|
|
519
587
|
.dev.vars
|
|
520
588
|
|
|
521
589
|
# Keep encrypted files (these ARE committed)
|
|
522
|
-
!.
|
|
523
|
-
!.
|
|
590
|
+
!.hush.encrypted
|
|
591
|
+
!.hush.*.encrypted
|
|
524
592
|
\`\`\`
|
|
525
593
|
|
|
526
594
|
### Step 9: Commit encrypted files
|
|
527
595
|
|
|
528
596
|
\`\`\`bash
|
|
529
|
-
git add .sops.yaml hush.yaml .
|
|
597
|
+
git add .sops.yaml hush.yaml .hush*.encrypted .gitignore
|
|
530
598
|
git commit -m "chore: add Hush secrets management"
|
|
531
599
|
\`\`\`
|
|
532
600
|
|
|
@@ -552,8 +620,8 @@ After setup, verify everything works:
|
|
|
552
620
|
- [ ] \`npx hush status\` shows configuration
|
|
553
621
|
- [ ] \`npx hush inspect\` shows masked variables
|
|
554
622
|
- [ ] \`npx hush run -- env\` can decrypt and run (secrets stay in memory!)
|
|
555
|
-
- [ ] \`.
|
|
556
|
-
- [ ] Plaintext \`.env\` files are in \`.gitignore\`
|
|
623
|
+
- [ ] \`.hush.encrypted\` files are committed to git
|
|
624
|
+
- [ ] Plaintext \`.hush\` and \`.env\` files are in \`.gitignore\`
|
|
557
625
|
|
|
558
626
|
---
|
|
559
627
|
|
|
@@ -594,7 +662,7 @@ Complete reference for all Hush CLI commands with flags, options, and examples.
|
|
|
594
662
|
|
|
595
663
|
## Security Model: Encrypted at Rest
|
|
596
664
|
|
|
597
|
-
All secrets are stored encrypted
|
|
665
|
+
All secrets are stored encrypted in \`.hush.encrypted\` files. Never read \`.env\` files directly - they indicate legacy/misconfigured setup. Use \`hush inspect\` or \`hush has\` to check secrets safely.
|
|
598
666
|
|
|
599
667
|
## Global Options
|
|
600
668
|
|
|
@@ -632,23 +700,27 @@ hush run -t api -- wrangler dev # Run filtered for 'api' target
|
|
|
632
700
|
|
|
633
701
|
---
|
|
634
702
|
|
|
635
|
-
### hush set <KEY> ⭐
|
|
703
|
+
### hush set [VALUE] <KEY> ⭐
|
|
636
704
|
|
|
637
|
-
Add or update a single secret
|
|
705
|
+
Add or update a single secret. Prompts for value if not provided inline.
|
|
638
706
|
|
|
639
707
|
\`\`\`bash
|
|
640
|
-
hush set DATABASE_URL #
|
|
708
|
+
hush set DATABASE_URL # Prompts for value interactively
|
|
709
|
+
hush set "postgres://..." DATABASE_URL # Inline value (no prompt)
|
|
641
710
|
hush set API_KEY -e production # Set in production secrets
|
|
642
711
|
hush set DEBUG --local # Set personal local override
|
|
643
712
|
\`\`\`
|
|
644
713
|
|
|
645
|
-
|
|
714
|
+
**Input methods (in priority order):**
|
|
715
|
+
1. **Inline value**: \`hush set "myvalue" KEY\` - value provided directly
|
|
716
|
+
2. **Piped input**: \`echo "myvalue" | hush set KEY\` - reads from stdin
|
|
717
|
+
3. **Interactive prompt**: Opens dialog/prompt for user input
|
|
646
718
|
|
|
647
|
-
**
|
|
719
|
+
**GUI dialog (--gui flag):**
|
|
648
720
|
\`\`\`bash
|
|
649
|
-
|
|
650
|
-
cat cert.pem | hush set CERTIFICATE
|
|
721
|
+
hush set API_KEY --gui # Opens visible dialog (for AI agents)
|
|
651
722
|
\`\`\`
|
|
723
|
+
The GUI dialog shows the value as you type/paste for easier verification.
|
|
652
724
|
|
|
653
725
|
---
|
|
654
726
|
|
|
@@ -701,7 +773,7 @@ hush init
|
|
|
701
773
|
|
|
702
774
|
### hush encrypt
|
|
703
775
|
|
|
704
|
-
Encrypt source \`.
|
|
776
|
+
Encrypt source \`.hush\` files to \`.hush.encrypted\` files.
|
|
705
777
|
|
|
706
778
|
\`\`\`bash
|
|
707
779
|
hush encrypt
|
|
@@ -782,7 +854,7 @@ hush trace STRIPE_SECRET_KEY # Trace another variable
|
|
|
782
854
|
|
|
783
855
|
### hush template
|
|
784
856
|
|
|
785
|
-
Show the resolved template for the current directory's \`.
|
|
857
|
+
Show the resolved template for the current directory's \`.hush\` file.
|
|
786
858
|
|
|
787
859
|
\`\`\`bash
|
|
788
860
|
cd apps/mobile
|
|
@@ -801,7 +873,7 @@ hush template -e production # Show for production
|
|
|
801
873
|
|
|
802
874
|
### hush expansions
|
|
803
875
|
|
|
804
|
-
Show the expansion graph across all subdirectories that have \`.
|
|
876
|
+
Show the expansion graph across all subdirectories that have \`.hush\` templates.
|
|
805
877
|
|
|
806
878
|
\`\`\`bash
|
|
807
879
|
hush expansions # Scan all subdirectories
|
|
@@ -809,7 +881,7 @@ hush expansions -e production # Show for production
|
|
|
809
881
|
\`\`\`
|
|
810
882
|
|
|
811
883
|
**Output shows:**
|
|
812
|
-
- Which subdirectories have \`.
|
|
884
|
+
- Which subdirectories have \`.hush\` templates
|
|
813
885
|
- What variables each template references from root
|
|
814
886
|
- Resolution status for each reference
|
|
815
887
|
|
|
@@ -989,9 +1061,9 @@ hush skill --local # Install to ./.claude/skills/
|
|
|
989
1061
|
|
|
990
1062
|
\`\`\`yaml
|
|
991
1063
|
sources:
|
|
992
|
-
shared: .
|
|
993
|
-
development: .
|
|
994
|
-
production: .
|
|
1064
|
+
shared: .hush
|
|
1065
|
+
development: .hush.development
|
|
1066
|
+
production: .hush.production
|
|
995
1067
|
|
|
996
1068
|
targets:
|
|
997
1069
|
- name: root
|
|
@@ -1046,7 +1118,7 @@ targets:
|
|
|
1046
1118
|
| Expo | \`EXPO_PUBLIC_*\` | \`include: [EXPO_PUBLIC_*]\` |
|
|
1047
1119
|
| Gatsby | \`GATSBY_*\` | \`include: [GATSBY_*]\` |
|
|
1048
1120
|
|
|
1049
|
-
### Variable Interpolation (
|
|
1121
|
+
### Variable Interpolation (v5+)
|
|
1050
1122
|
|
|
1051
1123
|
Reference other variables using \`\${VAR}\` syntax:
|
|
1052
1124
|
|
|
@@ -1061,8 +1133,8 @@ PORT=\${PORT:-3000}
|
|
|
1061
1133
|
# System environment (explicit opt-in)
|
|
1062
1134
|
CI=\${env:CI}
|
|
1063
1135
|
|
|
1064
|
-
# Pull from root (subdirectory .
|
|
1065
|
-
# apps/mobile/.
|
|
1136
|
+
# Pull from root (subdirectory .hush can reference root secrets)
|
|
1137
|
+
# apps/mobile/.hush:
|
|
1066
1138
|
EXPO_PUBLIC_API_URL=\${API_URL} # Renamed from root
|
|
1067
1139
|
\`\`\`
|
|
1068
1140
|
|
|
@@ -1079,380 +1151,7 @@ targets:
|
|
|
1079
1151
|
|
|
1080
1152
|
**Pull (subdirectory templates):** Transformation, renaming, defaults
|
|
1081
1153
|
\`\`\`bash
|
|
1082
|
-
#
|
|
1083
|
-
EXPO_PUBLIC_API_URL=\${API_URL} # Rename required
|
|
1084
|
-
PORT=\${PORT:-3000} # Default value
|
|
1085
|
-
\`\`\`
|
|
1086
|
-
|
|
1087
|
-
**Decision:** Use push for "all X → Y". Use pull for rename/transform/defaults.
|
|
1088
|
-
`,
|
|
1089
|
-
'examples/workflows.md': `# Hush Workflow Examples
|
|
1090
|
-
|
|
1091
|
-
Step-by-step examples for common workflows when working with secrets.
|
|
1092
|
-
|
|
1093
|
-
**CRITICAL: NEVER read .env files directly. Use hush commands instead.**
|
|
1094
|
-
|
|
1095
|
-
---
|
|
1096
|
-
|
|
1097
|
-
## First-Time Setup (Most Important!)
|
|
1098
|
-
|
|
1099
|
-
### "Help me set up Hush for this project"
|
|
1100
|
-
|
|
1101
|
-
**Step 1: Check current state**
|
|
1102
|
-
\`\`\`bash
|
|
1103
|
-
npx hush status
|
|
1104
|
-
\`\`\`
|
|
1105
|
-
|
|
1106
|
-
This will show:
|
|
1107
|
-
- If Hush is already configured
|
|
1108
|
-
- If there are unencrypted .env files (security risk!)
|
|
1109
|
-
- What prerequisites are missing
|
|
1110
|
-
|
|
1111
|
-
**Step 2: Based on the output, follow the appropriate path:**
|
|
1112
|
-
|
|
1113
|
-
#### Path A: "SECURITY WARNING: Unencrypted .env files detected"
|
|
1114
|
-
\`\`\`bash
|
|
1115
|
-
npx hush init # If no hush.yaml exists
|
|
1116
|
-
npx hush encrypt # Encrypts files and DELETES plaintext automatically
|
|
1117
|
-
npx hush status # Verify the warning is gone
|
|
1118
|
-
\`\`\`
|
|
1119
|
-
|
|
1120
|
-
#### Path B: "No hush.yaml found"
|
|
1121
|
-
\`\`\`bash
|
|
1122
|
-
npx hush init # Creates config and sets up keys
|
|
1123
|
-
npx hush set <KEY> # Add secrets (if none exist yet)
|
|
1124
|
-
\`\`\`
|
|
1125
|
-
|
|
1126
|
-
#### Path C: "age key not found"
|
|
1127
|
-
\`\`\`bash
|
|
1128
|
-
npx hush keys setup # Pull from 1Password or generate new key
|
|
1129
|
-
\`\`\`
|
|
1130
|
-
|
|
1131
|
-
#### Path D: Everything looks good
|
|
1132
|
-
\`\`\`bash
|
|
1133
|
-
npx hush inspect # See what secrets are configured
|
|
1134
|
-
\`\`\`
|
|
1135
|
-
|
|
1136
|
-
---
|
|
1137
|
-
|
|
1138
|
-
## Running Programs (Most Common)
|
|
1139
|
-
|
|
1140
|
-
### "Start the development server"
|
|
1141
|
-
\`\`\`bash
|
|
1142
|
-
npx hush run -- npm run dev
|
|
1143
|
-
\`\`\`
|
|
1144
|
-
|
|
1145
|
-
### "Build for production"
|
|
1146
|
-
\`\`\`bash
|
|
1147
|
-
npx hush run -e production -- npm run build
|
|
1148
|
-
\`\`\`
|
|
1149
|
-
|
|
1150
|
-
### "Run tests with secrets"
|
|
1151
|
-
\`\`\`bash
|
|
1152
|
-
npx hush run -- npm test
|
|
1153
|
-
\`\`\`
|
|
1154
|
-
|
|
1155
|
-
### "Run Wrangler for Cloudflare Worker"
|
|
1156
|
-
\`\`\`bash
|
|
1157
|
-
npx hush run -t api -- wrangler dev
|
|
1158
|
-
\`\`\`
|
|
1159
|
-
|
|
1160
|
-
---
|
|
1161
|
-
|
|
1162
|
-
## Checking Secrets
|
|
1163
|
-
|
|
1164
|
-
### "What environment variables does this project use?"
|
|
1165
|
-
\`\`\`bash
|
|
1166
|
-
npx hush inspect # Shows all variables with masked values
|
|
1167
|
-
\`\`\`
|
|
1168
|
-
|
|
1169
|
-
### "Is the database configured?"
|
|
1170
|
-
\`\`\`bash
|
|
1171
|
-
npx hush has DATABASE_URL
|
|
1172
|
-
\`\`\`
|
|
1173
|
-
|
|
1174
|
-
If "not found", help user add it:
|
|
1175
|
-
\`\`\`bash
|
|
1176
|
-
npx hush set DATABASE_URL
|
|
1177
|
-
\`\`\`
|
|
1178
|
-
Tell user: "Enter your database URL when prompted"
|
|
1179
|
-
|
|
1180
|
-
### "Check all required secrets"
|
|
1181
|
-
\`\`\`bash
|
|
1182
|
-
npx hush has DATABASE_URL -q && \\
|
|
1183
|
-
npx hush has API_KEY -q && \\
|
|
1184
|
-
echo "All configured" || \\
|
|
1185
|
-
echo "Some missing"
|
|
1186
|
-
\`\`\`
|
|
1187
|
-
|
|
1188
|
-
---
|
|
1189
|
-
|
|
1190
|
-
## Adding Secrets
|
|
1191
|
-
|
|
1192
|
-
### "Help me add DATABASE_URL"
|
|
1193
|
-
\`\`\`bash
|
|
1194
|
-
npx hush set DATABASE_URL
|
|
1195
|
-
\`\`\`
|
|
1196
|
-
Tell user: "Enter your database URL when prompted (input will be hidden)"
|
|
1197
|
-
|
|
1198
|
-
### "Add a production-only secret"
|
|
1199
|
-
\`\`\`bash
|
|
1200
|
-
npx hush set STRIPE_SECRET_KEY -e production
|
|
1201
|
-
\`\`\`
|
|
1202
|
-
|
|
1203
|
-
### "Add a personal local override"
|
|
1204
|
-
\`\`\`bash
|
|
1205
|
-
npx hush set DEBUG --local
|
|
1206
|
-
\`\`\`
|
|
1207
|
-
|
|
1208
|
-
### "Edit multiple secrets at once"
|
|
1209
|
-
\`\`\`bash
|
|
1210
|
-
npx hush edit
|
|
1211
|
-
\`\`\`
|
|
1212
|
-
Tell user: "Your editor will open. Add or modify secrets, then save and close."
|
|
1213
|
-
|
|
1214
|
-
---
|
|
1215
|
-
|
|
1216
|
-
## Debugging
|
|
1217
|
-
|
|
1218
|
-
### "My app can't find DATABASE_URL"
|
|
1219
|
-
|
|
1220
|
-
1. **Trace the variable** to see where it exists and where it goes:
|
|
1221
|
-
\`\`\`bash
|
|
1222
|
-
npx hush trace DATABASE_URL
|
|
1223
|
-
\`\`\`
|
|
1224
|
-
This shows which source files have it and which targets include/exclude it.
|
|
1225
|
-
|
|
1226
|
-
2. **Check if it exists** in your current environment:
|
|
1227
|
-
\`\`\`bash
|
|
1228
|
-
npx hush has DATABASE_URL
|
|
1229
|
-
\`\`\`
|
|
1230
|
-
|
|
1231
|
-
3. **Resolve the target** to see what variables it receives:
|
|
1232
|
-
\`\`\`bash
|
|
1233
|
-
npx hush resolve api-workers
|
|
1234
|
-
\`\`\`
|
|
1235
|
-
|
|
1236
|
-
### "Target is missing expected variables"
|
|
1237
|
-
|
|
1238
|
-
\`\`\`bash
|
|
1239
|
-
npx hush resolve <target-name> # See included/excluded variables
|
|
1240
|
-
npx hush resolve <target-name> -e prod # Check production
|
|
1241
|
-
\`\`\`
|
|
1242
|
-
|
|
1243
|
-
Look at the 🚫 EXCLUDED section to see which pattern is filtering out your variable.
|
|
1244
|
-
|
|
1245
|
-
### "Wrangler dev not seeing secrets"
|
|
1246
|
-
|
|
1247
|
-
If you are using \`hush run -- wrangler dev\` and secrets are missing:
|
|
1248
|
-
|
|
1249
|
-
**Step 1: Check for blocking files**
|
|
1250
|
-
\`\`\`bash
|
|
1251
|
-
ls -la .dev.vars # If this exists, it blocks Hush secrets
|
|
1252
|
-
\`\`\`
|
|
1253
|
-
|
|
1254
|
-
**Step 2: Delete the blocking file**
|
|
1255
|
-
\`\`\`bash
|
|
1256
|
-
rm .dev.vars
|
|
1257
|
-
\`\`\`
|
|
1258
|
-
|
|
1259
|
-
**Step 3: Run normally**
|
|
1260
|
-
\`\`\`bash
|
|
1261
|
-
npx hush run -t api -- wrangler dev
|
|
1262
|
-
\`\`\`
|
|
1263
|
-
|
|
1264
|
-
**Step 4: If still not working, update Wrangler**
|
|
1265
|
-
\`\`\`bash
|
|
1266
|
-
npm update wrangler
|
|
1267
|
-
\`\`\`
|
|
1268
|
-
|
|
1269
|
-
**Why this happens:**
|
|
1270
|
-
- Wrangler has a strict rule: if \`.dev.vars\` exists (even empty!), it ignores ALL environment variables
|
|
1271
|
-
- Hush automatically sets \`CLOUDFLARE_INCLUDE_PROCESS_ENV=true\` for you
|
|
1272
|
-
- But Wrangler only respects this when no \`.dev.vars\` file exists
|
|
1273
|
-
- Older Wrangler versions may not support \`CLOUDFLARE_INCLUDE_PROCESS_ENV\` at all
|
|
1274
|
-
|
|
1275
|
-
**Prevention tip:** Never use \`hush decrypt\` for Wrangler targets—always use \`hush run\`.
|
|
1276
|
-
|
|
1277
|
-
### "Variable appears in wrong places"
|
|
1278
|
-
|
|
1279
|
-
\`\`\`bash
|
|
1280
|
-
npx hush trace <VARIABLE_NAME>
|
|
1281
|
-
\`\`\`
|
|
1282
|
-
|
|
1283
|
-
This shows the full disposition across all targets - which include it and which exclude it.
|
|
1284
|
-
|
|
1285
|
-
### "Push is missing some secrets"
|
|
1286
|
-
|
|
1287
|
-
\`\`\`bash
|
|
1288
|
-
npx hush push --dry-run --verbose
|
|
1289
|
-
\`\`\`
|
|
1290
|
-
|
|
1291
|
-
This shows exactly what would be pushed to each target.
|
|
1292
|
-
|
|
1293
|
-
---
|
|
1294
|
-
|
|
1295
|
-
## Team Workflows
|
|
1296
|
-
|
|
1297
|
-
### "New team member setup"
|
|
1298
|
-
|
|
1299
|
-
Guide them through these steps:
|
|
1300
|
-
\`\`\`bash
|
|
1301
|
-
# 1. Pull key from 1Password (or get from team member)
|
|
1302
|
-
npx hush keys setup
|
|
1303
|
-
|
|
1304
|
-
# 2. Verify setup
|
|
1305
|
-
npx hush status
|
|
1306
|
-
|
|
1307
|
-
# 3. Check secrets are accessible
|
|
1308
|
-
npx hush inspect
|
|
1309
|
-
|
|
1310
|
-
# 4. Start developing
|
|
1311
|
-
npx hush run -- npm run dev
|
|
1312
|
-
\`\`\`
|
|
1313
|
-
|
|
1314
|
-
### "Someone added new secrets"
|
|
1315
|
-
\`\`\`bash
|
|
1316
|
-
git pull
|
|
1317
|
-
npx hush inspect # See what's new
|
|
1318
|
-
\`\`\`
|
|
1319
|
-
|
|
1320
|
-
---
|
|
1321
|
-
|
|
1322
|
-
## Deployment
|
|
1323
|
-
|
|
1324
|
-
### "Push to Cloudflare Workers"
|
|
1325
|
-
\`\`\`bash
|
|
1326
|
-
npx hush push --dry-run # Preview first
|
|
1327
|
-
npx hush push # Actually push
|
|
1328
|
-
npx hush push -t api # Push specific target
|
|
1329
|
-
\`\`\`
|
|
1330
|
-
|
|
1331
|
-
### "Push to Cloudflare Pages"
|
|
1332
|
-
|
|
1333
|
-
First, add \`push_to\` to your target in \`hush.yaml\`:
|
|
1334
|
-
\`\`\`yaml
|
|
1335
|
-
targets:
|
|
1336
|
-
- name: app
|
|
1337
|
-
path: ./app
|
|
1338
|
-
format: dotenv
|
|
1339
|
-
push_to:
|
|
1340
|
-
type: cloudflare-pages
|
|
1341
|
-
project: my-pages-project
|
|
1342
|
-
\`\`\`
|
|
1343
|
-
|
|
1344
|
-
Then push:
|
|
1345
|
-
\`\`\`bash
|
|
1346
|
-
npx hush push -t app --dry-run # Preview first
|
|
1347
|
-
npx hush push -t app # Actually push
|
|
1348
|
-
\`\`\`
|
|
1349
|
-
|
|
1350
|
-
### "Build and deploy"
|
|
1351
|
-
\`\`\`bash
|
|
1352
|
-
npx hush run -e production -- npm run build
|
|
1353
|
-
npx hush push
|
|
1354
|
-
\`\`\`
|
|
1355
|
-
|
|
1356
|
-
---
|
|
1357
|
-
|
|
1358
|
-
## Setting Up Subdirectory Templates (Pull-Based Secrets)
|
|
1359
|
-
|
|
1360
|
-
### "Set up secrets for a subdirectory app (Expo, Next.js, etc.)"
|
|
1361
|
-
|
|
1362
|
-
**Use this when:** You need to rename, transform, or add defaults to root secrets for a specific package.
|
|
1363
|
-
|
|
1364
|
-
**Step 1: Verify root secrets exist**
|
|
1365
|
-
\`\`\`bash
|
|
1366
|
-
cd /path/to/repo/root
|
|
1367
|
-
npx hush inspect
|
|
1368
|
-
\`\`\`
|
|
1369
|
-
|
|
1370
|
-
**Step 2: Create the subdirectory template file**
|
|
1371
|
-
|
|
1372
|
-
Create a \`.env\` file in the subdirectory. This file is committed to git - it's just a template, not actual secrets.
|
|
1373
|
-
|
|
1374
|
-
\`\`\`bash
|
|
1375
|
-
# Example: apps/mobile/.env
|
|
1376
|
-
EXPO_PUBLIC_API_URL=\${API_URL} # Pulls API_URL from root, renames it
|
|
1377
|
-
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_KEY} # Pulls and renames
|
|
1378
|
-
PORT=\${PORT:-8081} # Uses root PORT if set, otherwise 8081
|
|
1379
|
-
DEBUG=\${DEBUG:-false} # Uses root DEBUG if set, otherwise false
|
|
1380
|
-
\`\`\`
|
|
1381
|
-
|
|
1382
|
-
**Step 3: Run from the subdirectory**
|
|
1383
|
-
\`\`\`bash
|
|
1384
|
-
cd apps/mobile
|
|
1385
|
-
npx hush run -- npm start
|
|
1386
|
-
\`\`\`
|
|
1387
|
-
|
|
1388
|
-
### Variable Expansion Syntax Reference
|
|
1389
|
-
|
|
1390
|
-
| Syntax | What It Does | Example |
|
|
1391
|
-
|--------|--------------|---------|
|
|
1392
|
-
| \`\${VAR}\` | Pull VAR from root secrets | \`API_URL=\${API_URL}\` |
|
|
1393
|
-
| \`\${VAR:-default}\` | Pull VAR, use default if not set | \`PORT=\${PORT:-3000}\` |
|
|
1394
|
-
| \`\${env:VAR}\` | Read from system environment | \`CI=\${env:CI}\` |
|
|
1395
|
-
|
|
1396
|
-
### Framework Examples
|
|
1397
|
-
|
|
1398
|
-
**Expo/React Native:**
|
|
1399
|
-
\`\`\`bash
|
|
1400
|
-
# apps/mobile/.env
|
|
1401
|
-
EXPO_PUBLIC_API_URL=\${API_URL}
|
|
1402
|
-
EXPO_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
1403
|
-
EXPO_PUBLIC_ENV=\${ENV:-development}
|
|
1404
|
-
\`\`\`
|
|
1405
|
-
|
|
1406
|
-
**Next.js:**
|
|
1407
|
-
\`\`\`bash
|
|
1408
|
-
# apps/web/.env
|
|
1409
|
-
NEXT_PUBLIC_API_URL=\${API_URL}
|
|
1410
|
-
NEXT_PUBLIC_STRIPE_KEY=\${STRIPE_PUBLISHABLE_KEY}
|
|
1411
|
-
DATABASE_URL=\${DATABASE_URL}
|
|
1412
|
-
\`\`\`
|
|
1413
|
-
|
|
1414
|
-
**Cloudflare Worker:**
|
|
1415
|
-
\`\`\`bash
|
|
1416
|
-
# apps/api/.env
|
|
1417
|
-
DATABASE_URL=\${DATABASE_URL}
|
|
1418
|
-
STRIPE_SECRET_KEY=\${STRIPE_SECRET_KEY}
|
|
1419
|
-
PORT=\${PORT:-8787}
|
|
1420
|
-
\`\`\`
|
|
1421
|
-
|
|
1422
|
-
### Important Notes
|
|
1423
|
-
|
|
1424
|
-
- **Template files ARE committed** to git (they contain no secrets)
|
|
1425
|
-
- **Root secrets stay encrypted** - templates just reference them
|
|
1426
|
-
- **Run from subdirectory** - \`hush run\` finds the project root automatically
|
|
1427
|
-
- **Self-reference works** - \`PORT=\${PORT:-3000}\` uses root PORT if set
|
|
1428
|
-
|
|
1429
|
-
---
|
|
1430
|
-
|
|
1431
|
-
## Choosing Push vs Pull (Monorepos)
|
|
1432
|
-
|
|
1433
|
-
### "How should I set up secrets for a new package?"
|
|
1434
|
-
|
|
1435
|
-
**Ask yourself:** Does this package need to rename variables or add defaults?
|
|
1436
|
-
|
|
1437
|
-
#### If NO (simple filtering) → Use Push
|
|
1438
|
-
|
|
1439
|
-
Edit \`hush.yaml\` to add a target:
|
|
1440
|
-
\`\`\`yaml
|
|
1441
|
-
targets:
|
|
1442
|
-
- name: new-package
|
|
1443
|
-
path: ./packages/new-package
|
|
1444
|
-
format: dotenv
|
|
1445
|
-
include:
|
|
1446
|
-
- NEXT_PUBLIC_* # Or whatever pattern fits
|
|
1447
|
-
\`\`\`
|
|
1448
|
-
|
|
1449
|
-
**Benefits:** New \`NEXT_PUBLIC_*\` vars at root auto-flow. Zero maintenance.
|
|
1450
|
-
|
|
1451
|
-
#### If YES (transformation needed) → Use Pull
|
|
1452
|
-
|
|
1453
|
-
Create a template \`.env\` in the package:
|
|
1454
|
-
\`\`\`bash
|
|
1455
|
-
# packages/mobile/.env (committed to git)
|
|
1154
|
+
# packages/mobile/.hush
|
|
1456
1155
|
EXPO_PUBLIC_API_URL=\${API_URL} # Rename from root
|
|
1457
1156
|
EXPO_PUBLIC_DEBUG=\${DEBUG:-false} # With default
|
|
1458
1157
|
PORT=\${PORT:-8081} # Local default
|
|
@@ -1465,7 +1164,7 @@ PORT=\${PORT:-8081} # Local default
|
|
|
1465
1164
|
| Scenario | Update |
|
|
1466
1165
|
|----------|--------|
|
|
1467
1166
|
| New \`NEXT_PUBLIC_*\` var, web uses push | Nothing! Auto-flows |
|
|
1468
|
-
| New var mobile needs, mobile uses pull | \`packages/mobile/.
|
|
1167
|
+
| New var mobile needs, mobile uses pull | \`packages/mobile/.hush\` template |
|
|
1469
1168
|
| New package needs secrets | \`hush.yaml\` (push) or new template (pull) |
|
|
1470
1169
|
| Change var routing | \`hush.yaml\` include/exclude patterns |
|
|
1471
1170
|
|
|
@@ -1495,11 +1194,11 @@ Key Status:
|
|
|
1495
1194
|
\`\`\`
|
|
1496
1195
|
|
|
1497
1196
|
**Reading this:**
|
|
1498
|
-
- There
|
|
1197
|
+
- There are .env files present (legacy or output from decrypt)
|
|
1499
1198
|
- The project is configured with key management
|
|
1500
1199
|
- Keys are properly set up and backed up
|
|
1501
1200
|
|
|
1502
|
-
**To fix:** Run \`npx hush
|
|
1201
|
+
**To fix:** Run \`npx hush migrate\` (if v4) or delete/gitignore these .env files
|
|
1503
1202
|
|
|
1504
1203
|
### npx hush inspect output explained
|
|
1505
1204
|
|
|
@@ -1519,23 +1218,23 @@ Total: 3 variables
|
|
|
1519
1218
|
- \`API_KEY\` is not set - user needs to add it
|
|
1520
1219
|
`,
|
|
1521
1220
|
};
|
|
1522
|
-
function getSkillPath(location, root) {
|
|
1221
|
+
function getSkillPath(ctx, location, root) {
|
|
1523
1222
|
if (location === 'global') {
|
|
1524
|
-
return join(homedir(), '.claude', 'skills', 'hush-secrets');
|
|
1223
|
+
return ctx.path.join(homedir(), '.claude', 'skills', 'hush-secrets');
|
|
1525
1224
|
}
|
|
1526
|
-
return join(root, '.claude', 'skills', 'hush-secrets');
|
|
1225
|
+
return ctx.path.join(root, '.claude', 'skills', 'hush-secrets');
|
|
1527
1226
|
}
|
|
1528
|
-
async function promptForLocation() {
|
|
1227
|
+
async function promptForLocation(ctx) {
|
|
1529
1228
|
const rl = createInterface({
|
|
1530
|
-
input: process.stdin,
|
|
1531
|
-
output: process.stdout,
|
|
1229
|
+
input: ctx.process.stdin,
|
|
1230
|
+
output: ctx.process.stdout,
|
|
1532
1231
|
});
|
|
1533
1232
|
return new Promise((resolve) => {
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1233
|
+
ctx.logger.log(pc.bold('\nWhere would you like to install the Claude skill?\n'));
|
|
1234
|
+
ctx.logger.log(` ${pc.cyan('1)')} ${pc.bold('Global')} ${pc.dim('(~/.claude/skills/)')}`);
|
|
1235
|
+
ctx.logger.log(` Works across all your projects. Recommended for personal use.\n`);
|
|
1236
|
+
ctx.logger.log(` ${pc.cyan('2)')} ${pc.bold('Local')} ${pc.dim('(.claude/skills/)')}`);
|
|
1237
|
+
ctx.logger.log(` Bundled with this project. Recommended for teams.\n`);
|
|
1539
1238
|
rl.question(`${pc.bold('Choice')} ${pc.dim('[1/2]')}: `, (answer) => {
|
|
1540
1239
|
rl.close();
|
|
1541
1240
|
const choice = answer.trim();
|
|
@@ -1548,16 +1247,16 @@ async function promptForLocation() {
|
|
|
1548
1247
|
});
|
|
1549
1248
|
});
|
|
1550
1249
|
}
|
|
1551
|
-
function writeSkillFiles(skillPath) {
|
|
1552
|
-
mkdirSync(skillPath, { recursive: true });
|
|
1553
|
-
mkdirSync(join(skillPath, 'examples'), { recursive: true });
|
|
1250
|
+
function writeSkillFiles(ctx, skillPath) {
|
|
1251
|
+
ctx.fs.mkdirSync(skillPath, { recursive: true });
|
|
1252
|
+
ctx.fs.mkdirSync(ctx.path.join(skillPath, 'examples'), { recursive: true });
|
|
1554
1253
|
for (const [filename, content] of Object.entries(SKILL_FILES)) {
|
|
1555
|
-
const filePath = join(skillPath, filename);
|
|
1556
|
-
writeFileSync(filePath, content, 'utf-8');
|
|
1254
|
+
const filePath = ctx.path.join(skillPath, filename);
|
|
1255
|
+
ctx.fs.writeFileSync(filePath, content, 'utf-8');
|
|
1557
1256
|
}
|
|
1558
1257
|
}
|
|
1559
|
-
export async function skillCommand(options) {
|
|
1560
|
-
const {
|
|
1258
|
+
export async function skillCommand(ctx, options) {
|
|
1259
|
+
const { global: isGlobal, local: isLocal } = options;
|
|
1561
1260
|
let location;
|
|
1562
1261
|
if (isGlobal) {
|
|
1563
1262
|
location = 'global';
|
|
@@ -1566,30 +1265,30 @@ export async function skillCommand(options) {
|
|
|
1566
1265
|
location = 'local';
|
|
1567
1266
|
}
|
|
1568
1267
|
else {
|
|
1569
|
-
location = await promptForLocation();
|
|
1268
|
+
location = await promptForLocation(ctx);
|
|
1570
1269
|
}
|
|
1571
|
-
const skillPath = getSkillPath(location,
|
|
1572
|
-
const alreadyInstalled = existsSync(join(skillPath, 'SKILL.md'));
|
|
1270
|
+
const skillPath = getSkillPath(ctx, location, ctx.process.cwd());
|
|
1271
|
+
const alreadyInstalled = ctx.fs.existsSync(ctx.path.join(skillPath, 'SKILL.md'));
|
|
1573
1272
|
if (alreadyInstalled) {
|
|
1574
|
-
|
|
1575
|
-
|
|
1273
|
+
ctx.logger.log(pc.yellow(`\nSkill already installed at: ${skillPath}`));
|
|
1274
|
+
ctx.logger.log(pc.dim('To reinstall, delete the directory first.\n'));
|
|
1576
1275
|
return;
|
|
1577
1276
|
}
|
|
1578
|
-
|
|
1579
|
-
writeSkillFiles(skillPath);
|
|
1580
|
-
|
|
1277
|
+
ctx.logger.log(pc.blue(`\nInstalling Claude skill to: ${skillPath}`));
|
|
1278
|
+
writeSkillFiles(ctx, skillPath);
|
|
1279
|
+
ctx.logger.log(pc.green('\n✓ Skill installed successfully!\n'));
|
|
1581
1280
|
if (location === 'global') {
|
|
1582
|
-
|
|
1281
|
+
ctx.logger.log(pc.dim('The skill is now active for all projects using Claude Code.\n'));
|
|
1583
1282
|
}
|
|
1584
1283
|
else {
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1284
|
+
ctx.logger.log(pc.dim('The skill is now bundled with this project.'));
|
|
1285
|
+
ctx.logger.log(pc.dim('Commit the .claude/ directory to share with your team.\n'));
|
|
1286
|
+
ctx.logger.log(pc.bold('Suggested:'));
|
|
1287
|
+
ctx.logger.log(` git add .claude/`);
|
|
1288
|
+
ctx.logger.log(` git commit -m "chore: add Hush Claude skill"\n`);
|
|
1590
1289
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1290
|
+
ctx.logger.log(pc.bold('What the skill does:'));
|
|
1291
|
+
ctx.logger.log(` • Teaches AI to use ${pc.cyan('hush inspect')} instead of reading .env files`);
|
|
1292
|
+
ctx.logger.log(` • Prevents accidental exposure of secrets to LLMs`);
|
|
1293
|
+
ctx.logger.log(` • Guides AI through adding/modifying secrets safely\n`);
|
|
1595
1294
|
}
|