Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ Rules for coding agents working in this repository. Engineering conventions live
change would exceed that, split the rest onto a new branch based on the one you changed
and open it as a stacked pull request.

## Work plans

Agent work plans live in `docs/plans/<work-item>/` as Markdown files. This directory is
gitignored on purpose: plans are local, disposable handoff notes for chunks of work, not
reviewed project documentation or a source of truth. Keep durable decisions and user-facing
behavior in tracked docs, code, and tests; create a subfolder per work item and revise or
remove it as work progresses.

## Agent friction log

The CLI must be easy for AI agents to use. When it is not, record why in
Expand Down
248 changes: 50 additions & 198 deletions README.md

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Voltage CLI documentation

Start with the [README](../README.md) for install and a quick start.

## Do a task

- [Install](guides/install.md): platforms, download verification, building from source, credential stores, completions
- [Log in and use profiles](guides/login-and-profiles.md): browser login, API keys, scope, profiles, `voltage context`
- [Payments](guides/payments.md): receive, send, wait, invoices, quotes, prices
- [Requests](guides/requests.md): request bodies, friendly flags, filters, paging
- [Checkout](guides/checkout.md): session and stream tokens, events
- [Scripting](guides/scripting.md): JSON, `VOLTAGE_EXECUTE`, running without a person

## Look it up

- [Command reference](commands.md): every command and its API route
- [Output](reference/output.md): tables, the JSON envelope, stdout and stderr
- [Exit codes](reference/exit-codes.md)

## Understand

- [Safety model](concepts/safety-model.md): `--execute`, dry runs, payment IDs, Ctrl-C, redaction

## For AI agents

