This guide covers building the XE CLI from source, creating a wallet, funding it from the testnet faucet, and making your first transactions.
[!WARNING] Work in progress — not everything here works today XE is pre-1.0 and this is a testnet. Commands are added, changed and broken as development goes, and parts of the network go down. Two things to know before leasing:
- The faucet hands out XE, not XUSD. Leases are priced in XUSD, which is minted only by the operators'
sys.minteraccounts, so unless you have been granted XUSD you can read the compute market but not pay for a lease.- Provider availability is not guaranteed.
xe providersshows who is online right now (two providers at the time of writing); when it is empty,xe leasehas nobody to accept a lease andxe sshhas nothing to connect to. A provider advertisement alone does not prove that acceptance, provisioning and SSH work end to end.
Prerequisites
- Go 1.25+ (the repository pins the exact release in
.go-version;export GOTOOLCHAIN="go$(cat .go-version)"selects it without a version manager) - Git and curl
make— it drives the build and is not present on a clean Ubuntu image, so install it first (sudo apt-get install -y make) or the first build command below fails
Building from Source
The source is public at github.com/xeprotocol/xe, licensed under the GPL-3.0 — use it, modify it, redistribute it, provided you pass on the same freedoms and publish the source of anything you distribute. No release binary has been tagged yet, so building is the way to get one.
git clone https://github.com/xeprotocol/xe.git
cd xe
make build # produces ./xemake is the single definition of how the binary is built. Builds are reproducible — pinned toolchain (.go-version), -trimpath, -buildvcs=false, CGO_ENABLED=0 — so the same commit produces the same bytes on any machine. make verify-repro proves it locally.
One binary does everything: xe node runs the daemon, and every other subcommand is a client that talks to a node's HTTP API.
Running a Node
Start a node with default settings: libp2p on port 9000, HTTP API on port 8080, data in ./data.
./xe nodeConnecting to the Testnet
A node's identity is its genesis, not its binary. A stock build embeds a placeholder genesis that no live network uses, and peers ban a node whose network ID does not match theirs. Point the node at the published genesis bundle instead, and it joins the live network without a rebuild. Verify the bundle first, then start:
./xe verify-genesis --genesis-dir ./genesis/testnet-0005 \
--expect-network-id testnet-0005
./xe node \
--data "$HOME/.xe/testnet-0005" \
--genesis-dir ./genesis/testnet-0005 \
--dial /ip4/45.77.226.208/tcp/9000/p2p/12D3KooWJg4PQYGSfNCupBWZdEWbKj7pgdp5MmmUXbPdBcp6YDtT,/ip4/144.202.4.117/tcp/9000/p2p/12D3KooWEqv1BRZkSntgcgbrJh7bobFRSkBdRupubvNZLEx8hZLA \
--port 9000 --api --api-port 8080--data holds the node's persistent state and identity; after a testnet wipe use the new genesis bundle and a fresh data directory, or the node refuses to start with genesis mismatch: this data dir belongs to a different network.
The current network is testnet-0005, ledger genesis 3ff640410d78…d1e150, statechain genesis d01aabc1a29d…8f296d6 (confirm both against GET /node and GET /statechain/blocks/0 on a bootstrap node; the genesis bundle is published in the repository's genesis/testnet-0005/ directory). There are three bootstrap nodes:
| Node | API | p2p |
|---|---|---|
| London | https://ldn.core.test.network | /ip4/45.77.226.208/tcp/9000/p2p/12D3KooWJg4PQYGSfNCupBWZdEWbKj7pgdp5MmmUXbPdBcp6YDtT |
| Frankfurt | https://ffm.core.test.network | /ip4/192.248.176.245/tcp/9000/p2p/12D3KooWEbQ5zDvSz6kKE5ppzwBXKRFsnZbHNjRx94QZFaPeGA4e |
| New York | https://nyc.core.test.network | /ip4/144.202.4.117/tcp/9000/p2p/12D3KooWEqv1BRZkSntgcgbrJh7bobFRSkBdRupubvNZLEx8hZLA |
Peer IDs are derived from each node's key and change if that key is regenerated. Fetch the current one from the node's API:
curl -s https://ldn.core.test.network/node | jq -r .idThe bootstrap list you configure is your node's first view of the network, so use more than one. Once connected, the node also advertises itself in the DHT under xe/discovery/1/<network_id> and dials peers it finds there (up to 24, --discovery-peers; --no-discovery turns this off), and mDNS finds peers on your LAN (--disable-mdns). Check that it worked:
curl -s localhost:8080/node | jq '{network_id, peers: (.peers | length)}'Confirm network_id is testnet-0005 and peer_count becomes greater than zero.
[!NOTE] A testnet wipe retires these values
testnet-0005, the genesis hashes and the bundle above are all discarded when the protocol changes and the network is re-bootstrapped. Re-check this page after a wipe, or ask the network itself:curl -s https://ldn.core.test.network/statechain/kv/sys.network_id.
Using the CLI
Point the CLI at a testnet node and interact without running your own:
export XE_NODE=https://ldn.core.test.networkHello world — two wallets and a transfer
The shortest useful thing you can do: create two wallets, fund one, send to the other. Keep each wallet in its own file with XE_WALLET.
XE_WALLET=~/.xe/alice.seed xe wallet create
XE_WALLET=~/.xe/bob.seed xe wallet createWallet created!
Address: 7d27d0a34cc2a5cd08f65905a983fabec1a517baf6d3cdab0a921256ecb9af57
Public key: 665b50f96f8a4a86e1940386cce7fa1c0592c8eba9524fe9d579254fc341f02b
File: /home/you/.xe/alice.seedThe address is what you hand out, and it is not the public key — it is sha256("xe/account/v1" ‖ pubkey), so identity and credential stay separate. The seed file is the account: back it up, and treat anyone who has it as the owner of the funds.
Fund Alice, then claim the grant:
XE_WALLET=~/.xe/alice.seed xe faucet
XE_WALLET=~/.xe/alice.seed xe receive
XE_WALLET=~/.xe/alice.seed xe wallet balanceSend Bob 25 XE, using the address printed for Bob above, and let Bob claim it:
XE_WALLET=~/.xe/alice.seed xe send <bob-address> 25 --asset XE --memo "hello world"
XE_WALLET=~/.xe/bob.seed xe receive
XE_WALLET=~/.xe/bob.seed xe wallet balanceEvery transfer is two blocks — a send on the sender's chain and a receive on the recipient's — so funds sit as pending until the recipient signs for them. Nothing lands in an account without a block signed by its own key. Both sides settle in a few seconds, and neither pays a fee.
Any account is public, so you can watch the same thing from outside:
curl -s $XE_NODE/accounts/<address>/balance
curl -s $XE_NODE/accounts/<address>/chain[!NOTE] How the faucet works
xe faucetasks the faucet service (XE_FAUCET, defaulthttps://faucet.test.network) for a grant — a bare HTTP POST, no proof-of-work. It sends 1,000 XE per account per day from a pre-funded wallet; it mints nothing and holds no minter key. A repeat request inside that window returns429with aretry_after_seconds. The grant arrives as a pending send, so follow it withxe receive.
Messaging
Chat is off-chain and free. The recipient registers so the network knows which node they listen on; registrations lapse after 30 minutes, and --watch keeps one alive:
XE_WALLET=~/.xe/bob.seed xe directory register
XE_WALLET=~/.xe/alice.seed xe chat send <bob-address> "hello bob"
XE_WALLET=~/.xe/bob.seed xe chat read # --follow --json streams new messagesCompute
xe providers # List compute providers
xe lease --vcpus 1 --memory 1024 --duration 300 # Create a lease (needs XUSD)
xe lease status <hash> # Watch it get accepted and settled
xe ssh <hash> # SSH into the leased VM[!NOTE] Leasing needs XUSD and a provider Two providers are online on
testnet-0005and leases are being accepted, renewed and settled — but they are priced in XUSD, which only the operators' minter accounts issue, so a fresh wallet cannot fund one yet. Always checkxe providersfirst: provider availability changes independently of releases, and an empty list means a lease has nobody to accept it.
Docker Deployment
The repository ships a Dockerfile, a systemd unit, a pm2 ecosystem file and a Prometheus/Grafana/Alertmanager bundle in deploy/, and docs/run-a-node.md is the full operator guide — use those rather than the sketch below if you are running a node for real. A minimal equivalent:
FROM golang:1.25-alpine AS build
ARG VERSION=dev
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags "-X main.version=${VERSION}" -o /xe ./cmd/xe/
FROM alpine:3.20
COPY --from=build /xe /usr/local/bin/xe
EXPOSE 8080 9000
ENTRYPOINT ["xe", "node"]Web-Based Quick Start
If you prefer a browser-based experience, use the hosted web wallet at ldn.test.network/wallet:
- Open the wallet and create a new wallet (your seed is encrypted client-side with AES-GCM)
- Request testnet XE from the faucet — 1,000 XE per account per day
- Explore the network via the Explorer
- Send transactions or use chat. Leasing compute is CLI-only and needs XUSD, which testers cannot obtain yet
Environment Variables
| Variable | Default | Description |
|---|---|---|
XE_NODE | https://ldn.core.test.network | Node API URL |
XE_WALLET | ~/.xe/wallet.seed | Wallet seed file |
XE_FAUCET | https://faucet.test.network | Faucet service URL |
XE_SSH_HOST | ldn.test.network | SSH gateway hostname |
XE_SSH_PORT | 2222 | SSH gateway port |