Skip to content

Latest commit

Β 

History

80 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Bastion

BASTION

Sovereign Infrastructure, Operations & AI

Self-hosted systems for private networking, Bitcoin and Lightning, observability, and local AI workflows.


What It Is

Bastion is a deliberate Docker environment for the services that matter:

  • A guarded network edge with private DNS and WireGuard.
  • Bitcoin, Core Lightning, Tor, TEOS, swaps, automation, and backups.
  • Monitoring, administration, and a single operations Hub.
  • An AI control plane built around Claude Code Router and CodeDeck+.

⚠️ Disclaimer

Lightning nodes handle real funds. Software is provided as-is. Use only at your own risk. See CLN security docs.

Before you run this with real funds, read docs/disaster-recovery.md (what to back up, how to restore, how to move hosts) and set up the host firewall (docs/firewall.md).

πŸš€ Quick Start

Prerequisites

  • Docker >= 24.0 & Docker Compose >= 2.0
  • Linux (Debian/Ubuntu)
  • 16GB+ RAM, 4+ CPU cores, 50GB+ storage

Install

chmod +x bastion
./bastion up

What happens:

  • Generates bastion.conf (auto-prompted for Wireguard URL, port, CLN alias)
  • Creates .env symlinks β†’ bastion.conf (one source of truth)
  • Deploys: network β†’ bitcoin β†’ monitor β†’ web β†’ AI

For a subset, name the stacks β€” stack-network is added automatically and always comes up first:

./bastion up stack-ai            # brings up stack-network too
./bastion up network bitcoin

πŸ“¦ Bastion Stacks

Stack Services IPs
network unbound DNS, WireGuard VPN, Pi-hole, Tor 10.10.0.2-3 + transit
bitcoin bitcoind, lightningd, RTL (TEOS opt-in) 10.20.0.2-5 + transit
monitor prometheus, grafana, portainer, node-exporter 10.30.0.2-5
web Bastion operations hub 10.40.0.2
ai Claude Code Router, CodeDeck+ bridge 10.50.0.2-3 + transit

The private stack networks use 10.10.0.0/24 through 10.50.0.0/24. Cross-stack services use the restricted bastion-transit network at 10.254.0.0/24. Pinned there: Tor 10.254.0.2, Core Lightning 10.254.0.10, TEOS 10.254.0.11 β€” the last two publish their onion services through Tor and must advertise a transit address the tor container can route to.

βš™οΈ Commands

Run ./bastion with no arguments on a terminal for the interactive dashboard (stack picker, live status, config editor, log viewer). For scripts and cron, use the subcommands:

./bastion up       [stack ...]        # Start stacks in order (default: all)
./bastion stop     [stack | container]# Stop a stack set, or one container
./bastion down     [stack ...]        # Remove containers (reverse order)
./bastion build    [stack ...]        # Build images without starting
./bastion logs     [stack | container]# Tail logs
./bastion restart  <container>        # Restart one container
./bastion start    <container>        # Start one stopped container
./bastion exec     <container> -- ... # Run a command in a container
./bastion shell    <container>        # Shell into a container
./bastion ps                          # Every container: state + health + stack
./bastion status                      # docker ps table
./bastion versions                    # Image pin vs. running
./bastion audit                       # Check node profitability
./bastion tui                         # Force the dashboard

