Wallet & Backup Security

Last updated: 2026-05-10. Single source of truth: docs/wallet-and-backup-security.md in the repo. This page mirrors the same content; if the two diverge the markdown wins.

Your keys, your coins. Your loss if you lose them.
If you lose both the 12-word seed phrase AND the wallet file, your SOST stay on the chain forever but no one can move them. Back up both.

TL;DR

ConceptWhat it means
SOST coinsLive on-chain forever, public and permanent
Private keysLive ONLY in your local encrypted wallet file
RecoveryThe 12-word BIP39 phrase regenerates the keys
ResponsibilityYou custody the keys yourself. Your keys, your coins. Your loss if you lose them.

1. On-chain vs local

On-chain (the SOST blockchain)

Stored permanently on every node and indestructible while the network exists:

Anyone running a SOST node can read all of this. It is public.

Local only (your wallet file)

Stored exclusively on your machine:

If you lose the file AND the 12 words, you cannot move your SOST anymore. The SOST themselves remain in the chain forever, just unreachable.

Three storage layers, three encryption models

There is no single answer to "is my wallet encrypted?" — SOST has three distinct files and they behave differently. Mixing them up is the most common cause of accidental key exposure:

FileEncryptionWhere it should live
CLI active wallet (wallet.json)None — plain JSONLocal disk only. Never upload to cloud or share.
CLI encrypted backup (*.enc)scrypt N=32768 + AES-256-GCMAnywhere — USB, cloud, email, all safe
Web wallet entry in browser localStoragePBKDF2 100k + AES-256-GCM under passwordBrowser holds it; the Download Encrypted Backup button exports the same encrypted JSON, also cloud-safe

Why the CLI active wallet is plaintext: the miner and RPC need to sign blocks and transactions without prompting for a password every time. A prompt would block 24/7 mining. The encrypted backup format (wallet-export --encrypted) and the web wallet's encrypted localStorage are the layers that protect keys at rest; the active CLI wallet trades that protection for unattended operation. Only run the CLI miner on a machine you physically control.

2. Why aren't private keys stored automatically?

  1. The chain is public. Putting keys on-chain would publish them to every node — anyone could move your SOST instantly.
  2. Chain bloat. Storing wallet state for every user would balloon the chain. Lightweight nodes and bootstraps would become impractical.
  3. Trustless decentralisation. A core PoW principle is no third-party trust. If a foundation could "recover" your wallet, it could also seize it or be coerced. SOST avoids this by design.

3. Backup strategy — two layers

Layer 1 — The 12 BIP39 words on paper

Your master backup. Generated when you ran sost-cli hd create. Keep them written on paper, in order, in a secure physical location. Not digital, not photographed, not typed into a website you don't fully control.

Layer 2 — Wallet file backup

A convenience layer on top of the 12 words. What you copy depends on which wallet you're backing up:

CLI wallet (wallet.json, phase2-miner-wallet.json, …) — the active file is plain JSON, so a raw copy is not safe to upload to cloud or share. For an off-site copy, export an encrypted snapshot first:

# Encrypted export (cloud-safe). Prompts for a passphrase you invent
# now; remember it — it cannot be recovered. KDF: scrypt N=32768.
# Cipher: AES-256-GCM.
./sost-cli --wallet wallet.json wallet-export \
    --encrypted --output wallet-backup-$(date +%Y%m%d).enc

The .enc is what you upload to USB, Drive, email, or anywhere else. Never upload the raw wallet.json — it contains the private keys in plaintext. Restore later with wallet-import --encrypted; see section 9.

For an in-house copy on a machine you fully control (USB in a drawer, another machine on your LAN), copying the raw file is fine and faster:

cp wallet.json /path/to/usb/wallet-backup-$(date +%Y%m%d).json
chmod 600 /path/to/usb/wallet-backup-*.json

Web wallet (sost-wallet.html) — use Download Encrypted Backup on the seed screen and again from Settings. That file is already PBKDF2 + AES-256-GCM encrypted with your wallet password and is safe for cloud / email / USB without further wrapping.

Recovery from either is faster than the 12-word path (~1 minute vs 30–60 minutes), and preserves labels and the local UTXO index cache.

4. How to recover a wallet

Fast path (you have a wallet JSON backup)

# 1. Copy the backup to the new machine
cp wallet-backup-20260509.json ~/SOST/sostcore/sost-core/build/wallet.json
chmod 600 wallet.json

# 2. Verify the address list is what you expect
./sost-cli --wallet wallet.json listaddresses

# 3. Relaunch the miner with the same flags as before
./sost-miner --wallet wallet.json --mining-key-label phase2-miner ...

Web wallet equivalent: Import → upload encrypted backup → enter password.

Total time: about 1 minute.

Deep path (you only have the 12 words)

# 1. Get the sost-cli binary on the new machine.

# 2. Create an empty wallet shell.
./sost-cli --wallet wallet.json newwallet

# 3. Restore from seed phrase. The CLI prompts for the 12 words.
./sost-cli --wallet wallet.json hd restore

