CLI

Daily use

The CLI works mostly from local state. fd0 sync is the explicit network command; the agent can also sync after unlock when on_unlock = true.

One grammar for every module

Six modules hold items: secret, pass, ssh, key, kube, and talos. They take the same verbs, so learning one teaches the rest.

$ fd0 <module> add <name> ...              # create; refuses an existing name
$ fd0 <module> edit <name> --flag value    # change only the fields you name
$ fd0 <module> show <name>                 # human-readable, secrets masked
$ fd0 <module> list                        # alias: ls
$ fd0 <module> rename <name> <new-name>
$ fd0 <module> move <name> --to-scope <scope>
$ fd0 <module> rm <name>                   # tombstone
$ fd0 <module> history show <name>         # versions, newest first
$ fd0 <module> history restore <name> <seq>

list takes --json on every module, with the same key names throughout and [] rather than null when empty. Secret material is never in that output — fd0 key list --json carries the fingerprint and the authorized_keys line, never the private half.

edit patches; add --force replaces. An edit leaves every field you did not name alone, and passing an empty value clears exactly that one field. A forced add rewrites the whole record, so anything the command did not carry returns to its default. An edit that changes nothing writes nothing and does not burn a revision.

Restore writes forward. history restore appends a new version carrying the old content instead of rewinding the chain, so the history stays append-only and the undo is itself auditable.

Module-specific commands sit alongside the shared verbs, not instead of them: pass field/notes/totp/file, ssh connect/tag/enable, kube sync, talos secrets.

Plain secrets

A plain secret is a name and a value. Everything richer belongs to one of the other modules.

fd0 secret set <NAME> <value> [--scope <scope>]

Store a string secret in a scope. Use - as the value to read it from stdin. Without --scope, fd0 uses the only live scope, asks interactively, or requires --scope in non-interactive use.

fd0 secret get [<NAME>] [--raw] [--scope <scope>]

Print a secret. Without a name, fd0 opens the interactive picker. --raw drops the trailing newline.

fd0 secret copy <NAME> [--clear-after=30s]

Copy a secret to the clipboard and clear it after the timeout.

fd0 secret list [--json] [--all]

List plain secret names across scopes. --all also lists records owned by other modules — hosts, keys, pass items, clusters. Values stay encrypted until you request one.

fd0 secret rm <NAME> [--scope <scope>]

Write a tombstone for a secret. The old event remains audit history.

Each module owns its own records, and the secret commands say so rather than acting on someone else's:

$ fd0 secret rm host:prod
✗ "host:prod" is a host, not a plain secret
  use: fd0 ssh rm prod
The older top-level spellings — fd0 get, fd0 set, fd0 rm, fd0 ls, and fd0 copy — still work and will keep working. They no longer appear in --help. Prefer the fd0 secret form in anything you write down.

Item history

Every module keeps earlier versions of its items. The listing gives you the sequence numbers restore takes.

$ fd0 secret history show DEPLOY_KEY
$ fd0 secret history restore DEPLOY_KEY 4
$ fd0 ssh history show prod-db --json

Scopes and sharing

A scope is the sharing boundary. Add a member to share every current secret in the scope. Remove a member to rotate the scope key for future writes.

# Alice exports her card.
$ fd0 card export

# Bob imports Alice, Alice imports Bob, then Alice grants access.
$ fd0 card import "fd0://card/..." --label bob
$ fd0 scope add-member bob --scope work
$ fd0 sync

# Bob discovers the scope on his next sync.
$ fd0 sync
$ fd0 secret list
fd0 card export

Print your signed card and safety number. Share the card over any channel; verify the safety number over a trusted channel.

fd0 card import <fd0://card/...> --label <name>

Pin another identity under a local label.

fd0 scope add-member <label> --scope <scope>

Grant a pinned card access to the scope.

fd0 scope remove-member <label> --scope <scope>

Remove access and rotate the scope key.

Unlock methods

Auth methods are stored in the vault. The default unlock method is a local device preference in ~/.fd0/config.toml; it is not synced to other machines. When several methods are available and no default or --method override is set, an interactive unlock asks which method to use.

fd0 auth ls

List enrolled unlock methods. The current session is marked with *, and the local default is marked with default.

fd0 auth default

Show the default unlock method for this device.

fd0 auth default yubikey

Use YubiKey unlock by default on this device. Use passphrase or a method_id instead when needed.

fd0 auth default --clear

Clear the local default. Interactive unlocks ask which enrolled method to use; non-interactive commands retain a deterministic fallback.

Local health

fd0 status

Show whether the agent is running and whether the vault is unlocked.

fd0 doctor

Replay local chains, check vault tips, auth wraps, scope keys, YubiKey flavor state, orphan chain files, and SSH socket health.

fd0 lock

Lock the vault in the running agent and zeroize in-memory keys.

fd0 agent status

Show fd0-agent process, vault, agent socket, and SSH socket state.

fd0 agent restart

Replace fd0-agent with the current binary and repair stale agent sockets.

fd0 agent stop

Stop fd0-agent and clean stale sockets when safe.

If a command needs an unlocked vault in an interactive terminal, fd0 prompts for the passphrase instead of failing immediately.