logicsrc/docs/opencreds/cli.md
Anthony Ettinger 3d155af970
vault + teams: filter secrets by category, export to CSV, simpler help (#195)
Every secret now has a category derived from its name (db, social, server,
api, cloud, finance, crypto, ai, email, messaging, storage, dns, analytics,
devtools, auth, config, other). One rule table in @logicsrc/opencreds serves
both vaults; services win over generic words, so STRIPE_WEBHOOK_SECRET is
finance, not auth. Checked against the 1,208 distinct key names in the
profullstack team: 74 fall to "other".

Team vaults (where the shared .env secrets live):
- teams categories [team]    the filter words, with per-category counts
- teams secrets <team> [project] [env] --category/-c --search/-s
                             names + categories, never decrypts; --format csv
- teams export  <team> [project] [env] --category -o file.csv [--yes]
                             decrypts into team,project,env,category,key,
                             value,updated_at (0600); skips vaults without a
                             grant and names them

Personal vault (OpenCreds):
- vault list --category, and the category column in list output
- vault export --format csv: one flat row per item, keeps key/account
  secrets that a Bitwarden CSV drops; --category on every export format

DX:
- examples in `logicsrc vault help` / -h / --help that start by saying which
  of the two vaults you want, plus examples on teams and each subcommand
- password prompts go to stderr, so eval "$(logicsrc vault unlock)" works
- hints name the command you actually ran (logicsrc vault init, not
  opencreds init)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 21:08:08 -07:00

171 lines
6.7 KiB
Markdown

# The OpenCreds CLI
The CLI is part of the conformance surface: flags, output shapes and exit codes
are specified, not incidental. The same commands ship twice — as
`logicsrc vault …` and as the standalone `opencreds` binary — from one
implementation, so the two can never drift.
## Exit codes
| Code | Meaning |
| --- | --- |
| 0 | Success. |
| 1 | Usage error — unknown flag, missing argument, unreadable file. |
| 2 | Validation failure — a document did not conform. |
| 3 | Crypto failure — wrong password, failed tag, manifest mismatch. |
| 4 | Refused — the operation needs a confirmation that was not given. |
## Vault
```bash
opencreds init [--namespace opencreds] [--iterations 600000] [--password-stdin]
```
Creates a vault. Prompts for a master password twice, prints a recovery key
once, and never prints it again. Refuses if a vault already exists at the target
unless `--force`. `--password-stdin` reads one line instead and skips the
confirmation — for scripted provisioning, where there is nobody to mistype.
```bash
opencreds unlock # prints a session token to export
opencreds unlock --persist [--timeout 15]
opencreds lock # drops a persisted session
opencreds status # vault present? locked? counts by type
```
Unlocking has two shapes, and the difference is a flag rather than a default
because it is a real trade:
- **Token (default).** `unlock` prints `export OPENCREDS_SESSION="…"`. Nothing
touches disk and the session dies with the shell.
- **Persisted (`--persist`).** The same token in a 0600 file with an expiry, so
a script can unlock once and run many commands. A readable user key on disk is
the vault; the command says so when you use it, and `lock` removes it.
`status` is the one command that works locked. It reports counts and never
values, because counts are already observable to whoever holds the storage.
```bash
opencreds recover # unlock with the recovery key, set a new password
```
A password change re-wraps the user key. Not one item is re-encrypted, which is
why it is instant on a vault of any size.
## Items
```bash
opencreds add <type> --name <name> [type flags…]
opencreds list [--type <type>] [--category <names>] [--folder <name>] [--search <text>] [--json]
opencreds get <id|name> [--field <path>] [--reveal]
opencreds edit <id|name> [flags…]
opencreds rm <id|name> [--purge]
opencreds restore <id>
```
`list` and `get` MUST NOT print secret values by default. `get` prints the item
with every secret field masked; `--reveal` prints one field named by `--field`,
so revealing is always a deliberate act naming a single value. `--json` output is
masked identically — a pipeline is not an authorization.
`list` prints one line per item: short id, type, category, name. The category
(`db`, `social`, `server`, `api`, `finance`, … and `other`) is derived from the
item type, name, account provider and login hosts, and is never stored, so it
cannot disagree between implementations that share the rule table.
`--category db,social` filters on it and accepts aliases (`database`,
`payments`); an unknown word exits 1 and names the valid ones.
Type flags follow the field group names, kebab-cased:
`--username`, `--password`, `--totp`, `--url`,
`--cardholder-name`, `--number`, `--exp-month`, `--exp-year`, `--code`,
`--first-name`, `--last-name`, `--email`, `--phone`, `--address1`, …,
`--key-type`, `--algorithm`, `--public-key`, `--private-key`, `--file`, `--path`, `--mode`,
`--provider`, `--account-id`, `--handle`, `--access-token`, `--refresh-token`, `--scope`.
`--password -` and every other secret flag read from stdin when given `-`, so a
secret need not appear in the shell history or the process list.
## Database
```bash
opencreds export [--out vault.opencreds] [--passphrase-stdin]
opencreds export --plaintext --out vault.json --yes
opencreds export --format csv --out vault.csv --yes
opencreds export --format bitwarden-csv --out vault.csv --yes
opencreds export --format csv --category db --out db.csv --yes
opencreds import <file> [--dry-run] [--merge skip|replace|duplicate]
opencreds import <file> --source bitwarden|onepassword|chrome|lastpass|keepass
```
`export` writes the encrypted form. `--plaintext` prints what it is about to do
and exits 4 without `--yes`.
`--format csv` writes one flat row per item —
`folder,category,type,name,username,password,url,value,totp,notes` — where
`value` is the single opaque secret of a key, account or card. It keeps the key
and account items a Bitwarden CSV has no column for, and is meant for people and
scripts, not re-import. `--format bitwarden-csv` is the one to hand another
password manager. `--category` limits any export format to those categories.
`import` with `--dry-run` reports counts by type, folders to be created,
duplicates detected and rows that could not be mapped, and writes nothing. A
manifest mismatch exits 3 and writes nothing regardless of flags.
Output of a dry run:
```
opencreds import vault.opencreds --dry-run
Source vault.opencreds (opencreds 0.1, encrypted, namespace opencreds)
Exported 2026-08-29T18:00:00.000Z by @logicsrc/opencreds 0.1.0
Manifest verified — 42 items, 3 folders
login 38 2 already present (skip)
card 2
key 1
account 1
Folders Work, Personal (new), Archive
Skipped 0
Nothing written. Re-run without --dry-run to import.
```
## Validation
```bash
opencreds validate <file> # a database, or a plaintext item document
opencreds validate --stdin
```
Exits 0 when the document conforms, 2 when it does not, and prints one diagnostic
per failure with a JSON pointer into the document:
```
/items/17/login/uris/0/match "fuzzy" is not a valid match rule
/manifest/itemCount says 42, payload has 41
```
## Conformance
```bash
opencreds conformance # a table, one row per requirement
opencreds conformance --json # the report, for CI
opencreds conformance --emit-fixtures <dir> # generate the fixture set
```
Runs the suite against this implementation and reports each requirement as pass,
fail or skip. Exits 2 when a MUST does not pass, so it can gate CI directly. An
implementation claiming conformance SHOULD run it there.
`--emit-fixtures` writes the generated fixture set, so another implementation
can be tested against exactly what this one produces and accepts. See
[conformance.md](./conformance.md).
## What the CLI never does
- It never prints a secret value except through `get --reveal --field`.
- It never writes a plaintext file without an explicit flag and a confirmation.
- It never sends anything anywhere. There is no telemetry, no account, and no
network call in any command listed on this page.