@chriscode/hush 2.4.1 → 2.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +446 -0
  2. package/package.json +1 -1
package/README.md ADDED
@@ -0,0 +1,446 @@
1
+ # @chriscode/hush
2
+
3
+ > **The AI-native secrets manager.** Secrets stay encrypted at rest. AI helps—without ever seeing values.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@chriscode/hush)](https://www.npmjs.com/package/@chriscode/hush)
6
+ [![Documentation](https://img.shields.io/badge/docs-hush--docs.pages.dev-blue)](https://hush-docs.pages.dev)
7
+
8
+ Hush keeps secrets **encrypted at rest** and only decrypts them in memory when running programs. AI assistants can help manage your secrets without ever seeing the actual values—because there are no plaintext files to read.
9
+
10
+ **[Read the full documentation →](https://hush-docs.pages.dev)**
11
+
12
+ ## Quick Start (with AI)
13
+
14
+ ```bash
15
+ npx @chriscode/hush skill
16
+ ```
17
+
18
+ That's it. Once installed, ask your AI assistant: *"Set up Hush for this project"* — it knows what to do.
19
+
20
+ ## Why Hush?
21
+
22
+ **The Problem:** AI coding assistants are incredibly helpful, but they can accidentally expose your secrets. Even with instructions to "not read .env files", LLMs find creative ways to access them using `cat`, `grep`, or shell tricks.
23
+
24
+ **The Solution:** Hush keeps secrets **encrypted at rest**—there are no plaintext `.env` files to read. When you need to run a program, `hush run -- <command>` decrypts secrets to memory and injects them as environment variables. The secrets never touch the disk.
25
+
26
+ ## Features
27
+
28
+ - **Encrypted at rest** - No plaintext secrets on disk, ever
29
+ - **Run with secrets** - `hush run -- npm start` decrypts to memory only
30
+ - **AI-safe commands** - `hush inspect`, `hush has`, `hush set` never expose values
31
+ - **Interactive secret input** - `hush set API_KEY` prompts user, AI never sees value
32
+ - **Every framework** - Next.js, Vite, Remix, Expo, Cloudflare Workers, and more
33
+ - **Smart routing** - Route `NEXT_PUBLIC_*` to frontend, server secrets to API
34
+ - **Claude Code Skill** - AI automatically uses safe commands
35
+
36
+ ## Framework Support
37
+
38
+ Hush works with **every major framework** out of the box. Use `include`/`exclude` patterns to route the right variables to each target:
39
+
40
+ | Framework | Client Prefix | Example Pattern |
41
+ |-----------|--------------|-----------------|
42
+ | **Next.js** | `NEXT_PUBLIC_*` | `include: [NEXT_PUBLIC_*]` |
43
+ | **Vite** | `VITE_*` | `include: [VITE_*]` |
44
+ | **Create React App** | `REACT_APP_*` | `include: [REACT_APP_*]` |
45
+ | **Vue CLI** | `VUE_APP_*` | `include: [VUE_APP_*]` |
46
+ | **Nuxt** | `NUXT_PUBLIC_*` | `include: [NUXT_PUBLIC_*]` |
47
+ | **Astro** | `PUBLIC_*` | `include: [PUBLIC_*]` |
48
+ | **SvelteKit** | `PUBLIC_*` | `include: [PUBLIC_*]` |
49
+ | **Expo / React Native** | `EXPO_PUBLIC_*` | `include: [EXPO_PUBLIC_*]` |
50
+ | **Gatsby** | `GATSBY_*` | `include: [GATSBY_*]` |
51
+ | **Remix** | (server-only) | No filtering needed |
52
+ | **Cloudflare Workers** | (server-only) | `format: wrangler` |
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ pnpm add -D @chriscode/hush
58
+ # or
59
+ npm install -D @chriscode/hush
60
+ ```
61
+
62
+ ### Prerequisites
63
+
64
+ ```bash
65
+ brew install sops age
66
+ ```
67
+
68
+ **Optional but recommended:** Install 1Password CLI for automatic key backup:
69
+ ```bash
70
+ brew install --cask 1password
71
+ brew install 1password-cli
72
+ ```
73
+
74
+ ## Quick Start
75
+
76
+ ### 1. Initialize Hush (auto-generates keys)
77
+
78
+ ```bash
79
+ npx hush init
80
+ ```
81
+
82
+ This will:
83
+ - Auto-detect your project structure
84
+ - Generate an age encryption key
85
+ - Back up the key to 1Password (if available)
86
+ - Create `hush.yaml` and `.sops.yaml`
87
+
88
+ **No 1Password?** Keys are saved locally to `~/.config/sops/age/keys/`. Share them securely with your team.
89
+
90
+ ### 3. Create your env files
91
+
92
+ ```bash
93
+ # .env (shared across environments)
94
+ DATABASE_URL=postgres://localhost/mydb
95
+ STRIPE_SECRET_KEY=sk_test_xxx
96
+ NEXT_PUBLIC_API_URL=${API_BASE}/v1
97
+
98
+ # .env.development
99
+ API_BASE=http://localhost:3000
100
+ DEBUG=true
101
+
102
+ # .env.production
103
+ API_BASE=https://api.example.com
104
+ DEBUG=false
105
+ ```
106
+
107
+ ### 4. Encrypt and run
108
+
109
+ ```bash
110
+ npx hush encrypt # Encrypt secrets
111
+ npx hush run -- npm start # Run with secrets (never written to disk!)
112
+ npx hush run -e prod -- npm build # Run with production secrets
113
+ npx hush status # Check your setup
114
+ ```
115
+
116
+ ## Configuration
117
+
118
+ ### hush.yaml
119
+
120
+ ```yaml
121
+ sources:
122
+ shared: .env
123
+ development: .env.development
124
+ production: .env.production
125
+
126
+ targets:
127
+ # Root gets all variables
128
+ - name: root
129
+ path: .
130
+ format: dotenv
131
+
132
+ # Next.js app gets only public variables
133
+ - name: web
134
+ path: ./apps/web
135
+ format: dotenv
136
+ include:
137
+ - NEXT_PUBLIC_*
138
+
139
+ # API gets everything except public variables
140
+ - name: api
141
+ path: ./apps/api
142
+ format: wrangler
143
+ exclude:
144
+ - NEXT_PUBLIC_*
145
+ - VITE_*
146
+ - EXPO_PUBLIC_*
147
+
148
+ # Kubernetes config
149
+ - name: k8s
150
+ path: ./k8s
151
+ format: yaml
152
+ ```
153
+
154
+ ### Output Formats
155
+
156
+ | Format | Output File | Use Case |
157
+ |--------|-------------|----------|
158
+ | `dotenv` | `.env.development` | Next.js, Vite, CRA, Vue, Nuxt, Remix, Astro, SvelteKit, Expo, Node.js |
159
+ | `wrangler` | `.dev.vars` | Cloudflare Workers & Pages |
160
+ | `json` | `.env.development.json` | AWS Lambda, serverless functions, custom tooling |
161
+ | `shell` | `.env.development.sh` | CI/CD pipelines, Docker builds, shell scripts |
162
+ | `yaml` | `.env.development.yaml` | Kubernetes ConfigMaps, Docker Compose |
163
+
164
+ ### Target Options
165
+
166
+ | Option | Description |
167
+ |--------|-------------|
168
+ | `name` | Identifier for the target |
169
+ | `path` | Directory to write output file |
170
+ | `format` | Output format: `dotenv`, `wrangler`, `json`, `shell`, `yaml` |
171
+ | `include` | Glob patterns to include (e.g., `NEXT_PUBLIC_*`) |
172
+ | `exclude` | Glob patterns to exclude |
173
+
174
+ ## Commands
175
+
176
+ | Command | Description | AI-Safe? |
177
+ |---------|-------------|----------|
178
+ | `hush run -- <cmd>` | Run command with secrets in memory | ✅ |
179
+ | `hush set <KEY>` | Set a secret interactively | ✅ |
180
+ | `hush set <KEY> --gui` | Set secret via macOS dialog (for AI agents) | ✅ |
181
+ | `hush edit [env]` | Edit secrets in `$EDITOR` | ✅ |
182
+ | `hush inspect` | List variables (masked values) | ✅ |
183
+ | `hush has <KEY>` | Check if a secret exists | ✅ |
184
+ | `hush init` | Generate config + keys (auto 1Password backup) | ✅ |
185
+ | `hush encrypt` | Encrypt `.env` files | ✅ |
186
+ | `hush keys setup` | Pull key from 1Password or use local | ✅ |
187
+ | `hush keys generate` | Generate new key + backup to 1Password | ✅ |
188
+ | `hush keys list` | List local and 1Password keys | ✅ |
189
+ | `hush push` | Push to Cloudflare Workers | ✅ |
190
+ | `hush status` | Show configuration | ✅ |
191
+ | `hush skill` | Install AI skill | ✅ |
192
+ | `hush check` | Verify encryption sync | ✅ |
193
+ | `hush list` | List variables (shows values!) | ⚠️ |
194
+ | `hush decrypt` | Write secrets to disk (deprecated) | ⚠️ |
195
+
196
+ ## AI-Native Design
197
+
198
+ Hush is built for a world where AI helps write code. Traditional secrets management fails because LLMs can read `.env` files using `cat`, `grep`, or other tools—even when told not to.
199
+
200
+ **Hush solves this by keeping secrets encrypted at rest.** There are no plaintext files to read. When you need secrets, `hush run` decrypts them to memory and injects them as environment variables.
201
+
202
+ ### `hush run` - Run Programs with Secrets
203
+
204
+ The primary way to use secrets. Decrypts to memory, never writes to disk.
205
+
206
+ ```bash
207
+ $ hush run -- npm start # Development
208
+ $ hush run -e prod -- npm build # Production
209
+ $ hush run -t api -- wrangler dev # Filter for specific target
210
+ ```
211
+
212
+ ### `hush set <KEY>` - Add Secrets Safely
213
+
214
+ AI invokes this command, user enters the value. The secret is never visible to the AI.
215
+
216
+ ```bash
217
+ $ hush set DATABASE_URL
218
+ Enter value for DATABASE_URL: ••••••••••••••••
219
+ ✓ DATABASE_URL set (45 chars, encrypted)
220
+ ```
221
+
222
+ ### `hush inspect` - See What's Configured
223
+
224
+ Shows all secrets with **masked values**. AI can see what exists without seeing actual secrets.
225
+
226
+ ```bash
227
+ $ hush inspect
228
+
229
+ Secrets for development:
230
+
231
+ DATABASE_URL = post****************... (45 chars)
232
+ STRIPE_SECRET_KEY = sk_t****************... (32 chars)
233
+ API_KEY = (not set)
234
+
235
+ Total: 3 variables
236
+ ```
237
+
238
+ ### `hush has <KEY>` - Check Specific Secrets
239
+
240
+ ```bash
241
+ $ hush has DATABASE_URL
242
+ DATABASE_URL is set (45 chars)
243
+
244
+ $ hush has MISSING_KEY
245
+ MISSING_KEY not found
246
+ ```
247
+
248
+ ### Claude Code / OpenCode Skill
249
+
250
+ For [Claude Code](https://docs.anthropic.com/en/docs/claude-code) or [OpenCode](https://github.com/opencode-ai/opencode) users, Hush includes a ready-to-use **Agent Skill** that automatically teaches the AI to never read `.env` files directly.
251
+
252
+ **Install the skill:**
253
+
254
+ ```bash
255
+ npx hush skill # Interactive: choose global or local
256
+ npx hush skill --global # Install to ~/.claude/skills/ (all projects)
257
+ npx hush skill --local # Install to ./.claude/skills/ (this project)
258
+ ```
259
+
260
+ **Global vs Local:**
261
+ - **Global** (`~/.claude/skills/`) - Works across all your projects. Recommended for personal use.
262
+ - **Local** (`./.claude/skills/`) - Bundled with the project. Recommended for teams (commit to git).
263
+
264
+ **What the skill does:**
265
+ - Detects when you're working with secrets or environment variables
266
+ - Uses `hush inspect` and `hush has` instead of reading `.env` files
267
+ - Guides you through adding or modifying secrets safely
268
+ - Never exposes secret values to the LLM
269
+
270
+ The skill includes `SKILL.md` (core instructions), `REFERENCE.md` (command details), and `examples/workflows.md` (step-by-step guides).
271
+
272
+ ## Example: Monorepo with Next.js + Cloudflare Worker
273
+
274
+ ```yaml
275
+ # hush.yaml
276
+ sources:
277
+ shared: .env
278
+ development: .env.development
279
+ production: .env.production
280
+
281
+ targets:
282
+ # Next.js frontend - only public vars
283
+ - name: web
284
+ path: ./apps/web
285
+ format: dotenv
286
+ include:
287
+ - NEXT_PUBLIC_*
288
+
289
+ # Cloudflare Worker API - server secrets only
290
+ - name: api
291
+ path: ./apps/api
292
+ format: wrangler
293
+ exclude:
294
+ - NEXT_PUBLIC_*
295
+ ```
296
+
297
+ ```bash
298
+ # .env
299
+ DATABASE_URL=postgres://...
300
+ STRIPE_SECRET_KEY=sk_live_...
301
+ NEXT_PUBLIC_API_URL=${API_BASE}/v1
302
+ NEXT_PUBLIC_STRIPE_KEY=pk_live_...
303
+
304
+ # .env.development
305
+ API_BASE=http://localhost:8787
306
+
307
+ # .env.production
308
+ API_BASE=https://api.myapp.com
309
+ ```
310
+
311
+ When running with `hush run -t web -- npm start`:
312
+ - Web app receives only `NEXT_PUBLIC_*` variables in memory
313
+ - API receives `DATABASE_URL`, `STRIPE_SECRET_KEY` (no public vars)
314
+
315
+ ## How It Works
316
+
317
+ ### Source File Merging
318
+
319
+ When you run `hush run`:
320
+
321
+ 1. **Shared** (`.env.encrypted`) - Base variables
322
+ 2. **Environment** (`.env.development.encrypted` or `.env.production.encrypted`) - Overrides
323
+ 3. **Local** (`.env.local.encrypted`) - Personal overrides (not committed)
324
+
325
+ Later files override earlier ones for the same key. All decryption happens in memory.
326
+
327
+ ### Variable Interpolation
328
+
329
+ Reference other variables with `${VAR}`:
330
+
331
+ ```bash
332
+ HOST=localhost
333
+ PORT=3000
334
+ BASE_URL=http://${HOST}:${PORT}
335
+ API_URL=${BASE_URL}/api
336
+ ```
337
+
338
+ ### Target Filtering
339
+
340
+ Use `include` and `exclude` patterns to route variables to the right places:
341
+
342
+ ```yaml
343
+ targets:
344
+ - name: frontend
345
+ include: [NEXT_PUBLIC_*, VITE_*] # Only client-safe vars
346
+
347
+ - name: backend
348
+ exclude: [NEXT_PUBLIC_*, VITE_*] # Everything except client vars
349
+ ```
350
+
351
+ ## Git Hook Integration
352
+
353
+ Prevent committing unencrypted changes with `hush check`:
354
+
355
+ ```bash
356
+ # .husky/pre-commit
357
+ npx hush check || exit 1
358
+ ```
359
+
360
+ Bypass when needed: `HUSH_SKIP_CHECK=1 git commit -m "emergency fix"`
361
+
362
+ ## File Reference
363
+
364
+ | File | Committed | Purpose |
365
+ |------|-----------|---------|
366
+ | `hush.yaml` | Yes | Configuration |
367
+ | `.sops.yaml` | Yes | SOPS config with public key |
368
+ | `.env.encrypted` | Yes | Encrypted shared secrets |
369
+ | `.env.development.encrypted` | Yes | Encrypted dev secrets |
370
+ | `.env.production.encrypted` | Yes | Encrypted prod secrets |
371
+ | `.env.local.encrypted` | No | Encrypted personal overrides |
372
+
373
+ **Note:** With the new `hush run` command, plaintext `.env` files are no longer generated. Secrets only exist in memory when running programs.
374
+
375
+ ## Programmatic Usage
376
+
377
+ ```typescript
378
+ import {
379
+ loadConfig,
380
+ parseEnvContent,
381
+ interpolateVars,
382
+ filterVarsForTarget,
383
+ mergeVars,
384
+ formatVars,
385
+ } from '@chriscode/hush';
386
+
387
+ const config = loadConfig('/path/to/repo');
388
+ const vars = parseEnvContent(decryptedContent);
389
+ const interpolated = interpolateVars(vars);
390
+ const filtered = filterVarsForTarget(interpolated, config.targets[0]);
391
+ const output = formatVars(filtered, 'dotenv');
392
+ ```
393
+
394
+ ## Team Setup
395
+
396
+ ### New team member onboarding
397
+
398
+ **With 1Password (recommended):**
399
+ ```bash
400
+ npx hush keys setup # Pulls key from 1Password automatically
401
+ ```
402
+
403
+ **Without 1Password:**
404
+ 1. Get the private key from a team member (via secure channel)
405
+ 2. Save to `~/.config/sops/age/keys/{project}.txt`
406
+ 3. Run `npx hush status` to verify
407
+
408
+ ### Key management commands
409
+
410
+ ```bash
411
+ hush keys setup # Pull from 1Password or verify local key
412
+ hush keys generate # Generate new key + backup to 1Password
413
+ hush keys pull # Pull key from 1Password
414
+ hush keys push # Push local key to 1Password
415
+ hush keys list # List all keys (local + 1Password)
416
+ ```
417
+
418
+ ## Troubleshooting
419
+
420
+ ### "SOPS is not installed" or "age not found"
421
+ ```bash
422
+ brew install sops age
423
+ ```
424
+
425
+ ### "No identity matched"
426
+ Your age key doesn't match the one used to encrypt. Options:
427
+ 1. **With 1Password:** Run `hush keys setup` to pull the correct key
428
+ 2. **Without 1Password:** Get the private key from a team member
429
+
430
+ ### "1Password CLI not available"
431
+ Hush works without 1Password - keys are stored locally. For backup:
432
+ ```bash
433
+ brew install --cask 1password
434
+ brew install 1password-cli
435
+ # Enable "Integrate with 1Password CLI" in 1Password settings
436
+ ```
437
+
438
+ ### Target not receiving expected variables
439
+ Check your `include`/`exclude` patterns in `hush.yaml`. Run `hush status` to see target configuration.
440
+
441
+ ### AI assistant reading .env files directly
442
+ Install the Claude Code skill: `npx hush skill --global`
443
+
444
+ ## License
445
+
446
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chriscode/hush",
3
- "version": "2.4.1",
3
+ "version": "2.4.2",
4
4
  "description": "SOPS-based secrets management for monorepos. Encrypt once, decrypt everywhere.",
5
5
  "type": "module",
6
6
  "bin": {