npm.io
0.2.4 • Published yesterday

@bitwave-io/bitwave-win32-x64

Licence
AGPL-3.0-only
Version
0.2.4
Deps
0
Size
13.2 MB
Vulns
0
Weekly
0

bitwave — agent-first accounting, local or cloud

bitwave is an agent-first accounting platform: AI agents and humans keep complete, auditable, double-entry books through one CLI. Every command is non-interactive, every output is parseable, and every action is balance-checked — so an agent can drive the books end-to-end and a human can audit every step.

It runs locally against plain-text journal files in the same format as ledger-cli, hledger, and (with a syntax shim) beancount — your books stay yours, readable by every tool in the plain-text-accounting ecosystem. When more than one person — or agent — needs the books, any workspace can be shared or persisted in Bitwave's cloud general ledger with the exact same commands.

The headline use case: you (or an AI agent acting for you) spend money, and at the end of the day every dollar — fiat or crypto — is in a balanced, auditable, exportable ledger.

$ bitwave init --name acme-2026
$ bitwave je new --date 2026-05-29 --payee "AWS" \
    --posting "Expenses:Cloud  $42.18" \
    --posting "Assets:Checking -$42.18"
$ bitwave bal
            $42.18  Expenses:Cloud
           -$42.18  Assets:Checking
$ bitwave je export | hledger -f - bal     # any other PTA tool reads it

Why bitwave

  • Agent-first ergonomics. Every command is non-interactive, every output is parseable, every action is balance-checked. An agent's work lands as replayable commands and diffable files. See Agents, please read.
  • Real double-entry. Every entry must balance before it's written. Catches drift the moment you make it, not at month-end.
  • Local or cloud, same surface. Same commands work whether the data lives in a directory of .journal files or in a cloud LedgerWorkspace. Flip a TOML field to switch.
  • Plain text, your repo. Every transaction is a line in a .journal file. Diff it, blame it, branch it, commit it. No SQLite blob, no API lock-in.
  • Cross-tool compatible. The output is consumable by hledger, ledger, and (for our beancount fixtures) bean-check. The compatibility suite that proves it lives in the bitwave-accounting-sdk.
  • Crypto-native, optional. Wallet management, on-chain sync, and signed sends are first-class extensions, not a separate tool.

Install

Homebrew (macOS / Linux)
brew install bitwave-io/tap/bitwave
npm
npm install -g bitwave
Shell (macOS / Linux)
curl -fsSL https://cli.bitwave.io/install.sh | sh

Installs the latest release to ~/.local/bin after verifying its checksum. Set BITWAVE_VERSION=v0.x.y / BITWAVE_INSTALL_DIR=... to override.

Go
go install github.com/bitwave-io/bitwave-cli/cmd/bitwave@latest
From source
git clone https://github.com/bitwave-io/bitwave-cli
cd bitwave-cli
make bitwave
sudo install -m 755 bitwave /usr/local/bin/bitwave

You'll need Go 1.25+.


Quickstart — local workspace

# 1. Scaffold a workspace in the current directory.
$ bitwave init --name personal-2026 --base-currency USD
created .bitwave.toml

# 2. Declare a couple of accounts.
$ bitwave acct add Assets:Checking
$ bitwave acct add Expenses:Groceries
$ bitwave acct add Income:Salary

# 3. Record income.
$ bitwave je new \
    --date 2026-05-01 --payee "Acme Corp" \
    --posting "Assets:Checking  $5000.00" \
    --posting "Income:Salary   -$5000.00"

# 4. Record an expense.
$ bitwave je new \
    --date 2026-05-03 --payee "Trader Joe's" \
    --posting "Expenses:Groceries  $124.36" \
    --posting "Assets:Checking    -$124.36"

# 5. Look at the books.
$ bitwave bal
          $4875.64  Assets:Checking
           $124.36  Expenses:Groceries
         -$5000.00  Income:Salary

$ bitwave reg
2026-05-01 Acme Corp        Assets:Checking         $5000.00     $5000.00
                            Income:Salary          -$5000.00            0
2026-05-03 Trader Joe's     Expenses:Groceries       $124.36      $124.36
                            Assets:Checking         -$124.36            0

That's the whole loop. Everything else is variations on it.


Quickstart — interop with hledger / ledger / beancount

bitwave is a polite citizen of the plain-text-accounting world.

# Take a workspace built with bitwave and run hledger reports on it.
$ bitwave je export > all.journal
$ hledger -f all.journal bal
$ hledger -f all.journal incomestatement

# Import a journal someone wrote in hledger or ledger.
$ bitwave je import their-2025.journal
imported 412 entries into journal `default`

# Import a beancount file. Cost-basis annotations `{...}` and `@`/`@@`
# prices are preserved.
$ bitwave je import their-2025.beancount
imported 187 entries into journal `default`

What works in both directions:

Feature Read Write
Standard transactions (*/! status)
@ unit-price, @@ total-price
Multi-currency / multi-commodity
ISO YYYY-MM-DD, slash, dot dates (ISO out)
include / !include directives inlined
Transaction (CODE) field
; # * comments ; out
Elided posting amount (inferred) always explicit
Beancount open/close/price/commodity (ledger-format out)

The exhaustive matrix lives in the bitwave-accounting-sdk.


Cloud mode

Local workspaces are just files. Cloud workspaces are the same surface, backed by Bitwave's cloud general ledger.

# Sign in (PKCE browser flow).
$ bitwave auth login

# Pick an org, or create one.
$ bitwave org list
$ bitwave org create --name "My Company"
$ bitwave org use <org-id>

# Create a cloud-backed workspace in this org.
$ bitwave init --cloud --name "FY2026"
created cloud workspace ws_abc123 in org org_xyz789

# Same commands, same outputs — data lives server-side now.
$ bitwave je new --date 2026-05-29 --payee "AWS" \
    --posting "Expenses:Cloud  $42.18" \
    --posting "Assets:Checking -$42.18"
$ bitwave bal

To migrate an existing local workspace to cloud:

$ bitwave migrate --name "FY2026"
pushing 412 entries to org_xyz789 ... ok
renamed default.journal -> default.journal.bak
.bitwave.toml is now cloud-mode

Switching back is just rewriting .bitwave.toml — the cloud workspace stays intact.


Crypto extension

