601 lines
32 KiB
Markdown
601 lines
32 KiB
Markdown
# Run an AERE node, operator runbook
|
|
|
|
This is the runbook for standing up an independent Hyperledger Besu node on the
|
|
AERE Network, chain ID **2800**, and applying to become one of the first
|
|
non-Foundation validators.
|
|
|
|
Read it end to end before you start. It is written for someone who has run a
|
|
node before and is comfortable with Docker and a Linux server.
|
|
|
|
> **Corrected 2026-10-20.** Until this date this runbook said that a validator's post-quantum keys
|
|
> are "issued at admission", listed a property (`aere.falcon.genesis`) that no running validator
|
|
> carries, and did not name the SLH-DSA key at all. All three were wrong. Every key of a validator is
|
|
> generated by its operator, on the operator's own machine, with the tools in the `validator/`
|
|
> directory of the `aere-node` repository; nothing private ever leaves that machine. Section 6
|
|
> describes admission as it now works, step by step, and section 3b the clock requirement.
|
|
|
|
---
|
|
|
|
## What AERE actually is today (read this first)
|
|
|
|
AERE runs **Hyperledger Besu** with **QBFT** consensus and **sub-second blocks** (0.62 s on average
|
|
over the 100,000 blocks before 2026-10-15).
|
|
Consensus is a permissioned proof-of-authority BFT set, not proof-of-work and
|
|
not on-chain proof-of-stake.
|
|
|
|
- The active validator set is **10 validators as read from the chain at 2026-09-29 07:55 UTC**, all Foundation-operated; f = 3, quorum 7-of-10 (as read from the chain at 2026-09-29 07:55 UTC). Read the live set with `qbft_getValidatorsByBlockNumber` and believe the chain over this line.
|
|
- QBFT tolerates `f = floor((n - 1) / 3)` faulty validators. At **n = 10, f = 3**:
|
|
the network tolerates three simultaneous Byzantine faults (commit quorum 7 of 10).
|
|
It is honest to say the set is small and single-operator today.
|
|
- The public path is **10 to 21** validators. This program exists
|
|
specifically to admit the **first independent (non-Foundation) operators**.
|
|
- Admission is a **governance vote of the existing validator set**, not an
|
|
automatic or permissionless process. There is **no staking, no bond, and no
|
|
slashing.** A validator that misbehaves or goes down for a prolonged period is
|
|
removed by the same governance vote that admitted it. Accountability is social
|
|
and on-chain, not economic.
|
|
|
|
If you are looking for a stake-and-earn validator with slashing and unbonding,
|
|
that is not how AERE works. Do not trust older pages that describe self-staking,
|
|
slashing, unbonding periods, or APY tiers; they predate the current QBFT design.
|
|
|
|
---
|
|
|
|
## What running this gives you
|
|
|
|
A **full, syncing node** on chain 2800, built from the same published patch series the
|
|
validators run. A validator differs from a following node in four things, all of them yours to
|
|
hold and none of them issued by anyone else:
|
|
|
|
1. its node key (`data/key`), which is also its consensus key: the address voted into the set;
|
|
2. a Falcon-512 key and an SLH-DSA-SHA2-128s key, generated on your machine (section 3);
|
|
3. a row in a post-quantum registry epoch that carries those public keys, signed by them (section 6);
|
|
4. the validator-only properties: the paths of the two key files and the heights from which the node
|
|
emits post-quantum seals (section 7b).
|
|
|
|
So the sequence is:
|
|
|
|
1. Stand up a following node with this runbook (or with `run-node.sh` in `aere-node`), and keep it
|
|
healthy. The Foundation observes it from outside for fourteen days before a vote is proposed.
|
|
2. Generate your keys and write your candidacy file on your own machine (sections 3 and 6).
|
|
3. The Foundation verifies the candidacy independently and puts your row into a new registry epoch,
|
|
which every node, yours included, loads before your admission.
|
|
4. Your node restarts as a validator and connects to every validator; then the existing validators
|
|
vote. After the vote your node proposes and seals blocks. No reinstall, no migration.
|
|
|
|
---
|
|
|
|
## 1. Prerequisites
|
|
|
|
**Software**
|
|
|
|
- A Linux host (the live nodes run Docker on Linux).
|
|
- Docker and the Docker Compose plugin, or a build from source (`run-node.sh` in `aere-node` needs git
|
|
and a JDK 21). The key tools of section 3 need the same JDK 21 and a build from the published patch
|
|
series, which `./run-node.sh --no-start` produces.
|
|
|
|
**Hardware**
|
|
|
|
There is no published minimum-hardware spec sheet for AERE, so treat the
|
|
following as **guidance, not a documented requirement**. The Foundation confirms
|
|
final sizing on application.
|
|
|
|
- The node is configured with a **4 GB JVM heap** (`-Xmx4g`), matching the live
|
|
validators. Give the host meaningful headroom above that. A reasonable
|
|
starting point is **4 vCPU, 8 GB RAM, NVMe SSD**, but confirm on application.
|
|
- **A CPU with SHA hardware instructions** (x86 SHA-NI, or the ARMv8 SHA2 extension). A validator signs
|
|
an SLH-DSA-SHA2-128s seal on the consensus thread at the parent of every anchor block; measured on
|
|
chain 2800 that costs 0.3 to 0.7 s per signature with the JDK digest, which uses those instructions
|
|
(AIP-22, table of constants). A slower signature delays the anchor round.
|
|
- **Local NVMe, not network-attached block storage.** The node writes its post-quantum seal store on the
|
|
consensus thread; on a network volume every such write adds milliseconds to each slot.
|
|
- Storage uses Besu **Bonsai** format. At sub-second blocks the trie-log data
|
|
grows faster than on a slow chain, so provision generous, growable NVMe disk
|
|
and monitor free space over time. Block headers are most of what a node stores.
|
|
|
|
**Network / firewall**
|
|
|
|
- **30303/tcp and 30303/udp**: open to the internet. This is P2P. Peers must be
|
|
able to reach you.
|
|
- **8545 (JSON-RPC)**: keep private. The compose binds it to `127.0.0.1` on the
|
|
host. Do not expose it to the public internet without an authenticating proxy
|
|
in front.
|
|
- **9545 (metrics)**: keep private, localhost only.
|
|
|
|
**RPC policy for a validator.** A validator's JSON-RPC stays on localhost, always: it serves the
|
|
`qbft_*` methods, including `qbft_proposeValidatorVote`, and a public endpoint on a consensus node is a
|
|
denial-of-service surface on the very node that has to answer within a slot. If you want to offer
|
|
public reads, run a separate following node for them and keep the validator private.
|
|
|
|
---
|
|
|
|
## 2. Get the config bundle
|
|
|
|
Everything you need to start is in this directory
|
|
(`https://aere.network/validator/`):
|
|
|
|
| File | What it is | Source |
|
|
| --- | --- | --- |
|
|
| `docker-compose.yml` | The node service, real QBFT flags | Published here |
|
|
| `config.toml` | Same config for bare-metal / from-source runs | Published here |
|
|
| `genesis.json` | Chain 2800 genesis (no private keys) | Published here |
|
|
| `static-nodes.json` | Current peer / bootnode enode list | **Provided by the Foundation on application** |
|
|
| Bootnode enode string | For `--bootnodes` | **Provided by the Foundation on application** |
|
|
| Canonical genesis hash | To verify your genesis | **Provided by the Foundation on application** |
|
|
|
|
Download the published files into a working directory, for example
|
|
`/opt/aere`:
|
|
|
|
```bash
|
|
mkdir -p /opt/aere && cd /opt/aere
|
|
curl -fSLO https://aere.network/validator/docker-compose.yml
|
|
curl -fSLO https://aere.network/validator/config.toml
|
|
curl -fSLO https://aere.network/validator/genesis.json
|
|
```
|
|
|
|
### The post-quantum registry files (added 2026-09-12)
|
|
|
|
`docker-compose.yml` mounts two directories that the steps above did not create, and a node
|
|
started without them cannot judge an anchor certificate at all. Fetch them:
|
|
|
|
```bash
|
|
mkdir -p /opt/aere/aere-pqc /opt/aere/pq && cd /opt/aere/aere-pqc
|
|
curl -fSLO https://aere.network/pq/manifests/manifest-13014000.json
|
|
curl -fSLO https://aere.network/pq/manifests/manifest-18082816.json
|
|
curl -fSLO https://aere.network/pq/manifests/registru-2800-v2-13600000.properties
|
|
cd /opt/aere/pq
|
|
curl -fSLO https://aere.network/pq/manifests/hibrid-10.properties
|
|
```
|
|
|
|
**Check what you downloaded, because a 200 is not proof of a file.** Until 2026-09-12 two of
|
|
these paths were not published, and the site answered every one of them with HTTP 200 and the
|
|
51 KB HTML page of the web app. `curl -fSLO` succeeds on that: exit code 0, a file on disk, the
|
|
wrong bytes. So look at the first character of each file, and refuse anything that starts with
|
|
`<`:
|
|
|
|
```bash
|
|
for f in /opt/aere/aere-pqc/* /opt/aere/pq/*; do
|
|
head -c 1 "$f" | grep -q '<' && echo "NOT A REGISTRY FILE: $f" || echo "ok: $f"
|
|
done
|
|
```
|
|
|
|
The same four files ship inside the `aere-node` package, with a `SHA256SUMS` beside them, if you
|
|
prefer to take them from there and compare.
|
|
|
|
**Added 2026-10-07: a fifth file, the stage-3 registry epoch.** Since block 20,836,228 (AIP-22 stage 3,
|
|
active on chain 2800 since 2026-09-30) the header carries post-quantum validator identities, and
|
|
`docker-compose.yml` now passes `aere.pq.identityBlock=20836228` with `aere.pq.registryV3=/opt/aere-pqc/epoca-v3-2800-20836228.json`.
|
|
Until 2026-10-07 this file did not list that option at all: a validator started from it would have stopped at
|
|
block 20,836,228. The epoch file is the one published as `registry-v3-20836228.json`; save it under the name
|
|
the option expects:
|
|
|
|
```bash
|
|
cd /opt/aere/aere-pqc
|
|
curl -fSL -o epoca-v3-2800-20836228.json https://aere.network/pq/manifests/registry-v3-20836228.json
|
|
sha256sum epoca-v3-2800-20836228.json
|
|
```
|
|
|
|
Expected: `0212d8ff9e857f1c756b0004929e9ad4c7988341af4837d947e7b5263fb8c581` (194,122 bytes; measured on a
|
|
running validator of the fleet and on the served file on 2026-10-07). The option `aere.pq.registryV3.hashes`
|
|
pins the epoch's identity hash, so a node given the wrong file refuses to start rather than following the
|
|
chain with a wrong registry; the first-character check above applies to this file too.
|
|
|
|
**Every later epoch is added, never swapped.** When an epoch that admits a new validator is announced, the
|
|
index at `https://aere.network/pq/manifests/index.json` lists it (`registry-v3-<height>.json`, with its
|
|
sha256 and registry hash), and every node appends the file to `aere.pq.registryV3` and its
|
|
`<height>:<hash>` pin to `aere.pq.registryV3.hashes`, keeping the earlier ones. The announcement gives the
|
|
exact values and the height by which every node must carry them.
|
|
|
|
### Verify the genesis
|
|
|
|
The `genesis.json` published here is the chain-2800 genesis. It contains **no
|
|
private keys**. The `comment` fields inside `alloc` are human labels that Besu
|
|
ignores, so they do not affect the genesis block hash.
|
|
|
|
After your node's first start, confirm your node computed the **same genesis
|
|
hash** the Foundation gives you on application:
|
|
|
|
```bash
|
|
curl -s -X POST http://127.0.0.1:8545 \
|
|
-H 'Content-Type: application/json' \
|
|
--data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0",false],"id":1}' \
|
|
| grep -o '"hash":"0x[0-9a-f]*"'
|
|
```
|
|
|
|
The value must equal the canonical chain-2800 genesis hash provided by the
|
|
Foundation on application. If it does not match, stop and contact the Foundation;
|
|
do not keep running against a divergent genesis.
|
|
|
|
---
|
|
|
|
## 3. Your keys: generated on your machine, never sent anywhere
|
|
|
|
A validator holds three private keys. You generate all three; the Foundation never sees, holds or
|
|
issues any of them.
|
|
|
|
**The node key, `data/key` (secp256k1).** Besu creates it the first time the node starts. It is your
|
|
node's P2P identity **and your consensus key**: the address derived from it is the one voted into the
|
|
set, and Besu sends consensus messages only to peers whose node-key address is in the set. A validator
|
|
whose node key is not its validator key receives no consensus message at all. So never start the node
|
|
with a node key from another place than the data directory you register.
|
|
|
|
Create the data directory, then start the node once so Besu writes the key:
|
|
|
|
```bash
|
|
cd /opt/aere
|
|
mkdir -p data
|
|
docker compose up -d
|
|
```
|
|
|
|
Read your node's public info (enode and address) once it is up:
|
|
|
|
```bash
|
|
curl -s -X POST http://127.0.0.1:8545 \
|
|
-H 'Content-Type: application/json' \
|
|
--data '{"jsonrpc":"2.0","method":"net_enode","params":[],"id":1}'
|
|
```
|
|
|
|
**The post-quantum keys (Falcon-512 and SLH-DSA-SHA2-128s).** They are generated by
|
|
`PqRegistryV3Tool keygen`, a tool inside your own build of the published patch series (patch 0008),
|
|
through the kit in the `aere-node` repository:
|
|
|
|
```bash
|
|
git clone https://git.aere.network/aere-network/aere-node && cd aere-node
|
|
./run-node.sh --no-start # builds the Aere fork from the published patches (JDK 21)
|
|
validator/validator-keys.sh --params admission-params.json --data-path <your node's data directory>
|
|
```
|
|
|
|
`admission-params.json` is a small public file the Foundation sends you (the chain id, the height and
|
|
size of the registry epoch that will carry your row, and your row index). The script refuses unless the
|
|
build comes from the bundle's patch series, writes the two key files owner-only (mode 600) into an
|
|
owner-only directory (default `~/.aere-validator/keys`, outside the repository on purpose), never
|
|
overwrites an existing key, prints only public keys and your post-quantum id, and checks its own output
|
|
for any private value before it ends.
|
|
|
|
### Protect your keys
|
|
|
|
- `data/key` **and** the two post-quantum key files **are** your validator identity. Back them up
|
|
together, somewhere secret and offline. Anyone with them can sign as your validator; losing them means
|
|
a new admission.
|
|
- **Never** `rm -rf data/`. That destroys the node key and, if you were already a
|
|
validator, forces re-admission. To wipe chain data while keeping the key:
|
|
|
|
```bash
|
|
find /opt/aere/data -mindepth 1 ! -name key -exec rm -rf {} +
|
|
```
|
|
|
|
---
|
|
|
|
## 3b. The clock (added 2026-10-20)
|
|
|
|
Chain 2800 rejects a block header whose timestamp is more than **one second** ahead of the node's own
|
|
clock (QBFT's `TimestampBoundedByFutureParameter(1)`), and at about 0.62 s per block that rule bites at every
|
|
head:
|
|
|
|
- a node whose clock is **behind** rejects the head blocks it is sent and disconnects the peer with
|
|
`BREACH_OF_PROTOCOL`, which reads like an attack and is a clock;
|
|
- a validator whose clock is **ahead** proposes blocks the others reject, and loses its slot.
|
|
|
|
What to do: run NTP or chrony against good sources, and **measure against the chain head, not against a
|
|
flag**: a host that reports `NTPSynchronized=yes` was measured one full second behind on 2026-09-12.
|
|
The kit measures it for you, in the same terms the Foundation measures its own fleet (head timestamp
|
|
minus local clock, at most 1 s behind and at most 2 s ahead):
|
|
|
|
```bash
|
|
validator/validator-candidacy.sh --clock-only --params admission-params.json --enode-host <your host>
|
|
```
|
|
|
|
On the day of the vote the Foundation asks for exactly this report, fresh (at most six hours old). It is
|
|
signed by your node key and holds nothing private.
|
|
|
|
---
|
|
|
|
## 4. Add peers and finish syncing
|
|
|
|
Until you have the Foundation's peer list, the node has nothing to connect to.
|
|
On application you receive:
|
|
|
|
- a **bootnode enode** string, and
|
|
- a **`static-nodes.json`** file (belt-and-braces; Besu reads it from inside the
|
|
data directory).
|
|
|
|
Wire them in:
|
|
|
|
1. Put `static-nodes.json` next to the compose file. It is bind-mounted to
|
|
`data/static-nodes.json` by the compose (uncomment that volume line if you
|
|
commented it out).
|
|
2. Replace `BOOTNODE_ENODE_PROVIDED_ON_APPLICATION` in `docker-compose.yml`
|
|
(or `config.toml`) with the real enode string.
|
|
3. Restart:
|
|
|
|
```bash
|
|
docker compose up -d
|
|
```
|
|
|
|
Watch it find peers and import blocks:
|
|
|
|
```bash
|
|
docker compose logs -f aere-node
|
|
```
|
|
|
|
### The two traps that stop a fresh node cold
|
|
|
|
- **The 5-peer wait.** Besu will not begin BFT block import until it has enough
|
|
peers, and it defaults to needing **5**. A small network never reaches 5 and
|
|
the node waits forever, never importing block 1. The compose already sets
|
|
**`--sync-min-peers=1`** to fix this. Keep it.
|
|
- **Gas price.** AERE nodes run **`--min-gas-price=0`**: the protocol's base-fee floor
|
|
(from block 10,141,734) sets the price of a transaction, not the node. Do not set a
|
|
nonzero min gas price of your own; you would drop normal AERE transactions. The
|
|
compose already sets it to 0.
|
|
|
|
You do not need the public RPC fee-shim proxy. That proxy exists only for the
|
|
public wallet-facing RPC endpoint; a syncing full node talks to the chain
|
|
directly.
|
|
|
|
---
|
|
|
|
## 5. Confirm the node is healthy
|
|
|
|
```bash
|
|
# Current block height (should be climbing, then tracking the public head):
|
|
curl -s -X POST http://127.0.0.1:8545 -H 'Content-Type: application/json' \
|
|
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
|
|
|
|
# Peer count (should be >= 1, ideally several):
|
|
curl -s -X POST http://127.0.0.1:8545 -H 'Content-Type: application/json' \
|
|
--data '{"jsonrpc":"2.0","method":"net_peerCount","params":[],"id":1}'
|
|
|
|
# The current QBFT validator set (you will NOT be in it yet):
|
|
curl -s -X POST http://127.0.0.1:8545 -H 'Content-Type: application/json' \
|
|
--data '{"jsonrpc":"2.0","method":"qbft_getValidatorsByBlockNumber","params":["latest"],"id":1}'
|
|
```
|
|
|
|
Compare your height against the public head at `https://aere.network/validators`
|
|
(the live validator-state dashboard). When your `eth_blockNumber` is tracking the
|
|
public head and you have steady peers, your node is ready to apply.
|
|
|
|
---
|
|
|
|
## 6. Admission (the governance vote)
|
|
|
|
This is the honest part: **admission is a decision of the existing validators,
|
|
not a transaction you can send yourself.** Here is the whole path, in order.
|
|
|
|
1. **Observation.** Your following node runs for fourteen days while the Foundation measures it from
|
|
outside (that it follows the head, with the same block hashes as the fleet). You tell the Foundation
|
|
the read endpoint it may use for that.
|
|
2. **Admission parameters.** The Foundation sends `admission-params.json`: chain id 2800, the height of
|
|
the registry epoch that will carry your row, its size, and your row index. Public data.
|
|
3. **Your keys and your candidacy, on your machine.** `validator/validator-keys.sh` (section 3), then:
|
|
|
|
```bash
|
|
validator/validator-candidacy.sh --params admission-params.json --enode-host <your public P2P host or IP> \
|
|
--data-path <your node's data directory>
|
|
```
|
|
|
|
With your node running, the script checks that the node runs with exactly the key in your data
|
|
directory (the enode it reports must match), measures your clock against the chain head (section
|
|
3b), writes your registry row with `PqRegistryV3Tool row` from your build (your post-quantum id,
|
|
both public keys, a proof of possession by each, the link signed by your node key, and your node
|
|
key's address as the transport address), signs the candidacy message with all three keys, verifies
|
|
its own signatures, and checks that no private value appears in anything it wrote. Send the
|
|
resulting `aere-candidacy.json`. It holds public data only.
|
|
4. **Independent verification.** The Foundation verifies the candidacy with an implementation that
|
|
shares no code with the client (every signature, the proofs of possession, that your id is derived
|
|
from your Falcon key, that the enode's key is the row's address, that the date and the clock are
|
|
fresh and in margin), and refuses it otherwise, with the reason.
|
|
5. **The new registry epoch.** The existing validators re-sign their own rows for the new epoch (the same
|
|
keys; a proof of possession is bound to the epoch's height and size), your row is added as the last
|
|
one, and the epoch is checked by two independent loaders. The epoch is **published** at
|
|
`https://aere.network/pq/manifests/` in the same hour it takes effect, and every node, yours
|
|
included, appends its file and pin (section 2).
|
|
6. **Your node becomes a validator.** You restart it with the validator properties of section 7b and the
|
|
new epoch, connected to every validator (with the peer list the Foundation gives you, as in section 4),
|
|
and you send a fresh clock report (section 3b).
|
|
7. **The vote.** A majority of the current validators propose your post-quantum id with
|
|
`qbft_proposeValidatorVote(<your id>, true)`. Since block 20,836,228 (AIP-22 stage 3) the set and the
|
|
votes are on post-quantum ids, and a vote for an id that has no row in the registry in force is
|
|
invalid: that is why the epoch comes first. The vote is proposed only after every check of the steps
|
|
above passes, including that your node is connected to all validators.
|
|
8. **After the vote.** Your already-running, already-synced node detects that it is now in the set and
|
|
begins proposing and sealing blocks. The Foundation checks, from the chain and from its own nodes,
|
|
that you propose in turn, that your post-quantum commit seal is heard and verified by every node,
|
|
that your index appears in the anchor certificates, and that the chain's rhythm did not change.
|
|
|
|
The Foundation decides whether an admission grows the set or whether a Foundation validator steps out
|
|
after you are in; for you the steps are the same.
|
|
|
|
**Removal** is the same mechanism in reverse:
|
|
`qbft_proposeValidatorVote(<id>, false)` by a majority. This is how a
|
|
misbehaving or long-offline validator is dropped. There is no slashing and no
|
|
stake to lose; the consequence is removal from the set.
|
|
|
|
You can always read the live set yourself with
|
|
`qbft_getValidatorsByBlockNumber` (see step 5), so admission and removal are
|
|
publicly verifiable on-chain.
|
|
|
|
### The candidacy file
|
|
|
|
`aere-candidacy.json` (format `aere-validator-candidacy/1`) carries: `chainId`, `bindHeight`, `count`,
|
|
`index`, `issued` (UTC date), `enode`, `address`, `id`, `row` (the registry v3 row, AIP-22 section 3.2),
|
|
`rowHash`, `proof` and `clock`. The two signed messages, so that anyone can verify a candidacy without
|
|
our tools (integers big-endian, `i32` two's complement, `||` concatenation):
|
|
|
|
```
|
|
candidacy = keccak256("AERE-CANDIDACY-1" || u64 chainId || u64 bindHeight || u32 count || u32 index
|
|
|| u32 len(issued) || issued || id (20 bytes) || rowHash (32 bytes))
|
|
clock = keccak256("AERE-CLOCK-1" || u64 chainId || u64 measuredAt || u64 headNumber || u32 samples
|
|
|| i32 dMin || i32 dMax)
|
|
```
|
|
|
|
`proof.secp256k1` (65 bytes, r || s || v) must recover to the row's `ecdsa.addr`, which must equal its
|
|
`transport` address and the address of the enode's public key; `proof.falcon-512` and
|
|
`proof.slh-dsa-sha2-128s` must verify under the row's two keys; `clock.secp256k1` must recover to the
|
|
same address. `dMin` and `dMax` are the smallest and largest "head timestamp minus local clock" over the
|
|
samples; the margin is `dMax <= 1` and `dMin >= -2`.
|
|
|
|
---
|
|
|
|
## 7. How validators are paid
|
|
|
|
Rewards are transparent and on-chain. **Measured 2026-10-15:** the beneficiary of
|
|
every block in a 40-block sample is the proposing validator's own identity address
|
|
(ten addresses, none with code), and blocks carry almost no transactions. The split
|
|
below is the design of the **AereCoinbaseSplitterV2** contract
|
|
(`0x8C1A48eFA57b66fEE743A00E3899c29ad3Fd27b4`); block fees do not pass through it
|
|
today (its balance was 0 that day), so read it as a deployed mechanism, not a
|
|
present-tense flow. It does a fixed basis-points split:
|
|
|
|
- **Burn:** 3750 bps (**37.5%**) to the fee-burn vault.
|
|
- **Sink:** 1500 bps (**15.0%**) routed through AereSink (the sAERE flywheel).
|
|
- **Rebate to the validator:** the remainder, **47.5%** at the defaults.
|
|
|
|
These are the current default splits. They are owner-adjustable within hard
|
|
caps written into the contract (burn at most 5000 bps, sink at most 3000 bps),
|
|
and any change to the sink destination is time-locked for 7 days. If the sink
|
|
is unset, its share is added to the validator rebate instead. Everything is
|
|
readable on-chain; nothing here is off-book.
|
|
|
|
There is no staking yield, because there is no staking. A delegation UI tied to
|
|
the sAERE flywheel is on the roadmap but is not live today; do not assume any
|
|
delegation mechanics until the Foundation announces them.
|
|
|
|
---
|
|
|
|
## 7b. If you are being admitted as a validator: the validator properties
|
|
|
|
Everything above gets you a **following node**, and that is the normal case until the vote. A
|
|
validator passes, through `BESU_OPTS`, the whole line of `docker-compose.yml` (it is derived from a
|
|
running validator of the fleet and carries the validators' own seal cap) plus the properties below.
|
|
The Foundation confirms the exact values when your admission is scheduled.
|
|
|
|
| property | what it is |
|
|
|---|---|
|
|
| `aere.falcon.key` | the path of YOUR Falcon-512 key file, written by `validator-keys.sh` |
|
|
| `aere.pq.hybrid.key.slh-dsa-sha2-128s` | the path of YOUR SLH-DSA-SHA2-128s key file, written by `validator-keys.sh` |
|
|
| `aere.pq.registryV3`, `aere.pq.registryV3.hashes` | every registry epoch and its pin, including the one that carries your row (section 2) |
|
|
| `aere.pq.hybrid.attachBlock`, `aere.pq.proposalPq.attachBlock`, `aere.pq.preparePq.attachBlock`, `aere.pq.roundChangePq.attachBlock` | the heights from which the node EMITS post-quantum seals; already in `docker-compose.yml` |
|
|
|
|
Two things a validator must NOT carry: `aere.pq.anchor.minSealsCeiling` (a node syncing from genesis
|
|
with `run-node.sh` sets it to 1 to start inside the small genesis set; a validator starts into the full
|
|
set and must judge anchors at the scheduled threshold), and a node key from anywhere else than its data
|
|
directory (section 3). From block 20,836,228 the key a node signs with, and the index it signs under, are
|
|
looked up in the registry epoch in force by the PUBLIC key, and the client refuses to start a validator
|
|
registered in an epoch whose row does not match the key it holds (`AERE-PQC-KEY-NEXT-02`).
|
|
|
|
**One path not yet measured, stated so you do not lose an evening to it.** A node that synced as a
|
|
follower and restarts as a validator passes the client's attach-height guard when it finds a recent
|
|
Falcon-sealed header; otherwise it refuses to start with `AERE-PQC-CFG-UNSAFE-05`, and the recovery
|
|
is `aere.falcon.attachBlockAcknowledgePast=<attachBlock>` for one restart. Both behaviours were measured
|
|
on 2026-08-12 on the fleet of that time; for a node joining after block 20,836,228 they have not been
|
|
measured yet (the Foundation measures them on its rehearsal network before the first admission). If you
|
|
hit that refusal, send the log line; do not work around it alone: an attachment height that disagrees
|
|
with the fleet is a liveness incident, not a local setting.
|
|
|
|
**One more measured fact, because it decides how you schedule your upgrades.** If
|
|
your node is a validator and it is still on a build without a rule the fleet has armed, it does
|
|
not fork and it does not accept anything wrong. But every time it is proposer at
|
|
an anchor height, the upgraded majority rejects its block and the round is lost.
|
|
Measured with two of seven nodes un-upgraded (2026-08): the chain head paused for about
|
|
twenty seconds across a hundred blocks. Being behind costs the network rhythm, not
|
|
safety.
|
|
|
|
### 7c. Post-quantum seals on consensus messages (live since September 2026)
|
|
|
|
Every 32nd block carries a post-quantum certificate under its hash. The blocks between anchors carry
|
|
no Falcon seal of their own, and their validity at import rests on ECDSA, so the measured depth a
|
|
fabricated segment could reach before an anchor stops it is **31 consecutive blocks**.
|
|
|
|
Beyond the anchors, each of the four QBFT messages (proposal, prepare, commit, round-change) carries a
|
|
Falcon-512 seal of its author, each under its own domain. On chain 2800 the nine Besu-derived
|
|
validators refuse a message without a valid seal: COMMIT from block 17,250,000, PROPOSAL from
|
|
17,400,000, ROUND-CHANGE from 17,550,000 and PREPARE from 17,700,000. The tenth validator, which runs
|
|
the second client, refuses a COMMIT without one from block 18,131,633 and applies the other three
|
|
types since block 24,300,000 (AIP-22 stage 4, 2026-10-25). Each message has two switches, passed like
|
|
the anchor properties:
|
|
|
|
| property | what it does |
|
|
|---|---|
|
|
| `aere.pq.<message>Pq.attachBlock` | height from which **this node emits** a seal on that message |
|
|
| `aere.pq.<message>Pq.forkBlock` | height from which **this node refuses** that message without a valid seal |
|
|
|
|
where `<message>` is one of `prepare`, `proposal`, `roundChange` (commit uses
|
|
`aere.pq.commitPq.forkBlock`).
|
|
|
|
**Two things worth knowing, both measured:**
|
|
|
|
- **Emission and enforcement are separate on purpose.** The fleet emitted for a while before
|
|
anything refused. If they were one switch, the first node to start emitting would also start
|
|
refusing, and that is a flag day.
|
|
- **A mistyped value refuses to start, it does not quietly disarm.** If you set one of these to
|
|
something that is not a plain block height, the client stops with a configuration error: a node
|
|
that boots disarmed because of a stray character looks exactly like a correctly configured one,
|
|
right up to the day it matters.
|
|
|
|
**What this asks of you as a validator:** your post-quantum keys (Falcon-512, and SLH-DSA for the
|
|
hybrid anchor certificate), generated by you on your machine and carried in the registry epoch that
|
|
admits you, and the same emission and enforcement heights as the fleet, which the Foundation gives you.
|
|
A validator whose seals are refused does not fork and does not accept anything wrong: it loses its
|
|
turn as proposer, and the chain continues without it as long as a quorum of the others can seal.
|
|
|
|
**What we do not claim.** Since block 24,300,000 (AIP-22 stage 4) every validator attributes a consensus vote only through its verified post-quantum seal, but headers still carry ECDSA commit seals until AIP-22 stage 5, so AERE is
|
|
not a "fully post-quantum consensus" and we do not describe it as one. What you can verify yourself,
|
|
without taking our word for it, is the anchor certificate at every 32nd block, with the public
|
|
verifier.
|
|
|
|
---
|
|
|
|
## 8. Day-two operations
|
|
|
|
- **Updates:** the live network runs a pinned build of the **Aere fork** of Besu,
|
|
not stock upstream. Every new fleet build is published as a patch in `aere-node` before it is
|
|
required, with the height from which it is needed; rebuild with `./run-node.sh --no-start --rebuild`
|
|
and restart before that height. Do not move to `:latest` on your own.
|
|
- **Restarts:** tell the Foundation before a planned restart. A restarted validator starts with an empty
|
|
post-quantum seal store, and two validator restarts within minutes of each other can stall an anchor
|
|
round. A node restarting with its `data/` intact keeps its identity and does not re-hit the 5-peer
|
|
trap.
|
|
- **Emergencies:** the emergency settings of the anchor rules are applied uniformly to every node, never
|
|
to one; when the Foundation announces one, it applies to your node too, at the same time.
|
|
- **Backups:** back up `data/key` and the two post-quantum key files. Those are the irreplaceable files.
|
|
- **Monitoring:** scrape Prometheus metrics on `127.0.0.1:9545`. Track block
|
|
height versus the public head, peer count, disk free space (Bonsai growth) and the clock against the
|
|
chain head (section 3b).
|
|
- **Disk:** watch free space. At sub-second blocks storage grows steadily; plan
|
|
for a growable NVMe volume.
|
|
|
|
---
|
|
|
|
## Quick reference
|
|
|
|
| Item | Value |
|
|
| --- | --- |
|
|
| Network name | AERE Network |
|
|
| Chain ID / network ID | 2800 |
|
|
| Consensus | Hyperledger Besu, QBFT |
|
|
| Block time | sub-second since block 2,137,652 (0.62 s average over the 100,000 blocks before 2026-10-15) |
|
|
| Gas price | min-gas-price 0 on the node; the protocol base-fee floor sets the price |
|
|
| Storage format | Bonsai |
|
|
| Client | **Aere fork of Besu**, built from the public patch series of the `aere-node` bundle (0001 to 0010 as of 2026-10-14) on the named upstream commit. Stock upstream cannot follow chain 2800 past block 9,189,161. See RUN-A-NODE.md in `aere-node`. |
|
|
| Required JVM flags | the full `BESU_OPTS` line in `docker-compose.yml` (base-fee floor, post-quantum anchor, registry epochs, post-quantum identities, post-quantum transactions); a node missing one stops at the first height that needs it |
|
|
| Validator keys | node key `data/key` (also the consensus key), Falcon-512 and SLH-DSA-SHA2-128s; all generated by the operator (`validator/validator-keys.sh` in `aere-node`) |
|
|
| Candidacy | `validator/validator-candidacy.sh` in `aere-node`: registry row, enode, proof of possession, signed clock report |
|
|
| Clock | at most 1 s behind and 2 s ahead of the chain head, measured against the head |
|
|
| JVM heap | `-Xmx4g` |
|
|
| P2P port | 30303 tcp + udp (public) |
|
|
| RPC port | 8545 (keep private; on a validator, localhost only) |
|
|
| Metrics port | 9545 (keep private) |
|
|
| Active validators today | 10 (Foundation), as read from the chain on 2026-10-15 |
|
|
| Fault tolerance | f = 3, quorum 7-of-10 (as read from the chain at 2026-09-29 07:55 UTC) |
|
|
| Public path | 10 to 21 |
|
|
| Staking / bond / slashing | None |
|
|
| Admission | Governance vote of existing validators, after the registry epoch that carries your row |
|
|
| Reward contract | AereCoinbaseSplitterV2 `0x8C1A48eFA57b66fEE743A00E3899c29ad3Fd27b4` |
|
|
| Genesis hash | Provided by the Foundation on application |
|
|
| Bootnode enode | Provided by the Foundation on application |
|
|
| `static-nodes.json` | Provided by the Foundation on application |
|
|
|
|
Live consensus explainer: `https://aere.network/consensus`
|
|
Live validator state: `https://aere.network/validators`
|