Vous devez manuellement charger vos secrets avant chaque commande, un source .env, un export VAULT_TOKEN=..., un eval $(op signin), et vous finissez par tout mettre dans .zshrc par paresse. Lade (prononcé /leɪd/) est une CLI open source qui injecte automatiquement les secrets dans vos processus grâce à des hooks shell transparents. Quand vous entrez dans un répertoire projet, Lade charge les secrets. Quand la commande se termine, il les nettoie.
Ce guide vous apprend à installer Lade, configurer les sources de secrets, utiliser les hooks shell automatiques et personnaliser l'injection par commande et par utilisateur. Il s'adresse aux développeurs qui veulent que leurs secrets soient toujours disponibles sans jamais être exposés.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Ce qu'est Lade et comment fonctionnent les hooks shell
- Comment configurer un fichier
lade.ymlavec le raw loader, le file loader et les vault loaders - Comment filtrer les secrets par commande grâce aux regex
- Comment gérer les secrets par utilisateur dans une équipe
- Comment générer des fichiers de secrets temporaires (YAML, JSON) nettoyés automatiquement
- L'injection manuelle avec
lade injectpour les scripts et la CI
Qu'est-ce que Lade ?
Section intitulée « Qu'est-ce que Lade ? »Lade est une CLI cross-platform (Linux, macOS) développé en Rust. Il intercepte chaque commande que vous tapez dans le shell grâce à des hooks (preexec/precmd) et injecte les secrets correspondants dans les variables d'environnement du processus, puis les supprime quand la commande se termine.
Le principe repose sur la durée de vie des secrets. Un fichier .env persiste sur le disque entre les sessions, survit aux redémarrages et finit régulièrement dans un dépôt Git par inadvertance. Lade charge les valeurs depuis leur coffre d'origine au lancement de la commande, les expose dans l'environnement du processus, puis disparaît avec lui : les secrets n'existent que pendant l'exécution, et rien n'est écrit sur le disque.
| Aspect | Détail |
|---|---|
| Objectif | Injecter les secrets automatiquement via des hooks shell, les nettoyer après exécution |
| Approche | Hooks shell (preexec/precmd) + injection par regex de commande |
| Loaders | Raw (valeurs en clair), File (JSON/YAML/TOML/INI), Vault, Infisical, 1Password, Doppler, Passbolt |
| Filtrage | Par regex de commande (ex: terraform.*, python.*, .* pour tout) |
| Per-user | Secrets différents selon l'utilisateur (équipe) |
| Sortie fichier | Génération de fichiers YAML/JSON temporaires, supprimés après exécution |
| Licence | Apache-2.0 |
| Langage | Rust |
Comment fonctionnent les hooks ?
Section intitulée « Comment fonctionnent les hooks ? »Lade utilise les mécanismes natifs de votre shell pour intercepter chaque commande :
-
Avant l'exécution (preexec) : Lade remonte l'arborescence de répertoires à la recherche de fichiers
lade.yml, agrège les secrets qui correspondent à la commande que vous êtes sur le point d'exécuter, et les injecte dans les variables d'environnement. -
Après l'exécution (precmd) : Lade supprime toutes les variables d'environnement qu'il a injectées, les secrets ne persistent jamais dans la session shell.
Ce mécanisme fonctionne avec Zsh, Bash et Fish.
Prérequis
Section intitulée « Prérequis »- Un système Linux ou macOS
- Un shell compatible : Zsh, Bash ou Fish
- Rust/Cargo pour l'installation via Cargo (recommandé)
- Optionnel : un vault (HashiCorp Vault, 1Password CLI, Infisical, Doppler, Passbolt)
Installation
Section intitulée « Installation »-
Installer Lade
Fenêtre de terminal cargo install lade --lockedL'option
--lockedimpose leCargo.lockpublié par le projet : les dépendances compilées sont exactement celles que l'auteur a testées, et non les dernières versions disponibles au moment de votre build. C'est la différence entre une compilation reproductible et une compilation qui peut embarquer une dépendance publiée entre-temps.Le
READMEamont propose aussi uncurl ... | bashsurinstaller.sh: ne l'utilisez pas, il exécute du code distant sans aucune vérification d'intégrité. Si vous préférez un binaire pré-compilé à la compilation Rust, les pages de release GitHub publient depuis la v0.15.3 une archive et son fichier.sha256par plateforme, à contrôler avecsha256sum --checkavant extraction. -
Installer les hooks shell
Cette commande ajoute les hooks dans votre profil shell (
.zshrc,.bashrcouconfig.fish) :Fenêtre de terminal lade install -
Redémarrer votre shell
Fenêtre de terminal exec $SHELL -
Vérifier l'installation
Fenêtre de terminal lade --versionRésultat attendu :
lade 0.13.0
Configurer le premier lade.yml
Section intitulée « Configurer le premier lade.yml »Le fichier lade.yml se place à la racine de votre projet. Sa structure est simple : une regex de commande comme clé, et les secrets comme valeurs.
Structure de base
Section intitulée « Structure de base ».*: DB_HOST: localhost DB_PORT: "5432" DB_USER: devuser DB_PASSWORD: s3cr3t_p4ssw0rd API_KEY: sk-test-1234567890abcdefLa clé .* est une regex qui matche toutes les commandes. Chaque variable est injectée dans l'environnement de chaque commande exécutée dans ce répertoire (ou ses sous-répertoires).
Filtrer par commande
Section intitulée « Filtrer par commande »La puissance de Lade réside dans le filtrage par regex de commande. Vous pouvez injecter des secrets différents selon l'outil utilisé :
# Secrets communs à toutes les commandes.*: DB_HOST: localhost DB_PORT: "5432" DB_USER: devuser
# Secrets pour Terraform uniquementterraform.*: TF_VAR_db_password: s3cr3t_terraform AWS_REGION: eu-west-1 AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE
# Secrets pour Python/Node uniquementpython.*|node.*: API_KEY: sk-test-1234567890abcdef DB_PASSWORD: s3cr3t_p4ssw0rdAvec cette configuration :
terraform planreçoitDB_HOST,DB_PORT,DB_USER,TF_VAR_db_password,AWS_REGIONetAWS_ACCESS_KEY_IDpython3 app.pyreçoitDB_HOST,DB_PORT,DB_USER,API_KEYetDB_PASSWORDlsreçoit uniquementDB_HOST,DB_PORTetDB_USER
Cette approche limite l'exposition : Terraform n'a pas accès à l'API key, et Python n'a pas accès aux credentials AWS.
Les loaders
Section intitulée « Les loaders »Lade supporte 7 loaders pour récupérer les secrets depuis différentes sources.
Raw loader (valeurs en clair)
Section intitulée « Raw loader (valeurs en clair) »Le loader par défaut injecte les valeurs telles quelles. Préfixer une valeur par ! force l'utilisation du raw loader (utile pour les valeurs qui commencent par un protocole comme vault://) :
.*: SIMPLE_VALUE: hello_world ESCAPED_VALUE: "!vault://this-is-not-a-vault-ref"File loader
Section intitulée « File loader »Le file loader lit les secrets depuis un fichier JSON, YAML, TOML ou INI local. Le paramètre query utilise la syntaxe JMESPath pour extraire des champs spécifiques :
.*: DB_HOST: "file://secrets.json?query=.database.host" DB_PORT: "file://secrets.json?query=.database.port" DB_PASSWORD: "file://secrets.json?query=.database.password"Avec le fichier secrets.json :
{ "database": { "host": "db.prod.example.com", "port": 5432, "password": "pr0d_p4ss" }}Le chemin peut être relatif au répertoire du lade.yml, commencer par ~ ou $HOME, ou être absolu.
Vault loader (HashiCorp Vault)
Section intitulée « Vault loader (HashiCorp Vault) »C'est le loader le plus utilisé en entreprise, et le seul point d'attention est
qu'il ne parle pas directement à l'API : Lade délègue au CLI vault installé
sur votre poste. Concrètement, votre token Vault reste géré par vault login
et son fichier ~/.vault-token, Lade ne le stocke jamais. Corollaire : si votre
token a expiré, Lade échouera sans message explicite côté Vault.
.*: DB_PASSWORD: "vault://vault.company.com/secret/myapp/DB_PASSWORD" API_KEY: "vault://vault.company.com/secret/myapp/API_KEY"Le format est vault://DOMAINE/MOUNT/CLE/CHAMP. L'authentification utilise le CLI vault local (vous devez être connecté via vault login).
Infisical loader
Section intitulée « Infisical loader »Le piège de ce loader tient au domaine, qui s'écrit sans le suffixe /api :
Lade l'ajoute lui-même au moment de l'appel. Le second champ n'est pas le nom
lisible du projet mais son identifiant technique, celui qui apparaît dans
l'URL de l'interface web.
.*: DB_PASSWORD: "infisical://app.infisical.com/PROJECT_ID/production/DB_PASSWORD"Le format est infisical://DOMAINE/PROJECT_ID/ENVIRONNEMENT/NOM_SECRET. Lade ajoute automatiquement /api au domaine. L'authentification utilise le CLI infisical local.
1Password loader
Section intitulée « 1Password loader »Le domaine de l'URI correspond au compte 1Password, pas au serveur : il
s'écrit sous la forme my.1password.eu ou my.1password.com selon la région
d'hébergement de votre organisation. Se tromper de suffixe produit une erreur
d'authentification et non une erreur de secret introuvable, ce qui rend le
diagnostic déroutant.
.*: DB_PASSWORD: "op://my.1password.eu/DevVault/Database/password" API_KEY: "op://my.1password.eu/DevVault/APIKeys/production"Le format est op://DOMAINE/VAULT/ITEM/CHAMP. L'authentification utilise soit une session op active, soit un OP_SERVICE_ACCOUNT_TOKEN (pour la CI).
Pour utiliser un token de service stocké dans un autre vault (lookup récursif) :
.*: .: 1password_service_account: "vault://vault.company.com/secret/1password/service-token" SECRET: "op://my.1password.eu/DevVault/Item/field"Doppler loader
Section intitulée « Doppler loader »Doppler organise ses secrets en projet puis environnement (dev,
staging, production), et l'URI reprend exactement cette hiérarchie. Comme
pour Vault, l'accès passe par le CLI doppler authentifié localement : Lade
ne gère aucun jeton propre.
.*: DB_PASSWORD: "doppler://api.doppler.com/my-project/production/DB_PASSWORD"Passbolt loader
Section intitulée « Passbolt loader »Passbolt se distingue des autres coffres par son adressage : on ne référence pas
un secret par son nom mais par un identifiant de ressource au format UUID,
qu'il faut relever dans l'interface web. Le dernier segment désigne le champ à
extraire de la ressource, le plus souvent password.
.*: DB_PASSWORD: "passbolt://passbolt.company.com/RESOURCE_ID/password"Tableau récapitulatif des loaders
Section intitulée « Tableau récapitulatif des loaders »La colonne à regarder en premier est « CLI requis » : elle conditionne ce que vous devez installer sur chaque poste de développement et dans chaque runner de CI. Seuls le raw loader et le file loader fonctionnent sans dépendance externe, ce qui en fait les deux seules options utilisables dans un conteneur minimal.
| Loader | URI | CLI requis | Authentification |
|---|---|---|---|
| Raw | Valeur directe | Non | Aucune |
| File | file://chemin?query=.champ | Non | Aucune |
| Vault | vault://domaine/mount/clé/champ | vault | vault login |
| Infisical | infisical://domaine/projet/env/secret | infisical | infisical login |
| 1Password | op://domaine/vault/item/champ | op | Session ou service account |
| Doppler | doppler://domaine/projet/env/secret | doppler | doppler login |
| Passbolt | passbolt://domaine/resource_id/champ | passbolt | CLI Passbolt authentifié |
Injection manuelle avec lade inject
Section intitulée « Injection manuelle avec lade inject »Pour les scripts non-interactifs et les pipelines CI, utilisez lade inject :
# Injecter les secrets et lancer une commandelade inject -- python3 app.py
# Équivalent à "teller run" pour les utilisateurs de Tellerlade inject -- npm start
# Avec un chemin absolu si nécessairelade inject -- /usr/bin/python3 app.pyRésultat :
Lade loaded: DB_PASSWORD, API_KEY, DB_HOST, DB_PORT, DB_USER.Connexion à localhost:5432User: devuserAPI Key: configuréeLade affiche les noms des variables chargées (sans les valeurs) sur stderr, puis exécute la commande.
Secrets par utilisateur
Section intitulée « Secrets par utilisateur »Quand plusieurs membres d'une équipe partagent le même projet, chacun peut avoir des secrets différents. Lade résout automatiquement l'utilisateur courant :
.*: DB_HOST: localhost DB_PORT: "5432" DB_PASSWORD: alice: alice_db_password bob: bob_db_password .: default_passwordAvec cette configuration :
- bob reçoit
DB_PASSWORD=bob_db_password - alice reçoit
DB_PASSWORD=alice_db_password - Tout autre utilisateur reçoit
DB_PASSWORD=default_password - Si la clé
"."est absente, les autres utilisateurs ne reçoivent rien
Gérer l'utilisateur Lade
Section intitulée « Gérer l'utilisateur Lade »Par défaut, Lade utilise le nom du compte système. La commande lade user
permet de forcer une autre identité, ce qui sert dans deux cas concrets :
vérifier qu'un collègue recevra bien les bons secrets sans emprunter son poste,
et faire correspondre un runner de CI (dont le compte s'appelle souvent root
ou runner) à une entrée nommée ci dans le lade.yml. Le réglage est
persistant : il survit à la fermeture du shell, d'où l'importance de
lade user --reset une fois le test terminé.
# Voir l'utilisateur courantlade user
# Changer d'utilisateur (utile pour tester)lade user alice
# Revenir à l'utilisateur OSlade user --resetSortie en fichier
Section intitulée « Sortie en fichier »Par défaut, Lade injecte les secrets en variables d'environnement. Vous pouvez aussi les écrire dans un fichier temporaire (YAML ou JSON) qui sera automatiquement supprimé après l'exécution de la commande :
.*: .: file: secrets.yml DB_HOST: localhost DB_PORT: "5432" DB_PASSWORD: s3cr3t_p4ssw0rdQuand vous exécutez une commande, Lade :
- Crée le fichier
secrets.ymlavec les secrets - Exécute la commande
- Supprime le fichier
secrets.yml
Le format du fichier est déterminé par l'extension (.yml/.yaml pour YAML, .json pour JSON).
Hooks shell : activer et désactiver
Section intitulée « Hooks shell : activer et désactiver »Les hooks sont gérés par les commandes on et off :
# Désactiver les hooks (temporaire, session courante)eval "$(lade off)"
# Réactiver les hookseval "$(lade on)"
# Installer les hooks (permanent, modifie .zshrc/.bashrc)lade install
# Désinstaller les hooks (permanent)lade uninstallLa commande eval "$(lade off)" est utile pour déboguer : si une commande échoue de façon inattendue, désactivez les hooks pour vérifier si Lade est en cause.
Intégration CI/CD
Section intitulée « Intégration CI/CD »GitLab CI
Section intitulée « GitLab CI »Deux choses coûtent du temps dans un pipeline GitLab : la compilation Rust de
Lade à chaque job, et l'authentification du CLI de votre coffre. Pour la
première, mettez en cache ~/.cargo ou construisez une image de base contenant
déjà le binaire. Pour la seconde, le VAULT_TOKEN doit être une variable
masquée et protégée du projet, jamais une valeur en clair dans le
.gitlab-ci.yml.
deploy: stage: deploy image: rust:1.78-slim before_script: - cargo install lade --locked script: - lade inject -- ./deploy.sh variables: VAULT_TOKEN: $CI_VAULT_TOKENGitHub Actions
Section intitulée « GitHub Actions »Trois précautions structurent ce workflow. L'action checkout est épinglée
par SHA et non par tag, car un tag peut être redéplacé vers un autre commit.
Le bloc permissions: {} au niveau du workflow retire tous les droits par
défaut du GITHUB_TOKEN, chaque job ne redemandant que ce dont il a besoin.
Enfin persist-credentials: false empêche checkout de laisser le jeton dans
.git/config, où n'importe quelle étape suivante pourrait le relire.
name: Deployon: [push]
permissions: {}
jobs: deploy: runs-on: ubuntu-latest permissions: contents: read steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false
- name: Install Lade run: cargo install lade --locked
- name: Deploy with secrets run: lade inject -- ./deploy.sh env: VAULT_TOKEN: ${{ secrets.VAULT_TOKEN }}Comparaison Lade vs Teller
Section intitulée « Comparaison Lade vs Teller »Les deux outils répondent au même besoin mais pas au même moment de la journée.
Lisez ce tableau en gardant deux lignes en tête : le nettoyage et le
filtrage. Ce sont les seules capacités que Teller ne rattrape pas, et elles
comptent surtout sur un poste de développement où le shell reste ouvert des
heures. À l'inverse, les lignes Scan secrets et Template désignent des
usages que Lade ne couvre pas du tout : ce ne sont pas des lacunes, c'est un
périmètre différent.
| Critère | Lade | Teller |
|---|---|---|
| Injection | Hooks shell automatiques ou lade inject | teller run (toujours explicite) |
| Nettoyage | Automatique après chaque commande | Les secrets persistent dans le processus |
| Filtrage | Par regex de commande | Tous les secrets sont injectés |
| Per-user | Oui, natif | Non |
| Scan secrets | Non | Oui (teller scan) |
| Redact logs | Non | Oui (teller redact) |
| Export | Non (sortie fichier uniquement) | JSON, YAML, env, CSV |
| Template | Non | Oui (moteur Tera) |
| Providers | Vault, 1Password, Infisical, Doppler, Passbolt, File | Vault, AWS SM, AWS SSM, GCP SM, Consul, dotenv |
| Héritage config | Récursif (remonte l'arborescence) | Fichier unique .teller.yml |
En résumé : Lade est idéal pour le workflow développeur quotidien grâce aux hooks transparents et au filtrage par commande. Teller excelle pour le scanning de secrets et la manipulation programmatique (export, redact, template, copy).
Dépannage
Section intitulée « Dépannage »La quasi-totalité des incidents avec Lade se ramène à trois questions, à poser
dans cet ordre. Le binaire est-il trouvé ? Les hooks sont-ils actifs
dans ce contexte, sachant qu'ils ne se déclenchent jamais hors session
interactive ? La regex de commande correspond-elle bien à ce que vous
tapez ? Le message Lade loaded: sert de point de repère : s'il s'affiche, la
configuration est lue et le problème se situe en aval, sur la résolution d'un
loader ou sur la regex.
| Symptôme | Cause probable | Solution |
|---|---|---|
lade: command not found | Binaire pas dans le PATH | Vérifier avec which lade ou ajouter ~/.cargo/bin au PATH |
| Secrets non injectés en shell interactif | Hooks non installés | Exécuter lade install puis redémarrer le shell |
| Secrets non injectés en script | Hooks inactifs en non-interactif | Utiliser lade inject -- <commande> |
command failed avec lade inject | Commande non trouvée dans le PATH | Utiliser le chemin absolu : /usr/bin/python3 |
| Mauvais secret per-user | Utilisateur Lade différent de l'OS | Vérifier avec lade user, réinitialiser avec lade user --reset |
Conflit entre fichiers lade.yml | Héritage récursif des répertoires parents | Le lade.yml le plus proche du CWD a priorité |
vault:// non résolu | CLI vault non authentifié | Exécuter vault login avant d'utiliser Lade |
op:// non résolu | CLI op non authentifié | Exécuter eval $(op signin) ou configurer un OP_SERVICE_ACCOUNT_TOKEN |
| Fichier de sortie non créé | Bloc .: absent ou mal formaté | Vérifier que .: { file: secrets.yml } est sous le bon regex |
Error: file already exists | Un fichier du même nom traîne dans le répertoire (exécution précédente interrompue) | Supprimer le fichier avant de relancer : Lade refuse d'écraser une sortie existante |
Lade loaded: mais variables absentes | Regex de commande ne matche pas | Vérifier que le regex matche la commande (.*: pour tout matcher) |
À retenir
Section intitulée « À retenir »-
Lade injecte les secrets automatiquement grâce à des hooks shell, vous tapez simplement votre commande, sans wrapper ni préfixe.
-
Les secrets sont nettoyés après chaque commande : ils n'existent que pendant la durée de vie du processus enfant.
-
Le filtrage par regex de commande permet de n'exposer que les secrets nécessaires à chaque outil : Terraform reçoit les credentials AWS, Python reçoit les clés API, rien de plus.
-
Les secrets par utilisateur permettent à chaque membre de l'équipe d'avoir ses propres credentials tout en partageant le même
lade.ymldans Git. -
7 loaders couvrent les principaux vaults du marché : Vault, 1Password, Infisical, Doppler, Passbolt, plus un file loader et un raw loader.
-
lade injectest l'équivalent deteller runpour les environnements non-interactifs (CI/CD, scripts).