# Run a Bremo node

Bremo is only as trustworthy as it is inspectable. This guide is for anyone who
wants to run their own Bremo node instead of taking our word for anything. It is
written to be reproducible: every value below is a real value from this
repository, not a placeholder.

There are three levels, from easiest to most involved:

1. **Verify from genesis (no permission, works today).** Build the chain from the
   open genesis file on your own machine and confirm the rules for yourself.
2. **Query the live chain (works today).** Point at the public RPC and read the
   live state directly.
3. **Peer as a full node, and become an independent validator (in progress).**
   This is how Bremo decentralizes. The honest status of this is stated plainly
   at the bottom, because it is not finished.

Do not trust these steps. Run them. If a result does not match what is written
here, that is a bug or a lie and you should say so publicly.

## What you need

- `geth` version **1.13.15-stable** (git commit `c5ba367`). This is the pinned
  version in `bin/geth-1.13`; it is the last geth line with Clique proof-of-
  authority sealing, which Bremo uses. Any 1.13.x with Clique will interoperate.
- This repository, for `chain/genesis.json`.
- About 100 MB of disk for a young chain.

Confirm your geth version:

    geth version
    # Version: 1.13.15-stable
    # Git Commit: c5ba367eb6232e3eddd7d6226bfd374449c63164

## Level 1: verify from genesis on your own machine

This proves the *rules* of the chain: chain ID 48555, Clique PoA with a 5 second
period, no block reward, and the entire 21,000,000 supply allocated once in the
genesis block. It does not require reaching our servers at all.

Initialize a data directory from the open genesis file:

    geth init --datadir ~/bremo-verify chain/genesis.json

geth prints the genesis hash it computed. On this repository's genesis it is
deterministic and is exactly:

    Successfully wrote genesis state   hash=9e8fd3..a3a1b4

The full hash is
`0x9e8fd355d206271d30bce81b5afa36491c6ad6273ac0fcabd1a0142498a3a1b4`.

This one value is the whole proof. The genesis hash is a cryptographic
commitment to every rule and every balance in the genesis block: the chain ID,
the Clique 5 second period, the zero block reward, and the single 21,000,000
allocation. If even one byte of the allocation or config differed, the hash
would differ. So confirm your locally computed hash equals the genesis hash the
**live chain** reports for block 0. Set `RPC` to the current public endpoint
first (`RPC=$(curl -s https://bremo.tech/rpc.json | python3 -c 'import sys,json;print(json.load(sys.stdin)["rpc"])')`, explained in Level 2), then:

    curl -s -X POST -H "Content-Type: application/json" \
      --data '{"jsonrpc":"2.0","method":"eth_getBlockByNumber","params":["0x0",false],"id":1}' "$RPC" \
      | python3 -c 'import sys,json;print(json.load(sys.stdin)["result"]["hash"])'
    # 0x9e8fd355d206271d30bce81b5afa36491c6ad6273ac0fcabd1a0142498a3a1b4

If the two hashes match, your node and the live network started from the exact
same genesis, and you are validating the same chain. If they do not match, the
networks are different and any "same coin" claim is false. You can also read the
allocation in plain text: open `chain/genesis.json` and confirm the `alloc`
block funds one address with `21000000000000000000000000` wei (21,000,000 BREMO)
and nothing else. That single allocation is the only creation event the chain
will ever have, because Clique pays sealers no block reward (see `docs/VERIFY.md`
section 2).

## Level 2: query the live chain

The live sovereign chain answers over a public RPC endpoint. Because that
endpoint is currently exposed through an ephemeral tunnel, **the URL can change
between sessions.** Never hard-code it. Always read the current endpoint from the
machine-readable pointer the site publishes:

    curl -s https://bremo.tech/rpc.json
    # {"rpc":"https://<current-tunnel-host>","chainId":48555,"updated":"..."}

