Nixeon documentation
Nixeon is a post-quantum payments chain. Every signature on it is ML-DSA, a NIST-standardized lattice scheme, and there is no classical fallback anywhere in the protocol. These pages cover using the wallet, building against the node, and running a node yourself.
What Nixeon is
A layer-one chain for payments, built so the payments outlive the cryptography that authorized them. Spends are authorized with ML-DSA-44 and consensus votes with ML-DSA-65, both FIPS 204, both through the fips204 crate rather than a hand-written port. That crate has not been formally audited either, and neither has this stack. Blocks and transactions hash with SHA3-256. The ledger is UTXO, and state is committed with a lattice gadget tree whose root SHA3-256 then wraps into the application hash.
Consensus is permissioned BFT across four validators with f=1 fault tolerance, and finality is deterministic. Validators confirm state by re-executing every transaction in a block and comparing the resulting application hash, so every signature is verified by every validator on every block.
What it is not
Worth reading before you build anything that matters on it.
- This is a testnet. Coins have no value and the chain may be reset.
- Nothing in the stack has been externally audited.
- Four permissioned validators. This is not a decentralized network.
- Amounts and the transaction graph are public. The confidential lane is closed at genesis, so Nixeon offers no privacy today.
- The folded block proof is built and benchmarked but is not on the consensus path and is verified by nothing on the live chain.
Create a wallet
Open the wallet and create an account. It generates a BIP-39 mnemonic in your browser and derives your keys from it with Argon2id. The mnemonic and every key derived from it stay in the browser. Nothing is uploaded, and there is no account on any server to recover from, so if you lose the mnemonic the funds are gone.
Get testnet funds
Paste your nixaddr1 address into the faucet and request. It pays a fixed amount per request, shown on that page, and the payment lands in the next block. The amount is a server-side setting rather than a protocol constant, which is why this page does not repeat the number. You can also call the faucet directly.
curl -X POST https://pq-faucet.nixprotocol.com/faucet \
-H 'content-type: application/json' \
-d '{"address":"nixaddr1..."}'Send a payment
In the wallet, enter a recipient nixaddr1 address and an amount, then send. The wallet selects inputs from your unspent outputs, builds the transaction, signs each input with ML-DSA-44 in the browser, and broadcasts it. A transaction settles when it is included in a committed block, which is a single block time. There are no confirmations to wait for beyond that, because finality is deterministic rather than probabilistic.
Read the explorer
The explorer resolves addresses, transaction hashes and block heights. Paste an address to see its balance and history, a nixtx1 hash to see a transaction and its inputs and outputs, or a block height to see a block. Every value it shows comes from the same public RPC documented below, so anything the explorer can show you, your own client can read too.
Addresses and encodings
Four identifier types, all bech32m, each with its own prefix so a value of the wrong kind cannot be mistaken for another.
| Prefix | What it identifies |
|---|---|
nixaddr1 | A payment address |
nixutxo1 | An outpoint, meaning one unspent output |
nixtx1 | A transaction hash |
nixapp1 | An application state hash |
An address commits to a small Merkle tree of authorization schemes rather than to a single public key, so a spend reveals only the scheme it actually used. Each input carries its own public key, the slot that key occupies, the Merkle path proving the slot is committed, and its own signature, and the verifier recomputes the address from those rather than trusting one.
Slot 0 is ML-DSA-44 and slot 1 is SLH-DSA-SHAKE-128s, deliberately hash-based rather than a second lattice scheme, so that a structural result against lattices does not take both. Slot 1 is committed but inert. Nothing spends through it today and activating it would need a consensus rule, so treat ML-DSA-44 as the only spend path. It exists so a lattice break has somewhere to go without every user changing address.
Signatures
| Use | Scheme | Public key | Signature |
|---|---|---|---|
| Spend authorization | ML-DSA-44 (FIPS 204) | 1,312 bytes | 2,420 bytes |
| Consensus and peer identity | ML-DSA-65 (FIPS 204) | 1,952 bytes | 3,309 bytes |
Those lengths are asserted at compile time against the fips204 crate, so they cannot drift from what the chain actually accepts. You can confirm the consensus scheme yourself from /consensus/status, which reports both the scheme tag and the validator public key.
Public RPC reference
The public node is at https://pq-node.nixprotocol.com. All routes below are GET unless marked otherwise, and all return JSON.
| Route | Returns |
|---|---|
/consensus/status | Chain id, height, latest app hash, validator info |
/consensus/block?height=N | A block header and its transactions |
/consensus/commit?height=N | The commit, meaning the validator signatures for a height |
/app/balance/:address | Spendable balance for an address |
/app/utxo/:outpoint | One unspent output |
/app/utxos_by_recipient/:address | Every unspent output payable to an address |
/app/txs_by_address/:address | Transaction history for an address, paginated |
/app/tx_details?hash=nixtx1... | One transaction with its resolved inputs and outputs |
/app/txs | Recent transactions across the chain, paginated |
/consensus/broadcast_tx_sync (POST) | Submit a signed transaction |
Two examples you can run now.
curl -s https://pq-node.nixprotocol.com/consensus/status curl -s https://pq-node.nixprotocol.com/app/balance/nixaddr1...
The paginated routes return a next cursor alongside their results. Pass it back to continue, and treat its absence as the end of the range rather than assuming a fixed page count.
Submitting a transaction
Build and sign the transaction client-side, then post the bincode encoding of it as a hex string.
POST https://pq-node.nixprotocol.com/consensus/broadcast_tx_sync
content-type: application/json
{"tx": "<hex-encoded bincode bytes>"}The response tells you whether it was accepted.
{
"code": 0,
"log": "",
"hash": "nixtx1..."
}A code of 0 means accepted into the mempool and 1 means rejected. On rejection the body also carries an error_kind discriminant you can branch on, and log carries the human-readable reason. Acceptance is not inclusion, so poll /app/tx_details?hash= with the returned hash to see the transaction once it lands in a block.
Endpoints that are not public
The node serves two separate routers. Proof, per-transaction result and app-hash routes live on an internal router bound only where an operator puts it, and they are deliberately absent from the public endpoint. If you are building against https://pq-node.nixprotocol.com, treat these as unavailable rather than as something to request.
- /consensus/block_results
- /consensus/tx and /consensus/tx_result
- /app/app_hash
Initialize and start a node
A node is one binary. Initialize a data directory from genesis files once, then start it against that directory.
pqe-node init \ --chain-dir ./data \ --genesis genesis.json \ --accounts accounts.json \ --validator-key validator_key.json \ [--config config.toml] pqe-node start --chain-dir ./data \ [--rpc-bind 0.0.0.0:26657] [--log-level info]
The chain directory must be empty or absent when you initialize. Storage carries a schema version and there is no on-disk migration, so a schema change means re-initializing from genesis rather than upgrading in place.
Consensus modes
The mode is chosen at startup from the [node].consensus key in config.toml. There is a single-validator instant-commit mode, useful for local development, and a permissioned two-thirds BFT mode, which is what the public testnet runs. Byzantine fault tolerance of f=1 needs a minimum of four authorities, which is why the testnet has four.
Deploying for real
Multi-host deployment, generating per-host bundles, funding genesis accounts and the Docker and Railway paths are covered by the operator runbook in the repository rather than duplicated here, because a second copy of deploy instructions drifts from the binaries it describes. See deploy/RUNBOOK.md, which is cross-checked against the shipped CLI.
For a local multi-process cluster, the testnet tool brings one up in a single command.
pqe-testnet up --validators 4 --base-dir ./cluster \ --node-bin target/release/pqe-node