montana/Node
Montana Moscow Node 33daa755d1 Montana/Node/bin/montana-node: публичный бинарь для join.sh
Linux x86_64 ELF, 9138360 bytes.
Любой может скачать через wget/curl и поднять p2p-узел Montana.
2026-05-15 16:57:08 +03:00
..
bin Montana/Node/bin/montana-node: публичный бинарь для join.sh 2026-05-15 16:57:08 +03:00
genesis-manifest.json Montana/Node/: инструкция развёртывания валидаторского узла 2026-05-14 20:45:14 +03:00
join.sh Montana/Node/join.sh + orchestrator: auto-registration новых узлов 2026-05-15 16:47:16 +03:00
montana-node.service Montana/Node/: инструкция развёртывания валидаторского узла 2026-05-14 20:45:14 +03:00
README.md Montana/Node/join.sh + orchestrator: auto-registration новых узлов 2026-05-15 16:47:16 +03:00

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. Подготовка сервера

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):

# на 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 одинаков на всех узлах сети — берём с любого существующего.

scp montana-moscow:/etc/montana/genesis-manifest.json /tmp/
scp /tmp/genesis-manifest.json <new-server>:/etc/montana/genesis-manifest.json

4. Сгенерировать identity нового узла

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. Открыть порты

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 из этой папки:

scp Montana/Node/montana-node.service <new-server>:/etc/systemd/system/
ssh <new-server> "systemctl daemon-reload && systemctl enable --now montana-node"

7. Проверка

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

# например: <alias>.montana.quest → <new-ip>, DNS-only (не proxied, Reality не работает за CF)

9.2. Добавить алиас на Moscow (orchestrator)

# /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 на каждом узле

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).

Удаление узла

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 шагов — запусти автоматический скрипт:

# на новом сервере (под 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)