Use `--json` and branch on exit codes. Describe a change with `--dry-run` before you send it with `--execute`. Never pass `--show-secrets`. For product concepts, see the [Voltage API docs](https://voltageapi.com/v1/docs).

## For contributors

- [Development guide](development.md): build, test, release
- [Style guide](quality.md): code conventions
11 changes: 11 additions & 0 deletions docs/assets/voltage-logo-dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions docs/assets/voltage-logo-light.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions docs/concepts/safety-model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Safety model

`voltage` moves money. You see every change before it is sent. You can trace every payment after a failure. Secrets stay out of output.

## Changes need `--execute`

Commands that change something (any method but GET) only describe their request unless you pass `--execute`, then exit with code 6. Reads run as usual. `--execute` is the only approval: nothing asks again before sending. A described change reports the same `resource_id` a real run would use, so chained commands see the same IDs in both modes. Scripts can set `VOLTAGE_EXECUTE=1` instead; see [scripting](../guides/scripting.md#send-changes-from-a-script).

## Dry runs
Comment thread
thebrandonlucas marked this conversation as resolved.

`-n, --dry-run` validates any API command and describes the request it would send, then stops: it reports the method, URL, query, `Origin` header, and body, plus a `run` object with what a real run would do next. That is the same plan a real run follows:

- A `read` says whether it checks the wallet's environment and whether it then pages, waits for a payment, or waits for a checkout session.
- A `mutation` says whether it verifies the wallet's environment, the ID it would journal for recovery, whether a failed payment submission is reconciled, and what it waits for.
- An `event_stream` follows checkout events.

A dry run authenticates nothing, makes no network request (including the wallet environment check a real mutation performs), prompts for nothing, writes no journal entry, and does not create `--output-file`. Body and metadata-filter values are replaced with `[OMITTED]` unless the field holds only identifiers, enums, or amounts; `--show-secrets` shows them. `--data -` still reads stdin, and a friendly create generates a fresh ID each run unless you pass `--id`. Passing local validation does not mean the API will accept the request.

```sh
voltage payments send --profile prod --wallet WALLET_ID --currency btc \
--invoice BOLT11_INVOICE --max-fee 10 --fee-unit sats --dry-run
voltage wallets delete WALLET_ID --profile prod -n --json
```

The result is a normal envelope with `outcome: dry_run` and no `http_status`.

## Payment IDs and the request journal

Payments and treasury movements carry an ID, generated for you unless you pass `--id`. Before sending, the CLI records the ID, operation, scope, and request hash (never the body) in a private journal under `requests/`, and it refuses to reuse an ID with a different request. If the connection drops after a submission, the CLI makes one short read of the ID: if the payment is visible, it reports acceptance; otherwise it exits with code 4 and the ID, without resubmitting. Check the ID before deciding to retry.

## Ctrl-C

Ctrl-C exits with code 130, including at the hidden API-key prompt. Before a write is sent, it reports that no resource change was submitted, or, if a login was being refreshed, created, or logged out, how to recover it. Once a write may have been sent, it reports the outcome as unknown, with the original ID and reconciliation instructions for a payment. It never cancels a submitted request.

## Secrets and redaction

Known secret fields and the credentials the CLI presented are redacted from normal output and from error details. Operations that return a one-time secret (webhook creation and key rotation, checkout sessions, stream tokens) refuse to run unless you pass `--output-file PATH`, which writes the complete response to a new owner-only file, or `--show-secrets`. Treat your own metadata as potentially sensitive; redaction cannot recognise it.

API text is stripped of terminal control, bidirectional formatting, and invisible characters (including tag characters that can carry hidden text) in tables, notices, and errors.

Related: [payments](../guides/payments.md), [exit codes](../reference/exit-codes.md).
2 changes: 1 addition & 1 deletion docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ the same way you would against production.
voltage profiles create staging --account me --org ORG_ID --env ENV_ID
voltage wallets list --profile staging
voltage payments receive --profile staging --wallet WALLET_ID \
--currency btc --kind bolt11 --amount 1000 --unit sats --qr
--currency btc --kind bolt11 --amount 1000 --unit sats --qr --execute
```

Once the profile exists, fold it into the alias to drop the flag as well:
Expand Down
12 changes: 12 additions & 0 deletions docs/guides/checkout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Checkout

Checkout commands use their own credentials and never fall back to your account. Provide a session token through `VOLTAGE_CHECKOUT_TOKEN` and a stream token through `VOLTAGE_STREAM_TOKEN`, or read either from a private file or stdin with `--token-file PATH` / `--token-file -`. Pass `--origin https://shop.example.com` when the session requires a browser origin.

```sh
voltage checkout sessions get SESSION_ID --token-file ./session.token
voltage checkout events watch --token-file - < ./stream.token
```

`checkout sessions get` retries while the session projection is being created, following the API's retry hints. `checkout events watch` streams server-sent events as `outcome: event` envelopes until the connection closes or `--timeout` passes; it does not reconnect. Ctrl-C exits cleanly.

Related: [output reference](../reference/output.md).
48 changes: 48 additions & 0 deletions docs/guides/install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Install

Download the archive for your platform from the [releases page](https://github.com/voltagecloud/voltage-cli/releases). Each release ships macOS (Apple Silicon and Intel), Linux (x86-64 and ARM64), and Windows (x86-64) archives with shell completions, a signed `SHA256SUMS` file, and a Homebrew formula (`voltage.rb`). Extract the archive and put `voltage` (or `voltage.exe`) on your `PATH`.

## Verify a download

Every archive carries a SLSA build-provenance attestation, and `SHA256SUMS` is signed with Sigstore (keyless; no keys to trust out of band). Before installing, verify both with the [GitHub CLI](https://cli.github.com) and [cosign](https://docs.sigstore.dev/cosign/system_config/installation/):

```sh
# The archive was built by this repository's release workflow from its version tag.
gh attestation verify voltage-*.tar.gz --repo voltagecloud/voltage-cli

# The checksums were signed by that same workflow run.
cosign verify-blob SHA256SUMS --bundle SHA256SUMS.sigstore.json \
--certificate-identity-regexp 'https://github.com/voltagecloud/voltage-cli/.github/workflows/release.yml' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com

# The archive matches the signed checksums.
sha256sum --check SHA256SUMS --ignore-missing
```

## Build from source

Install [Rust](https://rustup.rs) and run:

```sh
cargo install --git https://github.com/voltagecloud/voltage-cli --locked
voltage --version
```

From a clone, `cargo install --path . --locked` does the same. The toolchain is pinned in `rust-toolchain.toml`; no system libraries are needed.

## Credential stores

Credentials are stored in the macOS Keychain, the Linux Secret Service (over D-Bus, with no system library required), or the Windows Credential Manager. On a headless Linux machine without an unlocked Secret Service, pass `--credential-store file` when you log in or import a key.

## Shell completions

Completions come from the same definitions as the commands:

```sh
voltage completions bash > voltage.bash
voltage completions zsh > _voltage
voltage completions fish > voltage.fish
voltage completions powershell > _voltage.ps1
```

Related: [log in and use profiles](login-and-profiles.md).
70 changes: 70 additions & 0 deletions docs/guides/login-and-profiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Log in and use profiles

## Log in

`voltage login` prints a verification URL and a code, opens the URL in your browser, and waits for your approval after your usual login and MFA. Use `--no-browser` on a remote machine and open the URL yourself. The saved login refreshes its tokens automatically; the refresh token rotates.

```sh
voltage login # saved under your email address
voltage login --account work # saved under a name of your choice
voltage login --no-browser --credential-store file
voltage auth status # identity and expiry, never the secret
voltage logout # revokes the session, then removes it
voltage logout --local # removes local credentials only
```

A login grants the permissions your account already has. Profiles do not restrict them.

## API keys

For automation, use an environment API key. Import one so it never lands in shell history, or pass it through `VOLTAGE_API_KEY`:

```sh
voltage auth import-key --account ci --org ORG_ID --env ENV_ID
secret-manager-command | voltage auth import-key --stdin --account ci --org ORG_ID --env ENV_ID
VOLTAGE_API_KEY=... voltage wallets list --org ORG_ID
```

An imported key remembers the organization and environment it belongs to, and a command that selects a different one fails before any request is sent. `VOLTAGE_API_KEY` is used only when neither `--profile` nor `--account` is given. `organizations list` and `environments list` require a browser login.

With several saved credentials, select one with `--account` or through a profile.

## Scope

Resource IDs are positional. The enclosing scope comes from `--org`, `--env`, `--wallet`, and `--webhook`:

```sh
voltage wallets list --org ORG_ID --env ENV_ID
voltage wallets get WALLET_ID --org ORG_ID
voltage payments list --org ORG_ID --env ENV_ID --env OTHER_ENV_ID
```

Without a profile, flags override `VOLTAGE_ORGANIZATION_ID`, `VOLTAGE_ENVIRONMENT_ID`, and `VOLTAGE_WALLET_ID`.

Only endpoints that filter by environment receive `--env`. Organization-wide commands say so in their help, and wallet mutations verify a supplied environment against the wallet before changing anything.

## Profiles

A profile bundles an organization, an environment, and a credential, and ignores the scope variables above and `VOLTAGE_API_KEY`; flags still override its scope for one command.

```sh
voltage profiles create staging --org ORG_ID --env ENV_ID --account work
voltage profiles list
voltage profiles get staging
voltage profiles delete staging
```

## See the effective scope

`voltage context` shows the effective profile, account, organization, environments, wallet, API URL, and configuration directory, and where each came from (`flag`, `profile`, `environment`, `default`, or `unset`). It reports why commands would reject the credential selection as `credential_problem` (`empty_api_key`, `no_credential`, `multiple_credentials`, `unknown_credential`, or `bound_elsewhere` for an API key used outside its organization or environment), using the same selection commands use. It also lists `VOLTAGE_*` variables that are set but ignored because a flag or the selected profile takes precedence. It reads no saved secret, makes no network request, and creates no files:

```sh
voltage context --profile staging
voltage context --json | jq -r '.data.organization_id.value'
```

## Configuration directory

Configuration lives in `$XDG_CONFIG_HOME/voltage` or `~/.config/voltage`; override the directory with `VOLTAGE_CONFIG_DIR` or `--config-dir`. The directory and everything in it are owner-only.

Related: [install](install.md), [payments](payments.md), [scripting](scripting.md).
Loading
Loading