# XE Testnet — full site as Markdown

> Public testnet for XE, an open-source Layer 1 for decentralized compute and networking: per-account block lattice, ~3s finality, zero fees, signed P2P chat, multisig governance and compute leasing, with a JSON HTTP API on every node.

Every page of https://test.network, generated from the same content the HTML renders from. The index with when-to-use guidance is at https://test.network/llms.txt; the API contract is at https://test.network/openapi.json.

Contents:

- XE Testnet — https://test.network/
- About — XE Testnet — https://test.network/about
- Bug Bounty — XE Testnet — https://test.network/bounty
- Contact — XE Testnet — https://test.network/contact
- Developers — XE Testnet API, OpenAPI spec, CLI and SDK docs — https://test.network/developers
- XE Network Documentation — https://test.network/docs
- Accounts & Keys — https://test.network/docs/accounts
- API Reference — https://test.network/docs/api
- Architecture — https://test.network/docs/architecture
- Assets (XE & XUSD) — https://test.network/docs/assets
- Block Lattice — https://test.network/docs/block-lattice
- CLI Reference — https://test.network/docs/cli
- Compute Leasing — https://test.network/docs/compute
- Consensus — https://test.network/docs/consensus
- System Constants — https://test.network/docs/constants
- Cryptography — https://test.network/docs/cryptography
- Deployment — https://test.network/docs/deployment
- Binary Encoding — https://test.network/docs/encoding
- Explorer & Web UI — https://test.network/docs/explorer
- Getting Started — https://test.network/docs/getting-started
- Networking — https://test.network/docs/networking
- State Chain — https://test.network/docs/state-chain
- Proof of Uptime — https://test.network/docs/uptime
- Web Wallet — https://test.network/docs/wallet
- Explorer — XE Testnet — https://test.network/explorer
- Privacy — XE Testnet — https://test.network/privacy
- XE Testnet — https://test.network/wallet

# XE is a decentralized compute & networking platform.

`Ledger` · `Compute` · `Messaging` · `Governance` · `Identity (soon)`

An account chain each, transactions final in seconds with zero fees, XE carrying the vote weight, signed P2P chat and multisig governance — with leasing built on top, so a VM can be rented from a stranger and settled on-chain. Built for people and machines. **This is a testnet under active development, and not all of it works yet** — the status below is honest about which parts. Come stress-test it.

