Download Ubuntu
Choose one laptop as the online computer and Bitcoin node. On the computer you are currently using, download the Ubuntu 26.04 AMD64 desktop image. Do not write it to the USB drive yet.
Operational guide · Version 1.0
A methodical, visual adaptation of Yeti 2.0 for establishing a 3-of-7 Bitcoin Core multisig arrangement with an air-gapped signing computer.
Mistakes can permanently lose funds or expose keys. Rehearse with insignificant value, independently verify commands and software versions, and stop whenever an expected result differs.
Security boundary: this page never asks for or accepts keys, seeds, descriptors, wallet files, addresses or PSBTs. Copy buttons only copy public commands shown on screen.
Adapted in original wording from Yeti 2.0. Compare the pinned guide content. Ice Core has not independently audited the Yeti 2.0 security design.
Begin with A1 ↓Stage A
Choose one laptop as the online computer and Bitcoin node. On the computer you are currently using, download the Ubuntu 26.04 AMD64 desktop image. Do not write it to the USB drive yet.
Use Terminal
Run these commands in order:
Hover over any part of the command for an explanation
cdChanges Terminal’s current working directory to the location that follows. ~/DownloadsThe Downloads folder inside the current user’s home directory; subsequent downloads will be saved here.
wgetDownloads a file from a web address to the current directory. https://releases.ubuntu.com/26.04/SHA256SUMSThe remote HTTPS address from which the named file is downloaded.
wgetDownloads a file from a web address to the current directory. https://releases.ubuntu.com/26.04/SHA256SUMS.gpgThe remote HTTPS address from which the named file is downloaded.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --keyid-formatTells GPG how to display key identifiers in its output. longRequests full-length key identifiers, making the signing keys easier to distinguish. --keyserverTells GPG that the next value identifies the public key server to contact. hkp://keyserver.ubuntu.comIdentifies Ubuntu’s public key server using the HKP keyserver protocol. GPG contacts it to retrieve the specified public signing keys. --recv-keysTells GPG to download and import each public key identifier that follows. 0x46181433FBB754510x46181433FBB75451 identifies one of the Ubuntu signing public keys named by the Yeti 2.0 verification procedure. 0xD94AA3F0EFE210920xD94AA3F0EFE21092 identifies one of the Ubuntu signing public keys named by the Yeti 2.0 verification procedure.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --keyid-formatTells GPG how to display key identifiers in its output. longRequests full-length key identifiers, making the signing keys easier to distinguish. --verifyChecks that a signature was made by a recognised key and that the signed file has not changed. SHA256SUMS.gpgThis is Ubuntu’s detached GPG signature for SHA256SUMS. GPG checks it using the imported Ubuntu signing keys. SHA256SUMSThis is Ubuntu’s downloaded checksum list. The verification command authenticates it, then the hash-checking command reads it to find the expected SHA-256 fingerprint for the ISO.
sha256sumRuns Linux’s SHA-256 checksum program to compare the downloaded ISO with Ubuntu’s authenticated checksum list. -cChecks local files against the fingerprints recorded in the following checksum list. SHA256SUMSThis is Ubuntu’s downloaded checksum list. The verification command authenticates it, then the hash-checking command reads it to find the expected SHA-256 fingerprint for the ISO. 2>&1Combines error output with normal output so grep can inspect every checksum result. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. grepFinds text matching the supplied pattern. OKKeeps only checksum-result lines containing OK, which marks a successful match.Use Terminal
Need Homebrew? Install it only from brew.sh.
Run these commands in order:
Hover over any part of the command for an explanation
cdChanges Terminal’s current working directory to the location that follows. ~/DownloadsThe Downloads folder inside the current user’s home directory; subsequent downloads will be saved here.
brewRuns Homebrew, the package manager used here to install GnuPG on macOS. installTells Homebrew to install the package named next. gnupgNames the GnuPG package, which provides the gpg program required to verify Ubuntu’s signed checksum list.
curlcurl downloads files from web addresses. The .exe form explicitly invokes the Windows curl executable. -OSaves the downloaded file using the filename at the end of its web address. https://releases.ubuntu.com/26.04/SHA256SUMShttps://releases.ubuntu.com/26.04/SHA256SUMS is the local filename wget will use for the downloaded file. The preceding -O option assigns this name instead of retaining the filename from the web address.
curlcurl downloads files from web addresses. The .exe form explicitly invokes the Windows curl executable. -OSaves the downloaded file using the filename at the end of its web address. https://releases.ubuntu.com/26.04/SHA256SUMS.gpghttps://releases.ubuntu.com/26.04/SHA256SUMS.gpg is the local filename wget will use for the downloaded file. The preceding -O option assigns this name instead of retaining the filename from the web address.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --keyid-formatTells GPG how to display key identifiers in its output. longRequests full-length key identifiers, making the signing keys easier to distinguish. --keyserverTells GPG that the next value identifies the public key server to contact. hkp://keyserver.ubuntu.comIdentifies Ubuntu’s public key server using the HKP keyserver protocol. GPG contacts it to retrieve the specified public signing keys. --recv-keysTells GPG to download and import each public key identifier that follows. 0x46181433FBB754510x46181433FBB75451 identifies one of the Ubuntu signing public keys named by the Yeti 2.0 verification procedure. 0xD94AA3F0EFE210920xD94AA3F0EFE21092 identifies one of the Ubuntu signing public keys named by the Yeti 2.0 verification procedure.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --keyid-formatTells GPG how to display key identifiers in its output. longRequests full-length key identifiers, making the signing keys easier to distinguish. --verifyChecks that a signature was made by a recognised key and that the signed file has not changed. SHA256SUMS.gpgThis is Ubuntu’s detached GPG signature for SHA256SUMS. GPG checks it using the imported Ubuntu signing keys. SHA256SUMSThis is Ubuntu’s downloaded checksum list. The verification command authenticates it, then the hash-checking command reads it to find the expected SHA-256 fingerprint for the ISO.
shasumRuns macOS’s checksum program; the following options select SHA-256 and checking mode. -aTells shasum that the next value selects the hashing algorithm. 256Selects SHA-256 as the hashing algorithm. -cChecks local files against the fingerprints recorded in the following checksum list. SHA256SUMSThis is Ubuntu’s downloaded checksum list. The verification command authenticates it, then the hash-checking command reads it to find the expected SHA-256 fingerprint for the ISO. 2>&1Combines error output with normal output so grep can inspect every checksum result. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. grepFinds text matching the supplied pattern. OKKeeps only checksum-result lines containing OK, which marks a successful match.Use Command Prompt
Install Gpg4win only from gpg4win.org.
Run these commands in order:
Hover over any part of the command for an explanation
cdChanges Terminal’s current working directory to the location that follows. %USERPROFILE%\DownloadsThe Downloads folder inside the current Windows user profile. Command Prompt changes to this folder so it can find the downloaded Ubuntu ISO.
curl.execurl.exe downloads files from web addresses. The .exe form explicitly invokes the Windows curl executable. -OSaves the downloaded file using the filename at the end of its web address. https://releases.ubuntu.com/26.04/SHA256SUMShttps://releases.ubuntu.com/26.04/SHA256SUMS is the local filename wget will use for the downloaded file. The preceding -O option assigns this name instead of retaining the filename from the web address.
curl.execurl.exe downloads files from web addresses. The .exe form explicitly invokes the Windows curl executable. -OSaves the downloaded file using the filename at the end of its web address. https://releases.ubuntu.com/26.04/SHA256SUMS.gpghttps://releases.ubuntu.com/26.04/SHA256SUMS.gpg is the local filename wget will use for the downloaded file. The preceding -O option assigns this name instead of retaining the filename from the web address.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --keyid-formatTells GPG how to display key identifiers in its output. longRequests full-length key identifiers, making the signing keys easier to distinguish. --keyserverTells GPG that the next value identifies the public key server to contact. hkp://keyserver.ubuntu.comIdentifies Ubuntu’s public key server using the HKP keyserver protocol. GPG contacts it to retrieve the specified public signing keys. --recv-keysTells GPG to download and import each public key identifier that follows. 0x46181433FBB754510x46181433FBB75451 identifies one of the Ubuntu signing public keys named by the Yeti 2.0 verification procedure. 0xD94AA3F0EFE210920xD94AA3F0EFE21092 identifies one of the Ubuntu signing public keys named by the Yeti 2.0 verification procedure.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --keyid-formatTells GPG how to display key identifiers in its output. longRequests full-length key identifiers, making the signing keys easier to distinguish. --verifyChecks that a signature was made by a recognised key and that the signed file has not changed. SHA256SUMS.gpgThis is Ubuntu’s detached GPG signature for SHA256SUMS. GPG checks it using the imported Ubuntu signing keys. SHA256SUMSThis is Ubuntu’s downloaded checksum list. The verification command authenticates it, then the hash-checking command reads it to find the expected SHA-256 fingerprint for the ISO.
certUtilRuns Windows’ built-in certificate and file-hashing utility. -hashfileTells certUtil to calculate a cryptographic hash for the file named next. ubuntu-26.04-desktop-amd64.isoNames the downloaded Ubuntu desktop ISO whose fingerprint is being calculated. SHA256Selects SHA-256 as the algorithm certUtil uses for the ISO fingerprint.
typeRuns the Windows Command Prompt command that displays a text file’s contents. SHA256SUMSThis is Ubuntu’s downloaded checksum list. The verification command authenticates it, then the hash-checking command reads it to find the expected SHA-256 fingerprint for the ISO.Do not create the Linux USB unless both the signature check and ISO checksum check succeed. If either check fails or reports a mismatch, delete the downloaded files and stop.
Write the verified Ubuntu image to a new USB drive, then label it with tape as the Linux USB.
Windows users can use Rufus; macOS users can use balenaEtcher. Experienced terminal users may use dd, but selecting the wrong drive can destroy its contents.
Power off the online laptop, insert the labelled Linux USB, and boot from it. Adjust the BIOS boot order if the installer does not appear.
Connect the online computer to Wi-Fi or Ethernet. Open Terminal with Ctrl + Alt + T, run each command, approve prompts, and wait for completion.
Linux terminals use Ctrl + Shift + C and Ctrl + Shift + V for copy and paste.
Copy the commands below one at a time, paste each one into Terminal, and press the Enter/Return key on your keyboard to run it.
Hover over any part of the command for an explanation
sudoRuns the following program with administrator privileges, which are required for system-level changes. aptUbuntu’s package-management program; it finds, verifies, installs and updates software. updateTells apt to refresh its catalogue of available packages; it does not install upgrades itself.Hover over any part of the command for an explanation
sudoRuns the following program with administrator privileges, which are required for system-level changes. aptUbuntu’s package-management program; it finds, verifies, installs and updates software. -yAutomatically answers “yes” to apt confirmation prompts. full-upgradeTells apt to install all available upgrades, including changes that add or remove dependencies.Open Terminal, change to ~/Downloads, and download the binary archive plus its signed checksums.
Hover over any part of the command for an explanation
cdChanges Terminal’s current working directory to the location that follows. ~/DownloadsThe Downloads folder inside the current user’s home directory; subsequent downloads will be saved here.Hover over any part of the command for an explanation
wgetDownloads a file from a web address to the current directory. https://bitcoincore.org/bin/bitcoin-core-31.1/bitcoin-31.1-x86_64-linux-gnu.tar.gzThe remote HTTPS address from which the named file is downloaded.
wgetDownloads a file from a web address to the current directory. https://bitcoincore.org/bin/bitcoin-core-31.1/SHA256SUMSThe remote HTTPS address from which the named file is downloaded.
wgetDownloads a file from a web address to the current directory. https://bitcoincore.org/bin/bitcoin-core-31.1/SHA256SUMS.ascThe remote HTTPS address from which the named file is downloaded.Hover over any part of the command for an explanation
sha256sumRuns Linux’s SHA-256 checksum program to compare the downloaded ISO with Ubuntu’s authenticated checksum list. --ignore-missingSkips checksum entries for files that are not present locally. --checkReads published checksums and reports whether each downloaded file matches. SHA256SUMSThis is Bitcoin Core’s downloaded checksum list. sha256sum reads it to find the expected SHA-256 fingerprint for the archive being checked.Continue only if the Bitcoin archive reports “OK”. If it does not, stop and do not use the download.
Download the Bitcoin Core builder keys, import them, and verify the signed checksum file.
Hover over any part of the command for an explanation
wgetDownloads a file from a web address to the current directory. -OTells wget to save the download under the filename that follows. guix.sigs.tar.gzguix.sigs.tar.gz is the local filename wget will use for the downloaded file. The preceding -O option assigns this name instead of retaining the filename from the web address. https://github.com/bitcoin-core/guix.sigs/archive/refs/heads/main.tar.gzThe remote HTTPS address from which the named file is downloaded.
tarReads or creates archive files. Here it is used to unpack downloaded archives. -xzfCombines three tar options: extract files, decompress gzip data, and read the following archive filename. guix.sigs.tar.gzguix.sigs.tar.gz is the gzip-compressed tar archive that tar will decompress and extract. The preceding -xzf options tell tar how to process this file.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --importAdds the listed public signing keys to the local GPG keyring. guix.sigs-main/builder-keys/*This wildcard selects every builder public-key file inside the extracted guix.sigs directory so gpg can import all of them.
gpgThe GNU Privacy Guard program used to import public keys and verify digital signatures. --verifyChecks that a signature was made by a recognised key and that the signed file has not changed. SHA256SUMS.ascThis is the downloaded GPG signature for SHA256SUMS. gpg verifies it against the imported Bitcoin Core builder public keys.Look for good signatures from several Bitcoin Core contributors. If none are good, stop and do not proceed.
Hover over any part of the command for an explanation
tarReads or creates archive files. Here it is used to unpack downloaded archives. -xzfCombines three tar options: extract files, decompress gzip data, and read the following archive filename. bitcoin-31.1-x86_64-linux-gnu.tar.gzbitcoin-31.1-x86_64-linux-gnu.tar.gz is the gzip-compressed tar archive that tar will decompress and extract. The preceding -xzf options tell tar how to process this file. -CTells tar to change to the following destination directory before extracting. ~~ is the extraction destination. Here ~ means the current user’s home folder, so tar places the unpacked Bitcoin Core folder there.The verified software is now available at ~/bitcoin-31.1.
Open bitcoin.conf inside the bitcoin-31.1 folder. Add the following on its own active line, save, and close the file.
Hover over any part of the command for an explanation
prune=5500A Bitcoin Core setting that keeps roughly 5,500 MiB of recent block data instead of retaining the entire blockchain.In Terminal, change to ~/bitcoin-31.1/bin and start Bitcoin Core.
Hover over any part of the command for an explanation
./bitcoindRuns the Bitcoin Core daemon located in the current directory; “./” explicitly means this directory. -daemonStarts Bitcoin Core in the background so the terminal can be used for other commands.Initial synchronisation can take days or weeks and must finish before wallet testing. Always stop Bitcoin Core cleanly before shutting down:
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. stopAsks Bitcoin Core to shut down cleanly before the computer is powered off.Label the second laptop with tape as the offline computer. With it powered off, insert the labelled Linux USB and boot from it. Adjust BIOS boot order if needed.
Download the self-contained Ice Core Guide v1 onto this live session before disabling networking. It includes the complete guide, styles, diagrams and animation code in one HTML file.
Open ice-core-guide-v1.html in the browser after networking is disabled. Keep the file in the live session so exact public commands remain available for copy and paste. The package contains no wallet data and makes no runtime network requests.
Repeat every part of A3 on the offline computer: download Bitcoin Core, verify its hash and contributor signatures, and unpack it. Do not skip verification.
Install Brasero while the temporary network connection is still available.
Hover over any part of the command for an explanation
sudoRuns the following program with administrator privileges, which are required for system-level changes. aptUbuntu’s package-management program; it finds, verifies, installs and updates software. updateTells apt to refresh its catalogue of available packages; it does not install upgrades itself.
sudoRuns the following program with administrator privileges, which are required for system-level changes. aptUbuntu’s package-management program; it finds, verifies, installs and updates software. -yAutomatically answers “yes” to apt confirmation prompts. installTells apt to install the named package. braseroNames the Brasero disc-writing package that apt should install.Ubuntu apt checks package authenticity. Yeti 2.0 notes that a dpkg message may appear after installation. Brasero will write the key backups to archival discs.
Hover over any part of the command for an explanation
nmcliThe command-line controller for Ubuntu’s NetworkManager service. networkingSelects NetworkManager’s overall networking state as the setting nmcli should change. offTells nmcli to switch NetworkManager networking off, disabling its managed network connections.
rfkillControls the operating system’s radio-frequency devices, such as Bluetooth adapters. blockTells rfkill to disable the radio device type named next. bluetoothSelects Bluetooth as the radio technology rfkill should block.Networking must be off before any key material is generated or inserted. This disables Wi-Fi, Ethernet and Bluetooth at the operating-system level.
Prevent the live system from writing memory contents to internal storage.
Hover over any part of the command for an explanation
sudoRuns the following program with administrator privileges, which are required for system-level changes. swapoffDisables swap so memory contents are not written to storage. -aApplies swapoff to every active swap area.Stage B
Change to ~/bitcoin-31.1/bin in Terminal, then start Bitcoin Core.
Hover over any part of the command for an explanation
cdChanges Terminal’s current working directory to the location that follows. ~/bitcoin-31.1/binThe folder containing the verified Bitcoin Core executable programs unpacked earlier in the guide.
./bitcoindRuns the Bitcoin Core daemon located in the current directory; “./” explicitly means this directory. -daemonStarts Bitcoin Core in the background so the terminal can be used for other commands.From ~/bitcoin-31.1/bin, create the seven participant wallets.
Hover over any part of the command for an explanation
forStarts a shell loop. Together with the condition that follows, it repeats the enclosed commands for the required numbered wallets. ((n=1;n<=7;n++))Controls the loop: start n at 1, continue while n is at most 7, and increase n by one after each pass.
doBegins the body of the for loop. The following commands run once for each value selected by the loop above.
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. createwalletCreates a new Bitcoin Core wallet using the name that follows. "key_${n}"Creates the wallet name key_ followed by the current loop number, producing key_1 through key_7 across the seven iterations.
doneEnds the for loop after all iterations have completed.Build an in-memory array of the receiving descriptors from all seven wallets.
Hover over any part of the command for an explanation
declareUses Bash’s declare builtin to create the variable described by the following option and name. -AMakes the declared variable an associative array, whose entries can be addressed by text keys rather than only numeric positions. xpubsNames the new associative array xpubs. The loop below stores each wallet’s extracted public descriptor in this array.
forStarts a shell loop. Together with the condition that follows, it repeats the enclosed commands for the required numbered wallets. ((n=1;n<=7;n++))Controls the loop: start n at 1, continue while n is at most 7, and increase n by one after each pass.
doBegins the body of the for loop. The following commands run once for each value selected by the loop above.
xpubs["xpub_${n}"]=$(./bitcoin-cliSelects the xpubs array entry numbered by the current loop value, runs bitcoin-cli inside $(...), and stores the processed descriptor output in that entry. -rpcwallet="key_${n}"Directs the Bitcoin Core instruction to the named wallet rather than another loaded wallet. listdescriptorsReturns the descriptors belonging to the selected wallet. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. jqReads JSON output and selects or reformats the requested fields. '.descriptors | [.[] | select(.desc | startswith("wpkh") and contains("/0/*") )][0] | .desc'This jq filter examines the wallet’s descriptor list, selects the first wpkh receiving descriptor whose path contains /0/*, and outputs its desc field. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. grepFinds text matching the supplied pattern. -PoCombines grep’s -P and -o options: use a Perl-compatible regular expression and output only the matching text. '(?<=\().*(?=\))'This grep pattern returns only the text inside the descriptor’s outer parentheses, removing the surrounding descriptor function. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. sedTransforms text according to the supplied substitution expression. 's /0/\* /<0;1>/* ')This sed substitution changes the descriptor’s single receive branch /0/* into the combined external-and-change branch /<0;1>/*.
doneEnds the for loop after all iterations have completed.Combine all seven xpubs into a sorted 3-of-7 descriptor, calculate its checksum, create a blank watch-only wallet, and import the descriptor.
Hover over any part of the command for an explanation
desc="wsh(sortedmulti(3,${xpubs["xpub_1"]},${xpubs["xpub_2"]},${xpubs["xpub_3"]},${xpubs["xpub_4"]},${xpubs["xpub_5"]},${xpubs["xpub_6"]},${xpubs["xpub_7"]}))"Creates the shell variable desc and stores the complete wsh(sortedmulti(...)) descriptor in it. The descriptor requires any three of the seven xpubs collected in B3, and sortedmulti orders the public keys deterministically.
checksum=$(./bitcoin-cliCreates the shell variable checksum and begins a command substitution: the output produced inside $(...) becomes the variable’s value. ./bitcoin-cli calls the local Bitcoin Core command-line client. getdescriptorinfoAsks Bitcoin Core to inspect the descriptor supplied by the next element and calculate metadata including its checksum. $descExpands the desc variable created above, supplying the complete 3-of-7 descriptor to getdescriptorinfo. |Pipes Bitcoin Core’s JSON output into jq so only the required checksum is retained. jqRuns jq, a command-line JSON processor. -rTells jq to output the selected value as raw text rather than as a quoted JSON string. '.checksum')Selects the checksum property from Bitcoin Core’s JSON response, then closes the command substitution assigned to checksum.
multisig_desc="[{\"desc\": \"${desc}#${checksum}\", \"active\": true, \"timestamp\": \"now\"}]"Creates the multisig_desc shell variable containing the JSON import request. It combines the descriptor and checksum, marks the descriptor active, and uses “now” as its scan timestamp because this is a newly constructed wallet.
./bitcoin-cliRuns bitcoin-cli from the current ~/bitcoin-31.1/bin directory to send the following wallet-creation request to the local Bitcoin Core daemon. -namedEnables bitcoin-cli’s named-parameter mode for the createwallet RPC. The following values correspond to createwallet’s wallet_name, disable_private_keys, and blank parameters. createwalletCalls Bitcoin Core’s createwallet RPC to create the watch-only wallet container used by this guide. "multisig_watch_wallet"Sets the new wallet’s name to multisig_watch_wallet. The quotation marks keep the name together as one shell argument. trueSets disable_private_keys to true. This makes the wallet watch-only: it will contain no private signing keys. trueSets blank to true. Bitcoin Core creates an empty wallet so the multisig descriptor prepared above can be imported explicitly on the next line.
./bitcoin-cliRuns the local Bitcoin Core command-line client. -rpcwallet="multisig_watch_wallet"Directs this RPC call specifically to multisig_watch_wallet, the blank watch-only wallet created on the preceding line. importdescriptorsCalls importdescriptors, which adds the prepared multisig descriptor to the selected wallet. "$multisig_desc"Expands the multisig_desc variable and passes its JSON import request as one quoted argument.
./bitcoin-cliRuns the local Bitcoin Core command-line client. -rpcwallet="multisig_watch_wallet"Selects multisig_watch_wallet for this check. getwalletinfoRequests the wallet’s information so you can confirm the watch-only wallet was created and configured as expected.Label the second, unmarked USB as the transfer USB. Insert it in the offline computer and copy ~/.bitcoin/wallets/multisig_watch_wallet to it.
In Files, enable “Show hidden files” to see ~/.bitcoin. Run sync and wait for it to finish before removing any USB.
Move the transfer USB to the online computer and copy multisig_watch_wallet into ~/.bitcoin/wallets. After blockchain synchronisation finishes, load it from ~/bitcoin-31.1/bin.
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. loadwalletLoads the named wallet directory so Bitcoin Core can use it. "multisig_watch_wallet"Supplies the literal wallet name multisig_watch_wallet as one argument to the preceding wallet command.Bitcoin CLI or Bitcoin-Qt can now show history and balance, generate receive addresses and PSBTs, and broadcast signed transactions. This wallet cannot sign.
In Brasero, create seven separate archival-disc backups. Each disc contains one key wallet, multisig_watch_wallet, and README.md. Number the physical discs permanently before burning.
Stage C
This is intentionally destructive. Confirm all seven discs were written before continuing.
Stop Bitcoin Core, wait for it to exit, delete the entire ~/.bitcoin/wallets folder, and restart the daemon.
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. stopAsks Bitcoin Core to shut down cleanly before the computer is powered off.Hover over any part of the command for an explanation
./bitcoindRuns the Bitcoin Core daemon located in the current directory; “./” explicitly means this directory. -daemonStarts Bitcoin Core in the background so the terminal can be used for other commands.With multisig_watch_wallet loaded online through Bitcoin-Qt or the CLI, generate a fresh receive address.
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. -rpcwallet="multisig_watch_wallet"Directs the Bitcoin Core instruction to the named wallet rather than another loaded wallet. getnewaddressGenerates the next receive address from the watch-only wallet.Send a small amount of bitcoin to this receive address from an exchange account or another Bitcoin wallet.
Send only a very small test amount—less than US$5 in Yeti 2.0. Bitcoin-Qt may display the address as a QR code.
Only the synchronised online node can confirm the wallet balance.
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. -rpcwallet="multisig_watch_wallet"Directs the Bitcoin Core instruction to the named wallet rather than another loaded wallet. getbalancesReports confirmed and pending balances known to the selected wallet.Set $amount and $destination_address yourself in the command below and verify both before running. Enter the amount in BTC using eight decimal places, for example 0.00010000.
Hover over any part of the command for an explanation
funded_psbt=$(./bitcoin-cliStarts bitcoin-cli inside a command substitution and assigns the PSBT text ultimately selected by jq to the shell variable funded_psbt. -rpcwallet="multisig_watch_wallet"Directs the Bitcoin Core instruction to the named wallet rather than another loaded wallet. -namedAllows Bitcoin Core arguments to be supplied by name rather than only by position. \Continues this same command onto the next displayed line. Press Enter only after the complete multi-line command has been pasted.
walletcreatefundedpsbtSelects wallet coins and creates a funded but unsigned Partially Signed Bitcoin Transaction. \Continues this same command onto the next displayed line. Press Enter only after the complete multi-line command has been pasted.
outputs="{\"$destination_address\": $amount}"Supplies the named outputs argument to walletcreatefundedpsbt. Its JSON maps the destination address you set to the BTC amount you set. \Continues this same command onto the next displayed line. Press Enter only after the complete multi-line command has been pasted.
options='{"subtractFeeFromOutputs":[0]}'Supplies walletcreatefundedpsbt’s named options argument. subtractFeeFromOutputs:[0] deducts the transaction fee from output index 0. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.psbt')Selects the psbt field from jq’s JSON input as raw text, then closes the surrounding command substitution.
echoWrites the following value as text. "$funded_psbt"Expands the shell variable funded_psbt and preserves its complete value as one argument by surrounding it with quotes. >Redirects the preceding command’s output into the file named next, replacing that file if it already exists. ~/Desktop/unsigned.psbtNames the Desktop file that receives the newly created unsigned PSBT through the preceding > redirection.Insert the transfer USB in the online computer, copy unsigned.psbt from the Desktop onto it, safely remove it, and carry it to the offline computer.
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. decodepsbtConverts a PSBT into readable structured information so its inputs, outputs, amount and destination can be inspected. "$(cat ~/Desktop/unsigned.psbt)"Runs cat to read the PSBT file from the Desktop and passes the complete file contents to decodepsbt as one quoted argument.Inspect tx.vout and confirm destination_address and amount exactly match your intention. If either differs, stop and reevaluate.
Choose three archival discs. Insert them one by one and copy each key_# directory into ~/.bitcoin/wallets. Replace key_# below with each recovered wallet name.
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. loadwalletLoads the named wallet directory so Bitcoin Core can use it. "key_#"This is a placeholder wallet name. Replace # with the number of the recovered key wallet you intend to load, such as key_3.
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. loadwalletLoads the named wallet directory so Bitcoin Core can use it. "key_#"This is a placeholder wallet name. Replace # with the number of the recovered key wallet you intend to load, such as key_3.
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. loadwalletLoads the named wallet directory so Bitcoin Core can use it. "key_#"This is a placeholder wallet name. Replace # with the number of the recovered key wallet you intend to load, such as key_3.Load only the three key wallets. If the watch wallet is loaded, unload it first with ./bitcoin-cli unloadwallet multisig_watch_wallet.
Hover over any part of the command for an explanation
psbt=$(catRuns cat inside a command substitution to read the signed or unsigned PSBT file, then assigns that file’s contents to the shell variable psbt. ~/Desktop/unsigned.psbt)Names the PSBT file that cat reads from the Desktop; the closing parenthesis ends the command substitution whose output is stored in the variable at the start of this line.
wallet1=$(./bitcoin-cliRuns bitcoin-cli to obtain the loaded-wallet list and assigns the selected wallet name to wallet1. listwalletsReturns the names of wallets currently loaded in Bitcoin Core. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it.jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.[0]')Selects loaded-wallet array entry 0 in jq, outputs that wallet name, and closes the surrounding command substitution.
wallet2=$(./bitcoin-cliRuns bitcoin-cli to obtain the loaded-wallet list and assigns the selected wallet name to wallet2. listwalletsReturns the names of wallets currently loaded in Bitcoin Core. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it.jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.[1]')Selects loaded-wallet array entry 1 in jq, outputs that wallet name, and closes the surrounding command substitution.
wallet3=$(./bitcoin-cliRuns bitcoin-cli to obtain the loaded-wallet list and assigns the selected wallet name to wallet3. listwalletsReturns the names of wallets currently loaded in Bitcoin Core. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it.jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.[2]')Selects loaded-wallet array entry 2 in jq, outputs that wallet name, and closes the surrounding command substitution.
psbt_1=$(./bitcoin-cliStarts a Bitcoin Core signing call and assigns its returned PSBT to psbt_1, ready for the next signing wallet. -rpcwallet="$wallet1"Directs the Bitcoin Core instruction to the named wallet rather than another loaded wallet. walletprocesspsbtLets the selected wallet inspect and, when it has the required private key, add its signature to the PSBT. "$psbt"Expands the shell variable psbt and preserves its complete value as one argument by surrounding it with quotes. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.psbt')Selects the psbt field from jq’s JSON input as raw text, then closes the surrounding command substitution.
psbt_2=$(./bitcoin-cliStarts a Bitcoin Core signing call and assigns its returned PSBT to psbt_2, ready for the next signing wallet. -rpcwallet="$wallet2"Directs the Bitcoin Core instruction to the named wallet rather than another loaded wallet. walletprocesspsbtLets the selected wallet inspect and, when it has the required private key, add its signature to the PSBT. "$psbt_1"Expands the shell variable psbt_1 and preserves its complete value as one argument by surrounding it with quotes. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.psbt')Selects the psbt field from jq’s JSON input as raw text, then closes the surrounding command substitution.
psbt_3=$(./bitcoin-cliStarts a Bitcoin Core signing call and assigns its returned PSBT to psbt_3, ready for the next signing wallet. -rpcwallet="$wallet3"Directs the Bitcoin Core instruction to the named wallet rather than another loaded wallet. walletprocesspsbtLets the selected wallet inspect and, when it has the required private key, add its signature to the PSBT. "$psbt_2"Expands the shell variable psbt_2 and preserves its complete value as one argument by surrounding it with quotes. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.psbt')Selects the psbt field from jq’s JSON input as raw text, then closes the surrounding command substitution.
echoWrites the following value as text. "$psbt_3"Expands the shell variable psbt_3 and preserves its complete value as one argument by surrounding it with quotes. >Redirects the preceding command’s output into the file named next, replacing that file if it already exists. ~/Desktop/signed.psbtNames the Desktop file that receives the three-signature PSBT through the preceding > redirection.Copy signed.psbt from the offline Desktop to the transfer USB. Remove it, insert it online, and copy signed.psbt to the online Desktop.
Hover over any part of the command for an explanation
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. decodepsbtConverts a PSBT into readable structured information so its inputs, outputs, amount and destination can be inspected. "$(cat ~/Desktop/signed.psbt)"Runs cat to read the PSBT file from the Desktop and passes the complete file contents to decodepsbt as one quoted argument.Again confirm destination_address and amount in tx.vout. If they do not match, stop and reevaluate.
Hover over any part of the command for an explanation
psbt=$(catRuns cat inside a command substitution to read the signed or unsigned PSBT file, then assigns that file’s contents to the shell variable psbt. ~/Desktop/signed.psbt)Names the PSBT file that cat reads from the Desktop; the closing parenthesis ends the command substitution whose output is stored in the variable at the start of this line.
hex=$(./bitcoin-cliRuns Bitcoin Core’s finalisation call inside a command substitution and assigns the extracted raw transaction hexadecimal to the shell variable hex. finalizepsbtCombines the completed PSBT data and produces the raw transaction hexadecimal needed for broadcast. "$psbt"Expands the shell variable psbt and preserves its complete value as one argument by surrounding it with quotes. |Pipes the output produced on the left into the program on the right, allowing the next part of this command to process it. jqReads JSON output and selects or reformats the requested fields. -rTells jq to output the selected value as raw text instead of a JSON-quoted string. '.hex')Selects the final transaction hex field from jq’s JSON input as raw text, then closes the surrounding command substitution.
./bitcoin-cliRuns Bitcoin Core’s command-line client from the current directory and sends the following instruction to bitcoind. sendrawtransactionSubmits the final raw transaction to the Bitcoin network through the online node. "$hex"Expands the shell variable hex and preserves its complete value as one argument by surrounding it with quotes.After each transaction, delete old signed.psbt and unsigned.psbt files from the transfer USB and both Desktops.
Repeat C4–C6 three times so every key backup participates in a successful spend.
If a test fails, retry from C4 once. If it still fails, stop. Delete PSBTs, remove the watch wallet from the transfer USB, delete both computers’ ~/.bitcoin/wallets directories, and restart from A5 using fresh archival discs.
Place each disc in a separate envelope with a discreet succession message. Store the seven envelopes in seven geographically separate locations.
Three locations can jointly authorise a spend. Do not reveal wallet details in the external labelling. Only regard the wallet as tested after all three C6 transactions succeed.
Stage D
With the offline laptop powered off, insert the Linux USB, boot, and select “Try Ubuntu”. The live session intentionally has no persistence.
Temporarily connect to your home network, download and verify Bitcoin Core as in A3, and save the guide if needed. Brasero does not need to be reinstalled for routine signing.
Complete this before inserting any key material.
Hover over any part of the command for an explanation
nmcliThe command-line controller for Ubuntu’s NetworkManager service. networkingSelects NetworkManager’s overall networking state as the setting nmcli should change. offTells nmcli to switch NetworkManager networking off, disabling its managed network connections.
rfkillControls the operating system’s radio-frequency devices, such as Bluetooth adapters. blockTells rfkill to disable the radio device type named next. bluetoothSelects Bluetooth as the radio technology rfkill should block.Hover over any part of the command for an explanation
sudoRuns the following program with administrator privileges, which are required for system-level changes. swapoffDisables swap so memory contents are not written to storage. -aApplies swapoff to every active swap area.Create unsigned.psbt on the online computer as in C4, verify the amount and destination, and copy it to the transfer USB.
Move unsigned.psbt from the transfer USB to the offline Desktop and decode it as in C5. Confirm every output before proceeding.
Collect any three key discs, copy their wallet directories into ~/.bitcoin/wallets, and load only those three wallets.
Run the C5 signing sequence to produce signed.psbt. Keep the watch-only wallet unloaded.
Copy signed.psbt to the transfer USB, remove it from the offline computer, insert it online, and copy the file to the online Desktop.
Decode the signed PSBT online, verify its destination and amount again, then finalise and broadcast it as in C6.
Delete transaction files from both Desktops and the transfer USB. Shut down the offline computer after signing and export; its temporary keys disappear with the live session.
Keep both laptops dedicated exclusively to the Bitcoin Core workflow described by this guide.
Guide complete
Completion here is only that you've arrived at the end. It does not verify checklist state, or that any physical or Bitcoin operation succeeded.