# envchain + Fish — Guide d'usage quotidien (macOS)

> Stocker tes secrets (clés API, tokens, DB URLs…) dans le **Keychain macOS** au lieu de les
> laisser en clair dans des `.env`, `config.fish` ou variables exportées globalement.
> Principe d'envchain : **« Don't give any credentials implicitly! »** — les secrets ne sont
> chargés que quand tu lances explicitement une commande via `envchain`.

---

## 1. Concept en 30 secondes

- Un **namespace** = un coffre nommé dans le Keychain (ex. `aws`, `proj-monsite`).
- `envchain --set <namespace> VAR…` → enregistre des variables dans ce coffre.
- `envchain <namespace> <commande>` → exécute la commande avec ces variables injectées
  **dans son environnement seulement** (pas dans ton shell courant).
- macOS : chaque coffre apparaît dans le Keychain sous le label **`envchain-<namespace>`**.

⚠️ envchain fait un `exec` du programme : il **n'injecte rien dans ta session Fish courante**.
Tu dois toujours préfixer la commande cible (ou lancer un sous-shell, voir §6).

---

## 2. Installation

```fish
brew install envchain
envchain --version   # vérifie l'install
```

---

## 3. Convention de nommage des namespaces (clé de la cohabitation multi-projets)

Les namespaces sont **globaux au Keychain** (pas liés à un dossier). Une convention de nommage
stricte évite les collisions entre projets :

| Type | Préfixe | Exemple | Usage |
|------|---------|---------|-------|
| **Service partagé** | *(nom du service)* | `aws`, `openai`, `stripe`, `firecrawl` | Réutilisé par plusieurs projets |
| **Projet** | `proj-` | `proj-elevaseo`, `proj-monsite` | Secrets spécifiques à un projet |
| **Perso/throwaway** | `tmp-` | `tmp-test` | Tests jetables |

Règle simple : **un secret réutilisable → namespace de service ; un secret propre au projet → `proj-<nom>`.**
Tu peux combiner plusieurs coffres : `envchain aws,proj-monsite <cmd>`.

---

## 4. Enregistrer des secrets

Toujours utiliser **`--noecho`** pour ne pas afficher la valeur à l'écran :

```fish
# Un service partagé
envchain --set --noecho aws AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY

# Les secrets d'un projet
envchain --set --noecho proj-monsite DATABASE_URL STRIPE_SECRET_KEY NEXTAUTH_SECRET
```

Chaque variable est demandée interactivement :

```
proj-monsite.DATABASE_URL (noecho):
proj-monsite.STRIPE_SECRET_KEY (noecho):
```

**Mettre à jour une variable** : refais simplement `--set` sur la même variable → ça écrase.

**Sécurité renforcée** : pour un coffre sensible, force la saisie du mot de passe Keychain
à *chaque* utilisation (au lieu de cliquer « Toujours autoriser ») :

```fish
envchain --set --noecho --require-passphrase proj-prod-banque API_SECRET
```

---

## 5. Utiliser les secrets au quotidien

```fish
# Lancer une commande avec le coffre du projet
envchain proj-monsite npm run dev
envchain proj-monsite pnpm build
envchain aws s3cmd ls

# Combiner plusieurs coffres
envchain aws,proj-monsite node deploy.js

# Vérifier ce qu'un coffre contient (affiche les valeurs ! à faire dans un terminal privé)
envchain proj-monsite env | grep -E 'DATABASE|STRIPE'

# Lister tous les coffres existants
envchain --list
```

---

## 6. Charger les secrets dans une session interactive (le cas Fish)

Comme envchain n'injecte rien dans ton shell courant, deux options pour « travailler dans »
un projet avec ses secrets chargés :

**Option A — sous-shell Fish dédié** (recommandé, explicite et propre) :

```fish
envchain proj-monsite fish
# → nouveau shell Fish avec les secrets en env. Tape `exit` pour les « décharger ».
```

**Option B — injecter dans la session courante** (à éviter, contraire à la philosophie, mais possible) :

```fish
# charge le coffre dans le Fish courant
envchain proj-monsite env | string match -rv '^_=' | while read -l line
    set -gx (string split -m1 '=' $line)
end
```

→ Préfère l'option A : elle isole les secrets dans un sous-shell qu'on ferme.

---

## 7. Helpers Fish pour le confort (optionnel mais recommandé)

L'idée : déposer un fichier **`.envchain`** à la racine de chaque projet contenant le nom du
namespace, puis une fonction `er` (« env run ») qui le détecte automatiquement.

### 7.1 Fichier projet `.envchain`

À la racine du projet (commit-able, ne contient **aucun secret**, juste le nom du coffre) :

```
proj-monsite
```

### 7.2 Fonction de résolution — `~/.config/fish/functions/envchain-ns.fish`

```fish
function envchain-ns --description 'Trouve le namespace envchain du projet courant (.envchain en remontant)'
    set -l dir $PWD
    while test "$dir" != /
        if test -f "$dir/.envchain"
            string trim < "$dir/.envchain"
            return 0
        end
        set dir (path dirname $dir)
    end
    echo "envchain-ns: aucun fichier .envchain trouvé" >&2
    return 1
end
```

### 7.3 Fonction de lancement — `~/.config/fish/functions/er.fish`

```fish
function er --description 'Lance une commande avec le coffre envchain du projet'
    set -l ns (envchain-ns); or return 1
    envchain $ns $argv
end
```

### 7.4 Sous-shell projet — `~/.config/fish/functions/eshell.fish`

```fish
function eshell --description 'Ouvre un sous-shell Fish avec les secrets du projet'
    set -l ns (envchain-ns); or return 1
    echo "→ shell avec le coffre '$ns' (exit pour quitter)"
    envchain $ns fish
end
```

