Say hello to a local encrypted watch only Bitcoin wallet you can talk to anywhere in the world.
Bitcoin Wallet Watchguard allows you to get notified of any transaction to or from your wallets by using the xpub. It then lets you talk to your wallet if you enable Conversation Mode, so you can query all your addresses, transactions, and more from your phone.
All of this is made possible by ntfy, an open source self-hostable project supported by both Umbrel and Start9. Therefore, Wallet Watchguard does all of this while maintaining sovereignty and privacy so long as you configure it to use your own node and host ntfy yourself.
Your xpub is only ever stored locally on the machine you install Wallet Watchguard on. It is encrypted at rest using XSalsa20-Poly1305 and protected by a passphrase hashed with Argon2id, both provided by the battle tested libsodium cryptography library.
The current iteration of this tool is only scratching the surface of its potential. At its core, this lets you use Ntfy to talk to your node from your phone anywhere in the world without any third parties or port forwarding required. It's like a Telegram bot but fully self-hosted.
The existing view only wallet features via command line syntax are useful but I'm already working on more big updates to this because it fills a genuine gap in my own setup. This will get a lot cooler very quickly.
Docker Compose is the recommended and officially supported installation method:
mkdir -p bitcoin-wallet-watchguard && cd bitcoin-wallet-watchguard
curl -fsSL -o docker-compose.yml \
https://raw.githubusercontent.com/xannythepleb/bitcoin-wallet-watchguard/main/docker-compose.yml
docker compose pull
docker compose run --rm wallet-watchguard wwg init
docker compose up -dAlternatively, if you prefer wget:
wget -O docker-compose.yml https://raw.githubusercontent.com/xannythepleb/bitcoin-wallet-watchguard/refs/heads/main/docker-compose.ymlPaste this in your terminal to get up and running:
mkdir -p bitcoin-wallet-watchguard && cd bitcoin-wallet-watchguard && curl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/xannythepleb/bitcoin-wallet-watchguard/main/docker-compose.yml && docker compose pull && docker compose run --rm wallet-watchguard wwg init && docker compose up -dTo launch in the future:
WWG_PASSPHRASE='your passphrase here' docker compose up -dYou can choose to store your WWG_PASSPHRASE in your .env for convenience, especially seamless restarts, with obvious security tradeoffs.
Don't stress if you got something wrong during the initial configuration or need to change something later. You can jump directly to an init section to alter your configuration whenever you need:
wwg init --add ntfy
wwg init --add electrum
wwg init --add wallet
wwg init --add mempool
wwg init --add tor
wwg init --add app
wwg init --resetIf the config already exists, wwg init asks whether to update part of the config, reset it, or exit.
Once it's installed, simply cd into the directory you installed it in and run:
docker compose pull
docker compose up -dWallet Watchguard publishes Docker images to GitHub automatically. Images are built and pushed when a new version is released.
For example:
docker pull ghcr.io/xannythepleb/bitcoin-wallet-watchguard:latestOr:
docker run ghcr.io/xannythepleb/bitcoin-wallet-watchguard:latest wwg initWe recommend using Docker Compose as described above. It is the simplest way to install WWG: simply download docker-compose.yml into the bitcoin-wallet-watchguard directory (or whatever you want to name it) then run docker compose pull to automatically grab the latest stable image.
git clone https://github.com/xannythepleb/bitcoin-wallet-watchguard.git
cd bitcoin-wallet-watchguard
wwg init
wwg runWe only recommend using a local install unless you really really don't like Docker or you want to help with development and debugging in your local environment. With how Python manages dependencies, Docker is much cleaner, and the containerised environment provides you with extra security - for example, WWG in Docker only has access to its own database and config. It has zero access to other files and applications on your OS. It can't see your hot wallet private keys, your emails, your photos, your browser history, nothing.
It is also much easier to uninstall a Docker container fully if you decide you want to (although we hope you don't) as all the dependencies are inside the container.
This guide and the example commands it provides will assume you are using Docker Compose as this is the recommended install method. Adjust accordingly if you aren't.
To run commands, e.g. status:
docker compose exec wallet-watchguard wwg statusNote the distinction between how we run the Docker Compose container during init and after setup is complete:
# First time setup - docker compose run --rm to create a temporary container that launches the onboarding wizard to create your config
docker compose run --rm wallet-watchguard wwg init
# Start the daemon
docker compose up -d
# Example usage commands - docker compose exec to run commands inside the daemon
## Check status of WWG
docker compose exec wallet-watchguard wwg status
## Check balance of your wallets
docker compose exec wallet-watchguard wwg balance
## Show next unused address from one of your wallets with QR code
docker compose exec wallet-watchguard wwg next
## Generate QR code to request Bitcoin with optional message, enter amount in either sats or BTC
docker compose ecec wallet-watchguard wwg request
## Add, remove, or rename a wallet
docker compose exec wallet-watchguard wwg wallet add
docker compose exec wallet-watchguard wwg wallet remove
docker compose exec wallet-watchguard wwg wallet renameYou can get plain output from commands such as wwg next and wwg request for use in scripts, allowing you to use WWG as a tool for automating your own Bitcoin workflow or projects.
For example:
addr="$(docker compose exec wallet-watchguard wwg request --wallet-index=1 --sats 50000 --note "Coffee" --plain)"Stores the following within the $addr variable:
bitcoin:bc1p7lwvtp2mlv4l6wkmkcjh9rjxhma8ttwpz7pyf93f4srtcdehm3ustwkef0?amount=0.0005&message=CoffeeYou can feed this variable into your own script or application to automate requests, create your own QR codes, write NFC tags, whatever you want.
Whereas if you run the same command without the --plain flag in the interactive terminal, you get both the metadata and the human friendly output with a QR code:
Wallet: Cake Wallet Nostr (bitcoin / taproot)
Receive address:
m/86'/0'/0'/0/2 bc1p7lwvtp2mlv4l6wkmkcjh9rjxhma8ttwpz7pyf93f4srtcdehm3ustwkef0
Amount: 0.0005 BTC
Note: Coffee
Payment request:
bitcoin:bc1p7lwvtp2mlv4l6wkmkcjh9rjxhma8ttwpz7pyf93f4srtcdehm3ustwkef0?amount=0.0005&message=Coffee
QR code:
βββββββββββββββββββββββββββββββββββββββββββββ
ββ βββββ ββ βββββββββββ ββββββ βββ β βββββ ββ
ββ β β β βββ βββββββ ββββ βββββββ β β ββ
ββ βββββ β ββββββ ββββββββ βββββ βββ βββββ ββ
ββββββββββ β β βββ β βββββ βββββ ββββββββββββ
ββββ β βββββ β βββββ βββ ββ ββ β ββ ββββββ
ββββ ββββββ ββ βββ βββββ β βββ β βββββ ββ βββ
ββ β ββββββββ β ββββ βββ βββ βββββ ββββ
βββ ββ ββββββββββββββ β βββ βββββ ββ βββββ
ββββββ ββββββ β βββ β ββββ βββββββββββββ βββ
ββ β ββββββ ββββββ βββ ββββ β βββ βββββββββ
ββββ βββββββ β βββββ βββ βββββββ βββββββ ββ
ββββ β ββ ββββββ ββββ β β βββββββ ββββββ
βββββββ β β β ββββββββββββββ ββ ββββββββ ββββ
ββββ β ββββββββ βββββββ βββββ ββ ββββββ β βββ
ββ βββ β β βββββββββββ βββ ββββ βββββ β ββ
ββ ββββββββββ βββ ββ βββ ββββ βββ ββββ βββββ
βββββββββββ β β ββββ βββ βββββ βββ βββ ββββββ
ββ βββββ βββββββββββββ βββ ββ βββ βββ ββββββ
ββ β β β ββββββ βββββββββββββββββββββββββββ
ββ βββββ βββββ ββ βββ βββββ βββ β ββββββ
βββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββThese QR codes are extremely clear and readable even within a terminal:
When scanned, you will get something like this on your mobile Bitcoin wallet (UX varies by wallet, of course):
QR codes are also generated when you run wwg next to get a new unused Bitcoin address. These do not include amounts or messages, just the address. If you don't want the QR codes when running wwg next, you can simply add the --no-qr flag, or use --plain to get only the address and nothing else, useful for variables and scripting.
For example if you want a list of 10 unused addresses for your first wallet without QR codes or any other metadata printed at all, just a simple list of line separated addresses, you can run:
docker compose exec wallet-watchguard wwg next 10 --wallet-index=1 --plainIf you want a list of three new addresses from your second wallet with both the derivation path and the addresses but no QR codes, use --no-qr instead:
docker compose exec wallet-watchguard wwg next 3 --wallet-index=2 --no-qrAs you can see, this tool is designed to be extremely flexible and usable both within the interactive terminal and within your own scripts and automations.
Whenever a transaction occurs, you will be notified via Ntfy.
If you have Mempool integration configured correctly, you will get extra details about the transaction like so:
Wallet: Jade Cold Wallet
Type: received
Status: unconfirmed/mempool
Amount: +69,420 sats
vsize: 420 vB
Fee rate: 2.67 sat/vB
Inputs:
* bc1q67xanz...
* bc1p420asd...
Outputs:
* β
bc1p999tap... - 69,418 sats
Wallet path: m/86'/0'/0'/0/4
Tx: abc123...
Without Mempool, you will get a simpler notification:
Wallet: Jade Cold Wallet
Type: activity
Status: confirmed at height 9999999
Address: bc1p999tap...
Wallet path: m/86'/0'/0'/0/4
Tx: abc123...
As you can see, Mempool allows WWG to pick up the amount, whether it was sent or received, details of the input and output, and fee paid. Instructions on how to configure Mempool integration are further down.
Currently, Conversation Mode is only available with Ntfy. However, you can configure Nostr for notifications. Nostr notifications use NIP-17 DMs that protect both the message and the metadata via NIP-44 encryption and NIP-59 gift wrapping.
The initial setup wizard will ask you if you want to configure Ntfy, Nostr, or both.
If you already configured WWG, you can add a Nostr config at any time by running:
docker compose exec wallet-watchguard wwg init --add nostrThis will create an npub for your WWG instance, ask for your npub (you can add multiple if you wish), and of course the relays you want to use - make sure these relays are configured as your DM inbox relays so the DMs reach your Nostr client.
WWG will then print its npub and follow yours. It is recommended you follow it back so the DMs don't get filtered out by your client. If you follow each other, this also allows you to use WoT relays for your WWG notifications.
You can enable or disable the notification providers you have configured via the CLI:
docker compose exec wallet-watchguard wwg enable nostr
docker compose exec wallet-watchguard wwg enable ntfy
docker compose exec wallet-watchguard wwg disable nostr
docker compose exec wallet-watchguard wwg disable ntfyNote: Make sure you use the full image if you want Nostr support. The full image is the default one tagged latest. The slim image only supports Ntfy. If you want to switch, simply change your docker-compose.yml to:
image: ghcr.io/xannythepleb/bitcoin-wallet-watchguard:latestInstead of:
image: ghcr.io/xannythepleb/bitcoin-wallet-watchguard:slimAgain, this is the default, so you only have the slim image if you actively chose it. If you pulled from the docker-compose.yml without editing it, you already have the full image.
Conversation Mode lets you query Wallet Watchguard remotely through ntfy. You can keep an eye on even your hardware cold storage wherever you are via 100% self-hosted infrastructure. No third party middleman if you configure it correctly with your own node and your own ntfy instance. You can run both of these on your own physical hardware using Start9 or Umbrel for true sovereignty.
I specifically added this feature because I had a hard time finding a reliable watch only wallet that could work with any hardware wallet and allowed you to use your own node on the backend. So I made one. And because ntfy runs on Android, iOS, and in the browser, it is truly universal.
It is off by default and checks are in place to ensure it is only enabled on secure sessions (password protected, no public read/write access).
Enable it in config:
docker compose exec wallet-watchguard wwg init --add conversationConversation Mode will only start if the configured ntfy topic passes runtime protection checks:
configured token/basic credentials can read the topic
configured token/basic credentials can publish to the topic
anonymous read is denied
anonymous write is denied
If the topic is public, or anonymous read/write is allowed, Wallet Watchguard will continue normal wallet monitoring but will not honour Conversation Mode.
Conversation Mode performs an anonymous write probe on startup because ntfy's normal client APIs can publish, subscribe and authenticate, but do not expose a harmless per-topic ACL inspection endpoint for ordinary clients. The probe uses Cache: no, Firebase: no and minimum priority. If that anonymous probe succeeds, conversation Mode is refused.
Example ntfy commands:
wwg help
wwg status
wwg wallets
wwg next address
wwg next 3
wwg addresses --wallet-index 1 --limit 5
wwg addresses --wallet "Main Taproot wallet" --include-change --only-used
wwg balance
wwg fees
The default command prefix is wwg. The prefix avoids accidental replies to unrelated messages on the same topic.
All of these commands are also accepted by the CLI.
The standard method of generating ntfy access tokens already works perfectly for Conversation Mode:
ntfy token add wallet-watchguardYou can also create a new user and use it to generate an access token through the web UI under the account section.
For Start9/StartOS, Provision Publisher is not enough for Conversation Mode. Provision Publisher creates a write-only token, which is ideal for normal alerts but cannot subscribe/read commands.
Use the standard ntfy access token process instead:
1. In the Start9 ntfy service, create a regular user, e.g. wallet-watchguard.
2. Grant that user read-write access to the Wallet Watchguard topic.
3. Log into the ntfy web UI as that user.
4. Create an access token manually in the ntfy web UI.
5. Run: wwg init --add ntfy
6. Choose token auth and paste that access token.
7. Keep anonymous read and anonymous write denied.
TL;DR: Make an access token through ntfy instead of the StartOS UI. Use this token to configure ntfy for WWG and Conversation Mode will work.
See the ntfy docs for more on how their access tokens work.
You can use the same topic as your normal alerts. If your existing config already stores a topic name, the wizard will remind you of it so you can grant access to the correct topic.
The expected Conversation Mode protection check is:
authenticated read: ok
authenticated write: ok
anonymous read: blocked HTTP 401/403
anonymous write: blocked HTTP 401/403
If you see:
authenticated read: failed HTTP 403
authenticated write: ok
This means Wallet Watchguard has a write-only credential. Normal notifications will work, but Conversation Mode cannot receive commands. On Start9 this usually means you are still using a Provision Publisher token rather than a token created by a regular read-write user.
If you use Start9/StartOS ntfy, run:
docker compose exec wallet-watchguard wwg init --add ntfyChoose the Start9/StartOS provision publisher path.
In the Start9 ntfy UI, use Provision Publisher if you only want notifications and do not want Conversation Mode. If you want Conversation Mode to work, follow the Conversation Mode Setup instructions above to generate an access token through ntfy.
Note: you can always generate a new token and configure WWG to use it if you change your mind later.
For the reference name, use something like wallet-watchguard. For the topic name, use the topic shown by the wizard, especially if one is already stored in your config.
Start9 will show:
publishUrl
token
topic
username
Paste those into the wizard. Wallet Watchguard stores the token encrypted at rest using the same passphrase used for your xpub.
Note: publishUrl can sometimes print something useless like ntfy.start. If it does, ignore it and put the actual base domain or IP address you set for your ntfy instance. If you aren't using StartTunnel, make sure to also include the port like so:
https://192.168.67.69:4200
StartOS, Umbrel, and other local node systems often use private or self-signed TLS certificates. If you see an error like:
[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: self-signed certificate in certificate chain
Run the relevant setup section and disable TLS certificate verification for that local service:
docker compose exec wallet-watchguard wwg init --add electrum
docker compose exec wallet-watchguard wwg init --add ntfy
docker compose exec wallet-watchguard wwg init --add mempoolTLS encryption still remains enabled; only public CA/hostname verification is skipped for that local self-hosted service.
For existing configs without electrum.tls_verify, Wallet Watchguard automatically relaxes Electrum TLS verification for localhost, private IPs, .local, .lan, .onion, and similar local targets.
Wallet Watchguard supports .onion Electrum/Fulcrum hosts through both external SOCKS5 proxies and, for Docker and Docker Compose deployments, you can now enable an internal Tor proxy so the container directly supports upstream Tor only Bitcoin nodes without any manual networking config required.
This is off by default. Enable/disable it persistently from the CLI:
docker compose exec wallet-watchguard wwg tor enable
docker compose exec wallet-watchguard wwg tor disable
docker compose exec wallet-watchguard wwg tor statusYou can also enable it for just a single session with an environment variable:
WWG_TOR_UPSTREAM=true docker compose up -dOr if you don't use Docker Compose:
wwg run --tor-upstreamWhen the switch is on, Wallet Watchguard automatically sets the effective Electrum SOCKS proxy to tor.socks_proxy. The startup summary and wwg status output include a multi-line Tor Upstream section showing whether it is enabled, which proxy is used, and whether Wallet Watchguard manages the Tor process. The Electrum/Fulcrum section shows the effective SOCKS proxy and, after a connectivity probe, the Electrum/Fulcrum server version.
Test Tor connectivity manually with:
WWG_TOR_UPSTREAM=true docker compose run --rm wallet-watchguard wwg test-torThis test probes your configured upstream Electrum/Fulcrum server. If the output shows the bootstrapping is successful but the connection to the server fails for some reason, the issue is with either your config or your server.
Make sure WWG is not configured to connect to your node via a local IP (e.g. 192.168.x.x) and make sure the port is not inside the domain (e.g. do not type domain.com:50002 into the config or the CLI). The port is separate in the config. Ideally, when using Tor to connect to Electrum, it should advertise an onion address. Put that as your Electrum server URL in the config. The Electrum port is 50002 if TLS is on or 50001 if it is not.
Do not enter your Electrum URL with an https:// prefix even if it is using TLS/SSL. Just make sure you set port 50002. The address itself should contain no prefix at all.
If it succeeds you will see output like this when running the test:
[notice] Bootstrapped 95% (circuit_create): Establishing a Tor circuit
[notice] Bootstrapped 100% (done): Done
[notice] Catching signal TERM, exiting cleanly.
Tor connectivity test: ok (Fulcrum 2.1.1, 1.4)If it times out, just try again. Tor can be fickle sometimes, that is the nature of the beast. If you run a Tor only Bitcoin node you are certainly no stranger to that. Once the connection is established it usually remains stable for low bandwidth applications. The bootstrapping itself is successful first time in 90% of cases.
After configuring ntfy, restart the daemon and send a test notification:
docker compose restart wallet-watchguard
docker compose exec wallet-watchguard wwg test-ntfyOnce you have both Ntfy and Electrum up and running, you can test with a real transaction notification:
docker compose exec wallet-watchguard wwg test-ntfy --latest-txThis will ask which wallet you want to test with, or you can select one directly by index or name:
docker compose exec wallet-watchguard wwg test-ntfy --latest-tx --wallet-index 1
docker compose exec wallet-watchguard wwg test-ntfy --latest-tx --wallet "Jade Cold Wallet"If the test-ntfy command works but the notifications themselves don't, you can use live-debug to help diagnose this. With your daemon running:
docker compose exec wallet-watchguard wwg live-debugThis will test the full end-to-end process exactly as if WWG had just picked up a new transaction and print debug info on the terminal as it does it. This should help you troubleshoot issues in your config or pick up bugs in WWG. If it looks like the latter, please open an issue.
Fulcrum/Electrum remains the source of truth for wallet activity detection.
Mempool support is optional and only enriches notifications with decoded transaction data such as:
amount
fee
vsize
fee rate
received/sent/self-transfer classification
Enable it with:
docker compose run --rm wallet-watchguard wwg init --add mempoolUse a local/self-hosted Mempool API where possible, for example:
mempool:
enabled: true
base_url: https://mempool.local:1234/api
tls_verify: false
timeout_seconds: 15
enrich_notifications: trueTest it with:
docker compose exec wallet-watchguard wwg feesList receive addresses and balances. If more than one wallet is configured, Wallet Watchguard asks which wallet you want to view:
docker compose exec wallet-watchguard wwg addresses --limit 20Show the next unused wallet address:
docker compose exec wallet-watchguard wwg nextShow the total balance across all wallets:
docker compose exec wallet-watchguard wwg balancewwg status prints the same non-secret summary format used at startup. When run as a one shot CLI command it cannot know the daemon's live subscription count, but it still shows the effective config, Electrum/Fulcrum target, Tor Upstream setting, ntfy target, Mempool setting, Conversation Mode setting, and configured wallets.
Conversation Mode also understands:
wwg statusThat ntfy response is generated by the running daemon, so it includes live runtime values such as subscribed script count and the startup Tor probe result.
When running successfully, the daemon prints a non-secret startup summary to the logs. This can be checked again at any time by running wwg status from the CLI.
π¦ Version: v0.1.8
βοΈ Config: ./data/config.yaml
πΎ Database: ./data/watchguard.sqlite3
π‘ Electrum/Fulcrum:
π Tor Upstream: disabled
π ntfy: https://ntfy.example/wwg auth=bearer tls_verify=true
π Mempool API: disabled
π£οΈ Conversation Mode: disabled
π Subscribed scripts: not connected in this status command
π΅ Wallets:
π Useful commands:
View logs:
docker compose logs -f wallet-watchguardI made sure to use only emojis that correctly rendered in my terminal. But no one likes the "it works on my machine bro" guy so if the emojis cause issues, you can view status without emojis by running:
docker compose exec wallet-watchguard wwg status --no-emojiOr disable them entirely by launching with the --no-emoji flag.
Please open an issue if you have crashes or any other weirdness with emojis enabled and tell me what distro you're using. It should not cause issues on any modern one (Ubuntu added emoji support in 18.04), but I'm documenting this just in case - if you have a niche or legacy distro and it won't run, try --no-emoji first.
There is a slim build of the Docker image that does not include the Tor package if you to not need it.
Modify your docker-compose.yml to use slim instead of latest:
image: ghcr.io/xannythepleb/bitcoin-wallet-watchguard:slimThen run:
docker compose pullCurrently the only difference between the two not including Tor. The changes will become more substantial in the future.
Bitcoin Wallet Watchguard uses:
- Python for orchestration, CLI, async networking, config, SQLite, ntfy, and Mempool integration
- Rust for exact Bitcoin address/script derivation
- Fulcrum/Electrum for wallet activity subscriptions
- SOCKS5 proxy support, both external and internal (for Docker), for upstream connectivity to Tor only Bitcoin nodes
- SQLite for local deduplication and state
- Ntfy for self-hosted notifications and commands (effectively a self-hosted bot for your node)
- Optional Mempool integration for richer transaction and fee data
- Argon2id for password hashing
- XSalsa20-Poly1305 for encrypted-at-rest xpubs and credentials (via PyNaCl/libsodium)
- Docker Compose for reliable 24/7 deployment
WWG uses a short list of dependencies. I wanted to strike the right balance between using battletested libraries, especially for sensitive and difficult operations such as cryptography, and reducing bloat and attack surface.
Below are the Python dependencies and what they're used for:
- PyYAML: Reads and writes WWGβs
config.yaml, including wallet definitions, Electrum settings, Tor options, notifications, and all other configuration options. - httpx: Makes HTTP requests for external services such as Mempool lookups, Ntfy endpoints, Electrum servers, and other API calls.
- aiosqlite: Provides async SQLite access for WWGβs local database, including wallet events, transaction history, and stats.
- PyNaCl: Handles cryptographic operations for secure passphrase based encryption and password hashing. This is used to encrypt xpubs at rest.
- python-socks[asyncio]: Adds async SOCKS proxy support, used for routing Electrum or HTTP connections through Tor, especially when connecting to
.onionservices. - segno: Generates QR codes for latest Bitcoin addresses and payment requests.
And for the Rust helper:
- anyhow: Used for simple fallible CLI flow:
Result<()>, custom validation errors viaanyhow!, and extra context on parsing/derivation failures via.context(...) - bitcoin: The core wallet derivation library. Used to parse the supplied
xpub, derive public keys, and generate the four main Bitcoin address types:- Taproot:
p2tr - Native SegWit:
p2wpkh - Nested SegWit:
p2shwpkh - Legacy:
p2pkh
- Taproot:
- clap: Defines the
wwg-deriveCLI interface. It parses thederivesubcommand and arguments like--xpub,--network,--wallet-type,--start,--end, etc.ValueEnumis used for valid wallet/network choices. - hex: Used once to encode each derived addressβs
script_pubkeybytes into a hex string for JSON output. - serde: Used only to derive
SerializeforDerivedRow, the struct representing each derived address row. - serde_json: Serialises the list of derived rows into pretty printed JSON, which the Python side of WWG can consume.
- nostr-sdk: For Nostr notifications via end-to-end encrypted gift wrapped DMs.
Additionally, the Tor Debian package is installed on the Docker image in order to provide the built in Tor functionality that allows connection to upstream Bitcoin nodes that are accessible via an onion service without external proxy configuration.
The xpub and ntfy credentials are encrypted at rest using the Wallet Watchguard passphrase. You need this passphrase whenever the daemon starts.
An xpub cannot spend funds, but it can reveal wallet history and future addresses. Run Wallet Watchguard on infrastructure you control, including your own Bitcoin node, Fulcrum, and ntfy instance for the highest level of privacy.
The Docker image has limited permissions by design. Even though this application is not designed to be accessible to the internet, and therefore the only attack vector would be whatever else is on your own machine, I've still ensured that gaining access to the WWG Docker container doesn't grant root access to it - it runs in its own user inside the Debian based environment the image is built on.

