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 are not available right now:
- XUSD is not issued. Only XE exists on this testnet. The stablecoin that prices compute leases has no issuance path yet, so leasing arrives with it.
- No compute providers are online.
xe providersreturns an empty list, soxe leasehas nobody to accept a lease andxe sshhas nothing to connect to.
Prerequisites
- Go 1.25+
- Git
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 (network_id: "testnet") 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:
./xe node \
--data ./data \
--genesis-dir ./genesis/testnet-0001 \
--dial /ip4/45.77.226.208/tcp/9000/p2p/12D3KooWJg4PQYGSfNCupBWZdEWbKj7pgdp5MmmUXbPdBcp6YDtT,/ip4/144.202.4.117/tcp/9000/p2p/12D3KooWEqv1BRZkSntgcgbrJh7bobFRSkBdRupubvNZLEx8hZLAConfirm what you are about to join before you join it:
./xe verify-genesis --genesis-dir ./genesis/testnet-0001The current network is testnet-0001, ledger genesis e813eefe3b61…1ed68, statechain genesis 41946029…33a29. 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/144.202.4.117/tcp/9000/p2p/12D3KooWEqv1BRZkSntgcgbrJh7bobFRSkBdRupubvNZLEx8hZLA |
| New York | https://nyc.core.test.network | /ip4/192.248.176.245/tcp/9000/p2p/12D3KooWEbQ5zDvSz6kKE5ppzwBXKRFsnZbHNjRx94QZFaPeGA4e |
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 .idThere is no ambient peer discovery: mDNS only reaches your LAN and the DHT only resolves peer IDs already known, so the bootstrap list you configure is your node's view of the network. Use more than one. Check that it worked:
curl -s localhost:8080/node | jq '{network_id, peers: (.peers | length)}'[!NOTE] A testnet wipe retires these values
testnet-0001, 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.
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.
Compute
xe providers # List compute providers
xe lease --vcpus 1 --memory 1024 --duration 300 # Create a lease
xe ssh <hash> # SSH into the leased VM[!WARNING] No providers are online
xe providersreturns an empty list on the live testnet today — the two testnet providers were retired and none have replaced them. A lease has nobody to accept it, so it stays open until it is cancelled, and there is no VM to SSH into. The commands work; the counterparty is missing.
Docker Deployment
The repository ships a Dockerfile and a systemd unit in deploy/ — 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 needs a provider, and none are online
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 |