montana/Node/README.md
Montana Moscow Node 4deb04cd4e Montana/Node/join.sh + orchestrator: auto-registration новых узлов
POST /vpn/node/register на Moscow orchestrator автоматически:
- peer-health PEERS на всех узлах
- montana-net-pull, montana-cities-build
- Helsinki cascade outbound + selector

join.sh — одна команда на новом сервере.
2026-05-15 16:47:16 +03:00

205 lines
9.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Montana Node — установка валидаторского узла
Полная инструкция запуска нового p2p-узла сети Montana с присоединением к существующей сети без перегенерации genesis.
## Требования к серверу
- Ubuntu 22.04 / 24.04 (или совместимый Linux x86_64)
- Минимум 2 CPU / 2 GB RAM / 20 GB диск
- Публичный IP, открытый порт `:8444` (TCP) — M8 cross-machine
- Открытый порт `:443` (TCP) — опционально, для xray VLESS+Reality VPN-фронта/бэкенда
## Шаги установки
### 1. Подготовка сервера
```bash
apt-get update
apt-get install -y curl ca-certificates ufw
useradd -r -m -d /var/lib/montana -s /usr/sbin/nologin montana
mkdir -p /var/lib/montana && chown montana:montana /var/lib/montana && chmod 700 /var/lib/montana
mkdir -p /etc/montana
```
### 2. Скопировать `montana-node` бинарь
С любого существующего узла Montana (`montana-moscow`, `montana-frankfurt`, `montana-finland`, `montana-us`):
```bash
# на macOS:
scp montana-moscow:/usr/local/bin/montana-node /tmp/
scp /tmp/montana-node <new-server>:/usr/local/bin/montana-node
ssh <new-server> "chmod +x /usr/local/bin/montana-node"
```
### 3. Скопировать `genesis-manifest.json`
`genesis-manifest.json` определяет **bootstrap-peers** (с кем новый узел синхронизируется при первом запуске). Manifest **одинаков на всех узлах сети** — берём с любого существующего.
```bash
scp montana-moscow:/etc/montana/genesis-manifest.json /tmp/
scp /tmp/genesis-manifest.json <new-server>:/etc/montana/genesis-manifest.json
```
### 4. Сгенерировать identity нового узла
```bash
ssh <new-server> "sudo -u montana /usr/local/bin/montana-node init --data-dir /var/lib/montana"
```
Команда выведет **24 слова мнемоники****сохрани их в надёжном месте** (это единственный способ восстановить ключи узла). Также покажет:
- `account_id` (hex 64 символа)
- `node_id` (hex 64 символа)
- `master_seed_fp` (8-байтный отпечаток)
### 5. Открыть порты
```bash
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp comment 'SSH'
ufw allow 8444/tcp comment 'Montana M8 p2p'
ufw allow 443/tcp comment 'optional xray VPN' # если узел работает как VPN
echo 'y' | ufw enable
```
### 6. systemd unit
Скопировать `montana-node.service` из этой папки:
```bash
scp Montana/Node/montana-node.service <new-server>:/etc/systemd/system/
ssh <new-server> "systemctl daemon-reload && systemctl enable --now montana-node"
```
### 7. Проверка
```bash
ssh <new-server> "systemctl status montana-node; journalctl -u montana-node --no-pager -n 30"
```
В логах должно появиться:
```
[network] CONNECTION ESTABLISHED peer=12D3Koo... label=moscow
[network] CONNECTION ESTABLISHED peer=12D3Koo... label=frankfurt
[network] CONNECTION ESTABLISHED peer=12D3Koo... label=helsinki
[network] heartbeat OK peer=...
```
### 8. Lifecycle нового узла
После запуска узел проходит фазы автоматически:
1. **Bootstrap** (несколько секунд) — подключение к peer-ам manifest, дискавери.
2. **CandidateVdf** — тикает VDF (Verifiable Delay Function), накапливая `vdf_chain_length` до `τ₂ = 20160` окон (≈10 часов wall-clock на M-class CPU).
3. **Registered** — формирует NodeRegistration через `apply_noderegistrations_batch`, попадает в `CandidatePool`.
4. **Active** — на ближайшем selection-окне (`window % 336 == 0`) проходит `apply_selection_event` и появляется в `NodeTable`. С этого момента — полноправный валидатор: per-window VdfReveal + BundledConfirmation + ProposalHeader + эмиссия `13 Ɉ`.
### 9. Регистрация на карте (опционально)
Чтобы новый узел отображался на `/net/` карте `montana.quest`:
#### 9.1. Добавить DNS A-запись через Cloudflare
```bash
# например: <alias>.montana.quest → <new-ip>, DNS-only (не proxied, Reality не работает за CF)
```
#### 9.2. Добавить алиас на Moscow (orchestrator)
```bash
# /root/.ssh/config на Moscow:
Host montana-<alias>-stats
HostName <alias>.montana.quest
User root
IdentityFile /root/.ssh/montana_stats
IdentitiesOnly yes
StrictHostKeyChecking accept-new
ConnectTimeout 6
BatchMode yes
```
#### 9.3. Добавить в `montana-net-pull` arrays + `montana-cities-build` CITIES dict
Файлы:
- `/usr/local/bin/montana-net-pull` — добавить `[<id>]="..."` в SRC/LABEL/REGION/COUNTRY/HOSTING + `for NODE in msk fra fin us <id>;`
- `/usr/local/bin/montana-cities-build` — добавить `<id>: {label, country, hosting, coords, role, peers}` в CITIES
#### 9.4. Добавить в `peer-health.py` на каждом узле
```python
PEERS = [
...
{"name": "<id>", "ip": "<alias>.montana.quest", "country": "<CC>"},
]
```
Распространить через scp на все узлы и перезапустить `montana-net-aggregator.timer`.
## Архитектурные принципы
- **Никакого rebuild genesis при добавлении узла**. `genesis-manifest.json` определяет bootstrap-peers, state узла живёт в `/var/lib/montana/`.
- **Manifest одинаков на всех узлах** — peer_id хэши участвуют в M8 handshake.
- **Identity рандомная** — каждый новый узел генерирует свою (мнемоника + ML-DSA-65 ключи + ML-KEM-768 для PQ-шифрования).
- **IP в публичных файлах запрещены** — везде использовать `<alias>.montana.quest` через CF DNS-only (см. `feedback_no_ip_in_public.md`).
## Удаление узла
```bash
ssh <server> "systemctl stop montana-node && systemctl disable montana-node && rm -rf /var/lib/montana /etc/montana /etc/systemd/system/montana-node.service && systemctl daemon-reload"
```
После остановки узел перестанет получать NodeRegistration heartbeat, существующие peer-ы через `τ₂` окон уберут его из активного `NodeTable` (cooldown logic).
## Текущая сеть (на момент написания)
| ID | Город | Страна | Хостинг | Роль |
|-----|-----------|--------|--------------|---------------------|
| msk | Москва | RU | Timeweb | genesis, vpn origin |
| fra | Frankfurt | DE | Timeweb | genesis, vpn backend|
| fin | Helsinki | FI | THE.Hosting | genesis, vpn frontend|
| us | New York | US | WorkTitans | non-genesis joined 2026-05-14, vpn backend |
---
## Альтернатива: одной командой через `join.sh`
Не хочешь читать 9 шагов — запусти автоматический скрипт:
```bash
# на новом сервере (под root):
scp <existing-server>:/usr/local/bin/montana-node /usr/local/bin/
scp <existing-server>:/etc/montana/genesis-manifest.json /etc/montana/
ALIAS=amsterdam \
LABEL=Amsterdam \
COUNTRY=NL \
HOSTING=DigitalOcean \
COORDS="52.37,4.89" \
TOKEN=<orchestrator_token> \
bash <(curl -sk https://hub.montana.quest/efir369999/montana/raw/branch/main/Node/join.sh)
```
Скрипт автоматически:
1. Поставит xray + ufw
2. Сгенерирует Reality keypair, UUID, shortId
3. Создаст systemd unit для montana-node и xray
4. Откроет порты :443 и :8444
5. Сделает identity через `montana-node init`
6. Зарегистрирует узел в Moscow **orchestrator** (`POST /vpn/node/register`)
7. Orchestrator автоматически обновит на **всех** узлах:
- `peer-health.py` (PEERS)
- `montana-net-pull` (SRC/LABEL/HOSTING/COUNTRY)
- `montana-cities-build` (CITIES dict)
- Helsinki `xray/config.json` (добавит outbound + расширит cascade balancer selector)
8. Через ~30 секунд новый узел появится на `montana.quest/net/` и `/vpn/`
`TOKEN` — секрет orchestrator, лежит на Moscow в `/etc/montana-orchestrator.token`. Запроси у admin Montana.
## Orchestrator API
- `GET https://montana.quest/vpn/node/health``{"ok":true,"nodes":<count>}`
- `GET https://montana.quest/vpn/node/nodes` → список всех известных узлов сети
- `POST https://montana.quest/vpn/node/register` → регистрация нового узла (требует `secret` в body)