envy-secrets 0.5.1

A secure, deterministic, and frictionless environment variable manager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
<div align="center">

<!-- Replace with your logo -->
<!-- <img src="docs/assets/logo.svg" alt="Envy" width="120" /> -->

# envy

### Encrypted secrets, zero friction.

**Local-first secret management for teams who take security seriously.**
No SaaS. No internet. No plaintext — ever.

[![CI](https://github.com/anguriatech/envy/actions/workflows/ci.yml/badge.svg)](https://github.com/anguriatech/envy/actions/workflows/ci.yml)
[![Latest Release](https://img.shields.io/github/v/release/anguriatech/envy)](https://github.com/anguriatech/envy/releases/latest)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)

```bash
brew install anguriatech/tap/envy
```

```bash
npm install -g @anguriatech/envy
```

```bash
cargo install envy-secrets
```

</div>

---

<div align="center">
  <img src="docs/assets/demo.gif" alt="Envy demo" width="100%" />
</div>

---

## The Problem

Every project starts with a `.env` file. Every `.env` file eventually ends up somewhere it shouldn't.

- **Committed to git** — accidentally or by a junior dev following a tutorial
- **Pasted in Slack** — "hey, can you check this config?" becomes a security incident
- **Left on disk** — cloned repos, CI artifacts, and Docker image layers carry your secrets forever
- **Shared as plaintext** — emailed, screenshot, airdropped, or typed into a Google Doc

The tools meant to solve this — hosted vaults, secrets managers, SaaS platforms — trade one risk for another: now your secrets live on someone else's server, behind their authentication, subject to their breach.

**There is no good reason for production secrets to ever exist in plaintext.** Envy makes that guarantee practical.

---

## Why Envy

<table>
<tr>
<td width="50%" valign="top">

### 🔐 Zero-Trust Storage

Secrets are encrypted with AES-256-GCM before they touch the database. The database itself is encrypted with SQLCipher. The master key lives exclusively in your OS Keychain — never written to any file, never exposed to the filesystem.

Stealing your `~/.envy/vault.db` gets an attacker nothing without the OS credential entry. Stealing your OS credential entry gets them nothing without the encrypted database. **Defense in depth, not defense by hope.**

</td>
<td width="50%" valign="top">

### 🧠 Memory-Safe Secret Injection

`envy run -- your-app` decrypts secrets in RAM and passes them to your process via `std::process::Command::envs()`. When the process exits, the memory is zeroed. Nothing is written to disk, shell history, or environment exports.

All secret values are wrapped in Rust's `Zeroizing<T>` — backing memory is overwritten to zero on drop, even if the program panics.

</td>
</tr>
<tr>
<td width="50%" valign="top">

### 🌿 GitOps-Native

`envy encrypt` produces `envy.enc` — a single JSON file sealed with Argon2id + AES-256-GCM. It contains **no key names, no values, no project identifiers**. Commit it to your public repo. Post it on Twitter. It is pure ciphertext.

`envy decrypt` on any machine restores your vault from the artifact. Onboarding a new team member is a `git pull` and one passphrase.

</td>
<td width="50%" valign="top">

### 🔬 Pre-Encrypt Audit Trail

`envy diff` shows exactly what will change before you seal — additions, deletions, and modifications — with `diff(1)` exit codes for CI/CD gating. Values are hidden by default. `--reveal` requires explicit opt-in with a stderr warning.

Know exactly what you're committing to the artifact before you commit it.

</td>
</tr>
<tr>
<td width="50%" valign="top">

### 🏢 Multi-Team Progressive Disclosure

Seal `development`, `staging`, and `production` with separate passphrases. A developer with the dev key imports their environment; `production` is listed as skipped — **not an error, not a prompt, not an alarm**. Access follows the passphrase, not a permissions UI.

</td>
<td width="50%" valign="top">

### 🤖 CI/CD Native, Zero Config

Set `ENVY_PASSPHRASE_<ENV>` in your pipeline's secret store. Envy detects it and goes fully headless — no TTY, no prompts, no code changes required. Works with GitHub Actions, GitLab CI, CircleCI, Jenkins, and any shell that supports environment variables.

</td>
</tr>
</table>

> **SOC 2 / Compliance note**: Envy eliminates the most common source of secret leakage — plaintext `.env` files in version control, chat logs, and build artifacts. It does not replace a full secrets management platform for regulated workloads, but it is a substantial step toward auditability: every secret change is a vault write, every seal is a committed `envy.enc` diff.

---

## Quickstart

**Step 1 — Install and initialise**

```bash
# Homebrew (macOS & Linux)
brew install anguriatech/tap/envy

# NPM (all platforms)
npm install -g @anguriatech/envy
```

> Windows, curl, or build-from-source? See the [Installation]#installation section below.

```bash
cd my-project
envy init       # creates envy.toml (safe to commit)
```

**Step 2 — Store secrets and run your app**

```bash
envy set DATABASE_URL=postgres://localhost/myapp
envy set API_KEY=sk_live_abc123

envy run -- npm run dev
# secrets injected into the child process, never written to disk
```

**Step 3 — Seal and share with your team**

```bash
# Preview what you're about to commit
envy diff
#   + API_KEY
#   + DATABASE_URL
# 2 changes: 2 added, 0 removed, 0 modified

envy encrypt      # prompts for a passphrase (or set ENVY_PASSPHRASE in CI)

git add envy.enc envy.toml
git commit -m "chore: add encrypted secrets"
git push
```

A teammate pulls the repo and runs `envy decrypt`. Done. No Slack messages, no shared spreadsheets, no plaintext ever leaving your encrypted vault.

### Interactive TUI

<div align="center">
  <img src="docs/assets/tui-demo.gif" alt="Envy TUI demo" width="100%" />
</div>

Run `envy` without a subcommand from an interactive terminal to open the full-screen vault
workstation: a three-panel console showing your project tree, the selected environment's
masked secrets, and an inspector column with sync state and metadata — all without changing
the output contract of existing commands. Launching from a project directory focuses that
project's entry in the tree.

The TUI is **workspace-scoped**: the tree shows the projects whose `envy.toml` manifests sit
at or below the directory where you launched `envy` (full depth, `.gitignore` respected), and
every project carries its own artifact path and rotation threshold. Projects from the rest of
the vault stay out of the way — launch from your monorepo root or from a single project, and
only what you can reach gets shown.

The gradient banner opens expanded on terminals tall enough (32+ rows); otherwise — or after
pressing `B` — it collapses to a one-line identity strip with the current version and
workspace name, so the brand is present without eating screen space.

| Key | Panel | Action |
|---|---|---|
| `↑↓` / `j` `k` | both | Move selection |
| `Tab` | both | Switch between Projects and Secrets |
| `Enter` / `` / `` | tree | Expand/select or collapse |
| `Space` | secrets | Reveal selected value (re-masks on move) |
| `Y` | secrets | Copy value to clipboard (clears 30s after last copy) |
| `N` / `E` / `D` | secrets | Create, edit, or delete a secret |
| `F` | secrets | Filter keys (filter persists, shown in the title) |
| `S` | both | Seal project into `envy.enc` (preview confirmation) |
| `T` / `G` | tree | Status / diff against `envy.enc` |
| `Y` | tree | Import environment from `envy.enc` (confirms) |
| `R` | tree | Rotate environment passphrase in-place |
| `X` | tree | Delete selected project (exact-name confirmation) |
| `B` | both | Toggle banner (full gradient logo or compact strip) |
| `L` / `U` | both | Lock or unlock the vault |
| `:` | both | Command palette — every action, searchable |
| `?` | both | Full help (scrollable) |
| `Q` / `Ctrl+C` | both | Quit and restore terminal |

The bottom legend always shows the keys of the focused panel, so nothing has to be
memorized. The inspector column on the right shows sync state (in sync / modified / never
sealed), secret counts, stale secrets, and the artifact location for whatever is selected;
it hides automatically on terminals narrower than 100 columns.

Color is functional: brand violet marks the focused panel, green means in sync, amber means
drift (needs re-seal), red marks errors and destructive confirmations. Set `NO_COLOR=1` for
a plain presentation. The TUI never starts for subcommands such as `envy run -- command`;
when stdout is piped, bare `envy` prints help to stderr and exits successfully, keeping
scripts and CI safe.
The footer intentionally shows only primary navigation. Press `?` for complete grouped help;
long project trees and project-picker results scroll while keeping the current selection visible.

#### Nested projects (monorepo / multi-project support)

Since v0.3.2, `envy init` works in subdirectories of existing envy projects. Each project gets its own UUID in the vault and its own `envy.toml` + `envy.enc`. Commands resolve the closest `envy.toml` automatically — running `envy list` from a child directory shows the child's secrets, not the parent's.

```
/monorepo/
  envy.toml     ← org-wide credentials
  envy.enc
  /project-a/
    envy init   ← project-specific credentials (different UUID)
    envy.toml
    envy.enc
  /project-b/
    envy init
    envy.toml
    envy.enc
```

---

## Sync Status at a Glance

`envy status` tells you the state of every environment — no passphrase, no decryption.

```
$ envy status

+-------------+---------+------------------+----------------+-----------+
| Environment | Secrets | Last Modified    | Status         | Rotation  |
+=======================================================================+
| development | 4       | 2 minutes ago    | ⚠ Modified     | ✓ Fresh   |
| production  | 3       | 3 days ago       | ✓ In Sync      | ⚠ 1 due   |
| staging     | 2       | 1 week ago       | ✗ Never Sealed | ✓ Fresh   |
+-------------+---------+------------------+----------------+-----------+

Artifact: ./envy.enc  (last written: 3 days ago)
  Sealed environments: production
```

See **Modified**? Run `envy diff` to see exactly what changed, then `envy encrypt` to seal.

---

## CI/CD Integration

```yaml
# .github/workflows/deploy.yml
- name: Decrypt secrets
  env:
    ENVY_PASSPHRASE_PRODUCTION: ${{ secrets.ENVY_PASSPHRASE_PRODUCTION }}
  run: envy decrypt

# Gate on exact artifact state before deploying
- name: Assert no unsealed drift
  env:
    ENVY_PASSPHRASE_PRODUCTION: ${{ secrets.ENVY_PASSPHRASE_PRODUCTION }}
  run: |
    envy diff -e production          # exit 1 if vault ≠ artifact
    echo "✓ Artifact matches vault"

- name: Deploy
  run: envy run -e production -- ./scripts/deploy.sh
```

The `ENVY_PASSPHRASE_<ENV>` env var is the only config change required. Your application code and deploy scripts are untouched.

---

<details>
<summary><strong>📐 Architecture & Cryptography</strong></summary>

## How It Works

```
Local development:

  envy.toml             ~/.envy/vault.db              OS Keyring
  (project UUID)  →     (SQLCipher-encrypted DB)  ←   (32-byte master key)
                        AES-256-GCM per-secret
                        sync_markers (sealed_at per env)

Team sync via Git:

  ~/.envy/vault.db  →[envy encrypt]→  envy.enc (Argon2id + AES-256-GCM)
                                            │
                                       git commit/push
                                            │
                    ←[envy decrypt]←  envy.enc
```

### The Two-Key Model

| | Vault master key | Artifact passphrase |
|---|---|---|
| **Purpose** | Encrypts secrets at rest in `vault.db` | Encrypts `envy.enc` for sharing |
| **Stored in** | OS Keychain / Secret Service (never on disk) | Not stored — entered by user or `ENVY_PASSPHRASE` |
| **Scope** | Per machine, per user | Per team, per project |
| **Format** | 32 random bytes | Human-readable string |

These keys are entirely independent. Knowing the passphrase does not help with the vault. Copying the vault without the OS credential entry is useless.

### Cryptography Stack

```
Passphrase (user input)
    │
    ▼  Argon2id  (64 MiB memory, 3 iterations, parallelism 4)
256-bit derived key
    │
    ▼  AES-256-GCM  (random 96-bit nonce per seal)
Ciphertext + 128-bit authentication tag
    │
    ▼  base64ct  (constant-time Base64)
envy.enc  →  git commit
```

**Argon2id** is the Password Hashing Competition winner (2015). Memory-hard and side-channel resistant — GPU-based brute-force against the passphrase requires 64 MiB of RAM per attempt.

**AES-256-GCM** provides authenticated encryption — any modification to the ciphertext is detected before a single byte of plaintext is returned. This is what makes Progressive Disclosure safe: a wrong passphrase fails authentication silently, it never returns garbage data.

**Fresh nonce per seal** — re-sealing the same secrets produces different ciphertext every time. Ciphertext comparison attacks are not possible.

### The `envy.enc` Structure

```json
{
  "version": 1,
  "environments": {
    "development": {
      "ciphertext": "<base64>",
      "nonce":      "<base64>",
      "kdf": {
        "algorithm":    "argon2id",
        "memory_kib":   65536,
        "time_cost":    3,
        "parallelism":  4,
        "salt":         "<base64>"
      }
    }
  }
}
```

Every envelope is self-describing — it carries its own KDF parameters. You can decrypt any envelope without external metadata or a version registry. The `environments` map is a `BTreeMap` so JSON keys are always alphabetically ordered, producing deterministic `git diff` output.

### Memory Safety

Every secret value travels through the codebase in `zeroize::Zeroizing<String>`. When the container is dropped (on function return, scope exit, or panic), the backing memory is overwritten to zero by the OS. Secret values are never stored in a plain `String`.

</details>

---

<details>
<summary><strong>📋 Full Command Reference</strong></summary>

| Command | Alias | Description |
|---------|-------|-------------|
| [`envy init`]docs/commands/envy-init.md || Create `envy.toml`, register project in vault |
| [`envy set KEY=VALUE [-e ENV] [--stdin]`]docs/commands/envy-set.md || Store or update a secret |
| [`envy get KEY [-e ENV]`]docs/commands/envy-get.md || Print a single decrypted value to stdout |
| [`envy list [-e ENV]`]docs/commands/envy-list.md | `ls` | List all key names (values never printed by default) |
| [`envy rm KEY [-e ENV]`]docs/commands/envy-rm.md | `remove`, `unset` | Delete a secret |
| [`envy run [-e ENV] -- CMD`]docs/commands/envy-run.md || Inject secrets and run a child process |
| [`envy migrate FILE [-e ENV]`]docs/commands/envy-migrate.md || Import all `KEY=VALUE` pairs from a `.env` file |
| [`envy encrypt [-e ENV]`]docs/commands/envy-encrypt.md | `enc` | Seal vault into `envy.enc` (strict: passphrase must match an existing envelope — use `envy rotate` to change it) |
| [`envy decrypt`]docs/commands/envy-decrypt.md | `dec` | Unseal `envy.enc` and restore secrets |
| [`envy export [-e ENV] [--format]`]docs/commands/envy-export.md || Print all secrets to stdout (dotenv / JSON / shell) |
| [`envy diff [-e ENV] [--reveal]`]docs/commands/envy-diff.md | `df` | Compare vault against `envy.enc` before encrypting |
| [`envy status`]docs/commands/envy-status.md | `st` | Show sync status dashboard, including a rotation reminder (no passphrase required) |
| [`envy rotate [-e ENV]`]docs/commands/envy-rotate.md || Re-seal an envelope with a new passphrase (verifies current first) |
| [`envy scan [-e ENV] [--reveal]`]docs/commands/envy-scan.md || Scan the working tree for plaintext copies of vault secrets |
| [`envy audit [-e ENV] [--limit N]`]docs/commands/envy-audit.md | `au` | Show the local history of `set`/`get`/`rm`/`run` actions |
| [`envy hooks install [--force]`]docs/commands/envy-hooks.md || Install a pre-commit hook that blocks commits leaking a vault secret |
| [`envy completions SHELL`]docs/commands/envy-completions.md || Print shell completion script to stdout |

### Output Formats

Most read commands accept `--format` (or `-f`):

| Format | Description |
|--------|-------------|
| `table` | Human-readable (default) |
| `json` | Machine-readable JSON |
| `dotenv` | `KEY=value` pairs |
| `shell` | `export KEY='value'` — safe for `eval $(...)` |

### `envy diff` — the pre-encrypt review loop

```bash
# Table output (key names only, colored)
envy diff [-e ENV]

# With values (stderr warning emitted first)
envy diff [-e ENV] --reveal

# JSON for scripts — old_value/new_value absent without --reveal
envy diff [-e ENV] --format json
```

**Exit codes for `envy diff`**: `0` = no differences, `1` = differences found, `2+` = error.

### Shell Autocompletion

```bash
envy completions bash   >> ~/.bash_completion
envy completions zsh    >  ~/.zfunc/_envy      # then: autoload -Uz compinit && compinit
envy completions fish   >  ~/.config/fish/completions/envy.fish
envy completions powershell >> $PROFILE
```

### Legacy Migration

```bash
envy migrate .env              # import development secrets
envy migrate .env.staging -e staging
envy list                      # verify
rm .env .env.staging
echo '.env*' >> .gitignore
```

### Multi-Environment with Separate Passphrases

```bash
envy enc -e development    # dev passphrase
envy enc -e staging        # staging passphrase
envy enc -e production     # prod passphrase (restricted)

# Smart Merge: each seal preserves the other envelopes untouched
git add envy.enc && git commit -m "chore: rotate secrets"
```

```bash
# Junior dev — has only the dev key
envy decrypt
#   ✓  development   (4 secrets upserted)
#   ⚠  production    skipped — different passphrase or key
# exit code: 0  ← partial access is success
```

#### Rotating a passphrase

Use `envy rotate` as the safe path for key rotation. Unlike `envy encrypt`, it verifies the current passphrase against the existing envelope before accepting a new one — a typo can never silently change the envelope's passphrase.

```bash
envy rotate -e production
# Passphrase for 'production':    <old-pass>
# New passphrase for 'production': <new-pass>
# Confirm new passphrase:          <new-pass>
#   ✓  'production' rotated. Passphrase changed.
#      Previous passphrase can no longer decrypt this artifact.
```

In CI / headless mode, set both `ENVY_PASSPHRASE_<ENV>` and `ENVY_PASSPHRASE_<ENV>_NEW`:

```bash
ENVY_PASSPHRASE_PRODUCTION=old-pass \
ENVY_PASSPHRASE_PRODUCTION_NEW=new-pass \
  envy rotate -e production
```

The rotation is **forward-only** — the old passphrase can no longer decrypt the artifact, and any other `envy.enc` sealed with the old passphrase can never be decrypted. The team's responsibility is to distribute the new passphrase through a secure channel (1Password, password manager, secure Slack DM, etc.).

#### Strict `envy encrypt` — no silent key rotation

Since v0.3.1, `envy encrypt` is strict: the passphrase you provide must either match the existing envelope (re-seal) or be the first time you're creating the envelope. If neither condition holds, `envy encrypt` fails with:

```
error: passphrase input failed: passphrase does not match the existing envelope.
hint: use `envy rotate -e ENV` to change the envelope's passphrase.
```

Exit code 2. The artifact is left unchanged. Use `envy rotate -e ENV` to change the passphrase — `envy encrypt` will not do it for you.

### Rotation reminder

`envy status` flags secrets that haven't been touched in a while — no decryption involved, just `updated_at` timestamps:

```
$ envy status
...
| production  | 3       | 3 days ago       | ✓ In Sync      | ⚠ 1 due   |

⚠ Rotation reminder (no changes in over 90 days):
  production: LEGACY_API_KEY
```

The threshold defaults to 90 days and is configurable per project in `envy.toml`:

```toml
rotation_reminder_days = 30
```

### Vault-leak scanner

`envy scan` walks the working tree looking for **exact** occurrences of secret values already stored in the vault — not a generic regex-based secrets scanner, so false positives are essentially zero: a hit means a value you're already managing with envy was also pasted in plaintext somewhere in the repo (`.env` files included — dotfiles are scanned on purpose).

```bash
envy scan               # masked output, exit 1 if anything is found
envy scan --reveal      # show the matched value (stderr warning first)
envy scan --format json # for CI
```

Respects `.gitignore`. Exit codes follow the `diff(1)` convention (0 clean, 1 found, 2+ error), so it composes with CI or `envy hooks install` below.

### Local audit trail

`envy audit` lists the local history of `set`/`get`/`rm`/`run` actions — key names and timestamps only, never values:

```bash
envy audit                 # newest first, all environments
envy audit -e production   # filter to one environment
```

Sync/crypto actions (`encrypt`/`decrypt`/`rotate`) aren't recorded here; that history already lives in `envy.enc`'s git log and `envy status`.

### Pre-commit hook

`envy hooks install` writes a `pre-commit` hook that runs `envy scan` on every commit — attempting to commit a leaked secret is blocked, not just logged:

```bash
envy hooks install
git commit -m "..."
# envy: blocked -- a vault secret's plaintext value was found in a file
# you're about to commit. Run 'envy scan --reveal' for details.
```

It also prints a non-blocking warning when `envy status` shows unsealed drift. Nothing leaves the machine. A pre-existing hook envy didn't install is never overwritten without `--force`, and even then the previous file is backed up first.

### Rotation reminder

`envy status` flags secrets that haven't been touched in over `rotation_reminder_days` days (default 90, configurable in `envy.toml`):

```toml
# envy.toml
rotation_reminder_days = 120
```

When any secret exceeds the threshold, a `⚠ Rotation reminder` section lists the affected key names — values are never shown. Set `rotation_reminder_days = 0` to disable the reminder entirely. This is read-only and never decrypts anything; it simply compares `updated_at` timestamps against the current wall-clock time.

### Passphrase strength hint

When you type a passphrase by hand during interactive `envy encrypt` or `envy rotate` prompts, a non-blocking strength estimate is printed:

```
  ℹ passphrase strength: weak (~36 bits estimated)
    hint: press Enter on an empty prompt next time to accept envy's suggested Diceware phrase.
```

This is purely informational — a low score never blocks or rejects a passphrase. Security rests on Argon2id + AES-256-GCM, not on this heuristic. The hint nudges you toward envy's built-in Diceware passphrase generator (press Enter on an empty prompt).

</details>

---

## Documentation

- [Per-command reference]docs/commands/ — one page per command: what it does, syntax, exit codes, and related commands
- [Examples]examples/ — copy-pasteable, CI-verified workflows (basic, team-sync, CI/CD, monorepo)
- [Developer guide]docs/developer-guide.md — architecture, module map, and contribution notes
- [Demo videos]docs/assets/ — terminal walkthroughs of quickstart, team sync, CI/CD, and the interactive TUI (generated with VHS)

---

<details>
<summary><strong>🔢 Exit Codes</strong></summary>

| Code | Meaning |
|------|---------|
| `0` | Success; partial decrypt (≥ 1 env imported); `envy diff`/`envy scan` — no differences/leaks found |
| `1` | Not found (manifest, secret, `envy.enc`, `.git`); zero envs imported; `envy diff` — differences found; `envy scan` — leak(s) found |
| `2` | Invalid input (key name, assignment format, empty or wrong passphrase) |
| `3` | Initialisation conflict; environment not found in vault or artifact; `envy hooks install` — conflict |
| `4` | Vault or crypto failure |
| `5` | `envy.enc` unreadable (malformed JSON or unsupported schema version) |
| `127` | Child binary not found (`envy run`) |
| `N` | Child process exit code (proxied exactly by `envy run`) |

Note: `envy diff` and `envy scan` follow the `diff(1)` convention — exit 1 means "differences/leaks exist", not "an error occurred". This makes it safe to use in shell pipelines with `||` without masking real errors.

</details>

---

## Installation

**Homebrew (macOS & Linux)**

```bash
brew install anguriatech/tap/envy
```

**NPM (Cross-platform wrapper)**

```bash
npm install -g @anguriatech/envy
# or run without installing:
npx @anguriatech/envy
```

**Cargo (Rust toolchain)**

```bash
cargo install envy-secrets
# installs the `envy` command
```

**macOS & Linux (shell installer)**

```bash
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/anguriatech/envy/releases/latest/download/envy-installer.sh | sh
```

**Windows (PowerShell)**

```powershell
irm https://github.com/anguriatech/envy/releases/latest/download/envy-installer.ps1 | iex
```

**Build from source** (requires Rust 1.85+)

```bash
git clone https://github.com/anguriatech/envy.git
cd envy && cargo install --path .
```

---

## Roadmap

Envy has completed **Phase 1** (encrypted local vault), **Phase 2** (GitOps sync & CI/CD), **Phase 2.x** (multi-env encrypt, output formats, sync status, pre-encrypt diff), and **Phase 2.y** (rotation reminders, passphrase strength hints, local audit trail, vault-leak scanner, pre-commit hook).

**Phase 3 — Ecosystem & GUI**: An official VS Code Extension to make secret management visual and seamless, without leaving the editor.

---

<div align="center">

Built with Rust, SQLCipher, AES-256-GCM, and Argon2id by [Anguria Tech](https://github.com/anguriatech).
MIT License — audit the code, fork it, ship it.

</div>