docker compose never needs to be run by hand: the per-container verbs above (and the TUI's Containers view) cover restart / stop / start / logs / shell.

A stack name may be given with or without the stack- prefix (./bastion up web ai). On up, --recreate-networks drops a stale bastion-network left from the old flat-network layout before starting. --with-watchtower (or BASTION_PROFILES=watchtower) also starts teosd - your own watchtower - which is otherwise off. ./bastion versions shows the image pin vs. what is running.

On a fresh install ./bastion up seeds data/rtl/RTL-Config.json and mints an RTL access rune from CLN; both are created only if absent, so an existing install is untouched. See stack-bitcoin/README.md.

stack-network is the foundation - it owns the bastion-transit network and runs Tor, which the Bitcoin and AI stacks attach to. ./bastion up always brings it up first and adds it automatically when it is left out of the list. For the same reason it will not stop or down stack-network while another stack still has containers running; bring those down first, or pass --force.

Any non-interactive invocation (a pipe, cron, services/bastion-daemon.sh) runs the plain path; the dashboard only opens on a real terminal.

🌐 Access

Service Port Location Version
Hub (Bastion operations hub) 80 http://bastion.node nginx 1.31.5-alpine
RTL (Lightning UI) 3000 http://bastion.node:3000 v0.15.8
Grafana 4001 http://bastion.node:4001 13.2.1
Portainer 4000 https://bastion.node:4000 2.45.0
Prometheus 9090 http://bastion.node:9090 v3.14.0
Pi-hole 8081 http://bastion.node:8081/admin 2026.07.2
CLN REST API 3001 http://bastion.node:3001 (CLN native)
CCR management UI 3458 http://bastion.node:3458 CCR fork commit ec9fc53
Wireguard VPN 51820/udp External (WAN) 1.0.20260223-r0-ls121

Image versions are pinned by digest; ./bastion versions shows the pin and what is actually running. Bitcoin Core (v26.0) and the built images (lightningd-custom, teosd, tor-custom, bastion-claude-code-router) are not on this list β€” they carry no published UI.

Access model & firewall

Every service above is published on 0.0.0.0 by design, so you can reach it three ways:

  • over WireGuard β€” the recommended path from outside the LAN;
  • from localhost on the host itself;
  • from the trusted LAN β€” e.g. a desktop on the same network.

Pi-hole holds a local-DNS record (bastion.node β†’ <host LAN IP>) so the Hub and every panel work by name from all three. The Hub itself links panels using whatever hostname you are browsing with, so you never need to remember a port.

Because the ports are open on every interface, the host firewall is the access control β€” this is not optional in production. docs/firewall.md gives a ready-to-use nftables ruleset and a ufw recipe that allow these ports from the WireGuard subnet and the LAN and drop them everywhere else, in particular from any WAN interface / router port-forward. Only 51820/udp (WireGuard) is meant to face the internet. Portainer (port 4000) mounts the Docker socket β€” firewall it the most tightly of all.

Defaults (change immediately):

  • Grafana: admin:admin
  • Pi-hole: admin:${PIHOLE_PASSWORD}
  • Bitcoin RPC: bitcoind.user:bitcoind.pass
  • CLN REST API: Access via http://localhost:3001

πŸ› οΈ Configuration

./bastion creates and maintains an organized bastion.conf - the single file you edit:

  • Interactive prompts: Wireguard URL/port, CLN node alias
  • Auto-generated: TIMEZONE, PIHOLE_PASSWORD, USER_ID, GROUP_ID, CCR web token
  • AI settings: CodeDeck relay, Tor proxy, Git, Claude, and GitHub variables
  • Idempotent: managed variables are rewritten without duplicates on repeated runs
  • Symlinks: each stack references ../bastion.conf via .env on Linux
  • Secrets: the four secret values (PIHOLE_PASSWORD, CCR_WEB_AUTH_TOKEN, CLAUDE_CODE_OAUTH_TOKEN, GITHUB_TOKEN) are also projected into secrets/ (git-ignored, 600) and delivered to the one service that needs each as a file under /run/secrets/…, not a plaintext env var - so they never appear in docker inspect. bastion.conf stays the source of truth; secrets/ is regenerated from it and rewritten only when a value changes.

On Windows, use a real .env file in each stack directory if symlinks are not enabled. Keep credentials out of Git in every environment.

Key Files

stack-bitcoin/docker-compose.yml    # Edit RPC user/pass, pruning settings
stack-bitcoin/config/cln_config     # CLN configuration (alias, plugins, proxy)
stack-network/docker-compose.yml    # Network, WireGuard, and Tor config
stack-web/html/index.html           # Hub landing page (edit to add/remove panels)
bastion.conf                        # GENERATED - in .gitignore
stack-*/.env                        # SYMLINKS - in .gitignore
stack-*/data/                       # Volumes - in .gitignore
stack-ai/docker-compose.yml        # CCR + CodeDeck+ integration
stack-ai/Dockerfile.ccr             # CCR pinned commit + Claude Code

AI Stack

The AI stack builds Claude Code Router from a pinned commit of the project's CCR fork (CCR_REF / CCR_REPOSITORY in stack-ai/Dockerfile.ccr; bump with gh api repos/deymosh/claude-code-router/commits/<branch> --jq .sha), including its Docker worker fix, and bakes in an OAuth token refresher so the login stays valid without manual re-auth. CCR includes Claude Code and runs unprivileged (cap_drop: ALL, no-new-privileges): its entrypoint wrapper starts as root only to align file ownership to USER_ID/GROUP_ID, then gosu-drops to that user. CodeDeck+ uses its published bridge image (ghcr.io/deymosh/codedeck-plus-bridge), runs as its own non-root user, and routes Claude Code requests through CCR at http://ccr:8080.

AI state is persisted under stack-ai/data/. CCR authenticates with an interactive claude login stored under stack-ai/data/ccr/.claude/; CodeDeck's Claude OAuth token is a separate credential used by the bridge.

Before starting Bastion, add these values to the generated bastion.conf:

CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat-...
GITHUB_TOKEN=                 # optional
CODEDECK_RELAYS=wss://your-trusted-relay.example

CodeDeck relay connections use Bastion's existing Tor service at socks5h://tor:9050 by default. Pair the Android app by reading the bridge logs:

docker logs codedeck-bridge

Bitcoin Core Defaults

-rpcuser=bitcoind.user              # RPC user (change if desired)
-rpcpassword=bitcoind.pass          # RPC password (change if desired)
-prune=20000                        # ~20GB block storage (adjust as needed)
-txindex=0                          # Disabled (not needed for CLN)
-rpcallowip=10.20.0.0/24            # Allow RPC from the Bitcoin stack network
-rpcbind=0.0.0.0                    # Listen on all interfaces (container network)

Note: RPC credentials must match those in stack-bitcoin/config/cln_config and RTL config.

Core Lightning Plugins

Built into the lightningd-custom image; cln_config enables them by default (except backup, which is installed but left commented out):

Plugin Commit Purpose
clboss 95d195f8 Channel autopilot & rebalancing
watchtower-client be344ecc TEOS breach watching
peerswap 23b32d3a Submarine swap rebalancing
backup cb3adab Replication to USB/external
trustedcoin v0.8.6 Chain backend in place of bcli: bitcoind when reachable, public block explorers over Tor as fallback
darknet Local Prefer .onion addresses for peers

CLN Configuration:

# Autopilot settings (clboss)
--clboss-min-channel=1000000       # Minimum channel: 1M sats
--clboss-max-rebalance-fee-ppm=250 # Max rebalancing fee
--clboss-auto-close=false          # Don't auto-close channels

# Autoclean settings (remove failed payments & invoices)
autoclean-failedpays-age=604800    # Remove failed pays after 7 days
autoclean-failedforwards-age=604800 # Remove failed forwards after 7 days
autoclean-expiredinvoices-age=2592000 # Remove expired invoices after 30 days

# Wallet & backup
wallet=sqlite3:///root/.lightning/bitcoin/lightningd.sqlite3:/backup_usb/lightningd.sqlite3

# Network settings
proxy=10.254.0.2:9050              # Tor SOCKS proxy (tor on bastion-transit)
addr=statictor:10.254.0.2:9051     # Tor control port -> static onion
always-use-proxy=true              # Route all traffic through Tor
bind-addr=10.254.0.10:9735         # CLN's own bastion-transit address; this is
                                   # the hidden-service forward target Tor dials,
                                   # so it must NOT be 0.0.0.0 (Tor can't reach it)

Important Plugins:

  • trustedcoin - the chain backend in place of the built-in bcli. It uses bitcoind through the bitcoin-rpc* lines in cln_config when that node is reachable and has the block, and only falls back to public block explorers (over Tor) otherwise. Marked important-plugin.
  • watchtower-client - Critical for channel security (marked as important-plugin)
  • bcli - the built-in backend, disabled (disable-plugin=bcli) so trustedcoin can take over.

TEOS Configuration

TEOS (teosd) is opt-in and off by default β€” start it with ./bastion up --with-watchtower (it carries the watchtower compose profile). Running a watchtower on the same machine as your node is a deliberate choice: it is meant for offering the service to other nodes, not for watching your own (that is the always-on watchtower-client plugin, pointed at an external tower).

stack-bitcoin/config/teos.toml is a template. On ./bastion up --with-watchtower it is copied to stack-bitcoin/data/teos/teos.toml only if that file does not exist yet β€” an established install is never touched. Without it teosd would fall back to rust-teos's compiled-in defaults (api_bind 127.0.0.1, Tor off) and never be reachable at its pinned transit address.

RTL Configuration

RTL connects to CLN via the Bitcoin stack network (10.20.0.2:3001). stack-bitcoin/config/RTL-Config.json is a template; on ./bastion up it is seeded to stack-bitcoin/data/rtl/RTL-Config.json and an access rune is minted from CLN into stack-bitcoin/data/rtl/access.rune (LIGHTNING_RUNE="…", mode 600) β€” both only if absent. If you change CLN RPC credentials or ports, edit the live copy under data/. See stack-bitcoin/README.md.

πŸ”§ Troubleshooting

General troubleshooting steps for common issues. Always check container logs first (docker logs <container>). The commands below assume you are in the project root and have the necessary permissions to run Docker commands. Adjust paths and container names as needed based on your specific setup.

# CLN backup plugin backup not initialized - ensure USB mount is correct and accessible
# Example command to initialize backup plugin with mounted USB path:
docker run --rm -it -v $(pwd)/stack-bitcoin/data/cln:~/.lightning/bitcoin -v /mnt/backup_cln:/backup_usb --entrypoint /usr/local/bin/backup/backup-cli lightningd-custom:latest init --lightning-dir ~/.lightning/bitcoin file:///backup_usb/backup.sqlite.bkp

# CLN backup plugin - restore from backup file
docker run --rm -it -v $(pwd)/stack-bitcoin/data/cln:~/.lightning/bitcoin -v /mnt/backup_cln:/backup_usb --entrypoint /usr/local/bin/backup/backup-cli lightningd-custom:latest restore file:///backup_usb/backup.sqlite.bkp --lightning-dir ~/.lightning/bitcoin

# CLN backup plugin - compact backup file
docker exec lightningd lightning-cli backup-compact

# CLN not connecting to Bitcoin
docker logs lightningd
docker exec lightningd ping -c 3 10.20.0.3

# RTL cannot reach CLN
docker logs rtl
docker exec lightningd lightning-cli getinfo

# DNS issues
docker exec unbound dig @127.0.0.1 google.com

# TOR connectivity (config restart to refresh circuits)
docker kill --signal=HUP tor
docker logs tor

# TOR connectivity (delete Tor state for fresh circuits)
docker stop tor
docker volume rm bastion-tor-data
docker start tor

# Container resource usage
docker stats
df -h

πŸ”’ Security and Boundaries

Each stack has a private Docker subnet. Cross-stack dependencies use the restricted bastion-transit network (10.254.0.0/24). Only 51820/udp (WireGuard) is meant to face the internet; the Hub ports are open on every interface on purpose and the host firewall is the ACL β€” see "Access model & firewall" above and docs/firewall.md.

Internal isolation (never published on the host):

  • Bitcoin RPC: 10.20.0.3:8332 (Bitcoin stack network only)
  • CLN P2P / TEOS API: only on bastion-transit (10.254.0.10:9735 / 10.254.0.11:9814), reachable only through their Tor onion services
  • CCR gateway: ccr:8080 (AI stack network only; the management UI on 3458 is the only CCR port published)
  • CodeDeck bridge: no published host port; relay traffic uses bastion-transit
  • Tor SOCKS/control (9050/9051): bastion-transit only, no host publish

Recommended:

  • Change default passwords (Grafana admin:admin, Pi-hole, Bitcoin RPC)
  • Keep .gitignore / secrets/ protected; never commit bastion.conf
  • Use WireGuard for remote access; apply docs/firewall.md before exposing the host

πŸ“Š Versions

Component Version
Bitcoin Core v26.0
Core Lightning v25.12.1
RTL v0.15.8
Claude Code Router pinned commit ec9fc53 of the CCR fork (CCR_REF in stack-ai/Dockerfile.ccr)
CodeDeck+ bridge v0.11.1
CLN Plugins:
clboss 95d195f8
watchtower-client be344ecc
backup cb3adab
trustedcoin v0.8.6 (replaces the disabled bcli)

πŸ“š Resources


Software provided as-is. Use at your own risk. Change defaults immediately. Never commit secrets.

About

πŸ΄β€β˜ οΈ Sovereign self-hosted infrastructure for Bitcoin, Lightning, privacy, observability, and AI-powered operations.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages