A single xe binary serves everything: the block lattice, the HTTP API, and the web UI (embedded via //go:embed, enabled with --ui). On the public hosts, Caddy sits in front of it as reverse proxy and TLS terminator.
Embedded UI (Recommended)
Single binary deployment. No static file rsync, no separate builds.
xe node --ui \
-dial /ip4/45.77.226.208/tcp/9000/p2p/12D3KooW... \
-api-bind 0.0.0.0 \
-ssh-port 2222(Replace 12D3KooW... with a current peer ID — see Bootstrap Peers.)
[!WARNING] Provider mode requires KVM
--provideis deliberately absent from the example above — it is opt-in, and the live bootstrap nodes do not run it. It enables QEMU/Lima VM provisioning, which hard-requires KVM (/dev/kvm) on the host, and a provider node auto-accepts matching leases as soon as the flag is set. Only add--provideon bare metal or a VPS with nested virtualization enabled.
Reverse Proxy (Caddy + pm2)
On the bootstrap hosts, Caddy terminates TLS and proxies to the node's two listeners; both processes are managed by pm2.
┌─────────────┐
│ Caddy │ :80/:443 (TLS termination)
└──────┬──────┘
┌───────┴────────┐
▼ ▼
xe-node UI server xe-node HTTP API
127.0.0.1:8000 127.0.0.1:8080
(-ui, -ui-port) (-api-port)
│ ▲
└─ /api/* proxy ─┘The explorer and wallet are not separate applications: they are embedded in the binary and served by the node's UI listener. There are no static UI assets to deploy.
Bootstrap Node Setup
Ubuntu 24.04 with Caddy, Node.js 22, pm2, QEMU, Lima. Host provisioning is scripted by deploy/setup-node.sh in the xeprotocol/explorer repository — note that repo is private and archived, so treat the steps below as the canonical list:
- Install Caddy (disable systemd unit, managed by pm2)
- Install Node.js 22 + pm2
- Install QEMU + Lima 1.0.6
- Create
xeservice user with KVM access - Create directories at
/opt/xe/
Directory Layout
/opt/xe/deploy/.env, ecosystem.config.js
/opt/xe/web/docs/
/etc/caddy/Caddyfile
/usr/local/bin/xe-node
/var/lib/xe-node/ledger/, host.key, node.key, lima/, images/Only /docs/* is served from disk; everything else is served by the embedded UI. Older hosts may still have /opt/xe/web/explorer/ and /opt/xe/web/wallet/ directories — those are unserved leftovers from the retired static-asset pipeline.
Data Directory
| Path | Content |
|---|---|
ledger/ | BadgerDB database |
host.key | libp2p identity |
node.key | Node account key |
ssh_host_key | SSH gateway host key |
lima/ | Lima VM state |
lima-templates/ | Lima YAML templates |
images/ | VM base images |
The database lives in ledger/ — wiping any other directory does not clear the chain state.
Provider Node Setup
Bare-metal provider using systemd instead of pm2. No Caddy or web interfaces needed.
- Requirements: Ubuntu 24.04,
/dev/kvm, QEMU, Lima 1.0.6 - Advertise 60–70% of physical resources
Bootstrap Peers
| Node | Location | IP | Core domain |
|---|---|---|---|
| ldn | London | 45.77.226.208 | ldn.core.test.network |
| ffm | Frankfurt | 192.248.176.245 | ffm.core.test.network |
| nyc | New York | 144.202.4.117 | nyc.core.test.network |
Dial multiaddrs take the form /ip4/<ip>/tcp/9000/p2p/<peer-id>. Peer IDs are derived from each node's host.key and change if that key is regenerated, so they are not listed here. Fetch the current ID from each node's API:
curl -s https://ldn.core.test.network/node | jq -r .id
curl -s https://ffm.core.test.network/node | jq -r .id
curl -s https://nyc.core.test.network/node | jq -r .idCurrent Providers
There are none. The two bare-metal provider hosts that served the testnet were retired, and nothing has replaced them yet — so no leases can be accepted, no VMs provisioned and no performance certificates produced on the live network. Query the live list rather than trusting any static table, here or elsewhere:
curl -s https://ldn.core.test.network/providers | jq
# []A provider participates over libp2p only; it has no public DNS or HTTP API of its own.
CI/CD Pipeline
Deploys are manual. Pushing to master runs the test suite but never deploys; a deploy is triggered via workflow_dispatch and gated on a preflight-network-id check that aborts unless the binary's embedded genesis ID matches the live network's ID. The workflow fans out to the bootstrap nodes, and to provider hosts when any are enrolled — there are none at the moment:
workflow_dispatch (ref, expected network id)
│
▼
test ──► preflight-network-id
│
├─► deploy-bootstrap (ldn, ffm, nyc):
│ go build → scp xe-node → setcap → pm2 restart
│
└─► deploy-providers (2 hosts):
go build → scp xe-node → systemctl restartBecause the web UI is embedded in the binary, a UI change ships with the same binary roll — there are no static assets to sync. The documentation sites are separate Next.js applications with their own deploy workflows.