Share `.env` files as an encrypted `lazysecrets://share/…` link. The files are encrypted with a secret's value, so anyone who can read that secret can open the link. **Sharing:** `S` → `f` on a repo (all its plain env files), an env file, or a key in one (its file). - **The key** is `share.key` from the repo's `.lazysecrets.yaml`, else from the config. It can be a `lazysecrets://` link, a Key Vault URL or an `@Microsoft.KeyVault(...)` reference. - **Without `share.key`,** a picker built on Go to opens, titled "Share .env", with "Pick a secret to encrypt with" in its own border. It lists only secrets, leaving out values from plain `.env` files. Pasting a reference picks it. Picking shares at once, and the picker stays open until the link is copied. If the secret can't be used, the reason shows in red in the picker: it can't be read, or it doesn't meet `share.keyRules` (which rule failed is named). `esc` cancels. - **`share.keyRules`** (defaults `minLength: 32`, `minClasses: 2`; never under 16) applies when sharing, not when opening. - **On Azure,** the link pins the key's version, so rotating the key doesn't break old links, and disabling a version revokes them. - **The command line:** `lazysecrets share [--key <secret>] <env files>`. **Opening** works by paste, `ctrl+o`, the clipboard offer, the link handler or `lazysecrets <link>`. 1. It first says which repo the link is from, how many files it holds and which key it uses, and decrypts only after `o`. 2. It saves into the repo's clone, found by its remote, showing which files are new, which would replace one there, and which git doesn't ignore. 3. Without a clone, it asks for a folder and saves straight away. The prompt stays open, with the reason in red, until the files are written. 4. If decryption fails, a popup says why: the first line of the reason, `r` for the whole message, `g` to go to the key's secret. A key that has changed is named by where it lives: a Databricks or SOPS value changed in place, an Azure current version that isn't the one the link was made with, or a pinned version, meaning the link may be damaged. **Format** (`internal/share`): a version byte, then a JSON header with the key, the repo and the file count, then an age file (scrypt 2^15, with the key's value as passphrase) holding the files as deflated JSON, all as unpadded base64url. Links are capped at 16 KB. A future format gets a new version, and old ones stay readable. **Safety:** - A link can only write env files, inside the folder: never in `.git`, never through a symlink. - New files are created with mode 0600. - Opening accepts an scrypt work factor of at most 2^20, and unpacks at most 1 MB. **Along the way:** - Opening a link to a secret ends on its details, as `enter` would. - `o` on an Azure secret opens its portal page, and in the versions view, that version's page. - The details pane follows its secret's list when it reloads in the background. Before, it kept what it showed first, including an old portal address. - `ref.Read` is shared by `get` and sharing, with Azure `VersionedValue` for pinning. - `provider.Contents` lets the demo repos share their env files. `--demo` has a share key (`kv-shared-prod / env-share`), so the whole flow works there. - New dependency: `filippo.io/age`. Tested end to end in the TUI and the demo, the CLI, and the `share` package. In a real vault: version pinning, opening into a clone, and revocation by disabling the version. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #54
4.3 KiB
SOPS and .env files in git repos
lazysecrets shows git repositories under Repos: their
SOPS-encrypted files (YAML, JSON,
.env, INI) and plain env files as vaults, and the keys in them
(db.password, list[0]) as secrets.
Which repos
Repos lists every repo with secret files under the roots (~/git
by default; one or two levels down, repo or org/repo), any listed in
paths, and any you starred something in (the repo, a file or a key),
wherever it is. They're checked in the background.
Started inside a repo, that repo gets a row of its own at the top,
Repo · <name>, and lazysecrets opens on it, with the files under the
directory you started in first. In the flat list (`) it sorts first, marked
· here.
A repo without secret files isn't listed. --dir <path> starts somewhere
else, for example in one project of a monorepo.
Repos load themselves as they're found, files and keys, since they're local and cheap: Go to searches them without loading anything by hand.
Which files
Env files: .env, .env.local, prod.env and the like, also when
git-ignored (as they usually are), in the folders git tracks files in;
not .env.example, .sample, .template and the like. They're read
directly; a file with SOPS metadata is always treated as SOPS, whatever
its name.
Folders of dependencies and caches are never looked in, tracked or not:
node_modules, vendor, .venv, venv, __pycache__, .terraform,
.gradle, .yarn, .next and the like.
SOPS files: those tracked by git, then the first of
include/excludein the repo's own.lazysecrets.yaml(committed, so everyone sees the same files),include/excludein the config,- where the repo's
.sops.yamlcreation_rulessay SOPS files go, - the whole repo.
include/exclude apply to env files too.
providers:
repos:
roots: [~/git] # folders of repos (the default; [] turns it off)
paths: [~/work/infra] # single repos elsewhere
include: ["secrets/**", "**/*.sops.yaml"] # globs from the repo root; ** for any depth
exclude: ["**/testdata/**"]
A repo's .lazysecrets.yaml can also name the secret its env files are
shared with:
share:
key: https://kv-team.vault.azure.net/secrets/env-share # or its lazysecrets:// link
Decrypting
Listing never decrypts: SOPS encrypts values only, so keys and metadata
(encryption, last modified) are read straight from the files. Revealing,
copying or lazysecrets get decrypts one value with your sops binary, so
whatever sops uses works (age keys, Azure Key Vault through az, AWS/GCP
KMS, PGP). When sops can't decrypt, its whole error message is shown.
On a file, the right pane shows how it's encrypted and the keys it's
encrypted for (public: age recipients, Key Vault keys, KMS ARNs, PGP
fingerprints), when it was last encrypted, and what git knows of it:
tracked, ignored or neither, and its last commit. A plain .env file that
git tracks, or that isn't ignored, is marked in red: its values are in the
repo's history, or easily could be.
Opening files
e opens the selected file, or on a secret, its file at that key's line:
- SOPS files open with
sops edit, which decrypts them into your editor ($SOPS_EDITORor$EDITOR) and encrypts them again when you save. lazysecrets comes back after, with the file's keys reloaded. - Other files, when lazysecrets runs inside nvim (its plugin, or
:terminal): lazysecrets quits and the file opens in that nvim, in the window you came from. - Otherwise in
$VISUALor$EDITORin the terminal, coming back after; without either, in the system's default app for the file.
Editors that take a line (vim, nvim, nano, emacs, micro, helix, VS Code,
Zed, Sublime and others) start on the key. With sops edit that works for
editors taking +N (vim, nvim, nano, emacs, micro and others), and needs
sops 3.8 or later.
Links
S → f shares env files as an encrypted link.
Links name the repo by its git remote, so they open in a colleague's clone wherever it is:
lazysecrets://repo/git.example.com%2Fteam%2Finfra/secrets%2Fapp.yaml/db.password
See links.