Then talk to whatever URL that returns. Confirm the chain ID is 48555 (0xbdab):

    RPC=$(curl -s https://bremo.tech/rpc.json | python3 -c 'import sys,json;print(json.load(sys.stdin)["rpc"])')
    curl -s -X POST -H "Content-Type: application/json" \
      --data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' "$RPC"

Confirm blocks are advancing (call it twice a few seconds apart; the number must
increase, roughly one every 5 seconds):

    curl -s -X POST -H "Content-Type: application/json" \
      --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' "$RPC"

Read the live treasury balance on this chain:

    curl -s -X POST -H "Content-Type: application/json" \
      --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0x304C0F278a46Cd09720655851fE05569a128F704","latest"],"id":1}' "$RPC"

**Do not compare that number to the transparency page. They are two different
ledgers and they are supposed to differ.** This is the single most likely place
for an honest reader of this guide to conclude Bremo is lying when it is not, so
read this before you draw a conclusion from a mismatch.

`BremoToken` mints a fixed 21,000,000 in its constructor on whatever chain it is
deployed to, so the sovereign chain (chain ID 48555) has its own 21,000,000 and
the Ethereum mainnet instance has its own 21,000,000. Two separate ledgers, as
spelled out in [VERIFY.md](VERIFY.md) and [VERIFY-TRANSPARENCY.md](VERIFY-TRANSPARENCY.md).
The balance you just read is the *native* treasury on chain 48555, at
`0x304C0F278a46Cd09720655851fE05569a128F704`. The transparency page and
`/holders.json` report something else entirely: the *Ethereum ERC-20* treasury,
at `0x2410A309d09b5887D281921aBc5316ff84A0D121`, which is the address every
distributed BREMO has actually been sent from. Gifting happened on Ethereum, not
on 48555, so the two balances have drifted apart by roughly the amount
distributed, and they will keep drifting.

Checked on 2026-07-24 over the published pointer, the two read:

| Ledger | Treasury address | Balance | How to check |
|---|---|---|---|
| Sovereign chain 48555 (native) | `0x304C0F27...8F704` | 20,999,999.9967 | the `eth_getBalance` call above, against `$RPC` |
| Ethereum mainnet (ERC-20) | `0x2410A309...A0D121` | 20,550,200 | `balanceOf` on the token, see [VERIFY-TRANSPARENCY.md](VERIFY-TRANSPARENCY.md) |

Both numbers are real and both are checkable. Neither is the other's total. Do
not add them, do not net them, and do not treat either as a single cross-chain
"21,000,000 total" until that reconciliation is decided and published. Whether
one chain is canonical and the other deprecated is an open, disclosed design
question, not something this guide is entitled to settle for you. To verify what
people actually hold today, use [VERIFY-TRANSPARENCY.md](VERIFY-TRANSPARENCY.md)
against Ethereum mainnet, not this endpoint.

The strongest possible verification never depends on our endpoint at all: weight
"I ran the node myself" above "I queried their URL." A permanent, named public
RPC and a hosted block explorer are roadmap items; until they ship, the pointer
above and your own node are the source of truth. The live explorer at
https://bremo.tech/network reads exactly this pointer and queries the chain in
your browser, so you can watch the same numbers update there.

## Level 3: peer as a full node and add validators

This is how Bremo stops being centralized. It is the honest, unfinished part.

A full node that *syncs the live chain over peer-to-peer* (rather than just
importing genesis) needs at least one reachable peer. The network's bootnode
enode is published in the machine-readable status file:

    curl -s https://bremo.tech/status.json | python3 -c 'import sys,json;print(json.load(sys.stdin)["bootnode"])'

**Read the honest caveat that ships next to it.** As of this writing the same
status file marks the bootnode "reachable once the public validator host is
live." Until that host is up, outside machines cannot yet complete a p2p sync of
the live chain, and Level 1 (verify from genesis) plus Level 2 (query the RPC)
are the two paths that work end to end today. Standing up that public validator
host is an owner action on the roadmap, tracked on the live gate board at
https://bremo.tech/roadmap and in `status.json` under `gates`.

When the public host is live, a non-mining full node looks like this. It reuses
the same real values as the validator (`chain/scripts/run-node.sh`) but does not
seal and does not need a signer key:

    geth init --datadir ~/bremo-node chain/genesis.json
    geth \
      --datadir ~/bremo-node \
      --networkid 48555 \
      --syncmode full \
      --port 30388 \
      --bootnodes "<enode from status.json>" \
      --http --http.addr 127.0.0.1 --http.port 8545 \
      --http.api eth,net,web3,clique

No `--mine`, no `--unlock`, no keys. This node independently checks every block
against the same rules and lets you serve your own RPC, so you never have to
trust ours.

### Becoming an independent validator

Bremo's consensus (Clique PoA) adds and removes signers by on-chain vote. A new
validator is added when a majority of existing signers each call
`clique.propose(address, true)` for the candidate. So a second validator requires
two real things:

1. An independent party running a full node on a separate machine, reachable on
   the network (Level 3 above).
2. The current signer(s) voting that party in with `clique.propose`.

Both are deliberate, non-secret steps. Bremo will only be honestly described as
decentralized once independent parties, not the founder, run validators on their
own infrastructure. Adding a signer you do not control is a one-way increase in
credible neutrality, which is the entire point of the project.

### Prove the validator set for yourself

The claim "Bremo has exactly one validator today" should not be taken on trust.
There are two independent ways to confirm it, and they must agree.

First, offline, from the open genesis file, with no network at all. In Clique the
signer set at genesis is committed inside `extraData`: 32 vanity bytes, then each
signer's 20-byte address, then a 65-byte seal. Decode it:

    python3 - <<'PY'
    import json
    e = json.load(open("chain/genesis.json"))["extradata"][2:]
    mid = e[64:-130]            # strip 32-byte vanity and 65-byte seal
    signers = [ "0x"+mid[i:i+40] for i in range(0, len(mid), 40) ]
    print(len(signers), "signer(s):", signers)
    PY
    # 1 signer(s): ['0xC62E793af02a2d5000476507Fc88D8501CF6F77E']

Because the genesis hash (verified in Level 1) is a cryptographic commitment to
every byte of `extraData`, this is not just a text file assertion: any other
signer set would produce a different genesis hash than the live chain reports for
block 0.

Second, against the live chain, through the public RPC. The read-only Clique
method `clique_getSigners` is allowed on the public endpoint:

    RPC=$(curl -s https://bremo.tech/rpc.json | python3 -c 'import sys,json;print(json.load(sys.stdin)["rpc"])')
    curl -s -X POST -H "Content-Type: application/json" \
      --data '{"jsonrpc":"2.0","method":"clique_getSigners","params":["latest"],"id":1}' "$RPC"
    # {"result":["0xc62e793af02a2d5000476507fc88d8501cf6f77e"]}

Both answers are the same single address, and they match `chain/validator.addr`
and the "validators 1/3" figure on the gate board. The signer-changing method
`clique_propose` is deliberately blocked on the public endpoint, so reading the
validator set never lets anyone alter it. When a second, independent signer is
voted in, this same query will return two addresses, and the gate board will move
from 1 to 2 on its own. That is the honest signal to watch for real
decentralization, not a press release.

## Honest decentralization status

- Validators today: **1** (the founder's). Target for launch: **3 independent**.
  This is disclosed, not hidden.
- P2P peering for outside full nodes: **pending the public validator host**
  (owner/roadmap gate). Genesis verification and RPC queries work today.
  The chain itself is confirmed syncable: on 2026-07-16 a second full node,
  initialised from this genesis file and given the validator as its only peer,
  synced 0 to 84,130 blocks in about 10 minutes and independently agreed on the
  genesis hash, the block hashes, the signer set, and the treasury balance. The
  blocker is that the validator advertises `127.0.0.1` with discovery off, so
  outside machines cannot dial it yet. See `docs/VALIDATOR-2.md` for the measured
  result and the runbook for adding validator two.
- The live count and the launch gates are on the public board at
  https://bremo.tech/roadmap and in https://bremo.tech/status.json.

A single-validator chain is centralized and must be described that way until the
board says otherwise. If you run a node from this guide and want to become one of
the independent validators, that is the most useful contribution you can make.
See `CONTRIBUTING.md`.