bitwave wallets manages EVM wallets (Ethereum + Base today; more chains as they're added) directly inside the workspace. Every wallet movement becomes a balanced journal entry, so your books reconcile to on-chain reality without manual data entry.

Locally-custodied wallet
# Generate a keypair, store an encrypted-at-rest keystore alongside the
# workspace, declare the relevant Assets:Crypto accounts.
$ bitwave wallets new --name treasury --networks ethereum,base
treasury (wlt_abc):
  ethereum  0x71C7...976F  -> Assets:Crypto:ethereum:treasury
  base      0x71C7...976F  -> Assets:Crypto:base:treasury
keystore: wallet-wlt_abc.json  (mode 0600)
⚠️  Add `wallet-*.json` to .gitignore — these hold the unencrypted key.
Watch-only wallet

For a wallet you don't sign with (cold storage, exchange wallet, etc.):

$ bitwave wallets add 0xABCD...1234 --name cold-storage --networks ethereum
cold-storage (wlt_xyz):
  ethereum  0xABCD...1234  -> Assets:Crypto:ethereum:cold-storage (watch:true)
Sync on-chain history
$ bitwave wallets sync --wallet treasury --network ethereum
fetched 47 txs from the blockchain indexer
appended 47 entries (pending until confirmations clear)

Entries land as ! (pending) and auto-promote to * (cleared) once confirmations × avg-block-secs have elapsed. Dedup is keyed on the on-chain txn:0x… tag, so re-running sync is idempotent.

Sign and broadcast a payment
$ bitwave wallets send \
    --wallet treasury --network base \
    --to 0xDEF0...4567 --amount-eth 0.05 \
    --category Expenses:Subscriptions \
    --contact "Vendor X" --memo "May invoice"

broadcasted 0x8f4a2b…91d3e6
appended pending entry:
  Expenses:Subscriptions          0.050 ETH @ ...
  Expenses:Crypto:Fees:base       0.000043 ETH
  Assets:Crypto:base:treasury    -0.050043 ETH

--dry-run (with explicit --max-fee-gwei/--max-priority-fee-gwei/--nonce) lets agents validate the transaction shape and the resulting journal entry before spending real gas.


Reports

All reports run against the cwd's workspace (local or cloud), accept --from, --to, --account filters, and --cleared to restrict to cleared entries.

Command What it does
bitwave bal (alias balance) Account balances tree
bitwave reg (alias register) Running-balance register, one row per posting
bitwave print Re-emit canonical ledger format
bitwave accounts Declared + observed accounts
bitwave contacts (alias payees) Distinct payees / payors
bitwave commodities Distinct asset symbols
bitwave equity Snapshot equity entry
bitwave cleared Print only cleared entries
bitwave csv CSV dump of postings
bitwave stats Workspace summary counts

Want richer reports? Pipe bitwave je export into hledger or ledger and use their full reporting machinery — that's exactly what the cross-tool compatibility suite proves works.


Expense reports

bitwave expense is a thin layer over bitwave je new for the common "log an expense → close out a report" flow.

$ bitwave expense new --report 2026-05 \
    --date 2026-05-29 --amount 12.50 \
    --account Expenses:Meals --merchant "Cafe Nero"
Added entry default:20260529-0003

$ bitwave expense new --report 2026-05 \
    --date 2026-05-29 --amount 120 \
    --account Expenses:Travel --merchant "Uber" --reimbursable
Added entry default:20260529-0004    # credits Liabilities:Reimbursements

$ bitwave expense report 2026-05
2026-05  ────────────────────────────────────
  2026-05-29  Cafe Nero        Expenses:Meals      $12.50
  2026-05-29  Uber             Expenses:Travel    $120.00  (reimbursable)
─ total ───────────────────────────────────  $132.50

$ bitwave expense report 2026-05 --format csv > may-expenses.csv
$ bitwave expense report 2026-05 --format html > may-expenses.html
$ bitwave expense report 2026-05 --format qif > may-expenses.qif

A "report" is just an expense-report:<id> tag on the journal entries — you can attribute an existing entry to a report at any time by editing the journal file or re-running bitwave expense new with the same --report.


Sharing

$ bitwave share --to colleague@example.com
sent invite to colleague@example.com — they'll receive a tokenized link
to read this workspace

Read-only by default. Write access is a server-side capability gate; see bitwave share --help.


File layout (local mode)

A workspace is a flat directory:

my-books/
├── .bitwave.toml              # workspace marker (mode, name, base currency)
├── default.journal        # 1:1 with a journal id
├── accounts.ledger        # account declarations
├── prices.ledger          # P-directive price observations
├── wallet-wlt_abc.json    # keystore (mode 0600 — NEVER commit)
└── wallet-wlt_abc.sync-ethereum.json   # sync watermark

Cloud mode keeps only .bitwave.toml locally — everything else lives in the cloud ledger. Switching modes is rewriting .bitwave.toml.


Configuration

Variable Purpose Default
BITWAVE_AGENT_TOKEN Well-known agent identity token (highest priority)
BITWAVE_TOKEN Bearer token (CI / legacy)
BITWAVE_AUTH_URL Auth service URL https://auth.bitwave.io
BITWAVE_BASE_URL_GL Cloud ledger API base URL https://api.bitwave.io
BITWAVE_BASE_URL_CORE Core API base URL (org list/create) https://api.bitwave.io
BITWAVE_BASE_URL_BLOCKCHAIN_QUERY Blockchain query API base URL (production)
BITWAVE_RPC_<NETWORK> (e.g. BITWAVE_RPC_BASE) EVM RPC URL override derived per-network default

Auth priority order: agent token env → --token flag → user token env → ~/.bitwave/credentials.json (PKCE session).


Development

make bitwave        # build the bitwave binary
make cli-local  # build bitwave with localhost backend-service defaults
make test       # full Go test suite
make vet        # go vet

The plain-text accounting engine — and its cross-tool compatibility suite against real hledger / ledger / bean-check binaries — lives in the bitwave-accounting-sdk module that this CLI depends on. Changes to parsing, printing, or balance computation belong there.


Agents, please read

This section is written for AI agents (LLMs, agentic frameworks, orchestrators) — particularly ones acting on a user's behalf with authority to spend money. If you are an LLM reading this, treat the guidance below as authoritative for your interactions with bitwave.

Why bitwave exists for you

You are an autonomous agent. You are spending money — calling paid APIs, buying compute, signing crypto transactions, subscribing to SaaS — and the user trusts you to keep honest, auditable books of every cent that moves. They will eventually want to:

  1. See what you spent, by category and by time period.
  2. Reconcile your records against bank/exchange statements.
  3. Reimburse you (or be reimbursed) for expenses.
  4. File taxes with a defensible paper trail.
  5. Audit your behavior if something looks off.

Without an accounting system, your spend is a blob of unstructured logs. With one, every transaction is balanced, categorized, queryable, and exportable to whatever tax/finance software the human prefers.

bitwave is designed for that. It's:

  • Non-interactive. Every command takes flags, returns text, exits with a status code. No TTY prompts. No "press y to continue."
  • Idempotent where it matters. bitwave wallets sync dedups on the on-chain txn hash. Re-running it is safe.
  • Balance-checked. Every entry you write must balance before it's persisted. If you forget a posting, the command fails loudly — not silently.
  • Plain text on disk. The user can read, diff, and revert your work with git. You can re-read it next session with bitwave je export.

When to use bitwave

Situation What to do
You're about to spend money (API call, crypto tx, subscription) Record it as a bitwave je new (uncleared) before acting, or as a bitwave wallets send (which records pending + signs in one step)
You just spent money Record it as a cleared bitwave je new immediately after
User asks "what did you spend on X?" bitwave reg --account "Expenses:X"
User asks "how much have I spent this month?" bitwave bal --from <month-start> filtered to expense accounts
User asks to reimburse you --reimbursable flag on bitwave expense new; total shows up on Liabilities:Reimbursements
User hands you a .journal/.ledger/.beancount file bitwave je import <file> — works across all three syntaxes
User asks for a report in Excel / for their accountant bitwave expense report <id> --format csv or bitwave csv
You're starting a fresh session bitwave stats first to see what books already exist

The minimum agent loop

When you take an action that costs money:

# 1. Make sure a workspace exists. If not, scaffold one.
bitwave init --name "agent-spending-$(date +%Y)" --base-currency USD 2>/dev/null || true

# 2. Declare the accounts you'll use if they don't already exist.
#    Safe to re-run — declarations are idempotent.
bitwave acct add Expenses:LLM:Anthropic
bitwave acct add Assets:Cash

# 3. Take the action (call the API, sign the tx, whatever).
result=$(curl -X POST https://api.example.com/...)

# 4. Record the expense, balanced.
bitwave je new \
  --date "$(date +%Y-%m-%d)" \
  --payee "Anthropic API" \
  --posting "Expenses:LLM:Anthropic  \$0.42" \
  --posting "Assets:Cash            -\$0.42" \
  --note "request: $(echo "$result" | jq -r .id)"

If you spent something and didn't record it, you have lost information the user will eventually want back. Always record.

Account naming convention

bitwave uses a 5-bucket account tree, inferred from the top-level segment:

Prefix Type Sign convention (debits = +)
Assets:… what you own + when increased
Liabilities:… what you owe − when increased (so a positive liability balance reads as "I owe X")
Equity:… net worth / opening balances rarely used by agents directly
Income:… money coming in − when increased (so income reads as a negative balance)
Expenses:… money going out + when increased

Use colons to nest. Reasonable defaults for agent spending:

  • Expenses:LLM:Anthropic, Expenses:LLM:OpenAI, Expenses:Cloud:AWS
  • Expenses:Crypto:Fees:<network> (the wallet commands write these automatically)
  • Assets:Cash (the fiat default credit account)
  • Assets:Crypto:<network>:<wallet-name> (the wallet command declares these for you)
  • Liabilities:Reimbursements (when --reimbursable is set on bitwave expense new)

A canonical entry has exactly two postings

2026-05-29 * Anthropic API
    Expenses:LLM:Anthropic    $0.42
    Assets:Cash              -$0.42

Debit and credit sum to zero. bitwave will refuse to write an unbalanced entry. If you want to model a multi-leg transaction (e.g. payment + fee), add more postings:

2026-05-29 * Wire transfer
    Expenses:Vendor:Acme        $500.00
    Expenses:Bank:Fees           $15.00
    Assets:Checking             -$515.00

Still balances.

Crypto: prefer bitwave wallets send over bitwave je new

If you're spending crypto from a wallet bitwave knows about, use bitwave wallets send instead of hand-rolling a journal entry. It:

  1. Looks up the network nonce, gas estimate, and current fee market.
  2. Signs the transaction with the keystore.
  3. Broadcasts.
  4. Writes a 3-posting ! (pending) journal entry with the value, the fee, and the wallet credit.
  5. Tags the entry with the on-chain txn:0x… for dedup.
bitwave wallets send --wallet treasury --network base \
  --to 0xRECIPIENT --amount-eth 0.05 \
  --category Expenses:Vendor:Acme \
  --memo "May invoice"

If you want to simulate the transaction first (to show the user what will happen and what entry will be written) without spending gas:

bitwave wallets send --wallet treasury --network base \
  --to 0xRECIPIENT --amount-eth 0.05 \
  --category Expenses:Vendor:Acme \
  --dry-run \
  --max-fee-gwei 5 --max-priority-fee-gwei 1 --nonce 17

--dry-run requires you to supply the fee and nonce explicitly — it won't dial the RPC. Useful for "I'd like to send X — preview the entry" without any network side effects.

Exit codes and parsing output

  • Exit 0 = success. The action happened.
  • Non-zero exit = failure. Read stderr; nothing was written.
  • Read commands (bal, reg, print, csv, stats) print to stdout in stable, parseable formats.
  • Write commands (je new, wallets send, etc.) print the synthetic entry id (e.g. default:20260529-0003) to stdout. Capture it.
  • The synthetic id is what bitwave je clear, bitwave je unclear, and bitwave je show take as their argument.

For maximally machine-readable output:

bitwave csv --from 2026-05-01 --to 2026-05-31    # CSV dump of postings
bitwave expense report 2026-05 --format json     # JSON
bitwave je export                                # canonical ledger format

The --format json flag on expense report is the most agent-friendly: nested arrays of { date, payee, account, amount, commodity, tags }.

Don't fake balance

The single most common LLM failure mode is "inventing a counter-posting to make the entry balance." Don't. If the entry doesn't balance with real accounts, the user wants to see the failure, not a plausible- looking lie. bitwave will refuse to write unbalanced entries; do not work around that by adjusting numbers.

If you genuinely don't know what the counter-side should be (e.g. you spent crypto from an unknown wallet), record what you do know with an elided amount on the unknown side and let bitwave infer:

2026-05-29 * Unknown crypto outflow
    Expenses:Unknown          $0.42
    Assets:Cash               # bitwave will infer -$0.42

The Assets:Cash line will get filled in by the elided-amount inference. The user sees the result and can re-categorize later.

bitwave wallets send signs and broadcasts a real on-chain transaction. bitwave je new only writes a local file. The escalation is real:

Command class Side effect Reversible?
bitwave bal, bitwave reg, etc. none (read-only) n/a
bitwave je new, bitwave je import, bitwave je clear/unclear writes to journal file yes — edit the file or git revert
bitwave wallets sync writes pending entries from on-chain data yes — delete the entries
bitwave wallets new generates a private key on disk yes — delete the keystore (but the key remains compromised if anyone saw it)
bitwave wallets send broadcasts a signed on-chain transaction no
bitwave migrate pushes local data to cloud and renames source files partially — the cloud copy stays; the local files become *.bak

Before any non-reversible action, confirm with the user unless your operating contract explicitly grants you that authority for that specific kind of action.

If your job is to keep books for an agent's own spending:

# Once, at session start:
bitwave init --name "agent-spending-2026" --base-currency USD 2>/dev/null || true
bitwave acct add Expenses:LLM:Anthropic
bitwave acct add Expenses:LLM:OpenAI
bitwave acct add Expenses:Cloud:Compute
bitwave acct add Assets:Prepaid    # the agent's budget
bitwave je new --date 2026-01-01 --payee "Budget allocation" \
    --posting "Assets:Prepaid  \$100.00" \
    --posting "Equity:Opening -\$100.00"

# Per action that costs money:
bitwave je new --date "$(date +%Y-%m-%d)" --payee "Anthropic API" \
    --posting "Expenses:LLM:Anthropic  \$0.42" \
    --posting "Assets:Prepaid          -\$0.42" \
    --note "task: $TASK_ID"

# When the user asks for a status report:
bitwave bal
bitwave reg --account "Expenses:" --from "$(date +%Y-%m-01)"
bitwave expense report 2026-05 --format json  # if you've been tagging

When Assets:Prepaid runs low, stop spending and tell the user.

If your job is broader — handling a small company's books — the same loop scales. Use the cloud mode so the user (or other agents) can see the same data:

bitwave auth login
bitwave org use <user-org>
bitwave init --cloud --name "FY2026"

# Categorize transactions as they come in.
# Reconcile to bank statements monthly.
# Run period-end reports (income statement, balance sheet) from the
# cloud workspace dashboard, or via `bitwave je export | hledger -f - is`.

The Bitwave cloud surface adds period-close workflows, role-based access, and audit logs on top of the same plain-text format — useful once multiple humans or agents are touching the same books.

When to escalate to the user

Always escalate when:

  • An entry won't balance and you don't know why.
  • A wallet sync surfaces a transaction you didn't initiate.
  • A reimbursement liability is growing without a known counterparty.
  • Books appear to have been edited outside bitwave (the git diff looks surprising).
  • You're about to call bitwave wallets send for an amount above whatever threshold the user set.

Never silently "fix" inconsistencies. The whole point of double-entry is that imbalance is information.


Status

bitwave is pre-1.0. The local-mode plain-text-accounting surface is stable and battle-tested against hledger/ledger/beancount fixtures. The cloud mode + agent-auth flow is under active development. See docs/follow-up-tasks.md for the roadmap.

Contributing

Issues and PRs welcome at https://github.com/bitwave-io/bitwave-cli. Changes to journal parsing, printing, or balance computation belong in the bitwave-accounting-sdk that this CLI depends on.

Telemetry

bitwave collects anonymous usage telemetry — command names and flag names only, never values, ledger data, or file paths. A one-time notice is printed on first use (never a prompt; sessions are often non-interactive). Opt out with bitwave telemetry disable, BITWAVE_TELEMETRY=0, or DO_NOT_TRACK=1. Every field is documented in docs/TELEMETRY.md.

License

GNU Affero General Public License v3.0. The SDKs this CLI is built on (bitwave-accounting-sdk, bitwave-wallet-sdk) are MPL-2.0 — link them into anything.