- [Open Web Wallet](https://ldn.test.network/wallet/)
- [Get Started](https://test.network/docs/getting-started)
- [Source on GitHub](https://github.com/xeprotocol/xe)
- [Open Explorer](https://test.network/explorer)

```sh
git clone https://github.com/xeprotocol/xe
cd xe && make build
./xe wallet create
```

- network: testnet-0001
- finality: ~3s
- fees: 0
- providers: 0

## Status

XE is pre-1.0 and this is a testnet, so some of what is described on this site is built and running, and some of it is not. Here is the split, as it actually stands today.

### Working today

- Build the binary and run a node that joins testnet-0001
- Join the P2P XE network
- Wallets, addresses and keys
- Get 1,000 XE per account per day from the faucet
- Send, receive and check balances in XE
- Burn XE
- Explorer and wallet UI, embedded in the node
- REST API and SSE event streams
- Signed peer-to-peer chat

### On the roadmap, not testable yet

- XUSD issuance and use for compute leasing
- Compute leasing including escrow, collateral, attested timing and settlement
- SSH over XE into your leased VM
- GPU compute
- End-to-end encrypted messaging
- Dispute arbitration

## We want this testnet hammered

- Make transactions, run a node, and try to make the ledger disagree with itself
- Try to spam it, grief it, or find consensus bugs
- Feed a node malformed blocks and see what falls over
- Report anything weird → [open an issue on GitHub](https://github.com/xeprotocol/xe/issues)

Top bug hunters & heavy users will be **recognized on mainnet**. The bug bounty runs in phases — Phase 1, transactions & running a node, is open now. [Bug Bounty](https://test.network/bounty)

- Finality: ~3s
- Fees: 0
- Bootstrap Nodes: 3
- Providers Online: 0
- XE Supply: 42M

## Quick Start

Build the binary, make two wallets, move XE between them. The terminal below is a simulation of the real CLI — type in it. Against the live network the faucet works; the compute commands have no provider to talk to, but the shell will still show you what they do.

## Tooling

- **[xeprotocol/xe](https://github.com/xeprotocol/xe)** — The node source, GPL-3 — clone it, build it, run it
- **[xe](https://test.network/docs/cli)** — Wallet, lease, SSH, send/receive, offline signing
- **[HTTP API](https://test.network/docs/api)** — REST :8080 — accounts, blocks, leases, chat, VMs
- **[Explorer](https://test.network/explorer)** — Embedded web UI — accounts, blocks, state chain
- **[Web Wallet](https://ldn.test.network/wallet/)** — Client-side keys, multi-wallet, chat, DAO signing

## Capabilities

- **Block Lattice** — Every account has its own chain. Transactions are parallel, non-blocking, and final in seconds. No miners, no mempool.
- **P2P Networking** — Peer discovery and data propagation over libp2p. Nodes find each other automatically and sync directly, peer to peer.
- **Fast Consensus** — Every block finalizes through representative voting: converge, commit-lock, then an irrevocable final vote at ≥67% of delegated weight — in parallel across account chains.
- **XE Today, XUSD Later** — XE is the native asset: it carries voting weight and is what providers earn when a lease settles. XUSD, the stablecoin that will price compute, is specified but not issued on this testnet yet — a mintable asset must never mint consensus power, so only XE ever votes. Zero transaction fees.
- **Signed Messaging** — Send ed25519-signed messages between any two accounts, in real time. Transport-encrypted via libp2p Noise/TLS 1.3; end-to-end payload encryption is coming.
- **Earn by Providing** — Run a provider node, offer your spare compute, earn XE emissions every time a lease settles. Bare metal or cloud — your choice. The testnet has no providers right now, so this side of the market is wide open.
- **Compute Leasing (not open yet)** — Lease a real VM from a provider, SSH in, settle on-chain. Escrow, collateral, attested timing and cancellation are all built — but leases are priced in XUSD, which is not issued on this testnet yet, and no providers are online to accept one.
- **Multisig Governance** — Network parameters evolve through state-chain op blocks with M-of-N multisig signing. No hard forks.
- **Provable Uptime (upcoming)** — Design stage — providers will prove they were online and serving via cryptographic uptime proofs. Not yet implemented.

## What can you build?

- **Decentralized VPS** — Lease VMs from providers. SSH in, run workloads, settle on-chain.
- **CI/CD Runners** — Ephemeral compute for build pipelines. Lease, test, settle.
- **Inference Swarms** — Lease VMs across providers. Deploy models in parallel.
- **Agent Payments** — Finality in seconds, zero fees, own chain per agent.
- **Signed Chat** — libp2p streams. ed25519 signed. SSE delivery. DHT discovery.
- **Governance Tools** — State-chain op blocks. M-of-N multisig signing. Live tip updates.
- **Block Explorers** — REST API analytics. Accounts, leases, conflicts.
- **Provider Nodes** — Advertise resources, provision VMs, earn XE emissions.
- **Uptime Proofs** — Design stage. Heartbeat chains, merkle epochs planned.
- **Wallet Apps** — tweetnacl + blakejs. AES-GCM seeds. PBKDF2 derivation.
- **Multisig Treasuries** — M-of-N accounts. Hash-derived addresses. Keyset rotation.
- **Directories** — Account-to-peer mapping. Signed registrations, TTL expiry.

## Start Building

The node source is public under the GPL-3 — clone it, build it, run it, break it. Findings in the active bounty phase are worth XE.

- [Get Started](https://test.network/docs/getting-started)
- [Source on GitHub](https://github.com/xeprotocol/xe)
- [Documentation](https://test.network/docs)
- [Bug Bounty](https://test.network/bounty)

---

Canonical HTML: https://test.network/ · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# About XE Testnet

test.network is the public testnet site for XE, an open-source Layer 1 protocol for decentralized compute and networking. It is built and operated by **XE L1 Ltd.**, a UK company (registration number 17245674), and everything on this site — the node, the CLI, the wallet, the explorer and these docs — is published under the GPL-3.

- [Read the Docs](https://test.network/docs)
- [Source on GitHub](https://github.com/xeprotocol/xe)
- [Contact](https://test.network/contact)

## What XE is

XE gives every account its own block chain — a block lattice rather than a single global chain — so transactions are parallel, settle in about three seconds and carry no fee. XE is the native asset and the only one that carries voting weight; XUSD, the stablecoin that will price compute, is specified but not issued on this testnet. On top of the ledger sit signed peer-to-peer chat over libp2p, a multisig-governed state chain for network parameters, and a compute market in which a provider leases a real VM to a consumer and the lease is escrowed, attested and settled on-chain.

The protocol is designed for people and machines alike: an autonomous agent can hold an account, draw from the faucet, pay another agent and, once leasing opens, rent compute — all through the same JSON HTTP API that every node exposes and that this site documents in full, including an OpenAPI 3.1 description at /openapi.json.

## Where the project stands

XE is pre-1.0 and this is a testnet under active development, so the site is deliberately honest about what works. Today you can build the node, join testnet-0001, create wallets, receive 1,000 XE per account per day from the faucet, send, receive and burn XE, read everything through the REST API and SSE streams, and chat between accounts with signed messages. Compute leasing, XUSD issuance, SSH into leased VMs, GPU compute, end-to-end encrypted messaging and dispute arbitration are built or designed but not yet testable. The testnet may be wiped at any time, and XE on it has no monetary value.

The purpose of the testnet is to be broken: a phased bug bounty pays in native XE at mainnet launch for findings against the active phase, and top bug hunters and heavy users are recognised on mainnet. The mainnet project itself lives at xe.network; this site covers the testnet only.

## Resources

- **[Documentation](https://test.network/docs)** — Getting started, architecture, protocol and reference
- **[API Reference](https://test.network/docs/api)** — Every node endpoint, executable, with an OpenAPI spec
- **[xeprotocol/xe](https://github.com/xeprotocol/xe)** — The node source, GPL-3 — clone it, build it, run it
- **[Bug Bounty](https://test.network/bounty)** — Phased programme, paid in XE at mainnet launch
- **[llms.txt](https://test.network/llms.txt)** — A map of the site for agents, with when-to-use guidance

---

Canonical HTML: https://test.network/about · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Find a bug. Earn XE.

Help harden the protocol before genesis. The programme runs in phases — one area of functionality at a time, open across the foreseeable future of development. **Phase 1 — transactions & running a node** — is the active scope. Report vulnerabilities on GitHub and earn up to **10,000 XE** per finding — paid in native XE at mainnet launch. Rewards are discretionary and contingent on launch; the bounty pool is still being finalized, and XE carries no guaranteed monetary value.

- [Submit a Report](https://github.com/xeprotocol/xe/issues/new)
- [Phase 1 Scope](https://test.network/bounty#scope)
- [Reward Tiers](https://test.network/bounty#tiers)
- [Critical? Email first](mailto:security@xe.network)

- Active Phase: 1 / 7
- Top Tier (XE): 10,000
- Severity Tiers: 5
- Submission Channel: GitHub
- Payout Date: Genesis

## Programme Phases

The bounty is a standing programme, not a one-off event: it opens one area of the system at a time and stays open as the protocol is built. Phase 1 is live now. Later phases are listed here by title only — each one's scope, examples and rules go up when that phase opens, and no phase closes the programme.

- **Phase 1** — transactions & running a node (open now)
- **Phase 2** — p2p networking & sync (upcoming)
- **Phase 3** — consensus & finality (upcoming)
- **Phase 4** — api, web ui & user surfaces (upcoming)
- **Phase 5** — leases, escrow & compute (upcoming)
- **Phase 6** — tokenomics & emission (upcoming)
- **Phase 7** — gpu compute (upcoming)

Phases open as each area settles, not on a fixed calendar. Findings outside the active phase are not eligible for reward yet — except Critical and Severe findings, which we want immediately whatever area they land in, and always pay.

## Phase 1 Scope

Phase 1 is the entry point to the system: everything one node and one keypair can do on their own or against themselves. That is the scope of the programme today — set your testing to it.

### In Scope — Phase 1

- Block types and validation — send, receive, mint, burn
- XE accounting in micro-units
- Wallet and CLI key handling — ed25519 keygen, signing, addresses
- Node lifecycle — config, storage, restart and recovery
- Crash resistance against malformed or crafted input
- Local node API basics
- Docs and site errors that misstate any of the above

### Out of Scope

- Areas held for a later phase (unless Critical or Severe)
- Social engineering of XE staff or users
- Physical attacks on hardware
- Volumetric DDoS against testnet
- Spam/rate-limit abuse without protocol impact
- Third-party deps without XE-specific exploit
- Marketing pages without functional impact
- Self-XSS and missing headers without exploit
- Issues requiring a compromised user device

## Reward Tiers

The same five tiers apply in every phase. Severity assigned by the XE core team based on impact, exploitability, and report quality. Amounts are targeted ceilings — exceptional findings may exceed them — and are provisional until the bounty pool is finalized ahead of mainnet launch.

| Tier | Reward | Description |
| --- | --- | --- |
| Critical | 10,000 XE | Catastrophic protocol breaks. Unauthorized mint, double-spend, key recovery, or lattice compromise. |
| Severe | 5,000 XE | Serious breaks short of catastrophe. Signature forgery, validation bypass, node compromise — exploitable and damaging at scale. |
| High | 2,500 XE | Targeted DoS, race conditions, replay attacks, privilege escalation. |
| Medium | 1,000 XE | Validation gaps, fee mismatch, non-sensitive disclosure, inconsistent API responses. |
| Minor | 100 XE | UI bugs, typos, broken explorer views, misleading log messages, documentation errors. |

## Bug Classes

Illustrative Phase 1 examples per tier. If you find something impactful inside the Phase 1 scope that doesn't fit below, report it anyway.

| Severity | Class | Examples | Reward |
| --- | --- | --- | --- |
| Critical | Supply & ledger integrity | unauthorized mint · double spend · balance inflation · micro-unit overflow | 10,000 XE |
| Critical | Cryptographic compromise | key recovery · signature forgery · identity hijack | 10,000 XE |
| Severe | Block validation bypass | malformed block accepted · send/receive/burn rule bypass · unsigned state change | 5,000 XE |
| Severe | Node compromise & ledger loss | crash from crafted input · storage corruption · unrecoverable restart | 5,000 XE |
| High | Race conditions & state transition bugs | race condition · replay attack · state inconsistency · TOCTOU | 2,500 XE |
| High | Key handling & local privilege | key material exposure · unsafe file permissions · unauthorized local API action | 2,500 XE |
| Medium | Validation & accounting edge cases | fee mismatch · validation gap · API inconsistency · rounding | 1,000 XE |
| Medium | Information disclosure | metadata leak · verbose error · debug exposure | 1,000 XE |
| Minor | UI/UX, docs & cosmetic | layout · responsive · a11y · typo · broken link · log noise | 100 XE |

## Leaderboard

Ranked by total XE awarded across every phase. Updated when the site is redeployed after reports are triaged.

**No reports accepted yet.** File a finding to claim the top spot.

Updated manually when the site is redeployed as reports are accepted and paid.

## How to Report

Reports are filed as public issues on [github.com/xeprotocol/xe](https://github.com/xeprotocol/xe/issues/new). Critical/Severe findings go privately by email first — we coordinate disclosure and open the public issue once a patch has shipped.

1. **Reproduce against testnet** Verify on `test.network`. Capture tx hashes, block heights, exact reproduction steps.
2. **File a GitHub issue** Open an issue at [github.com/xeprotocol/xe/issues/new](https://github.com/xeprotocol/xe/issues/new) with a suggested severity tier and the Phase 1 area it lands in. Public by default — for Critical/Severe findings, see step 4 instead.
3. **Include a clear PoC** Minimal reproduction script or test case. Impact analysis: who's affected, worst case.
4. **Critical/Severe: email first** Findings that risk funds or the network go privately to `security@xe.network`— not a public issue. Ask for an encryption key first; we'll reply with one before you send details, then with a tracking ID. A public issue goes up once a patch ships.
5. **Triage & acceptance** Core team confirms, assigns severity, and replies on the issue (or by email with a tracking ID for Critical/Severe) — we apply tracking labels ourselves on triage.
6. **Payout at genesis** Accepted bounties pay in native XE at mainnet launch, if and when it happens. Provide an XE address (or a designated mainnet address) in your report.

## Rules

- Phase 1 is the active scope — findings inside it are eligible now, and other areas become eligible as their phase opens
- Critical and Severe findings are eligible in any area, in any phase — send those straight away
- Test only on testnet (test.network) — never on mainnet once live
- Don't pivot to attack other users' funds, keys, or workloads
- Report Critical/Severe findings by email, not a public issue — we'll reply with a tracking ID, and a public issue goes up once a patch ships
- Good-faith research that follows these rules is authorized; we won't pursue legal action over it
- One report per bug. Chained findings can be split across reports
- Duplicate reports go to the first verifiable submission
- Severity, eligibility, and reward at discretion of XE core team
- Payouts in native XE at mainnet genesis, contingent on launch. No cash equivalent, no guaranteed monetary value
- XE team members and direct contractors are not eligible

## Ready to break things?

Spin up an account on testnet, hammer Phase 1 — transactions and a node of your own — and tell us what falls over.

- [Submit a Report](https://github.com/xeprotocol/xe/issues/new)
- [How to Report](https://test.network/bounty#how)
- [Critical? Email first](mailto:security@xe.network)

---

Canonical HTML: https://test.network/bounty · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Get in touch.

The XE testnet is run by XE L1 Ltd. Most conversations happen in the open on GitHub; general enquiries go to [`hello@xe.network`](mailto:hello@xe.network), and anything security-sensitive goes to the security address first. There is no support desk and no ticketing system — the channels below are the real ones, and they are all read.

- [Open an Issue](https://github.com/xeprotocol/xe/issues/new)
- [Email Us](mailto:hello@xe.network)
- [Email Security](mailto:security@xe.network)
- [Bug Bounty](https://test.network/bounty)

## Channels

1. **Security vulnerabilities** Email [`security@xe.network`](mailto:security@xe.network). Critical and Severe findings must go by email rather than a public issue: we reply with a tracking ID, coordinate disclosure, and open the public issue once a patch has shipped. Findings in the active bug bounty phase are rewarded in XE at mainnet launch.
2. **Bugs, questions and feature requests** Open an issue at [github.com/xeprotocol/xe/issues](https://github.com/xeprotocol/xe/issues). Include the node version, the network id (testnet-0001), what you did and what you expected — the more reproducible, the faster it gets fixed. Documentation errors on this site are bugs too and count for the bounty's Minor tier.
3. **General, business, press and legal** Email [`hello@xe.network`](mailto:hello@xe.network) for anything that is not a bug or a vulnerability — partnerships, press, legal and privacy requests about this site. Postal correspondence goes to the registered office of XE L1 Ltd. (company number 17245674): 128 City Road, London, EC1V 2NX, United Kingdom.
4. **Agents and automated integrations** No human is needed to integrate: the API needs no key for reads, the contract is at [`/openapi.json`](https://test.network/openapi.json), and [`/llms.txt`](https://test.network/llms.txt) explains when XE is the right tool. If something in those files is wrong, that is a GitHub issue.

Response times are best effort. Security reports are acknowledged first; everything else is triaged on GitHub in the order it arrives.

---

Canonical HTML: https://test.network/contact · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Build on XE Testnet.

Everything needed to integrate with the XE testnet from code or from an AI agent, on one page: the JSON HTTP API every node exposes, its [OpenAPI 3.1 description](https://test.network/openapi.json), how it is versioned, how it is rate limited, how it reports errors, and where the CLI, wallet and source live. Reads need no API key. Amounts are integer micro-units (1 XE = 1,000,000).

- [API Reference](https://test.network/docs/api)
- [OpenAPI spec](https://test.network/openapi.json)
- [Getting Started](https://test.network/docs/getting-started)
- [llms.txt](https://test.network/llms.txt)

## Resources

- **[API Reference](https://test.network/docs/api)** — Every endpoint, executable against the live node
- **[OpenAPI 3.1](https://test.network/openapi.json)** — operationIds, schemas, security schemes, headers
- **[API discovery](https://test.network/api/v1)** — JSON index of endpoints, servers, auth and limits
- **[xe CLI](https://test.network/docs/cli)** — Wallet, faucet, send/receive, node, offline signing
- **[@xeprotocol/sdk](https://github.com/xeprotocol/sdk)** — TypeScript client — pre-release, not on npm, build from source
- **[xeprotocol/xe](https://github.com/xeprotocol/xe)** — Node, CLI, wallet and explorer source, GPL-3
- **[llms.txt](https://test.network/llms.txt)** — Site map for agents with when-to-use guidance

## Base URLs and versioning

The API is versioned in the URL path. The current major version is **v1** at [`https://test.network/api/v1`](https://test.network/api/v1). Pin that in anything that must not move. The unversioned [`https://test.network/api`](https://test.network/api) is an alias for the current major version and follows it when it changes. Every response carries `X-API-Version: 1`. The node itself, `https://ldn.test.network/api`, is unversioned and tracks the node release; it does not add the headers or JSON error wrapping described here.

- Within a major version changes are additive only: new endpoints, new optional fields, new enum values. Ignore fields you do not recognise.
- Removing or renaming a field, changing a type or an endpoint's meaning happens only in a new major version, and the previous major keeps working for at least 180 days after the new one ships.
- Deprecated endpoints and versions are marked deprecated: true in the OpenAPI document and answer with a Deprecation header (RFC 9745, Deprecation: @<unix-seconds>), a Sunset header (RFC 8594, the HTTP-date of removal) and a Link rel="deprecation" to the migration notes. Sunset is never less than 90 days after the Deprecation date.
- Testnet wipes reset ledger state, not the API. Identifiers change; the contract does not.

The same policy is published machine-readably as x-versioning in /openapi.json and as versioning in the /api/v1 discovery document.

## Authentication

Most reads are open: no key, no sign-up. Two endpoints are operator-only and take `Authorization: Bearer <XE_API_ADMIN_TOKEN>` (`POST /lease/request` and the unfiltered `GET /chat/events` firehose); the public node does not hand that token out. Reading a specific account's chat needs an ownership proof: fetch a challenge from `GET /chat/auth/challenge`, sign sha256("xe/chat-read-auth/v1\0" || challenge) with the account key, and pass account, pub_key, challenge and sig as query parameters. Writes are signed blocks you construct and sign yourself — the node never signs on your behalf. The security schemes are declared in the OpenAPI document.

## Rate limits

Limits are per client IP and per request class, matching the node's own: **reads** (GET, HEAD) 1,000 requests per 5 seconds, **writes** (POST, PUT, PATCH, DELETE) 50 requests per 5 seconds — 200/s and 10/s sustained. Every response from test.network/api tells you where you stand, so you can throttle before being refused:

- RateLimit-Policy: "read";q=1000;w=5 — the policy in force (quota q per window w seconds), per draft-ietf-httpapi-ratelimit-headers.
- RateLimit: "read";r=998;t=4 — requests remaining (r) and seconds until the window resets (t).
- RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset — the same three numbers in the older header form.
- Retry-After — on a 429 only: how many seconds to wait. The body is the JSON Error shape with code rate_limited.

Streaming endpoints (SSE) count once when opened. The direct node endpoint enforces the same limits but does not send the headers.

## Errors

Every 4xx and 5xx from test.network/api is JSON. The body is the `Error` schema from the OpenAPI document: `error.code` (machine-readable, snake_case: bad_request, unauthorized, forbidden, not_found, method_not_allowed, rate_limited, upstream_error, upstream_unavailable, upstream_timeout), `error.status`, `error.message` (what happened), `error.hint` (what to do next) and `error.docs` / `error.openapi` links. When the node itself answered with a non-JSON error, its status and body are kept under `error.upstream`. The node's own JSON errors pass through unchanged. Each operation in the OpenAPI document lists its error responses, all typed against the same schema.

## For AI agents

Every page on this site returns Markdown for `Accept: text/markdown` at its own URL. [`/llms.txt`](https://test.network/llms.txt) maps the site and says when XE is the right tool; [`/llms-full.txt`](https://test.network/llms-full.txt) is the whole site in one file. The OpenAPI document has a unique operationId, a description and typed parameters on every operation, so it can be loaded directly as a tool definition.

---

Canonical HTML: https://test.network/developers · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# XE Network Documentation

> Block-lattice cryptocurrency with compute leasing, DAO governance, and P2P messaging.

XE is a decentralized compute and networking platform. Consumers pay **XUSD** to lease real VMs from providers; providers earn newly minted **XE** when a lease settles. Underneath sits a lattice ledger where every account maintains its own chain, with cross-chain references forming a directed acyclic graph — parallel transactions, no miners, no global bottleneck — extended with DAO governance, peer-to-peer messaging and an account directory.

> \[!WARNING] Everything here is a work in progress
> XE is pre-1.0 and runs on a **testnet only**. These docs describe the system as designed and as built, and the two are not always the same thing: **not every command or endpoint documented here works today**, and parts that do work go down without notice. Where something is known to be unavailable, the page says so — start with [Getting Started](/docs/getting-started), which lists what is currently broken.
>
> There is no backward compatibility. Protocol changes ship as a testnet wipe: the network is re-bootstrapped under a new network ID and every account, balance and block on it is discarded. Testnet coins have no value.
>
> The source is public at [github.com/xeprotocol/xe](https://github.com/xeprotocol/xe) under the GPL-3.0, and bugs are worth money — see the [bug bounty](/bounty).

## Core Properties [#core-properties]

* **Account-chain architecture.** Each account has its own chain of blocks. Sends debit the sender's chain; receives credit the recipient's chain. No miners, no global ordering.
* **Dual assets.** XE is emitted as a reward for compute providers and confers voting weight for consensus. XUSD is used to pay for compute leases; it carries no consensus weight, because XUSD is mintable by authorized minters and minting must never mint voting power.
* **Compute leasing.** Consumers lease virtual machines from providers. Resource costs are denominated in XUSD; providers stake collateral and earn XE emissions upon settlement.
* **DAO governance.** A deterministic state chain allows the network to evolve parameters through signed proposals without hard forks.
* **P2P messaging and directory.** Nodes exchange messages and register in a decentralised account directory.

## Components [#components]

| Component           | Stack                                                           | Description                                                               |
| ------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------- |
| **Core node**       | Go, libp2p, BadgerDB                                            | Block lattice, consensus, networking, API                                 |
| **Embedded Web UI** | Plain HTML + ES modules + Web Crypto, embedded via `//go:embed` | Explorer, wallet, DAO console, state inspector — served by `xe node --ui` |
| **Proof of Uptime** | Design proposal                                                 | Verifiable uptime proofs for compute providers (not yet implemented)      |

## Documentation Sections [#documentation-sections]

### Overview [#overview]

* [Getting Started](/docs/getting-started) — build, run, and connect to the testnet
* [Architecture](/docs/architecture) — package breakdown and startup sequence

### Concepts [#concepts]

* [Block Lattice](/docs/block-lattice) — the DAG structure and cross-chain references
* [Accounts & Keys](/docs/accounts) — ed25519 key pairs, addresses, multisig
* [Assets](/docs/assets) — the dual-asset model (XE and XUSD)

### Protocol [#protocol]

* [Consensus](/docs/consensus) — delegation, conflict detection, voting, quorum
* [Networking](/docs/networking) — libp2p, gossip, frontier sync, DHT, messaging
* [Compute Leasing](/docs/compute) — lifecycle, cost model, attestations, VM management
* [State Chain](/docs/state-chain) — DAO governance via deterministic state machine
* [Proof of Uptime](/docs/uptime) — heartbeat chains, merkle epochs (design stage — not yet implemented)

### Interfaces [#interfaces]

* [API Reference](/docs/api) — HTTP REST API for accounts, blocks, leases, chat, and more
* [CLI Reference](/docs/cli) — unified CLI for node and client operations
* [Explorer & Web UI](/docs/explorer) — embedded web UI served by `xe node --ui`
* [Web Wallet](/docs/wallet) — client-side wallet with Web Crypto, embedded in binary

### Reference [#reference]

* [Binary Encoding](/docs/encoding) — block and vote wire formats
* [Cryptography](/docs/cryptography) — ed25519, SHA-256, signing contexts
* [Constants](/docs/constants) — all system constants
* [Deployment](/docs/deployment) — configuration, production setup

---

Canonical HTML: https://test.network/docs · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Accounts & Keys

> Ed25519 key pairs, addresses, signing, multisig, and delegation.

Every XE account is an ed25519 key pair. The public key, hex-encoded (64 hex chars / 32 bytes), is the account address. No prefixes, no checksums.

## Key Generation [#key-generation]

```go
kp, err := core.GenerateKeyPair()        // random
kp := core.KeyPairFromSeed(seed)         // deterministic from 32-byte seed
```

## Block Signing [#block-signing]

1. Canonical encoding via `MarshalBlockCanonical()`
2. `SHA-256(networkID ‖ canonical ‖ aux)` → `b.Hash` — the aux tail (`MarshalBlockAux`) is the certificate hash plus timekeeper attestations, empty for non-lease blocks; genesis is hashed with the network-ID prefix cleared
3. `ed25519.Sign(privateKey, hashBytes)` → `b.Signature`

## Multisig Accounts [#multisig-accounts]

* Address = `sha256(canonical(keyset))` (hash-derived)
* Opened with `multisig_open` block
* Rotated via `multisig_update` blocks
* Spending: M-of-N threshold; Receiving: 1-of-N

## Delegation [#delegation]

Each block includes an optional `Representative` field. Empty means keep current delegation. Delegation weight is the account's **XE** balance in micro-XE — XUSD is mintable by authorized minters and must not mint consensus weight, so it contributes nothing.

---

Canonical HTML: https://test.network/docs/accounts · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# API Reference

> Executable HTTP API playground for accounts, blocks, leases, state chain, directory, chat, and node operations.

Every XE node exposes the same JSON HTTP API. By default it listens on
`http://127.0.0.1:8080` (`--api` is on by default; change the port and bind
address with `--api-port` and `--api-bind`). The playground below targets the
live London testnet node at `https://ldn.test.network/api` through a local docs
proxy, so requests work from this site without browser CORS issues.

## Conventions [#conventions]

* **Amounts are micro-units.** 1 XE = 1 XUSD = 1,000,000 micro-units, and every
  amount, balance, and weight in the API is an integer micro-unit count. A
  balance of `100000000` is 100 XUSD.
* **The API is self-describing.** `GET /` returns a manifest of every route
  with its method and a one-line description — the same list the node uses to
  register its handlers.
* **Authentication** is per-endpoint. Most reads are open. `POST /lease/request`
  and the parameterless `/chat/events` firehose are operator-only, gated behind
  an admin bearer token (`Authorization: Bearer <XE_API_ADMIN_TOKEN>`). Reading
  a specific account's chat requires an ownership proof: fetch a challenge from
  `GET /chat/auth/challenge`, sign
  `sha256("xe/chat-read-auth/v1\0" || challenge)` with the account key, and pass
  `account`, `pub_key`, `challenge` and `sig` as query parameters. `pub_key` is
  required — an address is `sha256("xe/account/v1" || pubkey)` and so cannot
  verify its own signature — and each challenge is single-use.
* **Rate limits** are per-IP with two classes: reads at 200 requests/second
  (burst 1,000) and writes (POST) at 10 requests/second (burst 50). Exceeding
  a bucket returns `429`. Through `https://test.network/api` every response
  also carries `RateLimit-Policy` and `RateLimit` headers (plus the older
  `RateLimit-Limit`/`-Remaining`/`-Reset`) so a client can throttle itself,
  and a `429` adds `Retry-After` — see [rate limits](/developers#rate-limits).
* **Versioning.** The proxied API is versioned in the URL path: pin
  `https://test.network/api/v1`; `https://test.network/api` is an alias of the
  current major and every response carries `X-API-Version`. Changes within a
  major are additive only, and deprecations are announced with `Deprecation`
  and `Sunset` headers at least 90 days ahead — see
  [versioning](/developers#versioning). The machine-readable contract is the
  [OpenAPI 3.1 document](/openapi.json).

## Endpoints

Base URL: `https://test.network/api/v1` (versioned proxy; `https://test.network/api` is an alias of the current major, `https://ldn.test.network/api` is the node itself). Machine-readable: [OpenAPI 3.1](https://test.network/openapi.json). Each entry gives the operationId used there.

### Accounts

- `GET /accounts` — **List all accounts** (`listAccounts`). Returns accounts known by the node with balances and frontier hashes. Balances are micro-units (1 XE = 1 XUSD = 1,000,000 micro-units).
  - Response: JSON `AccountList` — Every account the node knows.
- `GET /accounts/{address}/balance` — **Get account balance** (`getAccountBalance`). Returns per-asset balances for one account, in micro-units.
  - Path `address`: Account address, 64 hex characters.
  - Response: JSON `Balances` — Balances keyed by asset code.
- `GET /accounts/{address}/chain` — **Get account chain** (`getAccountChain`). Returns the account block chain from oldest to newest, paginated.
  - Path `address`: Account address, 64 hex characters.
  - Query `offset` (optional, integer): Number of entries to skip.
  - Query `limit` (optional, integer): Maximum number of entries to return.
  - Response: JSON `BlockList` — Blocks, oldest first.
- `GET /accounts/{address}/keyset` — **Get multisig keyset** (`getAccountKeyset`). Returns the multisig keyset for an account when present.
  - Path `address`: Account address, 64 hex characters.
  - Response: JSON `Keyset` — The account's current keyset.
- `GET /accounts/{address}/reputation` — **Get account reputation** (`getAccountReputation`). Returns the reputation aggregate for one account, built from on-chain lease activity.
  - Path `address`: Account address, 64 hex characters.
  - Response: JSON `Reputation` — Reputation aggregate.
- `GET /reputation` — **All reputations** (`listReputations`). Returns reputation aggregates for every known account.
  - Response: JSON `ReputationList` — One aggregate per account.

### Blocks

- `POST /blocks/send` — **Submit send block** (`submitSendBlock`). Submits a signed send block. Requires a valid signature, hash, post-block balance, and PoW nonce; the destination must differ from the sending account.
  - Body: JSON `SendBlock` — see `#/components/schemas/SendBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/receive` — **Submit receive block** (`submitReceiveBlock`). Submits a signed receive block. 'source' is the hash of the pending send block being received.
  - Body: JSON `ReceiveBlock` — see `#/components/schemas/ReceiveBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/lease` — **Submit lease block** (`submitLeaseBlock`). Submits a signed compute lease block: XUSD escrow to a provider plus the requested vcpus/memory_mb/disk_gb/duration and an access public key.
  - Body: JSON `LeaseBlock` — see `#/components/schemas/LeaseBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/lease_accept` — **Submit lease accept** (`submitLeaseAcceptBlock`). Submits a provider lease acceptance block referencing the lease block via 'source'.
  - Body: JSON `LeaseAcceptBlock` — see `#/components/schemas/LeaseAcceptBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/lease_settle` — **Submit lease settle** (`submitLeaseSettleBlock`). Submits a provider lease settlement block referencing the lease block via 'source'.
  - Body: JSON `LeaseSettleBlock` — see `#/components/schemas/LeaseSettleBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/lease_cancel` — **Submit lease cancel** (`submitLeaseCancelBlock`). Submits a consumer lease cancellation block. Only the consumer can cancel, and only before the lease is accepted.
  - Body: JSON `LeaseCancelBlock` — see `#/components/schemas/LeaseCancelBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/lease_force_settle` — **Submit lease force settle** (`submitLeaseForceSettleBlock`). Submits a consumer force-settle block to recover escrow from an expired lease the provider never settled.
  - Body: JSON `LeaseForceSettleBlock` — see `#/components/schemas/LeaseForceSettleBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/multisig_open` — **Open multisig account** (`submitMultisigOpenBlock`). Submits a multisig account open block with a keyset {keys, threshold} and a signatures array in place of the single signature.
  - Body: JSON `MultisigOpenBlock` — see `#/components/schemas/MultisigOpenBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/multisig_update` — **Update multisig keyset** (`submitMultisigUpdateBlock`). Submits a multisig keyset update block signed by the current keyset's threshold.
  - Body: JSON `MultisigUpdateBlock` — see `#/components/schemas/MultisigUpdateBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/burn` — **Submit burn block** (`submitBurnBlock`). Submits a signed burn block, permanently destroying XE (XE only).
  - Body: JSON `BurnBlock` — see `#/components/schemas/BurnBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `POST /blocks/mint` — **Submit mint block** (`submitMintBlock`). Submits a signed XUSD mint block. Only accounts listed in the sys.minter state key are authorized. XUSD is not issued on this testnet today — the faucet hands out XE from a pre-funded wallet instead.
  - Body: JSON `MintBlock` — see `#/components/schemas/MintBlock` in the OpenAPI document.
  - Response: JSON `SubmitResult` — Block accepted.
- `GET /blocks/{hash}` — **Get block by hash** (`getBlock`). Returns a block by its hash.
  - Path `hash`: Block hash, 64 hex characters.
  - Response: JSON `Block` — The block.
- `GET /blocks/recent` — **Recent blocks** (`listRecentBlocks`). Returns the newest account-chain blocks. Optional ?limit and ?since (unix nanoseconds).
  - Query `limit` (optional, integer): Maximum number of blocks to return.
  - Query `since` (optional, integer): Only blocks newer than this unix-nanosecond timestamp.
  - Response: JSON `BlockList` — Newest blocks first.

### Leases

- `GET /leases` — **List leases** (`listLeases`). Returns known compute leases. Filter with ?state=created|accepted|settled|cancelled|unfulfilled.
  - Query `state` (optional, one of `created`, `accepted`, `settled`, `cancelled`, `unfulfilled`): Only leases in this lifecycle state.
  - Response: JSON `LeaseList` — Leases; [] while the testnet has none.
- `GET /leases/{hash}` — **Get lease** (`getLease`). Returns one lease by lease hash. Needs a real lease hash — the testnet currently has no open leases (GET /leases returns []).
  - Path `hash`: Lease block hash.
  - Response: JSON `Lease` — The lease.
- `GET /providers` — **List providers** (`listProviders`). Returns compute providers registered with the node.
  - Response: JSON `ProviderList` — Providers; [] while none are online.
- `GET /certificate` — **Node perf certificate** (`getNodeCertificate`). Returns this node's performance certificate (provider mode only).
  - Response: JSON `Certificate` — This node's certificate.
- `GET /certificate/{provider}` — **Provider perf certificate** (`getProviderCertificate`). Returns the performance certificate for a specific provider address.
  - Path `provider`: Provider account address.
  - Response: JSON `Certificate` — The provider's certificate.
- `POST /lease/request` — **Request lease** (`requestLease`). Asks the node to build, sign, and escrow a lease from its own wallet.
  - Auth: operator-only, `Authorization: Bearer <XE_API_ADMIN_TOKEN>`.
  - Body: JSON `LeaseRequest` — see `#/components/schemas/LeaseRequest` in the OpenAPI document.
  - Response: JSON `Lease` — The lease the node created.
  - Note: Operator-only: requires Authorization: Bearer <XE_API_ADMIN_TOKEN>; signs and escrows from the node operator's own wallet. Shown for reference.
- `POST /attestation/request` — **Request attestation** (`requestAttestation`). Requests a timekeeper attestation for a lease. Needs a real lease hash — the testnet currently has no open leases (GET /leases returns []).
  - Body: JSON `AttestationRequest` — see `#/components/schemas/AttestationRequest` in the OpenAPI document.
  - Response: JSON `Attestation` — The attestation.
- `GET /vms` — **List VMs** (`listVms`). Returns VM records known to the provider.
  - Response: JSON `VmList` — VM records.
- `GET /vms/{lease}` — **Get VM** (`getVm`). Returns VM details for a lease. Needs a real lease hash — the testnet currently has no open leases (GET /leases returns []).
  - Path `lease`: Lease block hash.
  - Response: JSON `Vm` — The VM record.
- `POST /tunnel/{leaseHash}/tcp` — **TCP tunnel** (`openTcpTunnel`). Opens a raw TCP tunnel into a leased VM. Takes no JSON body: authentication is an X-Signature header (ed25519 signature over the 32 decoded lease-hash bytes with the lease's access key), after which the HTTP connection is hijacked into a byte stream.
  - Auth: `X-Signature` header signed with the lease access key.
  - Path `leaseHash`: Lease block hash.
  - Response: `application/octet-stream` — The connection is hijacked into a raw TCP byte stream.
  - Note: Not runnable from the browser: the response is a hijacked raw TCP stream, and the required X-Signature header needs the lease access key. Use the xe CLI or curl instead. Needs a real lease hash — the testnet currently has no open leases (GET /leases returns []).

### State Chain

- `GET /statechain/tip` — **State chain tip** (`getStateChainTip`). Returns the latest state chain block.
  - Response: JSON `StateBlock` — The newest state block.
- `GET /statechain/blocks/{index}` — **State block by index** (`getStateBlock`). Returns a state chain block by index.
  - Path `index`: Zero-based block index.
  - Response: JSON `StateBlock` — The state block.
- `GET /statechain/blocks` — **State blocks** (`listStateBlocks`). Returns state chain blocks and block count. Optional ?start and ?limit.
  - Query `start` (optional, integer): First block index to return.
  - Query `limit` (optional, integer): Maximum number of blocks to return.
  - Response: JSON `StateBlockPage` — A page of state blocks.
- `GET /statechain/kv/{key}` — **State value** (`getStateValue`). Returns one state chain key-value entry (or a prefix lookup).
  - Path `key`: State key, e.g. sys.network_id.
  - Response: JSON `StateValue` — The entry.
- `GET /statechain/kv` — **All state values** (`listStateValues`). Returns state chain key-value entries. Optional ?prefix filter plus ?offset/?limit pagination.
  - Query `prefix` (optional, string): Only keys starting with this prefix.
  - Query `offset` (optional, integer): Number of entries to skip.
  - Query `limit` (optional, integer): Maximum number of entries to return.
  - Response: JSON `StateValueList` — Entries.
- `GET /statechain/keyset` — **DAO keyset** (`getDaoKeyset`). Returns the current DAO keyset (M-of-N timekeepers).
  - Response: JSON `Keyset` — The DAO keyset.
- `POST /statechain/blocks` — **Submit state block** (`submitStateBlock`). Submits a state chain block signed by the DAO keyset threshold.
  - Body: JSON `StateBlockSubmission` — see `#/components/schemas/StateBlockSubmission` in the OpenAPI document.
  - Response: JSON `StateBlock` — The appended state block.

### Directory

- `POST /directory/register` — **Register directory entry** (`registerDirectoryEntry`). Registers an account-to-peer mapping, signed by the account key over the registration payload.
  - Body: JSON `DirectoryRegistration` — see `#/components/schemas/DirectoryRegistration` in the OpenAPI document.
  - Response: JSON `DirectoryEntry` — The stored registration.
- `GET /directory` — **List directory** (`listDirectory`). Returns account-to-peer registrations, paginated.
  - Query `offset` (optional, integer): Number of entries to skip.
  - Query `limit` (optional, integer): Maximum number of entries to return.
  - Response: JSON `DirectoryEntryList` — Registrations.
- `GET /directory/{account}` — **Lookup directory entry** (`getDirectoryEntry`). Returns the registration for one account (404 if the account has not registered or its entry expired).
  - Path `account`: Account address, 64 hex characters.
  - Response: JSON `DirectoryEntry` — The registration.

### Chat

- `POST /chat/send` — **Send chat message** (`sendChatMessage`). Sends a peer-to-peer chat message. Requires a full pre-signed envelope — the node no longer signs messages on behalf of callers.
  - Body: JSON `ChatMessage` — see `#/components/schemas/ChatMessage` in the OpenAPI document.
  - Response: JSON `ChatMessage` — The accepted envelope.
- `GET /chat/auth/challenge` — **Chat auth challenge** (`getChatAuthChallenge`). Issues a single-use challenge. Sign it with your account key and pass account, challenge, and sig to the chat read endpoints as ownership proof.
  - Response: JSON `ChatChallenge` — A fresh challenge.
- `GET /chat/messages` — **Chat messages** (`listChatMessages`). Returns stored chat messages for an account. Requires an ownership proof: get a challenge from /chat/auth/challenge, sign it, and pass account, challenge, and sig. Optional ?since (unix nanoseconds).
  - Auth: ownership proof (`account`, `pub_key`, `challenge`, `sig`).
  - Query `account` (required, string): Account address, 64 hex characters.
  - Query `pub_key` (required, string): Hex ed25519 public key of `account`. Required: an address is a hash of the key and cannot verify a signature on its own.
  - Query `challenge` (required, string): Single-use challenge from GET /chat/auth/challenge.
  - Query `sig` (required, string): ed25519 signature over sha256("xe/chat-read-auth/v1\0" || challenge) by the account key, hex.
  - Query `since` (optional, integer): Only messages newer than this unix-nanosecond timestamp.
  - Response: JSON `ChatMessageList` — Stored messages.
- `GET /chat/contacts` — **Chat contacts** (`listChatContacts`). Returns known chat contacts.
  - Response: JSON `ChatContactList` — Contacts.
- `GET /chat/events` — **Chat event stream** (`streamChatEvents`). SSE stream of new messages. A named account requires an ownership proof (account, challenge, sig); with no account it is the unfiltered firehose, which is operator-only and returns 403 without the admin token.
  - Auth: ownership proof (`account`, `pub_key`, `challenge`, `sig`).
  - Query `account` (optional, string): Account address, 64 hex characters.
  - Query `pub_key` (optional, string): Hex ed25519 public key of `account`. Required: an address is a hash of the key and cannot verify a signature on its own.
  - Query `challenge` (optional, string): Single-use challenge from GET /chat/auth/challenge.
  - Query `sig` (optional, string): ed25519 signature over sha256("xe/chat-read-auth/v1\0" || challenge) by the account key, hex.
  - Response: `text/event-stream` — Server-sent events, one `ChatMessage` JSON object per event.

### Node

- `GET /node` — **Node info** (`getNodeInfo`). Returns node identity, version, network, and peers.
  - Response: JSON `NodeInfo` — Node info.
- `GET /pending/{address}` — **Pending for account** (`listPendingForAccount`). Returns pending sends for an account.
  - Path `address`: Account address, 64 hex characters.
  - Response: JSON `PendingSendList` — Unreceived sends to the account.
- `GET /pending` — **All pending sends** (`listPending`). Returns all pending sends known to the node.
  - Response: JSON `PendingSendList` — All unreceived sends.
- `GET /frontiers` — **Frontiers** (`listFrontiers`). Returns account frontier hashes.
  - Response: JSON `Frontiers` — Address → frontier hash.
- `GET /delegation` — **Delegation weights** (`getDelegation`). Returns representative vote weights in micro-XE.
  - Response: JSON `Delegation` — Representative → weight.
- `GET /conflicts` — **Conflicts** (`listConflicts`). Returns detected account-chain conflicts.
  - Response: JSON `ConflictList` — Conflicts.
- `GET /conflicts/{account}` — **Conflicts for account** (`listConflictsForAccount`). Returns conflicts scoped to one account.
  - Path `account`: Account address, 64 hex characters.
  - Response: JSON `ConflictList` — Conflicts on that account chain.

### Versioning

URL-path versioning; the current major is v1 at `https://test.network/api/v1` (pin this). `https://test.network/api` is an alias of the current major and every response carries `X-API-Version`. Changes within a major are additive only; a new major keeps the previous one for 180 days; deprecations are flagged with `deprecated: true` in the OpenAPI document and `Deprecation` (RFC 9745) + `Sunset` (RFC 8594) headers at least 90 days ahead. Full policy: https://test.network/developers#versioning.

### Rate limits

Per client IP and request class: read 1000 per 5s, write 50 per 5s. Every response carries `RateLimit-Policy` and `RateLimit` (draft-ietf-httpapi-ratelimit-headers, e.g. `"read";q=1000;w=5` and `"read";r=998;t=4`) plus `RateLimit-Limit`/`RateLimit-Remaining`/`RateLimit-Reset`; a `429` adds `Retry-After`. Details: https://test.network/developers#rate-limits.

### Errors

Every 4xx/5xx from the `https://test.network/api` proxy is JSON: `{"error": {"code", "status", "message", "hint", "docs", "openapi"}}` (schema `Error` in the OpenAPI document, referenced by each operation's error responses). Codes: bad_request, unauthorized, forbidden, not_found, method_not_allowed, rate_limited, upstream_error, upstream_unavailable, upstream_timeout. The node's own JSON errors pass through unchanged.

## Notes [#notes]

* Most `GET` endpoints run immediately. Chat reads need the ownership proof
  described above, and lease-scoped lookups need a real lease hash — the
  testnet currently has no open leases, so those entries return 404 until one
  exists.
* `POST` endpoints include editable starter JSON showing the exact shape the
  node decodes. Block submissions must be fully formed: valid signature, block
  hash, post-block `balance`, and proof-of-work nonce. The node does not sign
  or complete blocks for you.
* Entries marked as reference-only (`/lease/request`, the TCP tunnel) cannot
  run from the browser; the panel explains what each needs instead.
* Sample account and block identifiers are refreshed from the live network
  with `node scripts/refresh-playground-samples.mjs` — rerun it after a
  testnet wipe.

---

Canonical HTML: https://test.network/docs/api · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Architecture

> Core node package map, block validation pipeline, and startup sequence.

The core node is written in Go. It uses libp2p for peer-to-peer networking, BadgerDB for persistent storage, blake2b for proof-of-work, and ed25519 for signatures.

## Package Map [#package-map]

```
core/
├── cmd/
│   └── xe/         Single binary — node daemon, wallet, send/receive, leases, ssh
├── core/           Domain logic — ledger, crypto, encoding, PoW, voting, quorum
├── store/          Pluggable storage — MemStore (testing), BadgerStore (production)
├── net/            libp2p networking — gossip, sync, DHT, marketplace, messaging
├── node/           Orchestration — ties all packages together into a running node
├── api/            HTTP REST API — handler, routes, CORS
├── statechain/     Deterministic state machine — DAO governance, KV store, sync
├── vm/             VM abstraction — manager interface, mock, credentials
├── perf/           Performance certificates — workload benchmark, price multiplier
├── web/            Embedded web UI — HTML + ES modules + CSS, served by xe node --ui
├── client/         Client-side helpers shared by CLI subcommands
├── directory/      P2P account directory — registration, verification, gossip
├── chat/           P2P messaging — envelope format, chat store
└── scripts/        Test and utility scripts — e2e, stress, genesis generation
```

## Core Package Files [#core-package-files]

| File                | Purpose                                                                              |
| ------------------- | ------------------------------------------------------------------------------------ |
| `types.go`          | Block, Vote, Conflict, Lease, PendingSend structs; BlockType constants               |
| `ledger.go`         | Ledger struct — validates/adds blocks, per-account locking, delegation tracking      |
| `amount.go`         | Per-asset decimal precision; micro-unit amount parsing and formatting                |
| `crypto.go`         | KeyPair, GenerateKeyPair, HashBlock (SHA-256), SignBlock, VerifyBlock (ed25519)      |
| `encoding.go`       | MarshalBlockCanonical (binary encoding), MarshalBlock, UnmarshalBlock; vote encoding |
| `pow.go`            | blake2b PoW — ComputePoW, ComputePoWConcurrent, ComputePoWWithContext, ValidatePoW   |
| `vote.go`           | VoteManager — casts and validates votes for conflict resolution                      |
| `quorum.go`         | QuorumManager — tallies votes, finalizes/rejects blocks at 67% weight                |
| `finalization.go`   | Two-phase finalization voting — the central consensus mechanism                      |
| `conflict.go`       | Conflict detection — equivocation checks                                             |
| `cascade_commit.go` | Conflict-promotion overlay — atomically commits a winning fork's cascade             |
| `rollback.go`       | Cascading cross-account rollback of rejected forks                                   |
| `spendable.go`      | Spendable-balance computation — settlement surfaces honor finalization               |
| `genesis.go`        | Embedded genesis — loads and validates the genesis allocation at startup             |
| `mint.go`           | Authorized XUSD mint validation — sys.minter accounts only                           |
| `memo.go`           | On-chain memo size and validation rules                                              |
| `multisig.go`       | Multisig address derivation, keyset validation, threshold signatures                 |
| `reputation.go`     | Deterministic per-account reputation from on-chain lease activity                    |
| `retryable.go`      | Classifies errors that may resolve on retry (missing dependencies)                   |
| `attestation.go`    | Timekeeper attestation validation for lease blocks                                   |
| `store.go`          | Store interface and optional interfaces                                              |

## Block Validation Pipeline [#block-validation-pipeline]

```
AddBlock(b *Block)
  │
  ├── 1. Normalize hex fields (lowercase)
  ├── 2. VerifyBlock — recompute hash + check ed25519 signature
  ├── 3. ValidatePoW — blake2b(nonce || hash) >= difficulty
  ├── 4. Timestamp check — within ±1 hour of local time
  ├── 5. Duplicate check — block hash not already in store
  ├── 6. Conflict detection — check if Previous hash is shared
  │     ├── No conflict → continue on main chain
  │     └── Conflict → stage block, fire callback, return
  │
  ├── 7. Type-specific validation (per-account lock held)
  │     ├── send     → balance sufficient, frontier matches, amount > 0
  │     ├── receive  → pending send exists, destination matches
  │     ├── mint     → XUSD only, account in sys.minter, no source/destination/memo, amount > 0
  │     ├── burn     → XE only, no source/destination, amount > 0, balance sufficient
  │     ├── lease    → XUSD only, cost formula correct, balance sufficient
  │     ├── lease_accept → lease exists, stake = ceil(cost/5), attestations valid
  │     ├── lease_settle → lease expired, XE emission formula
  │     ├── lease_cancel → consumer only, source lease exists and is cancellable
  │     ├── lease_force_settle → consumer only, lease accepted but unsettled
  │     └── multisig_open/update → keyset valid; open derives the account address, update rotates the keyset
  │
  ├── 8. Update in-memory state (asset balances, delegation weights)
  └── 9. Write to store (atomic commit via AtomicBlockStore)
```

## Startup Sequence [#startup-sequence]

1. Open or create key pair (loads `{dataDir}/node.key` or generates new)
2. Open store (BadgerStore at `{dataDir}/ledger`)
3. Create libp2p host (TCP, noise encryption, yamux)
4. Setup pubsub (GossipSub)
5. Create gossip layers (block, vote, marketplace, directory, state chain)
6. Setup mDNS
7. Create ledger (wraps store with validation)
8. Wire voting (VoteManager + QuorumManager)
9. Setup frontier sync
10. Setup DHT (Kademlia)
11. Create messenger
12. Initialize state chain
13. Wire timekeeper config
14. Register gossip handlers
15. Dial bootstrap peers
16. Start background goroutines

---

Canonical HTML: https://test.network/docs/architecture · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Assets (XE & XUSD)

> Dual-asset model — XE for consensus weight and provider emission, XUSD for compute payment.

XE supports two native assets with distinct economic roles.

## Units [#units]

Both assets use 6 decimal places (`core.Decimals = 6`). Every on-chain amount — balances, send amounts, lease costs, stakes — is an unsigned 64-bit integer in **micro-units**:

```
1 XE   = 1,000,000 µXE
1 XUSD = 1,000,000 µXUSD
```

A balance of `1000000` is one whole token, not a million. Where integer division on an amount is unavoidable, the protocol always rounds up.

## XE [#xe]

The native asset. Supply comes from the genesis block (42,000,000 XE) and from lease settlement, which mints XE to the provider in proportion to the lease's XUSD cost:

```
emission_µXE = max(1, ceil(cost_µXUSD × R_capped / 1000))
R_capped     = CapR(LockedR, LockedPayoutCap, LockedTWAP)
```

The emission parameters are locked onto the lease when the provider accepts it, so every node computes the same emission. The live `epoch.0` publishes `r_effective: 2000`, so providers currently earn 2× the lease cost.

**XE confers voting weight** — an account's delegation weight is its XE balance in micro-XE.

## XUSD [#xusd]

> \[!NOTE] Not issued on this testnet yet
> XE is the only asset in circulation on `testnet-0001`. XUSD has no issuance path today — the faucet hands out XE from a pre-funded wallet rather than minting XUSD — so the lease pricing below describes the protocol, not something you can pay for right now.

* **Lease payment** — consumers pay XUSD for compute
* **No voting weight** — XUSD is mintable by authorized minters, so letting it carry consensus weight would let a minter mint governance power along with the balance; only XE counts
* **Issuance** — XUSD enters circulation only via minter `mint` blocks from accounts in `sys.minter` (claim logic was removed from the ledger). No such minter is active on this testnet; the faucet sends XE and holds no minter key.

## Asset Encoding [#asset-encoding]

8-byte field, left-aligned UTF-8, zero-padded:

```
"XE"   → 58 45 00 00 00 00 00 00
"XUSD" → 58 55 53 44 00 00 00 00
```

## Cost Formula [#cost-formula]

Lease pricing is deterministic, in micro-XUSD:

```
perHourMicro = vCPUs × 20_000 + ceil(memoryMB / 1024) × 10_000 + diskGB × 1_000
hours        = ceil(duration / 3600)
cost         = max(1, ceil(perHourMicro × hours × multiplierMilli / 1000))
```

`multiplierMilli` is the provider's price multiplier scaled ×1000 (1000 = 1.000×), read from the performance certificate the lease references.

| Resource | Rate                 |
| -------- | -------------------- |
| vCPU     | 20,000 µXUSD/hour    |
| Memory   | 10,000 µXUSD/GB/hour |
| Disk     | 1,000 µXUSD/GB/hour  |

Worked example: 1 vCPU, 1 GB memory, 10 GB disk, 1 day at 1.000× → 24 × (20,000 + 10,000 + 10,000) = **960,000 µXUSD** (0.96 XUSD).

Stake = `ceil(cost / 5)` µXUSD (minimum 1). XE emission at settlement = `ceil(cost × R_capped / 1000)` µXE (minimum 1).

---

Canonical HTML: https://test.network/docs/assets · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Block Lattice

> Per-account chains, cross-chain links, and the twelve block types.

Every account address corresponds to a chain of blocks. Each block's `Previous` field points to the hash of the preceding block. The first block has `Previous` set to `"0"`.

## Cross-Chain Links [#cross-chain-links]

Transfers require two blocks:

1. A **send block** on the sender's chain debits the sender (creates a pending send)
2. A **receive block** on the recipient's chain credits the recipient (consumes the pending send)

The sender and receiver do not need to be online at the same time.

Account chains open with a crediting block — `receive`, `mint`, `multisig_open`, or `genesis`:

```
Alice            Bob              Carol
┌─────────┐      ┌────────┐      ┌─────────┐
│ recv    │      │ recv   │      │ recv    │
│ bal: 10 │      │ bal: 5 │      │ bal: 10 │
└───┬─────┘      └───┬────┘      └───┬─────┘
    │                │               │
┌───▼─────┐      ┌───▼────┐      ┌───▼─────┐
│ send    │─────▶│ recv   │      │ send    │
│ to: Bob │      │ src:   │      │ to: Bob │
│ amt: 5  │      │ bal: 10│      │ amt: 3  │
│ bal: 5  │      └───┬────┘      │ bal: 7  │
└─────────┘          │           └─────────┘
                 ┌───▼────┐
                 │ recv   │◀── from Carol
                 │ bal: 13│
                 └────────┘
```

## Properties [#properties]

* No contention between accounts (parallel processing)
* Instant finality for non-conflicting transactions

## XE Extensions [#xe-extensions]

* Dual assets (XE and XUSD)
* Additional block types (lease, lease\_accept, lease\_settle)
* State chain for governance
* blake2b PoW as anti-spam
* Voting weight from delegated XE balances (XUSD is mintable and confers none)

## Block Types [#block-types]

Twelve block types are defined:

| Type                 | Byte | Asset      | Debits            | Credits                  |
| -------------------- | ---- | ---------- | ----------------- | ------------------------ |
| `send`               | 0x01 | XE or XUSD | sender            | —                        |
| `receive`            | 0x02 | XE or XUSD | —                 | recipient                |
| `lease`              | 0x04 | XUSD       | consumer (escrow) | —                        |
| `lease_accept`       | 0x05 | XUSD       | provider (stake)  | —                        |
| `lease_settle`       | 0x06 | XE         | —                 | provider (emission)      |
| `genesis`            | 0x07 | XE         | —                 | treasury (42,000,000 XE) |
| `multisig_open`      | 0x08 | XE or XUSD | —                 | —                        |
| `multisig_update`    | 0x09 | XE or XUSD | —                 | —                        |
| `lease_cancel`       | 0x0A | XUSD       | —                 | consumer (refund)        |
| `burn`               | 0x0B | XE         | self              | —                        |
| `lease_force_settle` | 0x0C | XUSD       | —                 | consumer (refund)        |
| `mint`               | 0x0D | XUSD       | —                 | minter (new supply)      |

`0x03` is a retired, unallocated gap: the permissionless `claim` type was removed — XUSD now enters circulation only via `mint` blocks on accounts registered under the state chain's `sys.minter` key. Genesis is XE-only and its balance must equal `GenesisSupply` (42,000,000 XE).

---

Canonical HTML: https://test.network/docs/block-lattice · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# CLI Reference

> The xe binary — unified CLI for node operation and client interaction.

The `xe` binary is a unified CLI for both node operation and client interaction.

_The HTML version of this page (https://test.network/docs/cli) embeds an interactive simulation of the xe CLI; the commands it accepts are documented below._

## Commands [#commands]

```
xe node [flags]           Start a node daemon
xe wallet create          Create a new wallet
xe wallet balance         Show wallet balance
xe faucet                 Request testnet XE from the faucet service
xe send <addr> <amount> [--asset XE|XUSD] [--memo "text"]
                          Send funds; --memo attaches up to 64 bytes of UTF-8
xe receive                Receive pending sends
xe mint <amount>          Ops/bootstrap only — wallet must be an authorized sys.minter
xe burn <amount> [--yes]  Burn funds from the loaded wallet
xe providers              List providers
xe lease [flags]          Create a lease
xe lease status <hash>    Check lease status
xe vm <hash>              Get VM info
xe ssh <hash>             SSH into leased VM
xe reputation <address>   Show provider reputation
xe keygen                 Generate ed25519 SSH keypair
xe sign-block             Sign a block from stdin (seed from XE_SEED)
xe version                Print version
xe help                   Print usage
```

`xe faucet` talks to the standalone faucet service (`XE_FAUCET`, default
`https://faucet.test.network`). It sends 1,000 testnet XE per account per
day from a pre-funded wallet; the grant lands as a pending send, so claim it
with `xe receive`.

`xe sign-block` takes no seed argument — the binary rejects one because a
positional seed leaks via `ps`, `/proc`, and shell history. Set `XE_SEED`
instead.

## Environment Variables [#environment-variables]

| Variable             | Default                         | Description                                                               |
| -------------------- | ------------------------------- | ------------------------------------------------------------------------- |
| `XE_NODE`            | `https://ldn.core.test.network` | Node API URL                                                              |
| `XE_WALLET`          | `~/.xe/wallet.seed`             | Wallet seed file                                                          |
| `XE_SEED`            | (none)                          | Hex seed for `sign-block` (required — the command takes no seed argument) |
| `XE_FAUCET`          | `https://faucet.test.network`   | Faucet service URL                                                        |
| `XE_API_ADMIN_TOKEN` | (none)                          | Node: enables operator-only API endpoints when set                        |
| `XE_SSH_HOST`        | `ldn.test.network`              | SSH gateway hostname                                                      |
| `XE_SSH_PORT`        | `2222`                          | SSH gateway port                                                          |

## Node Flags [#node-flags]

| Flag                     | Default    | Description                                                                   |
| ------------------------ | ---------- | ----------------------------------------------------------------------------- |
| `-port`                  | 9000       | libp2p TCP port                                                               |
| `-api-port`              | 8080       | HTTP API port                                                                 |
| `-api-bind`              | 127.0.0.1  | API bind address                                                              |
| `-api`                   | true       | Enable/disable HTTP API server                                                |
| `-dial`                  | (none)     | Bootstrap multiaddrs                                                          |
| `-data`                  | ./data     | Storage directory                                                             |
| `-cors-origin`           | (none)     | Allowed CORS origin                                                           |
| `-ui`                    | false      | Enable embedded web UI (requires `-api`)                                      |
| `-ui-port`               | 8000       | Web UI port                                                                   |
| `-ui-bind`               | 127.0.0.1  | Web UI bind address                                                           |
| `-ui-dir`                | (embedded) | Dev override: serve UI from filesystem                                        |
| `-wallet`                | true       | Expose `/wallet/` in the embedded UI; `--wallet=false` serves 404             |
| `-provide`               | false      | Enable provider mode                                                          |
| `-vcpus`                 | 2          | vCPUs to offer                                                                |
| `-memory`                | 2048       | Memory in MB                                                                  |
| `-disk`                  | 20         | Disk in GB                                                                    |
| `-price-multiplier`      | 1000       | Provider price multiplier ×1000 (1000 = baseline)                             |
| `-ssh-port`              | 0          | SSH gateway port (0 = disabled)                                               |
| `-limactl-path`          | (PATH)     | Path to limactl                                                               |
| `-max-conns-per-ip`      | 8          | Max inbound connections per source IP                                         |
| `-min-lease-duration`    | (none)     | Policy: minimum lease duration as a Go duration (e.g. `1h`); empty = no limit |
| `-max-lease-duration`    | (none)     | Policy: maximum lease duration (e.g. `720h`); empty = no limit                |
| `-min-lease-cost`        | 0          | Policy: minimum XUSD cost (0 = no min)                                        |
| `-max-lease-cost`        | 0          | Policy: maximum XUSD cost (0 = no max)                                        |
| `-max-concurrent-leases` | 0          | Policy: max active leases (0 = capacity-bound only)                           |

The lease-duration flags are duration strings, not numbers — passing `0` fails
validation and stops the node at startup. Leave them unset for no limit.

---

Canonical HTML: https://test.network/docs/cli · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Compute Leasing

> Lease lifecycle, cost model, attestations, VM management, and provider economics.

XE extends the block lattice with an on-chain compute marketplace. Consumers lease virtual machines from providers, paying XUSD. Providers earn XE emission rewards upon settlement.

> \[!WARNING] Nothing on this page can be exercised right now
> No compute providers are online — `GET /providers` returns an empty list. The lease block types, escrow, attestation and VM machinery described here are implemented and in the binary, but with no provider to accept a lease, none of it can run end to end on the live testnet. GPU leasing is not implemented at all.

## Lifecycle [#lifecycle]

1. **Consumer creates `lease` block** — escrows XUSD for the full cost, specifying vCPUs, memory, disk, and duration
2. **Provider creates `lease_accept` block** — stakes XUSD collateral, locks the emission rate, provisions VM, gets attestations
3. **Provider creates `lease_settle` block** — mints XE emission, recovers stake, burns the escrow, tears down VM

Two blocks cover the paths where that does not happen — the consumer's escrow is recoverable on both:

* **`lease_cancel`** — the consumer withdraws a lease no provider accepted; the escrow is refunded in full
* **`lease_settle` never arrives*&#x2A; — once the provider has abandoned the lease, the consumer submits &#x2A;*`lease_force_settle`**: full escrow refund, provider stake burned

A lease ends in one of six states: `created`, `accepted`, `settled`, `cancelled`, `unfulfilled` (force-settled), or `expired` (neither party acted; escrow burned after the refund window closes). Windows: `LeaseSettleGrace` 1 h (expiry → end of provider settle window), `LeaseForceSettleGap` 25 min (dead zone before the consumer force-settle window opens), `LeaseEscrowExpiry` 365 d (expiry → escrow burn deadline).

## Cost Model [#cost-model]

All amounts are micro-units. Single ceiling divide for the provider price multiplier:

```
perHourMicro = vCPUs × 20_000 + ceil(memMB / 1024) × 10_000 + diskGB × 1_000
hours        = ceil(duration / 3600)
cost         = max(1, ceil(perHourMicro × hours × multiplierMilli / 1000))
stake        = ceil(cost / 5)                        // min 1 µXUSD
XE emission  = max(1, ceil(cost × R_capped / 1000))  // µXE, rate locked at accept
```

Provider `PriceMultiplierMilli` range: 500 (0.5×) to 10000 (10×), default 1000 (1×). Duration limits: 60s to 31,536,000s (365 days).

## Attestations [#attestations]

* Signed timestamps from trusted timekeeper nodes
* Timekeeper keys stored in state chain under `sys.timekeepers`
* SHA-256(leaseHash || timestamp) signed with ed25519
* Max skew: ±10 minutes
* Median of valid timestamps used as canonical time
* Max 20 attestations per block
* Rate limited: 1 per peer per lease per 30s

## VM Management [#vm-management]

* Lima (QEMU-based) VMs with KVM acceleration
* Ubuntu 24.04 cloud images
* Cloud-init for SSH key injection
* \~21 second boot time
* VMs named `xe-{leaseHash[:12]}`
* Manager interface: Provision, Teardown, DialSSH, Get, List
* VM access is SSH-only, through the tunnel — there is no exec API

## SSH Gateway & Tunnel [#ssh-gateway--tunnel]

* Protocol: `/xe/tunnel/2.0.0`
* SSH gateway authenticates via lease's `AccessPubKey`
* ProxyJump for end-to-end encryption
* HTTP tunnel endpoint: `POST /tunnel/{leaseHash}/tcp`
* Max 100 concurrent SSH connections

## Economics [#economics]

* XUSD is **escrowed** at lease creation, burned at settle, and refunded in full on cancel or force-settle
* XE is **inflationary** — minted on lease settlement: emission = ceil(cost × R\_capped / 1000) µXE, with the rate locked at accept (live `epoch.0`: `r_effective` 2000 ⇒ 2× cost)
* Provider price multiplier range: 500 (0.5×) to 10000 (10×), default 1000 (1×)
* 5:1 stake-to-escrow ratio (stake = ceil(cost/5)); an abandoned lease is recoverable via `lease_force_settle`, which refunds the consumer's escrow and burns the provider's stake

## Provider Policy [#provider-policy]

Providers can filter incoming leases before expensive stake/attestation/VM-provision gates run. Five flags:

* `--min-lease-duration` — Go duration string (e.g. `5m`)
* `--max-lease-duration` — Go duration string (e.g. `720h`)
* `--min-lease-cost` — minimum XUSD cost (uint64)
* `--max-lease-cost` — maximum XUSD cost (uint64)
* `--max-concurrent-leases` — max active leases (uint64)

All default to zero (fully permissive). Gate runs in `autoAcceptLease` immediately after idempotency check. Defined in `core/node/policy.go`.

## Performance Certificates [#performance-certificates]

* \~60 second benchmark on startup
* Phase 1: 375M sequential SHA-256 iterations (CPU)
* Phase 2: 256 MB memory table + 1M random reads (memory)
* Score = 1.0 / elapsed\_seconds
* 7-day validity
* Required for `lease_accept` blocks
* Broadcast via `xe/certificates` gossip topic

---

Canonical HTML: https://test.network/docs/compute · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Consensus

> Two-phase ORV finality — every block finalizes at ≥67% of delegated XE weight.

Every block reaches a finalized state via representative voting — not just blocks that fork. Each chain **position** (account + `Previous` hash) runs an election; a fork just means the election has more than one candidate.

## Two-Phase Voting [#two-phase-voting]

| Phase       | Vote kind       | Behaviour                                                                                                                                                                                                                   |
| ----------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1. Converge | `Final = false` | **Mutable.** A representative votes for its preferred candidate (lowest hash among candidates it holds) and may re-vote as its preference shifts. Converge votes establish a leader; they never finalize anything.          |
| 2. Final    | `Final = true`  | **Irrevocable.** Once a candidate has a visible ≥67% converge supermajority, the representative writes a **write-once commit-lock** for the position and casts a final vote for the locked hash. Only final votes finalize. |

A block finalizes when representatives holding **≥67% of delegated XE weight** have final-voted it:

```
blockWeight * 100 >= totalWeight * 67
```

Uses `big.Int` arithmetic. Deterministic preference: lexicographically lowest hash.

The commit-lock is never deleted — not on resolution, rollback, or restart — so a representative is structurally unable to final-vote two hashes at one position. Adversarial timing can delay finalization, never double it. The 67% quorum lock path additionally waits out a **3-second candidate-stability window** after the last new candidate appears, so a transient supermajority on a higher-hash sibling cannot grab the lock.

## Delegation [#delegation]

* Voting weight is the delegated **XE** balance, in micro-XE. XUSD confers **no** weight: XUSD is mintable (faucet/bridge), and mintable supply must not mint consensus weight.
* Weight snapshots frozen at conflict detection time
* Persistent via DelegationStore interface
* `sync.RWMutex` for concurrency

## Dependency Gating [#dependency-gating]

A representative withholds its own vote for a block whose dependencies are not yet finalized locally — its `Previous`, plus the cross-account `Source` for receives and lease lifecycle blocks. It still tallies incoming votes, so a lagging node never deadlocks an election.

## The Finality Wall [#the-finality-wall]

* Each account has a `final_height` watermark — the height of its highest finalized block. The ledger refuses to replace any block at or below it.
* **Spendable balance** counts only finalized inflows; an account with nothing finalized has no spendable balance.

## Fallback Resolution [#fallback-resolution]

If 67% is unreachable (non-voting reps inflate the total), then after 10 seconds only the **lowest-hash** final-voted candidate may finalize, and only if it holds a &#x2A;*strict majority (>50%)** of total delegated weight in final votes. Otherwise the node withholds rather than risk divergent finalization — since each representative is commit-locked to one hash per position, at most one candidate can ever hold a strict majority.

## Conflict Detection [#conflict-detection]

* A conflict = two blocks with the same `Previous` hash (equivocation)
* Max 10 block hashes per conflict
* Conflicting blocks placed in staging
* Conflict callback fires on detection **and** whenever a new sibling body is staged for an existing conflict; only gossip re-deliveries of an already-staged body are suppressed

## Voting [#voting]

* Per-conflict mutex prevents double-voting
* Vote buffering for votes arriving before local conflict detection (max 10 per conflict)
* ±5 minute timestamp window
* Vote wire format: 204 bytes (138 signing bytes + 2 length + 64 signature) — see [Binary Encoding](/docs/encoding)

## Quorum Manager [#quorum-manager]

* Stale conflict sweep every 15 seconds
* Block status: Pending (0), Finalized (1), Rejected (2)
* Block swap mechanism for staged winners

---

Canonical HTML: https://test.network/docs/consensus · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# System Constants

> Difficulty, timing, size limits, lease economics, performance certificates.

## Proof of Work [#proof-of-work]

* DefaultDifficulty: `0xfffff80000000000`
* TestDifficulty: `0x0000000000000002`

## Timing [#timing]

| Constant                  | Value          |
| ------------------------- | -------------- |
| Block timestamp tolerance | ±1 hour        |
| Vote window               | ±5 minutes     |
| Attestation skew          | ±10 minutes    |
| Quorum fallback           | 10 seconds     |
| Stale conflict sweep      | 15 seconds     |
| Periodic sync             | 10 seconds     |
| Sync rate limit           | 5 seconds/peer |
| Wallet session timeout    | 5 minutes      |
| Wallet poller             | 5 seconds      |
| Directory TTL             | 30 minutes     |

## Size Limits [#size-limits]

| Limit                    | Value     |
| ------------------------ | --------- |
| Conflict hashes          | 10 max    |
| Pending votes/conflict   | 10 max    |
| Attestations/block       | 20 max    |
| Gossip message           | 256 KB    |
| Sync request             | 1 MiB     |
| Sync response            | 10 MiB    |
| Sync blocks/session      | 10,000    |
| Frontiers/request        | 10,000    |
| State key                | 128 bytes |
| State value              | 64 KB     |
| State block              | 256 KB    |
| Message request/response | 64 KB     |

## Lease Economics [#lease-economics]

| Parameter    | Value                                                            |
| ------------ | ---------------------------------------------------------------- |
| Min duration | 60s                                                              |
| Max duration | 31,536,000s (365 days)                                           |
| vCPU rate    | 20,000 µXUSD/hour                                                |
| Memory rate  | 10,000 µXUSD/GB/hour                                             |
| Disk rate    | 1,000 µXUSD/GB/hour                                              |
| Stake        | ceil(cost / 5) µXUSD (min 1)                                     |
| XE reward    | ceil(cost × R\_capped / 1000) µXE (min 1, rate locked at accept) |

## Performance Certificates [#performance-certificates]

| Parameter           | Value              |
| ------------------- | ------------------ |
| BenchmarkIterations | 375,000,000        |
| MemoryTableSize     | 8,388,608 (256 MB) |
| MemoryReads         | 1,000,000          |
| WorkloadVersion     | 3                  |
| CertificateValidity | 7 days             |

## Consensus & Network [#consensus--network]

| Parameter                        | Value                               |
| -------------------------------- | ----------------------------------- |
| Quorum threshold                 | 67%                                 |
| Connection manager low           | 100                                 |
| Connection manager high          | 400                                 |
| Connection grace                 | 1 minute                            |
| API rate limit (reads, non-POST) | 200 req/s, burst 1,000              |
| API rate limit (writes, POST)    | 10 req/s, burst 50                  |
| Rate limiter idle TTL            | 5 minutes (max 100,000 tracked IPs) |
| Default API port                 | 8080                                |
| Default libp2p port              | 9000                                |

---

Canonical HTML: https://test.network/docs/constants · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Cryptography

> ed25519, SHA-256, blake2b PoW. Cross-implementation compatibility (Go ↔ JS).

XE uses standard cryptographic primitives with cross-implementation compatibility between Go and JavaScript (tweetnacl/blakejs).

## Key Generation [#key-generation]

Ed25519 via `crypto/rand` or deterministic from 32-byte seed.

## Signing Contexts [#signing-contexts]

* **Block hashing** — `SHA-256(networkID ‖ canonical bytes ‖ aux bytes)`. The aux tail binds the certificate hash and timekeeper attestations into the hash (empty for non-lease blocks). Genesis blocks are hashed with the network-ID prefix cleared — they are created before the network ID is known.
* **Block signing** — `ed25519.Sign(privateKey, hash)`
* **Vote signing** — ed25519 over canonical vote encoding
* **Attestation signing** — ed25519 over `SHA-256(leaseHash || timestamp)`
* **Chat signing** — ed25519 detached signature over message envelope
* **Directory signing** — `ed25519.Sign(priv, SHA-256("xe/directory-registration/v1\x00" ‖ len‖networkID ‖ len‖account ‖ len‖nodePeer ‖ len‖timestamp))`, where each `len` is an 8-byte big-endian length prefix framing the field that follows

## Proof of Work [#proof-of-work]

Anti-spam only, not consensus. Always computed client-side.

```
result = blake2b_8(nonce_LE || blockHash)
valid  = result >= difficulty
```

* Block DefaultDifficulty: `0xfffff80000000000` (\~2²¹ attempts, \~1s)
* Chat DefaultPoWDifficulty: `0xffffc00000000000` (\~2¹⁸ attempts — chat spam pricing is tuned independently of block mining)
* TestDifficulty: `0x0000000000000002` (instant)
* Nonce: little-endian; result compared as big-endian
* Both difficulties are advertised in `GET /node`

Functions: `ComputePoW`, `ComputePoWConcurrent`, `ComputePoWWithContext`, `ValidatePoW`

---

Canonical HTML: https://test.network/docs/cryptography · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


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

## Embedded UI (Recommended) [#embedded-ui-recommended]

Single binary deployment. No static file rsync, no separate builds.

```bash
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](#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) [#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 [#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 [#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 [#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 [#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 [#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:

```bash
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 [#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:

```bash
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 [#cicd-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.

---

Canonical HTML: https://test.network/docs/deployment · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Binary Encoding

> Version 2 (0x02) — deterministic block and vote wire formats.

Version 2 (`0x02`). Deterministic layout for hashing and signing.

## Block Canonical Encoding [#block-canonical-encoding]

| Offset     | Size | Field                                                                                                                                                                                                               |
| ---------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 0          | 1    | Version byte (0x02)                                                                                                                                                                                                 |
| 1          | 1    | Type byte                                                                                                                                                                                                           |
| 2          | 8    | Asset (left-aligned UTF-8, zero-padded)                                                                                                                                                                             |
| 10         | 32   | Account (hex-decoded)                                                                                                                                                                                               |
| 42         | 32   | Previous (hex-decoded; "0" → 32 zero bytes)                                                                                                                                                                         |
| 74         | 8    | Balance (big-endian uint64)                                                                                                                                                                                         |
| 82         | 8    | Timestamp (big-endian int64)                                                                                                                                                                                        |
| 90+        | var  | Type-specific tail                                                                                                                                                                                                  |
| after tail | 32   | Representative (32 zero bytes if empty)                                                                                                                                                                             |
| after rep  | 48   | Genesis lease-timing tail — genesis only, present iff any timing field is non-zero: six big-endian uint64s (min\_duration, settle\_grace, force\_settle\_gap, escrow\_expiry, archive\_gap, max\_attestation\_skew) |
| trailer    | 1+N  | Send/burn only: `memo_len` (1 byte, 0–64) + memo bytes — **always present on send and burn, even when the memo is empty**, so a missing memo and an empty memo encode identically                                   |

## Memos [#memos]

Send and burn blocks may carry a UTF-8 memo of up to 64 bytes (`MaxMemoBytes`, byte-counted). The memo is part of the canonical encoding, so it is covered by the block hash and signature.

## Type Bytes [#type-bytes]

Send=0x01, Receive=0x02, Lease=0x04, LeaseAccept=0x05, LeaseSettle=0x06, Genesis=0x07, MultisigOpen=0x08, MultisigUpdate=0x09, LeaseCancel=0x0A, Burn=0x0B, LeaseForceSettle=0x0C, Mint=0x0D

`0x03` is a retired, unallocated gap (the removed permissionless claim type).

## Full Block Encoding [#full-block-encoding]

```
[canonical bytes] [8 bytes PoW nonce (little-endian uint64)]
```

## Vote Encoding [#vote-encoding]

| Offset | Size | Field                                |
| ------ | ---- | ------------------------------------ |
| 0      | 1    | Version (0x02)                       |
| 1      | 32   | RepPubKey                            |
| 33     | 32   | BlockHash                            |
| 65     | 32   | ConflictAccount                      |
| 97     | 32   | ConflictPrev                         |
| 129    | 8    | Timestamp (big-endian)               |
| 137    | 1    | Final flag (0 = converge, 1 = final) |
| 138    | 2    | Signature length                     |
| 140    | N    | Signature bytes                      |

Signing bytes are offsets 0–137 (138 bytes); the final flag is signed so a converge vote cannot be flipped into a final vote. Total wire size with a 64-byte ed25519 signature: 204 bytes. `DecodeVote` rejects any version byte other than 0x02.

PoW nonce = little-endian; all other numeric fields = big-endian.

---

Canonical HTML: https://test.network/docs/encoding · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Explorer & Web UI

> Embedded web UI shipped inside the xe binary via //go:embed. No JS deps.

The embedded web UI ships inside the `xe` binary via `//go:embed`. No JavaScript dependencies, no build step, no separate deployment. Enable with `xe node --ui`.

## Technology [#technology]

* Plain HTML + ES modules + Web Crypto
* No framework, no bundler, zero JS dependencies
* Embedded in the Go binary via `//go:embed`
* Served by a separate HTTP server on `--ui-port` (default 8000, bound to 127.0.0.1) that reverse-proxies `/api/*` to the API server

## Enabling [#enabling]

```bash
xe node --ui                     # serve UI on 127.0.0.1:8000 (default)
xe node --ui --ui-port 8081      # serve UI on a different port
xe node --ui --wallet=false      # disable wallet pages (on by default)
```

`--ui` requires `--api` — the node exits at startup otherwise, because the UI
proxies `/api/*` to the API server. `--ui-bind` defaults to 127.0.0.1, so a
remote box needs a reverse proxy in front of the UI port.

## Pages [#pages]

* **Dashboard (/)** — node identity, stat tiles with sparklines, block-rate chart, recent blocks
* **Accounts (/accounts)** — sortable table of all accounts
* **Blocks (/blocks)** — recent blocks with asset, amount, balance
* **Leases (/leases)** — all compute leases
* **Providers (/providers)** — capacity metrics (capacity, used, active, total leases, last update)
* **Provider (/provider)** — own provider status and stats
* **Wallet (/wallet)** — send and receive (served by default; `--wallet=false` disables)
* **Wallets (/wallet/wallets)** — create, import, rename, remove wallets; reveal seed
* **DAO (/dao)** — draft, sign, submit state chain blocks
* **State Chain (/statechain)** — block history and KV browser
* **Conflicts (/conflicts)** — active/resolved conflicts
* **Peers (/peers)** — network connectivity
* **Pending (/pending)** — unreceived sends
* **Frontiers (/frontiers)** — all account frontiers
* **Chat (/chat)** — P2P messaging interface

## Development Override [#development-override]

Use `--ui-dir ./web/` to serve from filesystem instead of embedded assets (hot reload during development).

## Legacy [#legacy]

The standalone explorer and web wallet apps — including the bundled React explorer this site still serves at its own `/explorer` route — are deprecated. The embedded UI (`xe node --ui`) replaces both.

---

Canonical HTML: https://test.network/docs/explorer · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Getting Started

> Build the XE CLI, create a wallet, request XE from the testnet faucet, and make your first transactions.

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 providers` returns an empty list, so `xe lease` has nobody to accept a lease and `xe ssh` has nothing to connect to.

## Prerequisites [#prerequisites]

* Go 1.25+
* Git
* `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 [#building-from-source]

The source is public at [github.com/xeprotocol/xe](https://github.com/xeprotocol/xe), licensed under the [GPL-3.0](https://github.com/xeprotocol/xe/blob/master/LICENSE) — 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.

```bash
git clone https://github.com/xeprotocol/xe.git
cd xe
make build          # produces ./xe
```

`make` 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 [#running-a-node]

Start a node with default settings: libp2p on port 9000, HTTP API on port 8080, data in `./data`.

```bash
./xe node
```

## Connecting to the Testnet [#connecting-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:

```bash
./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/12D3KooWEqv1BRZkSntgcgbrJh7bobFRSkBdRupubvNZLEx8hZLA
```

Confirm what you are about to join before you join it:

```bash
./xe verify-genesis --genesis-dir ./genesis/testnet-0001
```

The current network is &#x2A;*`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/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:

```bash
curl -s https://ldn.core.test.network/node | jq -r .id
```

There 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:

```bash
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 [#using-the-cli]

Point the CLI at a testnet node and interact without running your own:

```bash
export XE_NODE=https://ldn.core.test.network
```

### Hello world — two wallets and a transfer [#hello-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`.

```bash
XE_WALLET=~/.xe/alice.seed xe wallet create
XE_WALLET=~/.xe/bob.seed   xe wallet create
```

```
Wallet created!
  Address:    7d27d0a34cc2a5cd08f65905a983fabec1a517baf6d3cdab0a921256ecb9af57
  Public key: 665b50f96f8a4a86e1940386cce7fa1c0592c8eba9524fe9d579254fc341f02b
  File:       /home/you/.xe/alice.seed
```

The **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:

```bash
XE_WALLET=~/.xe/alice.seed xe faucet
XE_WALLET=~/.xe/alice.seed xe receive
XE_WALLET=~/.xe/alice.seed xe wallet balance
```

Send Bob 25 XE, using the address printed for Bob above, and let Bob claim it:

```bash
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 balance
```

Every 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:

```bash
curl -s $XE_NODE/accounts/<address>/balance
curl -s $XE_NODE/accounts/<address>/chain
```

> \[!NOTE] How the faucet works
> `xe faucet` asks the faucet service (`XE_FAUCET`, default `https://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 returns `429` with a `retry_after_seconds`. The grant arrives as a pending send, so follow it with `xe receive`.

### Compute [#compute]

```bash
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 providers` returns 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 [#docker-deployment]

The repository ships a Dockerfile and a systemd unit in [`deploy/`](https://github.com/xeprotocol/xe/tree/master/deploy) — use those rather than the sketch below if you are running a node for real. A minimal equivalent:

```dockerfile
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 [#web-based-quick-start]

If you prefer a browser-based experience, use the hosted web wallet at [ldn.test.network/wallet](https://ldn.test.network/wallet/):

1. Open the wallet and create a new wallet (your seed is encrypted client-side with AES-GCM)
2. Request testnet XE from the faucet — 1,000 XE per account per day
3. Explore the network via the [Explorer](/explorer)
4. Send transactions or use chat. Leasing compute needs a provider, and none are online

## Environment Variables [#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     |

---

Canonical HTML: https://test.network/docs/getting-started · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Networking

> libp2p host, peer admission, GossipSub topics, frontier sync, DHT, and request-response messaging.

Fully peer-to-peer using libp2p. No central servers.

## Components [#components]

| Component       | Purpose                                                     | Protocol                    |
| --------------- | ----------------------------------------------------------- | --------------------------- |
| Host            | TCP transport, connection management                        | libp2p core                 |
| Netcheck        | Peer admission — network-ID + version handshake             | `/xe/netcheck/1`            |
| GossipSub       | Broadcast blocks, votes, marketplace, statechain, directory | libp2p GossipSub            |
| Sync            | Frontier-based block synchronization                        | `/xe/sync/1.0.0`            |
| Statechain sync | State-chain block synchronization                           | `/xe/statechain-sync/1.0.0` |
| Messaging       | Request-response over streams                               | `/xe/msg/1.0.0`             |
| Tunnel          | SSH-over-network tunnels to leased VMs                      | `/xe/tunnel/2.0.0`          |
| DHT             | Kademlia peer discovery                                     | `/xe` prefix                |

## Discovery Mechanisms [#discovery-mechanisms]

1. **mDNS** — automatic local network discovery
2. **Bootstrap peers** — explicit via `-dial` flag; a watchdog re-dials disconnected bootstraps every 30s (10s timeout per dial)
3. **Kademlia DHT** — distributed hash table with `/xe` prefix

## GossipSub Topics [#gossipsub-topics]

| Topic             | Data Type       |
| ----------------- | --------------- |
| `xe/blocks`       | Block           |
| `xe/votes`        | Vote            |
| `xe/marketplace`  | MarketplaceMsg  |
| `xe/statechain`   | StateChainBlock |
| `xe/directory`    | Registration    |
| `xe/certificates` | Certificate     |

Max gossip message size: 256 KB.

## Sync Protocol [#sync-protocol]

* Frontier-based: exchange `account → latest block hash`
* Paginated responses (default 64 blocks/page, max 256)
* Max 10,000 blocks per sync session
* 5-second per-peer cooldown
* Periodic re-sync every 10 seconds (with dirty flag optimization)
* Cross-account dependency retry passes, with early exit when a pass makes no progress
* Block quarantine for permanently invalid blocks

## Messaging Protocol [#messaging-protocol]

* Request-response semantics
* 30-second stream deadline
* 64 KB max request/response
* Message types: `vm_credentials`, `vm_status`, `account_chat`, `attest_timestamp`, `block_request`, `vote_request`, `cert_request`
* `block_request`, `vote_request`, and `cert_request` are targeted sync-repair RPCs — a node missing a block body, an election's votes, or a lease certificate asks a peer for it directly
* DHT-based peer discovery fallback

## Security [#security]

* **Peer admission**: on connect, peers exchange network ID and protocol version over `/xe/netcheck/1`; a mismatch disconnects the peer and applies a bounded ban. After a testnet wipe, this is why a node built against the wrong network ID silently fails to join.
* Inbound connections are capped per source IP (default 8, `--max-conns-per-ip`)
* Transport encryption (Noise or TLS 1.3)
* Persistent Ed25519 identity
* GossipSub pre-validates field lengths
* Sync rate limiting (5s per peer)
* Max message sizes enforced

## Host Configuration [#host-configuration]

* TCP on all interfaces, configurable port
* Connection manager: low=100, high=400, grace=1min
* Persistent identity at `{dataDir}/host.key`
* Optional relay + hole-punching support

---

Canonical HTML: https://test.network/docs/networking · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# State Chain

> Linear multisig chain — a deterministic state machine for DAO governance and emission.

A linear chain of multisig blocks forming a deterministic state machine. It carries DAO governance and drives emission: the oracle, tokenomics parameters, and network phase all live here.

## Properties [#properties]

* **Linear** — one chain, one tip
* **Multisig** — threshold signatures from DAOKeyset
* **Self-referential** — keyset stored in own KV store at `sys.dao_keyset`
* **Deterministic** — replay all blocks → same KV state

## Block Structure [#block-structure]

```go
type Block struct {
    Index      uint64
    PrevHash   string
    Ops        []Op            // [{action: "set"/"delete", key, value}]
    Signatures []BlockSignature
    Hash       string          // SHA-256 of the canonical encoding (below)
    Timestamp  int64
}
```

The hash covers the canonical encoding `index(8 BE) ‖ prev_hash(32) ‖ num_ops(4 BE) ‖ ops ‖ timestamp(8 BE)` — signatures excluded, so signing does not change the hash.

## System Keys [#system-keys]

Six validated `sys.*` keys:

* `sys.dao_keyset` — DAO signer quorum (min threshold: 2)
* `sys.timekeepers` — trusted timekeeper keys and threshold
* `sys.minter` — authorized XUSD issuers
* `sys.oracle` — oracle configuration
* `sys.phase` — network phase (transitions are one-directional)
* `sys.tokenomics` — emission R-curve parameters and Hermite cap

`sys.network_id` is read-only — set at genesis, never writable over the chain.

## Operations [#operations]

* `set` — create/update key with JSON value
* `delete` — remove key (cannot delete `sys.*` keys)
* Key format: `^[a-z0-9_.-]+$`, max 128 chars
* Value max: 64 KB, must be valid JSON

## Sync [#sync]

* Protocol: `/xe/statechain-sync/1.0.0`
* Gossip topic: `xe/statechain`
* Page size: 64 blocks
* 30-second per-peer cooldown
* Genesis never sent over sync (configured locally)

---

Canonical HTML: https://test.network/docs/state-chain · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Proof of Uptime

> Design proposal for verifiable uptime proofs — not yet implemented.

> \[!WARNING] Design stage — not implemented
> Proof of uptime is a **design**, not shipped behaviour. Nothing on this page runs in an XE node today: the protocol implementation contains no heartbeat, epoch, or claim code, and no block type carries an uptime proof. The constructions described here were explored as standalone prototypes in the separate `proof-of-uptime` repository, and will change before they ship.
>
> What *is* implemented for leases today is timekeeper attestation on `lease_accept` and `lease_settle` blocks (see [Compute Leasing](/docs/compute)). It does not prove continuous uptime.

The proposal: verifiable uptime proofs for compute providers, using a three-layer architecture.

## Layer 1: Heartbeat Chain [#layer-1-heartbeat-chain]

* \~60 second interval, dual-signed (provider + consumer)
* Each heartbeat would chain to the previous via SHA-256
* \~250 bytes per heartbeat

## Layer 2: Merkle Epochs [#layer-2-merkle-epochs]

* 60 heartbeats → merkle tree → 32-byte epoch root
* Epochs would chain via `prevEpochHash`
* Selective disclosure via merkle proofs

## Layer 3: Chained Claims [#layer-3-chained-claims]

* 24 epoch roots → merkle tree → claim root
* \~200 bytes on-chain per claim
* \~1,800x compression vs raw heartbeat data (24h at \~250 bytes / \~60 s; a PoC-4 demo run in the prototype repo measured 3,681x)

## Design Decision: No VDF [#design-decision-no-vdf]

Economics would solve collusion: emission rate \< lease cost, so collusion is a net loss.

## Threat Analysis [#threat-analysis]

Status reflects the design on paper — none of these mitigations are implemented.

| Threat                 | Severity | Status                              |
| ---------------------- | -------- | ----------------------------------- |
| Ghost Node Attack      | Critical | Addressed in design (multi-layer)   |
| Collusion              | High     | Addressed in design (economics)     |
| Key Compromise         | Critical | Open — must solve before production |
| Timestamp Manipulation | Medium   | Open — must solve before production |

---

Canonical HTML: https://test.network/docs/uptime · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Web Wallet

> Client-side wallet using Web Crypto. Private keys never leave the browser.

Client-side wallet embedded in the `xe` binary, served as part of the embedded web UI (`xe node --ui` — wallet pages are on by default). Private keys never leave the browser. Zero JavaScript dependencies.

## Security [#security]

* Seeds encrypted with AES-GCM via Web Crypto API
* Keys derived via PBKDF2 (600,000 iterations, SHA-256)
* Fresh random IV per encryption
* Unlock lasts a hard 30 minutes from unlock (checked lazily on use) — there is no idle timer; lock explicitly or close the tab to end it sooner
* Seeds stored in localStorage as encrypted ciphertext only
* Private keys never leave the browser
* No JS dependencies — uses native Web Crypto

> **Warning:** after unlock, the raw passphrase is cached in `sessionStorage`
> under `xe.session.unlock` for up to 30 minutes so page navigations don't
> re-prompt. Any code running in the same origin can read it — roughly
> equivalent in risk to holding the decrypted seed in JS memory. The vault on
> disk stays encrypted regardless. Use the lock button on shared machines.

## Features [#features]

* **Multi-wallet management** — create, import, rename, delete wallets
* **Send/Receive** — transfer XE between accounts; pending sends are claimed with a per-item receive button (there is no auto-receive polling)
* **Faucet** — request testnet XE; the button appears when the node is started with a faucet target
* **P2P Chat** — real-time delivery, ed25519 signed envelopes
* **Provider dashboard** — read-only view of providers; creating leases and VM/SSH access are CLI-only
* **DAO governance** — draft, sign, submit state chain blocks
* **State inspector** — browse state chain blocks and KV entries
* **Client-side signing** — blocks are signed in the browser, but not offline: the node supplies the frontier, balance, network ID, and PoW parameters, and the same call submits the block

## Enabling [#enabling]

```bash
xe node --ui                     # embedded UI, wallet pages included (default)
xe node --ui --wallet=false      # UI without wallet (explorer only)
```

Wallet pages are enabled by default whenever the UI is on; `--wallet=false`
makes the node serve 404 for `/wallet/*`. Because keys stay client-side this is
a containment convenience, not the security boundary — the UI binds to
`--ui-bind` (default 127.0.0.1), so exposing it publicly is an explicit choice
that should go through a reverse proxy.

## Technology [#technology]

Plain HTML + ES modules + Web Crypto. Embedded in the Go binary via `//go:embed`. No build step, no framework, no bundler. Signing requires Web Crypto Ed25519: Chrome 113+, Firefox 130+, Safari 17+.

---

Canonical HTML: https://test.network/docs/wallet · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# XE Testnet Explorer

The explorer at https://test.network/explorer is an interactive single-page application (accounts, blocks, leases, the state chain and network conflicts) rendered in the browser from the live node. It has no static content to read as Markdown.

Agents should use the JSON API the explorer itself reads from: `https://ldn.test.network/api` (OpenAPI at https://test.network/openapi.json, reference at https://test.network/docs/api). For example `GET https://ldn.test.network/api/blocks/recent?limit=20` lists the newest blocks and `GET https://ldn.test.network/api/accounts` lists every account with its balances.

The explorer is also documented at https://test.network/docs/explorer.

---

Canonical HTML: https://test.network/explorer · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Privacy notice.

This notice covers the website at test.network and the public London testnet node it proxies to. It is written to be read, not to be scrolled past: the short version is that there are no accounts, no sign-ups and no advertising, and the site keeps as little as it can while still running. The data controller is **XE L1 Ltd.** (UK company 17245674), 128 City Road, London, EC1V 2NX.

- [Contact](https://test.network/contact)
- [About](https://test.network/about)

## What is recorded

1. **Server logs** The web server records each request's IP address, user agent, the URL requested, the time and the response status. These logs are used to keep the site up, to debug it and to defend against abuse, and are rotated on a short schedule. They are not used to build profiles and are not shared, other than with the hosting provider on whose machines they sit.
2. **API requests** Requests to /api on this site are forwarded to the London testnet node at ldn.test.network. The node applies per-IP rate limits, so it necessarily sees your IP address for as long as the limit window lasts. Requests you make directly to ldn.test.network go there without passing through this site at all.
3. **The ledger itself** Anything you write to the testnet — blocks, account addresses, balances, directory registrations, chat envelopes — is public by design: it is replicated to every node and shown in the explorer. Do not put personal data in a chat message or a block. The testnet may be wiped, but you should treat anything written to it as permanent and public.
4. **The assistant widget** The question box on this site is Edge Assist, a third-party service loaded from assist.edge.network. Questions you type into it are sent to Edge Network to be answered from this site's public content and are subject to their privacy policy. Nothing else on the site loads from a third party: fonts are served from test.network, and there are no analytics scripts, tracking pixels or advertising tags.

## What is not recorded

- No cookies are set by test.network for tracking, preferences or sessions; there is nothing to log in to.
- Wallet keys never reach this site or the node: the web wallet and the CLI generate and encrypt keys on your own device.
- No email addresses or names are collected by the site. If you email hello@xe.network or security@xe.network, or open a GitHub issue, that correspondence lives with the email provider or GitHub under their terms.
- No data is sold, and none is used for advertising.

## Your rights and changes

Under UK GDPR you can ask what we hold about you, ask for it to be corrected or deleted, and complain to the Information Commissioner's Office. Because the site holds only short-lived server logs keyed by IP address, most requests will find nothing to return; write to [`hello@xe.network`](mailto:hello@xe.network) or to the registered office and we will check. This notice is versioned with the site's source code, so its history is public in the repository; material changes are noted in the commit that makes them.

---

Canonical HTML: https://test.network/privacy · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)


# Every XE node ships a browser wallet.

_Web Wallet · NODE-SERVED UI_

Run `xe node --ui` and the wallet is served at `/wallet`. Private keys stay client-side, encrypted with Web Crypto. Try it now on the hosted testnet node — no install needed.

- [Open Hosted Wallet](https://ldn.test.network/wallet/)
- [Read Wallet Docs](https://test.network/docs/wallet)
- [Use CLI Wallet](https://test.network/docs/cli)

```sh
$ xe node --ui
ui:      enabled
wallet:  /wallet
keys:    client-side
```

- Client-side seed encryption
- Send and receive XE
- P2P chat and provider dashboard
- DAO signing and state inspection

---

Canonical HTML: https://test.network/wallet · This Markdown is served for `Accept: text/markdown`.
More for agents: [llms.txt](https://test.network/llms.txt) · [Full docs as Markdown](https://test.network/llms-full.txt) · [OpenAPI](https://test.network/openapi.json) · [Sitemap](https://test.network/sitemap.xml)
