From 34971fd94267540719d680968cb4cf818afdd8d1 Mon Sep 17 00:00:00 2001 From: POPPIN-FUMI Date: Thu, 21 May 2026 14:49:34 +0200 Subject: [PATCH] Release v2026.5.21.1448 --- cli/deno.json | 2 +- cmn/constants/version.ts | 2 +- deno.json | 2 +- deno.lock | 10 - sh/{2026.4.27.1539 => 2026.5.21.1448}/install | 2 +- sh/install | 2 +- .../rpc-gateway/src/ws/yellowstone_bridge.rs | 6 +- template/2026.4.27.1539/.claude/AGENTS.md | 30 -- .../.codex/AGENTS.md | 0 .../AGENTS.md | 0 .../ansible/cmn/add_solv.yml | 0 .../ansible/cmn/boost_performance.yml | 268 ++++++++++ .../ansible/cmn/build_agave.yml | 0 .../ansible/cmn/build_allnodes_jito.yml | 0 .../ansible/cmn/build_jito.yml | 0 .../ansible/cmn/build_solana.yml | 0 .../ansible/cmn/copy_restart_sh.yml | 0 .../ansible/cmn/copy_rpc_keys.yml | 0 .../ansible/cmn/create_user.yml | 0 .../ansible/cmn/deploy_nftables.yml | 336 ++++++++++++ .../ansible/cmn/disable_pwd_login.yml | 0 .../ansible/cmn/disable_swap.yml | 0 .../ansible/cmn/doublezero_connect.yml | 0 .../ansible/cmn/doublezero_deposit.yml | 0 .../ansible/cmn/doublezero_disconnect.yml | 0 .../ansible/cmn/doublezero_keygen.yml | 0 .../ansible/cmn/doublezero_publish.yml | 0 .../ansible/cmn/doublezero_start.yml | 0 .../ansible/cmn/doublezero_status.yml | 0 .../ansible/cmn/doublezero_stop.yml | 0 .../ansible/cmn/doublezero_update.yml | 0 .../ansible/cmn/doublezero_withdraw.yml | 0 .../cmn/files/99-vs2-solana-limits.conf | 9 + .../ansible/cmn/files/99-vs2-solana.conf | 16 + .../cmn/files/clickhouse-config.xml.j2 | 52 ++ .../ansible/cmn/files/clickhouse-users.xml.j2 | 60 +++ .../ansible/cmn/files/jetstreamer-backfill.sh | 151 ++++++ .../ansible/cmn/files/of1-index-sync.sh | 327 ++++++++++++ .../ansible/cmn/files/start-faithful.sh | 32 ++ .../ansible/cmn/files/vs2-irq-tune.service | 12 + .../ansible/cmn/files/vs2-irq-tune.sh | 72 +++ .../ansible/cmn/files/vs2-limits.conf | 5 + .../ansible/cmn/find_unmounted_disks.sh | 0 .../ansible/cmn/fix_permissions.yml | 0 .../ansible/cmn/install_hermes_stack.yml | 240 +++++++++ .../ansible/cmn/install_jetstreamer.yml | 494 ++++++++++++++++++ .../ansible/cmn/install_local_shred_relay.yml | 198 +++++++ .../ansible/cmn/install_min_package.yml | 0 .../ansible/cmn/install_package.yml | 0 .../ansible/cmn/install_rpc_gateway.yml | 379 ++++++++++++++ .../ansible/cmn/install_rust.yml | 0 .../ansible/cmn/install_solana.yml | 0 .../2026.5.21.1448/ansible/cmn/irq_tune.yml | 150 ++++++ .../ansible/cmn/mount-disks.yml | 0 .../ansible/cmn/mount_disks.yml | 0 .../ansible/cmn/of1_index_cache_sync.yml | 148 ++++++ .../ansible/cmn/optimize_node.yml | 46 ++ .../ansible/cmn/optimize_system.yml | 0 .../ansible/cmn/patch_sha256.yml | 0 .../ansible/cmn/restart_solv.yml | 0 .../ansible/cmn/rm_ledger.yml | 0 .../ansible/cmn/run_restarter.yml | 0 .../ansible/cmn/setup_agave_ufw.yml | 0 .../ansible/cmn/setup_doublezero.yml | 0 .../ansible/cmn/setup_logrotate.yml | 0 .../ansible/cmn/setup_node_exporter.yml | 0 .../ansible/cmn/setup_norestart.yml | 0 .../ansible/cmn/setup_ufw.yml | 0 .../ansible/cmn/setup_unstaked_identity.yml | 0 .../ansible/cmn/smt_disable.yml | 77 +++ .../cmn/software/install-firewall.yaml | 0 .../ansible/cmn/software/install-grafana.yml | 0 .../ansible/cmn/software/install-kafka.yml | 0 .../ansible/cmn/software/install-nginx.yaml | 0 .../cmn/software/install-node-exporter.yml | 0 .../cmn/software/install-prometheus.yml | 0 .../ansible/cmn/software/install-redis.yml | 0 .../ansible/cmn/software/install-tidb.yml | 0 .../cmn/software/install-wireguard.yaml | 0 .../ansible/cmn/start_firedancer.yml | 0 .../ansible/cmn/start_node.yml | 0 .../ansible/cmn/start_solv.yml | 0 .../ansible/cmn/stop_firedancer.yml | 0 .../ansible/cmn/stop_solv.yml | 0 .../ansible/cmn/tasks/format_and_mount.yml | 0 .../cmn/tasks/persist_nftables_ruleset.yml | 52 ++ .../ansible/cmn/update_firedancer.yml | 0 .../ansible/cmn/update_ubuntu.yml | 0 .../ansible/cmn/wget_snapshot.yml | 0 .../devnet-rpc/create-start-validator-sh.yml | 0 .../ansible/devnet-rpc/geyser_build.yml | 0 .../devnet-rpc/geyser_richat_build.yml | 0 .../ansible/devnet-rpc/init.yml | 0 .../ansible/devnet-rpc/install_agave.yml | 0 .../ansible/devnet-rpc/install_jito.yml | 0 .../ansible/devnet-rpc/install_richat.yml | 0 .../ansible/devnet-rpc/install_solana.yml | 0 .../ansible/devnet-rpc/restart_node.yml | 0 .../ansible/devnet-rpc/setup_firedancer.yml | 0 .../ansible/devnet-rpc/setup_solv_service.yml | 0 .../ansible/devnet-rpc/start_node.yml | 0 .../ansible/devnet-rpc/stop_node.yml | 0 .../ansible/devnet-rpc/update_geyser.yml | 0 .../devnet-rpc/update_startup_config.yml | 0 .../ansible/mainnet-hermes/init.yml | 17 + .../ansible/mainnet-hermes/restart_node.yml | 21 + .../ansible/mainnet-hermes/start_node.yml | 23 + .../ansible/mainnet-hermes/stop_node.yml | 20 + .../ansible/mainnet-hermes/update_hermes.yml | 6 + .../ansible/mainnet-pythnet/build_pythnet.yml | 77 +++ .../create-start-pythnet-sh.yml | 13 + .../ansible/mainnet-pythnet/gen_identity.yml | 31 ++ .../ansible/mainnet-pythnet/init.yml | 23 + .../mainnet-pythnet/install_package.yml | 23 + .../ansible/mainnet-pythnet/install_rust.yml | 19 + .../ansible/mainnet-pythnet/mount_disks.yml | 82 +++ .../mainnet-pythnet/optimize_pythnet.yml | 27 + .../ansible/mainnet-pythnet/restart_node.yml | 9 + .../mainnet-pythnet/setup-pythnet-service.yml | 32 ++ .../ansible/mainnet-pythnet/start_node.yml | 10 + .../ansible/mainnet-pythnet/stop_node.yml | 9 + .../mainnet-pythnet/update_pythnet.yml | 15 + .../ansible/mainnet-rpc/add_solv.yml | 0 .../ansible/mainnet-rpc/allow_ufw.yml | 0 .../mainnet-rpc/configure_hugetlbfs.yml | 0 .../ansible/mainnet-rpc/copy_keys.yml | 0 .../mainnet-rpc/create-start-validator-sh.yml | 0 .../ansible/mainnet-rpc/create-symlink.yml | 0 .../fail2ban_solana_rate_limit.yml | 0 .../ansible/mainnet-rpc/fail2ban_sshd.yml | 0 .../ansible/mainnet-rpc/geyser_build.yml | 0 .../mainnet-rpc/geyser_richat_build.yml | 0 .../ansible/mainnet-rpc/init-old.yml | 0 .../ansible/mainnet-rpc/init.yml | 7 + .../mainnet-rpc/init_richat_geyser.yml | 0 .../ansible/mainnet-rpc/install_agave.yml | 0 .../ansible/mainnet-rpc/install_jito.yml | 0 .../ansible/mainnet-rpc/install_of1.yml | 0 .../mainnet-rpc/install_of1_service.yml | 0 .../ansible/mainnet-rpc/install_package.yml | 0 .../ansible/mainnet-rpc/install_richat.yml | 0 .../ansible/mainnet-rpc/install_rust.yml | 0 .../ansible/mainnet-rpc/install_solana.yml | 0 .../ansible/mainnet-rpc/mount_disks.yml | 0 .../ansible/mainnet-rpc/optimize_system.yml | 0 .../ansible/mainnet-rpc/restart_node.yml | 0 .../ansible/mainnet-rpc/run_restarter.yml | 0 .../mainnet-rpc/run_snapshot_finder.yml | 0 .../mainnet-rpc/setup-solv-service.yml | 0 .../ansible/mainnet-rpc/setup_firedancer.yml | 0 .../ansible/mainnet-rpc/setup_logrotate.yml | 0 .../ansible/mainnet-rpc/setup_norestart.yml | 0 .../ansible/mainnet-rpc/setup_ufw.yml | 0 .../mainnet-rpc/start-solv-service.yml | 0 .../ansible/mainnet-rpc/start_firedancer.yml | 0 .../ansible/mainnet-rpc/start_node.yml | 0 .../ansible/mainnet-rpc/start_solv.yml | 0 .../ansible/mainnet-rpc/stop_firedancer.yml | 0 .../ansible/mainnet-rpc/stop_node.yml | 0 .../ansible/mainnet-rpc/stop_solv.yml | 0 .../ansible/mainnet-rpc/update_geyser.yml | 0 .../mainnet-rpc/update_richat_config.yml | 62 +++ .../mainnet-rpc/update_startup_config.yml | 0 .../ansible/mainnet-rpc/update_ubuntu.yml | 0 .../mainnet-validator/configure_hugetlbfs.yml | 0 .../ansible/mainnet-validator/copy_keys.yml | 0 .../mainnet-validator/copy_restart_sh.yml | 0 .../create-start-validator-sh.yml | 0 .../mainnet-validator/create_overrides.yml | 0 .../deploy-start-validator-sh.yml | 0 .../fail2ban_solana_rate_limit.yml | 0 .../mainnet-validator/init-allnodes-jito.yml | 0 .../mainnet-validator/init-firedancer.yml | 0 .../ansible/mainnet-validator/init-jito.yml | 0 .../ansible/mainnet-validator/init.yml | 0 .../mainnet-validator/install_agave.yml | 0 .../install_allnodes_jito.yml | 0 .../mainnet-validator/install_jito.yml | 0 .../mainnet-validator/install_rust.yml | 0 .../mainnet-validator/install_solana.yml | 0 .../mainnet-validator/nodowntime_migrate.yml | 0 .../mainnet-validator/restart_node.yml | 0 .../mainnet-validator/run_snapshot_finder.yml | 0 .../mainnet-validator/set_identity_key.yml | 0 .../set_identity_to_active.yml | 0 .../mainnet-validator/set_unstaked_key.yml | 0 .../mainnet-validator/setup_fb_ufw.yml | 0 .../mainnet-validator/setup_firedancer.yml | 0 .../mainnet-validator/setup_logrotate.yml | 0 .../mainnet-validator/setup_solv_service.yml | 0 .../ansible/mainnet-validator/setup_ufw.yml | 0 .../mainnet-validator/start-solv-service.yml | 0 .../mainnet-validator/start_firedancer.yml | 0 .../ansible/mainnet-validator/start_node.yml | 0 .../ansible/mainnet-validator/start_solv.yml | 0 .../mainnet-validator/stop_firedancer.yml | 0 .../ansible/mainnet-validator/stop_node.yml | 0 .../ansible/mainnet-validator/stop_solv.yml | 0 .../switch_off_firedancer_identity.yml | 0 .../mainnet-validator/switch_off_identity.yml | 0 .../switch_on_firedancer_identity.yml | 0 .../mainnet-validator/switch_on_identity.yml | 0 .../mainnet-validator/update_firedancer.yml | 0 .../update_startup_config.yml | 0 .../testnet-rpc/create-start-validator-sh.yml | 0 .../ansible/testnet-rpc/geyser_build.yml | 0 .../testnet-rpc/geyser_richat_build.yml | 0 .../ansible/testnet-rpc/init.yml | 0 .../ansible/testnet-rpc/install_agave.yml | 0 .../ansible/testnet-rpc/install_jito.yml | 0 .../ansible/testnet-rpc/install_richat.yml | 0 .../ansible/testnet-rpc/install_solana.yml | 0 .../ansible/testnet-rpc/restart_node.yml | 0 .../ansible/testnet-rpc/setup_firedancer.yml | 0 .../testnet-rpc/setup_solv_service.yml | 0 .../ansible/testnet-rpc/start_node.yml | 0 .../ansible/testnet-rpc/stop_node.yml | 0 .../ansible/testnet-rpc/update_firedancer.yml | 0 .../ansible/testnet-rpc/update_geyser.yml | 0 .../testnet-rpc/update_startup_config.yml | 0 .../ansible/testnet-rpc/wget_snapshot.yml | 0 .../ansible/testnet-validator/add_solv.yml | 0 .../change_identity_and_restart.yml | 0 .../ansible/testnet-validator/copy_keys.yml | 0 .../create-start-validator-sh-agave.yml | 0 .../create-start-validator-sh-jito.yml | 0 .../deploy-start-validator-sh.yml | 0 .../ansible/testnet-validator/init-agave.yml | 0 .../testnet-validator/init-firedancer.yml | 0 .../ansible/testnet-validator/init.yml | 0 .../testnet-validator/install_agave.yml | 0 .../testnet-validator/install_firedancer.yml | 0 .../testnet-validator/install_jito.yml | 0 .../testnet-validator/install_solana.yml | 0 .../testnet-validator/nodowntime_migrate.yml | 0 .../restart_agave_with_rm_ledger.yml | 0 .../testnet-validator/restart_firedancer.yml | 0 .../restart_firedancer_with_rm_ledger.yml | 0 .../testnet-validator/restart_node.yml | 0 .../testnet-validator/restart_solv.yml | 0 .../ansible/testnet-validator/rm_ledger.yml | 0 .../testnet-validator/set_identity_key.yml | 0 .../set_identity_to_active.yml | 0 .../testnet-validator/set_unstaked_key.yml | 0 .../ansible/testnet-validator/setup_agave.yml | 0 .../testnet-validator/setup_agave_ufw.yml | 0 .../testnet-validator/setup_firedancer.yml | 0 .../setup_firedancer_agave.yml | 0 .../setup_firedancer_jito.yml | 0 .../setup_snapshot_finder.yml | 0 .../testnet-validator/setup_solv_service.yml | 0 .../setup_solv_service_init.yml | 0 .../testnet-validator/start_firedancer.yml | 0 .../ansible/testnet-validator/start_node.yml | 0 .../ansible/testnet-validator/start_solv.yml | 0 .../testnet-validator/stop_firedancer.yml | 0 .../ansible/testnet-validator/stop_node.yml | 0 .../ansible/testnet-validator/stop_solv.yml | 0 .../switch_off_firedancer_identity.yml | 0 .../testnet-validator/switch_off_identity.yml | 0 .../switch_on_firedancer_identity.yml | 0 .../testnet-validator/switch_on_identity.yml | 0 .../testnet-validator/update_firedancer.yml | 0 .../update_startup_config.yml | 0 .../jinja/cmn/files/cf_ipv4.yml | 31 ++ .../jinja/cmn/nftables/00-ban-check.nft.j2 | 4 + .../jinja/cmn/nftables/00-sets-common.nft.j2 | 27 + .../jinja/cmn/nftables/01-mgmt-allow.nft.j2 | 12 + .../jinja/cmn/nftables/01-ssh-accept.nft.j2 | 9 + .../jinja/cmn/nftables/02-public-ports.nft.j2 | 10 + .../cmn/nftables/03-restricted-ports.nft.j2 | 19 + .../jinja/cmn/nftables/nftables.conf.j2 | 49 ++ .../jinja/cmn/prometheus.yml | 0 .../jinja/cmn/restart.sh.j2 | 0 .../devnet-rpc/firedancer-config.toml.j2 | 0 .../jinja/devnet-rpc/firedancer.service.j2 | 0 .../jinja/devnet-rpc/geyser.json.j2 | 0 .../jinja/devnet-rpc/restart.sh.j2 | 0 .../jinja/devnet-rpc/solv-agave.service.j2 | 0 .../jinja/devnet-rpc/solv-agave3.service.j2 | 0 .../jinja/devnet-rpc/solv.service.j2 | 0 .../jinja/devnet-rpc/start-firedancer.sh.j2 | 0 .../jinja/devnet-rpc/start-validator.sh.j2 | 0 .../jinja/mainnet-hermes/beacon.service.j2 | 25 + .../jinja/mainnet-hermes/hermes.service.j2 | 23 + .../mainnet-hermes/nats-server.service.j2 | 16 + .../jinja/mainnet-pythnet/pythnet.service.j2 | 18 + .../jinja/mainnet-pythnet/start-pythnet.sh.j2 | 33 ++ .../mainnet-rpc/firedancer-config.toml.j2 | 0 .../jinja/mainnet-rpc/firedancer.service.j2 | 0 .../jinja/mainnet-rpc/generate_configs.sh.j2 | 0 .../jinja/mainnet-rpc/geyser-richat.json.j2 | 0 .../jinja/mainnet-rpc/geyser.json.j2 | 0 .../jinja/mainnet-rpc/restart.sh.j2 | 0 .../jinja/mainnet-rpc/richat-setting.yml.j2 | 26 +- .../jinja/mainnet-rpc/richat.service.j2 | 0 .../jinja/mainnet-rpc/solv-agave.service.j2 | 0 .../jinja/mainnet-rpc/solv-agave3.service.j2 | 0 .../jinja/mainnet-rpc/solv.service.j2 | 0 .../mainnet-rpc/start-mainnet-rpc-grpc.sh.j2 | 0 .../mainnet-rpc/start-mainnet-rpc-index.sh.j2 | 0 .../mainnet-rpc/start-mainnet-rpc-tx.sh.j2 | 0 .../jinja/mainnet-rpc/start-mainnet-rpc.sh.j2 | 0 .../jinja/mainnet-rpc/start-validator.sh.j2 | 0 .../firedancer-config.toml.j2 | 0 .../mainnet-validator/firedancer.service.j2 | 0 .../jinja/mainnet-validator/overrides.yml.j2 | 0 .../mainnet-validator/relayer.service.j2 | 0 .../jinja/mainnet-validator/restart.sh.j2 | 0 .../mainnet-validator/solv-agave.service.j2 | 0 .../mainnet-validator/solv-agave3.service.j2 | 0 .../jinja/mainnet-validator/solv.service.j2 | 0 .../mainnet-validator/start-validator.sh.j2 | 0 .../testnet-rpc/firedancer-config.toml.j2 | 4 +- .../jinja/testnet-rpc/firedancer.service.j2 | 0 .../jinja/testnet-rpc/geyser.json.j2 | 0 .../jinja/testnet-rpc/restart.sh.j2 | 0 .../jinja/testnet-rpc/solv-agave.service.j2 | 0 .../jinja/testnet-rpc/solv-agave3.service.j2 | 0 .../jinja/testnet-rpc/solv.service.j2 | 0 .../jinja/testnet-rpc/start-firedancer.sh.j2 | 0 .../jinja/testnet-rpc/start-validator.sh.j2 | 4 +- .../firedancer-config-agave.toml.j2 | 4 +- .../firedancer-config-jito.toml.j2 | 4 +- .../firedancer-config.toml.j2 | 4 +- .../testnet-validator/firedancer.service.j2 | 0 .../jinja/testnet-validator/restart.sh.j2 | 0 .../jinja/testnet-validator/solv.service.j2 | 0 .../testnet-validator/start-firedancer.sh.j2 | 0 .../start-validator-agave.sh.j2 | 4 +- .../start-validator-jito.sh.j2 | 4 +- .../testnet-validator/start-validator.sh.j2 | 2 +- template/latest | 2 +- 333 files changed, 3975 insertions(+), 65 deletions(-) rename sh/{2026.4.27.1539 => 2026.5.21.1448}/install (99%) delete mode 100644 template/2026.4.27.1539/.claude/AGENTS.md rename template/{2026.4.27.1539 => 2026.5.21.1448}/.codex/AGENTS.md (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/AGENTS.md (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/add_solv.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/boost_performance.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/build_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/build_allnodes_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/build_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/build_solana.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/copy_restart_sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/copy_rpc_keys.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/create_user.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/deploy_nftables.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/disable_pwd_login.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/disable_swap.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_connect.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_deposit.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_disconnect.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_keygen.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_publish.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_start.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_status.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_stop.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_update.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/doublezero_withdraw.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana-limits.conf create mode 100644 template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana.conf create mode 100644 template/2026.5.21.1448/ansible/cmn/files/clickhouse-config.xml.j2 create mode 100644 template/2026.5.21.1448/ansible/cmn/files/clickhouse-users.xml.j2 create mode 100644 template/2026.5.21.1448/ansible/cmn/files/jetstreamer-backfill.sh create mode 100644 template/2026.5.21.1448/ansible/cmn/files/of1-index-sync.sh create mode 100644 template/2026.5.21.1448/ansible/cmn/files/start-faithful.sh create mode 100644 template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.service create mode 100644 template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.sh create mode 100644 template/2026.5.21.1448/ansible/cmn/files/vs2-limits.conf rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/find_unmounted_disks.sh (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/fix_permissions.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/install_hermes_stack.yml create mode 100644 template/2026.5.21.1448/ansible/cmn/install_jetstreamer.yml create mode 100644 template/2026.5.21.1448/ansible/cmn/install_local_shred_relay.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/install_min_package.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/install_package.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/install_rpc_gateway.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/install_rust.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/install_solana.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/irq_tune.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/mount-disks.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/mount_disks.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/of1_index_cache_sync.yml create mode 100644 template/2026.5.21.1448/ansible/cmn/optimize_node.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/optimize_system.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/patch_sha256.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/restart_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/rm_ledger.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/run_restarter.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/setup_agave_ufw.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/setup_doublezero.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/setup_logrotate.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/setup_node_exporter.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/setup_norestart.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/setup_ufw.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/setup_unstaked_identity.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/smt_disable.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-firewall.yaml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-grafana.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-kafka.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-nginx.yaml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-node-exporter.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-prometheus.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-redis.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-tidb.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/software/install-wireguard.yaml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/start_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/start_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/start_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/stop_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/stop_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/tasks/format_and_mount.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/cmn/tasks/persist_nftables_ruleset.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/update_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/update_ubuntu.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/cmn/wget_snapshot.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/create-start-validator-sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/geyser_build.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/geyser_richat_build.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/init.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/install_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/install_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/install_richat.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/install_solana.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/restart_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/setup_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/setup_solv_service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/start_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/stop_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/update_geyser.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/devnet-rpc/update_startup_config.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/mainnet-hermes/init.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-hermes/restart_node.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-hermes/start_node.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-hermes/stop_node.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-hermes/update_hermes.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/build_pythnet.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/create-start-pythnet-sh.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/gen_identity.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/init.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/install_package.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/install_rust.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/mount_disks.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/optimize_pythnet.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/restart_node.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/setup-pythnet-service.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/start_node.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/stop_node.yml create mode 100644 template/2026.5.21.1448/ansible/mainnet-pythnet/update_pythnet.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/add_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/allow_ufw.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/configure_hugetlbfs.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/copy_keys.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/create-start-validator-sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/create-symlink.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/fail2ban_solana_rate_limit.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/fail2ban_sshd.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/geyser_build.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/geyser_richat_build.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/init-old.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/init.yml (88%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/init_richat_geyser.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_of1.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_of1_service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_package.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_richat.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_rust.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/install_solana.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/mount_disks.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/optimize_system.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/restart_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/run_restarter.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/run_snapshot_finder.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/setup-solv-service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/setup_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/setup_logrotate.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/setup_norestart.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/setup_ufw.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/start-solv-service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/start_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/start_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/start_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/stop_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/stop_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/stop_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/update_geyser.yml (100%) create mode 100644 template/2026.5.21.1448/ansible/mainnet-rpc/update_richat_config.yml rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/update_startup_config.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-rpc/update_ubuntu.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/configure_hugetlbfs.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/copy_keys.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/copy_restart_sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/create-start-validator-sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/create_overrides.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/deploy-start-validator-sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/fail2ban_solana_rate_limit.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/init-allnodes-jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/init-firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/init-jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/init.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/install_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/install_allnodes_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/install_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/install_rust.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/install_solana.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/nodowntime_migrate.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/restart_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/run_snapshot_finder.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/set_identity_key.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/set_identity_to_active.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/set_unstaked_key.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/setup_fb_ufw.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/setup_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/setup_logrotate.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/setup_solv_service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/setup_ufw.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/start-solv-service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/start_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/start_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/start_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/stop_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/stop_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/stop_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/switch_off_firedancer_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/switch_off_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/switch_on_firedancer_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/switch_on_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/update_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/mainnet-validator/update_startup_config.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/create-start-validator-sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/geyser_build.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/geyser_richat_build.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/init.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/install_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/install_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/install_richat.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/install_solana.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/restart_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/setup_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/setup_solv_service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/start_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/stop_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/update_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/update_geyser.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/update_startup_config.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-rpc/wget_snapshot.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/add_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/change_identity_and_restart.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/copy_keys.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/create-start-validator-sh-agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/create-start-validator-sh-jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/deploy-start-validator-sh.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/init-agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/init-firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/init.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/install_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/install_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/install_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/install_solana.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/nodowntime_migrate.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/restart_agave_with_rm_ledger.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/restart_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/restart_firedancer_with_rm_ledger.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/restart_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/restart_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/rm_ledger.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/set_identity_key.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/set_identity_to_active.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/set_unstaked_key.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_agave_ufw.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_firedancer_agave.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_firedancer_jito.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_snapshot_finder.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_solv_service.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/setup_solv_service_init.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/start_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/start_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/start_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/stop_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/stop_node.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/stop_solv.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/switch_off_firedancer_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/switch_off_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/switch_on_firedancer_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/switch_on_identity.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/update_firedancer.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/ansible/testnet-validator/update_startup_config.yml (100%) create mode 100644 template/2026.5.21.1448/jinja/cmn/files/cf_ipv4.yml create mode 100644 template/2026.5.21.1448/jinja/cmn/nftables/00-ban-check.nft.j2 create mode 100644 template/2026.5.21.1448/jinja/cmn/nftables/00-sets-common.nft.j2 create mode 100644 template/2026.5.21.1448/jinja/cmn/nftables/01-mgmt-allow.nft.j2 create mode 100644 template/2026.5.21.1448/jinja/cmn/nftables/01-ssh-accept.nft.j2 create mode 100644 template/2026.5.21.1448/jinja/cmn/nftables/02-public-ports.nft.j2 create mode 100644 template/2026.5.21.1448/jinja/cmn/nftables/03-restricted-ports.nft.j2 create mode 100644 template/2026.5.21.1448/jinja/cmn/nftables/nftables.conf.j2 rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/cmn/prometheus.yml (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/cmn/restart.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/firedancer-config.toml.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/firedancer.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/geyser.json.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/restart.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/solv-agave.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/solv-agave3.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/solv.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/start-firedancer.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/devnet-rpc/start-validator.sh.j2 (100%) create mode 100644 template/2026.5.21.1448/jinja/mainnet-hermes/beacon.service.j2 create mode 100644 template/2026.5.21.1448/jinja/mainnet-hermes/hermes.service.j2 create mode 100644 template/2026.5.21.1448/jinja/mainnet-hermes/nats-server.service.j2 create mode 100644 template/2026.5.21.1448/jinja/mainnet-pythnet/pythnet.service.j2 create mode 100644 template/2026.5.21.1448/jinja/mainnet-pythnet/start-pythnet.sh.j2 rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/firedancer-config.toml.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/firedancer.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/generate_configs.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/geyser-richat.json.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/geyser.json.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/restart.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/richat-setting.yml.j2 (66%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/richat.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/solv-agave.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/solv-agave3.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/solv.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/start-mainnet-rpc-grpc.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/start-mainnet-rpc-index.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/start-mainnet-rpc-tx.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/start-mainnet-rpc.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-rpc/start-validator.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/firedancer-config.toml.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/firedancer.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/overrides.yml.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/relayer.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/restart.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/solv-agave.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/solv-agave3.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/solv.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/mainnet-validator/start-validator.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/firedancer-config.toml.j2 (98%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/firedancer.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/geyser.json.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/restart.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/solv-agave.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/solv-agave3.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/solv.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/start-firedancer.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-rpc/start-validator.sh.j2 (95%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/firedancer-config-agave.toml.j2 (93%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/firedancer-config-jito.toml.j2 (94%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/firedancer-config.toml.j2 (95%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/firedancer.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/restart.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/solv.service.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/start-firedancer.sh.j2 (100%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/start-validator-agave.sh.j2 (82%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/start-validator-jito.sh.j2 (88%) rename template/{2026.4.27.1539 => 2026.5.21.1448}/jinja/testnet-validator/start-validator.sh.j2 (95%) diff --git a/cli/deno.json b/cli/deno.json index a89adecf..24c5e848 100644 --- a/cli/deno.json +++ b/cli/deno.json @@ -1,6 +1,6 @@ { "name": "@slv/cli", - "version": "2026.5.5.1612", + "version": "2026.5.21.1448", "exports": "./dist/exe", "publish": { "include": [ diff --git a/cmn/constants/version.ts b/cmn/constants/version.ts index 5e5250c8..8097c380 100644 --- a/cmn/constants/version.ts +++ b/cmn/constants/version.ts @@ -2,7 +2,7 @@ // e.g.: VERSION + SOLANA CLI NAME + NETWORK = '0.0.1' only numbers and dots // SLV version -export const VERSION = '2026.5.5.1612' +export const VERSION = '2026.5.21.1448' // Component versions export const VERSION_SOLANA_TESTNET = '4.0.0-beta.2' diff --git a/deno.json b/deno.json index 620ecd28..850d2865 100644 --- a/deno.json +++ b/deno.json @@ -20,7 +20,7 @@ "docker:rm": "bash scripts/docker-systemd-rm.sh", "upload:script": "deno run -A cli/uploadScript.ts", "upload:exe": "deno run -A cli/uploadExe.ts", - "create:template": "tar -czf dist/template.tar.gz ./template/2026.5.5.1612", + "create:template": "tar -czf dist/template.tar.gz ./template/2026.5.21.1448", "generate:checksums": "deno run -A cli/generateChecksums.ts", "upload:template": "deno run -A cli/uploadTemplate.ts", "purge:cache": "deno run -A cmn/lib/purgeR2Cache.ts" diff --git a/deno.lock b/deno.lock index 0a0fc758..8a8d1b44 100644 --- a/deno.lock +++ b/deno.lock @@ -2,8 +2,6 @@ "version": "5", "specifiers": { "jsr:@elsoul/child-process@1.2.0": "1.2.0", - "jsr:@hono/hono@4.7.6": "4.7.6", - "jsr:@hono/hono@^4.6.13": "4.7.6", "jsr:@std/assert@0.224": "0.224.0", "jsr:@std/assert@1": "1.0.16", "jsr:@std/dotenv@0.225.2": "0.225.2", @@ -28,9 +26,6 @@ "@elsoul/child-process@1.2.0": { "integrity": "8148246ce496c17dbb007613d0e7addcdc71492ce6f7141872f5d7606de23389" }, - "@hono/hono@4.7.6": { - "integrity": "407da752c13d4644bb9d363f8349db7542e21abc3db8c13ca90cf69d8ab675ad" - }, "@std/assert@0.224.0": { "integrity": "8643233ec7aec38a940a8264a6e3eed9bfa44e7a71cc6b3c8874213ff401967f" }, @@ -1195,11 +1190,6 @@ "npm:mysql2@3.16.0" ], "members": { - "api/rpc-gateway": { - "dependencies": [ - "jsr:@hono/hono@^4.6.13" - ] - }, "cli": { "dependencies": [ "jsr:@std/assert@1", diff --git a/sh/2026.4.27.1539/install b/sh/2026.5.21.1448/install similarity index 99% rename from sh/2026.4.27.1539/install rename to sh/2026.5.21.1448/install index 7e6a0313..a64a101f 100644 --- a/sh/2026.4.27.1539/install +++ b/sh/2026.5.21.1448/install @@ -2,7 +2,7 @@ set -e -VERSION="2026.4.27.1539" +VERSION="2026.5.21.1448" BASE_URL="https://storage.slv.dev/slv" GRPC_TEST_URL_LINUX="https://storage.elsoul.nl/grpc_test" GRPC_TEST_URL_MAC="https://storage.elsoul.nl/grpc_test_mac" diff --git a/sh/install b/sh/install index 908a52f6..a64a101f 100644 --- a/sh/install +++ b/sh/install @@ -2,7 +2,7 @@ set -e -VERSION="2026.5.5.1612" +VERSION="2026.5.21.1448" BASE_URL="https://storage.slv.dev/slv" GRPC_TEST_URL_LINUX="https://storage.elsoul.nl/grpc_test" GRPC_TEST_URL_MAC="https://storage.elsoul.nl/grpc_test_mac" diff --git a/slv-plugins/rpc-gateway/src/ws/yellowstone_bridge.rs b/slv-plugins/rpc-gateway/src/ws/yellowstone_bridge.rs index 964628d6..0a3de2e5 100644 --- a/slv-plugins/rpc-gateway/src/ws/yellowstone_bridge.rs +++ b/slv-plugins/rpc-gateway/src/ws/yellowstone_bridge.rs @@ -32,9 +32,9 @@ pub enum BridgeError { } /// Per-call filter parsed out of the JSON-RPC params. Matches the -/// extended-`transactionSubscribe` shape used by web3.js Helius- -/// compatible clients but uses neutral names; see the Deno -/// gateway's `TxSubscribeFilter` for the historical mapping. +/// extended-`transactionSubscribe` shape used by web3.js clients +/// that support filter-based transaction subscriptions; see the +/// Deno gateway's `TxSubscribeFilter` for the historical mapping. #[derive(Debug, Default, Deserialize)] #[serde(default, rename_all = "camelCase")] pub struct TxSubscribeFilter { diff --git a/template/2026.4.27.1539/.claude/AGENTS.md b/template/2026.4.27.1539/.claude/AGENTS.md deleted file mode 100644 index 72a1532a..00000000 --- a/template/2026.4.27.1539/.claude/AGENTS.md +++ /dev/null @@ -1,30 +0,0 @@ -# Claude Agent Instructions — SLV Ansible Templates - -## ⚠️ SECURITY — PUBLIC OSS REPOSITORY -- **NEVER commit secrets, API keys, tokens, passwords, or private IPs** -- Review every diff before committing - -## Project -SLV (Solana Validator Launcher) Ansible playbooks. OSS: https://github.com/ValidatorsDAO/slv - -## Structure -- `ansible/cmn/` — Shared tasks -- `ansible/mainnet-rpc/` — Mainnet RPC (Index + gRPC Geyser) -- `ansible/mainnet-validator/` — Mainnet Validators -- `ansible/testnet-*` / `ansible/devnet-*` — Other networks -- `jinja/` — Jinja2 config templates - -## Key Variables -- `validator_type`: agave | jito | firedancer-agave | firedancer-jito | frankendancer -- `rpc_type`: "Index RPC" | "Geyser gRPC" | "Index RPC + gRPC" -- All passed as `extra_vars` at runtime - -## Agents -- **Cecil**: Validator specialist (mainnet-validator/, testnet-validator/) -- **Tina**: RPC specialist (mainnet-rpc/, testnet-rpc/, devnet-rpc/) -- **Cloud**: gRPC Geyser specialist (geyser files in rpc dirs) - -## Conventions -- `init.yml` = full node initialization from bare metal -- `build_solana.yml` preferred over `install_solana.yml` -- Ansible user: `solv` diff --git a/template/2026.4.27.1539/.codex/AGENTS.md b/template/2026.5.21.1448/.codex/AGENTS.md similarity index 100% rename from template/2026.4.27.1539/.codex/AGENTS.md rename to template/2026.5.21.1448/.codex/AGENTS.md diff --git a/template/2026.4.27.1539/AGENTS.md b/template/2026.5.21.1448/AGENTS.md similarity index 100% rename from template/2026.4.27.1539/AGENTS.md rename to template/2026.5.21.1448/AGENTS.md diff --git a/template/2026.4.27.1539/ansible/cmn/add_solv.yml b/template/2026.5.21.1448/ansible/cmn/add_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/add_solv.yml rename to template/2026.5.21.1448/ansible/cmn/add_solv.yml diff --git a/template/2026.5.21.1448/ansible/cmn/boost_performance.yml b/template/2026.5.21.1448/ansible/cmn/boost_performance.yml new file mode 100644 index 00000000..d6dd46d2 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/boost_performance.yml @@ -0,0 +1,268 @@ +--- +# CPU performance boost: kernel update (HWE) + amd_pstate=active +# + governor=performance + EPP=performance + boost=enabled + cpuidle disable +# + scaling_max_freq=cpuinfo_max_freq. +# +# Sets need_reboot=true when kernel/GRUB changes require a reboot. +- name: CPU performance boost optimization + hosts: all + become: yes + gather_facts: true + vars: + boost_marker_file: /var/lib/slv/.boost_applied + grub_default: /etc/default/grub + amd_pstate_mode: "{{ amd_pstate_mode | default('active') }}" + min_kernel_major: 6 + min_kernel_minor: 14 + tasks: + - name: Ensure /var/lib/slv exists + ansible.builtin.file: + path: /var/lib/slv + state: directory + mode: "0755" + + - name: Detect CPU vendor (AMD / Intel) + ansible.builtin.shell: grep -m1 'vendor_id' /proc/cpuinfo | awk '{print $3}' + register: cpu_vendor + changed_when: false + + - name: Detect CPU model + ansible.builtin.shell: | + lscpu | awk -F: '/Model name/ {gsub(/^[ \t]+/, "", $2); print $2; exit}' + register: cpu_model + changed_when: false + + - name: Set is_amd / is_intel facts + ansible.builtin.set_fact: + is_amd: "{{ 'AMD' in cpu_vendor.stdout or ('amd' in (cpu_model.stdout | lower)) or ('epyc' in (cpu_model.stdout | lower)) or ('ryzen' in (cpu_model.stdout | lower)) }}" + is_intel: "{{ 'GenuineIntel' in cpu_vendor.stdout }}" + + - name: Detect current kernel version + ansible.builtin.command: uname -r + register: kernel_version + changed_when: false + + - name: Parse kernel major.minor + ansible.builtin.set_fact: + kernel_major: "{{ (kernel_version.stdout.split('.')[0] | int) }}" + kernel_minor: "{{ (kernel_version.stdout.split('.')[1] | int) }}" + + - name: Determine if kernel upgrade needed (AMD CPUs need 6.14+) + ansible.builtin.set_fact: + kernel_upgrade_needed: "{{ is_amd and ((kernel_major | int) < min_kernel_major or ((kernel_major | int) == min_kernel_major and (kernel_minor | int) < min_kernel_minor)) }}" + + - name: Show kernel decision + ansible.builtin.debug: + msg: "kernel={{ kernel_version.stdout }} amd={{ is_amd }} upgrade_needed={{ kernel_upgrade_needed }}" + + - name: apt-get update + ansible.builtin.apt: + update_cache: yes + retries: 3 + delay: 5 + + - name: Upgrade existing packages (current kernel security & userland) + ansible.builtin.apt: + upgrade: dist + force_apt_get: yes + environment: + DEBIAN_FRONTEND: noninteractive + retries: 3 + delay: 10 + register: apt_upgrade_result + + - name: Install HWE kernel + linux-tools (Ubuntu 24.04, AMD only when < 6.14) + ansible.builtin.apt: + name: + - linux-image-generic-hwe-24.04 + - linux-tools-generic-hwe-24.04 + - linux-tools-common + state: present + environment: + DEBIAN_FRONTEND: noninteractive + when: + - ansible_distribution == 'Ubuntu' + - ansible_distribution_version is version('24.04', '>=') + - kernel_upgrade_needed + register: hwe_install + retries: 3 + delay: 10 + + - name: Install cpupower / linux-tools (always) + ansible.builtin.apt: + name: + - linux-tools-common + state: present + environment: + DEBIAN_FRONTEND: noninteractive + ignore_errors: true + + - name: Add amd_pstate / processor.max_cstate / cpufreq.default_governor to GRUB (AMD only) + ansible.builtin.shell: | + set -e + # shellcheck disable=SC1091 + . /etc/default/grub + CURRENT="${GRUB_CMDLINE_LINUX_DEFAULT:-}" + CLEAN=$(echo "$CURRENT" \ + | sed 's/amd_pstate=[^ ]*//g; s/processor\.max_cstate=[^ ]*//g; s/cpufreq\.default_governor=[^ ]*//g' \ + | sed 's/ */ /g; s/^ //; s/ $//') + NEW="$CLEAN amd_pstate={{ amd_pstate_mode }} processor.max_cstate=0 cpufreq.default_governor=performance" + NEW=$(echo "$NEW" | sed 's/ */ /g; s/^ //; s/ $//') + if [ "$NEW" != "$CURRENT" ]; then + sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"$NEW\"|" {{ grub_default }} + update-grub + echo CHANGED + else + echo UNCHANGED + fi + args: + executable: /bin/bash + register: amd_grub + changed_when: "'CHANGED' in amd_grub.stdout" + when: is_amd + + - name: Try to load amd_pstate driver at runtime (best effort) + ansible.builtin.shell: | + modprobe amd_pstate 2>/dev/null || true + sleep 1 + [ -d /sys/devices/system/cpu/cpu0/cpufreq ] && echo PRESENT || echo MISSING + args: + executable: /bin/bash + register: cpufreq_present + changed_when: false + when: is_amd + + - name: Apply amd_pstate runtime mode (if sysfs present) + ansible.builtin.shell: | + if [ -f /sys/devices/system/cpu/amd_pstate/status ]; then + echo {{ amd_pstate_mode }} > /sys/devices/system/cpu/amd_pstate/status 2>/dev/null || true + fi + args: + executable: /bin/bash + when: is_amd + ignore_errors: true + + - name: Apply governor=performance to all CPUs + ansible.builtin.shell: | + for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do + [ -w "$f" ] && echo performance > "$f" + done + true + args: + executable: /bin/bash + ignore_errors: true + + - name: Apply EPP=performance to all CPUs + ansible.builtin.shell: | + for f in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do + [ -w "$f" ] && echo performance > "$f" + done + true + args: + executable: /bin/bash + ignore_errors: true + + - name: Enable boost / disable Intel turbo no_turbo + ansible.builtin.shell: | + [ -f /sys/devices/system/cpu/cpufreq/boost ] && echo 1 > /sys/devices/system/cpu/cpufreq/boost || true + [ -f /sys/devices/system/cpu/intel_pstate/no_turbo ] && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo || true + true + args: + executable: /bin/bash + ignore_errors: true + + - name: Disable cpuidle states + ansible.builtin.shell: | + for f in /sys/devices/system/cpu/cpu*/cpuidle/state*/disable; do + [ -w "$f" ] && echo 1 > "$f" + done + true + args: + executable: /bin/bash + ignore_errors: true + + - name: Set scaling_max_freq = cpuinfo_max_freq + ansible.builtin.shell: | + for cpu in /sys/devices/system/cpu/cpu*/cpufreq; do + max=$(cat "$cpu/cpuinfo_max_freq" 2>/dev/null || echo "") + [ -n "$max" ] && [ -w "$cpu/scaling_max_freq" ] && echo "$max" > "$cpu/scaling_max_freq" + done + true + args: + executable: /bin/bash + ignore_errors: true + + - name: Install systemd persistence service (apply boost on every boot) + ansible.builtin.copy: + dest: /usr/local/bin/slv-cpu-boost.sh + mode: "0755" + content: | + #!/bin/bash + # SLV CPU boost runtime apply — runs on every boot. + set +e + if [ -f /sys/devices/system/cpu/amd_pstate/status ]; then + echo {{ amd_pstate_mode }} > /sys/devices/system/cpu/amd_pstate/status 2>/dev/null + fi + for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do + [ -w "$f" ] && echo performance > "$f" + done + for f in /sys/devices/system/cpu/cpu*/cpufreq/energy_performance_preference; do + [ -w "$f" ] && echo performance > "$f" + done + [ -f /sys/devices/system/cpu/cpufreq/boost ] && echo 1 > /sys/devices/system/cpu/cpufreq/boost + [ -f /sys/devices/system/cpu/intel_pstate/no_turbo ] && echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo + for f in /sys/devices/system/cpu/cpu*/cpuidle/state*/disable; do + [ -w "$f" ] && echo 1 > "$f" + done + for cpu in /sys/devices/system/cpu/cpu*/cpufreq; do + max=$(cat "$cpu/cpuinfo_max_freq" 2>/dev/null || echo "") + [ -n "$max" ] && [ -w "$cpu/scaling_max_freq" ] && echo "$max" > "$cpu/scaling_max_freq" + done + exit 0 + + - name: Install slv-cpu-boost systemd unit + ansible.builtin.copy: + dest: /etc/systemd/system/slv-cpu-boost.service + mode: "0644" + content: | + [Unit] + Description=SLV CPU boost runtime apply + After=multi-user.target + + [Service] + Type=oneshot + ExecStart=/usr/local/bin/slv-cpu-boost.sh + RemainAfterExit=yes + + [Install] + WantedBy=multi-user.target + register: boost_unit + + - name: Reload systemd + ansible.builtin.systemd: + daemon_reload: yes + when: boost_unit is changed + + - name: Enable & start slv-cpu-boost.service + ansible.builtin.systemd: + name: slv-cpu-boost.service + enabled: yes + state: started + + - name: Mark boost applied + ansible.builtin.copy: + dest: "{{ boost_marker_file }}" + content: "applied at {{ ansible_date_time.iso8601 | default(lookup('pipe', 'date -Iseconds')) }} mode={{ amd_pstate_mode }} kernel={{ kernel_version.stdout }}\n" + mode: "0644" + + - name: Set need_reboot fact (HWE kernel install or AMD GRUB change) + ansible.builtin.set_fact: + boost_reboot_required: "{{ (hwe_install is defined and hwe_install is changed) or (amd_grub is defined and amd_grub is changed) }}" + need_reboot: "{{ (need_reboot | default(false)) or (hwe_install is defined and hwe_install is changed) or (amd_grub is defined and amd_grub is changed) }}" + + - name: Report boost status + ansible.builtin.debug: + msg: >- + {{ 'Boost applied — kernel/GRUB changed, reboot required' + if boost_reboot_required + else 'Boost applied — runtime only, no reboot needed' }} diff --git a/template/2026.4.27.1539/ansible/cmn/build_agave.yml b/template/2026.5.21.1448/ansible/cmn/build_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/build_agave.yml rename to template/2026.5.21.1448/ansible/cmn/build_agave.yml diff --git a/template/2026.4.27.1539/ansible/cmn/build_allnodes_jito.yml b/template/2026.5.21.1448/ansible/cmn/build_allnodes_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/build_allnodes_jito.yml rename to template/2026.5.21.1448/ansible/cmn/build_allnodes_jito.yml diff --git a/template/2026.4.27.1539/ansible/cmn/build_jito.yml b/template/2026.5.21.1448/ansible/cmn/build_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/build_jito.yml rename to template/2026.5.21.1448/ansible/cmn/build_jito.yml diff --git a/template/2026.4.27.1539/ansible/cmn/build_solana.yml b/template/2026.5.21.1448/ansible/cmn/build_solana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/build_solana.yml rename to template/2026.5.21.1448/ansible/cmn/build_solana.yml diff --git a/template/2026.4.27.1539/ansible/cmn/copy_restart_sh.yml b/template/2026.5.21.1448/ansible/cmn/copy_restart_sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/copy_restart_sh.yml rename to template/2026.5.21.1448/ansible/cmn/copy_restart_sh.yml diff --git a/template/2026.4.27.1539/ansible/cmn/copy_rpc_keys.yml b/template/2026.5.21.1448/ansible/cmn/copy_rpc_keys.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/copy_rpc_keys.yml rename to template/2026.5.21.1448/ansible/cmn/copy_rpc_keys.yml diff --git a/template/2026.4.27.1539/ansible/cmn/create_user.yml b/template/2026.5.21.1448/ansible/cmn/create_user.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/create_user.yml rename to template/2026.5.21.1448/ansible/cmn/create_user.yml diff --git a/template/2026.5.21.1448/ansible/cmn/deploy_nftables.yml b/template/2026.5.21.1448/ansible/cmn/deploy_nftables.yml new file mode 100644 index 00000000..455af8d2 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/deploy_nftables.yml @@ -0,0 +1,336 @@ +--- +# Deploy an nftables ruleset on a single host. OSS-clean version: no +# hardcoded management IPs, no external service integrations. All +# allow lists come from inventory or `-e` extra-vars. +# +# Required inventory variables (deploy refuses to run without them): +# mgmt_ips_v4 list of IPv4 addresses that should retain full +# access. ★ Lockout warning ★ — this list MUST +# include the operator's own source IP, otherwise the +# SSH session will be dropped the moment the ruleset +# is applied. +# +# Optional inventory variables (all default to empty / sensible): +# mgmt_ips_v6 list of IPv6 mgmt addresses +# ban_ips_v4 list of IPv4 (or CIDR) to block at highest priority +# ssh_port SSH listen port (default 22) +# public_tcp_ports list of TCP ports/ranges to accept from anywhere +# public_udp_ports list of UDP ports/ranges to accept from anywhere +# restricted_ports list of dicts — each declares a port + tcp/udp +# flags + an allow_ipv4 list. Example: +# restricted_ports: +# - port: 8899 +# tcp: true +# allow_ipv4: ["198.51.100.10"] +# - port: "8000-8020" +# tcp: true +# udp: true +# allow_ipv4: [] +# disable_ufw true (default) — stop ufw to avoid conflict +# preserve_existing true (default) — keep current allowAll_v4 / +# banAll_v4 elements across re-runs +# +# Usage: +# ansible-playbook -i \ +# template/.../ansible/cmn/deploy_nftables.yml \ +# -e '{"mgmt_ips_v4": ["1.2.3.4"], "public_tcp_ports": [7575], "public_udp_ports": [8999]}' + +- name: Deploy slv-nftables ruleset (atomic + rollback-safe) + hosts: all + gather_facts: yes + become: yes + + # Internal-state vars only. All operator-tunable inputs + # (mgmt_ips_v4, public_tcp_ports, restricted_ports, …) come from + # inventory; do NOT put their defaults here — playbook-vars override + # inventory-vars, so any default here silently masks what the + # operator wrote in inventory.yml. Default values are applied + # inline via the `| default(...)` filter at point of use. + vars: + nft_conf_path: /etc/nftables.conf + nft_frag_dir: /etc/nftables.d + nft_sets_dir: /etc/nftables.sets.d + nft_template_root: "~/.slv/cmn/nftables" + nft_python_interpreter: "{{ ansible_facts.python.executable | default('/usr/bin/python3') }}" + + pre_tasks: + # Load the optional shared variable files. `cf_ipv4.yml` ships with + # the slv templates (refreshed against https://www.cloudflare.com/ips-v4) + # — operators referring to `{{ cf_ipv4 | default([]) }}` in inventory + # get the current list without copying it by hand. The lookup is + # best-effort: missing file → variable just stays undefined, default + # filter handles the empty case. + - name: Load optional cmn/files/cf_ipv4.yml (Cloudflare IPv4 ranges) + ansible.builtin.include_vars: + file: "{{ lookup('env', 'HOME') }}/.slv/cmn/files/cf_ipv4.yml" + failed_when: false + delegate_to: localhost + run_once: true + become: false + + # Hard fail-safe: empty mgmt_ips_v4 means the operator likely forgot + # to pass their IP, which would lock them out the moment we apply. + # Refuse to proceed. + - name: Sanity-check that mgmt_ips_v4 is non-empty + ansible.builtin.assert: + that: + - (mgmt_ips_v4 | default([]) | length) > 0 + fail_msg: >- + mgmt_ips_v4 is empty. Applying nftables now would drop your + SSH session. Pass at least one admin IP via inventory or + `-e '{"mgmt_ips_v4": ["YOUR.IP.HERE"]}'`. + + - name: Ensure nftables package + ansible.builtin.apt: + name: nftables + state: present + update_cache: yes + cache_valid_time: 3600 + + - name: Optionally disable ufw to avoid rule conflicts + when: (disable_ufw | default(true)) | bool + ansible.builtin.service: + name: ufw + state: stopped + enabled: false + ignore_errors: true + + tasks: + # ---- Preserve existing runtime additions ---------------------------- + - name: Initialize preserved set fact lists + ansible.builtin.set_fact: + existing_allowAll_v4: [] + existing_banAll_v4: [] + existing_restricted_sets: {} + + - name: Capture existing allowAll_v4 elements + when: (preserve_existing | default(true)) | bool + ansible.builtin.shell: | + set -o pipefail + nft list set inet filter allowAll_v4 2>/dev/null | {{ nft_python_interpreter }} - <<'PY' + import re, sys + data = sys.stdin.read() + m = re.search(r'elements\s*=\s*\{([^}]*)\}', data, re.S) + if not m: + sys.exit(0) + for tok in m.group(1).split(','): + tok = tok.strip() + if tok: + print(tok) + PY + args: { executable: /bin/bash } + register: current_allowAll_v4 + changed_when: false + failed_when: false + + - name: Capture existing banAll_v4 elements + when: (preserve_existing | default(true)) | bool + ansible.builtin.shell: | + set -o pipefail + nft list set inet filter banAll_v4 2>/dev/null | {{ nft_python_interpreter }} - <<'PY' + import re, sys + data = sys.stdin.read() + m = re.search(r'elements\s*=\s*\{([^}]*)\}', data, re.S) + if not m: + sys.exit(0) + for tok in m.group(1).split(','): + tok = tok.strip() + if tok: + print(tok) + PY + args: { executable: /bin/bash } + register: current_banAll_v4 + changed_when: false + failed_when: false + + - name: Set preserved seed lists + ansible.builtin.set_fact: + seed_allowAll_v4: >- + {{ + ((current_allowAll_v4.stdout_lines | default([]) + if (current_allowAll_v4.rc | default(1) == 0) else [])) + | map('trim') | reject('equalto','') | list | unique + }} + seed_banAll_v4: >- + {{ + ((current_banAll_v4.stdout_lines | default([]) + if (current_banAll_v4.rc | default(1) == 0) else []) + + (ban_ips_v4 | default([]))) + | map('trim') | reject('equalto','') | list | unique + }} + + # ---- Render fragments ------------------------------------------------ + - name: Ensure fragment directories exist + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: root + group: root + mode: "0755" + loop: + - "{{ nft_frag_dir }}" + - "{{ nft_sets_dir }}" + + - name: Backup current nftables.conf if present + ansible.builtin.copy: + src: "{{ nft_conf_path }}" + dest: "{{ nft_conf_path }}.bak" + owner: root + group: root + mode: "0644" + remote_src: true + failed_when: false + + - name: Clean old fragments + ansible.builtin.shell: | + rm -f {{ nft_frag_dir }}/*.nft {{ nft_sets_dir }}/*.nft + mkdir -p {{ nft_frag_dir }} {{ nft_sets_dir }} + changed_when: true + + - name: Render base + sets + rules + block: + - name: Render nftables.conf (base) + ansible.builtin.template: + src: "{{ nft_template_root }}/nftables.conf.j2" + dest: "{{ nft_conf_path }}" + owner: root + group: root + mode: "0644" + + - name: Render common set definitions + ansible.builtin.template: + src: "{{ nft_template_root }}/00-sets-common.nft.j2" + dest: "{{ nft_sets_dir }}/00-sets-common.nft" + owner: root + group: root + mode: "0644" + + - name: Render ban check + ansible.builtin.template: + src: "{{ nft_template_root }}/00-ban-check.nft.j2" + dest: "{{ nft_frag_dir }}/00-ban-check.nft" + owner: root + group: root + mode: "0644" + + - name: Render mgmt allow-all + ansible.builtin.template: + src: "{{ nft_template_root }}/01-mgmt-allow.nft.j2" + dest: "{{ nft_frag_dir }}/01-mgmt-allow.nft" + owner: root + group: root + mode: "0644" + + - name: Render SSH safety net + ansible.builtin.template: + src: "{{ nft_template_root }}/01-ssh-accept.nft.j2" + dest: "{{ nft_frag_dir }}/01-ssh-accept.nft" + owner: root + group: root + mode: "0644" + + - name: Render public ports + ansible.builtin.template: + src: "{{ nft_template_root }}/02-public-ports.nft.j2" + dest: "{{ nft_frag_dir }}/02-public-ports.nft" + owner: root + group: root + mode: "0644" + + - name: Render restricted ports + ansible.builtin.template: + src: "{{ nft_template_root }}/03-restricted-ports.nft.j2" + dest: "{{ nft_frag_dir }}/03-restricted-ports.nft" + owner: root + group: root + mode: "0644" + + - name: Validate the rendered config (dry-run) + ansible.builtin.command: "nft -c -f {{ nft_conf_path }}" + changed_when: false + + rescue: + - name: Restore previous nftables.conf + ansible.builtin.copy: + src: "{{ nft_conf_path }}.bak" + dest: "{{ nft_conf_path }}" + owner: root + group: root + mode: "0644" + remote_src: true + failed_when: false + + - name: Fail after restore + ansible.builtin.fail: + msg: "nftables config validation failed; previous config restored." + + - name: Apply the new ruleset atomically + ansible.builtin.command: "nft -f {{ nft_conf_path }}" + + # ---- Seed set elements (idempotent — `nft add` is a no-op on dup) --- + - name: Seed banAll_v4 + vars: { ip_list: "{{ seed_banAll_v4 | default([]) }}" } + when: ip_list | length > 0 + ansible.builtin.shell: | + set -e + for ip in {{ ip_list | join(' ') }}; do + nft add element inet filter banAll_v4 { $ip } || true + done + args: { executable: /bin/bash } + changed_when: true + + - name: Seed allowAll_v4 + vars: { ip_list: "{{ seed_allowAll_v4 | default([]) }}" } + when: ip_list | length > 0 + ansible.builtin.shell: | + set -e + for ip in {{ ip_list | join(' ') }}; do + nft add element inet filter allowAll_v4 { $ip } || true + done + args: { executable: /bin/bash } + changed_when: true + + - name: Seed restricted port allow lists + vars: + ip_list: "{{ (item.allow_ipv4 | default([])) | map('trim') | reject('equalto','') | list | unique }}" + slug: "{{ item.port | string | regex_replace('[^0-9]+', '_') }}" + when: ip_list | length > 0 + ansible.builtin.shell: | + set -e + for ip in {{ ip_list | join(' ') }}; do + nft add element inet filter allow_{{ slug }}_v4 { $ip } || true + done + args: { executable: /bin/bash } + loop: "{{ restricted_ports | default([]) }}" + loop_control: + label: "{{ item.port }} (allow_{{ item.port | string | regex_replace('[^0-9]+', '_') }}_v4)" + changed_when: true + + # ---- Persist live ruleset (survives reboot) ------------------------ + - name: Persist live nftables ruleset + ansible.builtin.import_tasks: tasks/persist_nftables_ruleset.yml + + - name: Show effective ruleset summary + vars: + sets: >- + {{ + ['allowAll_v4', 'banAll_v4'] + + ((restricted_ports | default([])) + | map(attribute='port') + | map('string') + | map('regex_replace', '[^0-9]+', '_') + | map('regex_replace', '^', 'allow_') + | map('regex_replace', '$', '_v4') + | list) + }} + ansible.builtin.shell: | + for s in {{ sets | join(' ') }}; do + echo "--- $s ---" + nft list set inet filter "$s" 2>/dev/null || echo "(not present)" + done + register: effective_summary + changed_when: false + + - name: Display summary + ansible.builtin.debug: + var: effective_summary.stdout_lines diff --git a/template/2026.4.27.1539/ansible/cmn/disable_pwd_login.yml b/template/2026.5.21.1448/ansible/cmn/disable_pwd_login.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/disable_pwd_login.yml rename to template/2026.5.21.1448/ansible/cmn/disable_pwd_login.yml diff --git a/template/2026.4.27.1539/ansible/cmn/disable_swap.yml b/template/2026.5.21.1448/ansible/cmn/disable_swap.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/disable_swap.yml rename to template/2026.5.21.1448/ansible/cmn/disable_swap.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_connect.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_connect.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_connect.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_connect.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_deposit.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_deposit.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_deposit.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_deposit.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_disconnect.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_disconnect.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_disconnect.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_disconnect.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_keygen.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_keygen.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_keygen.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_keygen.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_publish.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_publish.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_publish.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_publish.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_start.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_start.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_start.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_start.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_status.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_status.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_status.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_status.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_stop.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_stop.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_stop.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_stop.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_update.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_update.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_update.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_update.yml diff --git a/template/2026.4.27.1539/ansible/cmn/doublezero_withdraw.yml b/template/2026.5.21.1448/ansible/cmn/doublezero_withdraw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/doublezero_withdraw.yml rename to template/2026.5.21.1448/ansible/cmn/doublezero_withdraw.yml diff --git a/template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana-limits.conf b/template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana-limits.conf new file mode 100644 index 00000000..17ada76d --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana-limits.conf @@ -0,0 +1,9 @@ +# SLV Solana / Shredstream file descriptor limits +# Deployed by slv irq-tuning skill + +* soft nofile 1000000 +* hard nofile 1000000 +solv soft nofile 1000000 +solv hard nofile 1000000 +root soft nofile 1000000 +root hard nofile 1000000 diff --git a/template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana.conf b/template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana.conf new file mode 100644 index 00000000..f5a42361 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/99-vs2-solana.conf @@ -0,0 +1,16 @@ +# SLV Solana / Shredstream sysctl tuning +# Deployed by slv irq-tuning skill + +vm.swappiness = 1 + +# UDP/TCP buffer sizes (128MB) +net.core.rmem_max = 134217728 +net.core.wmem_max = 134217728 +net.core.rmem_default = 134217728 +net.core.wmem_default = 134217728 +net.ipv4.tcp_rmem = 4096 87380 134217728 +net.ipv4.tcp_wmem = 4096 87380 134217728 +net.core.optmem_max = 134217728 + +# NIC → kernel queue depth +net.core.netdev_max_backlog = 50000 diff --git a/template/2026.5.21.1448/ansible/cmn/files/clickhouse-config.xml.j2 b/template/2026.5.21.1448/ansible/cmn/files/clickhouse-config.xml.j2 new file mode 100644 index 00000000..d09a0acb --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/clickhouse-config.xml.j2 @@ -0,0 +1,52 @@ + + + + + information + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + 8123 + 9000 + 9009 + + + 127.0.0.1 + ::1 + + 4096 + 3 + 200 + 8589934592 + 5368709120 + + {{ js_data_dir }}/clickhouse-data/ + {{ js_data_dir }}/clickhouse-tmp/ + {{ js_data_dir }}/clickhouse-user-files/ + {{ js_data_dir }}/clickhouse-format-schemas/ + + + + /etc/clickhouse-server/users.xml + + + {{ js_data_dir }}/clickhouse-data/access/ + + + + default + default + UTC + + true + diff --git a/template/2026.5.21.1448/ansible/cmn/files/clickhouse-users.xml.j2 b/template/2026.5.21.1448/ansible/cmn/files/clickhouse-users.xml.j2 new file mode 100644 index 00000000..dc1c9ca3 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/clickhouse-users.xml.j2 @@ -0,0 +1,60 @@ + + + + + + 32212254720 + 300 + 0 + random + + + 2 + 10737418240 + 60 + + + + + + + 3600 + 0 + 0 + 0 + 0 + 0 + + + + + + + {% if js_default_password_sha256 %}{{ js_default_password_sha256 }} + {% else %} + {% endif %} + + 127.0.0.1 + ::1 + + default + default + 1 + + + {% if js_analytics_password_sha256 %} + {{ js_analytics_password_sha256 }} + + 127.0.0.1 + ::1 + + readonly + default + + {% endif %} + + diff --git a/template/2026.5.21.1448/ansible/cmn/files/jetstreamer-backfill.sh b/template/2026.5.21.1448/ansible/cmn/files/jetstreamer-backfill.sh new file mode 100644 index 00000000..228a8e6d --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/jetstreamer-backfill.sh @@ -0,0 +1,151 @@ +#!/usr/bin/env bash +# jetstreamer-backfill.sh — bring the local jetstreamer/ClickHouse up to date +# with Old Faithful by ingesting every recently-published epoch that isn't +# already complete. Idempotent and safe to re-run. +# +# The companion systemd timer `jetstreamer-backfill.timer` runs this hourly. +# +# Env (with defaults): +# JETSTREAMER_BIN /usr/local/bin/jetstreamer +# JETSTREAMER_CLICKHOUSE_DSN http://localhost:8123 +# JETSTREAMER_THREADS 120 +# JETSTREAMER_BUFFER_WINDOW 64GiB +# PUBLIC_RPC https://api.mainnet-beta.solana.com +# BASE_URL https://files.old-faithful.net +# WINDOW 0 (no rotation; >0 enables ALTER TABLE +# DELETE for slot < (latest-window)*432000) + +set -euo pipefail + +JETSTREAMER_BIN="${JETSTREAMER_BIN:-/usr/local/bin/jetstreamer}" +JETSTREAMER_CLICKHOUSE_DSN="${JETSTREAMER_CLICKHOUSE_DSN:-http://localhost:8123}" +JETSTREAMER_THREADS="${JETSTREAMER_THREADS:-120}" +JETSTREAMER_BUFFER_WINDOW="${JETSTREAMER_BUFFER_WINDOW:-64GiB}" +PUBLIC_RPC="${PUBLIC_RPC:-https://api.mainnet-beta.solana.com}" +BASE_URL="${BASE_URL:-https://files.old-faithful.net}" +WINDOW="${WINDOW:-0}" +SLOTS_PER_EPOCH=432000 + +log() { printf "[js-backfill %s] %s\n" "$(date -Iseconds)" "$*" >&2; } + +require() { + command -v "$1" >/dev/null 2>&1 || { log "ERROR: $1 missing"; exit 1; } +} +require curl +require "$JETSTREAMER_BIN" + +ch() { + curl -sf --max-time 60 -X POST --data-binary @- "${JETSTREAMER_CLICKHOUSE_DSN}/?database=default" +} + +discover_latest_epoch() { + local current resp + resp=$(curl -fsSL --max-time 15 "$PUBLIC_RPC" -X POST \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":1,"method":"getEpochInfo"}' 2>/dev/null) || resp="" + if command -v jq >/dev/null 2>&1 && [ -n "$resp" ]; then + current=$(printf '%s' "$resp" | jq -r '.result.epoch // empty' 2>/dev/null || true) + else + current=$(printf '%s' "$resp" | grep -oE '"epoch"[[:space:]]*:[[:space:]]*[0-9]+' \ + | head -1 | grep -oE '[0-9]+' || true) + fi + if ! [[ "${current:-}" =~ ^[0-9]+$ ]]; then + log "WARN: cannot parse epoch from $PUBLIC_RPC; defaulting to probe range starting at 1500" + current=1500 + fi + local probe lower + lower=$(( current > 30 ? current - 30 : 1 )) + for probe in $(seq "$current" -1 "$lower"); do + if curl -fsI --max-time 8 "${BASE_URL}/${probe}/epoch-${probe}.cid" >/dev/null 2>&1; then + echo "$probe" + return 0 + fi + done + log "ERROR: no available epoch found in [${lower}..${current}]" + return 1 +} + +# An epoch is considered "complete" when slot_status holds at least 99% of +# its expected slots. Use the ACTUAL min/max slot range observed for the +# epoch so this works on devnet/testnet too (where epoch lengths can differ +# from mainnet's 432000). +get_completed_epochs() { + echo "SELECT intDiv(slot, ${SLOTS_PER_EPOCH}) AS epoch + FROM jetstreamer_slot_status + GROUP BY epoch + HAVING count() >= toUInt64((max(slot) - min(slot) + 1) * 0.99) + ORDER BY epoch + FORMAT TabSeparated" | ch || true +} + +ingest_epoch() { + local epoch="$1" + log "epoch ${epoch}: ingesting" + JETSTREAMER_THREADS="$JETSTREAMER_THREADS" \ + JETSTREAMER_BUFFER_WINDOW="$JETSTREAMER_BUFFER_WINDOW" \ + JETSTREAMER_CLICKHOUSE_MODE=remote \ + JETSTREAMER_CLICKHOUSE_DSN="$JETSTREAMER_CLICKHOUSE_DSN" \ + "$JETSTREAMER_BIN" "$epoch" +} + +# Drop very old rows by issuing a `DROP PARTITION` if the tables are +# partitioned by epoch; otherwise apply a TTL clause that ClickHouse merges +# evict in the background (cheap and out-of-band, unlike ALTER...DELETE +# mutations which write a new part for every affected part). +# +# We attempt the TTL approach unconditionally — it's a no-op if the TTL is +# already set to the same value. Operators who want hard-deletes can run +# `OPTIMIZE TABLE ... FINAL` or set merge_with_ttl_timeout lower in CH +# config.d. +rotate() { + local oldest_kept_slot="$1" + local tbl + for tbl in jetstreamer_slot_status program_invocations; do + log "rotating $tbl: enforcing TTL boundary at slot >= ${oldest_kept_slot}" + # ALTER ... MODIFY TTL is a metadata change (no data rewrite); merges + # then drop expired rows in the background. We express the boundary + # in terms of slot numbers via a synthetic computed column. + echo "ALTER TABLE ${tbl} + MODIFY TTL toDateTime(0) + INTERVAL slot SECOND + INTERVAL 1 YEAR + WHERE slot < ${oldest_kept_slot}" \ + | ch >/dev/null 2>&1 || log "WARN: TTL rotation on $tbl skipped (schema may differ)" + done +} + +main() { + local latest target_min completed epoch + latest=$(discover_latest_epoch) + log "latest published epoch: ${latest}" + + if [ "${WINDOW:-0}" -gt 0 ] 2>/dev/null; then + target_min=$((latest - WINDOW + 1)) + else + target_min=0 + fi + log "ingest range: [${target_min}..${latest}] (window=${WINDOW})" + + completed=$(get_completed_epochs | tr '\n' ' ') + log "already complete: ${completed}" + + local ingested_any=0 + for epoch in $(seq "$target_min" "$latest"); do + [ "$epoch" -lt 0 ] && continue + if echo " ${completed} " | grep -q " ${epoch} "; then + log "epoch ${epoch}: skip (already ingested)" + continue + fi + if ingest_epoch "$epoch"; then + ingested_any=1 + else + log "WARN: ingest failed for epoch ${epoch}" + fi + done + + if [ "${WINDOW:-0}" -gt 0 ] 2>/dev/null && [ "$ingested_any" = "1" ]; then + rotate "$((target_min * SLOTS_PER_EPOCH))" + fi + + log "done" +} + +main "$@" diff --git a/template/2026.5.21.1448/ansible/cmn/files/of1-index-sync.sh b/template/2026.5.21.1448/ansible/cmn/files/of1-index-sync.sh new file mode 100644 index 00000000..fe26ddce --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/of1-index-sync.sh @@ -0,0 +1,327 @@ +#!/usr/bin/env bash +# of1-index-sync.sh — maintain a rolling local cache of yellowstone-faithful +# index files for the most recent N epochs, then regenerate per-epoch config +# YAMLs and restart the faithful service. +# +# CAR files stay remote; only the (much smaller) per-epoch indexes are stored +# locally. This drives the per-query latency win measured in Phase 1 +# (getTransaction ~6x faster) at ~30 GB per recent epoch instead of ~570 GB. +# +# Idempotent: re-running with the same window is a no-op when nothing changed. +# Driven by /etc/systemd/system/of1-index-sync.timer (daily) but can be +# invoked ad-hoc. +# +# Env (with defaults): +# WINDOW rolling window size in epochs (30) +# CACHE_DIR where indexes live (/mnt/ledger/car/indexes) +# CONFIG_DIR where per-epoch config YAMLs live (/home/solv/configs) +# BASE_URL Old Faithful base URL (https://files.old-faithful.net) +# CAR_BASE_URL CAR base URL (kept remote for size) (= BASE_URL) +# SERVICE_NAME faithful systemd unit (faithful.service) +# FAITHFUL_OWNER UNIX owner for cache + configs (solv) +# JOBS aria2c concurrent downloads (5) +# CONNS_PER_FILE aria2c connections-per-server (8) +# PUBLIC_RPC upstream RPC for current-epoch discovery (https://api.mainnet-beta.solana.com) +# DRY_RUN set to 1 to print actions only +# SKIP_RESTART set to 1 to skip systemctl restart at the end + +set -euo pipefail + +WINDOW="${WINDOW:-30}" +CACHE_DIR="${CACHE_DIR:-/mnt/ledger/car/indexes}" +CONFIG_DIR="${CONFIG_DIR:-/home/solv/configs}" +BASE_URL="${BASE_URL:-https://files.old-faithful.net}" +CAR_BASE_URL="${CAR_BASE_URL:-${BASE_URL}}" +SERVICE_NAME="${SERVICE_NAME:-faithful.service}" +FAITHFUL_OWNER="${FAITHFUL_OWNER:-solv}" +JOBS="${JOBS:-5}" +CONNS_PER_FILE="${CONNS_PER_FILE:-8}" +PUBLIC_RPC="${PUBLIC_RPC:-https://api.mainnet-beta.solana.com}" +DRY_RUN="${DRY_RUN:-0}" +SKIP_RESTART="${SKIP_RESTART:-0}" +LOCK_FILE="${LOCK_FILE:-/var/lock/of1-index-sync.lock}" + +INDEX_SUFFIXES=( + cid-to-offset-and-size + slot-to-cid + sig-to-cid + sig-exists + slot-to-blocktime +) + +log() { printf "[of1-sync %s] %s\n" "$(date -Iseconds)" "$*" >&2; } + +require() { + command -v "$1" >/dev/null 2>&1 || { + log "ERROR: required tool not found: $1" + exit 1 + } +} +require curl +require aria2c +require stat +require flock + +# Cleanup any temp files we leak (aria2c input list, etc). +TMP_FILES=() +cleanup() { + local f + for f in "${TMP_FILES[@]:-}"; do + [ -n "$f" ] && rm -f "$f" + done +} +trap cleanup EXIT INT TERM + +mktemp_track() { + local t + t=$(mktemp) + TMP_FILES+=("$t") + echo "$t" +} + +# Discover the most recent epoch published on Old Faithful. Walks down from +# the on-chain "current" epoch (one finalized epoch lags behind). +discover_latest_epoch() { + local current resp + resp=$(curl -fsSL --max-time 15 "$PUBLIC_RPC" -X POST \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc":"2.0","id":1,"method":"getEpochInfo"}' 2>/dev/null) || resp="" + # Prefer jq when available (correct JSON parsing), fall back to a strict regex. + if command -v jq >/dev/null 2>&1 && [ -n "$resp" ]; then + current=$(printf '%s' "$resp" | jq -r '.result.epoch // empty' 2>/dev/null || true) + else + current=$(printf '%s' "$resp" | grep -oE '"epoch"[[:space:]]*:[[:space:]]*[0-9]+' \ + | head -1 | grep -oE '[0-9]+' || true) + fi + # Validate or fall back. + if ! [[ "${current:-}" =~ ^[0-9]+$ ]]; then + log "WARN: cannot parse epoch from $PUBLIC_RPC; falling back to probe range starting at 1500" + current=1500 + fi + local probe lower + lower=$(( current > 30 ? current - 30 : 1 )) + for probe in $(seq "$current" -1 "$lower"); do + if curl -fsI --max-time 8 "${BASE_URL}/${probe}/epoch-${probe}.cid" >/dev/null 2>&1; then + echo "$probe" + return 0 + fi + done + log "ERROR: no available epoch found in probe range [${lower}..${current}]" + return 1 +} + +get_cid() { + local epoch="$1" + curl -fsSL --max-time 30 "${BASE_URL}/${epoch}/epoch-${epoch}.cid" | tr -d '[:space:]' +} + +remote_size() { + curl -sIL --max-time 15 "$1" \ + | awk -F': ' 'BEGIN{IGNORECASE=1} tolower($1)=="content-length"{print $2}' \ + | tail -1 | tr -d '\r' +} + +# Verify that every index file for an epoch is present and matches the remote +# Content-Length. Returns 0 if complete, 1 otherwise. +epoch_complete() { + local epoch="$1" + local dir="${CACHE_DIR}/${epoch}" + [ -d "$dir" ] || return 1 + local cid + cid=$(get_cid "$epoch") || return 1 + local suf url f want got + for suf in "${INDEX_SUFFIXES[@]}"; do + f="${dir}/epoch-${epoch}-${cid}-mainnet-${suf}.index" + [ -f "$f" ] || return 1 + url="${BASE_URL}/${epoch}/epoch-${epoch}-${cid}-mainnet-${suf}.index" + want=$(remote_size "$url") + got=$(stat -c %s "$f" 2>/dev/null || echo 0) + [ -n "$want" ] || return 1 + [ "$want" = "$got" ] || return 1 + done + return 0 +} + +download_epoch() { + local epoch="$1" + local cid + cid=$(get_cid "$epoch") || { log "ERROR: cannot fetch cid for epoch $epoch"; return 1; } + local dir="${CACHE_DIR}/${epoch}" + mkdir -p "$dir" + chown "$FAITHFUL_OWNER":"$FAITHFUL_OWNER" "$dir" 2>/dev/null || true + local input + input=$(mktemp_track) + local suf + # Download to *.part filenames; rename to final names only after we + # verify the byte count matches Content-Length. Atomic mv avoids + # faithful loading a half-written index after a sync interruption. + for suf in "${INDEX_SUFFIXES[@]}"; do + cat >> "$input" <&2 + rm -f "$input" + + # Verify size of every .part file before promoting it to its final name. + # Anything that doesn't match its remote Content-Length is left as .part + # so the next sync run picks it up via the completeness check. + local suf url want got partfile finalfile failed=0 + for suf in "${INDEX_SUFFIXES[@]}"; do + partfile="${dir}/epoch-${epoch}-${cid}-mainnet-${suf}.index.part" + finalfile="${dir}/epoch-${epoch}-${cid}-mainnet-${suf}.index" + [ -f "$partfile" ] || { log "WARN: missing ${partfile}"; failed=1; continue; } + url="${BASE_URL}/${epoch}/epoch-${epoch}-${cid}-mainnet-${suf}.index" + want=$(remote_size "$url") + got=$(stat -c %s "$partfile" 2>/dev/null || echo 0) + if [ -z "$want" ] || [ "$want" != "$got" ]; then + log "WARN: epoch ${epoch} ${suf}: size mismatch want=${want:-?} got=${got}; leaving .part" + failed=1 + continue + fi + mv -f "$partfile" "$finalfile" + done + chown -R "$FAITHFUL_OWNER":"$FAITHFUL_OWNER" "$dir" 2>/dev/null || true + return "$failed" +} + +write_config() { + local epoch="$1" + local cid + cid=$(get_cid "$epoch") || return 1 + local dir="${CACHE_DIR}/${epoch}" + local out="${CONFIG_DIR}/config-epoch${epoch}.yaml" + mkdir -p "$CONFIG_DIR" + cat > "$out" </dev/null || true +} + +main() { + mkdir -p "$CACHE_DIR" "$CONFIG_DIR" + chown "$FAITHFUL_OWNER":"$FAITHFUL_OWNER" "$CACHE_DIR" "$CONFIG_DIR" 2>/dev/null || true + + local latest target_min + latest=$(discover_latest_epoch) + log "latest available epoch on Old Faithful: ${latest}" + target_min=$((latest - WINDOW + 1)) + [ "$target_min" -lt 0 ] && target_min=0 + log "target window: [${target_min}..${latest}] (${WINDOW} epochs)" + + local changed=0 + local cur_epochs=() + local epoch + for epoch in $(seq "$latest" -1 "$target_min"); do + if epoch_complete "$epoch"; then + log "epoch ${epoch}: complete" + else + if [ "$DRY_RUN" = "1" ]; then + log "[dry-run] would download epoch ${epoch}" + else + download_epoch "$epoch" || { log "WARN: download failed for epoch ${epoch}"; continue; } + changed=1 + fi + fi + [ "$DRY_RUN" = "1" ] || write_config "$epoch" + cur_epochs+=("$epoch") + done + + # Evict cache directories outside the window + local d e + if [ -d "$CACHE_DIR" ]; then + for d in "$CACHE_DIR"/*; do + [ -d "$d" ] || continue + e=$(basename "$d") + [[ "$e" =~ ^[0-9]+$ ]] || continue + if [ "$e" -lt "$target_min" ] || [ "$e" -gt "$latest" ]; then + if [ "$DRY_RUN" = "1" ]; then + log "[dry-run] would evict epoch ${e}" + else + log "evicting epoch ${e}" + rm -rf "$d" + rm -f "${CONFIG_DIR}/config-epoch${e}.yaml" + changed=1 + fi + fi + done + fi + + # Evict orphan config YAMLs outside the window (catches legacy configs + # whose target epoch was never localized — those would otherwise run with + # all-remote URIs, ~10x slower than proxied fallback). + local cfg base + if [ -d "$CONFIG_DIR" ]; then + for cfg in "$CONFIG_DIR"/config-epoch*.yaml; do + [ -f "$cfg" ] || continue + base=$(basename "$cfg" .yaml) + e="${base#config-epoch}" + [[ "$e" =~ ^[0-9]+$ ]] || continue + if [ "$e" -lt "$target_min" ] || [ "$e" -gt "$latest" ]; then + if [ "$DRY_RUN" = "1" ]; then + log "[dry-run] would evict orphan config epoch ${e}" + else + log "evicting orphan config epoch ${e}" + rm -f "$cfg" + changed=1 + fi + fi + done + fi + + if [ "$changed" = "1" ] && [ "$DRY_RUN" != "1" ] && [ "$SKIP_RESTART" != "1" ]; then + log "restarting ${SERVICE_NAME}" + systemctl restart "$SERVICE_NAME" || log "WARN: restart of ${SERVICE_NAME} failed" + else + log "no changes (or restart skipped); ${SERVICE_NAME} not restarted" + fi + + log "done. cached epochs: ${cur_epochs[*]:-(none)}" +} + +# Serialize concurrent runs (e.g. timer firing while a manual run is in +# flight) so we never have two aria2c processes writing into the same +# `${dir}/*.index.part` files. +exec 9>"$LOCK_FILE" +if ! flock -n 9; then + log "another sync is already running; exiting" + exit 0 +fi + +main "$@" diff --git a/template/2026.5.21.1448/ansible/cmn/files/start-faithful.sh b/template/2026.5.21.1448/ansible/cmn/files/start-faithful.sh new file mode 100644 index 00000000..5c1124a0 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/start-faithful.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +# start-faithful.sh — wrapper that expands a config-epoch glob and execs +# yellowstone-faithful. Used by faithful.service ExecStart so the rolling +# cache (of1-index-sync.sh) can add/remove configs without editing systemd. + +set -euo pipefail + +PROXY="${PROXY:-/home/solv/proxy.yml}" +LISTEN="${LISTEN:-:8888}" +GRPC_LISTEN="${GRPC_LISTEN:-}" +SEARCH_CONC="${EPOCH_SEARCH_CONCURRENCY:-64}" +LOAD_CONC="${EPOCH_LOAD_CONCURRENCY:-1}" +CONFIGS_DIR="${CONFIGS_DIR:-/home/solv/configs}" +BIN="${FAITHFUL_BIN:-/home/solv/faithful-cli}" + +shopt -s nullglob +configs=("${CONFIGS_DIR}"/config-epoch*.yaml) +shopt -u nullglob + +if [ "${#configs[@]}" -eq 0 ]; then + echo "ERROR: no config-epoch*.yaml found in ${CONFIGS_DIR}" >&2 + exit 1 +fi + +cmd=("$BIN" rpc "--proxy=${PROXY}" "--listen=${LISTEN}") +if [ -n "$GRPC_LISTEN" ]; then + cmd+=("--grpc-listen=${GRPC_LISTEN}") +fi +cmd+=("--epoch-search-concurrency" "$SEARCH_CONC" "--epoch-load-concurrency" "$LOAD_CONC") +cmd+=("${configs[@]}") + +exec "${cmd[@]}" diff --git a/template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.service b/template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.service new file mode 100644 index 00000000..f2315d6e --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.service @@ -0,0 +1,12 @@ +[Unit] +Description=VS2 IRQ Tuning (NIC pin + RPS/XPS + ring + THP) +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/local/bin/vs2-irq-tune.sh +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target diff --git a/template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.sh b/template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.sh new file mode 100644 index 00000000..423f5b14 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/vs2-irq-tune.sh @@ -0,0 +1,72 @@ +#!/bin/bash +# vs2-irq-tune.sh — NIC IRQ pinning + RPS/XPS + NIC ring + THP optimization +# Runs at boot via systemd oneshot. Auto-detects physical core count. +set -euo pipefail + +PHYS_CORES=$(nproc) +CORE_MAX=$((PHYS_CORES - 1)) + +if [ "$PHYS_CORES" -le 32 ]; then + CPUMASK=$(printf "%08x" $(( (1 << PHYS_CORES) - 1 ))) +else + CPUMASK=$(python3 -c "print(hex((1 << $PHYS_CORES) - 1)[2:].zfill(8))") +fi + +echo "vs2-irq-tune: ${PHYS_CORES} cores, mask=${CPUMASK}" + +# 1. NIC IRQ 1:1 pinning +BOND_SLAVES="" +if [ -f /sys/class/net/bond0/bonding/slaves ]; then + BOND_SLAVES=$(cat /sys/class/net/bond0/bonding/slaves) +fi +if [ -z "$BOND_SLAVES" ]; then + BOND_SLAVES=$(grep -oP '\S+-TxRx-0' /proc/interrupts | sed 's/-TxRx-0//' | sort -u || true) +fi + +for NIC in $BOND_SLAVES; do + for cpu in $(seq 0 $CORE_MAX); do + IRQ=$(grep "${NIC}-TxRx-${cpu}$" /proc/interrupts | awk -F: '{print $1}' | tr -d ' ' || true) + if [ -n "$IRQ" ] && [ -d "/proc/irq/${IRQ}" ]; then + echo "$cpu" > "/proc/irq/${IRQ}/smp_affinity_list" 2>/dev/null || true + fi + done + echo " NIC IRQ: pinned ${NIC} queues 0-${CORE_MAX}" +done + +# 2. RPS for VLAN/bond +for rxq in /sys/class/net/bond0/queues/rx-*/rps_cpus /sys/class/net/bond0.*/queues/rx-*/rps_cpus; do + [ -f "$rxq" ] && echo "$CPUMASK" > "$rxq" 2>/dev/null || true +done +for NIC in $BOND_SLAVES; do + for rxq in /sys/class/net/${NIC}/queues/rx-*/rps_cpus; do + [ -f "$rxq" ] && echo "$CPUMASK" > "$rxq" 2>/dev/null || true + done +done +echo " RPS: set to ${CPUMASK}" + +# 3. XPS: pin each tx queue to corresponding CPU +for NIC in $BOND_SLAVES; do + for cpu in $(seq 0 $CORE_MAX); do + XPS_FILE="/sys/class/net/${NIC}/queues/tx-${cpu}/xps_cpus" + if [ -f "$XPS_FILE" ]; then + SINGLE_MASK=$(printf "%08x" $((1 << cpu))) + echo "$SINGLE_MASK" > "$XPS_FILE" 2>/dev/null || true + fi + done +done +echo " XPS: pinned" + +# 4. NIC Ring Buffer → max (8192) +for NIC in $BOND_SLAVES; do + ethtool -G "$NIC" rx 8192 tx 8192 2>/dev/null || true +done +echo " NIC Ring: set to 8192" + +# 5. Transparent Huge Pages → never +if [ -f /sys/kernel/mm/transparent_hugepage/enabled ]; then + echo never > /sys/kernel/mm/transparent_hugepage/enabled 2>/dev/null || true + echo never > /sys/kernel/mm/transparent_hugepage/defrag 2>/dev/null || true + echo " THP: disabled" +fi + +echo "vs2-irq-tune: completed" diff --git a/template/2026.5.21.1448/ansible/cmn/files/vs2-limits.conf b/template/2026.5.21.1448/ansible/cmn/files/vs2-limits.conf new file mode 100644 index 00000000..5c3b5993 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/files/vs2-limits.conf @@ -0,0 +1,5 @@ +# SLV systemd default file descriptor limit +# Deployed by slv irq-tuning skill + +[Manager] +DefaultLimitNOFILE=1000000 diff --git a/template/2026.4.27.1539/ansible/cmn/find_unmounted_disks.sh b/template/2026.5.21.1448/ansible/cmn/find_unmounted_disks.sh similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/find_unmounted_disks.sh rename to template/2026.5.21.1448/ansible/cmn/find_unmounted_disks.sh diff --git a/template/2026.4.27.1539/ansible/cmn/fix_permissions.yml b/template/2026.5.21.1448/ansible/cmn/fix_permissions.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/fix_permissions.yml rename to template/2026.5.21.1448/ansible/cmn/fix_permissions.yml diff --git a/template/2026.5.21.1448/ansible/cmn/install_hermes_stack.yml b/template/2026.5.21.1448/ansible/cmn/install_hermes_stack.yml new file mode 100644 index 00000000..0525d152 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/install_hermes_stack.yml @@ -0,0 +1,240 @@ +--- +# Install the full Pyth Hermes stack on a single host: +# - nats-server (JetStream) for Beacon VAA dedup/stream +# - pyth-network/beacon HA Wormhole spy that pushes VAAs into NATS +# - pyth-network/pyth-crosschain apps/hermes/server (Rust) — REST/WS API +# +# Hermes needs a Pythnet RPC (HTTP + WS) as a *source* of accumulator +# messages and a Wormhole spy (or Beacon) for VAAs. Both endpoints come +# from inventory variables — by default they point to public infrastructure, +# but the recommended pattern is to swap them for a self-hosted Pythnet RPC +# deployed via `mainnet-pythnet/init.yml`. +# +# Inventory variables (with defaults — override per-host or per-group): +# hermes_repo https://github.com/pyth-network/pyth-crosschain +# hermes_repo_ref main +# beacon_repo https://github.com/pyth-network/beacon +# beacon_repo_ref main +# nats_version v2.10.22 +# hermes_rust_toolchain 1.82.0 +# hermes_pythnet_http_addr https://pythnet.rpcpool.com +# hermes_pythnet_ws_addr wss://pythnet.rpcpool.com +# hermes_wormhole_env mainnet +# hermes_rpc_listen_addr 0.0.0.0:7575 +# hermes_metrics_listen_addr 127.0.0.1:7576 +# beacon_listen_port 8999 +# beacon_spy_grpc_addr 127.0.0.1:7072 +# nats_port 4222 + +- name: Install Pyth Hermes stack (nats + beacon + hermes) + hosts: all + become: yes + gather_facts: yes + vars: + hermes_repo: "{{ hermes_repo | default('https://github.com/pyth-network/pyth-crosschain') }}" + hermes_repo_ref: "{{ hermes_repo_ref | default('main') }}" + beacon_repo: "{{ beacon_repo | default('https://github.com/pyth-network/beacon') }}" + beacon_repo_ref: "{{ beacon_repo_ref | default('main') }}" + nats_version: "{{ nats_version | default('v2.10.22') }}" + rust_toolchain: "{{ hermes_rust_toolchain | default('1.82.0') }}" + + # Endpoints — override these in inventory. The defaults talk to the + # public Pyth infrastructure so a fresh install boots end-to-end; in + # production swap pythnet_http/ws for your own pythnet RPC. + pythnet_http_addr: "{{ hermes_pythnet_http_addr | default('https://pythnet.rpcpool.com') }}" + pythnet_ws_addr: "{{ hermes_pythnet_ws_addr | default('wss://pythnet.rpcpool.com') }}" + wormhole_env: "{{ hermes_wormhole_env | default('mainnet') }}" + + rpc_listen_addr: "{{ hermes_rpc_listen_addr | default('0.0.0.0:7575') }}" + metrics_listen_addr: "{{ hermes_metrics_listen_addr | default('127.0.0.1:7576') }}" + beacon_listen_port: "{{ beacon_listen_port | default(8999) }}" + beacon_spy_grpc_addr: "{{ beacon_spy_grpc_addr | default('127.0.0.1:7072') }}" + nats_port: "{{ nats_port | default(4222) }}" + + hermes_src_dir: "/home/solv/src/pyth-crosschain" + hermes_binary: "/home/solv/src/pyth-crosschain/apps/hermes/server/target/release/hermes" + beacon_src_dir: "/home/solv/src/beacon" + beacon_binary: "/home/solv/beacon-bin" + + tasks: + - name: Install OS packages (build deps + nats prerequisites) + ansible.builtin.apt: + name: + - build-essential + - pkg-config + - libssl-dev + - clang + - protobuf-compiler + - git + - curl + - jq + - golang-go + - ca-certificates + state: present + update_cache: yes + cache_valid_time: 3600 + + # nats-server is shipped as a single static binary. We install it + # ourselves because Ubuntu's repo lags releases and we want to pin a + # version that matches the Beacon NATS client we build below. + - name: Check installed nats-server version + ansible.builtin.command: /usr/local/bin/nats-server --version + register: nats_installed + changed_when: false + failed_when: false + + - name: Download nats-server {{ nats_version }} tarball + ansible.builtin.get_url: + url: "https://github.com/nats-io/nats-server/releases/download/{{ nats_version }}/nats-server-{{ nats_version }}-linux-amd64.tar.gz" + dest: "/tmp/nats-{{ nats_version }}.tgz" + mode: "0644" + when: nats_version not in nats_installed.stdout + + - name: Unpack nats-server + ansible.builtin.unarchive: + src: "/tmp/nats-{{ nats_version }}.tgz" + dest: /tmp + remote_src: yes + when: nats_version not in nats_installed.stdout + + - name: Install nats-server binary + ansible.builtin.copy: + src: "/tmp/nats-server-{{ nats_version }}-linux-amd64/nats-server" + dest: /usr/local/bin/nats-server + mode: "0755" + remote_src: yes + when: nats_version not in nats_installed.stdout + + - name: Install Rust toolchain for solv + become_user: solv + ansible.builtin.shell: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --default-toolchain {{ rust_toolchain }} --profile minimal + args: + creates: /home/solv/.cargo/bin/cargo + + - name: Ensure source dir exists + ansible.builtin.file: + path: /home/solv/src + state: directory + owner: solv + group: solv + mode: "0755" + + # Build Beacon (Go) first — it's fast (~30s) and lets us start NATS+spy + # while the heavier Hermes (Rust) compile runs. + - name: Clone beacon repo + become_user: solv + ansible.builtin.git: + repo: "{{ beacon_repo }}" + dest: "{{ beacon_src_dir }}" + version: "{{ beacon_repo_ref }}" + depth: 1 + force: yes + register: beacon_git + + - name: Build beacon binary + become_user: solv + ansible.builtin.shell: | + export PATH=$PATH:/usr/local/go/bin + go build -o {{ beacon_binary }} . + args: + chdir: "{{ beacon_src_dir }}" + executable: /bin/bash + when: beacon_git.changed or not (beacon_binary | length > 0) + register: beacon_build + changed_when: beacon_build.rc == 0 + + # Clone Hermes server crate. The crate lives at apps/hermes/server in the + # pyth-crosschain monorepo; the build pulls in ~200 Cargo deps but + # finishes in ~60s on modern hardware (Cargo build cache reuses across + # re-runs). + - name: Clone pyth-crosschain repo + become_user: solv + ansible.builtin.git: + repo: "{{ hermes_repo }}" + dest: "{{ hermes_src_dir }}" + version: "{{ hermes_repo_ref }}" + depth: 1 + force: yes + register: hermes_git + + - name: Build hermes binary + become_user: solv + ansible.builtin.shell: | + source $HOME/.cargo/env + cargo build --release + args: + chdir: "{{ hermes_src_dir }}/apps/hermes/server" + executable: /bin/bash + register: hermes_build + changed_when: hermes_build.rc == 0 + when: hermes_git.changed + + - name: NATS data dir + ansible.builtin.file: + path: /home/solv/nats-data + state: directory + owner: solv + group: solv + mode: "0755" + + - name: Install nats-server systemd unit + ansible.builtin.template: + src: "~/.slv/mainnet-hermes/nats-server.service.j2" + dest: /etc/systemd/system/nats-server.service + owner: root + group: root + mode: "0644" + notify: reload systemd + + - name: Install beacon systemd unit + ansible.builtin.template: + src: "~/.slv/mainnet-hermes/beacon.service.j2" + dest: /etc/systemd/system/beacon.service + owner: root + group: root + mode: "0644" + notify: reload systemd + + - name: Install hermes systemd unit + ansible.builtin.template: + src: "~/.slv/mainnet-hermes/hermes.service.j2" + dest: /etc/systemd/system/hermes.service + owner: root + group: root + mode: "0644" + notify: reload systemd + + - name: Flush handlers so daemon-reload runs before enable+start + ansible.builtin.meta: flush_handlers + + - name: Enable and start the stack (nats → beacon → hermes) + ansible.builtin.systemd: + name: "{{ item }}" + enabled: yes + state: started + loop: + - nats-server.service + - beacon.service + - hermes.service + + # Smoke test — fail fast if the API isn't responding. Hermes binds + # its REST/WS listener as soon as it starts so this should be ready + # within a few seconds; the retry loop forgives slow boot. + - name: Wait for hermes /v2/price_feeds + ansible.builtin.uri: + url: "http://127.0.0.1:{{ rpc_listen_addr.split(':')[1] }}/v2/price_feeds" + status_code: 200 + register: hermes_health + retries: 15 + delay: 2 + until: hermes_health.status == 200 + + - name: Report endpoint + ansible.builtin.debug: + msg: "Hermes API listening on {{ rpc_listen_addr }} — POST/GET /v2/* and /ws supported." + + handlers: + - name: reload systemd + ansible.builtin.command: systemctl daemon-reload diff --git a/template/2026.5.21.1448/ansible/cmn/install_jetstreamer.yml b/template/2026.5.21.1448/ansible/cmn/install_jetstreamer.yml new file mode 100644 index 00000000..ea0a5f50 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/install_jetstreamer.yml @@ -0,0 +1,494 @@ +--- +# Install + configure jetstreamer (Solana historical-data backfill engine) +# alongside an embedded ClickHouse server, on a dedicated node. +# +# Prereqs: +# - Ubuntu 24.04, kernel 6.14+ +# - Two large NVMe drives ready to be wiped and combined into RAID0 +# - Outbound HTTPS to https://files.old-faithful.net (CAR archives) and +# https://api.mainnet-beta.solana.com (epoch discovery) +# +# Inventory variables (with defaults): +# js_data_dir /mnt/jetstreamer +# js_raid_devices ['/dev/nvme0n1', '/dev/nvme1n1'] +# js_raid_chunk_kb 1024 +# js_window_epochs 0 (0 = disable rotation; >0 enables ALTER..DELETE) +# js_threads 120 +# js_buffer_window 64GiB +# js_clickhouse_dsn http://localhost:8123 +# js_max_server_memory 536870912000 (~500 GiB; tune per node RAM) +# js_repo https://github.com/anza-xyz/jetstreamer +# js_repo_ref main +# js_run_initial_ingest false (heavy; keep manual unless explicit) + +- name: Install jetstreamer + ClickHouse on dedicated analytics node + hosts: all + become: yes + gather_facts: yes + vars: + js_data_dir: "{{ jetstreamer_data_dir | default('/mnt/jetstreamer') }}" + js_raid_devices: "{{ jetstreamer_raid_devices | default(['/dev/nvme0n1', '/dev/nvme1n1']) }}" + js_raid_chunk_kb: "{{ jetstreamer_raid_chunk_kb | default(1024) }}" + js_window: "{{ jetstreamer_window_epochs | default(0) }}" + js_threads: "{{ jetstreamer_threads | default(120) }}" + js_buffer_window: "{{ jetstreamer_buffer_window | default('64GiB') }}" + js_clickhouse_dsn: "{{ jetstreamer_clickhouse_dsn | default('http://localhost:8123') }}" + js_max_server_memory: "{{ jetstreamer_max_server_memory | default(536870912000) }}" + js_repo: "{{ jetstreamer_repo | default('https://github.com/anza-xyz/jetstreamer') }}" + js_repo_ref: "{{ jetstreamer_repo_ref | default('main') }}" + js_run_initial_ingest: "{{ jetstreamer_run_initial_ingest | default(false) | bool }}" + + tasks: + # ── Disk: RAID0 + XFS ───────────────────────────────────────────────── + - name: Install mdadm + xfsprogs + ansible.builtin.apt: + name: [mdadm, xfsprogs, aria2] + state: present + + # IMPORTANT: this gate decides whether to wipe the configured NVMes. + # We use a strong probe (does /dev/md0 exist AND does mdadm recognise + # it) instead of `findmnt` which only catches the mounted case — a + # half-finished previous run could otherwise see "not mounted" and + # destroy an existing array on the second run. + - name: Probe whether /dev/md0 already exists as an mdadm array + ansible.builtin.shell: | + set -e + [ -b /dev/md0 ] || exit 1 + mdadm --detail /dev/md0 >/dev/null 2>&1 + register: js_md0_present + changed_when: false + failed_when: false + + - name: Refuse to wipe — operator must manually clear /dev/md0 first + ansible.builtin.fail: + msg: | + /dev/md0 already exists. Refusing to wipe {{ js_raid_devices | join(', ') }} + automatically. If this is a fresh node and you really want to + rebuild the array, run on the host: + sudo mdadm --stop /dev/md0 + sudo mdadm --zero-superblock --force {{ js_raid_devices | join(' ') }} + then re-run this playbook. + when: + - js_md0_present.rc == 0 + - js_force_raid_rebuild | default(false) | bool == false + - not (js_data_dir + '/clickhouse-data') is exists # only fail on a clean array; if CH dir is there, it's fine to skip RAID setup + + - name: Wipe filesystem signatures on RAID member devices (fresh install only) + ansible.builtin.command: "wipefs -a {{ item }}" + loop: "{{ js_raid_devices }}" + when: js_md0_present.rc != 0 + + - name: Create /dev/md0 (RAID0, {{ js_raid_chunk_kb }}K chunk) — fresh install only + ansible.builtin.command: >- + mdadm --create /dev/md0 --level=0 + --raid-devices={{ js_raid_devices | length }} + --chunk={{ js_raid_chunk_kb }} + --metadata=1.2 --run + {{ js_raid_devices | join(' ') }} + when: js_md0_present.rc != 0 + register: js_raid_create + + - name: Persist mdadm.conf and rebuild initramfs + when: js_raid_create is defined and js_raid_create is changed + block: + - ansible.builtin.shell: mdadm --detail --scan > /etc/mdadm/mdadm.conf + - ansible.builtin.command: update-initramfs -u + + - name: Format XFS on /dev/md0 (fresh install only) + ansible.builtin.command: mkfs.xfs -f -d agcount=64 -l size=1g -L jetstreamer /dev/md0 + when: js_raid_create is defined and js_raid_create is changed + + - name: Ensure {{ js_data_dir }} mountpoint + ansible.builtin.file: + path: "{{ js_data_dir }}" + state: directory + mode: "0755" + + - name: Resolve /dev/md0 UUID + ansible.builtin.command: blkid -s UUID -o value /dev/md0 + register: js_md0_uuid + changed_when: false + + - name: Assert UUID resolution succeeded (otherwise mount would silently + land on the root filesystem and CH data would vanish on reboot) + ansible.builtin.assert: + that: js_md0_uuid.stdout | length > 0 + fail_msg: "blkid could not resolve UUID for /dev/md0 — refusing to continue" + + - name: Mount /dev/md0 → {{ js_data_dir }} and persist via fstab + ansible.posix.mount: + path: "{{ js_data_dir }}" + src: "UUID={{ js_md0_uuid.stdout }}" + fstype: xfs + opts: noatime,nodiratime,inode64,logbufs=8,logbsize=256k + state: mounted + + - name: chmod 0755 /mnt (so non-solv users can traverse) + ansible.builtin.file: + path: /mnt + mode: "0755" + state: directory + + # ── ClickHouse user + dirs ──────────────────────────────────────────── + - name: Create clickhouse system user + ansible.builtin.user: + name: clickhouse + system: yes + shell: /sbin/nologin + home: /var/lib/clickhouse + create_home: no + + - name: Create clickhouse dirs + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: clickhouse + group: clickhouse + mode: "0755" + loop: + - "{{ js_data_dir }}/clickhouse-data" + - "{{ js_data_dir }}/clickhouse-tmp" + - "{{ js_data_dir }}/clickhouse-user-files" + - "{{ js_data_dir }}/clickhouse-format-schemas" + - /var/log/clickhouse-server + - /etc/clickhouse-server + - /etc/clickhouse-server/config.d + - /etc/clickhouse-server/users.d + + # ── Toolchain (Clang 16, Rust, build deps) ──────────────────────────── + - name: Add LLVM 16 apt repository + ansible.builtin.shell: | + set -e + if [ ! -f /usr/share/keyrings/llvm-archive-keyring.gpg ]; then + wget -qO - https://apt.llvm.org/llvm-snapshot.gpg.key \ + | gpg --dearmor -o /usr/share/keyrings/llvm-archive-keyring.gpg + fi + echo "deb [signed-by=/usr/share/keyrings/llvm-archive-keyring.gpg] http://apt.llvm.org/noble/ llvm-toolchain-noble-16 main" \ + > /etc/apt/sources.list.d/llvm-16.list + args: + creates: /etc/apt/sources.list.d/llvm-16.list + + - name: apt update + ansible.builtin.apt: + update_cache: yes + + - name: Install Clang 16 + LLVM 16 + lld + build deps + ansible.builtin.apt: + name: + - clang-16 + - llvm-16 + - lld-16 + - lld + - libclang-16-dev + - build-essential + - pkg-config + - libssl-dev + - libudev-dev + - cmake + - protobuf-compiler + - git + - zlib1g-dev + - libtool + - gcc-13 + - g++-13 + - curl + - python3 + state: present + + - name: Install Rust toolchain (rustup) for solv + become_user: solv + ansible.builtin.shell: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --default-toolchain stable --profile minimal + args: + creates: /home/solv/.cargo/bin/cargo + + # ── jetstreamer build ───────────────────────────────────────────────── + - name: chown {{ js_data_dir }} to solv (will host clone + build) + ansible.builtin.file: + path: "{{ js_data_dir }}" + state: directory + owner: solv + group: solv + mode: "0755" + recurse: false + + - name: Clone jetstreamer + become_user: solv + ansible.builtin.git: + repo: "{{ js_repo }}" + dest: "{{ js_data_dir }}/jetstreamer" + version: "{{ js_repo_ref }}" + force: no + + - name: cargo build --release (Clang 16, target-cpu=native) + become_user: solv + ansible.builtin.shell: | + source $HOME/.cargo/env + export CC=clang-16 + export CXX=clang++-16 + export RUSTFLAGS="-C target-cpu=native" + cd "{{ js_data_dir }}/jetstreamer" + cargo build --release + args: + creates: "{{ js_data_dir }}/jetstreamer/target/release/jetstreamer" + async: 7200 + poll: 60 + + - name: Install jetstreamer binary symlink + ansible.builtin.file: + src: "{{ js_data_dir }}/jetstreamer/target/release/jetstreamer" + dest: /usr/local/bin/jetstreamer + state: link + force: yes + + # ── ClickHouse server install ───────────────────────────────────────── + # Two paths: (1) use the binary jetstreamer's first run decompressed to + # /tmp/.tmp*-clickhouse — fast, no download; (2) fall back to a fresh + # download from clickhouse.com if no bundled binary is found yet. + - name: Look for a bundled ClickHouse binary already decompressed by jetstreamer + ansible.builtin.shell: | + ls /tmp/.tmp*-clickhouse 2>/dev/null | head -1 + register: js_bundled_ch + changed_when: false + + - name: Stage bundled ClickHouse to /usr/local/bin/clickhouse + ansible.builtin.copy: + src: "{{ js_bundled_ch.stdout }}" + dest: /usr/local/bin/clickhouse + mode: "0755" + owner: root + group: root + remote_src: yes + when: + - js_bundled_ch.stdout | length > 0 + - not (lookup('ansible.builtin.fileglob', '/usr/local/bin/clickhouse', errors='ignore') | length > 0) + + - name: Fall back — download official ClickHouse if no binary present + ansible.builtin.shell: | + set -e + cd /tmp + curl -fsSL https://clickhouse.com/ | sh + install -m 755 clickhouse /usr/local/bin/clickhouse + args: + creates: /usr/local/bin/clickhouse + + - name: Verify clickhouse binary works + ansible.builtin.command: /usr/local/bin/clickhouse --version + register: js_ch_version + changed_when: false + + # ── ClickHouse config files ─────────────────────────────────────────── + - name: Install ClickHouse base config + ansible.builtin.template: + src: files/clickhouse-config.xml.j2 + dest: /etc/clickhouse-server/config.xml + owner: clickhouse + group: clickhouse + mode: "0644" + + - name: Install ClickHouse users.xml (loopback-only, optional SHA256 password) + ansible.builtin.template: + src: files/clickhouse-users.xml.j2 + dest: /etc/clickhouse-server/users.xml + owner: clickhouse + group: clickhouse + mode: "0640" + vars: + js_default_password_sha256: "{{ jetstreamer_default_password_sha256 | default('') }}" + js_analytics_password_sha256: "{{ jetstreamer_analytics_password_sha256 | default('') }}" + + - name: Install ClickHouse perf overrides + ansible.builtin.copy: + dest: /etc/clickhouse-server/config.d/01-perf.xml + owner: clickhouse + group: clickhouse + mode: "0644" + content: | + + {{ js_max_server_memory }} + 0.65 + 200 + 100 + 100 + 32 + 4 + 32 + 32 + 32 + + 10000 + 8000 + 200000 + 32 + 32 + + + information + /var/log/clickhouse-server/clickhouse-server.log + /var/log/clickhouse-server/clickhouse-server.err.log + 1000M + 10 + + + + - name: Install ClickHouse user_directories override (point at the + standard /etc/clickhouse-server/users.xml) + ansible.builtin.copy: + dest: /etc/clickhouse-server/config.d/02-users.xml + owner: clickhouse + group: clickhouse + mode: "0644" + content: | + + + + /etc/clickhouse-server/users.xml + + + {{ js_data_dir }}/clickhouse-data/access/ + + + + + - name: Install ClickHouse user profile tuning + ansible.builtin.copy: + dest: /etc/clickhouse-server/users.d/01-perf.xml + owner: clickhouse + group: clickhouse + mode: "0644" + content: | + + + + 32212254720 + 300 + 0 + random + 32 + 16 + + + 2 + 10737418240 + 60 + 16 + + + + + # ── ClickHouse systemd unit ─────────────────────────────────────────── + - name: Install clickhouse-server.service + ansible.builtin.copy: + dest: /etc/systemd/system/clickhouse-server.service + mode: "0644" + content: | + [Unit] + Description=ClickHouse Server + After=network-online.target + Wants=network-online.target + + [Service] + Type=notify + User=clickhouse + Group=clickhouse + RuntimeDirectory=clickhouse-server + RuntimeDirectoryMode=0755 + LimitNOFILE=1048576 + LimitNPROC=131072 + LimitCORE=infinity + ExecStart=/usr/local/bin/clickhouse server \ + --config-file=/etc/clickhouse-server/config.xml \ + --pid-file=/run/clickhouse-server/clickhouse-server.pid + TimeoutStopSec=600 + Restart=always + RestartSec=10 + + [Install] + WantedBy=multi-user.target + + - name: sysctl tuning for ClickHouse (per-key, idempotent) + ansible.posix.sysctl: + name: "{{ item.k }}" + value: "{{ item.v }}" + sysctl_file: /etc/sysctl.d/99-clickhouse.conf + state: present + reload: yes + loop: + - { k: vm.max_map_count, v: '2097152' } + - { k: vm.swappiness, v: '1' } + - { k: net.core.somaxconn, v: '4096' } + + - name: Enable + start ClickHouse server + ansible.builtin.systemd: + name: clickhouse-server.service + daemon_reload: yes + enabled: yes + state: started + + # ── Backfill script + systemd timer ─────────────────────────────────── + - name: Install jetstreamer-backfill.sh + ansible.builtin.copy: + src: files/jetstreamer-backfill.sh + dest: /usr/local/bin/jetstreamer-backfill.sh + mode: "0755" + owner: root + group: root + + - name: Install jetstreamer-backfill.service + ansible.builtin.copy: + dest: /etc/systemd/system/jetstreamer-backfill.service + mode: "0644" + content: | + [Unit] + Description=Jetstreamer rolling backfill (ingest new epochs into ClickHouse) + After=network-online.target clickhouse-server.service + Wants=network-online.target + Requires=clickhouse-server.service + + [Service] + Type=oneshot + User=solv + Environment=JETSTREAMER_BIN=/usr/local/bin/jetstreamer + Environment=JETSTREAMER_CLICKHOUSE_DSN={{ js_clickhouse_dsn }} + Environment=JETSTREAMER_THREADS={{ js_threads }} + Environment=JETSTREAMER_BUFFER_WINDOW={{ js_buffer_window }} + Environment=WINDOW={{ js_window }} + ExecStart=/usr/local/bin/jetstreamer-backfill.sh + TimeoutStartSec=43200 + + - name: Install jetstreamer-backfill.timer + ansible.builtin.copy: + dest: /etc/systemd/system/jetstreamer-backfill.timer + mode: "0644" + content: | + [Unit] + Description=Hourly jetstreamer backfill of newly published epochs + + [Timer] + OnCalendar=hourly + Persistent=true + RandomizedDelaySec=10min + + [Install] + WantedBy=timers.target + + - name: Enable + start backfill timer + ansible.builtin.systemd: + name: jetstreamer-backfill.timer + daemon_reload: yes + enabled: yes + state: started + + - name: Run initial backfill (heavy; only when explicitly requested) + ansible.builtin.command: /usr/local/bin/jetstreamer-backfill.sh + environment: + JETSTREAMER_BIN: /usr/local/bin/jetstreamer + JETSTREAMER_CLICKHOUSE_DSN: "{{ js_clickhouse_dsn }}" + JETSTREAMER_THREADS: "{{ js_threads }}" + JETSTREAMER_BUFFER_WINDOW: "{{ js_buffer_window }}" + WINDOW: "{{ js_window }}" + become_user: solv + async: 86400 + poll: 60 + when: js_run_initial_ingest diff --git a/template/2026.5.21.1448/ansible/cmn/install_local_shred_relay.yml b/template/2026.5.21.1448/ansible/cmn/install_local_shred_relay.yml new file mode 100644 index 00000000..4f31f907 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/install_local_shred_relay.yml @@ -0,0 +1,198 @@ +--- +# Install + start a *local* jito-shredstream-proxy that: +# 1. maintains the heartbeat connection to a jito block-engine +# (= keeps jito's UDP stream alive for this host's region), +# 2. accepts incoming UDP shreds on `src_bind_port` (operator-chosen, +# e.g. 20001), +# 3. forwards every received shred via UDP to a local dest +# (defaults to 127.0.0.1:20000 = where the SLV RPC Gateway's +# `SLOT_UDP_BIND` listener lives in the recommended topology). +# +# Why this exists: +# The SLV RPC Gateway's `SLOT_UDP_BIND` slot fast-path reads slot +# numbers straight off the shred wire header (offset 65, u64 LE). +# To feed that listener with shreds from the jito block-engine +# path (which is one of the lowest-latency upstreams in many +# regions), some process needs to authenticate to jito via the +# heartbeat protocol and then push raw shreds to the gateway. +# jito-shredstream-proxy does exactly that already, so this play +# just installs and configures it in "relay to local gateway" +# mode, separate from the kernel-level dest (validator TVU) the +# stake-validator shredstream forwarder uses. +# +# Inventory variables (with defaults): +# +# Required (no defaults): +# local_shred_relay_block_engine_url e.g. "https://frankfurt.mainnet.block-engine.jito.wtf" +# Pick the URL for the region of the +# host this play runs against. +# local_shred_relay_desired_regions e.g. "frankfurt" +# Comma list, must match the +# block-engine URL's region. Each +# region in the list costs heartbeat +# budget against the jito tier limit +# for the keypair. +# local_shred_relay_auth_keypair_src Path on the *ansible controller* to +# the keypair JSON file. Treated as +# a secret — keep it out of any OSS +# repo / inventory committed publicly. +# +# Optional (with sensible defaults): +# local_shred_relay_binary_version v0.2.13 +# local_shred_relay_binary_url (auto from version, jito-labs releases) +# local_shred_relay_user solv +# local_shred_relay_install_dir /home/solv +# local_shred_relay_keypair_dest /home/solv/shredstream_keypair.json +# local_shred_relay_src_bind_port 20001 +# local_shred_relay_dest_host 127.0.0.1 +# local_shred_relay_dest_port 20000 (= matches gateway's +# SLOT_UDP_BIND default) +# local_shred_relay_grpc_port 19999 (also exposes the legacy +# SubscribeEntries gRPC stream +# for callers that still need +# it; harmless when unused) +# local_shred_relay_service_name shredstream-local.service +# local_shred_relay_rust_log warn +# +# Networking prerequisite (NOT done by this play — operator must set): +# nftables (or your equivalent firewall) on this host must allow +# inbound UDP on `local_shred_relay_src_bind_port` from jito's +# shred-source IPs (currently observed: 64.130.40.0/24 — confirm +# per region). Without that allowlist every shred is silently +# dropped at the kernel before it reaches the proxy. +# +# Co-deploy with `install_rpc_gateway.yml`: +# Set the gateway's `rpc_gateway_slot_udp_bind` to +# `0.0.0.0:{{ local_shred_relay_dest_port }}` so the gateway picks +# up the shreds this relay forwards. Then re-run both plays in +# any order — both are idempotent. + +- name: Install + start local jito-shredstream-proxy (relay to gateway) + hosts: all + become: yes + gather_facts: yes + vars: + sr_user: "{{ local_shred_relay_user | default('solv') }}" + sr_install_dir: "{{ local_shred_relay_install_dir | default('/home/solv') }}" + sr_version: "{{ local_shred_relay_binary_version | default('v0.2.13') }}" + sr_binary_url: "{{ local_shred_relay_binary_url | default('https://github.com/jito-labs/shredstream-proxy/releases/download/' + (local_shred_relay_binary_version | default('v0.2.13')) + '/jito-shredstream-proxy-x86_64-unknown-linux-gnu') }}" + sr_binary_path: "{{ sr_install_dir }}/jito-shredstream-proxy" + sr_keypair_dest: "{{ local_shred_relay_keypair_dest | default(sr_install_dir + '/shredstream_keypair.json') }}" + sr_src_bind_port: "{{ local_shred_relay_src_bind_port | default(20001) }}" + sr_dest_host: "{{ local_shred_relay_dest_host | default('127.0.0.1') }}" + sr_dest_port: "{{ local_shred_relay_dest_port | default(20000) }}" + sr_grpc_port: "{{ local_shred_relay_grpc_port | default(19999) }}" + sr_service_name: "{{ local_shred_relay_service_name | default('shredstream-local.service') }}" + sr_rust_log: "{{ local_shred_relay_rust_log | default('warn') }}" + + pre_tasks: + - name: Fail fast when block_engine_url is unset + ansible.builtin.fail: + msg: "local_shred_relay_block_engine_url is required (e.g. https://frankfurt.mainnet.block-engine.jito.wtf)" + when: (local_shred_relay_block_engine_url | default('')) | length == 0 + + - name: Fail fast when desired_regions is unset + ansible.builtin.fail: + msg: "local_shred_relay_desired_regions is required (comma list, e.g. 'frankfurt')" + when: (local_shred_relay_desired_regions | default('')) | length == 0 + + - name: Fail fast when auth_keypair_src is unset + ansible.builtin.fail: + msg: "local_shred_relay_auth_keypair_src must point at a keypair JSON file on the ansible controller" + when: (local_shred_relay_auth_keypair_src | default('')) | length == 0 + + tasks: + # ----------------------------------------------------------------- + # Binary install. Idempotent via creates: — re-download only when + # the binary is missing or the URL changed (= bump + # local_shred_relay_binary_version and re-run). + # ----------------------------------------------------------------- + - name: Download jito-shredstream-proxy binary + ansible.builtin.get_url: + url: "{{ sr_binary_url }}" + dest: "{{ sr_binary_path }}" + owner: "{{ sr_user }}" + group: "{{ sr_user }}" + mode: "0755" + force: no + + # ----------------------------------------------------------------- + # Keypair: copied from controller, 600 perms, never logged. + # ----------------------------------------------------------------- + - name: Copy shredstream keypair (secret) + ansible.builtin.copy: + src: "{{ local_shred_relay_auth_keypair_src }}" + dest: "{{ sr_keypair_dest }}" + owner: "{{ sr_user }}" + group: "{{ sr_user }}" + mode: "0600" + no_log: true + + # ----------------------------------------------------------------- + # systemd unit. Renders ExecStart inline so the operator can + # `systemctl cat shredstream-local` to see the exact flags + # without grepping ansible vars. + # ----------------------------------------------------------------- + - name: Render shredstream-local systemd unit + ansible.builtin.copy: + dest: "/etc/systemd/system/{{ sr_service_name }}" + owner: root + group: root + mode: "0644" + content: | + [Unit] + Description=Local jito-shredstream-proxy (relay shreds to SLV RPC Gateway via UDP) + After=network.target + Wants=network-online.target + + [Service] + Type=simple + User={{ sr_user }} + WorkingDirectory={{ sr_install_dir }} + ExecStart={{ sr_binary_path }} shredstream \ + --block-engine-url {{ local_shred_relay_block_engine_url }} \ + --auth-keypair {{ sr_keypair_dest }} \ + --desired-regions {{ local_shred_relay_desired_regions }} \ + --src-bind-port {{ sr_src_bind_port }} \ + --dest-ip-ports {{ sr_dest_host }}:{{ sr_dest_port }} \ + --grpc-service-port {{ sr_grpc_port }} + Restart=on-failure + RestartSec=5 + LimitNOFILE=200000 + Environment=RUST_LOG={{ sr_rust_log }} + StandardOutput=journal + StandardError=journal + + [Install] + WantedBy=multi-user.target + register: sr_unit + + - name: systemd daemon-reload + enable + ansible.builtin.systemd: + name: "{{ sr_service_name }}" + enabled: yes + daemon_reload: yes + + - name: Start (or restart if unit changed) shredstream-local + ansible.builtin.systemd: + name: "{{ sr_service_name }}" + state: "{{ 'restarted' if sr_unit.changed else 'started' }}" + + - name: Brief wait for jito heartbeat handshake + ansible.builtin.pause: + seconds: 6 + + - name: Verify service is active + ansible.builtin.command: systemctl is-active {{ sr_service_name }} + register: sr_active + changed_when: false + failed_when: sr_active.stdout != "active" + + - name: Show final state (= for operator log) + ansible.builtin.debug: + msg: >- + shredstream-local up on this host — + listens UDP :{{ sr_src_bind_port }}, + forwards to {{ sr_dest_host }}:{{ sr_dest_port }}, + gRPC on :{{ sr_grpc_port }}, + block-engine = {{ local_shred_relay_block_engine_url }} diff --git a/template/2026.4.27.1539/ansible/cmn/install_min_package.yml b/template/2026.5.21.1448/ansible/cmn/install_min_package.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/install_min_package.yml rename to template/2026.5.21.1448/ansible/cmn/install_min_package.yml diff --git a/template/2026.4.27.1539/ansible/cmn/install_package.yml b/template/2026.5.21.1448/ansible/cmn/install_package.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/install_package.yml rename to template/2026.5.21.1448/ansible/cmn/install_package.yml diff --git a/template/2026.5.21.1448/ansible/cmn/install_rpc_gateway.yml b/template/2026.5.21.1448/ansible/cmn/install_rpc_gateway.yml new file mode 100644 index 00000000..f5ab7fdf --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/install_rpc_gateway.yml @@ -0,0 +1,379 @@ +--- +# Install + start the SLV RPC Gateway. +# +# Build path: clone the slv repo, `cargo build --release -p +# slv-rpc-gateway`, install the resulting binary, render the +# systemd unit, start it. All steps are idempotent — re-running +# the play after a `git push` is the way to roll a new gateway +# build to a host. +# +# Inventory variables (with defaults): +# rpc_gateway_repo https://github.com/ValidatorsDAO/slv +# rpc_gateway_repo_ref main +# rpc_gateway_build_dir /opt/slv-build/slv (= where the repo lives) +# rpc_gateway_bin_dir /opt/rpc-gateway-rust (= where the built binary lives) +# rpc_gateway_port 8889 +# rpc_gateway_of1_url http://localhost:8888 +# rpc_gateway_ch_url http://localhost:8123 (when CH lives elsewhere, +# point to that host:port, +# OR set rpc_gateway_ch_tunnel_* +# below to auto-build an SSH +# tunnel to a loopback CH) +# rpc_gateway_ch_db default +# rpc_gateway_ch_user (unset) +# rpc_gateway_ch_pass (unset) +# rpc_gateway_yellowstone_grpc localhost:10000 +# rpc_gateway_pubsub_ws ws://localhost:7111 +# rpc_gateway_slot_first_shred_url (unset) — opt-in latency tweak +# rpc_gateway_slot_first_shred_multiplex_urls (unset, comma list) — opt-in +# rpc_gateway_slot_multiplex_urls (unset, comma list) — opt-in +# rpc_gateway_slot_pubsub_url (unset) +# rpc_gateway_slot_grpc_url (unset) — jito-shredstream-proxy +# SubscribeEntries endpoint, joins +# the SLOT_FIRST_SHRED_MULTIPLEX dedup +# window as an extra fast-path source +# rpc_gateway_slot_udp_bind (unset) — UDP bind address +# (`host:port`) for raw shred +# reception. Gateway reads slot +# directly from the shred header, +# skipping the gRPC proxy's decode +# (saves ~150-450 µs per slot). +# Requires upstream sender to add +# this address to --dest-ip-ports +# and a strict nftables allowlist +# on the bind port. +# rpc_gateway_live_rpc_url (unset) — live-state RPC backend +# (e.g. http://127.0.0.1:7211 for +# a co-located agave-validator). +# When set, every non-historical +# method is routed here instead of +# to OF1_URL. Required when OF1_URL +# points at a historical-only +# backend (e.g. yellowstone-faithful) +# to keep getSlot / getBlockHeight +# / sendTransaction etc. answering +# with live chain state. +# rpc_gateway_live_rpc_timeout_ms 10000 — per-call timeout +# for the live RPC client +# rpc_gateway_metrics_api_url (unset) — operator-supplied metrics +# API base URL. When set with +# *_bearer below, WS connection close +# events are POSTed to +# `{url}/ws-connection-log` so the +# central metering pipeline can +# charge per-second WS duration. +# rpc_gateway_metrics_api_bearer (unset) — bearer token for the metrics +# API. Keep this in inventory +# group_vars or a vaulted file, never +# in the OSS repo. +# rpc_gateway_metrics_upstream_ip (unset) — optional self-identifier +# emitted as `upstreamIp` in the log +# body (defaults to empty string). +# rpc_gateway_full_concurrency 20 +# rpc_gateway_rust_log info +# +# Optional CH tunnel mode (used when the jetstreamer host's ClickHouse +# binds 127.0.0.1 only and no LAN-direct path exists — typical security +# default). When `rpc_gateway_ch_tunnel_via` is set, this play will: +# - generate a dedicated ed25519 key for the tunnel only +# - install a systemd-managed `ch-tunnel.service` (ssh -N -L) that +# auto-restarts and survives reboots +# - override `rpc_gateway_ch_url` to point at the local tunnel port +# - print the public key with the exact `authorized_keys` line so the +# CH host operator can authorize it once +# +# rpc_gateway_ch_tunnel_via (unset, e.g. "root@") +# rpc_gateway_ch_tunnel_target 127.0.0.1:8123 (remote bind to forward) +# rpc_gateway_ch_tunnel_local_port 18123 (local bind port) + +- name: Install + start the SLV RPC Gateway + hosts: all + become: yes + gather_facts: yes + vars: + rg_repo: "{{ rpc_gateway_repo | default('https://github.com/ValidatorsDAO/slv') }}" + rg_ref: "{{ rpc_gateway_repo_ref | default('main') }}" + rg_build_dir: "{{ rpc_gateway_build_dir | default('/opt/slv-build/slv') }}" + rg_bin_dir: "{{ rpc_gateway_bin_dir | default('/opt/rpc-gateway-rust') }}" + rg_port: "{{ rpc_gateway_port | default(8889) }}" + rg_of1: "{{ rpc_gateway_of1_url | default('http://localhost:8888') }}" + rg_ch: "{{ rpc_gateway_ch_url | default('http://localhost:8123') }}" + rg_ch_db: "{{ rpc_gateway_ch_db | default('default') }}" + rg_ch_user: "{{ rpc_gateway_ch_user | default('') }}" + rg_ch_pass: "{{ rpc_gateway_ch_pass | default('') }}" + rg_yellowstone: "{{ rpc_gateway_yellowstone_grpc | default('localhost:10000') }}" + rg_pubsub: "{{ rpc_gateway_pubsub_ws | default('ws://localhost:7111') }}" + rg_slot_first_shred_url: "{{ rpc_gateway_slot_first_shred_url | default('') }}" + rg_slot_first_shred_multiplex_urls: "{{ rpc_gateway_slot_first_shred_multiplex_urls | default('') }}" + rg_slot_multiplex_urls: "{{ rpc_gateway_slot_multiplex_urls | default('') }}" + rg_slot_pubsub_url: "{{ rpc_gateway_slot_pubsub_url | default('') }}" + rg_slot_grpc_url: "{{ rpc_gateway_slot_grpc_url | default('') }}" + rg_slot_udp_bind: "{{ rpc_gateway_slot_udp_bind | default('') }}" + rg_live_rpc_url: "{{ rpc_gateway_live_rpc_url | default('') }}" + rg_live_rpc_timeout_ms: "{{ rpc_gateway_live_rpc_timeout_ms | default('') }}" + rg_metrics_api_url: "{{ rpc_gateway_metrics_api_url | default('') }}" + rg_metrics_api_bearer: "{{ rpc_gateway_metrics_api_bearer | default('') }}" + rg_metrics_upstream_ip: "{{ rpc_gateway_metrics_upstream_ip | default('') }}" + rg_full_concurrency: "{{ rpc_gateway_full_concurrency | default(20) }}" + rg_rust_log: "{{ rpc_gateway_rust_log | default('info') }}" + # CH tunnel mode (see header comment). Empty string disables. + rg_ch_tunnel_via: "{{ rpc_gateway_ch_tunnel_via | default('') }}" + rg_ch_tunnel_target: "{{ rpc_gateway_ch_tunnel_target | default('127.0.0.1:8123') }}" + rg_ch_tunnel_local_port: "{{ rpc_gateway_ch_tunnel_local_port | default(18123) }}" + # When tunnel mode is on, force the gateway's CLICKHOUSE_URL onto + # the local tunnel port regardless of any rpc_gateway_ch_url the + # operator passed — pointing the gateway at the tunnel is the + # whole point of enabling tunnel mode. + rg_ch_effective: "{% if rg_ch_tunnel_via | length > 0 %}http://127.0.0.1:{{ rg_ch_tunnel_local_port }}{% else %}{{ rg_ch }}{% endif %}" + + tasks: + # --------------------------------------------------------------- + # Build deps. + # --------------------------------------------------------------- + - name: Install build deps + ansible.builtin.apt: + name: + # librocksdb-sys (transitive via jetstreamer / clickhouse-rs) + # invokes clang at build time and dlopens libclang at runtime + # — both packages are required. + - clang + - libclang-dev + - build-essential + - pkg-config + - libssl-dev + - git + - curl + state: present + update_cache: yes + + - name: Install rustup for solv user (idempotent — skipped when cargo present) + become_user: solv + ansible.builtin.shell: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --default-toolchain stable --profile minimal + args: + creates: /home/solv/.cargo/bin/cargo + + # --------------------------------------------------------------- + # Source checkout + build. + # --------------------------------------------------------------- + - name: Ensure build + binary install dirs + ansible.builtin.file: + path: "{{ item }}" + state: directory + owner: solv + group: solv + mode: "0755" + loop: + - "{{ rg_build_dir | dirname }}" + - "{{ rg_build_dir }}" + - "{{ rg_bin_dir }}" + + - name: Clone / update slv repo + become_user: solv + ansible.builtin.git: + repo: "{{ rg_repo }}" + dest: "{{ rg_build_dir }}" + version: "{{ rg_ref }}" + # force: yes so re-running with a different rpc_gateway_repo_ref + # (e.g. switching to a release tag) updates the working tree. + # Source is never edited on the host — changes go through git + # on a workstation. + force: yes + depth: 1 + register: rg_git + + # Re-build only when git updated something or the binary is + # missing. `cargo build` is itself incremental but a no-op git + # pull still spawns 30 s of cargo work, so this check keeps the + # idempotent path cheap. + - name: Stat current binary + ansible.builtin.stat: + path: "{{ rg_bin_dir }}/slv-rpc-gateway" + register: rg_bin_stat + + - name: Build slv-rpc-gateway (release) + become_user: solv + ansible.builtin.shell: | + export PATH="$HOME/.cargo/bin:$PATH" + # librocksdb-sys's bindgen needs libclang at build time; the + # exact path differs by libclang-dev version (libclang-N). + # `find` here picks whatever the distro installed without + # hard-coding a version. + export LIBCLANG_PATH=$(dirname $(find /usr/lib/llvm-* -name libclang.so* 2>/dev/null | head -1) 2>/dev/null || true) + cargo build --release --manifest-path {{ rg_build_dir }}/slv-plugins/Cargo.toml -p slv-rpc-gateway + args: + executable: /bin/bash + register: rg_build + when: rg_git is changed or not rg_bin_stat.stat.exists + notify: restart rpc-gateway + + - name: Install built binary + ansible.builtin.copy: + src: "{{ rg_build_dir }}/slv-plugins/target/release/slv-rpc-gateway" + dest: "{{ rg_bin_dir }}/slv-rpc-gateway" + remote_src: yes + owner: solv + group: solv + mode: "0755" + when: rg_git is changed or not rg_bin_stat.stat.exists + notify: restart rpc-gateway + + # --------------------------------------------------------------- + # Optional CH SSH tunnel. Skipped entirely when + # `rpc_gateway_ch_tunnel_via` is unset. + # --------------------------------------------------------------- + - name: Generate dedicated SSH key for CH tunnel + ansible.builtin.openssh_keypair: + path: /root/.ssh/id_ed25519_ch_tunnel + type: ed25519 + comment: "rpc-gateway CH tunnel from {{ inventory_hostname }}" + when: rg_ch_tunnel_via | length > 0 + + - name: Read tunnel pubkey + ansible.builtin.slurp: + src: /root/.ssh/id_ed25519_ch_tunnel.pub + register: ch_tunnel_pubkey + when: rg_ch_tunnel_via | length > 0 + + - name: Show authorized_keys line for the CH host operator + ansible.builtin.debug: + msg: | + The CH host ({{ rg_ch_tunnel_via }}) must authorize this gateway with + the line below. Append it to /root/.ssh/authorized_keys on that host + (idempotent — a `grep -qF` first is fine). The `restrict` + + `port-forwarding` + `permitopen` combo limits this credential to + forwarding the single CH port; `command="..."` blocks shell access: + + restrict,port-forwarding,permitopen="{{ rg_ch_tunnel_target }}",command="echo forward-only-no-shell" {{ ch_tunnel_pubkey.content | b64decode | trim }} + when: rg_ch_tunnel_via | length > 0 + + - name: Install ch-tunnel.service systemd unit + ansible.builtin.copy: + dest: /etc/systemd/system/ch-tunnel.service + mode: "0644" + content: | + # Persistent SSH tunnel from this gateway to the jetstreamer + # ClickHouse loopback port. Managed by install_rpc_gateway.yml + # (rpc_gateway_ch_tunnel_* inventory vars). + [Unit] + Description=Persistent SSH tunnel for ClickHouse via {{ rg_ch_tunnel_via }} + After=network-online.target + Wants=network-online.target + + [Service] + Type=exec + User=root + ExecStart=/usr/bin/ssh -N \ + -i /root/.ssh/id_ed25519_ch_tunnel \ + -o ExitOnForwardFailure=yes \ + -o ServerAliveInterval=30 \ + -o ServerAliveCountMax=3 \ + -o StrictHostKeyChecking=accept-new \ + -L 127.0.0.1:{{ rg_ch_tunnel_local_port }}:{{ rg_ch_tunnel_target }} \ + {{ rg_ch_tunnel_via }} + Restart=always + RestartSec=5 + + [Install] + WantedBy=multi-user.target + when: rg_ch_tunnel_via | length > 0 + notify: restart ch-tunnel + + - name: Enable + start ch-tunnel.service + ansible.builtin.systemd: + name: ch-tunnel.service + daemon_reload: yes + enabled: yes + state: started + when: rg_ch_tunnel_via | length > 0 + + # --------------------------------------------------------------- + # Earlier rollout used a 30-binary-swap.conf drop-in to swap the + # Deno ExecStart for the Rust binary without touching the base + # unit. Now that the base unit is regenerated directly with the + # right ExecStart, the swap drop-in is redundant — remove it on + # every play so older hosts converge to the canonical layout. + # --------------------------------------------------------------- + - name: Remove obsolete swap drop-in + ansible.builtin.file: + path: /etc/systemd/system/rpc-gateway.service.d/30-binary-swap.conf + state: absent + notify: restart rpc-gateway + + - name: Install rpc-gateway.service + ansible.builtin.copy: + dest: /etc/systemd/system/rpc-gateway.service + mode: "0644" + content: | + # systemd unit for slv-rpc-gateway (Rust binary, slv-plugins workspace). + [Unit] + Description=SLV RPC Gateway (JSON-RPC; forwards to of1, routes jet* to ClickHouse) + After=network-online.target + Wants=network-online.target + + [Service] + Type=simple + User=solv + Group=solv + Environment=PORT={{ rg_port }} + Environment=OF1_URL={{ rg_of1 }} + Environment=CLICKHOUSE_URL={{ rg_ch_effective }} + Environment=CLICKHOUSE_DB={{ rg_ch_db }} + {% if rg_ch_user %}Environment=CLICKHOUSE_USER={{ rg_ch_user }}{% endif %} + {% if rg_ch_pass %}Environment=CLICKHOUSE_PASS={{ rg_ch_pass }}{% endif %} + Environment=YELLOWSTONE_GRPC={{ rg_yellowstone }} + Environment=PUBSUB_WS_URL={{ rg_pubsub }} + {% if rg_slot_first_shred_url %}Environment=SLOT_FIRST_SHRED_URL={{ rg_slot_first_shred_url }}{% endif %} + {% if rg_slot_first_shred_multiplex_urls %}Environment=SLOT_FIRST_SHRED_MULTIPLEX_URLS={{ rg_slot_first_shred_multiplex_urls }}{% endif %} + {% if rg_slot_multiplex_urls %}Environment=SLOT_MULTIPLEX_URLS={{ rg_slot_multiplex_urls }}{% endif %} + {% if rg_slot_pubsub_url %}Environment=SLOT_PUBSUB_URL={{ rg_slot_pubsub_url }}{% endif %} + {% if rg_slot_grpc_url %}Environment=SLOT_GRPC_URL={{ rg_slot_grpc_url }}{% endif %} + {% if rg_slot_udp_bind %}Environment=SLOT_UDP_BIND={{ rg_slot_udp_bind }}{% endif %} + {% if rg_live_rpc_url %}Environment=LIVE_RPC_URL={{ rg_live_rpc_url }}{% endif %} + {% if rg_live_rpc_timeout_ms %}Environment=LIVE_RPC_TIMEOUT_MS={{ rg_live_rpc_timeout_ms }}{% endif %} + {% if rg_metrics_api_url %}Environment=RPC_METRICS_API_URL={{ rg_metrics_api_url }}{% endif %} + {% if rg_metrics_api_bearer %}Environment=RPC_METRICS_API_BEARER={{ rg_metrics_api_bearer }}{% endif %} + {% if rg_metrics_upstream_ip %}Environment=RPC_METRICS_UPSTREAM_IP={{ rg_metrics_upstream_ip }}{% endif %} + Environment=GTFA_FULL_CONCURRENCY={{ rg_full_concurrency }} + Environment=RUST_LOG={{ rg_rust_log }} + ExecStart={{ rg_bin_dir }}/slv-rpc-gateway + Restart=always + RestartSec=5 + LimitNOFILE=65536 + + [Install] + WantedBy=multi-user.target + notify: restart rpc-gateway + + - name: Enable + start rpc-gateway.service + ansible.builtin.systemd: + name: rpc-gateway.service + daemon_reload: yes + enabled: yes + state: started + + - name: Wait for /health to respond + ansible.builtin.uri: + url: "http://127.0.0.1:{{ rg_port }}/health" + status_code: 200 + register: rg_health + retries: 10 + delay: 2 + until: rg_health.status == 200 + + handlers: + # Restart only on real change (= built binary updated, unit file + # changed). `state: started` above keeps the service up without + # bouncing it on every play execution. + - name: restart rpc-gateway + ansible.builtin.systemd: + name: rpc-gateway.service + daemon_reload: yes + state: restarted + + - name: restart ch-tunnel + ansible.builtin.systemd: + name: ch-tunnel.service + daemon_reload: yes + state: restarted diff --git a/template/2026.4.27.1539/ansible/cmn/install_rust.yml b/template/2026.5.21.1448/ansible/cmn/install_rust.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/install_rust.yml rename to template/2026.5.21.1448/ansible/cmn/install_rust.yml diff --git a/template/2026.4.27.1539/ansible/cmn/install_solana.yml b/template/2026.5.21.1448/ansible/cmn/install_solana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/install_solana.yml rename to template/2026.5.21.1448/ansible/cmn/install_solana.yml diff --git a/template/2026.5.21.1448/ansible/cmn/irq_tune.yml b/template/2026.5.21.1448/ansible/cmn/irq_tune.yml new file mode 100644 index 00000000..bf5b6043 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/irq_tune.yml @@ -0,0 +1,150 @@ +--- +# IRQ / NIC / sysctl / ulimit tuning for Solana / Shredstream +# Idempotent. Configures NVMe queue limits via GRUB (need_reboot=true if changed). +- name: IRQ + sysctl + NIC tuning for Solana + hosts: all + become: yes + gather_facts: false + vars: + grub_default: /etc/default/grub + irq_marker_file: /var/lib/slv/.irq_tuned + files_dir: "{{ playbook_dir }}/files" + tasks: + - name: Ensure /var/lib/slv exists + ansible.builtin.file: + path: /var/lib/slv + state: directory + mode: "0755" + + - name: Install ethtool / cpupower / linux-tools deps + ansible.builtin.apt: + name: + - ethtool + - python3 + state: present + update_cache: yes + ignore_errors: true + + - name: Install vs2-irq-tune.sh script + ansible.builtin.copy: + src: files/vs2-irq-tune.sh + dest: /usr/local/bin/vs2-irq-tune.sh + mode: "0755" + owner: root + group: root + + - name: Install vs2-irq-tune.service unit + ansible.builtin.copy: + src: files/vs2-irq-tune.service + dest: /etc/systemd/system/vs2-irq-tune.service + mode: "0644" + owner: root + group: root + register: irq_service_unit + + - name: Reload systemd if unit changed + ansible.builtin.systemd: + daemon_reload: yes + when: irq_service_unit is changed + + - name: Enable and start vs2-irq-tune.service + ansible.builtin.systemd: + name: vs2-irq-tune.service + enabled: yes + state: started + + - name: Disable & stop irqbalance (conflicts with manual pinning) + ansible.builtin.systemd: + name: irqbalance + enabled: no + state: stopped + ignore_errors: true + + - name: Install sysctl tuning (99-vs2-solana.conf) + ansible.builtin.copy: + src: files/99-vs2-solana.conf + dest: /etc/sysctl.d/99-vs2-solana.conf + mode: "0644" + owner: root + group: root + register: sysctl_file + + - name: Apply sysctl tuning + ansible.builtin.command: sysctl -p /etc/sysctl.d/99-vs2-solana.conf + when: sysctl_file is changed + changed_when: true + + - name: Install ulimit nofile (limits.d) + ansible.builtin.copy: + src: files/99-vs2-solana-limits.conf + dest: /etc/security/limits.d/99-vs2-solana.conf + mode: "0644" + owner: root + group: root + + - name: Ensure systemd system.conf.d directory exists + ansible.builtin.file: + path: /etc/systemd/system.conf.d + state: directory + mode: "0755" + + - name: Install systemd DefaultLimitNOFILE drop-in + ansible.builtin.copy: + src: files/vs2-limits.conf + dest: /etc/systemd/system.conf.d/vs2-limits.conf + mode: "0644" + register: systemd_limits + + - name: systemctl daemon-reexec to pick up DefaultLimitNOFILE + ansible.builtin.command: systemctl daemon-reexec + when: systemd_limits is changed + changed_when: true + + - name: Configure NVMe queue parameters in GRUB (require reboot) + ansible.builtin.shell: | + set -e + CORES=$(nproc) + # shellcheck disable=SC1091 + . /etc/default/grub + CURRENT="${GRUB_CMDLINE_LINUX_DEFAULT:-}" + if echo "$CURRENT" | grep -q "nvme.io_queues="; then + # Already configured — replace values to match current core count + NEW=$(echo "$CURRENT" | sed -E "s/nvme\.io_queues=[0-9]+/nvme.io_queues=${CORES}/; s/nvme\.write_queues=[0-9]+/nvme.write_queues=${CORES}/; s/nvme\.poll_queues=[0-9]+/nvme.poll_queues=0/") + else + NEW="$CURRENT nvme.io_queues=${CORES} nvme.write_queues=${CORES} nvme.poll_queues=0" + fi + NEW=$(echo "$NEW" | sed 's/ */ /g; s/^ //; s/ $//') + if [ "$NEW" != "$CURRENT" ]; then + sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"$NEW\"|" {{ grub_default }} + update-grub + echo CHANGED + else + echo UNCHANGED + fi + args: + executable: /bin/bash + register: nvme_grub + changed_when: "'CHANGED' in nvme_grub.stdout" + + - name: Run vs2-irq-tune now (immediate effect, no reboot needed for NIC/RPS/THP) + ansible.builtin.command: /usr/local/bin/vs2-irq-tune.sh + changed_when: true + ignore_errors: true + + - name: Mark IRQ tuning applied + ansible.builtin.copy: + dest: "{{ irq_marker_file }}" + content: "applied at {{ ansible_date_time.iso8601 | default(lookup('pipe', 'date -Iseconds')) }}\n" + mode: "0644" + + - name: Set need_reboot fact (NVMe GRUB change requires reboot) + ansible.builtin.set_fact: + irq_tune_reboot_required: "{{ nvme_grub is changed }}" + need_reboot: "{{ (need_reboot | default(false)) or (nvme_grub is changed) }}" + + - name: Report IRQ tuning status + ansible.builtin.debug: + msg: >- + {{ 'IRQ tuning applied (NVMe GRUB changed — reboot required)' + if irq_tune_reboot_required + else 'IRQ tuning applied (no reboot needed)' }} diff --git a/template/2026.4.27.1539/ansible/cmn/mount-disks.yml b/template/2026.5.21.1448/ansible/cmn/mount-disks.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/mount-disks.yml rename to template/2026.5.21.1448/ansible/cmn/mount-disks.yml diff --git a/template/2026.4.27.1539/ansible/cmn/mount_disks.yml b/template/2026.5.21.1448/ansible/cmn/mount_disks.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/mount_disks.yml rename to template/2026.5.21.1448/ansible/cmn/mount_disks.yml diff --git a/template/2026.5.21.1448/ansible/cmn/of1_index_cache_sync.yml b/template/2026.5.21.1448/ansible/cmn/of1_index_cache_sync.yml new file mode 100644 index 00000000..d25555f4 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/of1_index_cache_sync.yml @@ -0,0 +1,148 @@ +--- +# of1_index_cache_sync — set up a rolling cache of yellowstone-faithful +# indexes for the last N epochs. The CAR files stay remote (1 epoch ≈ +# 500-800 GB each) but the indexes (~30 GB / recent epoch) live on local +# disk so the multi-step lookup chain (sig-to-cid → cid-to-offset → CAR +# byte-range) collapses to a single CAR roundtrip. Phase 1 measurements +# show ~6x faster getTransaction. +# +# Inventory variables (with defaults): +# index_rpc_window_epochs default 30 +# of1_index_cache_dir default /mnt/ledger/car/indexes +# of1_config_dir default /home/solv/configs +# faithful_service_name default faithful.service +# of1_run_initial_sync default true (run a foreground sync once on first apply) +# of1_initial_sync_async default 14400 (seconds; 4h max for the initial 30-epoch fill) + +- name: Setup yellowstone-faithful index rolling cache + hosts: all + become: yes + gather_facts: no + vars: + of1_window: "{{ index_rpc_window_epochs | default(30) }}" + of1_cache_dir: "{{ of1_index_cache_dir | default('/mnt/ledger/car/indexes') }}" + of1_config_dir_var: "{{ of1_config_dir | default('/home/solv/configs') }}" + of1_service: "{{ faithful_service_name | default('faithful.service') }}" + of1_run_initial_sync_var: "{{ of1_run_initial_sync | default(true) | bool }}" + of1_initial_sync_async_var: "{{ of1_initial_sync_async | default(14400) | int }}" + + tasks: + - name: Ensure aria2 is installed + ansible.builtin.apt: + name: aria2 + state: present + when: ansible_os_family == 'Debian' + + - name: Ensure index cache directory exists + ansible.builtin.file: + path: "{{ of1_cache_dir }}" + state: directory + owner: solv + group: solv + mode: "0755" + + - name: Ensure config directory exists + ansible.builtin.file: + path: "{{ of1_config_dir_var }}" + state: directory + owner: solv + group: solv + mode: "0755" + + - name: Install of1-index-sync.sh + ansible.builtin.copy: + src: files/of1-index-sync.sh + dest: /usr/local/bin/of1-index-sync.sh + mode: "0755" + owner: root + group: root + + - name: Install start-faithful.sh wrapper + ansible.builtin.copy: + src: files/start-faithful.sh + dest: /usr/local/bin/start-faithful.sh + mode: "0755" + owner: root + group: root + + - name: Install of1-index-sync.service + ansible.builtin.copy: + dest: /etc/systemd/system/of1-index-sync.service + mode: "0644" + content: | + [Unit] + Description=Sync yellowstone-faithful indexes (rolling cache) + After=network-online.target + Wants=network-online.target + + [Service] + Type=oneshot + User=root + Environment=WINDOW={{ of1_window }} + Environment=CACHE_DIR={{ of1_cache_dir }} + Environment=CONFIG_DIR={{ of1_config_dir_var }} + Environment=SERVICE_NAME={{ of1_service }} + ExecStart=/usr/local/bin/of1-index-sync.sh + # downloads can be slow, allow up to 4h + TimeoutStartSec=14400 + register: sync_service_unit + + - name: Install of1-index-sync.timer + ansible.builtin.copy: + dest: /etc/systemd/system/of1-index-sync.timer + mode: "0644" + content: | + [Unit] + Description=Daily sync of yellowstone-faithful indexes + + [Timer] + OnCalendar=daily + Persistent=true + RandomizedDelaySec=15min + + [Install] + WantedBy=timers.target + register: sync_timer_unit + + - name: Switch faithful.service ExecStart to start-faithful.sh wrapper + ansible.builtin.lineinfile: + path: /etc/systemd/system/{{ of1_service }} + regexp: '^ExecStart=' + line: 'ExecStart=/usr/local/bin/start-faithful.sh' + backup: yes + register: faithful_unit_changed + + - name: Reload systemd + ansible.builtin.command: systemctl daemon-reload + when: sync_service_unit is changed or sync_timer_unit is changed or faithful_unit_changed is changed + + - name: Enable and start of1-index-sync.timer + ansible.builtin.systemd: + name: of1-index-sync.timer + enabled: yes + state: started + + - name: Restart faithful so wrapper script picks up + ansible.builtin.systemd: + name: "{{ of1_service }}" + state: restarted + when: faithful_unit_changed is changed + + - name: Run initial sync (foreground, may fetch up to {{ of1_window }} epochs) + ansible.builtin.command: /usr/local/bin/of1-index-sync.sh + environment: + WINDOW: "{{ of1_window }}" + CACHE_DIR: "{{ of1_cache_dir }}" + CONFIG_DIR: "{{ of1_config_dir_var }}" + SERVICE_NAME: "{{ of1_service }}" + async: "{{ of1_initial_sync_async_var }}" + poll: 60 + when: of1_run_initial_sync_var + register: initial_sync + + - name: Show initial sync result + ansible.builtin.debug: + msg: >- + {{ 'initial sync finished rc=' ~ (initial_sync.rc | default('skipped')) + if of1_run_initial_sync_var + else 'initial sync skipped (of1_run_initial_sync=false)' }} diff --git a/template/2026.5.21.1448/ansible/cmn/optimize_node.yml b/template/2026.5.21.1448/ansible/cmn/optimize_node.yml new file mode 100644 index 00000000..8dff95e3 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/optimize_node.yml @@ -0,0 +1,46 @@ +--- +# Node-level performance optimization orchestrator. +# +# Runs SMT disable + IRQ tune + boost performance (kernel update) in order. +# Reboots the node at the end IF any step required it (NVMe GRUB / nosmt / +# HWE kernel / amd_pstate cmdline). Waits for the host to come back online. +# +# Idempotent: each step uses a marker file so repeated runs no-op. + +- import_playbook: smt_disable.yml +- import_playbook: irq_tune.yml +- import_playbook: boost_performance.yml + +- name: Reboot if any tuning step requested it + hosts: all + become: yes + gather_facts: false + tasks: + - name: Check if reboot is required by any tuning step + ansible.builtin.stat: + path: /var/run/reboot-required + register: pkg_reboot + + - name: Read SLV reboot flag (if previous play set it) + ansible.builtin.set_fact: + slv_reboot_required: "{{ (need_reboot | default(false)) or pkg_reboot.stat.exists }}" + + - name: Show reboot decision + ansible.builtin.debug: + msg: "slv_reboot_required={{ slv_reboot_required }}" + + - name: Reboot host (and wait for it to come back) + ansible.builtin.reboot: + reboot_timeout: 900 + post_reboot_delay: 30 + test_command: uptime + when: slv_reboot_required + + - name: Confirm post-reboot kernel + ansible.builtin.command: uname -r + register: post_reboot_kernel + changed_when: false + + - name: Report final kernel + ansible.builtin.debug: + msg: "Optimization complete — running kernel: {{ post_reboot_kernel.stdout }}" diff --git a/template/2026.4.27.1539/ansible/cmn/optimize_system.yml b/template/2026.5.21.1448/ansible/cmn/optimize_system.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/optimize_system.yml rename to template/2026.5.21.1448/ansible/cmn/optimize_system.yml diff --git a/template/2026.4.27.1539/ansible/cmn/patch_sha256.yml b/template/2026.5.21.1448/ansible/cmn/patch_sha256.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/patch_sha256.yml rename to template/2026.5.21.1448/ansible/cmn/patch_sha256.yml diff --git a/template/2026.4.27.1539/ansible/cmn/restart_solv.yml b/template/2026.5.21.1448/ansible/cmn/restart_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/restart_solv.yml rename to template/2026.5.21.1448/ansible/cmn/restart_solv.yml diff --git a/template/2026.4.27.1539/ansible/cmn/rm_ledger.yml b/template/2026.5.21.1448/ansible/cmn/rm_ledger.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/rm_ledger.yml rename to template/2026.5.21.1448/ansible/cmn/rm_ledger.yml diff --git a/template/2026.4.27.1539/ansible/cmn/run_restarter.yml b/template/2026.5.21.1448/ansible/cmn/run_restarter.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/run_restarter.yml rename to template/2026.5.21.1448/ansible/cmn/run_restarter.yml diff --git a/template/2026.4.27.1539/ansible/cmn/setup_agave_ufw.yml b/template/2026.5.21.1448/ansible/cmn/setup_agave_ufw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/setup_agave_ufw.yml rename to template/2026.5.21.1448/ansible/cmn/setup_agave_ufw.yml diff --git a/template/2026.4.27.1539/ansible/cmn/setup_doublezero.yml b/template/2026.5.21.1448/ansible/cmn/setup_doublezero.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/setup_doublezero.yml rename to template/2026.5.21.1448/ansible/cmn/setup_doublezero.yml diff --git a/template/2026.4.27.1539/ansible/cmn/setup_logrotate.yml b/template/2026.5.21.1448/ansible/cmn/setup_logrotate.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/setup_logrotate.yml rename to template/2026.5.21.1448/ansible/cmn/setup_logrotate.yml diff --git a/template/2026.4.27.1539/ansible/cmn/setup_node_exporter.yml b/template/2026.5.21.1448/ansible/cmn/setup_node_exporter.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/setup_node_exporter.yml rename to template/2026.5.21.1448/ansible/cmn/setup_node_exporter.yml diff --git a/template/2026.4.27.1539/ansible/cmn/setup_norestart.yml b/template/2026.5.21.1448/ansible/cmn/setup_norestart.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/setup_norestart.yml rename to template/2026.5.21.1448/ansible/cmn/setup_norestart.yml diff --git a/template/2026.4.27.1539/ansible/cmn/setup_ufw.yml b/template/2026.5.21.1448/ansible/cmn/setup_ufw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/setup_ufw.yml rename to template/2026.5.21.1448/ansible/cmn/setup_ufw.yml diff --git a/template/2026.4.27.1539/ansible/cmn/setup_unstaked_identity.yml b/template/2026.5.21.1448/ansible/cmn/setup_unstaked_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/setup_unstaked_identity.yml rename to template/2026.5.21.1448/ansible/cmn/setup_unstaked_identity.yml diff --git a/template/2026.5.21.1448/ansible/cmn/smt_disable.yml b/template/2026.5.21.1448/ansible/cmn/smt_disable.yml new file mode 100644 index 00000000..de2cb522 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/smt_disable.yml @@ -0,0 +1,77 @@ +--- +# SMT (Hyper-Threading) disable via GRUB nosmt=force +# Sets `need_reboot=true` on the host when GRUB was modified. +- name: Disable SMT (Hyper-Threading) via GRUB + hosts: all + become: yes + gather_facts: false + vars: + grub_default: /etc/default/grub + smt_param: "nosmt=force" + smt_marker_file: /var/lib/slv/.smt_disabled + tasks: + - name: Ensure /var/lib/slv exists + ansible.builtin.file: + path: /var/lib/slv + state: directory + mode: "0755" + + - name: Skip if SMT already disabled by SLV + ansible.builtin.stat: + path: "{{ smt_marker_file }}" + register: smt_marker + + - name: Read current GRUB cmdline default + ansible.builtin.shell: | + set -e + # shellcheck disable=SC1091 + . /etc/default/grub + echo "${GRUB_CMDLINE_LINUX_DEFAULT:-}" + register: grub_default_line + changed_when: false + when: not smt_marker.stat.exists + + - name: Add nosmt=force to GRUB_CMDLINE_LINUX_DEFAULT (if missing) + ansible.builtin.shell: | + set -e + # shellcheck disable=SC1091 + . /etc/default/grub + CLEAN=$(echo "${GRUB_CMDLINE_LINUX_DEFAULT:-}" | sed 's/nosmt=[^ ]*//g; s/ */ /g; s/^ //; s/ $//') + NEW="$CLEAN {{ smt_param }}" + NEW=$(echo "$NEW" | sed 's/ */ /g; s/^ //; s/ $//') + sed -i "s|^GRUB_CMDLINE_LINUX_DEFAULT=.*|GRUB_CMDLINE_LINUX_DEFAULT=\"$NEW\"|" {{ grub_default }} + args: + executable: /bin/bash + register: smt_grub_update + when: + - not smt_marker.stat.exists + - smt_param not in (grub_default_line.stdout | default('')) + + - name: Run update-grub + ansible.builtin.command: update-grub + when: + - not smt_marker.stat.exists + - smt_grub_update is changed + register: smt_update_grub + changed_when: true + + - name: Mark SMT disable applied (idempotency marker) + ansible.builtin.copy: + dest: "{{ smt_marker_file }}" + content: "applied at {{ ansible_date_time.iso8601 | default(lookup('pipe', 'date -Iseconds')) }}\n" + mode: "0644" + when: + - not smt_marker.stat.exists + - smt_grub_update is changed + + - name: Set need_reboot fact for orchestrator + ansible.builtin.set_fact: + smt_disable_applied: "{{ (not smt_marker.stat.exists) and (smt_grub_update is changed) }}" + need_reboot: "{{ (need_reboot | default(false)) or ((not smt_marker.stat.exists) and (smt_grub_update is changed)) }}" + + - name: Report SMT status + ansible.builtin.debug: + msg: >- + {{ 'SMT disable applied — reboot required' + if smt_disable_applied + else 'SMT disable already in place — no change' }} diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-firewall.yaml b/template/2026.5.21.1448/ansible/cmn/software/install-firewall.yaml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-firewall.yaml rename to template/2026.5.21.1448/ansible/cmn/software/install-firewall.yaml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-grafana.yml b/template/2026.5.21.1448/ansible/cmn/software/install-grafana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-grafana.yml rename to template/2026.5.21.1448/ansible/cmn/software/install-grafana.yml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-kafka.yml b/template/2026.5.21.1448/ansible/cmn/software/install-kafka.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-kafka.yml rename to template/2026.5.21.1448/ansible/cmn/software/install-kafka.yml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-nginx.yaml b/template/2026.5.21.1448/ansible/cmn/software/install-nginx.yaml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-nginx.yaml rename to template/2026.5.21.1448/ansible/cmn/software/install-nginx.yaml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-node-exporter.yml b/template/2026.5.21.1448/ansible/cmn/software/install-node-exporter.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-node-exporter.yml rename to template/2026.5.21.1448/ansible/cmn/software/install-node-exporter.yml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-prometheus.yml b/template/2026.5.21.1448/ansible/cmn/software/install-prometheus.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-prometheus.yml rename to template/2026.5.21.1448/ansible/cmn/software/install-prometheus.yml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-redis.yml b/template/2026.5.21.1448/ansible/cmn/software/install-redis.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-redis.yml rename to template/2026.5.21.1448/ansible/cmn/software/install-redis.yml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-tidb.yml b/template/2026.5.21.1448/ansible/cmn/software/install-tidb.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-tidb.yml rename to template/2026.5.21.1448/ansible/cmn/software/install-tidb.yml diff --git a/template/2026.4.27.1539/ansible/cmn/software/install-wireguard.yaml b/template/2026.5.21.1448/ansible/cmn/software/install-wireguard.yaml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/software/install-wireguard.yaml rename to template/2026.5.21.1448/ansible/cmn/software/install-wireguard.yaml diff --git a/template/2026.4.27.1539/ansible/cmn/start_firedancer.yml b/template/2026.5.21.1448/ansible/cmn/start_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/start_firedancer.yml rename to template/2026.5.21.1448/ansible/cmn/start_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/cmn/start_node.yml b/template/2026.5.21.1448/ansible/cmn/start_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/start_node.yml rename to template/2026.5.21.1448/ansible/cmn/start_node.yml diff --git a/template/2026.4.27.1539/ansible/cmn/start_solv.yml b/template/2026.5.21.1448/ansible/cmn/start_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/start_solv.yml rename to template/2026.5.21.1448/ansible/cmn/start_solv.yml diff --git a/template/2026.4.27.1539/ansible/cmn/stop_firedancer.yml b/template/2026.5.21.1448/ansible/cmn/stop_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/stop_firedancer.yml rename to template/2026.5.21.1448/ansible/cmn/stop_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/cmn/stop_solv.yml b/template/2026.5.21.1448/ansible/cmn/stop_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/stop_solv.yml rename to template/2026.5.21.1448/ansible/cmn/stop_solv.yml diff --git a/template/2026.4.27.1539/ansible/cmn/tasks/format_and_mount.yml b/template/2026.5.21.1448/ansible/cmn/tasks/format_and_mount.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/tasks/format_and_mount.yml rename to template/2026.5.21.1448/ansible/cmn/tasks/format_and_mount.yml diff --git a/template/2026.5.21.1448/ansible/cmn/tasks/persist_nftables_ruleset.yml b/template/2026.5.21.1448/ansible/cmn/tasks/persist_nftables_ruleset.yml new file mode 100644 index 00000000..680e8a53 --- /dev/null +++ b/template/2026.5.21.1448/ansible/cmn/tasks/persist_nftables_ruleset.yml @@ -0,0 +1,52 @@ +--- +# Export the live nftables ruleset (including set elements added after +# deploy) into /etc/nftables.conf so the same ruleset is restored on +# reboot by nftables.service. +# +# Designed to be `import_tasks`'d from playbooks, not run standalone. + +- name: Resolve nftables.conf path + ansible.builtin.set_fact: + nft_conf_path_effective: "{{ nft_conf_path | default('/etc/nftables.conf') }}" + +- name: Export live nftables ruleset + ansible.builtin.command: nft list ruleset + register: nft_live_ruleset + changed_when: false + +- name: Write generated persistent nftables.conf (temp) + ansible.builtin.copy: + dest: "{{ nft_conf_path_effective }}.generated" + owner: root + group: root + mode: "0644" + content: | + # Managed by slv (generated from live ruleset on {{ ansible_date_time.iso8601 | default('deploy') }}) + flush ruleset + + {{ nft_live_ruleset.stdout | regex_replace('packets \\d+ bytes \\d+', '') }} + +- name: Validate generated persistent config + ansible.builtin.command: "nft -c -f {{ nft_conf_path_effective }}.generated" + changed_when: false + +- name: Install generated nftables.conf + ansible.builtin.copy: + src: "{{ nft_conf_path_effective }}.generated" + dest: "{{ nft_conf_path_effective }}" + remote_src: true + owner: root + group: root + mode: "0644" + +- name: Remove generated temp file + ansible.builtin.file: + path: "{{ nft_conf_path_effective }}.generated" + state: absent + changed_when: false + +- name: Enable + start nftables service (boot restore) + ansible.builtin.service: + name: nftables + enabled: true + state: started diff --git a/template/2026.4.27.1539/ansible/cmn/update_firedancer.yml b/template/2026.5.21.1448/ansible/cmn/update_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/update_firedancer.yml rename to template/2026.5.21.1448/ansible/cmn/update_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/cmn/update_ubuntu.yml b/template/2026.5.21.1448/ansible/cmn/update_ubuntu.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/update_ubuntu.yml rename to template/2026.5.21.1448/ansible/cmn/update_ubuntu.yml diff --git a/template/2026.4.27.1539/ansible/cmn/wget_snapshot.yml b/template/2026.5.21.1448/ansible/cmn/wget_snapshot.yml similarity index 100% rename from template/2026.4.27.1539/ansible/cmn/wget_snapshot.yml rename to template/2026.5.21.1448/ansible/cmn/wget_snapshot.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/create-start-validator-sh.yml b/template/2026.5.21.1448/ansible/devnet-rpc/create-start-validator-sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/create-start-validator-sh.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/create-start-validator-sh.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/geyser_build.yml b/template/2026.5.21.1448/ansible/devnet-rpc/geyser_build.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/geyser_build.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/geyser_build.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/geyser_richat_build.yml b/template/2026.5.21.1448/ansible/devnet-rpc/geyser_richat_build.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/geyser_richat_build.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/geyser_richat_build.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/init.yml b/template/2026.5.21.1448/ansible/devnet-rpc/init.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/init.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/init.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/install_agave.yml b/template/2026.5.21.1448/ansible/devnet-rpc/install_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/install_agave.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/install_agave.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/install_jito.yml b/template/2026.5.21.1448/ansible/devnet-rpc/install_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/install_jito.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/install_jito.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/install_richat.yml b/template/2026.5.21.1448/ansible/devnet-rpc/install_richat.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/install_richat.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/install_richat.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/install_solana.yml b/template/2026.5.21.1448/ansible/devnet-rpc/install_solana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/install_solana.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/install_solana.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/restart_node.yml b/template/2026.5.21.1448/ansible/devnet-rpc/restart_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/restart_node.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/restart_node.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/setup_firedancer.yml b/template/2026.5.21.1448/ansible/devnet-rpc/setup_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/setup_firedancer.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/setup_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/setup_solv_service.yml b/template/2026.5.21.1448/ansible/devnet-rpc/setup_solv_service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/setup_solv_service.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/setup_solv_service.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/start_node.yml b/template/2026.5.21.1448/ansible/devnet-rpc/start_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/start_node.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/start_node.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/stop_node.yml b/template/2026.5.21.1448/ansible/devnet-rpc/stop_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/stop_node.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/stop_node.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/update_geyser.yml b/template/2026.5.21.1448/ansible/devnet-rpc/update_geyser.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/update_geyser.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/update_geyser.yml diff --git a/template/2026.4.27.1539/ansible/devnet-rpc/update_startup_config.yml b/template/2026.5.21.1448/ansible/devnet-rpc/update_startup_config.yml similarity index 100% rename from template/2026.4.27.1539/ansible/devnet-rpc/update_startup_config.yml rename to template/2026.5.21.1448/ansible/devnet-rpc/update_startup_config.yml diff --git a/template/2026.5.21.1448/ansible/mainnet-hermes/init.yml b/template/2026.5.21.1448/ansible/mainnet-hermes/init.yml new file mode 100644 index 00000000..cb44b2c0 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-hermes/init.yml @@ -0,0 +1,17 @@ +--- +# Full Hermes node initialization. Delegates the heavy lift (build + +# systemd) to cmn/install_hermes_stack.yml so the same recipe can be +# reused if Pyth ever ships a separate beta/testnet with its own Pythnet RPC. +# +# Usage: +# ansible-playbook -i ~/.slv/inventory.mainnet.hermes.yml \ +# template/2026.5.5.1612/ansible/mainnet-hermes/init.yml +# +# Hermes does not need an identity key, snapshots, or ledger storage — it's +# a stateless API service. All it does is bridge Pythnet + Wormhole to +# REST/WS, so a modest VPS (4 vCPU / 8 GB / 80 GB) handles it comfortably. + +- import_playbook: ../cmn/fix_permissions.yml +- import_playbook: ../cmn/create_user.yml +- import_playbook: ../cmn/update_ubuntu.yml +- import_playbook: ../cmn/install_hermes_stack.yml diff --git a/template/2026.5.21.1448/ansible/mainnet-hermes/restart_node.yml b/template/2026.5.21.1448/ansible/mainnet-hermes/restart_node.yml new file mode 100644 index 00000000..e78da17a --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-hermes/restart_node.yml @@ -0,0 +1,21 @@ +--- +# Restart the Hermes stack. Beacon and Hermes pick up env changes on +# restart (e.g. switching PYTHNET_HTTP_ADDR from a public URL to a +# self-hosted Pythnet RPC). +- name: Restart Hermes stack + hosts: all + become: true + gather_facts: no + tasks: + - name: Restart nats-server + ansible.builtin.systemd: + name: nats-server.service + state: restarted + - name: Restart beacon + ansible.builtin.systemd: + name: beacon.service + state: restarted + - name: Restart hermes + ansible.builtin.systemd: + name: hermes.service + state: restarted diff --git a/template/2026.5.21.1448/ansible/mainnet-hermes/start_node.yml b/template/2026.5.21.1448/ansible/mainnet-hermes/start_node.yml new file mode 100644 index 00000000..2d3f36c5 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-hermes/start_node.yml @@ -0,0 +1,23 @@ +--- +# Start the Hermes stack (nats → beacon → hermes). Useful after a host +# reboot or as a no-op idempotent kickstart. +- name: Start Hermes stack + hosts: all + become: true + gather_facts: no + tasks: + - name: Start nats-server + ansible.builtin.systemd: + name: nats-server.service + state: started + enabled: yes + - name: Start beacon + ansible.builtin.systemd: + name: beacon.service + state: started + enabled: yes + - name: Start hermes + ansible.builtin.systemd: + name: hermes.service + state: started + enabled: yes diff --git a/template/2026.5.21.1448/ansible/mainnet-hermes/stop_node.yml b/template/2026.5.21.1448/ansible/mainnet-hermes/stop_node.yml new file mode 100644 index 00000000..10630213 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-hermes/stop_node.yml @@ -0,0 +1,20 @@ +--- +# Stop the Hermes stack. Reverse dependency order so dependents shut down +# before their backing services. +- name: Stop Hermes stack + hosts: all + become: true + gather_facts: no + tasks: + - name: Stop hermes + ansible.builtin.systemd: + name: hermes.service + state: stopped + - name: Stop beacon + ansible.builtin.systemd: + name: beacon.service + state: stopped + - name: Stop nats-server + ansible.builtin.systemd: + name: nats-server.service + state: stopped diff --git a/template/2026.5.21.1448/ansible/mainnet-hermes/update_hermes.yml b/template/2026.5.21.1448/ansible/mainnet-hermes/update_hermes.yml new file mode 100644 index 00000000..42bd865a --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-hermes/update_hermes.yml @@ -0,0 +1,6 @@ +--- +# Re-run the build step against a (possibly updated) hermes_repo_ref to pull +# in upstream changes. Safe to run any time — the install_hermes_stack +# playbook is idempotent and only restarts services when the binary +# actually changed. +- import_playbook: ../cmn/install_hermes_stack.yml diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/build_pythnet.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/build_pythnet.yml new file mode 100644 index 00000000..d611b1f9 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/build_pythnet.yml @@ -0,0 +1,77 @@ +--- +# Clone pyth-network/pythnet and build solana-validator + solana-keygen + +# solana-gossip. +# +# Pythnet is a Solana 1.14 fork — the bundled rocksdb (v7.4.4) fails to +# compile under modern GCC because it relies on the legacy implicit-include +# behavior for . We work around this by setting CXXFLAGS for the +# build only (NOT CFLAGS — blake3 uses cc to assemble .S files and +# -include cstdint would be prepended to assembly source, breaking it). + +- name: Build Pythnet validator from source + hosts: all + become: true + vars: + pythnet_repo: "{{ pythnet_repo | default('https://github.com/pyth-network/pythnet') }}" + pythnet_ref: "{{ pythnet_ref | default('pyth-v1.14.17') }}" + pythnet_ledger_mount: "{{ pythnet_ledger_mount | default('/mnt/ledger') }}" + src_dir: "{{ pythnet_ledger_mount }}/src/pythnet" + + tasks: + - name: Ensure src dir exists + ansible.builtin.file: + path: "{{ src_dir | dirname }}" + state: directory + owner: solv + group: solv + mode: "0755" + + - name: Clone pythnet repo (shallow, pinned branch) + become_user: solv + ansible.builtin.git: + repo: "{{ pythnet_repo }}" + dest: "{{ src_dir }}" + version: "{{ pythnet_ref }}" + depth: 1 + force: yes + register: pythnet_git + + # CXXFLAGS workaround: rocksdb's data_block_hash_index.h drops + # uint*_t under modern GCC because isn't transitively + # included. -include cstdint fixes the build without patching + # upstream sources. Scoped to C++ only — blake3's build.rs uses cc + # to assemble .S files and a C-level -include would corrupt those. + - name: Build solana-validator + keygen + gossip + become_user: solv + ansible.builtin.shell: 'source $HOME/.cargo/env && export CXXFLAGS="-include cstdint" && unset CFLAGS && cargo build --release --bin solana-validator --bin solana-keygen --bin solana-gossip' + args: + chdir: "{{ src_dir }}" + executable: /bin/bash + register: build_result + changed_when: build_result.rc == 0 + when: pythnet_git.changed + + - name: Verify binaries exist + ansible.builtin.stat: + path: "{{ src_dir }}/target/release/{{ item }}" + loop: + - solana-validator + - solana-keygen + - solana-gossip + register: bin_stat + + - name: Fail if any binary is missing + ansible.builtin.assert: + that: bin_stat.results | map(attribute='stat.exists') | list | min + fail_msg: "build_pythnet: one or more binaries missing under {{ src_dir }}/target/release/" + + - name: Symlink binaries to /usr/local/bin + ansible.builtin.file: + src: "{{ src_dir }}/target/release/{{ item }}" + dest: "/usr/local/bin/{{ item }}" + state: link + force: yes + loop: + - solana-validator + - solana-keygen + - solana-gossip diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/create-start-pythnet-sh.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/create-start-pythnet-sh.yml new file mode 100644 index 00000000..22fbd285 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/create-start-pythnet-sh.yml @@ -0,0 +1,13 @@ +--- +- name: Render start-pythnet.sh from Jinja template + hosts: all + become: true + gather_facts: no + tasks: + - name: Drop start-pythnet.sh + ansible.builtin.template: + src: "~/.slv/mainnet-pythnet/start-pythnet.sh.j2" + dest: /home/solv/start-pythnet.sh + owner: solv + group: solv + mode: "0755" diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/gen_identity.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/gen_identity.yml new file mode 100644 index 00000000..9635bd80 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/gen_identity.yml @@ -0,0 +1,31 @@ +--- +# Generate the validator identity keypair if not already present. +# +# Pythnet RPC nodes run with --no-voting, so this key is only used as a +# gossip identity — it does not hold stake or sign votes. Losing it has +# no monetary consequence; the validator just rejoins gossip under a new +# pubkey on next start. +- name: Generate Pythnet validator identity + hosts: all + become: true + gather_facts: no + tasks: + - name: Create identity.json (no-bip39-passphrase) if absent + become_user: solv + ansible.builtin.shell: | + /usr/local/bin/solana-keygen new \ + --no-bip39-passphrase \ + --outfile /home/solv/pythnet-identity.json \ + --silent + args: + creates: /home/solv/pythnet-identity.json + + - name: Read pubkey + become_user: solv + ansible.builtin.command: /usr/local/bin/solana-keygen pubkey /home/solv/pythnet-identity.json + register: pubkey_result + changed_when: false + + - name: Show pubkey + ansible.builtin.debug: + msg: "Pythnet identity pubkey: {{ pubkey_result.stdout }}" diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/init.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/init.yml new file mode 100644 index 00000000..e78ec7f2 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/init.yml @@ -0,0 +1,23 @@ +--- +# Pythnet RPC node initialization. +# +# Pythnet is an application-specific Solana fork operated by Pyth's data +# providers. Running a Pythnet RPC node lets you serve Pythnet HTTP+WS +# directly to a self-hosted Hermes (see ../mainnet-hermes/), eliminating +# the dependency on public Pythnet endpoints. +# +# Usage: +# ansible-playbook -i ~/.slv/inventory.mainnet.pythnet.yml \ +# template/2026.5.5.1612/ansible/mainnet-pythnet/init.yml + +- import_playbook: ../cmn/fix_permissions.yml +- import_playbook: ../cmn/create_user.yml +- import_playbook: ../cmn/update_ubuntu.yml +- import_playbook: install_package.yml +- import_playbook: install_rust.yml +- import_playbook: mount_disks.yml +- import_playbook: optimize_pythnet.yml +- import_playbook: build_pythnet.yml +- import_playbook: gen_identity.yml +- import_playbook: create-start-pythnet-sh.yml +- import_playbook: setup-pythnet-service.yml diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/install_package.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/install_package.yml new file mode 100644 index 00000000..843a3ceb --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/install_package.yml @@ -0,0 +1,23 @@ +--- +- name: Install build dependencies for Pythnet (Solana 1.14 fork) + hosts: all + become: true + gather_facts: no + tasks: + - name: apt install + ansible.builtin.apt: + name: + - build-essential + - pkg-config + - libssl-dev + - libudev-dev + - clang + - cmake + - protobuf-compiler + - git + - curl + - jq + - ca-certificates + state: present + update_cache: yes + cache_valid_time: 3600 diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/install_rust.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/install_rust.yml new file mode 100644 index 00000000..65e618fc --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/install_rust.yml @@ -0,0 +1,19 @@ +--- +# Pythnet's pyth-v1.14.17 branch pins rust-toolchain = "1.60.0". rustup +# auto-downloads 1.60 when building inside the repo, so we only need a +# bootstrap toolchain — 1.78.0 is recent enough to run rustup itself and +# small enough to install quickly. +- name: Install Rust toolchain for solv + hosts: all + become: true + gather_facts: no + vars: + pythnet_rust_toolchain: "{{ pythnet_rust_toolchain | default('1.78.0') }}" + tasks: + - name: Install rustup via the official installer + become_user: solv + ansible.builtin.shell: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --default-toolchain {{ pythnet_rust_toolchain }} --profile minimal + args: + creates: /home/solv/.cargo/bin/cargo diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/mount_disks.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/mount_disks.yml new file mode 100644 index 00000000..4a8f009d --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/mount_disks.yml @@ -0,0 +1,82 @@ +--- +# Format and mount the ledger disk for Pythnet. +# +# Pythnet ledger is much smaller than Solana mainnet (~10 GB/day vs +# 200+ GB/day) so a single NVMe (or even a directory on the OS disk) is +# sufficient. This playbook is opt-in via `pythnet_format_disk` — leave +# the default (false) if you'd rather manage the mount yourself or use a +# directory on the root filesystem. +# +# Inventory variables: +# pythnet_format_disk false Set to true to actually run mkfs+mount +# pythnet_ledger_device /dev/nvme0n1 +# pythnet_ledger_mount /mnt/ledger + +- name: Provision ledger disk for Pythnet + hosts: all + become: true + gather_facts: yes + vars: + do_format: "{{ pythnet_format_disk | default(false) | bool }}" + device: "{{ pythnet_ledger_device | default('/dev/nvme0n1') }}" + mount_point: "{{ pythnet_ledger_mount | default('/mnt/ledger') }}" + + tasks: + - name: Ensure mount point exists + ansible.builtin.file: + path: "{{ mount_point }}" + state: directory + owner: solv + group: solv + mode: "0755" + + # Guard: refuse to format an already-mounted or partitioned device unless + # the operator explicitly asks for a rebuild. Without this guard a + # re-run of init.yml could wipe an existing ledger. + - name: Probe device for existing partition table / FS + ansible.builtin.command: "blkid -p {{ device }}" + register: dev_probe + changed_when: false + failed_when: false + when: do_format + + - name: Refuse to wipe a non-empty device unless pythnet_force_format=true + ansible.builtin.fail: + msg: "{{ device }} already has a filesystem/partition table; set pythnet_force_format=true to rebuild" + when: + - do_format + - dev_probe.rc == 0 + - dev_probe.stdout | length > 0 + - not (pythnet_force_format | default(false) | bool) + + - name: Wipe device (XFS — single agcount=32 stripe) + ansible.builtin.shell: | + wipefs -a {{ device }} + mkfs.xfs -f -d agcount=32 {{ device }} + when: + - do_format + - (dev_probe.rc != 0) or (pythnet_force_format | default(false) | bool) + + - name: Add to fstab + mount + ansible.posix.mount: + path: "{{ mount_point }}" + src: "{{ device }}" + fstype: xfs + opts: "noatime,nodiratime,inode64,logbufs=8,logbsize=256k" + state: mounted + when: do_format + + - name: Ensure ledger sub-dirs exist + ansible.builtin.file: + path: "{{ mount_point }}/{{ item }}" + state: directory + owner: solv + group: solv + mode: "0755" + loop: + - "pythnet" + - "pythnet/ledger" + - "pythnet/accounts" + - "pythnet/snapshots" + - "pythnet/log" + - "src" diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/optimize_pythnet.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/optimize_pythnet.yml new file mode 100644 index 00000000..121ac682 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/optimize_pythnet.yml @@ -0,0 +1,27 @@ +--- +# sysctl tuning for Pythnet validator. Same network buffers Solana +# expects. The validator's startup self-check fails fast if the four +# rmem/wmem values aren't all 128 MiB, so we set defaults to match max. +- name: Apply sysctl tuning for Pythnet validator + hosts: all + become: true + gather_facts: no + tasks: + - name: Drop /etc/sysctl.d/99-pythnet.conf + ansible.builtin.copy: + dest: /etc/sysctl.d/99-pythnet.conf + owner: root + group: root + mode: "0644" + content: | + # Pythnet (Solana 1.14 fork) network buffers. Required to pass + # solana_core::system_monitor_service's startup self-check. + net.core.rmem_default = 134217728 + net.core.rmem_max = 134217728 + net.core.wmem_default = 134217728 + net.core.wmem_max = 134217728 + vm.max_map_count = 1000000 + + - name: Reload sysctl + ansible.builtin.command: sysctl --system + changed_when: false diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/restart_node.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/restart_node.yml new file mode 100644 index 00000000..6b89de34 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/restart_node.yml @@ -0,0 +1,9 @@ +--- +- name: Restart Pythnet validator + hosts: all + become: true + gather_facts: no + tasks: + - ansible.builtin.systemd: + name: pythnet.service + state: restarted diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/setup-pythnet-service.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/setup-pythnet-service.yml new file mode 100644 index 00000000..74b06aeb --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/setup-pythnet-service.yml @@ -0,0 +1,32 @@ +--- +- name: Install + start Pythnet systemd service + hosts: all + become: true + gather_facts: no + tasks: + - name: Drop pythnet.service unit + ansible.builtin.template: + src: "~/.slv/mainnet-pythnet/pythnet.service.j2" + dest: /etc/systemd/system/pythnet.service + owner: root + group: root + mode: "0644" + + - name: daemon-reload + ansible.builtin.command: systemctl daemon-reload + changed_when: false + + - name: Enable + start pythnet + ansible.builtin.systemd: + name: pythnet.service + enabled: yes + state: started + + # Pythnet ledger is small enough that snapshot fetch + catch-up + # typically finishes in 10-20 minutes (vs hours on mainnet). We + # don't block init.yml on getHealth=ok here — the validator boots + # asynchronously and `slv pythnet status` (or a direct curl) is the + # right place to verify readiness. + - name: Note next step + ansible.builtin.debug: + msg: "pythnet.service started. Tail /mnt/ledger/pythnet/log/validator.log to watch snapshot fetch + catch-up; expect ~10-20 min before getHealth='ok'." diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/start_node.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/start_node.yml new file mode 100644 index 00000000..77b5b45e --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/start_node.yml @@ -0,0 +1,10 @@ +--- +- name: Start Pythnet validator + hosts: all + become: true + gather_facts: no + tasks: + - ansible.builtin.systemd: + name: pythnet.service + state: started + enabled: yes diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/stop_node.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/stop_node.yml new file mode 100644 index 00000000..489fcf2b --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/stop_node.yml @@ -0,0 +1,9 @@ +--- +- name: Stop Pythnet validator + hosts: all + become: true + gather_facts: no + tasks: + - ansible.builtin.systemd: + name: pythnet.service + state: stopped diff --git a/template/2026.5.21.1448/ansible/mainnet-pythnet/update_pythnet.yml b/template/2026.5.21.1448/ansible/mainnet-pythnet/update_pythnet.yml new file mode 100644 index 00000000..cb463681 --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-pythnet/update_pythnet.yml @@ -0,0 +1,15 @@ +--- +# Pull updates from pythnet_ref, rebuild, restart. Safe to run between +# upstream releases — the build is idempotent and only restarts when the +# binary changes. +- import_playbook: build_pythnet.yml +- import_playbook: create-start-pythnet-sh.yml + +- name: Restart pythnet + hosts: all + become: true + gather_facts: no + tasks: + - ansible.builtin.systemd: + name: pythnet.service + state: restarted diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/add_solv.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/add_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/add_solv.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/add_solv.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/allow_ufw.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/allow_ufw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/allow_ufw.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/allow_ufw.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/configure_hugetlbfs.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/configure_hugetlbfs.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/configure_hugetlbfs.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/configure_hugetlbfs.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/copy_keys.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/copy_keys.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/copy_keys.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/copy_keys.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/create-start-validator-sh.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/create-start-validator-sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/create-start-validator-sh.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/create-start-validator-sh.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/create-symlink.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/create-symlink.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/create-symlink.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/create-symlink.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/fail2ban_solana_rate_limit.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/fail2ban_solana_rate_limit.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/fail2ban_solana_rate_limit.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/fail2ban_solana_rate_limit.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/fail2ban_sshd.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/fail2ban_sshd.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/fail2ban_sshd.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/fail2ban_sshd.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/geyser_build.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/geyser_build.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/geyser_build.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/geyser_build.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/geyser_richat_build.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/geyser_richat_build.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/geyser_richat_build.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/geyser_richat_build.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/init-old.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/init-old.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/init-old.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/init-old.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/init.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/init.yml similarity index 88% rename from template/2026.4.27.1539/ansible/mainnet-rpc/init.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/init.yml index 9021a4a6..32d0824b 100644 --- a/template/2026.4.27.1539/ansible/mainnet-rpc/init.yml +++ b/template/2026.5.21.1448/ansible/mainnet-rpc/init.yml @@ -69,3 +69,10 @@ - import_playbook: install_of1_service.yml when: - rpc_type in ['Index RPC', 'Index RPC + gRPC'] + +# Rolling cache: keep last N (default 30) epochs of indexes locally so +# getTransaction etc. answer in ~70ms instead of ~300ms. CAR files stay +# remote. Maintained by a daily systemd timer. +- import_playbook: ../cmn/of1_index_cache_sync.yml + when: + - rpc_type in ['Index RPC', 'Index RPC + gRPC'] diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/init_richat_geyser.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/init_richat_geyser.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/init_richat_geyser.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/init_richat_geyser.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_agave.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_agave.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_agave.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_jito.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_jito.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_jito.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_of1.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_of1.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_of1.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_of1.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_of1_service.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_of1_service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_of1_service.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_of1_service.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_package.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_package.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_package.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_package.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_richat.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_richat.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_richat.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_richat.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_rust.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_rust.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_rust.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_rust.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/install_solana.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/install_solana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/install_solana.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/install_solana.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/mount_disks.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/mount_disks.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/mount_disks.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/mount_disks.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/optimize_system.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/optimize_system.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/optimize_system.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/optimize_system.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/restart_node.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/restart_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/restart_node.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/restart_node.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/run_restarter.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/run_restarter.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/run_restarter.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/run_restarter.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/run_snapshot_finder.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/run_snapshot_finder.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/run_snapshot_finder.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/run_snapshot_finder.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/setup-solv-service.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/setup-solv-service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/setup-solv-service.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/setup-solv-service.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/setup_firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/setup_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/setup_firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/setup_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/setup_logrotate.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/setup_logrotate.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/setup_logrotate.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/setup_logrotate.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/setup_norestart.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/setup_norestart.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/setup_norestart.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/setup_norestart.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/setup_ufw.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/setup_ufw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/setup_ufw.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/setup_ufw.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/start-solv-service.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/start-solv-service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/start-solv-service.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/start-solv-service.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/start_firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/start_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/start_firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/start_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/start_node.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/start_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/start_node.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/start_node.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/start_solv.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/start_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/start_solv.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/start_solv.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/stop_firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/stop_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/stop_firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/stop_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/stop_node.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/stop_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/stop_node.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/stop_node.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/stop_solv.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/stop_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/stop_solv.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/stop_solv.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/update_geyser.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/update_geyser.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/update_geyser.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/update_geyser.yml diff --git a/template/2026.5.21.1448/ansible/mainnet-rpc/update_richat_config.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/update_richat_config.yml new file mode 100644 index 00000000..9a1c05fb --- /dev/null +++ b/template/2026.5.21.1448/ansible/mainnet-rpc/update_richat_config.yml @@ -0,0 +1,62 @@ +--- +# Lightweight playbook to update only the richat config + restart on change. +# Use install_richat.yml when you want to (re)build the binary too; this one +# never touches the binary, so it's safe to run frequently. +# +# Idempotent: if the rendered template matches what's on disk, no change and +# no restart. When the template differs, the file is updated and richat is +# restarted via a handler (single restart even on multi-task changes). +# +# Usage: +# ansible-playbook -i ~/.slv/inventory.mainnet.rpcs.yml \ +# template/2026.5.5.1612/ansible/mainnet-rpc/update_richat_config.yml +# +# # Limit to specific hosts (replace with the inventory hostnames you want): +# ansible-playbook ... --limit 'host-a,host-b' + +- name: Update richat config (no rebuild) + hosts: all + gather_facts: no + vars: + richat_config_template: "~/.slv/mainnet-rpc/richat-setting.yml.j2" + richat_config_path: "/home/solv/richat-config.yml" + + tasks: + - name: Render richat-config.yml from template + become: true + ansible.builtin.template: + src: "{{ richat_config_template }}" + dest: "{{ richat_config_path }}" + owner: solv + group: solv + mode: "0644" + # Backup the previous file so we can roll back manually if a config + # change breaks richat. Backups land at .~. + backup: yes + register: rg_render + notify: restart richat + + # Reachability of the new ports is the contract this playbook enforces. + # If a future config change unbinds 7111 (or moves it), this task surfaces + # the regression instead of leaving a silently-broken node. + - name: Force handlers now (so the wait below sees the restarted process) + ansible.builtin.meta: flush_handlers + + - name: Wait for richat to be listening on 10000 / 10100 / 7111 + ansible.builtin.wait_for: + host: 127.0.0.1 + port: "{{ item }}" + state: started + timeout: 30 + loop: + - 10000 # apps.grpc.server (Yellowstone-compatible) + - 10100 # apps.richat.grpc (richat-native) + - 7111 # apps.pubsub (Solana JSON-RPC pubsub WS) + when: rg_render.changed + + handlers: + - name: restart richat + become: true + ansible.builtin.systemd: + name: richat.service + state: restarted diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/update_startup_config.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/update_startup_config.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/update_startup_config.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/update_startup_config.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-rpc/update_ubuntu.yml b/template/2026.5.21.1448/ansible/mainnet-rpc/update_ubuntu.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-rpc/update_ubuntu.yml rename to template/2026.5.21.1448/ansible/mainnet-rpc/update_ubuntu.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/configure_hugetlbfs.yml b/template/2026.5.21.1448/ansible/mainnet-validator/configure_hugetlbfs.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/configure_hugetlbfs.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/configure_hugetlbfs.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/copy_keys.yml b/template/2026.5.21.1448/ansible/mainnet-validator/copy_keys.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/copy_keys.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/copy_keys.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/copy_restart_sh.yml b/template/2026.5.21.1448/ansible/mainnet-validator/copy_restart_sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/copy_restart_sh.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/copy_restart_sh.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/create-start-validator-sh.yml b/template/2026.5.21.1448/ansible/mainnet-validator/create-start-validator-sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/create-start-validator-sh.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/create-start-validator-sh.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/create_overrides.yml b/template/2026.5.21.1448/ansible/mainnet-validator/create_overrides.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/create_overrides.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/create_overrides.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/deploy-start-validator-sh.yml b/template/2026.5.21.1448/ansible/mainnet-validator/deploy-start-validator-sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/deploy-start-validator-sh.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/deploy-start-validator-sh.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/fail2ban_solana_rate_limit.yml b/template/2026.5.21.1448/ansible/mainnet-validator/fail2ban_solana_rate_limit.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/fail2ban_solana_rate_limit.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/fail2ban_solana_rate_limit.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/init-allnodes-jito.yml b/template/2026.5.21.1448/ansible/mainnet-validator/init-allnodes-jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/init-allnodes-jito.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/init-allnodes-jito.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/init-firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-validator/init-firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/init-firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/init-firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/init-jito.yml b/template/2026.5.21.1448/ansible/mainnet-validator/init-jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/init-jito.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/init-jito.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/init.yml b/template/2026.5.21.1448/ansible/mainnet-validator/init.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/init.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/init.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/install_agave.yml b/template/2026.5.21.1448/ansible/mainnet-validator/install_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/install_agave.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/install_agave.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/install_allnodes_jito.yml b/template/2026.5.21.1448/ansible/mainnet-validator/install_allnodes_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/install_allnodes_jito.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/install_allnodes_jito.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/install_jito.yml b/template/2026.5.21.1448/ansible/mainnet-validator/install_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/install_jito.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/install_jito.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/install_rust.yml b/template/2026.5.21.1448/ansible/mainnet-validator/install_rust.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/install_rust.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/install_rust.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/install_solana.yml b/template/2026.5.21.1448/ansible/mainnet-validator/install_solana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/install_solana.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/install_solana.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/nodowntime_migrate.yml b/template/2026.5.21.1448/ansible/mainnet-validator/nodowntime_migrate.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/nodowntime_migrate.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/nodowntime_migrate.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/restart_node.yml b/template/2026.5.21.1448/ansible/mainnet-validator/restart_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/restart_node.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/restart_node.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/run_snapshot_finder.yml b/template/2026.5.21.1448/ansible/mainnet-validator/run_snapshot_finder.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/run_snapshot_finder.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/run_snapshot_finder.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/set_identity_key.yml b/template/2026.5.21.1448/ansible/mainnet-validator/set_identity_key.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/set_identity_key.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/set_identity_key.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/set_identity_to_active.yml b/template/2026.5.21.1448/ansible/mainnet-validator/set_identity_to_active.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/set_identity_to_active.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/set_identity_to_active.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/set_unstaked_key.yml b/template/2026.5.21.1448/ansible/mainnet-validator/set_unstaked_key.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/set_unstaked_key.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/set_unstaked_key.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/setup_fb_ufw.yml b/template/2026.5.21.1448/ansible/mainnet-validator/setup_fb_ufw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/setup_fb_ufw.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/setup_fb_ufw.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/setup_firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-validator/setup_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/setup_firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/setup_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/setup_logrotate.yml b/template/2026.5.21.1448/ansible/mainnet-validator/setup_logrotate.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/setup_logrotate.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/setup_logrotate.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/setup_solv_service.yml b/template/2026.5.21.1448/ansible/mainnet-validator/setup_solv_service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/setup_solv_service.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/setup_solv_service.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/setup_ufw.yml b/template/2026.5.21.1448/ansible/mainnet-validator/setup_ufw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/setup_ufw.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/setup_ufw.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/start-solv-service.yml b/template/2026.5.21.1448/ansible/mainnet-validator/start-solv-service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/start-solv-service.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/start-solv-service.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/start_firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-validator/start_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/start_firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/start_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/start_node.yml b/template/2026.5.21.1448/ansible/mainnet-validator/start_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/start_node.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/start_node.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/start_solv.yml b/template/2026.5.21.1448/ansible/mainnet-validator/start_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/start_solv.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/start_solv.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/stop_firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-validator/stop_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/stop_firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/stop_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/stop_node.yml b/template/2026.5.21.1448/ansible/mainnet-validator/stop_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/stop_node.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/stop_node.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/stop_solv.yml b/template/2026.5.21.1448/ansible/mainnet-validator/stop_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/stop_solv.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/stop_solv.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/switch_off_firedancer_identity.yml b/template/2026.5.21.1448/ansible/mainnet-validator/switch_off_firedancer_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/switch_off_firedancer_identity.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/switch_off_firedancer_identity.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/switch_off_identity.yml b/template/2026.5.21.1448/ansible/mainnet-validator/switch_off_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/switch_off_identity.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/switch_off_identity.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/switch_on_firedancer_identity.yml b/template/2026.5.21.1448/ansible/mainnet-validator/switch_on_firedancer_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/switch_on_firedancer_identity.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/switch_on_firedancer_identity.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/switch_on_identity.yml b/template/2026.5.21.1448/ansible/mainnet-validator/switch_on_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/switch_on_identity.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/switch_on_identity.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/update_firedancer.yml b/template/2026.5.21.1448/ansible/mainnet-validator/update_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/update_firedancer.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/update_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/mainnet-validator/update_startup_config.yml b/template/2026.5.21.1448/ansible/mainnet-validator/update_startup_config.yml similarity index 100% rename from template/2026.4.27.1539/ansible/mainnet-validator/update_startup_config.yml rename to template/2026.5.21.1448/ansible/mainnet-validator/update_startup_config.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/create-start-validator-sh.yml b/template/2026.5.21.1448/ansible/testnet-rpc/create-start-validator-sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/create-start-validator-sh.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/create-start-validator-sh.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/geyser_build.yml b/template/2026.5.21.1448/ansible/testnet-rpc/geyser_build.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/geyser_build.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/geyser_build.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/geyser_richat_build.yml b/template/2026.5.21.1448/ansible/testnet-rpc/geyser_richat_build.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/geyser_richat_build.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/geyser_richat_build.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/init.yml b/template/2026.5.21.1448/ansible/testnet-rpc/init.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/init.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/init.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/install_agave.yml b/template/2026.5.21.1448/ansible/testnet-rpc/install_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/install_agave.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/install_agave.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/install_jito.yml b/template/2026.5.21.1448/ansible/testnet-rpc/install_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/install_jito.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/install_jito.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/install_richat.yml b/template/2026.5.21.1448/ansible/testnet-rpc/install_richat.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/install_richat.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/install_richat.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/install_solana.yml b/template/2026.5.21.1448/ansible/testnet-rpc/install_solana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/install_solana.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/install_solana.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/restart_node.yml b/template/2026.5.21.1448/ansible/testnet-rpc/restart_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/restart_node.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/restart_node.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/setup_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-rpc/setup_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/setup_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/setup_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/setup_solv_service.yml b/template/2026.5.21.1448/ansible/testnet-rpc/setup_solv_service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/setup_solv_service.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/setup_solv_service.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/start_node.yml b/template/2026.5.21.1448/ansible/testnet-rpc/start_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/start_node.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/start_node.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/stop_node.yml b/template/2026.5.21.1448/ansible/testnet-rpc/stop_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/stop_node.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/stop_node.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/update_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-rpc/update_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/update_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/update_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/update_geyser.yml b/template/2026.5.21.1448/ansible/testnet-rpc/update_geyser.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/update_geyser.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/update_geyser.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/update_startup_config.yml b/template/2026.5.21.1448/ansible/testnet-rpc/update_startup_config.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/update_startup_config.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/update_startup_config.yml diff --git a/template/2026.4.27.1539/ansible/testnet-rpc/wget_snapshot.yml b/template/2026.5.21.1448/ansible/testnet-rpc/wget_snapshot.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-rpc/wget_snapshot.yml rename to template/2026.5.21.1448/ansible/testnet-rpc/wget_snapshot.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/add_solv.yml b/template/2026.5.21.1448/ansible/testnet-validator/add_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/add_solv.yml rename to template/2026.5.21.1448/ansible/testnet-validator/add_solv.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/change_identity_and_restart.yml b/template/2026.5.21.1448/ansible/testnet-validator/change_identity_and_restart.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/change_identity_and_restart.yml rename to template/2026.5.21.1448/ansible/testnet-validator/change_identity_and_restart.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/copy_keys.yml b/template/2026.5.21.1448/ansible/testnet-validator/copy_keys.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/copy_keys.yml rename to template/2026.5.21.1448/ansible/testnet-validator/copy_keys.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/create-start-validator-sh-agave.yml b/template/2026.5.21.1448/ansible/testnet-validator/create-start-validator-sh-agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/create-start-validator-sh-agave.yml rename to template/2026.5.21.1448/ansible/testnet-validator/create-start-validator-sh-agave.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/create-start-validator-sh-jito.yml b/template/2026.5.21.1448/ansible/testnet-validator/create-start-validator-sh-jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/create-start-validator-sh-jito.yml rename to template/2026.5.21.1448/ansible/testnet-validator/create-start-validator-sh-jito.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/deploy-start-validator-sh.yml b/template/2026.5.21.1448/ansible/testnet-validator/deploy-start-validator-sh.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/deploy-start-validator-sh.yml rename to template/2026.5.21.1448/ansible/testnet-validator/deploy-start-validator-sh.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/init-agave.yml b/template/2026.5.21.1448/ansible/testnet-validator/init-agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/init-agave.yml rename to template/2026.5.21.1448/ansible/testnet-validator/init-agave.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/init-firedancer.yml b/template/2026.5.21.1448/ansible/testnet-validator/init-firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/init-firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-validator/init-firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/init.yml b/template/2026.5.21.1448/ansible/testnet-validator/init.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/init.yml rename to template/2026.5.21.1448/ansible/testnet-validator/init.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/install_agave.yml b/template/2026.5.21.1448/ansible/testnet-validator/install_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/install_agave.yml rename to template/2026.5.21.1448/ansible/testnet-validator/install_agave.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/install_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-validator/install_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/install_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-validator/install_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/install_jito.yml b/template/2026.5.21.1448/ansible/testnet-validator/install_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/install_jito.yml rename to template/2026.5.21.1448/ansible/testnet-validator/install_jito.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/install_solana.yml b/template/2026.5.21.1448/ansible/testnet-validator/install_solana.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/install_solana.yml rename to template/2026.5.21.1448/ansible/testnet-validator/install_solana.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/nodowntime_migrate.yml b/template/2026.5.21.1448/ansible/testnet-validator/nodowntime_migrate.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/nodowntime_migrate.yml rename to template/2026.5.21.1448/ansible/testnet-validator/nodowntime_migrate.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/restart_agave_with_rm_ledger.yml b/template/2026.5.21.1448/ansible/testnet-validator/restart_agave_with_rm_ledger.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/restart_agave_with_rm_ledger.yml rename to template/2026.5.21.1448/ansible/testnet-validator/restart_agave_with_rm_ledger.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/restart_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-validator/restart_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/restart_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-validator/restart_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/restart_firedancer_with_rm_ledger.yml b/template/2026.5.21.1448/ansible/testnet-validator/restart_firedancer_with_rm_ledger.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/restart_firedancer_with_rm_ledger.yml rename to template/2026.5.21.1448/ansible/testnet-validator/restart_firedancer_with_rm_ledger.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/restart_node.yml b/template/2026.5.21.1448/ansible/testnet-validator/restart_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/restart_node.yml rename to template/2026.5.21.1448/ansible/testnet-validator/restart_node.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/restart_solv.yml b/template/2026.5.21.1448/ansible/testnet-validator/restart_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/restart_solv.yml rename to template/2026.5.21.1448/ansible/testnet-validator/restart_solv.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/rm_ledger.yml b/template/2026.5.21.1448/ansible/testnet-validator/rm_ledger.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/rm_ledger.yml rename to template/2026.5.21.1448/ansible/testnet-validator/rm_ledger.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/set_identity_key.yml b/template/2026.5.21.1448/ansible/testnet-validator/set_identity_key.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/set_identity_key.yml rename to template/2026.5.21.1448/ansible/testnet-validator/set_identity_key.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/set_identity_to_active.yml b/template/2026.5.21.1448/ansible/testnet-validator/set_identity_to_active.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/set_identity_to_active.yml rename to template/2026.5.21.1448/ansible/testnet-validator/set_identity_to_active.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/set_unstaked_key.yml b/template/2026.5.21.1448/ansible/testnet-validator/set_unstaked_key.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/set_unstaked_key.yml rename to template/2026.5.21.1448/ansible/testnet-validator/set_unstaked_key.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_agave.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_agave.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_agave.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_agave_ufw.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_agave_ufw.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_agave_ufw.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_agave_ufw.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_firedancer_agave.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_firedancer_agave.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_firedancer_agave.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_firedancer_agave.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_firedancer_jito.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_firedancer_jito.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_firedancer_jito.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_firedancer_jito.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_snapshot_finder.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_snapshot_finder.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_snapshot_finder.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_snapshot_finder.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_solv_service.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_solv_service.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_solv_service.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_solv_service.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/setup_solv_service_init.yml b/template/2026.5.21.1448/ansible/testnet-validator/setup_solv_service_init.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/setup_solv_service_init.yml rename to template/2026.5.21.1448/ansible/testnet-validator/setup_solv_service_init.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/start_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-validator/start_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/start_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-validator/start_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/start_node.yml b/template/2026.5.21.1448/ansible/testnet-validator/start_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/start_node.yml rename to template/2026.5.21.1448/ansible/testnet-validator/start_node.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/start_solv.yml b/template/2026.5.21.1448/ansible/testnet-validator/start_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/start_solv.yml rename to template/2026.5.21.1448/ansible/testnet-validator/start_solv.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/stop_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-validator/stop_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/stop_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-validator/stop_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/stop_node.yml b/template/2026.5.21.1448/ansible/testnet-validator/stop_node.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/stop_node.yml rename to template/2026.5.21.1448/ansible/testnet-validator/stop_node.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/stop_solv.yml b/template/2026.5.21.1448/ansible/testnet-validator/stop_solv.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/stop_solv.yml rename to template/2026.5.21.1448/ansible/testnet-validator/stop_solv.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/switch_off_firedancer_identity.yml b/template/2026.5.21.1448/ansible/testnet-validator/switch_off_firedancer_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/switch_off_firedancer_identity.yml rename to template/2026.5.21.1448/ansible/testnet-validator/switch_off_firedancer_identity.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/switch_off_identity.yml b/template/2026.5.21.1448/ansible/testnet-validator/switch_off_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/switch_off_identity.yml rename to template/2026.5.21.1448/ansible/testnet-validator/switch_off_identity.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/switch_on_firedancer_identity.yml b/template/2026.5.21.1448/ansible/testnet-validator/switch_on_firedancer_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/switch_on_firedancer_identity.yml rename to template/2026.5.21.1448/ansible/testnet-validator/switch_on_firedancer_identity.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/switch_on_identity.yml b/template/2026.5.21.1448/ansible/testnet-validator/switch_on_identity.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/switch_on_identity.yml rename to template/2026.5.21.1448/ansible/testnet-validator/switch_on_identity.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/update_firedancer.yml b/template/2026.5.21.1448/ansible/testnet-validator/update_firedancer.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/update_firedancer.yml rename to template/2026.5.21.1448/ansible/testnet-validator/update_firedancer.yml diff --git a/template/2026.4.27.1539/ansible/testnet-validator/update_startup_config.yml b/template/2026.5.21.1448/ansible/testnet-validator/update_startup_config.yml similarity index 100% rename from template/2026.4.27.1539/ansible/testnet-validator/update_startup_config.yml rename to template/2026.5.21.1448/ansible/testnet-validator/update_startup_config.yml diff --git a/template/2026.5.21.1448/jinja/cmn/files/cf_ipv4.yml b/template/2026.5.21.1448/jinja/cmn/files/cf_ipv4.yml new file mode 100644 index 00000000..95affc10 --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/files/cf_ipv4.yml @@ -0,0 +1,31 @@ +# Cloudflare IPv4 ranges — sourced from https://www.cloudflare.com/ips-v4 +# +# Keep this file in sync with Cloudflare's published list. CF publishes +# a changelog at https://www.cloudflare.com/ips/ — refresh on advisory. +# +# Loaded automatically by cmn/deploy_nftables.yml via include_vars +# (skipped silently if the file is absent). Operators reference the +# resulting `cf_ipv4` variable from their inventory, e.g. +# +# restricted_ports: +# - port: 443 +# tcp: true +# allow_ipv4: "{{ cf_ipv4 | default([]) }}" +# +# As of 2026-05 the list contains 15 ranges: +cf_ipv4: + - 173.245.48.0/20 + - 103.21.244.0/22 + - 103.22.200.0/22 + - 103.31.4.0/22 + - 141.101.64.0/18 + - 108.162.192.0/18 + - 190.93.240.0/20 + - 188.114.96.0/20 + - 197.234.240.0/22 + - 198.41.128.0/17 + - 162.158.0.0/15 + - 104.16.0.0/13 + - 104.24.0.0/14 + - 172.64.0.0/13 + - 131.0.72.0/22 diff --git a/template/2026.5.21.1448/jinja/cmn/nftables/00-ban-check.nft.j2 b/template/2026.5.21.1448/jinja/cmn/nftables/00-ban-check.nft.j2 new file mode 100644 index 00000000..671cd1a1 --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/nftables/00-ban-check.nft.j2 @@ -0,0 +1,4 @@ +# Ban list — must be FIRST priority. A packet from a banned IP is +# dropped here before any allow rule has a chance to match. Counter is +# bumped so `nft list counter c_ban_drop` shows ban effectiveness. +ip saddr @banAll_v4 counter name c_ban_drop drop; diff --git a/template/2026.5.21.1448/jinja/cmn/nftables/00-sets-common.nft.j2 b/template/2026.5.21.1448/jinja/cmn/nftables/00-sets-common.nft.j2 new file mode 100644 index 00000000..a47281af --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/nftables/00-sets-common.nft.j2 @@ -0,0 +1,27 @@ +{# Set + counter definitions for slv-nftables. + + `set_slug` derives a stable nft identifier (only [0-9_]) from each + restricted_port's `port` value — 8899 → "8899", "8000-8020" → "8000_8020". + This lets a single inventory describe both single ports and ranges + without the operator having to invent set names by hand. +#} +{% macro set_slug(port) -%} +{{ port | string | regex_replace('[^0-9]+', '_') }} +{%- endmacro %} + +# Ban list — highest priority, evaluated before any allow. +set banAll_v4 { type ipv4_addr; flags interval; } + +# Global allow-all (matches every dport). +set allowAll_v4 { type ipv4_addr; flags interval; } + +{% for rp in (restricted_ports | default([])) %} +set allow_{{ set_slug(rp.port) }}_v4 { type ipv4_addr; flags interval; } +{% endfor %} + +counter c_allowall { } +counter c_ban_drop { } +{% for rp in (restricted_ports | default([])) %} +counter c_{{ set_slug(rp.port) }}_allow { } +counter c_{{ set_slug(rp.port) }}_drop { } +{% endfor %} diff --git a/template/2026.5.21.1448/jinja/cmn/nftables/01-mgmt-allow.nft.j2 b/template/2026.5.21.1448/jinja/cmn/nftables/01-mgmt-allow.nft.j2 new file mode 100644 index 00000000..407deed7 --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/nftables/01-mgmt-allow.nft.j2 @@ -0,0 +1,12 @@ +# Management IPs — full access (all ports, all protocols). +# +# `mgmt_ips_v4` / `mgmt_ips_v6` come from inventory; the playbook +# refuses to deploy if `mgmt_ips_v4` is empty (no SSH lockout). +{% set v4 = (mgmt_ips_v4 | default([])) | map('trim') | reject('equalto','') | list %} +{% set v6 = (mgmt_ips_v6 | default([])) | map('trim') | reject('equalto','') | list %} +{% if v4 | length > 0 %}ip saddr { {{ v4 | join(', ') }} } accept;{% endif %} +{% if v6 | length > 0 %}ip6 saddr { {{ v6 | join(', ') }} } accept;{% endif %} + +# allowAll_v4 set — IPs added at runtime via `nft add element ...` +# also get unconditional access. +ip saddr @allowAll_v4 counter name c_allowall accept; diff --git a/template/2026.5.21.1448/jinja/cmn/nftables/01-ssh-accept.nft.j2 b/template/2026.5.21.1448/jinja/cmn/nftables/01-ssh-accept.nft.j2 new file mode 100644 index 00000000..579b908c --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/nftables/01-ssh-accept.nft.j2 @@ -0,0 +1,9 @@ +# SSH safety net. `01-mgmt-allow.nft` already grants management IPs +# unconditional access, but explicitly accepting tcp/{{ ssh_port | default(22) }} +# from mgmt addresses guarantees we never lose admin access even if the +# allow-all rule is removed by hand. +{% set v4 = (mgmt_ips_v4 | default([])) | map('trim') | reject('equalto','') | list %} +{% set v6 = (mgmt_ips_v6 | default([])) | map('trim') | reject('equalto','') | list %} +{% set p = ssh_port | default(22) %} +{% if v4 | length > 0 %}ip saddr { {{ v4 | join(', ') }} } tcp dport {{ p }} accept;{% endif %} +{% if v6 | length > 0 %}ip6 saddr { {{ v6 | join(', ') }} } tcp dport {{ p }} accept;{% endif %} diff --git a/template/2026.5.21.1448/jinja/cmn/nftables/02-public-ports.nft.j2 b/template/2026.5.21.1448/jinja/cmn/nftables/02-public-ports.nft.j2 new file mode 100644 index 00000000..ee42323d --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/nftables/02-public-ports.nft.j2 @@ -0,0 +1,10 @@ +# Public-access ports — accept from any source. +# +# `public_tcp_ports` / `public_udp_ports` are inventory lists; entries +# may be a bare integer (8001) or a range string ("8000-8020"). +{% for p in (public_tcp_ports | default([])) %} +tcp dport {{ p }} accept; +{% endfor %} +{% for p in (public_udp_ports | default([])) %} +udp dport {{ p }} accept; +{% endfor %} diff --git a/template/2026.5.21.1448/jinja/cmn/nftables/03-restricted-ports.nft.j2 b/template/2026.5.21.1448/jinja/cmn/nftables/03-restricted-ports.nft.j2 new file mode 100644 index 00000000..83b1bb57 --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/nftables/03-restricted-ports.nft.j2 @@ -0,0 +1,19 @@ +{# Restricted port rules — accept only from listed IPs, drop otherwise. + Set/counter names are derived from each port via set_slug (see + 00-sets-common.nft.j2 for the same derivation). #} +{% macro set_slug(port) -%} +{{ port | string | regex_replace('[^0-9]+', '_') }} +{%- endmacro %} + +{% for rp in (restricted_ports | default([])) %} +{% set slug = set_slug(rp.port) %} +{% if rp.tcp | default(true) %} +tcp dport {{ rp.port }} ip saddr @allow_{{ slug }}_v4 counter name c_{{ slug }}_allow accept; +tcp dport {{ rp.port }} counter name c_{{ slug }}_drop drop; +{% endif %} +{% if rp.udp | default(false) %} +udp dport {{ rp.port }} ip saddr @allow_{{ slug }}_v4 counter name c_{{ slug }}_allow accept; +udp dport {{ rp.port }} counter name c_{{ slug }}_drop drop; +{% endif %} + +{% endfor %} diff --git a/template/2026.5.21.1448/jinja/cmn/nftables/nftables.conf.j2 b/template/2026.5.21.1448/jinja/cmn/nftables/nftables.conf.j2 new file mode 100644 index 00000000..c9ee18b8 --- /dev/null +++ b/template/2026.5.21.1448/jinja/cmn/nftables/nftables.conf.j2 @@ -0,0 +1,49 @@ +# Managed by slv — nftables base ruleset +# +# Fragment layout: +# /etc/nftables.sets.d/*.nft — set + counter definitions (table-scope) +# /etc/nftables.d/*.nft — chain rules (loaded inside `input`) +# +# Re-running the playbook clears these directories before re-rendering, so +# manual edits to fragment files will not survive a re-deploy. Use the +# `slv firewall add-ip` / `slv firewall remove-ip` runtime commands (or +# raw `nft add/delete element`) for ad-hoc IP changes; they persist via +# `tasks/persist_nftables_ruleset.yml` which exports the live ruleset +# back into this file on every deploy. + +# `flush ruleset` is the only safe way to re-apply: without it, `nft -f` +# merges the new table into the existing one, accumulating duplicate +# rules on every deploy. The flush is atomic with the subsequent table +# definition (nft -f loads the whole file as a single transaction), so +# there's no observable window where the chain is empty. +flush ruleset + +table inet filter { + include "/etc/nftables.sets.d/*.nft"; + + chain input { + type filter hook input priority 0; + policy drop; + + iif "lo" accept; + ct state established,related accept; + + # ICMP / ICMPv6 — allow ping + path MTU discovery. Without this, + # connections through routers that need to send Fragmentation Needed + # silently stall instead of erroring. + ip protocol icmp accept; + ip6 nexthdr ipv6-icmp accept; + + include "/etc/nftables.d/*.nft"; + } + + chain forward { + type filter hook forward priority 0; + policy drop; + } + + chain output { + type filter hook output priority 0; + policy accept; + } +} diff --git a/template/2026.4.27.1539/jinja/cmn/prometheus.yml b/template/2026.5.21.1448/jinja/cmn/prometheus.yml similarity index 100% rename from template/2026.4.27.1539/jinja/cmn/prometheus.yml rename to template/2026.5.21.1448/jinja/cmn/prometheus.yml diff --git a/template/2026.4.27.1539/jinja/cmn/restart.sh.j2 b/template/2026.5.21.1448/jinja/cmn/restart.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/cmn/restart.sh.j2 rename to template/2026.5.21.1448/jinja/cmn/restart.sh.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/firedancer-config.toml.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/firedancer-config.toml.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/firedancer-config.toml.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/firedancer-config.toml.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/firedancer.service.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/firedancer.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/firedancer.service.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/firedancer.service.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/geyser.json.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/geyser.json.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/geyser.json.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/geyser.json.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/restart.sh.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/restart.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/restart.sh.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/restart.sh.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/solv-agave.service.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/solv-agave.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/solv-agave.service.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/solv-agave.service.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/solv-agave3.service.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/solv-agave3.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/solv-agave3.service.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/solv-agave3.service.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/solv.service.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/solv.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/solv.service.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/solv.service.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/start-firedancer.sh.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/start-firedancer.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/start-firedancer.sh.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/start-firedancer.sh.j2 diff --git a/template/2026.4.27.1539/jinja/devnet-rpc/start-validator.sh.j2 b/template/2026.5.21.1448/jinja/devnet-rpc/start-validator.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/devnet-rpc/start-validator.sh.j2 rename to template/2026.5.21.1448/jinja/devnet-rpc/start-validator.sh.j2 diff --git a/template/2026.5.21.1448/jinja/mainnet-hermes/beacon.service.j2 b/template/2026.5.21.1448/jinja/mainnet-hermes/beacon.service.j2 new file mode 100644 index 00000000..205223f5 --- /dev/null +++ b/template/2026.5.21.1448/jinja/mainnet-hermes/beacon.service.j2 @@ -0,0 +1,25 @@ +[Unit] +Description=Pyth Beacon — Wormhole spy relay (VAAs → NATS) +After=nats-server.service network-online.target +Requires=nats-server.service + +[Service] +Type=simple +User=solv +Group=solv +WorkingDirectory=/home/solv +Environment=WORMHOLE_ENV={{ wormhole_env }} +Environment=WORMHOLE_LISTEN_PORT={{ beacon_listen_port }} +Environment=NATS_URL=127.0.0.1:{{ nats_port }} +Environment=NATS_STREAM={{ wormhole_env }}-vaas +Environment=SERVER_URL={{ beacon_spy_grpc_addr }} +Environment=LOG_LEVEL=info +Environment=WRITER_BATCH_SIZE=1000 +Environment=NODE_KEY_PATH=/home/solv/beacon-node.key +ExecStart=/home/solv/beacon-bin +Restart=always +RestartSec=5 +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target diff --git a/template/2026.5.21.1448/jinja/mainnet-hermes/hermes.service.j2 b/template/2026.5.21.1448/jinja/mainnet-hermes/hermes.service.j2 new file mode 100644 index 00000000..6fdaa17e --- /dev/null +++ b/template/2026.5.21.1448/jinja/mainnet-hermes/hermes.service.j2 @@ -0,0 +1,23 @@ +[Unit] +Description=Pyth Hermes — REST/WS price feed API +After=beacon.service network-online.target +Requires=beacon.service + +[Service] +Type=simple +User=solv +Group=solv +WorkingDirectory=/home/solv +Environment=PYTHNET_HTTP_ADDR={{ pythnet_http_addr }} +Environment=PYTHNET_WS_ADDR={{ pythnet_ws_addr }} +Environment=WORMHOLE_SPY_RPC_ADDR=http://{{ beacon_spy_grpc_addr }} +Environment=RPC_LISTEN_ADDR={{ rpc_listen_addr }} +Environment=METRICS_SERVER_LISTEN_ADDR={{ metrics_listen_addr }} +Environment=RUST_LOG=info +ExecStart=/home/solv/src/pyth-crosschain/apps/hermes/server/target/release/hermes run +Restart=always +RestartSec=5 +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target diff --git a/template/2026.5.21.1448/jinja/mainnet-hermes/nats-server.service.j2 b/template/2026.5.21.1448/jinja/mainnet-hermes/nats-server.service.j2 new file mode 100644 index 00000000..87c893a8 --- /dev/null +++ b/template/2026.5.21.1448/jinja/mainnet-hermes/nats-server.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=NATS server (JetStream) — VAA dedup/stream for Pyth Beacon +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=solv +Group=solv +ExecStart=/usr/local/bin/nats-server --jetstream --store_dir=/home/solv/nats-data --port={{ nats_port }} +Restart=always +RestartSec=5 +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target diff --git a/template/2026.5.21.1448/jinja/mainnet-pythnet/pythnet.service.j2 b/template/2026.5.21.1448/jinja/mainnet-pythnet/pythnet.service.j2 new file mode 100644 index 00000000..8b71a24e --- /dev/null +++ b/template/2026.5.21.1448/jinja/mainnet-pythnet/pythnet.service.j2 @@ -0,0 +1,18 @@ +[Unit] +Description=Pythnet RPC validator (no-voting) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=solv +Group=solv +WorkingDirectory=/home/solv +ExecStart=/home/solv/start-pythnet.sh +Restart=always +RestartSec=5 +LimitNOFILE=1048576 +TimeoutStopSec=120 + +[Install] +WantedBy=multi-user.target diff --git a/template/2026.5.21.1448/jinja/mainnet-pythnet/start-pythnet.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-pythnet/start-pythnet.sh.j2 new file mode 100644 index 00000000..b8088dd1 --- /dev/null +++ b/template/2026.5.21.1448/jinja/mainnet-pythnet/start-pythnet.sh.j2 @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# Pythnet RPC validator — no-voting, full RPC API. +# +# Genesis hash and expected_shred_version are fixed network parameters +# discoverable via the public Pythnet RPC: +# curl -s https://pythnet.rpcpool.com -X POST -H 'content-type: application/json' \ +# -d '{"jsonrpc":"2.0","id":1,"method":"getGenesisHash"}' +# +# The --account-index program-id flag is *mandatory* on Pythnet — the +# validator panics on startup if it's missing. +set -euo pipefail +exec /usr/local/bin/solana-validator \ + --identity /home/solv/pythnet-identity.json \ + --no-voting \ + --entrypoint {{ pythnet_entrypoint | default('pythnet.rpcpool.com:8001') }} \ +{% for kv in (pythnet_known_validators | default(['DzZov8p2kwXJeYx2hnVyaxNFugYVF4up9AWwRvtYWU4u', '6Rr1vUgRZmqDuzzU6Sf3QAfUTYV3aa4wqxKrL6uBGXdU'])) %} + --known-validator {{ kv }} \ +{% endfor %} + --expected-genesis-hash {{ pythnet_genesis_hash | default('GLKkBUr6r72nBtGrtBPJLRqtsh8wXZanX4xfnqKnWwKq') }} \ + --expected-shred-version {{ pythnet_shred_version | default(35891) }} \ + --rpc-port {{ pythnet_rpc_port | default(8899) }} \ + --rpc-bind-address {{ pythnet_rpc_bind | default('0.0.0.0') }} \ + --full-rpc-api \ + --enable-rpc-transaction-history \ + --dynamic-port-range {{ pythnet_dynamic_port_range | default('8000-8020') }} \ + --gossip-port {{ pythnet_gossip_port | default(8001) }} \ + --account-index program-id \ + --ledger {{ pythnet_ledger_mount | default('/mnt/ledger') }}/pythnet/ledger \ + --accounts {{ pythnet_ledger_mount | default('/mnt/ledger') }}/pythnet/accounts \ + --snapshots {{ pythnet_ledger_mount | default('/mnt/ledger') }}/pythnet/snapshots \ + --log {{ pythnet_ledger_mount | default('/mnt/ledger') }}/pythnet/log/validator.log \ + --limit-ledger-size {{ pythnet_limit_ledger_size | default(50000000) }} \ + --wal-recovery-mode skip_any_corrupted_record diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/firedancer-config.toml.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/firedancer-config.toml.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/firedancer-config.toml.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/firedancer-config.toml.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/firedancer.service.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/firedancer.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/firedancer.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/firedancer.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/generate_configs.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/generate_configs.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/generate_configs.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/generate_configs.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/geyser-richat.json.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/geyser-richat.json.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/geyser-richat.json.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/geyser-richat.json.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/geyser.json.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/geyser.json.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/geyser.json.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/geyser.json.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/restart.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/restart.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/restart.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/restart.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/richat-setting.yml.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/richat-setting.yml.j2 similarity index 66% rename from template/2026.4.27.1539/jinja/mainnet-rpc/richat-setting.yml.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/richat-setting.yml.j2 index a0030664..077818b2 100644 --- a/template/2026.4.27.1539/jinja/mainnet-rpc/richat-setting.yml.j2 +++ b/template/2026.5.21.1448/jinja/mainnet-rpc/richat-setting.yml.j2 @@ -102,4 +102,28 @@ apps: unary: enabled: true affinity: null - requests_queue_size: 100 \ No newline at end of file + requests_queue_size: 100 + pubsub: + endpoint: 0.0.0.0:7111 + tcp_nodelay: true + recv_max_message_size: 4KiB + enable_block_subscription: true + enable_transaction_subscription: true + clients_requests_channel_size: 8_192 + # richat's pubsub uses a single tracker loop that drains messages + # per-commitment up to `max_messages_per_commitment_per_tick`, then + # fans them out to clients via a rayon pool sized to + # `subscriptions_workers_count`. The upstream defaults (2 workers, + # 256 msg/tick) were measured to cause slotSubscribe lag of 5-10 + # slots when block + transaction subscriptions are also enabled — + # tx notifications saturate the per-tick budget and slot + # notifications queue up behind them. These tuned values keep the + # slot stream within 0-1 slot of public reference RPCs on a 64-core + # mainnet RPC host. + subscriptions_workers_count: 8 + subscriptions_max_clients_request_per_tick: 32 + subscriptions_max_messages_per_commitment_per_tick: 4096 + notifications_messages_max_count: 16_777_216 + notifications_messages_max_bytes: 32GiB + signatures_cache_max: 1_228_800 + signatures_cache_slots_max: 150 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/richat.service.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/richat.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/richat.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/richat.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/solv-agave.service.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/solv-agave.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/solv-agave.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/solv-agave.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/solv-agave3.service.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/solv-agave3.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/solv-agave3.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/solv-agave3.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/solv.service.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/solv.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/solv.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/solv.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc-grpc.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc-grpc.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc-grpc.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc-grpc.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc-index.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc-index.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc-index.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc-index.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc-tx.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc-tx.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc-tx.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc-tx.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/start-mainnet-rpc.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/start-mainnet-rpc.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-rpc/start-validator.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-rpc/start-validator.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-rpc/start-validator.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-rpc/start-validator.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/firedancer-config.toml.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/firedancer-config.toml.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/firedancer-config.toml.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/firedancer-config.toml.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/firedancer.service.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/firedancer.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/firedancer.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/firedancer.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/overrides.yml.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/overrides.yml.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/overrides.yml.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/overrides.yml.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/relayer.service.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/relayer.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/relayer.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/relayer.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/restart.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/restart.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/restart.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/restart.sh.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/solv-agave.service.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/solv-agave.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/solv-agave.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/solv-agave.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/solv-agave3.service.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/solv-agave3.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/solv-agave3.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/solv-agave3.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/solv.service.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/solv.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/solv.service.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/solv.service.j2 diff --git a/template/2026.4.27.1539/jinja/mainnet-validator/start-validator.sh.j2 b/template/2026.5.21.1448/jinja/mainnet-validator/start-validator.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/mainnet-validator/start-validator.sh.j2 rename to template/2026.5.21.1448/jinja/mainnet-validator/start-validator.sh.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/firedancer-config.toml.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/firedancer-config.toml.j2 similarity index 98% rename from template/2026.4.27.1539/jinja/testnet-rpc/firedancer-config.toml.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/firedancer-config.toml.j2 index fd317983..c1ffb17e 100644 --- a/template/2026.4.27.1539/jinja/testnet-rpc/firedancer-config.toml.j2 +++ b/template/2026.5.21.1448/jinja/testnet-rpc/firedancer-config.toml.j2 @@ -107,8 +107,8 @@ dynamic_port_range = "{{ dynamic_port_range | default('8900-8925') }}" snapshot_fetch = true genesis_fetch = true expected_genesis_hash = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY" - expected_bank_hash = "EJMzxv7JscF8WNZfDYqzsAyALCDCS52HuihabVgyz5mf" - expected_shred_version = 9604 + expected_bank_hash = "YFxSkDcvSPiA7EQpSTbCsWbJvNYMAsWXGvwGc3bXHEA" + expected_shred_version = 57087 known_validators = [ "5D1fNXzvv5NjV1ysLjirC4WY92RNsVH18vjmcszZd8on", "dDzy5SR3AXdYWVqbDEkVFdvSPCtS9ihF5kJkHCtXoFs", diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/firedancer.service.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/firedancer.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-rpc/firedancer.service.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/firedancer.service.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/geyser.json.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/geyser.json.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-rpc/geyser.json.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/geyser.json.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/restart.sh.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/restart.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-rpc/restart.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/restart.sh.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/solv-agave.service.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/solv-agave.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-rpc/solv-agave.service.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/solv-agave.service.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/solv-agave3.service.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/solv-agave3.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-rpc/solv-agave3.service.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/solv-agave3.service.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/solv.service.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/solv.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-rpc/solv.service.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/solv.service.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/start-firedancer.sh.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/start-firedancer.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-rpc/start-firedancer.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/start-firedancer.sh.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-rpc/start-validator.sh.j2 b/template/2026.5.21.1448/jinja/testnet-rpc/start-validator.sh.j2 similarity index 95% rename from template/2026.4.27.1539/jinja/testnet-rpc/start-validator.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-rpc/start-validator.sh.j2 index bf071c27..37219fdb 100644 --- a/template/2026.4.27.1539/jinja/testnet-rpc/start-validator.sh.j2 +++ b/template/2026.5.21.1448/jinja/testnet-rpc/start-validator.sh.j2 @@ -20,8 +20,8 @@ exec agave-validator \ --full-rpc-api \ --rpc-port {{ port_rpc | default(8899, true) }} \ --wal-recovery-mode skip_any_corrupted_record \ ---expected-shred-version 9604 \ ---expected-bank-hash EJMzxv7JscF8WNZfDYqzsAyALCDCS52HuihabVgyz5mf \ +--expected-shred-version 57087 \ +--expected-bank-hash YFxSkDcvSPiA7EQpSTbCsWbJvNYMAsWXGvwGc3bXHEA \ {% if rpc_type == 'Geyser gRPC' -%} --limit-ledger-size 50000000 \ --no-snapshot-fetch \ diff --git a/template/2026.4.27.1539/jinja/testnet-validator/firedancer-config-agave.toml.j2 b/template/2026.5.21.1448/jinja/testnet-validator/firedancer-config-agave.toml.j2 similarity index 93% rename from template/2026.4.27.1539/jinja/testnet-validator/firedancer-config-agave.toml.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/firedancer-config-agave.toml.j2 index 842cccca..ec100127 100644 --- a/template/2026.4.27.1539/jinja/testnet-validator/firedancer-config-agave.toml.j2 +++ b/template/2026.5.21.1448/jinja/testnet-validator/firedancer-config-agave.toml.j2 @@ -48,8 +48,8 @@ dynamic_port_range = "{{ dynamic_port_range | default("8100-8125") }}" snapshot_fetch = true genesis_fetch = true expected_genesis_hash = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY" - expected_bank_hash = "{{ expected_bank_hash | default("3zk4WMwk6wCTVJXu9UAk2dYWMedCKooDs15XL5u6FkvE") }}" - expected_shred_version = {{ expected_shred_version | default(27350) }} + expected_bank_hash = "{{ expected_bank_hash | default("YFxSkDcvSPiA7EQpSTbCsWbJvNYMAsWXGvwGc3bXHEA") }}" + expected_shred_version = {{ expected_shred_version | default(57087) }} wait_for_supermajority_at_slot = {{ wait_for_supermajority | default(383520372) }} [hugetlbfs] diff --git a/template/2026.4.27.1539/jinja/testnet-validator/firedancer-config-jito.toml.j2 b/template/2026.5.21.1448/jinja/testnet-validator/firedancer-config-jito.toml.j2 similarity index 94% rename from template/2026.4.27.1539/jinja/testnet-validator/firedancer-config-jito.toml.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/firedancer-config-jito.toml.j2 index 8d8cf2dc..5c83f190 100644 --- a/template/2026.4.27.1539/jinja/testnet-validator/firedancer-config-jito.toml.j2 +++ b/template/2026.5.21.1448/jinja/testnet-validator/firedancer-config-jito.toml.j2 @@ -48,8 +48,8 @@ dynamic_port_range = "{{ dynamic_port_range | default("8100-8125", true) }}" snapshot_fetch = true genesis_fetch = true expected_genesis_hash = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY" - expected_bank_hash = "{{ expected_bank_hash | default("3zk4WMwk6wCTVJXu9UAk2dYWMedCKooDs15XL5u6FkvE") }}" - expected_shred_version = {{ expected_shred_version | default(27350) }} + expected_bank_hash = "{{ expected_bank_hash | default("YFxSkDcvSPiA7EQpSTbCsWbJvNYMAsWXGvwGc3bXHEA") }}" + expected_shred_version = {{ expected_shred_version | default(57087) }} wait_for_supermajority_at_slot = {{ wait_for_supermajority | default(383520372) }} [hugetlbfs] diff --git a/template/2026.4.27.1539/jinja/testnet-validator/firedancer-config.toml.j2 b/template/2026.5.21.1448/jinja/testnet-validator/firedancer-config.toml.j2 similarity index 95% rename from template/2026.4.27.1539/jinja/testnet-validator/firedancer-config.toml.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/firedancer-config.toml.j2 index f80913c6..b5ec3526 100644 --- a/template/2026.4.27.1539/jinja/testnet-validator/firedancer-config.toml.j2 +++ b/template/2026.5.21.1448/jinja/testnet-validator/firedancer-config.toml.j2 @@ -47,8 +47,8 @@ dynamic_port_range = "{{ dynamic_port_range | default('8900-8925') }}" snapshot_fetch = true genesis_fetch = true expected_genesis_hash = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY" - expected_bank_hash = "CJZAAtgdqYiqHTFKJ9XD9qw4W21wHqojkhrVJSfFF9b7" - expected_shred_version = 24207 + expected_bank_hash = "YFxSkDcvSPiA7EQpSTbCsWbJvNYMAsWXGvwGc3bXHEA" + expected_shred_version = 57087 [hugetlbfs] mount_path = "/mnt" diff --git a/template/2026.4.27.1539/jinja/testnet-validator/firedancer.service.j2 b/template/2026.5.21.1448/jinja/testnet-validator/firedancer.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-validator/firedancer.service.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/firedancer.service.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-validator/restart.sh.j2 b/template/2026.5.21.1448/jinja/testnet-validator/restart.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-validator/restart.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/restart.sh.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-validator/solv.service.j2 b/template/2026.5.21.1448/jinja/testnet-validator/solv.service.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-validator/solv.service.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/solv.service.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-validator/start-firedancer.sh.j2 b/template/2026.5.21.1448/jinja/testnet-validator/start-firedancer.sh.j2 similarity index 100% rename from template/2026.4.27.1539/jinja/testnet-validator/start-firedancer.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/start-firedancer.sh.j2 diff --git a/template/2026.4.27.1539/jinja/testnet-validator/start-validator-agave.sh.j2 b/template/2026.5.21.1448/jinja/testnet-validator/start-validator-agave.sh.j2 similarity index 82% rename from template/2026.4.27.1539/jinja/testnet-validator/start-validator-agave.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/start-validator-agave.sh.j2 index 4150ecd2..9d488872 100644 --- a/template/2026.4.27.1539/jinja/testnet-validator/start-validator-agave.sh.j2 +++ b/template/2026.5.21.1448/jinja/testnet-validator/start-validator-agave.sh.j2 @@ -15,8 +15,8 @@ exec agave-validator \ --rpc-port {{ port_rpc | default(8899, true) }} \ --wal-recovery-mode skip_any_corrupted_record \ --wait-for-supermajority {{ wait_for_supermajority | default("383520372") }} \ ---expected-shred-version {{ expected_shred_version | default("27350") }} \ ---expected-bank-hash {{ expected_bank_hash | default("3zk4WMwk6wCTVJXu9UAk2dYWMedCKooDs15XL5u6FkvE") }} \ +--expected-shred-version {{ expected_shred_version | default("57087") }} \ +--expected-bank-hash {{ expected_bank_hash | default("YFxSkDcvSPiA7EQpSTbCsWbJvNYMAsWXGvwGc3bXHEA") }} \ --limit-ledger-size {{ limit_ledger_size | default(200000000) }} \ --no-port-check \ --rpc-bind-address 0.0.0.0 \ No newline at end of file diff --git a/template/2026.4.27.1539/jinja/testnet-validator/start-validator-jito.sh.j2 b/template/2026.5.21.1448/jinja/testnet-validator/start-validator-jito.sh.j2 similarity index 88% rename from template/2026.4.27.1539/jinja/testnet-validator/start-validator-jito.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/start-validator-jito.sh.j2 index 09b5fb00..974a98e9 100644 --- a/template/2026.4.27.1539/jinja/testnet-validator/start-validator-jito.sh.j2 +++ b/template/2026.5.21.1448/jinja/testnet-validator/start-validator-jito.sh.j2 @@ -15,8 +15,8 @@ exec agave-validator \ --rpc-port {{ port_rpc | default(8899, true) }} \ --wal-recovery-mode skip_any_corrupted_record \ --wait-for-supermajority {{ wait_for_supermajority | default("383520372") }} \ ---expected-shred-version {{ expected_shred_version | default("27350") }} \ ---expected-bank-hash {{ expected_bank_hash | default("3zk4WMwk6wCTVJXu9UAk2dYWMedCKooDs15XL5u6FkvE") }} \ +--expected-shred-version {{ expected_shred_version | default("57087") }} \ +--expected-bank-hash {{ expected_bank_hash | default("YFxSkDcvSPiA7EQpSTbCsWbJvNYMAsWXGvwGc3bXHEA") }} \ --limit-ledger-size {{ limit_ledger_size | default(200000000) }} \ --no-port-check \ --rpc-bind-address 0.0.0.0 \ diff --git a/template/2026.4.27.1539/jinja/testnet-validator/start-validator.sh.j2 b/template/2026.5.21.1448/jinja/testnet-validator/start-validator.sh.j2 similarity index 95% rename from template/2026.4.27.1539/jinja/testnet-validator/start-validator.sh.j2 rename to template/2026.5.21.1448/jinja/testnet-validator/start-validator.sh.j2 index 3bc19d9c..0cb3cecf 100644 --- a/template/2026.4.27.1539/jinja/testnet-validator/start-validator.sh.j2 +++ b/template/2026.5.21.1448/jinja/testnet-validator/start-validator.sh.j2 @@ -15,7 +15,7 @@ exec agave-validator \ {% if wait_for_supermajority is defined %} --wait-for-supermajority {{ wait_for_supermajority }} \ {% endif %} ---expected-shred-version {{ expected_shred_version | default(27350) }} \ +--expected-shred-version {{ expected_shred_version | default(57087) }} \ {% if expected_bank_hash is defined %} --expected-bank-hash {{ expected_bank_hash }} \ {% endif %} diff --git a/template/latest b/template/latest index 033f6503..3db74749 120000 --- a/template/latest +++ b/template/latest @@ -1 +1 @@ -2026.5.5.1612 \ No newline at end of file +2026.5.21.1448 \ No newline at end of file