# 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 :/usr/local/bin/montana-node ssh "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 :/etc/montana/genesis-manifest.json ``` ### 4. Сгенерировать identity нового узла ```bash ssh "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 :/etc/systemd/system/ ssh "systemctl daemon-reload && systemctl enable --now montana-node" ``` ### 7. Проверка ```bash ssh "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 # например: .montana.quest → , DNS-only (не proxied, Reality не работает за CF) ``` #### 9.2. Добавить алиас на Moscow (orchestrator) ```bash # /root/.ssh/config на Moscow: Host montana--stats HostName .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` — добавить `[]="..."` в SRC/LABEL/REGION/COUNTRY/HOSTING + `for NODE in msk fra fin us ;` - `/usr/local/bin/montana-cities-build` — добавить `: {label, country, hosting, coords, role, peers}` в CITIES #### 9.4. Добавить в `peer-health.py` на каждом узле ```python PEERS = [ ... {"name": "", "ip": ".montana.quest", "country": ""}, ] ``` Распространить через 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 в публичных файлах запрещены** — везде использовать `.montana.quest` через CF DNS-only (см. `feedback_no_ip_in_public.md`). ## Удаление узла ```bash ssh "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 :/usr/local/bin/montana-node /usr/local/bin/ scp :/etc/montana/genesis-manifest.json /etc/montana/ ALIAS=amsterdam \ LABEL=Amsterdam \ COUNTRY=NL \ HOSTING=DigitalOcean \ COORDS="52.37,4.89" \ 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":}` - `GET https://montana.quest/vpn/node/nodes` → список всех известных узлов сети - `POST https://montana.quest/vpn/node/register` → регистрация нового узла (требует `secret` в body)