montana/Node/join.sh
Montana Moscow Node f94a2b5873 Montana/Node/join.sh: полная децентрализация admission
По спеке Montana Protocol v35.25.0 и Montana Mesh whitepaper §2:
admission через VDF + Selection event, без admin-токенов.

Изменения:
- удалён TOKEN-gate из join.sh и orchestrator
- единый endpoint POST /vpn/node/join (без secret)
- orchestrator проверяет TCP :8444 + NodeTable membership
- если узел Active в NodeTable → сразу в Helsinki cascade VPN
- иначе candidate (видим на /net/), promote-таймер каждые 5 минут

Узел поднимает оба слоя одновременно (whitepaper §2):
montana-node :8444 + xray :443 Reality.

После прохождения CandidateVdf (~10ч) → Selection event →
Active + 13 Ɉ welcome-bonus → auto-promotion в cascade.
2026-05-15 18:49:33 +03:00

152 lines
7.7 KiB
Bash
Executable File
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.

#!/bin/bash
# Montana Node — полностью децентрализованное присоединение к сети.
# По спеке Montana Protocol v35.25.0 — admission через VDF + Selection event,
# без admin-токенов и человеческих gate-keepers.
#
# Одна команда (любой человек, никакого TOKEN):
# ALIAS=cologne LABEL=Köln COUNTRY=DE HOSTING=Hetzner COORDS="50.94,6.96" \
# bash <(curl -sk https://hub.montana.quest/efir369999/montana/raw/branch/main/Node/join.sh)
#
# Поднимает оба слоя сразу (Montana Mesh whitepaper §2: оператор обязан запустить оба):
# - montana-node :8444 — p2p TimeChain, self-sovereign identity, VDF kandidatura
# - xray :443 — VPN backend (Reality+Vision), готов к включению в cascade
#
# Жизненный цикл узла:
# 1. join.sh поднимает оба слоя на новом сервере
# 2. POST /vpn/node/join → orchestrator проверяет TCP :8444 + NodeTable membership
# 3. Если узел уже в NodeTable Active (повторный join после VDF) → попадает в cascade сразу
# 4. Иначе — статус 'candidate' (виден на /net/ карте). Montana-node идёт CandidateVdf (~10ч).
# 5. После selection event → Active в NodeTable + 13 Ɉ welcome-bonus
# 6. Orchestrator-promote.timer (каждые 5 минут) auto-promote в cascade когда видит Active
set -euo pipefail
: "${ALIAS:?нужен ALIAS (например: cologne)}"
: "${LABEL:?нужен LABEL (например: Köln)}"
: "${COUNTRY:?нужен COUNTRY (2 буквы, например: DE)}"
: "${HOSTING:?нужен HOSTING (например: Hetzner)}"
: "${COORDS:?нужны COORDS \"lat,lon\" (например: 50.94,6.96)}"
ORCH="${ORCH:-https://montana.quest/vpn/node}"
HUB="${HUB:-https://hub.montana.quest/efir369999/montana/raw/branch/main}"
PUBLIC_IP="$(curl -s https://api.ipify.org)"
echo "==> Montana Node join: $ALIAS ($LABEL, $COUNTRY/$HOSTING) @ $PUBLIC_IP"
# 1. зависимости
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl ca-certificates openssl unzip jq
# 2. ufw — оба порта открыты (whitepaper §2: и :8444 и :443)
ufw default deny incoming >/dev/null 2>&1 || true
ufw default allow outgoing >/dev/null 2>&1 || true
ufw allow 22/tcp >/dev/null
ufw allow 8444/tcp >/dev/null
ufw allow 443/tcp >/dev/null
echo "y" | ufw enable >/dev/null
# 3. montana-node бинарь + manifest с хаба (публично)
mkdir -p /etc/montana
[ -x /usr/local/bin/montana-node ] || { curl -sL "$HUB/Node/bin/montana-node" -o /usr/local/bin/montana-node; chmod +x /usr/local/bin/montana-node; }
[ -f /etc/montana/genesis-manifest.json ] || curl -sL "$HUB/Node/genesis-manifest.json" -o /etc/montana/genesis-manifest.json
id montana &>/dev/null || useradd -r -m -d /var/lib/montana -s /usr/sbin/nologin montana
mkdir -p /var/lib/montana
chown -R montana:montana /var/lib/montana && chmod 700 /var/lib/montana
[ -f /var/lib/montana/identity.bin ] || sudo -u montana /usr/local/bin/montana-node init --data-dir /var/lib/montana
cat > /etc/systemd/system/montana-node.service <<UNIT
[Unit]
Description=Montana Local Node (M8, Proof-of-Time, VDF candidate→Active)
After=network.target
Wants=network-online.target
[Service]
Type=simple
User=montana
Group=montana
ExecStart=/usr/local/bin/montana-node start --data-dir /var/lib/montana --listen /ip4/0.0.0.0/tcp/8444 --genesis-manifest /etc/montana/genesis-manifest.json
Restart=on-failure
RestartSec=10
NoNewPrivileges=yes
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/var/lib/montana
CPUQuota=200%
LimitNOFILE=8192
[Install]
WantedBy=multi-user.target
UNIT
systemctl daemon-reload
systemctl enable --now montana-node
sleep 3
# 4. account_id для NodeTable membership check
ACCOUNT_ID="$(sudo -u montana /usr/local/bin/montana-node inspect --data-dir /var/lib/montana | awk '/account_id/ {print $3}')"
NODE_ID="$(sudo -u montana /usr/local/bin/montana-node inspect --data-dir /var/lib/montana | awk '/node_id/ {print $3}')"
echo " account_id: $ACCOUNT_ID"
echo " node_id: $NODE_ID"
# 5. xray Reality VPN backend
curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh | bash -s -- install --without-geodata 2>&1 | tail -3
mkdir -p /usr/local/etc/xray
UUID="$(xray uuid)"
KEYS="$(xray x25519)"
PRIV="$(echo "$KEYS" | awk '/PrivateKey/ {print $NF}')"
PBK="$(echo "$KEYS" | awk '/Password/ {print $NF}')"
SID="$(openssl rand -hex 8)"
cat > /usr/local/etc/xray/config.json <<XCFG
{
"log": {"loglevel":"warning","access":"/var/log/xray/access.log","error":"/var/log/xray/error.log"},
"dns": {"servers":["https+local://1.1.1.1/dns-query","1.1.1.1"],"queryStrategy":"UseIP"},
"inbounds": [{"tag":"reality-${ALIAS}-backend","listen":"0.0.0.0","port":443,"protocol":"vless",
"settings":{"clients":[{"id":"${UUID}","email":"${ALIAS}-cascade","flow":"xtls-rprx-vision"}],"decryption":"none"},
"streamSettings":{"network":"tcp","security":"reality",
"realitySettings":{"show":false,"dest":"www.googletagmanager.com:443","xver":0,
"serverNames":["www.googletagmanager.com"],"privateKey":"${PRIV}","shortIds":["${SID}"]}},
"sniffing":{"enabled":true,"destOverride":["http","tls","quic"]}}],
"outbounds":[{"tag":"direct","protocol":"freedom","settings":{"domainStrategy":"UseIP"}},
{"tag":"blocked","protocol":"blackhole"},{"tag":"dns-out","protocol":"dns"}],
"routing":{"rules":[{"type":"field","port":"53","outboundTag":"dns-out"},
{"type":"field","ip":["10.0.0.0/8","172.16.0.0/12","192.168.0.0/16","127.0.0.0/8"],"outboundTag":"blocked"},
{"type":"field","protocol":["bittorrent"],"outboundTag":"blocked"}]}
}
XCFG
xray -test -config /usr/local/etc/xray/config.json >/dev/null
systemctl enable --now xray
# 6. POST /join (без TOKEN — admission по спеке Montana)
LAT="$(echo "$COORDS" | cut -d, -f1)"
LON="$(echo "$COORDS" | cut -d, -f2)"
PAYLOAD=$(jq -nc \
--arg alias "$ALIAS" --arg ip "$PUBLIC_IP" --arg country "$COUNTRY" \
--arg hosting "$HOSTING" --arg label "$LABEL" --argjson lat "$LAT" --argjson lon "$LON" \
--arg acc "$ACCOUNT_ID" --arg nid "$NODE_ID" \
--arg pbk "$PBK" --arg uuid "$UUID" --arg sid "$SID" \
'{alias:$alias,ip:$ip,country:$country,hosting:$hosting,label:$label,coords:[$lat,$lon],account_id:$acc,node_id:$nid,reality_pbk:$pbk,reality_uuid:$uuid,reality_sid:$sid}')
RESP=$(curl -sk -X POST "$ORCH/join" -H 'Content-Type: application/json' -d "$PAYLOAD")
echo
echo "orchestrator: $RESP"
cat <<DONE
╔════════════════════════════════════════════════════════╗
║ Montana Node "$ALIAS" подключён ║
╠════════════════════════════════════════════════════════╣
║ TimeChain :8444 → CandidateVdf (~10 ч) → Active
║ xray Reality :443 → VPN backend готов
║ account_id: $ACCOUNT_ID
║ reality_pbk: $PBK
║ Уже виден на /net/ карте Montana (как candidate).
║ После прохождения VDF и Selection event автоматически
║ попадёшь в Helsinki cascade VPN balancer и начнёшь
║ обслуживать клиентский трафик. Получишь 13 Ɉ welcome-bonus.
╚════════════════════════════════════════════════════════╝
DONE