Automated setup scripts for running a node on the Telcoin Network. Built for MNO operators — interactive, guided, and validated at every step.
There is one node identity. Every node installs validator-capable and follows consensus from day one; staking and on-chain activation are what let it validate. The protocol decides a node's role from on-chain committee membership each epoch, not from a setup flag.
Maintainers / AI agents: see
AGENTS.mdfor the operator-vs-maintainer repo boundary — what ships to operators vs. the maintainer-onlycommon/tooling that operators never have.
| File | Purpose |
|---|---|
install.sh |
One-command installer for fresh machines |
setup-node.sh |
Full guided setup for a node (canonical installer) |
check-node.sh |
Health check for any running node |
edit-config.sh |
Edit the configuration of a running node |
firewall-setup.sh |
Interactive firewall management and hardening |
remove-node.sh |
Safely remove a node installation |
update-node.sh |
Update a running node to a newer version (source build or Docker image), with a prepare/apply two-phase workflow and one-keystroke rollback |
update-scripts.sh |
Check for and download script updates from GitHub |
setup-observability.sh |
Opt-in centralized logging + health monitoring (testnet add-on) |
setup-vpn.sh |
Opt-in WireGuard admin SSH for the Telcoin Association (testnet add-on) |
lib/common.sh |
Shared functions used by the above scripts (not run directly) |
setup-observer.shandsetup-validator.shstill exist as thin deprecated shims that forward tosetup-node.sh.
Anyone can run a full node — no approval required. Install the scripts, run setup-node.sh, and the node syncs the full chain state, serves JSON-RPC, and follows Narwhal/Bullshark consensus.
Every node is provisioned validator-capable from day one. "Just following consensus" and "validating" are not two install options — the difference is on-chain state (stake plus committee membership) that the protocol reads each epoch. Until you stake and activate, the node behaves like any full node.
The ports are the same on every node:
- RPC: 8545 (HTTP) / 8546 (WS) — the reth defaults
- P2P: 49590 (primary) and 49594 (worker) — UDP/QUIC
- Metrics: 9101 (loopback only)
To validate you additionally need, in order:
- Approval — Telcoin Association onboarding. Validators must be GSMA-approved MNOs; email grant@telcoin.org before purchasing hardware.
- Stake — submit the stake transaction with your BLS public key and proof of possession.
- Activation — call
activate()on-chain and go active at the next epoch boundary.
The node software does not change. Once tn_isValidator(blsPubkey) returns true on-chain, the web UI automatically shows that node on the validator tab and renders the validator dashboard — there is no node-type toggle to flip. The step-by-step (with cast commands) is in Validator Onboarding Flow below.
Each machine runs exactly one node, installed under a single, consistent identity:
- systemd unit:
telcoin(telcoin.service) - Docker container name (Docker installs):
telcoin - config directory:
/etc/telcoin - data directory:
/var/lib/telcoin
/etc/telcoin/.node-meta records a NODE_TYPE= key, but it is only a non-authoritative
default-view hint (new installs write NODE_TYPE=observer) — the on-chain tn_isValidator
status is authoritative. Because there is only ever one node on the box, the binary is
launched with no node-instance flag and serves RPC on the reth default ports (8545/8546).
Upgrading from an older install? Earlier versions used a separate unit name and per-role config/data directories for each node type. Those legacy per-role installs keep working untouched — a compatibility shim (
lib/fallback.sh) detects the old layout and resolves the correct unit, container, and directories automatically. Nothing is renamed or migrated on its own; to move an existing install onto the unified layout, runmigrate-node-naming.sh. Fresh installs always use the unifiedtelcoinidentity above.
The baseline below runs a full node. The heavier "to validate" column is what you want if you intend to stake and validate — treat it as guidance, not a requirement to run a node. The hardware preflight checks against the baseline and prints the validate spec for reference.
| Component | Run a node (baseline) | To validate |
|---|---|---|
| CPU | 8 cores / 16 threads, x86-64/ARM64 | 16+ cores / 32 threads, x86-64, 4000+ PassMark single-thread |
| RAM | 16GB DDR4 ECC | 128GB DDR4/DDR5 ECC RDIMM |
| Storage | 500GB TLC NVMe SSD | 4TB TLC NVMe SSD |
| Network | 24Mbps+ stable | 1Gbps sustained, 1GbE+ |
Storage note: TLC NVMe drives are specifically required over QLC. TLC supports 1,000-3,000 P/E cycles vs 100-1,000 for QLC, making TLC significantly more durable for continuous blockchain write operations.
- Ubuntu 22.04+ LTS (minimum -- required for systemd 247+)
- Debian 12+
- Red Hat Enterprise Linux (RHEL) 8+
- Kernel version 3.10+ minimum
- macOS Sequoia 15+ (full node only)
The scripts will install or check for everything needed. You do not need to install anything manually beforehand.
- GSMA MNO status — only GSMA-approved MNOs may validate
- Hardware approval from the Telcoin Association — email grant@telcoin.org before purchasing equipment
- Prior governance approval from the Telcoin Association
- A registered Ethereum address for receiving TEL rewards
All scripts are interactive and guide you through each step. Work through these in order on a fresh Linux machine:
1. Install the scripts
One-liner with curl:
curl -fsSL https://install.telcoin.network | bashOr with wget:
wget -qO- https://install.telcoin.network | bashIf the install.telcoin.network domain is ever unreachable, the raw GitHub URL is a drop-in fallback:
curl -fsSL https://raw.githubusercontent.com/Telcoin-Association/tn-node-deployment/main/install.sh | bashOr clone the repo directly:
git clone https://github.com/Telcoin-Association/tn-node-deployment.git ~/telcoin-node-scripts
chmod +x ~/telcoin-node-scripts/*.sh2. Run the guided setup
sudo bash ~/telcoin-node-scripts/setup-node.shThis provisions a validator-capable node. Staking and on-chain activation (see Become a validator) are what let it validate later; until then it follows consensus as a full node.
3. Harden the firewall (recommended)
sudo bash ~/telcoin-node-scripts/firewall-setup.shOpens the ports every node needs: SSH, Uptime Kuma, and the P2P consensus ports UDP 49590/49594.
4. Check node health any time
bash ~/telcoin-node-scripts/check-node.sh
# Include on-chain validator status
bash ~/telcoin-node-scripts/check-node.sh --address 0xYOUR_ADDRESS# Edit a running node's configuration (multiaddrs, ports, RPC mode, etc.)
sudo bash ~/telcoin-node-scripts/edit-config.sh
# Update the node to a new version (rebuild from source OR pull a new Docker image)
sudo bash ~/telcoin-node-scripts/update-node.sh
# Update these scripts themselves to the latest version from GitHub
bash ~/telcoin-node-scripts/update-scripts.sh
# Remove a node installation (interactive, with explicit confirmations)
sudo bash ~/telcoin-node-scripts/remove-node.sh
update-node.shvsupdate-scripts.sh: the first updates the node binary or Docker image to a new release; the second updates these helper scripts themselves from GitHub. Different things.
Each script walks through numbered steps:
Step 1: Pre-flight Checks and Install Method
- Checks you are running as root
- Detects your Linux distribution and package manager
- Verifies hardware meets minimum requirements
- Checks internet connectivity and required ports
- Checks systemd version (247+ required -- Ubuntu 22.04+)
- Installs any missing tools (curl, git)
- Asks how to obtain the binary (build from source, Docker, or existing)
- Installs all dependencies upfront before configuration begins (Rust, build tools, Docker image pull, etc.)
- For binary/source installs, asks which passphrase protection method to use (LoadCredential or TPM/vTPM)
Step 2: Network Selection
- Asks which network to connect to (Adiri testnet or mainnet)
Step 3: Node Configuration
- Asks for port and directory configuration
- Asks for external and listener IP addresses for P2P
Step 4: System Infrastructure
- Creates a dedicated system user and group (default: telcoin/telcoin, customisable). The user has no login shell for security.
- Creates all required directories under /opt/telcoin, /var/lib/telcoin, /etc/telcoin, /var/log/telcoin
- Creates the reth internal log cache directory
- Verifies the binary is valid and executable
Step 5: Key Generation
- Asks for your Ethereum address and P2P multiaddrs
- Asks you to set a BLS key passphrase (entered twice to confirm, never shown on screen)
- Runs the telcoin-network keytool to create the node's cryptographic keys (BLS + P2P)
- Stores keys in /var/lib/telcoin/node-keys/ with strict permissions
- Stores passphrase in /etc/telcoin/bls-passphrase (mode 600)
- If TPM selected: seals passphrase to TPM chip, shows it once, prompts operator to store offline
Step 6: Configuration
- Copies the official chain-config files (genesis.yaml, committee.yaml, parameters.yaml) from the cloned repository
Step 7/8: Systemd Service
- Writes a wrapper script to /opt/telcoin/start-telcoin.sh that reads the passphrase securely at runtime
- Writes a systemd service file to /etc/systemd/system/telcoin.service using LoadCredential
- Configures the correct network listener addresses for P2P connectivity
- Optionally starts the node immediately
- Optionally enables auto-start on server reboot
After setup, files are organised as follows. There is one layout for every node — the
default-view hint is recorded in /etc/telcoin/.node-meta (NODE_TYPE=) rather than in the paths.
/opt/telcoin/
telcoin-network -- the node binary
start-telcoin.sh -- wrapper script (reads passphrase, starts node)
/var/lib/telcoin/
node-keys/ -- P2P + BLS keys (keep backed up!)
node-info.yaml -- public node identity (BLS pubkey + proof of possession)
genesis/
genesis.yaml -- chain genesis config
committee.yaml -- validator committee config
parameters.yaml -- consensus parameters
db/ -- chain database (grows over time)
/etc/telcoin/
bls-passphrase -- BLS key passphrase (mode 600, root only)
.node-meta -- internal metadata used by remove/edit scripts
/var/log/telcoin/
telcoin.log -- node output log
telcoin-error.log -- node error log
/etc/systemd/system/
telcoin.service -- systemd unit definition
/home/telcoin/
.cache/reth/logs/ -- reth internal log cache
/opt/telcoin-source/ -- cloned GitHub repository
chain-configs/ -- official chain config files
target/release/ -- compiled binary location (source builds only)
One machine runs one node, so there is a single telcoin.service and a single set of
directories regardless of node type. Installs created by older versions of these scripts used
per-role unit names and per-role subdirectories under /etc/telcoin and /var/lib/telcoin;
those keep working as-is and the helper scripts locate them automatically via the
compatibility shim in lib/fallback.sh.
The scripts follow Linux security best practices:
- Dedicated service user — the node runs as a dedicated system user (default:
telcoin) with no login shell and no sudo access. The user and group name can be customised during setup. If the process is compromised it cannot access your other files or accounts. - Strict file permissions — key files are mode 600 (readable only by owner). The node-keys directory is mode 700.
- Passphrase never embedded in the service file — for all install methods the BLS passphrase is loaded via systemd
LoadCredentialinto a secure temporary directory and never appears in the service file orsystemctl show/catoutput. Binary/source installs read it from$CREDENTIALS_DIRECTORYdirectly; Docker installs do the same in a small wrapper and pass it to the container with-e TN_BLS_PASSPHRASE(name only — the value is not on the command line). Note that, inherent to Docker, the value is still present in the container's environment (docker inspect); keeping it out of the persisted unit is the protection this provides. - Systemd hardening — the service uses
NoNewPrivileges,PrivateTmp, andProtectSystem=strictto limit what the process can do. - RPC localhost only — the RPC port defaults to 127.0.0.1 (localhost only). It is never exposed to the internet by default.
- CVE-2026-31431 check — the setup scripts check for the Copy Fail mitigation during preflight and will not proceed until it is applied.
A HIGH severity local privilege escalation vulnerability affecting all Linux kernels since 2017. A 732-byte Python script using only standard library modules can give any unprivileged local user a root shell — no race conditions, no kernel-specific offsets, 100% reliable.
The setup scripts detect whether the algif_aead kernel module is loaded or unblocked. If the mitigation has not been applied, the script stops and directs the operator to apply it before proceeding.
Details and mitigation: https://copy.fail
To apply the mitigation manually:
# Check current state
modprobe --showconfig | grep -q "install algif_aead /bin/false" && echo "BLOCKED" || echo "NOT BLOCKED"
grep -qE '^algif_aead ' /proc/modules && echo "LOADED" || echo "NOT LOADED"See https://copy.fail for the official mitigation steps.
All install methods use systemd LoadCredential by default (requires Ubuntu 22.04+ / systemd 247+). The passphrase is stored in a mode 600 file and loaded securely at runtime -- it never appears in the service file or systemctl show/cat output. Docker installs load it the same way via a small root-owned wrapper and pass it to the container with -e TN_BLS_PASSPHRASE (name only); the value is still visible in the container's own environment (docker inspect), inherent to Docker, but no longer in the persisted unit. For operators requiring even higher security, the following options are available.
Built into systemd (version 247+, available on Ubuntu 22.04+). Instead of embedding the passphrase directly in the service file, systemd loads it from a file and injects it into a secure temporary directory that only the service process can access. The passphrase never appears in systemctl show output or process listings.
The setup scripts configure this automatically for binary and source installs. Advantages:
- Passphrase never embedded in the service file
- Systemd manages the secure credential directory automatically
- No extra software required
- Credential is cleaned up when the service stops
Available as an option during setup for binary and source installs. The passphrase is sealed to the machine's TPM chip and can only be decrypted on that exact machine, even if someone obtains root access or copies the disk. Supported on GCP Shielded VMs (vTPM), AWS Nitro, and bare metal servers with a TPM2 chip.
The setup scripts handle sealing automatically using tpm2-tools. During setup you will be shown the passphrase once and prompted to store it offline before the plaintext file is deleted.
Advantages:
- Passphrase cannot be read by root or copied off the machine
- Works on GCP Shielded VMs, AWS Nitro, and bare metal TPM2
- No extra infrastructure required
- Falls back to LoadCredential file if TPM is unavailable
Disadvantages:
- Recovery requires your offline backup passphrase if the machine is rebuilt
- Not available on VMs without vTPM support
Vault is a dedicated secrets management server. The passphrase never touches disk on the node server at all — it is fetched from Vault via an authenticated API call at startup. Vault provides a full audit log of every access and supports secret rotation without touching the server.
A wrapper script would replace the direct ExecStart:
#!/usr/bin/env bash
# /opt/telcoin/start-telcoin.sh
export TN_BLS_PASSPHRASE=$(vault kv get -field=passphrase secret/telcoin/node)
exec /opt/telcoin/telcoin-network node --datadir /var/lib/telcoin \
--metrics 127.0.0.1:9101 \
--log.stdout.format log-fmt -vvv --httpAdvantages:
- Passphrase never stored on the node server
- Full audit trail of every secret access
- Central management across multiple nodes
- Secret rotation without touching node servers
- Enterprise access control policies
Disadvantages:
- Requires running and maintaining a separate Vault server
- Significantly more infrastructure overhead
- Overkill for a single node operator
Open the P2P consensus ports — UDP/QUIC 49590 (primary) and 49594 (worker) — on every node, not just ones that validate today. A node that later stakes and joins the committee behind a closed firewall is unreachable to its peers and silently misses consensus, so the ports are opened up front while the node is still just following consensus.
Linux firewall (ufw):
sudo ufw allow 49590/udp
sudo ufw allow 49594/udpRouter port forward (home/bare metal only): Forward UDP ports 49590 and 49594 from WAN to your server's local IP address. Cloud servers handle this via their network configuration.
The RPC port (8545) should not be opened to the internet unless you are specifically running a public RPC endpoint with a reverse proxy in front of it.
Required for all nodes. The Telcoin Association runs Uptime Kuma health monitoring against every deployed node — TCP port 43174 must be reachable by the Association monitor, plus any optional operator-chosen IPs. Restrict it to those source IPs rather than opening it to the whole internet (the endpoint binds on all interfaces, so a firewall rule is its only protection):
sudo ufw allow from 104.155.184.201/32 to any port 43174 proto tcpfirewall-setup.sh does this automatically — source-restricted to the monitor — when you run option 2 ("Enable firewall with recommended defaults"). To also expose it to your own monitoring host(s), run firewall-setup.sh → "Manage node ports" → choice 2 ("Association monitor + your IPs"); your additions are persisted and survive a firewall reset. Avoid sudo ufw allow 43174/tcp, which exposes the health endpoint to the entire internet.
Setting up a validator involves both off-chain (node setup) and on-chain (contract interaction) steps. The setup script handles the off-chain steps and guides you through what is needed on-chain.
Full staking guide: https://docs.telcoin.network/telcoin-network/staking/how-to-stake
Step 1 — Generate keys and set up node (script handles this)
Run setup-node.sh. The script installs the binary, generates your BLS keys, copies chain configs, and starts the node service.
Step 2 — Request Governance Approval (operator action)
Submit your ECDSA validator address to the Telcoin Association for off-chain verification. You do NOT need to send your node-info.yaml — just your address. Upon approval, governance calls mint(validatorAddress) on the ConsensusRegistry contract.
Verify you have received your ConsensusNFT:
cast call 0x07E17e17E17e17E17e17E17E17E17e17e17E17e1 \
"balanceOf(address)(uint256)" \
<VALIDATOR_ADDRESS> \
--rpc-url <RPC_URL>Step 3 — Stake your TEL (operator action)
Once whitelisted, submit the stake transaction using your BLS public key and proof of possession from node-info.yaml:
# Check required stake amount first
cast call 0x07E17e17E17e17E17e17E17E17E17e17e17E17e1 \
"getCurrentStakeConfig()" \
--rpc-url <RPC_URL>
# Submit stake
cast send 0x07E17e17E17e17E17e17E17E17E17e17e17E17e1 \
"stake(bytes,(bytes,bytes))" \
<BLS_PUBKEY_COMPRESSED> \
"(<UNCOMPRESSED_PUBKEY>,<UNCOMPRESSED_SIGNATURE>)" \
--value <STAKE_AMOUNT> \
--trezor \
--rpc-url <RPC_URL>Step 4 — Sync your node Wait for the node to fully sync. Check sync status:
curl -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' \
http://localhost:8545Step 5 — Activate (operator action)
Once synced, call activate() to enter the activation queue:
cast send 0x07E17e17E17e17E17e17E17E17E17e17e17E17e1 \
"activate()" \
--trezor \
--rpc-url <RPC_URL>Step 6 — Go active (automatic) At the next epoch boundary your status changes to Active and you begin participating in consensus.
bash ~/telcoin-node-scripts/check-node.sh --address 0xYOUR_VALIDATOR_ADDRESS| Status | Meaning | Next Action |
|---|---|---|
| No NFT found | Not yet whitelisted | Submit address to Telcoin Association |
| Undefined | NFT minted, not staked | Call stake() on ConsensusRegistry |
| Staked | Staked, not activated | Call activate() on ConsensusRegistry |
| PendingActivation | Activation in progress | Wait for next epoch |
| Active | Fully active in consensus | No action needed |
| PendingExit | Exiting the network | Wait for exit to complete |
| Exited | Exited | Call unstake() to reclaim TEL |
Run at any time after setup to verify your node is healthy:
# Health check for the local node
bash ~/telcoin-node-scripts/check-node.sh
# Force the validator or full-node view (overrides the .node-meta hint)
bash ~/telcoin-node-scripts/check-node.sh --validator
bash ~/telcoin-node-scripts/check-node.sh --observer
# Include validator on-chain status (queries the ConsensusRegistry contract)
bash ~/telcoin-node-scripts/check-node.sh --address 0xYOUR_VALIDATOR_ADDRESS
# Skip the network RPC query (fully local / air-gapped diagnostics)
bash ~/telcoin-node-scripts/check-node.sh --no-network
# Custom local RPC endpoint or service name
bash ~/telcoin-node-scripts/check-node.sh --rpc http://127.0.0.1:8545 --service telcoinThe health check verifies:
- Systemd service status — running, with restart-loop detection (warns if the unit has restarted more than 5 times)
- Local RPC mode — classified as
HEALTHY,SLOW(responding but >6s),DISABLED(HTTP 200 but-32601 method not found), orDOWN(connection refused). Previously all four looked the same. - Network consensus state — queries
https://rpc.telcoin.networkfor ground truth: current block, epoch, committee size, and how fresh the latest commit is. - Local consensus state — calls
tn_latestConsensusHeaderon the local node and applies the freshness contract:block == 0→ ERROR (fully stalled), commit-timestamp age > 60s → WARN (stale), else OK. Also reports lag vs network in blocks. - Author presence (validator-only) — checks whether your authority ID appears in the network's recent consensus headers. Catches the failure mode where a validator is running (systemd green, RPC up) but silent (not authoring headers). Auto-detects your authority ID from
<data-dir>/node-info.yaml(fieldprimary_network_key) or accepts an explicit--authority-id <BASE58>override. - Reputation score (validator-only) — your own score from
sub_dag.reputation_score.scores_per_authorityalongside the committee average. Flags scores below half-average. - Validator on-chain status — when
--addressis provided, calls the ConsensusRegistry contract and reports your validator state (Undefined / Staked / PendingActivation / Active / etc.). - Disk space — uses the actual data directory from
/etc/telcoin/.node-meta(falls back to/var/lib/telcoin), so the check reports usage on whichever mount actually holds chain data — not just the default. - Memory — total / available / percent used.
Earlier versions of check-node.sh grepped the node log file for fixed string markers like peer metrics heartbeat and got new consensus. That approach was fragile (any change to the node binary's log format silently broke it) and gave misleading output — for example a "P2P peers since startup" metric whose label was wrong and whose count had no time window. As of v1.1.31 the script uses tn_latestConsensusHeader directly, which is stable, accurate, and works whether or not the node writes a parseable log file.
The author-presence check is the most useful signal — it answers the question "is the network actually seeing my node participate?" using the network's own consensus headers as the source of truth. This works even when the local RPC is closed off entirely.
After setting up your node, run the firewall setup script to harden your server. This script can be run at any time — both to apply changes and to view the current state of your firewall.
sudo bash ~/telcoin-node-scripts/firewall-setup.shThe script is menu-driven and interactive. It never makes changes without explicit confirmation.
View current status — run this at any time to get a full overview of your firewall state, SSH configuration, open ports, and any security warnings. No changes are made.
Enable firewall with recommended defaults — sets default deny inbound, allow outbound, and keeps SSH accessible. Always do this before restricting SSH access.
Manage SSH access — disable password authentication (keys only), disable root login, change SSH port. Each option shows the current state and warns clearly before making any changes.
Manage node ports — opens the P2P consensus ports (UDP 49590/49594) inbound on every node, and optionally opens port 443 for public RPC via nginx.
Manage trusted IP whitelist — add or remove specific IP addresses or CIDR ranges that are allowed SSH access. Shows your current session IP so you don't accidentally lock yourself out.
- Test SSH in a new terminal before closing your current session after making any changes
- Whitelist your IP first before enabling default deny or restricting SSH
- Every node opens inbound UDP 49590/49594 — a node that later stakes behind a closed firewall would otherwise miss consensus
- Never open the RPC port (8545) directly to the internet — use nginx on port 443 instead
Run firewall-setup.sh after completing node setup and before going live. For production validator nodes this is strongly recommended. For home/testing setups it is optional but good practice.
# Start / stop / restart
sudo systemctl start telcoin
sudo systemctl stop telcoin
sudo systemctl restart telcoin
# View live logs
sudo tail -f /var/log/telcoin/telcoin.log
# View logs via journalctl
journalctl -u telcoin -f
# Enable auto-start on boot
sudo systemctl enable telcoin
# Reset after too many failed restarts
sudo systemctl reset-failed telcoinWhen prompted during setup you can choose how to obtain the telcoin-network binary:
| Option | Description | Notes |
|---|---|---|
| Build from source | Clones the GitHub repo and compiles with cargo build --release |
Takes 20-40 min, requires ~4GB RAM during build |
| Pre-built binary | Downloads a release binary | Coming soon — check releases |
| Docker | Pulls official image from Google Artifact Registry | us-docker.pkg.dev/telcoin-network/tn-public/adiri:VERSION |
| Existing binary | Use a binary already on this machine | Useful if you have already compiled it |
When Docker is selected the script will:
- Install Docker if not already present
- Ask for the full image URL and tag (default:
us-docker.pkg.dev/telcoin-network/tn-public/adiri:v0.9.2-adiri) - Pull the image
- Create the host service user with UID 1101 to match the container's internal
nonrootuser - Generate keys using the Docker image
- Create a systemd service that runs
docker runwith--userflag for correct volume permissions
The operator can still choose any service user name and group — UID 1101 is assigned transparently to ensure Docker volume permissions work correctly.
During setup you will be asked how the node should listen for incoming P2P connections:
IPv6 — recommended for cloud and data centre servers. Binds to all IPv6 interfaces (::) and is NAT-free, meaning no router port forward is required.
IPv4 — for home or bare metal servers. The script will auto-detect your server's internal IP address (e.g. 10.x.x.x on cloud, 192.168.x.x on home networks) and ask you to confirm it. On home or bare-metal networks, forward UDP ports 49590 and 49594 on your router to this server so the node stays reachable to its peers (and to the committee if it later stakes).
Important distinction for cloud/data centre operators:
- Internal IP (e.g.
10.70.70.2) — what the node binds its listener to. Auto-detected by the script viahostname -I. - External/Public IP — what peers use to reach your node. Fetched automatically via
api.ipify.organd used for the node's key registration innode-info.yaml.
These are two different addresses on cloud servers and the script handles both correctly.
Use the dedicated removal script to safely remove a node installation:
sudo bash ~/telcoin-node-scripts/remove-node.shThe script automatically detects what is installed (including legacy per-role layouts) and the install method (binary/source or Docker). It guides you through removal step by step with individual confirmations for each component.
What it removes:
- Systemd service (stops, disables and removes the service file)
- Docker container and optionally the image (if Docker install)
- Chain database
- Node keys and passphrase (requires typing
DELETEto confirm -- cannot be undone) - Binary and source code
- Log directory
- Service user and group
Wipe chain data only (keeps keys and config, forces resync) is also available as an option inside the removal script.
Your node keys are stored in /var/lib/telcoin/node-keys/. Back these up immediately after setup.
If you lose your keys you lose your node identity. A node that has already staked must re-register its replacement keys with the Telcoin Association; a node that has not can simply regenerate keys and restart.
Store your BLS passphrase separately from the key files — in a password manager or secure offline location. If you lose the passphrase the encrypted key files are unreadable.
Run the update script at any time to check for and download newer versions:
bash ~/telcoin-node-scripts/update-scripts.shThe script checks each file individually against the latest version on GitHub and shows a status table:
Script Local Remote Status
----------------------------------------------------------------
setup-node.sh 1.1.2 1.1.3 UPDATE AVAILABLE
check-node.sh 1.1.2 1.1.2 Up to date
edit-config.sh 1.1.2 1.1.3 UPDATE AVAILABLE
lib/common.sh 1.1.2 1.1.3 UPDATE AVAILABLE
If updates are available it will ask for confirmation before downloading. lib/common.sh is always included in any update since all scripts depend on it.
The updater also tracks the optional web UI (versioned independently, starting at 1.0.0). When a newer UI is published it fetches the bundle into ui/ and, if the UI is already installed, redeploys it via ui/install-ui.sh --update (refreshing the helper, sudoers, and restarting the service so the new code loads).
A small, self-contained web UI for managing a node from your browser: health at a glance, live logs, configuration, and OpenTelemetry traces. It is optional — nodes run fine without it.
sudo bash ~/telcoin-node-scripts/ui/install-ui.shThe installer creates an unprivileged telcoin-ui system user, installs the app under /opt/telcoin-ui, and runs it as a systemd service. No further manual sudo setup is required.
The UI binds to 127.0.0.1:8080 only — it is never exposed to the network. Reach it over an SSH tunnel.
From your local machine, the helper script opens the tunnel and your browser:
./open-ui.sh user@SERVER_IPOr do it manually:
ssh -L 8080:localhost:8080 user@SERVER_IP
# then open http://localhost:8080 in your browserBy default the UI is localhost-only (SSH tunnel). If you'd rather reach it at your own domain over HTTPS, install-caddy puts Caddy in front of it as a reverse proxy with automatic Let's Encrypt TLS and a login.
Public access is read-only — Caddy stamps an unforgeable header that the UI server enforces, so every management action (start/stop, config edit, update, setup, remove) is refused over the public path. Full management stays on the SSH tunnel (localhost).
Enable it from the UI under Settings → External Dashboard Access, or on the server:
sudo bash ~/telcoin-node-scripts/install-caddy.shYou choose the domain, a login username (not forced to admin), and a password.
- Set the DNS A record first. Point your domain at the server's public IP (the router's public IP if it's behind NAT) before enabling — Caddy requests the certificate on first start, so the record must already resolve or issuance fails and Let's Encrypt rate-limits retries. The wizard checks propagation before continuing.
- Ports: forward 443/tcp (required) to the node; 80/tcp is recommended (it adds the http→https redirect and a fallback for certificate issuance/renewal) but not required — Caddy obtains the certificate over 443. The script opens 80/443 in
ufwfor you. (Inbound forwarding is off by default on most routers, so this is something you set up explicitly.) - Conflicts: Apache/Nginx and Caddy can't share ports 80/443. The interactive installer detects a conflicting web server and offers to stop, disable, or remove it (or quit), and it won't overwrite a Caddy config it didn't create.
- Treat the login as a read-only credential, and rotate it. The username/password gate only the public read-only view (it's stored as a bcrypt hash in the Caddyfile); it grants no management access — that stays on the SSH tunnel. If the credential leaks, the blast radius is read-only, but rotate it anyway by re-running
install-caddy.sh(re-prompts and rewrites the hash). Don't reuse a password you use elsewhere.
Disable any time from the same Settings panel.
The Settings tab can start/stop a local Jaeger instance and toggle OpenTelemetry tracing on the node; the Traces tab browses the collected spans. Jaeger's own UI (:16686) and the OTLP endpoint (:4317) are likewise localhost-only and reached through the same tunnel.
- Binds
127.0.0.1only; never0.0.0.0. Reached via an SSH tunnel — no new firewall ports — unless you opt into public access via Caddy (above), which is read-only and enforced server-side. - The UI runs as the unprivileged
telcoin-uiuser. Every privileged action goes through one root-owned, argument-validated helper at/usr/local/sbin/telcoin-ui-helper. - That user's
sudorights are pinned by an explicit, no-wildcard sudoers drop-in (/etc/sudoers.d/telcoin-ui): the sixsystemctl start|stop|restartlines for the two node services, plus the exact helper sub-commands. Nothing else.
systemctl status telcoin-ui
journalctl -u telcoin-ui -fThe systemd unit is telcoin for fresh installs. (Nodes installed under an older version may
use a previous per-role unit name — see One node per VM; the helper scripts
detect it automatically, but with raw systemctl substitute that name below.)
# Start / stop / restart
sudo systemctl start telcoin
sudo systemctl stop telcoin
sudo systemctl restart telcoin
# Enable / disable auto-start on boot
sudo systemctl enable telcoin
sudo systemctl disable telcoin
# Reset after too many failed restarts
sudo systemctl reset-failed telcoin# Tail the node's stdout/stderr log file
sudo tail -f /var/log/telcoin/telcoin.log
# Or via journalctl
journalctl -u telcoin -f# Health check for the local node
bash ~/telcoin-node-scripts/check-node.sh
# Health check including on-chain validator state
bash ~/telcoin-node-scripts/check-node.sh --address 0xYOUR_ADDRESS
# Interactive config editor (backs up the unit file before any change)
sudo bash ~/telcoin-node-scripts/edit-config.shThe default RPC port is 8545 (the reth default).
# eth_chainId
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}' \
http://127.0.0.1:8545
# eth_blockNumber
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://127.0.0.1:8545
# eth_syncing
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"eth_syncing","params":[],"id":1}' \
http://127.0.0.1:8545
# tn_latestConsensusHeader -- the authoritative consensus state (use this
# rather than log-grepping for "got new consensus" entries)
curl -s -X POST -H 'Content-Type: application/json' \
--data '{"jsonrpc":"2.0","method":"tn_latestConsensusHeader","params":[],"id":1}' \
http://127.0.0.1:8545# Update scripts to latest version
bash ~/telcoin-node-scripts/update-scripts.sh
# Remove a node
sudo bash ~/telcoin-node-scripts/remove-node.sh
# Firewall management
sudo bash ~/telcoin-node-scripts/firewall-setup.shThree optional, testnet-only, reversible capabilities that let the Telcoin Association help run the testnet. All are off by default and additive — a node that opts out is unaffected. Full details and trust model: docs/testnet-addons.md.
- Health monitoring — exposes a health endpoint (port
43174) probed by the Association's uptime monitor (plus any IPs you choose to add), so they can alert you when your node drops. - Centralized logging — ships your node's logs to the Association's Loki (a Grafana Alloy sidecar) to help debug issues. Needs a per-operator ingest token.
- VPN admin SSH — lets the Association SSH into your node over a private WireGuard
overlay (a sudo
tnadminuser reachable only over the VPN) to help recover it.
You're offered each one during setup-node.sh (right after
network selection), or enable them later:
# Logging + health monitoring
sudo bash ~/telcoin-node-scripts/setup-observability.sh
# VPN admin SSH (or --disable to remove)
sudo bash ~/telcoin-node-scripts/setup-vpn.shEnabling the VPN requires explicit consent (you type I CONSENT); it never touches your
own SSH config, runs its host firewall dormant, and is undone by setup-vpn.sh --disable.
setup-vpn.sh opts your node into the Telcoin Association's private WireGuard overlay and
grants the core team SSH over that overlay only (a sudo tnadmin user), so they can help
recover a stuck node. It is opt-in, testnet-only, additive, and reversible — your own SSH
config is untouched and setup-vpn.sh --disable removes everything.
sudo bash setup-vpn.sh # enrol (interactive, consent-gated)
sudo bash setup-vpn.sh --status # diagnose tunnel + keys + firewall (read-only)
sudo bash setup-vpn.sh --sync-keys # re-apply the maintainer SSH keys after a git pull
sudo bash setup-vpn.sh --apply-firewall # (re)add the overlay->SSH ufw rule if you run ufw
sudo bash setup-vpn.sh --disable # tear everything down- WGVPN.md — full operator + maintainer guide (enrol, verify, re-key, rename, the firewall model).
- DEBUG.md — connectivity runbook: if
tn_sshtimes out, work both ends with a symptom → cause → fix table.
Run --status first whenever a maintainer can't reach the node — it pinpoints which of the
five things (tunnel, reboot persistence, keys, sshd drop-in, firewall) needs attention and
prints the exact fix command.
Versioning note (from v1.1.48 onwards): each script bumps
SCRIPT_VERSIONindependently, so entries are titled<script> vX.Y.Z. Earlier entries used a flat "all scripts bumped to vX.Y.Z" convention.
Release tags are occasionally re-cut at the same name (a bad v0.13.0-adiri build was
retagged to a corrected commit). Both prepare flows checked the requested ref out
local-first, so a node that already held the OLD tag silently rebuilt the retired
commit — and the git describe version marker still read like the right release.
Prepare (interactive and --json) now runs git fetch origin --tags --force before
checkout, and the newest-tag probe (latest_source_ref) fetches with --force too,
matching the thorough refresh setup-node.sh already does on reused clones. Offline
prepares keep working: a failed fetch is tolerated and checkout proceeds against
local refs.
Three operational fixes to the update engine (lib/common.sh v1.3.8):
- One update at a time. A UI-triggered update and a CLI run could previously run
concurrently — double service stop, racing writes to
.pending-update, interleaved binary/image swaps. Every mutating mode now takes/var/lock/telcoin-update.lock(flock, kernel-released on any exit, holder PID reported on contention); read-only--checkpolls never block a real update. - Verify means the new version is live. Post-apply verification used to pass on "service active + RPC responds", which a no-op update also satisfies. Source applies now require the installed binary to hash-match the prepared build; docker prepares record the pulled image ID and applies require the running container to be on it. Mismatch triggers the existing rollback. Pending states written by older versions still apply (the identity check skips with a warning).
- No more mid-apply aborts. Sourcing
lib/common.shhad silently re-enabledset -e, so an unguarded failure after "service stopped" aborted the script with the node down and the designed verify→rollback flow never ran. Errexit is now off (as the script's own header always intended), every state-changing step is explicitly handled, pending-state write failures can no longer report "saved", and a failed rollback restore stops loudly with manual recovery steps instead of restarting the new artifact and misreporting "rolled back".
Config detection grepped the unit's ExecStart for docker run, but current installs
launch through the start wrapper (/opt/telcoin/start-<svc>.sh) — docker installs were
misdetected as "binary", every field showed unknown, edits silently rewrote unit lines
the service never reads, and the docker-image editor refused to run. Detection and all
edits (listeners, p2p ports, metrics, verbosity, image — menu and --json set) now
resolve the launch file the same way update-node.sh does and write there; listener
edits on binary installs update the unit Environment= line and the wrapper export
together. Also fixes a latent verbosity-edit bug where the first -v on a docker
launch line — the volume flag — could be replaced instead of the verbosity flag. RPC
editing on wrapper installs is refused with manual instructions for now.
The docker keygen passed the BLS passphrase as -e TN_BLS_PASSPHRASE="<value>" — visible
in ps//proc/*/cmdline for the life of the keygen. It now uses name-only env
pass-through, the same convention as the runtime wrapper. The passphrase file is also
created 0600 from the first byte (umask subshell) instead of write-then-chmod.
update-scripts.sh v1.1.65 re-cut with refreshed .sha256 sidecars. ui/server.py v1.8.6 carries no UI change — the bump redeploys the root-owned update engine in
/opt/telcoin-ui-update/ so UI-driven updates and config edits pick these up.
The v0.12.0-adiri release moved the tn-contracts submodule pointer, and the new
code include_str!s files that only exist in the new submodule commit. Both source
prepare paths in update-node.sh did git checkout + git pull but never
git submodule update, so every source-build node hit a cargo error about a missing
deployments-*.json mid-update. (Fresh installs were unaffected — setup-node.sh
already syncs.)
lib/common.sh v1.3.7 adds tn_sync_submodules, which runs git submodule sync
then git submodule update --init --recursive --force to pin submodules to the
checked-out ref. Both update-node.sh prepare paths (interactive and UI/--json)
now call it and hard-fail with a clear message before any build starts; the
chain-config refresh paths (ensure_chain_configs_available, edit-config.sh v1.2.4) sync too but only warn on failure, since chain configs live in the
superproject.
If your source update already failed on this: just re-run the update — prepare now heals the submodule before building. Or fix it manually first:
sudo git -C /opt/telcoin-source submodule update --init --recursive --forceupdate-scripts.sh v1.1.64 re-cut with refreshed .sha256 sidecars. ui/server.py v1.8.5 carries no UI change — the bump redeploys the root-owned update engine in
/opt/telcoin-ui-update/ so UI-driven updates pick up the fix.
adiri testnet moved to v0.12.0-adiri, and this release points every default at it.
lib/common.sh v1.3.6 bumps DEFAULT_DOCKER_IMAGE (the fallback used only when the
registry is unreachable) and raises MIN_SOURCE_VERSION_TESTNET from 0.9.1 to
0.12.0; ui/server.py v1.8.4 and the setup wizard's docker-image placeholder follow.
This upgrade is wire-protocol-breaking — a v0.11 node cannot peer with a v0.12
node at all. Every libp2p protocol string is now namespaced by chain id, including
the gossipsub protocol id itself (/meshsub/1.1.0 → /tn-meshsub-{chain_id}/1.1.0),
all four gossip topics are renamed, and request-response moved to /0.0.2. Multistream
negotiation fails before a subscribe frame is exchanged, so a node left on v0.11.0-adiri
is not "behind" — it is partitioned, with no peers and no path back to consensus. Update.
No data-dir wipe and no resync: genesis is unchanged, and v0.12 reads existing v0 consensus packs in place. Note the reverse is NOT true — v0.11 cannot read the packs v0.12 writes, so a downgrade stops being clean once a new epoch's pack is created (adiri epochs are 6h).
Source installs rebuild from the tag: git checkout v0.12.0-adiri and
cargo build --release --features adiri, which update-node.sh --prepare does for you
and which takes 20-40 minutes on typical operator hardware. It runs before any downtime.
update-scripts.sh v1.1.63 re-cut with refreshed .sha256 sidecars.
Docker updates now edit the file that actually launches the container. Current docker
installs run docker run from the start wrapper (/opt/telcoin/start-<svc>.sh) rather
than the unit's ExecStart, but the update path still read and rewrote the systemd unit —
so --check reported no current image and prepare/apply failed on every wrapper-based
install. Image detection and both apply paths now resolve the wrapper-vs-legacy-unit
target via tn_node_launch_target and patch whichever file carries the image reference.
Two more fixes ride along: TN_UPDATE_VERIFY_TIMEOUT=<secs> overrides the 45s
post-restart health window (a fleet-wide simultaneous restart for a wire-protocol-breaking
upgrade re-forms quorum slower than one node restarting, and the default window triggered
a spurious auto-rollback), and the interactive docker apply no longer corrupts its restore
path — backup_unit_file printed its info line to stdout inside the caller's $(...)
capture, so a rollback would have copied from a garbage path. update-scripts.sh v1.1.62
re-cut with refreshed .sha256 sidecars.
setup-vpn.sh v1.4.0 fixes opted-in nodes going unreachable to maintainers. tnadmin was
created with no password and then passwd -l'd, leaving the shadow field !-locked; Ubuntu
sshd (UsePAM yes) runs PAM account management after the maintainer key matches, and a
!-locked / aging-flagged account is refused there — so the correct key was denied login. It
is now set password-less but login-enabled (shadow *, not !; aging/expiry cleared),
re-asserted on --selfheal, and reported by a new --status check (3b). A re-run now also
detects an existing install and offers to reuse the overlay IP already in .node-meta /
wg0.conf instead of re-walking the full assignment prompt. update-scripts.sh v1.1.61
re-cut with refreshed .sha256 sidecars (setup-vpn.sh, update-scripts.sh).
The installer now has a branded front door: curl -fsSL https://install.telcoin.network | bash
(the raw GitHub URL still works as a fallback). main stays the single source of truth — a
GitHub Pages deploy republishes install.sh on every push to main, and update-scripts.sh
keeps pulling each file from raw.githubusercontent.com/.../main as before. install.sh also
gains a git-less tarball install path so the one-liner works on a fresh macOS with no git.
update-scripts.sh v1.1.60 routes SHA-256 verification through a portable _sha256 helper
(sha256sum on Linux, shasum -a 256 on macOS), and every updater-tracked file now ships a
committed .sha256 sidecar (generated by tools/gen-checksums.sh, enforced fresh by CI), so
downloads are verified on macOS observers instead of erroring.
Fixes Docker tag auto-suggestion, which silently returned nothing. fetch_docker_tags
piped the registry JSON into python3 while the inline script arrived on the same stdin
via a heredoc, so the parser read its own source instead of the tag list (surfaced by the
new shellcheck CI gate, SC2259). The payload is now passed through the environment, so
the "Update available / pick a version" menu is populated again.
setup-vpn.sh v1.1.0 adds three verbs so operators can verify and repair overlay SSH
without an admin: --status (PASS/FAIL triage across tunnel, reboot persistence, the
maintainer key set, the scoped sshd drop-in, and the active firewall, each with the exact
fix command), --sync-keys (re-apply the vendored maintainer keys after a git pull;
local-only, so it works before connectivity is fixed), and --apply-firewall (idempotently
re-add the overlay→:22 ufw rule for operators who enable/tighten ufw after enrolling).
Completes the vendored maintainer key set (lib/wgvpn/peers/ssh/grant2.pub), and
update-scripts.sh v1.1.55 now ships every maintainer key in TESTNET_ADDONS_BUNDLE (it
previously delivered only two of them). New docs: WGVPN.md and DEBUG.md.
firewall-setup.sh v1.4.0 lets operators expose the health port (43174/tcp) to the
Association monitor plus their own monitoring host(s), not TA-only. Use "Manage node
ports" → choice 2 ("Association monitor + your IPs") to add/remove single IPv4, IPv6, or
CIDR sources. The set is persisted in .node-meta (KUMA_EXTRA_SRC) and reapplied
wherever the TA rule is applied (lib/common.sh v1.3.0 apply_kuma_rule), so it survives
the ufw --force reset that "Enable recommended defaults" performs. setup-observability.sh v1.1.0 restores the extras when health monitoring is re-enabled and removes their ufw
rules (keeping the persisted set) when it is disabled. See
docs/testnet-addons.md.
New opt-in, testnet-only, reversible add-ons for external operators (lib/common.sh v1.2.0; new setup-vpn.sh, setup-observability.sh, lib/observability.sh,
lib/testnet-addons.env, observability/config.alloy, vendored lib/wgvpn/).
Operators are offered each during node setup, or run the standalone scripts later.
firewall-setup.sh v1.3.0 source-restricts the health port to the Association monitor
(104.155.184.201/32), adds a testnet add-on rules menu, and keeps the WireGuard
overlay from being locked out by an SSH whitelist. setup-validator.sh v1.2.16 /
setup-observer.sh v1.2.17 bake the healthcheck + JSON-log flags on the first pass and
persist new .node-meta keys. remove-node.sh v1.2.6 / check-node.sh v1.1.51 tear
down and report the add-ons. See docs/testnet-addons.md.
Fixes the dashboard "Recent Traces / No traces yet" panel when spans are already
visible in the Jaeger UI: the node registers its OTLP service name as a
telcoin-prefixed name plus a node-identity suffix (e.g. telcoin-QCZPqMY2zfp),
so the backend's exact-name Jaeger query never matched. Traces, trace stats, and
the Jaeger service_registered flag now resolve the real service name by
telcoin prefix.
Tracing toggle now restarts the node with systemctl restart --no-block, so the
API returns once the restart is queued instead of blocking on the node's stop
window (fixes the "tracing change failed - timeout"). install-ui.sh gains a
port-8080 preflight: if the service fails to come up because another process
(e.g. a hand-run python3 server.py) is shadowing it, the installer names the
holder and the remedy instead of leaving a silent crash-loop.
First release of the optional web UI: node health, live logs, configuration, and
OpenTelemetry traces over an SSH tunnel. Binds 127.0.0.1 only; all privileged
actions go through one root-owned, arg-validated helper pinned by a no-wildcard
sudoers drop-in. Installed via ui/install-ui.sh.
Tracks the web UI (gated on ui/server.py's UI_VERSION): fetches the UI bundle
when a newer version is published and redeploys it via install-ui.sh --update.
Loosens the version grep to read Python constants alongside bash readonly.
Demotes "Consensus tip is STALE" from WARN+HEALTH_ISSUES to info -- it's a catching-up symptom, not a confirmed failure (completes the v1.1.49 audit).
Drops the "stuck on missing epoch pack" / "Block NOT advancing -- likely STUCK" verdicts; state-sync activity and unchanged-block windows are now info, not errors. Consolidates §3/§5 output and trims §10 boilerplate.
Adds a diagnostic that reads the node log for the "stuck on missing epoch pack" state-sync warning and surfaces the stuck epoch + consensus height. (Superseded by v1.1.49.)
Replaces the https://github.com homepage probe with a HEAD on
${GITHUB_RAW}/README.md to fix false-positive "No internet connection"
errors on residential links.
check-node: network probe failure is now a hard error; adds chain-ID sanity check; removes the unreliable eth_syncing branch; demotes §4 tip-lag warn to info; folds data dir into the §8 disk line.
Hotfix: ensure_chain_configs_available() no longer reassigns the readonly
TN_SOURCE_DIR constant (would otherwise abort setup under set -e).
Hotfix: disables set -e in check-node.sh so helpers returning non-zero no
longer abort the report mid-run; read_prev_block_state always returns 0.
check-node: network execution block now comes from a direct eth_blockNumber
call; tracks local-block advancement between runs via a /tmp state file to
detect frozen execution.
check-node: EVM execution lag is now the authoritative sync signal -- consensus-tip comparison only confirms connectivity, not catch-up. Fixes false "healthy" verdicts on nodes thousands of EVM blocks behind.
update-node.sh: Docker apply gets the hash-check parity that source got in v1.1.41; adds 5 GB pre-flight disk check, cp exit-code check, hand-off to check-node.sh on success.
update-node.sh: fixes silent "build complete" when nothing actually changed
-- cargo PATH under sudo, cargo exit-code check, binary hash compare before
and after cargo build / cp.
pick_source_version correctly identifies named feature branches (e.g.
log_db_name) instead of mislabelling them as "detached".
For older entries (v1.1.39 and earlier), see CHANGELOG.md.
These scripts are installed and updated live from main — operators fetch each file
directly from raw.githubusercontent.com/.../main, so a broken merge reaches them
immediately. Two CI gates (.github/workflows/ci.yml) protect that supply chain on every
pull request and push to main:
-
Shell parse + lint —
bash -non every script under both modern bash and macOS's bash 3.2 (observers run on macOS), plusshellcheck. A parse error onmainwould brickcurl … | bash, so these are blocking. -
Checksum integrity — every updater-tracked file has a committed
<file>.sha256sidecar thatupdate-scripts.shverifies after download. After editing any tracked script you must regenerate and commit its sidecar, or CI fails:bash tools/gen-checksums.sh git add '*.sha256' # quoted: git matches sidecars at any depth git commit -m "chore: refresh checksums"
tools/gen-checksums.shderives its file list from theSCRIPTS,UI_BUNDLE, andTESTNET_ADDONS_BUNDLEarrays inupdate-scripts.sh, so it always matches exactly what the updater fetches.
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
For issues with the Telcoin Network protocol or chain configuration, contact the Telcoin Association development team.
For issues with these setup scripts, raise them via the appropriate Telcoin Association channels.