# Fedimint Bitcoin Wallet

> Use when a user wants to hold and send Bitcoin with `fedimint-cli` — a wallet that keeps a Bitcoin balance in a Fedimint federation and can send or receive it three ways: over **Lightning** (instant payments via a gateway), **on-chain** (deposit/withdraw regular Bitcoin), and as **ecash** (private bearer notes handed directly to another person). Covers joining a federation from an invite code, checking your balance, and backing up. Triggers on: "fedimint-cli", "fedimint wallet", "bitcoin wallet", "hold bitcoin", "send bitcoin", "join federation", "invite code", "ecash", "e-cash", "reissue", "spend notes", "ln-invoice", "ln-pay", "lightning invoice", "deposit address", "peg-in", "peg-out", "withdraw on-chain", "fedimint balance", "fedimint backup".

- Skill: `fedimint/fedimint-bitcoin-wallet` (Agent Skill)
- Install (CLI): `npx skillmds@latest add fedimint/fedimint-bitcoin-wallet`
- Raw SKILL.md: https://api.skillmd.com/api/skills/fedimint/fedimint-bitcoin-wallet/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Finance & Business
- Author: fedimint (https://skillmd.com/u/fedimint)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/fedimint/fedimint-bitcoin-wallet

---


# A Bitcoin wallet with `fedimint-cli`

`fedimint-cli` lets a single user **hold and send Bitcoin** through a Fedimint federation. Your
balance is real Bitcoin, held on your behalf by the federation's guardians, and you can move it three
ways:

- **Lightning** — send and receive instant Bitcoin payments, brokered by a gateway.
- **On-chain** — deposit or withdraw ordinary Bitcoin (peg-in / peg-out with the federation's wallet).
- **Ecash** — hand private bearer notes directly to another person, no third party in the loop.

**Under the hood:** the balance you hold is Chaumian **ecash** — private, bearer Bitcoin tokens issued
by the federation. Lightning and on-chain are simply ways to convert value into or out of that ecash
balance. You mostly don't need to think about this; it's why payments are private and instant.

## Setup

### 1. Install `fedimint-cli`

Pick the method that matches the platform. **On Debian/Ubuntu use the `.deb`** — it is by far the
cleanest path. None of these paths require the **Nix package manager** — the `.deb`/`.rpm`/darwin
binaries simply *bundle* their libraries under `/nix/store` and run without any `nix` command, daemon,
or `/nix` setup. (Verified on Ubuntu 24.04, Debian 12, and Arch with no Nix installed.)

**Debian / Ubuntu (`.deb`):**
```bash
apt-get update && apt-get install -y curl                 # if curl is missing (fresh containers)
curl -L -O https://github.com/fedimint/fedimint/releases/download/v0.11.1/fedimint-cli_0.11.1_amd64.deb
apt-get install -y ./fedimint-cli_0.11.1_amd64.deb        # installs to /usr/bin/fedimint-cli
fedimint-cli version-hash                                  # sanity check -> prints a git hash
```
> The `.deb` is ~170 MB — let the download **finish** before installing. Installing a truncated file
> fails with a misleading `E: Invalid archive member header`. `curl -L -O` (no progress bar in scripts)
> gives no hint; check the file size or use `curl -fL` so a failed download errors instead of half-writing.
(Fedora/RHEL: the same page has `fedimint-cli-0.11.1-1.x86_64.rpm` — `dnf install ./…rpm`.)

**macOS (Apple Silicon):** use the prebuilt darwin tarball — no Homebrew/Nix/admin needed, and a
`curl` download isn't Gatekeeper-quarantined so it runs as-is:
```bash
curl -fL -O https://github.com/fedimint/fedimint/releases/download/v0.11.1/fedimint-pkgs-v0.11.1-aarch64-apple-darwin.tar.gz
tar xzf fedimint-pkgs-v0.11.1-aarch64-apple-darwin.tar.gz     # -> bin/fedimint-cli (+ bin/fedimintd)
./bin/fedimint-cli version-hash
# If you fetched it via a browser instead of curl, clear the quarantine flag first:
#   xattr -d com.apple.quarantine ./bin/fedimint-cli
```
Note the macOS/BSD userland lacks GNU niceties: `grep -P` and the `timeout` command aren't available
(install `coreutils` via Homebrew for `gtimeout`, or use `jq` for JSON extraction below).

**NixOS / any system with Nix:**
```bash
# --accept-flake-config lets Nix use Fedimint's binary cache (fedimint.cachix.org) instead of
# compiling from source — without it the install can take tens of minutes.
nix profile install --accept-flake-config github:fedimint/fedimint/v0.11.1#fedimint-cli
# or run without installing: nix run --accept-flake-config github:fedimint/fedimint/v0.11.1#fedimint-cli -- info
```
Requires Nix with flakes enabled (`experimental-features = nix-command flakes`). On NixOS do **not**
use the raw release binary below — an unpatched dynamically-linked binary won't run; use Nix.

**Arch / other Linux (no `apt`/`dnf`):** two nix-free options —
- The bare `fedimint-cli-v0.11.1` asset is a **self-extracting** `nix bundle` (needs `hexdump`, `tar`,
  `xz`; `pacman -S util-linux xz` if missing). It unpacks via `nix-user-chroot`, so it requires
  **unprivileged user namespaces** enabled — on by default on stock Arch/desktop kernels, but blocked
  in some hardened/container environments (you'll see a "namespace" hint if so; enable it or use the
  next option).
- **Robust (no user namespaces, no dpkg):** extract the `.deb` payload directly — the binary and its
  bundled libs live under `/nix/store`, and running them needs no Nix package manager:
  ```bash
  curl -fL -O https://github.com/fedimint/fedimint/releases/download/v0.11.1/fedimint-cli_0.11.1_amd64.deb
  ar x fedimint-cli_0.11.1_amd64.deb && sudo tar -C / -xf data.tar.*     # populates /usr + /nix/store
  sudo ln -sf /nix/store/*-fedimint-cli/bin/fedimint-cli /usr/local/bin/fedimint-cli
  fedimint-cli version-hash
  ```

**Docker:** the CLI ships in the `fedimintd` image:
```bash
docker run --rm -v fm-cli-data:/data fedimint/fedimintd:v0.11.1 fedimint-cli --data-dir /data info
```

### 2. Pick a data directory

The client stores its keys and database in a data directory. Set it once so every command uses the
same wallet — **a different data dir is a different, empty wallet.**

```bash
export FM_CLIENT_DIR="$HOME/.fedimint-cli"       # picked up automatically (same as --data-dir)
mkdir -p "$FM_CLIENT_DIR"
```

Every command below can also take `--data-dir <dir>` explicitly. In scripts or any non-interactive
setting where each command runs in a fresh shell (so the `export` above won't persist), pass
`--data-dir` on every invocation instead — otherwise you silently get a fresh, empty wallet.

### 3. Join a federation

You need an **invite code** (`fed11…`) from the federation you want to use.

```bash
fedimint-cli join <INVITE_CODE>
fedimint-cli info
```

`info` prints the federation id, network, meta fields, and your holdings:

```json
{
  "denominations_msat": { "1": 2, "2": 3, "512": 3, "1024": 3 },
  "federation_id": "c166a596345c126c2cde3fc57e399fec48b1ad741b49dcd48d313cb6cd4458b8",
  "meta": { "federation_name": "Devimint Federation" },
  "network": "regtest",
  "total_amount_msat": 495166,
  "total_num_notes": 41
}
```

> **Units — read this, it is the #1 footgun:** amounts default to **millisatoshis (msat)**. This
> includes `spend`, `ln-invoice --amount`, **and `withdraw --amount`**. A bare number is msat:
> `withdraw --amount 20000` means 20 000 **msat = 20 sats**, which fails as "under the dust limit".
> To withdraw 20 000 **sats**, write `--amount 20000000` (msat) or use an explicit unit suffix:
> `--amount '20000 sat'` or `--amount 0.0002btc`. `total_amount_msat: 495166` = 495 166 msat ≈ 495 sats.
> (`deposit-address` takes no amount — you send BTC to the address from elsewhere.)

> **Fees:** the credited amount is often slightly less than the nominal amount. Reissuing/receiving
> ecash can incur a small federation mint fee (e.g. `reissue` of 500 000 msat may credit ~495 000),
> and receiving Lightning pays the gateway's fee. This is expected, not an error — check `info` for
> the actual balance.

## Ecash (out-of-band notes)

Ecash is transferred by handing someone a base64 note string. The sender `spend`s (removing the value
from their wallet), the receiver `reissue`s (claiming it). Reissue also prevents double-spends.

```bash
# Sender: carve out 100 000 msat of notes (selects the smallest note set for the amount)
fedimint-cli spend 100000
# -> {"notes": "BgAAAA…"}   (hand this string to the receiver)

# Receiver: claim the notes into their own wallet.
# NOTE: `spend` returns JSON `{"notes": "BgAAAA…"}` — pass just the inner base64
# string to reissue, not the whole JSON object. Extract it with, e.g.:
#   NOTES=$(fedimint-cli spend 100000 | jq -r .notes)   # then: fedimint-cli reissue "$NOTES"
#   (no jq? portable across GNU/BSD: sed -n 's/.*"notes": *"\([^"]*\)".*/\1/p')
fedimint-cli reissue BgAAAA…
# -> the reissued amount, e.g. 100000

# Check validity WITHOUT claiming (signatures only; does not detect double-spends)
fedimint-cli validate BgAAAA…
# Inspect notes as JSON without touching them
fedimint-cli dev decode notes BgAAAA…
```

- `spend` fails if it can't represent the exact amount with available note denominations (notes come
  in powers of two, so round decimal amounts like 500 000 often can't be made exactly). Add
  `--allow-overpay` to send slightly more instead of failing.
- Spent-but-unclaimed notes are auto-reclaimed by the sender after `--timeout` seconds (default 1 week),
  so a receiver who never redeems doesn't cost the sender the money permanently.
- Add `--include-invite` to `spend` so the receiver can join the federation from the notes alone.

The forward-looking form is `fedimint-cli module mint {spend,reissue,split,combine}`; the top-level
`spend`/`reissue` still work and print a deprecation note — either is fine.

## Lightning (via a gateway)

Lightning payments are brokered by a **gateway**. List the gateways the federation knows and,
optionally, pick one:

```bash
fedimint-cli list-gateways
fedimint-cli switch-gateway <GATEWAY_ID>          # optional; otherwise one is chosen for you
```

### Receive over Lightning

```bash
fedimint-cli ln-invoice --amount 100000           # 100 000 msat
# -> {"invoice": "lnbcrt…", "operation_id": "5b37…"}
# Give the "invoice" to the payer, then wait for it to be paid:
fedimint-cli await-invoice 5b37…                  # pass the operation_id
# On success this prints your updated balance JSON (same shape as `info`) and returns.
fedimint-cli info                                 # balance increased
```

### Send over Lightning

```bash
fedimint-cli ln-pay "lnbcrt1u1p…"                 # a BOLT11 invoice (or lnurl)
```

Notes:
- Sending needs enough ecash balance; receiving needs the gateway to have inbound Lightning liquidity.
- If a payment fails, try `switch-gateway` to a different gateway, or check `list-gateways` for one
  that is active.
- The newer LNv2 protocol is under `fedimint-cli module lnv2 {receive,send,await-receive,await-send}`.

## On-chain Bitcoin (peg-in / peg-out)

### Deposit (peg-in): on-chain BTC -> ecash

```bash
fedimint-cli deposit-address
# -> {"address": "bcrt1q…", "idx": 0, "operation_id": "…"}
# Send BTC to that address from any wallet, then wait for confirmations:
fedimint-cli await-deposit <OPERATION_ID>
fedimint-cli info
```

Deposits need the federation's `finality_delay` in confirmations (often ~10 blocks) before the ecash
is credited. `await-deposit` blocks until then, and on success prints a bare `null` — that is **not**
an error; confirm the credit with `info`.

### Withdraw (peg-out): ecash -> on-chain BTC

```bash
fedimint-cli withdraw --amount 50000000 --address bcrt1q…    # 50 000 000 msat = 50 000 sats
fedimint-cli withdraw --amount '50000 sat' --address bcrt1q…  # same, explicit unit
fedimint-cli withdraw --amount all --address bcrt1q…         # sweep everything
```

The response contains a `txid` and `fees_sat`. On regtest the peg-out is broadcast once the federation
signs it; mine a block to confirm.

## Backup & restore

The client is deterministic from a BIP-39 mnemonic; ecash notes can be recovered by restoring from
the federation's encrypted backup.

```bash
fedimint-cli print-secret                          # reveals the client's secret — handle carefully
fedimint-cli backup                                # upload encrypted note snapshot to the federation
fedimint-cli restore --mnemonic "word1 word2 …" --invite-code fed11…   # into a fresh data dir
```

Restore is a scan and can take a while; run `fedimint-cli info` afterwards to confirm balances.

## Quick reference

| Goal | Command |
|------|---------|
| Join federation | `fedimint-cli join <invite>` |
| Balance / info / meta | `fedimint-cli info` |
| Read federation meta fields | `fedimint-cli dev meta-fields` |
| Send ecash | `fedimint-cli spend <msat>` → give `notes` |
| Receive ecash | `fedimint-cli reissue <notes>` |
| Receive Lightning | `fedimint-cli ln-invoice --amount <msat>` → `await-invoice <op_id>` |
| Send Lightning | `fedimint-cli ln-pay <bolt11>` |
| List/switch gateway | `fedimint-cli list-gateways` / `switch-gateway <id>` |
| Deposit on-chain | `fedimint-cli deposit-address` → `await-deposit <op_id>` |
| Withdraw on-chain | `fedimint-cli withdraw --amount <msat, or '<n> sat'> --address <addr>` |
| Backup / restore | `fedimint-cli backup` / `restore --mnemonic … --invite-code …` |

## Common mistakes & troubleshooting

- **Empty balance after `join`:** joining does not fund you. You need someone to `spend` you ecash,
  a Lightning payment, or an on-chain deposit.
- **Wrong wallet:** if `info` shows an unexpected balance, you are almost certainly pointing at a
  different `--data-dir`/`FM_CLIENT_DIR`. Keep it consistent.
- **msat vs sats:** every `--amount` (ecash, LN, **and on-chain `withdraw`**) defaults to **msat**. A
  small bare number on `withdraw` fails as "under the dust limit" — use `<n>000` msat or `'<n> sat'`.
- **`spend` fails with an amount error:** denominations can't represent it exactly — use
  `--allow-overpay`.
- **Lightning payment fails / no gateway:** `list-gateways`; if the active one is down, `switch-gateway`
  to another.
- **Deprecation warnings** on `spend`/`reissue`/`ln-invoice`/etc. are expected — the commands still work.
- **Deposit not credited:** on-chain deposits wait for the federation's `finality_delay` confirmations;
  keep `await-deposit` running (on regtest you must mine blocks).
- **A killed or contested `reissue` can wedge the wallet:** interrupting `reissue`, or reissuing notes
  that were already spent/double-spent, can hang and leave a stuck pending state that makes later
  commands panic (e.g. `Cannot claim input, additional funding needed`). Recover by starting over in a
  fresh `--data-dir` and re-joining (your on-federation `backup` can be `restore`d there). Only reissue
  notes you control and haven't already claimed.
- Discover any subcommand's flags with `fedimint-cli help` or `fedimint-cli <cmd> --help`.

