# Quantus Planck node, validator role, external-miner mode. # # Hardened per ~/git/architecture/generic.md §8. Two knobs are deliberately # relaxed; both are load-bearing, do not "tidy" them back: # # MemoryDenyWriteExecute=false # Substrate JITs the on-chain wasm runtime through wasmtime and needs W|X # pages. With this true the node aborts during block import. # # Type=simple (not notify) # quantus-node does not sd_notify(READY=1). Type=notify would block # `systemctl restart` until TimeoutStartSec expires # (deployment-gitea-actions.md §6). # # The reward preimage arrives via EnvironmentFile only because the node exposes # no environment-variable form for any flag (`--help` carries no `[env:]` # annotations) and the value is per-deployment while this unit is a static # asset. It is NOT hidden and does not need to be: the node embeds the preimage # verbatim in the PreRuntime digest of every block it authors, so it is public # on-chain from your first block onward. Do not add secret-handling around it. # $QUANTUS_PUBLIC_ADDR is deliberately a bare $VAR, not ${VAR}: systemd # word-splits the former, so a value of "--public-addr /dns4/host/tcp/30333" # becomes two arguments and an EMPTY value becomes zero arguments. That lets one # unit serve both a publicly-dialable node and a mesh-only one. # # --prometheus-external binds the exporter on all interfaces instead of # loopback, so the fleet Prometheus (a different host, a different site) can # scrape it. The exporter is unauthenticated, so what actually bounds access is # the firewalld rich rule the deploy scopes to the scrape host — not the bind. # # $QUANTUS_RPC_EXPOSE is the same bare-$VAR pattern: empty leaves the JSON-RPC # server on loopback (the default and the right choice for a validator), a # non-empty value opens it. Access is bounded by a firewalld rich rule scoped to # the site subnet, never by the bind. # # It matters behind NAT: without it libp2p advertises the address it can see — # a 10.x LAN address — which no external peer can dial. A port forward alone # gets packets in but never tells anyone to send them. [Unit] Description=Quantus node (Planck, validator, external-miner mode) Documentation=https://github.com/Quantus-Network/chain After=network-online.target Wants=network-online.target [Service] Type=simple User=quantus-node Group=quantus-node Environment=RUST_LOG=info EnvironmentFile=/etc/quantus-node/node.env WorkingDirectory=/var/lib/quantus-node ExecStart=/usr/local/bin/quantus-node \ --validator \ --chain ${QUANTUS_CHAIN} \ --name baba-gorchitsa \ --base-path /var/lib/quantus-node \ --node-key-file /var/lib/quantus-node/node_key.p2p \ --rewards-inner-hash ${QUANTUS_INNER_HASH} \ --miner-listen-port 9833 \ --port 30333 \ $QUANTUS_PUBLIC_ADDR \ $QUANTUS_RPC_EXPOSE \ --prometheus-port 9615 \ --prometheus-external \ --max-blocks-per-request 64 \ --sync full Restart=always RestartSec=10s # RocksDB needs time to flush cleanly; a SIGKILL mid-compaction means a slow # (or corrupt) recovery on next start. TimeoutStopSec=300 KillSignal=SIGINT LimitNOFILE=65536 NoNewPrivileges=true ProtectSystem=strict ProtectHome=true PrivateTmp=true PrivateDevices=true ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true RestrictRealtime=true RestrictSUIDSGID=true LockPersonality=true SystemCallArchitectures=native MemoryDenyWriteExecute=false ReadWritePaths=/var/lib/quantus-node # AF_NETLINK is REQUIRED, not decoration. libp2p calls getifaddrs() to enumerate # interfaces, and getifaddrs() opens a NETLINK_ROUTE socket. Without it the node # still starts and reports `active`, but logs # failed to fetch network interfaces error=GetIfAddrsError("getifaddrs", -1) # litep2p started with no listen addresses, cannot accept inbound connections # and never binds --port at all — a silently undialable peer. Verified on a live # host: adding AF_NETLINK binds 30333 on both stacks; removing it unbinds. RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK [Install] WantedBy=multi-user.target