# 4. Recreate any labels you used.
./sost-cli --wallet wallet.json getnewaddress phase2-miner

# 5. Confirm the address shows the same as before.
./sost-cli --wallet wallet.json listaddresses

# 6. Sync UTXOs from the node.
./sost-cli --wallet wallet.json info

Web wallet equivalent: Import → "From 12-word seed phrase" → paste the words → set a new password.

Total time: 30–60 minutes including any chain rescan. The result is identical — the same address, the same balance — because BIP44 derivation is deterministic.

5. The 1000-block coinbase maturity rule

When you mine a block the reward is immature for 1000 confirmations (~7 days at the 600-second target). Standard PoW protection: if a deep reorg invalidates a recent block, its reward becomes invalid too. Symptom you will see:

SPENDABLE:  0 SOST
IMMATURE:  31.40400000 SOST  (4 coinbase UTXOs)

Cure: time. Each new block adds 1 confirmation. After 1000 blocks on top of yours, the SOST become spendable.

6. Mining with the same address on multiple machines

Common assumption: "if I run two miners on the same wallet label, my hashrate adds up."

Actual behaviour: it does not add up cleanly. Both miners build very similar block templates (same coinbase address, same prev hash, same timestamp window) and explore overlapping nonce space. Whichever miner finds a valid nonce first wins; the other's recent work for that template is discarded once the next block lands. Two solo miners on the same label are not equivalent to one miner with double the hashrate.

Recommended approach: one address per machine, consolidate later

# On machine 1
./sost-cli --wallet wallet.json getnewaddress miner-rig-1
./sost-miner --wallet wallet.json --mining-key-label miner-rig-1 ...

# On machine 2
./sost-cli --wallet wallet.json getnewaddress miner-rig-2
./sost-miner --wallet wallet.json --mining-key-label miner-rig-2 ...

# Periodically consolidate
./sost-cli --wallet wallet.json --from-label miner-rig-2 \
    send sost1<consolidation-address> <amount>

This way each machine owns its own address and explores nonce space without colliding. When a stratum-style pool ships in the future, it will distribute disjoint nonce ranges to many workers signing under one shared payout address; that is the only architecture in which "same address, many machines" cleanly adds hashrate.

7. Common questions

I lost my computer. Are my SOST gone?

No. Your SOST are in the chain. If you have either the 12-word seed phrase OR a wallet JSON backup, you can recover them on any machine.

I lost both the 12 words and the wallet file.

Your SOST are unreachable. They remain in the chain forever, visible to everyone, but no one can spend them. Permanent.

Someone saw my 12 words.

They can move your SOST. Immediately: generate a new wallet, send your entire balance to the new address before the attacker does, then discard the compromised wallet forever.

My wallet JSON is on Google Drive. Is that safe?

Depends on which JSON. If it is the CLI active wallet (wallet.json / phase2-miner-wallet.json), no — that file is plain JSON and contains the private keys readable by anyone who downloads it. Delete it from cloud and replace with an encrypted export (wallet-export --encrypted.enc).

If it is a .enc (CLI) or a web wallet Download Encrypted Backup export, yes — both are AES-256-GCM encrypted under a key derived from your passphrase / password (scrypt or PBKDF2). With a strong passphrase (20+ random chars) the file is computationally infeasible to crack. With a weak one ("password123") it can be brute-forced. Use a password manager and a long random passphrase.

Can the SOST developers recover my wallet?

No. They have no privileged access. If they could, the system would not be trustless.

8. Best-practices checklist

9. Encrypted backup — commands cheat sheet

The CLI ships three commands for moving keys around. Verified in src/sost-cli.cpp:

# Export an encrypted backup. Prompts for a passphrase you invent NOW,
# repeated for confirmation. Min 8 chars. KDF: scrypt N=32768, r=8, p=1.
# Cipher: AES-256-GCM. Output is the .enc file, cloud-safe.
./sost-cli --wallet wallet.json wallet-export \
    --encrypted --output wallet-backup-$(date +%Y%m%d).enc

# Import an encrypted backup into a fresh active wallet. Destination
# wallet must already exist — create it first with newwallet.
./sost-cli --wallet /tmp/restored.json newwallet
./sost-cli --wallet /tmp/restored.json wallet-import \
    --encrypted --input wallet-backup-20260510.enc

# Print one private key from the active wallet (DANGER — the key is
# already plaintext on disk; this just prints it to your terminal).
./sost-cli --wallet wallet.json dumpprivkey sost1...

There are three different secrets in this system; do not mix them up:

SecretWhere it livesWho creates itRecoverable?
12-word BIP39 seedPaper, in your handThe CLI / web wallet at HD wallet creationNo — written once at creation, never shown again
.enc backup passphraseYour head, then on paperYou invent it at wallet-export timeNo — if you forget it, the .enc is unrecoverable
Web wallet passwordYour headYou invent it at wallet creationNo — wraps the entire web-wallet localStorage entry

The .enc passphrase and the 12-word seed are independent. You can write both on the same paper (label them) but they are not the same: the seed reconstructs HD-derived keys mathematically; the passphrase decrypts a snapshot of all keys (HD-derived AND non-HD).

