docs: add end-user guide covering GUI and CLI

Full reference for every user-facing behavior (wizard flows, script
types, fees, gap limit, TOFU cert pinning, CLI commands) with the
exact numbers the software enforces, so users don't have to guess or
read the source to understand a rule.
This commit is contained in:
2026-07-07 14:25:35 +02:00
parent b304996ff5
commit 1f4421ae16
+856
View File
@@ -0,0 +1,856 @@
# Palladium Wallet — User Guide
This guide covers **every user-facing feature** of Palladium Wallet (GUI and CLI), including
default values, validation rules, limits, and edge cases. It is written so that no behavior
has to be guessed: when the wallet enforces a rule, the rule is stated here with its exact
numbers.
Applies to version **0.9.x**. Where the GUI and the CLI differ, both are described.
---
## Table of contents
1. [What Palladium Wallet is (and is not)](#1-what-palladium-wallet-is-and-is-not)
2. [Key concepts you must understand before holding funds](#2-key-concepts)
3. [Installation and platform differences](#3-installation-and-platform-differences)
4. [First launch: the setup wizard](#4-first-launch-the-setup-wizard)
5. [Opening, closing and managing multiple wallets](#5-opening-closing-and-managing-multiple-wallets)
6. [The main screen](#6-the-main-screen)
7. [Receiving funds](#7-receiving-funds)
8. [Sending funds](#8-sending-funds)
9. [Transaction history and details](#9-transaction-history-and-details)
10. [The Addresses tab](#10-the-addresses-tab)
11. [Contacts](#11-contacts)
12. [Connection, servers and certificate pinning](#12-connection-servers-and-certificate-pinning)
13. [Settings](#13-settings)
14. [Wallet Information (xpub, seed, fingerprint)](#14-wallet-information)
15. [Backup and recovery](#15-backup-and-recovery)
16. [Security model and known limitations](#16-security-model-and-known-limitations)
17. [Command-line interface (CLI)](#17-command-line-interface-cli)
18. [Troubleshooting and error messages](#18-troubleshooting-and-error-messages)
19. [Glossary](#19-glossary)
---
## 1. What Palladium Wallet is (and is not)
Palladium Wallet is a **self-custody SPV (Simplified Payment Verification) wallet** for the
Palladium (PLM) cryptocurrency, a Bitcoin-derived UTXO chain with 2-minute blocks. It runs on
Windows, Linux and Android from the same codebase, plus a separate command-line interface.
**Self-custody** means: *you* hold the keys. There is no account, no server-side backup, no
password recovery service. If you lose your seed phrase (and your wallet file plus its
password), your funds are permanently unrecoverable. Nobody — including the developer — can
restore them.
**SPV** means the wallet does **not** download or validate the full blockchain. It connects to
an **indexing server** (ElectrumX-compatible, ports 50001 TCP / 50002 SSL) and downloads only
the data relevant to your addresses. Every *confirmed* transaction the server reports is
independently verified with a **Merkle proof** against the block header; a server that lies
about confirmed transactions is detected and the synchronization aborts. See
[section 16](#16-security-model-and-known-limitations) for exactly what the server can and
cannot do to you.
What this wallet intentionally does **not** include (as of this version):
- Lightning Network
- Hardware-wallet integration
- Coin control (manual UTXO selection) — coin selection is always automatic
- RBF fee bumping (transactions *signal* RBF, but there is no "bump fee" button)
- Tor/proxy support — the indexing server can observe which addresses you query
- Fiat currency conversion
- Network selection in the GUI — the **GUI operates on mainnet only**; testnet and regtest
are available through the CLI (`--net`)
---
## 2. Key concepts
Read this section once, carefully. Every rule below is enforced by the software exactly as
written.
### 2.1 The seed phrase (BIP39 mnemonic)
- When you create a new wallet in the GUI, it generates a **12-word English** BIP39 mnemonic.
It is shown **once**, during the wizard, and you must write it down **on paper, in order**.
- Whoever knows these words controls the funds — from any device, forever, with no further
information needed (unless you also set a passphrase, see below).
- When *restoring*, the wallet accepts any valid BIP39 mnemonic (12 or 24 words in the GUI
prompt; the parser also accepts 15/18/21-word mnemonics and auto-detects the wordlist
language). The checksum is validated: a mistyped word is rejected, not silently accepted.
### 2.2 The optional passphrase ("25th word")
During creation or restore you may set a **BIP39 passphrase**. Understand precisely what it
does:
- The passphrase is combined with the seed words to derive the keys. Seed + passphrase
produce a **completely different wallet** than the same seed without a passphrase (or with
any other passphrase, including one differing by a single character or by case).
- There is **no validity check possible**: entering the "wrong" passphrase during a restore
does not produce an error — it silently opens a different, empty wallet. If you restore and
see a zero balance where you expected funds, the first thing to check is the passphrase
(then the script type, see 2.3).
- Store the passphrase **separately** from the seed words. Losing the passphrase makes the
funds unrecoverable even if you still have the 12 words.
### 2.3 Script type (address format)
Each wallet is created with exactly one **script type**, which determines the address format
and the derivation path. It cannot be changed later; to switch, create a new wallet and move
the funds.
| Script type | Standard | Derivation | Mainnet addresses look like |
|---|---|---|---|
| Legacy | BIP44 | `m/44'/746'/0'` | start with `P` |
| Wrapped SegWit | BIP49 | `m/49'/746'/0'` | start with `3` |
| **Native SegWit** (default, recommended) | BIP84 | `m/84'/746'/0'` | start with `plm1q` |
| Taproot | BIP86 | `m/86'/746'/0'` | start with `plm1p` |
**Restoring with the wrong script type is the same trap as the wrong passphrase**: the seed
is accepted, but different addresses are derived and the balance shows zero. When restoring,
select the same script type the wallet was created with. If unsure, try Native SegWit first
(the default), then the others.
The coin type in the derivation path is **746** on mainnet (1 on testnet/regtest).
### 2.4 Wallet types
| Type | Created via | Can sign/spend | Notes |
|---|---|---|---|
| HD (BIP39 seed) | Create new / Restore from seed | Yes | The normal case |
| HD (imported xprv) | Import extended key (xprv/yprv/zprv) | Yes | Full account key, no seed words |
| Watch-only (xpub) | Import extended key (xpub/ypub/zpub) | **No** | Sees balances/history; cannot sign. In the GUI it can *prepare* a transaction preview but not sign or broadcast it. The CLI produces an unsigned PSBT for offline signing |
| Imported WIF keys | Import WIF key(s) | Yes | Fixed address list, **no HD derivation and no change chain — change always returns to the first imported address** (this links your coins together; prefer an HD wallet for privacy) |
The extended-key format is **SLIP-132**: `xpub`/`xprv` (Legacy and Taproot), `ypub`/`yprv`
(Wrapped SegWit), `zpub`/`zprv` (Native SegWit). The script type is auto-detected from the
prefix when importing.
### 2.5 The wallet file and its encryption
Everything the wallet knows is stored in **one file per wallet**:
`<data folder>/mainnet/wallets/<name>.wallet.json` (default name `default.wallet.json`).
It contains the seed (or xpub/xprv/WIF keys), the derivation settings, your **contacts**, and
a sync cache (balances, history, verified proofs).
- If you set a password, the file is encrypted with **AES-256-GCM**; the key is derived with
**PBKDF2-HMAC-SHA512, 600,000 iterations, random 16-byte salt** (fresh salt and nonce on
every save). Tampering with the file is detected before decryption.
- If you **decline encryption** (the wizard checkbox, or omitting `--password` in the CLI),
**the seed is stored in plaintext on disk**. The wizard warns you explicitly. Only do this
on a disk you fully trust (e.g. an encrypted volume).
- There are **no password strength requirements and no attempt limits**. An empty password is
not allowed when encryption is enabled. Choose a strong password yourself: an attacker who
copies the file can try passwords offline at their leisure.
- There is currently **no "change password" function** in the GUI. To change the password,
restore the wallet from its seed into a new wallet file with the new password, verify the
new file opens and syncs, then delete the old file. (Contacts must be re-entered — see
[section 15](#15-backup-and-recovery).)
### 2.6 Confirmations and spendability
Incoming funds are not immediately spendable. The exact rules on mainnet:
- **Unconfirmed (mempool)** transactions: shown as *"pending confirmation … not yet
spendable"*. Never spendable, and — important — unconfirmed amounts are reported by the
server **without cryptographic proof**, so treat them as provisional until confirmed.
- **Regular outputs**: spendable after **6 confirmations** (~12 minutes at 2-minute blocks).
- **Mining (coinbase) outputs**: spendable after **121 confirmations** (~4 hours). Until
then they appear as *"maturing … not yet spendable"*.
The main balance shown is *confirmed minus immature*; pending and maturing amounts are shown
as separate amber lines under the balance when present.
---
## 3. Installation and platform differences
### 3.1 Desktop (Windows / Linux)
Release binaries are self-contained — no runtime to install. Run the single executable
(`PalladiumWallet.exe` on Windows, `PalladiumWallet` on Linux).
### 3.2 Android
Install the `.apk` (sideloading must be allowed). Minimum Android version: 6.0 (API 23).
The app requests the **camera** permission only if you use the QR scanner; **internet** is
required for synchronization.
> **Updating in place**: releases built with the project's official signing key update over
> the installed app. If Android refuses to install an update ("app not installed" /
> signature mismatch), the new apk was signed with a different key — you would have to
> uninstall first, **which deletes the wallet file**. Back up your seed before uninstalling,
> always.
### 3.3 What differs between desktop and Android
| Aspect | Desktop | Android |
|---|---|---|
| Data location | Chosen at first run (default or custom folder) | Fixed: the app's private sandbox (step skipped) |
| Menu bar | File / Network / Settings / Wallet / Help | No menu bar; same functions reachable from the UI |
| Close overlays | `Esc` key or click the dark backdrop | Hardware/gesture **Back** button or tap the backdrop |
| QR code **scanning** (Send) | Not available | Camera scan button in the Send form |
| CLI | Available | Not available |
Everything else — wallet formats, security, sync, features — is identical.
### 3.4 Where your data lives (desktop)
Resolution order:
1. A `palladium-data/` folder next to the executable, if present (**portable mode** — create
the folder manually before first launch to get a fully portable install).
2. The custom folder you picked in the first-run wizard (remembered via a small pointer file
in the default location).
3. The default: `%APPDATA%\PalladiumWallet` on Windows, `~/.palladium-wallet` on Linux.
Inside the data root: `mainnet/wallets/*.wallet.json` (your wallets),
`mainnet/server-certs.json` (pinned TLS certificates), `mainnet/servers.json` (discovered
servers), `config.json` (language, unit, last server — shared across wallets).
---
## 4. First launch: the setup wizard
### 4.1 Step: data location (desktop only, very first run)
Choose where the wallet stores its data: **"Use the default path"** (shown on screen) or
**"Choose a folder…"** (native folder picker). This step never reappears once configured.
On Android it is skipped entirely.
### 4.2 Step: start
Five options:
1. **Open existing wallet** — see [section 5](#5-opening-closing-and-managing-multiple-wallets).
2. **Create a new wallet**
3. **Restore from seed**
4. **Import xpub / xprv**
5. **Import WIF key**
### 4.3 Creating a new wallet
1. **Your seed (12 words)** — the freshly generated mnemonic is displayed. Write the words
on paper, in order. *They are never shown in full again except through the password-gated
"Show seed" function ([section 14](#14-wallet-information)).* Do not photograph them, do
not store them in a cloud note.
2. **Confirm seed** — retype the 12 words, space-separated. Comparison is case-insensitive
and tolerant of extra spaces. A mismatch shows *"The words do not match: check what you
wrote on paper."* and lets you retry.
3. **Optional passphrase** — leave empty to skip. Re-read [section 2.2](#22-the-optional-passphrase-25th-word)
before setting one.
4. **Script type** — default Native SegWit. If unsure, keep the default.
5. **Password step** — see 4.7.
### 4.4 Restoring from seed
1. Enter the mnemonic, words separated by spaces. Invalid words or a bad checksum produce
*"Invalid mnemonic (wrong words or checksum): check again."*
2. Enter the **same passphrase** used originally (empty if none was used).
3. Select the **same script type** used originally.
4. Password step (4.7).
After the first synchronization the balance and full history reappear. If the balance is
zero when it shouldn't be: wrong passphrase or wrong script type
([sections 2.22.3](#22-the-optional-passphrase-25th-word)).
### 4.5 Importing an extended key (xpub / xprv)
Paste a SLIP-132 extended key:
- **Public key** (`xpub`/`ypub`/`zpub`) → **watch-only** wallet: monitors balances and
history, cannot spend.
- **Private key** (`xprv`/`yprv`/`zprv`) → fully spendable HD wallet (account level, no seed
words).
The script type is detected automatically from the key prefix and shown for confirmation
(e.g. *"NativeSegwit (watch-only)"*). An unrecognized key shows *"Extended key not
recognised for this network."* — check that it is a Palladium key of a supported format, not
a key from another coin.
### 4.6 Importing WIF private keys
Paste one or more WIF keys, **one per line**. Each key controls exactly one address; there
is no HD derivation. Select the script type (which address form the keys map to), then the
password step. Remember the change-address caveat from
[section 2.4](#24-wallet-types).
### 4.7 The password step (all flows)
- **Wallet name** (optional): letters/numbers recommended; invalid filesystem characters are
replaced with `_`. Left blank → automatic name (`default`, then `wallet-2`, `wallet-3`, …).
A name that already exists is rejected: *"A wallet with this name already exists."*
- **"Encrypt the wallet file with the password"** — checked by default. If you uncheck it,
the file (including the seed) is written **in plaintext** and the UI warns you.
- **Password + confirmation** — must match and be non-empty when encryption is on. No other
rules are imposed; strength is your responsibility.
- **Create wallet** finalizes: the file is written, locked against concurrent access, opened,
and the first connection/sync starts automatically.
---
## 5. Opening, closing and managing multiple wallets
- You can keep **any number of wallets**: each is a separate `*.wallet.json` file in the
wallets folder. With more than one file, "Open existing wallet" shows a chooser list; with
exactly one, it goes straight to the password prompt.
- **Password prompt**: leave empty for an unencrypted wallet. Wrong password → *"Wrong
password."* — retries are unlimited.
- The GUI **only lists wallets inside its own wallets folder**. To use a wallet file from
elsewhere, copy it into `<data folder>/mainnet/wallets/` first (while the app is closed,
or before pressing Open).
- **Single-instance lock**: a wallet file can be open in only one running instance at a
time. A second attempt fails with *"Wallet already open in another instance of the
application."* A stale `.lock` file left by a crash is released automatically when the
owning process is gone.
- **Switching wallets**: menu *File → Close wallet* (returns to the wizard), then *Open
existing wallet*. *File → New / restore wallet…* also closes the current wallet first.
- **Deleting a wallet**: there is no in-app delete. Close the app and delete the
`.wallet.json` file manually — **only after verifying you have the seed on paper** (or,
for imported wallets, the original keys). Deleting the file of an unbacked-up wallet
destroys the funds' keys.
After opening, the header shows the wallet's identity, e.g.
`Mainnet · NativeSegwit · m/84'/746'/0'`, with a ` · watch-only` or ` · imported` tag when
applicable.
---
## 6. The main screen
Five tabs: **History**, **Send**, **Receive**, **Addresses**, **Contacts**.
- **Balance** (top): confirmed, spendable balance in your chosen unit. Below it, when
applicable, two amber advisory lines: *pending confirmation* (mempool, unproven, not
spendable) and *maturing* (young coinbase, not spendable — see
[section 2.6](#26-confirmations-and-spendability)).
- **Status bar** (bottom): left, the sync status (e.g. *"Synchronized: height 123456, 42
SPV-verified transactions. Real-time updates active."*); right, the **connection
indicator** (*connected host:port* / *connecting…* / *not connected* / *reconnecting…*).
**Click/tap the connection indicator to open the server settings.**
- All secondary screens (settings, details, wallet info, help) are **in-app overlays**, not
separate windows. Close them with the ✕ button, by clicking the dark backdrop, with `Esc`
(desktop) or Back (Android).
---
## 7. Receiving funds
The **Receive** tab always shows the **next unused receive address**, as text and as a QR
code (the QR encodes the bare address).
- **Copy** puts the address on the clipboard (*"Address copied to clipboard"*).
- Give a **fresh address to each payer**. After an address receives a payment and a sync
runs, the tab automatically advances to the next unused address. Reusing addresses is not
blocked, but it degrades your privacy by linking payments together.
- Incoming payments appear in History **at the next synchronization** — normally within
seconds, because the wallet subscribes to server push notifications. A payment shows as
*mempool* first, then with its block height once mined.
- The address format is fixed by the wallet's script type
([section 2.3](#23-script-type-address-format)); senders must support that format (in
particular, very old software may not send to `plm1…` bech32 addresses).
**Gap limit — the one receiving rule you must know.** The wallet derives addresses on
demand and, when restoring from seed, scans forward until it finds **20 consecutive unused
addresses** and then stops. Consequence: if you hand out many addresses that never receive
funds, do not skip ahead by more than 20 — a payment to address #30, with #10#29 unused,
would **not be found after a restore from seed**. Under normal use (addresses handed out
sequentially, most getting used) this never triggers. The limit (20) is stored per wallet
and is not editable in the GUI.
---
## 8. Sending funds
Prerequisites: the wallet must be **connected and synchronized** (otherwise: *"Connect to
the server and synchronize before sending"* / *"Synchronize before sending"*), and the
wallet must be spendable (not watch-only) to actually broadcast.
### 8.1 The form
- **From contacts** — optional dropdown that fills the recipient address from your address
book.
- **Recipient address** — validated when you prepare: an address of the wrong network or
with a typo is rejected (checksums make silent typos virtually impossible).
- **Amount** — interpreted in the **currently selected display unit** (PLM by default — check
the unit in Settings before typing!). Rejected if negative, malformed, or more precise
than 1 satoshi (0.00000001 PLM).
- **Send all** — checkbox; disables the amount field and sends the entire spendable balance,
**with the fee deducted from the amount** (the recipient receives balance fee).
- **Fee (sat/vB)** — a single manually-entered fee rate. **Default: 1 sat/vB.** There are no
presets and no automatic fee estimation. On the Palladium chain, blocks are rarely full,
so 1 sat/vB normally confirms in the next few blocks; raise it if a transaction lingers in
the mempool. Must be a number greater than zero.
- **QR scan** (Android only) — camera button; reads a plain address or a
`palladium:` payment URI (the address part is used).
### 8.2 Prepare, then confirm
Sending is a **two-step** operation:
1. **Prepare transaction** — builds and signs the transaction locally, without sending
anything. The summary card shows the txid (truncated), the **exact fee**, and the virtual
size, e.g. `txid 4f3a…| fee 0.00000141 PLM (141 vB)`. Nothing has left your wallet yet;
you can change the fields and prepare again, or simply navigate away to abandon it.
2. **CONFIRM AND BROADCAST** — transmits the prepared transaction to the server. Success
shows the full txid; the form clears and a re-sync starts. After broadcasting, **a
transaction cannot be cancelled** — it is in the network's mempool and will confirm.
### 8.3 How the transaction is built (facts you may need)
- **Coin selection is automatic** (there is no coin control). The wallet spends from its
spendable UTXOs and sends any **change back to its own internal change chain** — this is
why, in transaction details, one output to an address you don't recognize as "yours" may
still be marked as yours: it is your change.
- All transactions **signal RBF** (BIP125) and use transaction version 2. The wallet does
not offer fee bumping; if you underpay the fee, wait — with RBF signaled, other wallets
could in principle replace it, but this wallet has no UI for that.
- **Dust change is absorbed into the fee**: if the change output would be uneconomically
small, it is added to the fee instead of creating a dust output. The fee shown in the
summary already accounts for this — always check the summary fee before confirming.
- A prepared transaction is fully verified against standardness policy before it can be
confirmed; a policy failure surfaces as *"Invalid transaction: …"* instead of a broadcast.
### 8.4 Why "insufficient funds" can appear despite a visible balance
The error message itemizes the reasons. Funds may be temporarily unspendable because they
are:
- **in the mempool** (unconfirmed — never spendable),
- **recently confirmed** (fewer than 6 confirmations),
- **immature coinbase** (mining rewards younger than 121 blocks).
Wait for confirmations and retry. Also remember that the fee itself must fit: sending your
exact full balance fails unless you use **Send all**.
### 8.5 Watch-only wallets and offline signing
A watch-only (xpub) wallet in the **GUI** can prepare a transaction to preview the fee and
size — the summary is tagged *"unsigned (watch-only)"* — but the confirm button stays
disabled: **the GUI cannot export the unsigned transaction**. To actually use the
watch-only + offline-signing workflow, use the **CLI**, whose `send` command prints an
**unsigned PSBT (base64)** for a watch-only wallet
([section 17](#17-command-line-interface-cli)). Sign that PSBT on the offline machine with
software of your choice, then broadcast the finalized transaction.
### 8.6 The Donate tab (Help → Donate)
The Help overlay includes a donation form (developer address
`plm1qdq3gu2zvg9lyr8gxd6yln4wavc5tlp8prmvfay`) using the same prepare/confirm flow and the
fee rate from the Send tab (falling back to 1 sat/vB if invalid). Entirely optional.
---
## 9. Transaction history and details
The **History** tab lists all wallet transactions, newest first; unconfirmed ones are
labeled **mempool** and sorted on top, confirmed ones show their **block height**. The
amount is the transaction's net effect on your wallet (`+` incoming).
**Double-click** (desktop) or **tap** (Android) a row to open the full details. This
requires a live server connection (details are fetched on demand): otherwise *"Connect to
the server to view transaction details."*
The details overlay shows:
- **Overview** — status (*"N confirmations (block H)"* or *"0 confirmations · in
mempool"*), local date/time, counterparty addresses, and the signed net amount. Mining
rewards show *"Newly generated (mining)"* as the sender.
- **Amounts & fees** — the absolute fee, your net amount, and the fee rate in sat/vB.
- **Technical details** — full transaction ID, total size, virtual size, **Replaceable
(RBF)** yes/no, version, locktime.
- **Inputs** — each spent outpoint with its address and amount; your own inputs are
highlighted. Coinbase inputs display the miner's **scriptSig message** when it is readable
text.
- **Outputs** — each output with index, address and amount; your own outputs (including
change) are highlighted. **OP_RETURN** outputs display their decoded text message when the
payload is valid readable text; non-standard outputs show their script type.
Notes and limits:
- Confirmation counts advance automatically as blocks arrive (real-time header
subscription).
- Every **confirmed** transaction in your history has been verified with a Merkle proof
during sync. **Unconfirmed** entries are the server's word only — do not treat a mempool
payment as received until it confirms.
- There are no per-transaction labels and no block-explorer links in this version. The
public explorer is at `https://explorer.palladium-coin.com/` — paste the txid there
manually if needed.
---
## 10. The Addresses tab
A complete table of every derived address: **type** (receive/change), **index**,
**address**, **balance**, and **transaction count**. Before the first sync it shows the
first 10 receive addresses with no data.
Tap (or right-click) a row to open the **address details** overlay:
- the full address and its **derivation path** (e.g. `m/84'/746'/0'/0/3`),
- the **public key** (hex),
- the **private key (WIF)**, hidden behind a **"Show"** button. For an encrypted wallet,
revealing it requires re-entering the wallet password (*"Enter the wallet password to
reveal the private key."*); for an unencrypted wallet it is shown directly. **Never
reveal a private key with anyone watching your screen** — one key controls that one
address's funds. Watch-only wallets have no private keys to show.
`change` addresses are where your own transactions send their change
([section 8.3](#83-how-the-transaction-is-built-facts-you-may-need)) — seeing balance on
them is normal.
---
## 11. Contacts
A simple per-wallet address book, on its own tab:
- **Add**: name + address, both required (whitespace is trimmed). Note: the address format
is **not validated when saving** — it is only validated when you actually send to it. Copy
addresses carefully.
- **Remove selected**: deletes the highlighted contact. There is no edit function — remove
and re-add.
- Contacts are used by the **From contacts** dropdown on the Send tab.
- Contacts are stored **inside the wallet file**. They travel with a file backup, but are
**not** recoverable from the seed phrase ([section 15](#15-backup-and-recovery)).
---
## 12. Connection, servers and certificate pinning
### 12.1 Server settings
Open by clicking the **connection indicator** in the status bar (or Settings → *Indexing
server…*, or menu *Network* on desktop). Fields:
- **Host** and **Port** — defaults: port **50001** (plain TCP) or **50002** (SSL). Editing
one of the known ports auto-toggles the SSL switch to match, and toggling SSL swaps the
port. Note these are the *indexing server* ports — not the Palladium node's P2P port
(2333), which this wallet never uses.
- **Use SSL** — default **on**. Strongly recommended: without SSL, traffic (including which
addresses you query) is readable on the wire.
- **Known servers** list — click an entry to select it. The wallet ships with four bootstrap
mainnet servers and remembers any servers learned via discovery.
- **Discover servers (peers)** — asks the connected server for its known peers and adds new
ones to the list.
Connection behavior: the wallet tries your selected server first, then walks the rest of
the known list until one answers. The last working server is remembered and reused at next
launch. A 20-second keep-alive ping detects drops and reconnects automatically;
server push notifications trigger instant re-syncs when a new block or a relevant
transaction appears (*"Real-time updates active"*).
### 12.2 TLS certificate pinning (TOFU) — read before "fixing" a certificate error
SSL connections use **Trust On First Use**: the first time you connect to a server, its
certificate's fingerprint is saved (in `server-certs.json`). Every later connection must
present the **same** certificate. If it doesn't, the connection is refused with a message
stating that the TLS certificate of that server **has changed**.
This is a security feature, not a bug. A changed certificate means one of two things:
1. **Benign**: the server operator renewed/rotated the certificate (common, e.g. Let's
Encrypt renewals).
2. **Hostile**: someone is intercepting your connection (man-in-the-middle).
You cannot distinguish the two from the wallet alone. If the change is expected or you can
verify it out-of-band, clear the pins: menu **Network → Reset SSL certificates** (desktop),
the **Reset certs** button in server settings, or `reset-certs` in the CLI. This deletes
**all** pinned certificates for the network; the next connection re-pins whatever it sees.
If you have any reason to suspect interception, switch networks (e.g. off a public Wi-Fi)
or servers instead of resetting.
### 12.3 What synchronization actually does
Each sync: gets the chain tip → scans your receive and change address chains (gap limit
20) → downloads your transactions → **verifies a Merkle proof for every confirmed
transaction** against its block header → rebuilds your UTXO set and balances locally.
Verified data is cached in the wallet file, so later syncs only fetch what is new — even
after a restart. If a proof does not check out, the sync **aborts** with an explicit
"server is not trustworthy" error rather than showing you unverified data; switch servers.
If the server is overloaded (busy responses), the wallet retries automatically up to 8
times with increasing back-off — a large wallet's first sync may take a little while, but it
resumes from the cache instead of restarting.
---
## 13. Settings
The Settings overlay (gear icon / menu *Settings*) contains exactly two preferences, plus
the door to the server settings:
- **Language** — Italiano, English, Español, Français, Português, Deutsch. Applies
immediately. Default: English. (A few low-level error messages are currently shown in
Italian regardless of language.)
- **Amount unit** — how amounts are displayed *and interpreted when you type them*:
| Unit | In satoshi | Decimals shown |
|---|---|---|
| PLM (default) | 100,000,000 | 8 |
| mPLM | 100,000 | 5 |
| µPLM | 100 | 2 |
| sat | 1 | 0 |
⚠ The Send form's amount field uses this unit. If you switch to `sat` and forget, typing
`100` means 100 satoshi, not 100 PLM. Double-check the unit before sending.
Settings are saved globally (per data folder, shared by all wallets), not per wallet.
There is no theme selector (the app uses its built-in dark theme) and no fiat display.
---
## 14. Wallet Information
Menu **Wallet → Wallet Information** (desktop) or the equivalent button. Shows:
- **File name**, **network**, **wallet type** (*HD (BIP39 seed)* / *HD (imported xprv)* /
*Imported WIF key* / *Watch-only (xpub)*), **script type**, **derivation path**.
- **Extended public key (xpub)** — displayed as selectable text. This is how you *export*
the xpub: select and copy it. Give the xpub to another device/app to create a
**watch-only** copy of this wallet — it reveals your entire address history and balance to
whoever holds it (privacy risk), but can never spend.
- **Master fingerprint** — 4-byte identifier of the master key, used in PSBT workflows.
- **BIP39 passphrase** — shows only whether one is *set*; the passphrase itself is never
displayed anywhere.
- **Show seed** — reveals the mnemonic, after re-entering the wallet password (for
encrypted wallets). Use it to re-verify your paper backup. The on-screen warning is
literal: *never share these words; whoever holds them controls the funds.* Watch-only and
imported wallets have no seed to show.
---
## 15. Backup and recovery
### 15.1 What to back up
There are two complementary backups; know exactly what each one restores:
| Backup | Restores | Does NOT restore |
|---|---|---|
| **Seed words on paper** (+ passphrase if set, + script type) | All keys, addresses, balance and on-chain history, on any device, forever | Contacts; wallet name; settings |
| **The wallet file** (+ its password) | Everything, including contacts | — |
Recommended: **always** have the paper seed (it survives disk failure, device loss, and
software obsolescence); *additionally* copy the `.wallet.json` file if you care about your
contacts list. An encrypted wallet file is safe to store on ordinary media — but it is only
as strong as its password, and it is useless without it.
For a **watch-only** wallet, the file only restores the watching capability; the actual
keys live wherever you keep the corresponding seed/xprv — back *that* up.
For an **imported WIF** wallet, back up the WIF keys themselves; they are not derivable from
anything else.
### 15.2 Restoring, step by step
1. Install the wallet on the new device.
2. Wizard → **Restore from seed** → enter the words → enter the **same passphrase** (or
leave empty if none) → choose the **same script type** → set a (new) password.
3. Let it synchronize. Balance and history are rebuilt from the chain.
If the balance is zero: wrong passphrase, or wrong script type, or (rare) funds beyond the
gap limit ([section 7](#7-receiving-funds)). Nothing is lost — the coins are still on the
chain; you are simply looking at the wrong derived wallet. Retry with the right parameters.
Restoring from a **file copy** instead: place the `.wallet.json` into
`<data folder>/mainnet/wallets/` and open it with its password.
### 15.3 Unrecoverable situations — be aware
- Seed lost **and** wallet file (or its password) lost → funds gone. No exceptions.
- Passphrase forgotten → funds gone, even with the 12 words in hand.
- Encrypted file's password forgotten, no seed backup → funds gone (the encryption has no
backdoor).
---
## 16. Security model and known limitations
A condensed version of the project's published threat model (`SECURITY.md`).
**The wallet protects you against:**
- Theft of the wallet file at rest (AES-256-GCM + PBKDF2, [section 2.5](#25-the-wallet-file-and-its-encryption)) —
provided you set a good password.
- A **lying indexing server**: it cannot fabricate a confirmed transaction or forge a
payment to a wrong address, because every confirmed transaction must carry a valid Merkle
proof, which the wallet checks itself.
- **Silent TLS interception after first contact** (certificate pinning,
[section 12.2](#122-tls-certificate-pinning-tofu--read-before-fixing-a-certificate-error)).
**The server can still (semi-trusted component):**
- lie about **unconfirmed** transactions (which is why they are marked not-spendable and
should not be trusted until confirmed);
- **withhold** information — hide transactions, delay new blocks, refuse to broadcast;
- **observe** which addresses belong to you (no Tor/proxy support in this version; using
your own indexing server is the strongest mitigation).
**The wallet cannot protect you against:**
- malware on your device (anything that can read process memory can take the keys once the
wallet is unlocked);
- an attacker holding both your wallet file **and** its password (or your seed);
- yourself: there are no confirmation "cool-downs", no address whitelists, and broadcast is
irreversible.
**Honest limitations of this version** (also listed in [section 1](#1-what-palladium-wallet-is-and-is-not)):
single server connection (no pooling), no coin control, no fee estimation, no RBF bumping,
no hardware wallets, no Tor, GUI mainnet-only, and PSBT export only via the CLI.
---
## 17. Command-line interface (CLI)
The CLI (`src/Cli`) runs on the same core as the GUI: same wallet files, same validation,
same security. It is desktop-only and its console output is currently in Italian. Run it
as `dotnet run --project src/Cli -- <command>` from the repository (or the published `Cli`
binary); with no arguments it prints usage.
**Shared conventions.** Default wallet file:
`~/.palladium-wallet/<network>/wallets/default.wallet.json` — override with `--file PATH`.
Default network mainnet — override with `--net testnet|regtest` (the CLI is the only way to
use test networks). `--password P` both decrypts an existing file and encrypts on save;
**omitting it on `create`/`restore` writes the seed in plaintext** (a warning is printed).
`--kind legacy|wrapped|segwit` selects the script type (default Native SegWit — note that
only the literal values `legacy` and `wrapped` switch away from the default). Errors print
to stderr and set exit code 1.
### Wallet commands
```
newseed [--words 24]
```
Prints a fresh mnemonic (12 words unless `--words 24`) and writes nothing to disk.
```
create [--words 24] [--kind K] [--net N] [--passphrase W] [--password P] [--file PATH] [--path m/...]
```
Generates a mnemonic, prints it once, saves the wallet, prints the file path and the first
receive address. `--path` overrides the derivation path (advanced; you must remember it to
restore).
```
restore "<mnemonic>" [same options as create]
```
Restores from an existing mnemonic (quoted, words space-separated).
```
restore-xpub <slip132-key> [--net N] [--password P] [--file PATH]
```
Creates a **watch-only** wallet from an xpub/ypub/zpub; the script type is inferred from
the key prefix.
```
info [--net N] [--password P] [--file PATH] [--addresses]
```
Prints the wallet's identity and, if it has ever synced, the balance breakdown (spendable /
maturing / pending), tip height, transaction count and next receive address.
`--addresses` adds the per-address list with balances.
```
addresses "<mnemonic>" [--kind K] [--net N] [--count N] [--passphrase W] [--path m/...]
```
Stateless tool: derives and prints receive (default 5) and change addresses from a mnemonic
without creating any file. Useful to check "which script type was this seed used with"
before restoring.
### Network commands
```
sync [--server host[:port]] [--ssl] [--net N] [--password P] [--file PATH]
```
Connects (default: the first known server; port defaults to 50001, or 50002 with `--ssl`),
synchronizes with full Merkle verification, persists the cache into the wallet file, and
prints balance and history. Transactions the server reported but that are unconfirmed are
marked as unverified.
```
send --to ADDRESS (--amount X | --all) [--feerate R] [--broadcast]
[--server ...] [--ssl] [--net N] [--password P] [--file PATH]
```
Builds a transaction from the synced cache (`sync` must have been run first). `--amount` is
in **PLM**; `--all` sends everything minus the fee. `--feerate` is in sat/vB, **default 1**.
Behavior by wallet type and flags:
- **Watch-only** wallet → prints the **unsigned PSBT in base64** and stops. This is the
air-gapped workflow: carry the PSBT to the offline signer, sign it there, broadcast the
final transaction by any means.
- Spendable wallet, **without `--broadcast`** → signs and prints the raw transaction hex,
explicitly marked as *not transmitted*. Nothing is sent — a dry run you can inspect or
broadcast elsewhere.
- Spendable wallet, **with `--broadcast`** → signs, transmits, prints the txid. Irreversible
from this point.
```
servers [--discover] [--server ...] [--ssl] [--net N]
```
Lists known servers; with `--discover`, connects and asks the server for its peers, adding
new ones.
```
reset-certs [--net N]
```
Deletes all pinned TLS certificates for the network (the CLI counterpart of *Network →
Reset SSL certificates* — see
[section 12.2](#122-tls-certificate-pinning-tofu--read-before-fixing-a-certificate-error)).
---
## 18. Troubleshooting and error messages
| Symptom / message | Meaning | What to do |
|---|---|---|
| *Wrong password.* | The password does not decrypt the file (or the file was tampered with — the two are indistinguishable by design). | Retry; check keyboard layout/caps lock. If truly lost, restore from seed ([15.2](#152-restoring-step-by-step)). |
| *Wallet already open in another instance of the application.* | Another running process holds this wallet's lock. | Close the other instance (GUI or CLI). |
| *The TLS certificate of host:port has changed…* | Certificate pin mismatch — renewal or interception. | Read [12.2](#122-tls-certificate-pinning-tofu--read-before-fixing-a-certificate-error) **before** resetting certificates. |
| Sync aborts, "server is not trustworthy" | A Merkle proof failed: the server served data it cannot prove. | Switch to another server. Your local data is intact; the wallet refused the bad data. |
| *Insufficient funds* with a non-zero balance | Funds are unconfirmed, under 6 confirmations, or immature coinbase; or the fee doesn't fit. | See [8.4](#84-why-insufficient-funds-can-appear-despite-a-visible-balance). Wait, or use Send all. |
| Restored wallet shows zero balance | Wrong passphrase, wrong script type, or funds beyond the gap limit. | See [15.2](#152-restoring-step-by-step) — the coins are not lost. |
| *Invalid mnemonic (wrong words or checksum)* | A word is misspelled, not on the BIP39 list, or the checksum fails. | Compare against your paper backup, word by word. |
| *Extended key not recognised for this network.* | The pasted xpub/xprv is not a SLIP-132 key of this network. | Check the key's prefix and the source wallet's export format. |
| *A wallet with this name already exists.* | Filename collision in the wallets folder. | Pick another name, or leave blank for an automatic one. |
| Payment sent to me doesn't appear | Not yet synced/connected, or sender hasn't broadcast. | Check the connection indicator; mempool entries appear within seconds of broadcast when connected. |
| Update prompt at startup (*"Update available"*) | A newer GitHub release exists (checked once at startup, silently skipped offline). | *Download* opens the release page; *Dismiss* continues. Never enter your seed into anything but the wallet itself. |
| Android: update apk refuses to install | Signature mismatch between builds. | Back up the seed **before** uninstalling; see [3.2](#32-android). |
| First sync is slow / server busy errors | Server throttling; the wallet retries automatically (up to 8 attempts, growing back-off). | Wait; progress is cached, so restarting resumes rather than repeats. |
---
## 19. Glossary
- **SPV** — Simplified Payment Verification: validating that transactions are included in
blocks via Merkle proofs, without running a full node.
- **Seed / mnemonic** — the 12 (or 24) BIP39 words that deterministically generate every key
in the wallet.
- **Passphrase (BIP39)** — optional extra secret combined with the seed; a different
passphrase yields a different wallet.
- **Script type** — the address format standard (Legacy/BIP44, Wrapped SegWit/BIP49, Native
SegWit/BIP84, Taproot/BIP86).
- **xpub / xprv** — extended public/private key of the wallet's account; the xpub watches,
the xprv spends. SLIP-132 variants: ypub/zpub etc.
- **WIF** — Wallet Import Format: a single address's private key as text.
- **UTXO** — Unspent Transaction Output: a discrete "coin" the wallet can spend.
- **Change** — the portion of spent UTXOs returned to your own (internal) addresses.
- **Coinbase** — the transaction paying the miner of a block; spendable after 121
confirmations.
- **Mempool** — the set of broadcast-but-unconfirmed transactions.
- **Gap limit** — how many consecutive unused addresses the wallet scans past before
stopping (20).
- **PSBT** — Partially Signed Bitcoin Transaction: the standard interchange format for
signing a transaction on a device other than the one that built it.
- **RBF** — Replace-By-Fee: a signal that a transaction may be replaced by a higher-fee
version while unconfirmed.
- **TOFU** — Trust On First Use: pinning a server's TLS certificate at first contact and
refusing silent changes afterwards.
- **Watch-only** — a wallet holding only public keys: full visibility, zero spending
ability.
---
*This guide documents observed behavior of the software and is kept in sync with the code.
For the formal threat model see [SECURITY.md](SECURITY.md); for per-release changes see
[CHANGELOG.md](CHANGELOG.md).*