### 7.5 Abréviations rapides — dans `~/.config/fish/config.fish`

```fish
abbr -a ecs 'envchain --set --noecho'   # ecs proj-monsite VAR1 VAR2
abbr -a ecl 'envchain --list'
```

### Usage final, ultra-court

```fish
cd ~/code/monsite      # contient .envchain → "proj-monsite"
er npm run dev         # = envchain proj-monsite npm run dev
er pnpm build
eshell                 # sous-shell avec les secrets chargés
ecs proj-monsite NEW_TOKEN   # ajoute un secret
ecl                    # liste les coffres
```

> Fish charge automatiquement les fichiers de `~/.config/fish/functions/` (autoload).
> Après création, ouvre un nouveau terminal ou tape `exec fish` pour recharger les abréviations.

---

## 8. Rewrite transparent de `npm` / `pnpm` / `bun` (installé ✅)

Des fonctions Fish interceptent `npm` / `pnpm` / `bun` et **réinjectent automatiquement
via envchain** — mais **seulement** si (a) un `.envchain` est présent dans le projet **et**
(b) la sous-commande est de type script. Partout ailleurs, c'est la commande normale, inchangée.

**Sous-commandes déclencheuses** : `run`, `dev`, `build`, `start`, `test`, `serve`,
`preview`, `watch`, `stop`, `restart`.
→ `npm install`, `pnpm add`, `bun ci`… passent en **direct** (pas de hit Keychain inutile).

```fish
cd ~/code/monsite     # .envchain = "proj-monsite"
npm run dev           # → envchain proj-monsite npm run dev   (auto)
bun dev               # → envchain proj-monsite bun dev        (auto)
pnpm build            # → envchain proj-monsite pnpm build     (auto)
npm install           # → npm install normal (non déclencheur)
cd ~/sans-envchain
npm run dev           # → npm normal, le wrapper est inerte ici
```

### Le moteur — `~/.config/fish/functions/__envchain_wrap.fish`

```fish
function __envchain_wrap --description 'Exécute <cmd> via envchain si .envchain présent ET sous-commande de type script'
    set -l real $argv[1]
    set -e argv[1]
    set -l triggers run dev build start test serve preview watch stop restart
    if contains -- "$argv[1]" $triggers
        set -l ns (envchain-ns 2>/dev/null)
        if test -n "$ns"; and type -q envchain
            envchain $ns $real $argv
            return
        end
    end
    command $real $argv
end
```

Chaque gestionnaire est un mince wrapper, ex. `~/.config/fish/functions/npm.fish` :

```fish
function npm;  __envchain_wrap npm  $argv; end
```

(idem `pnpm.fish`, `bun.fish`).

### ⚠️ Cas `--require-passphrase`

Si un namespace est en `--require-passphrase`, chaque commande déclencheuse demandera le
mot de passe Keychain. Pour ces coffres sensibles, **ne mets pas de `.envchain`** dans le
projet : lance plutôt explicitement `er npm run dev`.

> Les complétions Tab de `npm`/`pnpm`/`bun` restent intactes (indexées par nom de commande).

---

## 9. Inspecter, modifier, supprimer

| Action | Commande |
|--------|----------|
| Lister les coffres | `envchain --list` |
| Voir le contenu d'un coffre | `envchain <ns> env` |
| Ajouter / écraser une variable | `envchain --set --noecho <ns> VAR` |
| **Supprimer une variable** | ⚠️ pas de `--unset` → voir ci-dessous |
| **Supprimer un coffre entier** | Keychain Access.app ou `security` (ci-dessous) |

### Supprimer (pas de flag natif sur macOS)

**Via l'app graphique** : ouvre **Keychain Access** (Trousseaux d'accès), cherche
`envchain-<namespace>`, supprime les entrées voulues.

**Via la ligne de commande** (le « service » Keychain = `envchain-<namespace>`,
le « account » = le nom de la variable) :

```fish
# Supprimer UNE variable d'un coffre
security delete-generic-password -s "envchain-proj-monsite" -a "STRIPE_SECRET_KEY"

# Lister les entrées d'un coffre pour vérifier avant suppression
security find-generic-password -s "envchain-proj-monsite"
```

> Teste d'abord avec `find-generic-password` ; `delete` est irréversible.

---

## 10. Référence des flags

| Flag | Effet |
|------|-------|
| `--set` | Mode enregistrement |
| `--noecho` | Ne pas afficher la saisie (à mettre **systématiquement** pour les secrets) |
| `--require-passphrase` | Demander le mot de passe Keychain à chaque utilisation du coffre |
| `--no-require-passphrase` | Ne plus le demander (utilise « Toujours autoriser ») |
| `--list` | Lister les namespaces existants |

---

## 11. Bonnes pratiques (récap)

1. **`--noecho` toujours** lors d'un `--set`.
2. **Une convention de nommage** : `proj-<nom>` pour les projets, nom du service pour le partagé.
3. **Un `.envchain` par projet** (committable, sans secret) + la fonction `er` → zéro friction.
4. **Plus de `.env` en clair** : supprime-les du disque une fois migrés vers envchain
   (et garde-les dans `.gitignore` au cas où).
5. **`--require-passphrase`** pour les coffres de production / haute sensibilité.
6. Pour partager la *liste* des variables attendues (sans valeurs) avec l'équipe, garde un
   `.env.example` documentaire à côté du `.envchain`.
7. Intégration CI/serveur : envchain est local-macOS ; en CI, utilise plutôt les secrets du
   provider (GitHub Actions, Vercel env, etc.). envchain = ton poste de dev uniquement.

---

### Sources
- README officiel : https://github.com/sorah/envchain
