feat: lazysecrets list prints everything lazysecrets can find, for fzf and editor pickers #70

Merged
keb merged 45 commits from feat/list into main 2026-10-14 21:30:44 +02:00
4 changed files with 77 additions and 68 deletions
Showing only changes of commit ea58530068 - Show all commits

No files matched your search

+23 -11
View File
@@ -12,16 +12,12 @@ A terminal UI for finding, reading and sharing secrets. Inspired by the fantasti
CLI, `sops`, `git`). No config file needed. CLI, `sops`, `git`). No config file needed.
- **Search everything**: `ctrl+s` searches every source at once, with - **Search everything**: `ctrl+s` searches every source at once, with
fzf-style matching. fzf-style matching.
- **Share links**: Share `lazysecrets://` links with colleagues. A colleague opens it - **Share links**: Share `lazysecrets://` links with colleagues.
with their own access, from the terminal or with a click in the browser
or chat. `.env` files can be shared too, as a link encrypted with a
secret your team can read.
- **Neovim integration**: via [lazysecrets.nvim](https://github.com/karleinarb/lazysecrets.nvim) - **Neovim integration**: via [lazysecrets.nvim](https://github.com/karleinarb/lazysecrets.nvim)
- **Careful with values**: values stay hidden until you reveal them, and - **Careful with values**: values stay hidden until you reveal them, and
copied values are cleared from the clipboard after 30 seconds. copied values are cleared from the clipboard after 30 seconds.
- **Scriptable**: `lazysecrets get <link>` prints a value for scripts and - **Scriptable**: `lazysecrets get <link>` prints a value for scripts and
pipes, and `lazysecrets list` everything it can find, for fzf and editor pipes.
pickers ([integration](docs/integration.md)).
- **Favourites**: star the secrets you use most for easy access. - **Favourites**: star the secrets you use most for easy access.
- **Expiry warnings**: secrets, keys and certificates that expire soon are - **Expiry warnings**: secrets, keys and certificates that expire soon are
marked, listed together in the status pane. marked, listed together in the status pane.
@@ -81,21 +77,37 @@ use most:
| `?` | every key, searchable | | `?` | every key, searchable |
| `q` | quit | | `q` | quit |
## Integration
Lazysecret let you integrate it into your workflows by `lazysecrets` `list`/`get`/`open`
Here is an example of implementing a picker in `fzf`:
```sh
lazysecrets list | fzf --ansi --delimiter='\t' --with-nth='2..-3' --nth='2..' --accept-nth=-1 \
--header 'enter: link · ctrl-o: open · ctrl-y: copy value · ctrl-x: copy and close' \
--bind 'ctrl-o:become(lazysecrets open --action go --account {-2} {-1})' \
--bind 'ctrl-y:execute-silent(lazysecrets get --account {-2} {-1} | pbcopy)+change-header(Copied the value of {3})' \
--bind 'ctrl-x:become(lazysecrets get --account {-2} {-1} | pbcopy)'
```
![fzf-example](docs/assets/fzf-example.png)
More defails in the [Integration](docs/integration.md) docs
## Docs ## Docs
- [Keys](docs/keys.md): every key, Go to, the layout, and rebinding keys - [Keys](docs/keys.md): every key, Go to, the layout, and rebinding keys
- [Azure Key Vault](docs/azure.md): accounts, permissions, pinned vaults - [Azure Key Vault](docs/azure.md): accounts, permissions, pinned vaults
- [Databricks](docs/databricks.md): profiles and Key Vault-backed scopes - [Databricks](docs/databricks.md): profiles and Key Vault-backed scopes
- [SOPS and .env files](docs/repos.md): which repos and files, decrypting - [SOPS and .env files](docs/repos.md): which repos and files, decrypting
- [Links](docs/links.md): sharing, opening, and opening links from the - [Sharing](docs/sharing.md): links to secrets, encrypted env files, and
browser and chat opening links, also from the browser and chat
- [Integration](docs/integration.md): `lazysecrets get` in scripts, and - [Integration](docs/integration.md): how lazysecrets can integrate into other systems
`lazysecrets list` for fzf and editor pickers
- [Custom commands](docs/custom-commands.md): run your own commands on a - [Custom commands](docs/custom-commands.md): run your own commands on a
secret secret
- [Config](docs/config.md): every option, themes, layered configs, the - [Config](docs/config.md): every option, themes, layered configs, the
state file state file
- [Development](docs/development.md): building, tests, how changes land and - [Development](docs/development.md): building, tests, pull requests and
releasing releasing
## Privacy ## Privacy
Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 258 KiB

+54 -57
View File
@@ -2,7 +2,7 @@
lazysecrets works outside its own window too: `lazysecrets get` reads a lazysecrets works outside its own window too: `lazysecrets get` reads a
value for scripts, and `lazysecrets list` lists everything for fzf, editor value for scripts, and `lazysecrets list` lists everything for fzf, editor
pickers and your own tools. Both take the [links](links.md) lazysecrets pickers and your own tools. Both take the [links](sharing.md) lazysecrets
copies and shares. For Neovim, there's copies and shares. For Neovim, there's
[lazysecrets.nvim](https://github.com/karleinarb/lazysecrets.nvim). [lazysecrets.nvim](https://github.com/karleinarb/lazysecrets.nvim).
@@ -19,7 +19,8 @@ It reads the value directly, with your az accounts (the one that last read
it first), the Databricks profile for the workspace, or sops in your clone it first), the Databricks profile for the workspace, or sops in your clone
of the repo; a version in the link reads that version. `--account` tries of the repo; a version in the link reads that version. `--account` tries
that account first, as [`list`](#listing-for-fzf-and-pickers) prints it. that account first, as [`list`](#listing-for-fzf-and-pickers) prints it.
The value ends with a newline only on a terminal. The value ends with a newline only on a terminal. When it can't read the
value, it says why on stderr and exits with status 1.
## Listing, for fzf and pickers ## Listing, for fzf and pickers
@@ -29,69 +30,65 @@ tab-separated:
| Field | | | Field | |
|---|---| |---|---|
| kind | `source`, `vault`, `secret`, `key` or `certificate` | | kind | `source`, `vault`, `secret`, `key` or `certificate` |
| mark | ✗ when something's wrong (expired, or couldn't be read) or ⚠ when it expires soon; ★ when starred; ◷ when used lately (with `--recent`); and the provider's icon |
| name | `platform-prod / kv-app-prod / db-password` | | name | `platform-prod / kv-app-prod / db-password` |
| status | why a vault or source couldn't be read, or that it has `expired` or `expires in 12d` (within `ui.expiryWarningDays`), or empty |
| note | your note on it (`n` in lazysecrets), after a note icon (✎ without icons), or empty |
| account | what it's listed through: an az account (`Azure:<user>/<tenant>`), `Databricks` or `Repos` | | account | what it's listed through: an az account (`Azure:<user>/<tenant>`), `Databricks` or `Repos` |
| link | its `lazysecrets://` link | | link | its `lazysecrets://` link |
It's what Go to searches, so fzf, or an editor's picker, can search it What's cached prints at once, then what loads follows as it arrives: read
instead, showing the names and acting on the links: it as a stream. Values are never listed. It stops quietly when the reader
goes, like fzf after a pick. Kind stays the first field, and account and
link the last two; new fields only ever go between. The mark has a field of
its own so a search can leave it out: in fzf, `--nth` searches the name,
so `^prod` finds names starting with prod.
| Flag | |
|---|---|
| `--format json` | an object per line instead, for editor plugins (below) |
| `--kind secret,vault` | only these kinds: `source`, `vault`, `secret`, `key`, `certificate`; lists only other kinds need aren't loaded |
| `--cached` | only what's cached, at once: nothing is loaded from Azure or Databricks |
| `--max-age 2m` | load cached lists older than this again |
| `--errors=false` | leave out the vaults and sources it couldn't read |
| `--color never` | no colours, for scripts |
| `--icons=false` | no provider icons |
| `--stars=false` | no ★ before starred names (in `favouriteColor`) |
| `--notes=false` | no note icon before a note |
| `--recent` | ◷ before names used lately, in `mutedColor`; default `ui.recentIcon` |
| `--expiry=false` | no expiry: not in the status, and no ✗ or ⚠ for it |
With `--format json`, each line is an object with these fields; new ones
may be added:
| Field | |
|---|---|
| `link`, `kind`, `account` | as above |
| `error` | why it couldn't be read, only then |
| `provider` | `azure`, `databricks` or `repo` |
| `path` | the names: `["platform-prod", "kv-app-prod", "db-password"]` |
| `accountLabel` | the account's short name, when several az accounts are signed in |
| `starred` | starred for this account |
| `note` | your note on it, if any |
| `reference` | how config refers to a secret: `@Microsoft.KeyVault(SecretUri=…)` or `{{secrets/scope/key}}` (`Y` in lazysecrets) |
| `url` | its web page, in the Azure portal or the Databricks workspace (`o` in lazysecrets) |
| `recent` | used lately, as Recent (`R`) lists: in the last 90 days, among the 100 used last |
| `lastUsed`, `uses` | when it was last used, and how often; only when `recent`, to sort by |
| `expires` | when a secret, key or certificate expires, if it does (Azure) |
| `enabled` | `false` for a disabled one; only where the provider says (Azure) |
`lazysecrets open --action go --account <account> <link>` opens one in
lazysecrets without asking what to do or which account, ending in the
secrets list, like Go to.
### Example usage with fzf
```sh ```sh
lazysecrets list | fzf --ansi --with-nth='2..-3' --accept-nth=-1 \ lazysecrets list | fzf --ansi --delimiter='\t' --with-nth='2..-3' --nth='2..' --accept-nth=-1 \
--header 'enter: link · ctrl-o: open · ctrl-y: copy value · ctrl-x: copy and close' \ --header 'enter: link · ctrl-o: open · ctrl-y: copy value · ctrl-x: copy and close' \
--bind 'ctrl-o:become(lazysecrets open --action go --account {-2} {-1})' \ --bind 'ctrl-o:become(lazysecrets open --action go --account {-2} {-1})' \
--bind 'ctrl-y:execute-silent(lazysecrets get --account {-2} {-1} | pbcopy)+change-header(Copied the value of {2..-3})' \ --bind 'ctrl-y:execute-silent(lazysecrets get --account {-2} {-1} | pbcopy)+change-header(Copied the value of {3})' \
--bind 'ctrl-x:become(lazysecrets get --account {-2} {-1} | pbcopy)' --bind 'ctrl-x:become(lazysecrets get --account {-2} {-1} | pbcopy)'
``` ```
![fzf-example](assets/fzf-example.png)
`--with-nth='2..-3'` shows and searches only the names; `{-1}` and
`--accept-nth=-1` are the link, `{-2}` the account. All but the name are
one word, so this works without `--delimiter`. To filter by kind
yourself: `awk -F'\t' '$1 == "secret"'`.
`enter` prints the link (`lazysecrets get $(…)`), `ctrl-o` opens it in
lazysecrets, `ctrl-y` copies the value, saying so in the header, and
`ctrl-x` copies it and closes fzf (`pbcopy` on macOS; `wl-copy` or `xclip`
on Linux). A value copied this way isn't cleared from the
clipboard after `values.clipboardClear`, as one copied in lazysecrets is.
`--account` makes `open` and `get` use the account it was listed through,
when several of yours can see it: without, `open` asks which, and `get`
tries the one that last read it first. The link stays as it is, to copy
and share; the account isn't part of it. `--action` tells `open` what to
do rather than asking, as it does for a link clicked elsewhere: `go` to
it, `copy` its value too, or `show` it. It ends in the secrets list, like
Go to, rather than on the details as a clicked link does.
What's [cached](config.md#cache) prints at once. Then, like Go to, it loads
what's missing or older than `--max-age` (2m), and what's new in it follows
as it arrives: fzf shows it while you type. `--cached` loads nothing from
Azure or Databricks, for an answer at once. Repos are always read, from
disk. What it loads is cached for the TUI too. It stops when the reader
goes, after a pick in fzf.
`--kind` lists only some kinds, comma-separated: `source`, `vault` (also
Databricks scopes and repo files), `secret`, `key`, `certificate`. Lists
only other kinds need aren't loaded: `--kind source,vault` asks for no
secrets. Keys and certificates load only when asked for this way; else,
like Go to, they're listed as far as you opened them in lazysecrets.
A vault or source it can't read is a line too, as it fails: its name,
then `✗` and why in red (`errorColor`), with its link, so opening it
shows more. They're in the list, not on stderr, which would draw over fzf.
`--errors=false` leaves them out.
The name is coloured from your [theme](config.md#theme), with the
provider's icon when [icons](config.md#theme) are on (`ui.icons`); fzf
shows the colours with `--ansi`. `--color never` leaves them out, for
scripts, and `--icons=false` the icons. The link is never coloured.
`--format json` prints an object per line instead, for editor plugins,
without colours or icons, and with `error` on a list it couldn't read:
```json
{"link":"lazysecrets://azure/kv-app-prod.vault.azure.net/secrets/db-password","kind":"secret","provider":"azure","path":["platform-prod","kv-app-prod","db-password"],"account":"Azure:alice@example.com/<tenant>","accountLabel":"alice"}
```
`accountLabel`, the account's short name, is there when several az
accounts are signed in. Values are never listed.