10. The CLI mining label is freeform

There is no fixed list of valid mining labels and no "phase2-miner" prefix requirement. Verified in src/sost-cli.cpp:1269 and src/wallet.cpp:84: getnewaddress <label> takes the label as a plain string with no validation, and find_key_by_label is a direct string compare.

The only rule: the label you pass to getnewaddress <label> must be character-for-character identical to the one you pass to --mining-key-label <label> when launching the miner against the same wallet. Pick whatever you remember.

# Example A — label "phase2-miner"
./sost-cli --wallet phase2-miner-wallet.json newwallet
./sost-cli --wallet phase2-miner-wallet.json getnewaddress phase2-miner
./sost-miner --wallet phase2-miner-wallet.json \
             --mining-key-label phase2-miner [...]

# Example B — label "home-rig"
./sost-cli --wallet home-rig-wallet.json newwallet
./sost-cli --wallet home-rig-wallet.json getnewaddress home-rig
./sost-miner --wallet home-rig-wallet.json \
             --mining-key-label home-rig [...]

The miner echoes the chosen label at startup so you can confirm:

SbPoW signing key: label='<your-label>'
Miner address: sost1...   (derived from wallet key)

11. HD seed vs getnewaddress — recovery semantics

Two CLI commands generate a key, but they have different recovery guarantees, and confusing them is the most common way to lose seed-based recovery on a mining wallet:

CommandWhat it generatesLabelRecoverable from 12 words?
sost-cli hd createOne address derived from a fresh BIP39 12-word seedhd-seedYeshd restore regenerates it byte-for-byte
sost-cli getnewaddress <label>One address from a fresh random secp256k1 key<label>No — the random key is NOT derived from any seed; it only lives inside wallet.json

If you create an HD wallet and then run getnewaddress phase2-miner and mine to that label, the mined coinbase rewards land on a non-HD address. The 12 words on paper will not recover them. Your only backup for that address is wallet.json (or its .enc export). Lose both → lose those SOST.

To keep "12 words → full recovery" as your safety net, mine to the HD-derived address directly:

./sost-cli --wallet wallet.json hd create
# (write the 12 words on paper exactly once when they print)

./sost-cli --wallet wallet.json listaddresses
# look for the entry tagged [hd-seed]; that is the address you mine to

./sost-miner --wallet wallet.json \
             --mining-key-label hd-seed [...]

The web wallet (sost-wallet.html) avoids this trap entirely: each wallet has exactly one address, derived from the displayed 12 words. There is no equivalent of getnewaddress that would add a non-HD key.

12. Local cache vs chain truth

When you run info or restore a .enc backup, you may see two different SOST figures and they are both correct. Example after wallet-import --encrypted:

Wallet imported from: backup.enc
  Balance:   11.989... SOST          ← local UTXO cache
$ ./sost-cli --wallet /tmp/restored.json listaddresses
  sost1ca9097d...  1548.510... SOST  (chain truth)

This is expected behaviour, not a bug, and does not affect what you can spend. Why:

So: a .enc whose import shows a small Balance: is still the right backup. Verify by running listaddresses after import; the (chain truth) line is the one that matches the chain.

13. Transaction and block limits

Hard ceilings, enforced by the consensus rules. A block that violates any of these is rejected by every node on the network:

LimitConstantValue
Max transaction sizeMAX_TX_BYTES_CONSENSUS100 KB
Max block sizeMAX_BLOCK_BYTES_CONSENSUS1 MB
Max transactions per blockMAX_BLOCK_TXS_CONSENSUS65 536

Soft (policy) ceilings, enforced by the local mempool and the miner's template builder. A transaction that violates a policy ceiling is not relayed by peer nodes and is not picked up by the miner template:

LimitConstantValue
Standard transaction size (mempool relay)MAX_TX_BYTES_STANDARD16 KB
Default transactions per block (miner template)MAX_BLOCK_TX_COUNT4 096
Target block spacingTARGET_SPACING600 s

Practical throughput, given a 600-second block:

Transaction shapeTypical bytesTX/block (1 MB)TX/hour
Simple payment, 1–2 inputs, 2 outputs, no capsule~250–400 B2 500 – 4 00015 000 – 24 000
Payment with small capsule (Open Note ≤80 B)~500–700 B1 400 – 2 0008 400 – 12 000
Heavy capsule send (39 inputs + capsule)~5 850 B~170~1 020
Transaction at the 16 KB policy limit~16 000 B62372

What happens at congestion: the mempool holds the surplus until later blocks pick it up. The miner template orders candidates by fee rate (stocks per byte) and includes the highest-paying subset that fits under the size and count caps. Low-fee transactions wait — they do not vanish. The wallet's auto-split logic exists precisely so a single payment that would build to over 16 KB is split into smaller chunks that pass the relay filter.

The web wallet does not need to know any of these numbers explicitly — the fee-pass converging loop and the auto-split helper handle the limits automatically. They are documented here so you can recognise the symptoms when they appear (e.g. a tx size 22369 > standard limit 16000 rejection).