Deployment

Single-binary deployment with embedded UI, Caddy reverse proxy, and bootstrap peer setup.


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.

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 --provide is 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 --provide on 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 xe service 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

PathContent
ledger/BadgerDB database
host.keylibp2p identity
node.keyNode account key
ssh_host_keySSH 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

NodeLocationIPCore domain
ldnLondon45.77.226.208ldn.core.test.network
ffmFrankfurt192.248.176.245ffm.core.test.network
nycNew York144.202.4.117nyc.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 .id

Current 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 restart

Because 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.