ovtools 0.0.0-stage → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -2
  3. package/assets/common/bond-inspect.sh +368 -0
  4. package/assets/common/config.sh +263 -0
  5. package/assets/common/extract-data.sh +197 -0
  6. package/assets/common/harden-host.sh +37 -0
  7. package/assets/common/install-deps.sh +93 -0
  8. package/assets/common/node-enroll.sh +65 -0
  9. package/assets/common/node-setup.sh +150 -0
  10. package/assets/common/node-start.sh +90 -0
  11. package/assets/common/node-stop.sh +18 -0
  12. package/assets/common/node-upgrade.sh +45 -0
  13. package/assets/common/pre-run.sh +497 -0
  14. package/assets/common/promote-validator.sh +104 -0
  15. package/assets/deploy/10-prepare-host.sh +98 -0
  16. package/assets/deploy/20-install-toolchain.sh +93 -0
  17. package/assets/deploy/30-setup-node.sh +82 -0
  18. package/assets/deploy/40-start-node.sh +75 -0
  19. package/assets/deploy/50-verify.sh +259 -0
  20. package/assets/deploy/60-enroll.sh +304 -0
  21. package/assets/deploy/70-promote.sh +152 -0
  22. package/assets/deploy/lib.sh +398 -0
  23. package/assets/manage/10-bond-increase.sh +149 -0
  24. package/assets/manage/10-diagnose-node.sh +169 -0
  25. package/assets/manage/20-exit-validator.sh +259 -0
  26. package/assets/manage/25-recover-node.sh +291 -0
  27. package/assets/manage/30-status.sh +64 -0
  28. package/assets/manage/40-exit-withdraw.sh +133 -0
  29. package/assets/manage/bond_inc.sh +318 -0
  30. package/assets/manage/dashboard_monitor/app.js +1544 -0
  31. package/assets/manage/dashboard_monitor/assets/v2.2/css/base.css +113 -0
  32. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/content.css +133 -0
  33. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/controls.css +229 -0
  34. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/data.css +374 -0
  35. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/icons.css +35 -0
  36. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/motion.css +57 -0
  37. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/shell.css +51 -0
  38. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/sidebar.css +242 -0
  39. package/assets/manage/dashboard_monitor/assets/v2.2/css/components/topbar.css +138 -0
  40. package/assets/manage/dashboard_monitor/assets/v2.2/css/fonts.css +30 -0
  41. package/assets/manage/dashboard_monitor/assets/v2.2/css/synteq-ui-v2.css +25 -0
  42. package/assets/manage/dashboard_monitor/assets/v2.2/css/theme-dark.css +38 -0
  43. package/assets/manage/dashboard_monitor/assets/v2.2/css/theme-light.css +36 -0
  44. package/assets/manage/dashboard_monitor/assets/v2.2/css/tokens.css +61 -0
  45. package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-Medium.ttf +0 -0
  46. package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-Regular.ttf +0 -0
  47. package/assets/manage/dashboard_monitor/assets/v2.2/fonts/GoogleSansCode-SemiBold.ttf +0 -0
  48. package/assets/manage/dashboard_monitor/assets/v2.2/js/synteq-ui-v2.js +869 -0
  49. package/assets/manage/dashboard_monitor/deploy-dashboard.sh +230 -0
  50. package/assets/manage/dashboard_monitor/favicon.svg +13 -0
  51. package/assets/manage/dashboard_monitor/index.html +1197 -0
  52. package/assets/manage/dashboard_monitor/nginx-octra.conf +76 -0
  53. package/assets/manage/dashboard_monitor/octra-dashboard.service +29 -0
  54. package/assets/manage/dashboard_monitor/octra-node-prune.cron +12 -0
  55. package/assets/manage/dashboard_monitor/octra-node-prune.service +16 -0
  56. package/assets/manage/dashboard_monitor/octra-node-prune.timer +12 -0
  57. package/assets/manage/dashboard_monitor/prune-node-junk.sh +329 -0
  58. package/assets/manage/dashboard_monitor/server.py +1887 -0
  59. package/assets/manage/dashboard_monitor/ssl-certbot.sh +90 -0
  60. package/assets/manage/dashboard_monitor/styles.css +327 -0
  61. package/assets/manage/lib.sh +347 -0
  62. package/assets/manifest.json +143 -0
  63. package/assets/migrate/00-backup-identity.sh +67 -0
  64. package/assets/migrate/01-prepare-target.sh +145 -0
  65. package/assets/migrate/02-authorize-pull.sh +43 -0
  66. package/assets/migrate/03-pull-state.sh +102 -0
  67. package/assets/migrate/04-stop-source.sh +85 -0
  68. package/assets/migrate/05-finalize-target.sh +140 -0
  69. package/assets/migrate/06-start-target.sh +57 -0
  70. package/assets/migrate/07-verify.sh +237 -0
  71. package/assets/migrate/08-rollback.sh +54 -0
  72. package/assets/migrate/lib.sh +149 -0
  73. package/assets/upgrade/10-preflight.sh +120 -0
  74. package/assets/upgrade/20-upgrade.sh +142 -0
  75. package/assets/upgrade/30-manual-upgrade.sh +129 -0
  76. package/assets/upgrade/40-storage-prune.sh +65 -0
  77. package/assets/upgrade/50-status.sh +58 -0
  78. package/assets/upgrade/lib.sh +232 -0
  79. package/bin/ovtools.js +32 -0
  80. package/dist/api/checks.d.ts +6 -0
  81. package/dist/api/checks.d.ts.map +1 -0
  82. package/dist/api/checks.js +173 -0
  83. package/dist/api/checks.js.map +1 -0
  84. package/dist/api/hostkeys.d.ts +10 -0
  85. package/dist/api/hostkeys.d.ts.map +1 -0
  86. package/dist/api/hostkeys.js +58 -0
  87. package/dist/api/hostkeys.js.map +1 -0
  88. package/dist/api/jobs.d.ts +33 -0
  89. package/dist/api/jobs.d.ts.map +1 -0
  90. package/dist/api/jobs.js +486 -0
  91. package/dist/api/jobs.js.map +1 -0
  92. package/dist/api/nodes.d.ts +16 -0
  93. package/dist/api/nodes.d.ts.map +1 -0
  94. package/dist/api/nodes.js +126 -0
  95. package/dist/api/nodes.js.map +1 -0
  96. package/dist/api/server.d.ts +3 -0
  97. package/dist/api/server.d.ts.map +1 -0
  98. package/dist/api/server.js +443 -0
  99. package/dist/api/server.js.map +1 -0
  100. package/dist/cli.d.ts +3 -0
  101. package/dist/cli.d.ts.map +1 -0
  102. package/dist/cli.js +301 -0
  103. package/dist/cli.js.map +1 -0
  104. package/dist/constants.d.ts +76 -0
  105. package/dist/constants.d.ts.map +1 -0
  106. package/dist/constants.js +150 -0
  107. package/dist/constants.js.map +1 -0
  108. package/dist/index.d.ts +18 -0
  109. package/dist/index.d.ts.map +1 -0
  110. package/dist/index.js +60 -0
  111. package/dist/index.js.map +1 -0
  112. package/dist/orchestrator/base.d.ts +52 -0
  113. package/dist/orchestrator/base.d.ts.map +1 -0
  114. package/dist/orchestrator/base.js +219 -0
  115. package/dist/orchestrator/base.js.map +1 -0
  116. package/dist/orchestrator/deploy.d.ts +33 -0
  117. package/dist/orchestrator/deploy.d.ts.map +1 -0
  118. package/dist/orchestrator/deploy.js +313 -0
  119. package/dist/orchestrator/deploy.js.map +1 -0
  120. package/dist/orchestrator/manage.d.ts +28 -0
  121. package/dist/orchestrator/manage.d.ts.map +1 -0
  122. package/dist/orchestrator/manage.js +267 -0
  123. package/dist/orchestrator/manage.js.map +1 -0
  124. package/dist/orchestrator/migrate.d.ts +31 -0
  125. package/dist/orchestrator/migrate.d.ts.map +1 -0
  126. package/dist/orchestrator/migrate.js +442 -0
  127. package/dist/orchestrator/migrate.js.map +1 -0
  128. package/dist/ssh/client.d.ts +39 -0
  129. package/dist/ssh/client.d.ts.map +1 -0
  130. package/dist/ssh/client.js +418 -0
  131. package/dist/ssh/client.js.map +1 -0
  132. package/dist/test/core.test.d.ts +2 -0
  133. package/dist/test/core.test.d.ts.map +1 -0
  134. package/dist/test/core.test.js +188 -0
  135. package/dist/test/core.test.js.map +1 -0
  136. package/dist/types.d.ts +267 -0
  137. package/dist/types.d.ts.map +1 -0
  138. package/dist/types.js +3 -0
  139. package/dist/types.js.map +1 -0
  140. package/dist/utils/assets.d.ts +28 -0
  141. package/dist/utils/assets.d.ts.map +1 -0
  142. package/dist/utils/assets.js +583 -0
  143. package/dist/utils/assets.js.map +1 -0
  144. package/dist/utils/browser.d.ts +2 -0
  145. package/dist/utils/browser.d.ts.map +1 -0
  146. package/dist/utils/browser.js +23 -0
  147. package/dist/utils/browser.js.map +1 -0
  148. package/dist/utils/dashboard.d.ts +16 -0
  149. package/dist/utils/dashboard.d.ts.map +1 -0
  150. package/dist/utils/dashboard.js +177 -0
  151. package/dist/utils/dashboard.js.map +1 -0
  152. package/dist/utils/helpers.d.ts +29 -0
  153. package/dist/utils/helpers.d.ts.map +1 -0
  154. package/dist/utils/helpers.js +204 -0
  155. package/dist/utils/helpers.js.map +1 -0
  156. package/dist/utils/scripts.d.ts +60 -0
  157. package/dist/utils/scripts.d.ts.map +1 -0
  158. package/dist/utils/scripts.js +174 -0
  159. package/dist/utils/scripts.js.map +1 -0
  160. package/dist/utils/state.d.ts +12 -0
  161. package/dist/utils/state.d.ts.map +1 -0
  162. package/dist/utils/state.js +44 -0
  163. package/dist/utils/state.js.map +1 -0
  164. package/dist/utils/tar.d.ts +11 -0
  165. package/dist/utils/tar.d.ts.map +1 -0
  166. package/dist/utils/tar.js +93 -0
  167. package/dist/utils/tar.js.map +1 -0
  168. package/dist/utils/update.d.ts +9 -0
  169. package/dist/utils/update.d.ts.map +1 -0
  170. package/dist/utils/update.js +134 -0
  171. package/dist/utils/update.js.map +1 -0
  172. package/dist/utils/websocket.d.ts +10 -0
  173. package/dist/utils/websocket.d.ts.map +1 -0
  174. package/dist/utils/websocket.js +203 -0
  175. package/dist/utils/websocket.js.map +1 -0
  176. package/package.json +55 -4
  177. package/public/app.js +2736 -0
  178. package/public/assets/v2.2/css/base.css +113 -0
  179. package/public/assets/v2.2/css/components/content.css +133 -0
  180. package/public/assets/v2.2/css/components/controls.css +229 -0
  181. package/public/assets/v2.2/css/components/data.css +373 -0
  182. package/public/assets/v2.2/css/components/icons.css +35 -0
  183. package/public/assets/v2.2/css/components/motion.css +57 -0
  184. package/public/assets/v2.2/css/components/shell.css +51 -0
  185. package/public/assets/v2.2/css/components/sidebar.css +242 -0
  186. package/public/assets/v2.2/css/components/topbar.css +138 -0
  187. package/public/assets/v2.2/css/fonts.css +30 -0
  188. package/public/assets/v2.2/css/synteq-ui-v2.css +25 -0
  189. package/public/assets/v2.2/css/theme-dark.css +38 -0
  190. package/public/assets/v2.2/css/theme-light.css +36 -0
  191. package/public/assets/v2.2/css/tokens.css +61 -0
  192. package/public/assets/v2.2/fonts/GoogleSansCode-Medium.ttf +0 -0
  193. package/public/assets/v2.2/fonts/GoogleSansCode-Regular.ttf +0 -0
  194. package/public/assets/v2.2/fonts/GoogleSansCode-SemiBold.ttf +0 -0
  195. package/public/assets/v2.2/js/synteq-ui-v2.js +869 -0
  196. package/public/favicon.svg +13 -0
  197. package/public/index.html +1083 -0
  198. package/public/styles.css +580 -0
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env bash
2
+ # ===================================================================
3
+ # config.sh — shared configuration for Octra lite_node scripts
4
+ # Source this file from other scripts: source "$(dirname "$0")/config.sh"
5
+ # ===================================================================
6
+
7
+ set -euo pipefail
8
+
9
+ # --- Node identity -------------------------------------------------
10
+ NODE_REPO="https://github.com/octra-labs/lite_node.git"
11
+ NODE_BRANCH="main"
12
+ NODE_SRC_DIR="/opt/octra/libv_litecore"
13
+ NODE_DATA_ROOT="/var/lib/octra"
14
+ NODE_DATA_DIR="${NODE_DATA_ROOT}/devnet"
15
+ NODE_SYNC_STAGE="${NODE_DATA_ROOT}/devnet.state_sync"
16
+ NODE_USER="octra"
17
+
18
+ # --- Network / API ports (from official config_val.sh defaults) ---
19
+ API_PORT="8080" # REST API (dashboard reads from here)
20
+ CONSENSUS_PORT="19000" # consensus
21
+ P2P_PORT="9000" # peer-to-peer
22
+
23
+ # --- Disk / swap ---------------------------------------------------
24
+ SWAP_FILE="/swapfile"
25
+ # Swap size in GB. This kit is deployed for several projects, so the operator
26
+ # sets it in Project settings (console) or with SWAP_GB=<n> bash pre-run.sh.
27
+ SWAP_GB="${SWAP_GB:-8}"
28
+ SWAP_SIZE="${SWAP_SIZE:-${SWAP_GB}G}" # 8 GB swap by default
29
+ MOUNT_POINT="${NODE_DATA_ROOT}"
30
+
31
+ # Node data store: the chain lives here (~40 GB today, grows over time), so the
32
+ # target wants ~1 TB. pre-run.sh is fully automatic:
33
+ # * /dev/sdb (or BLOCK_DEVICE) when present and not the system disk
34
+ # * otherwise the largest non-system whole disk >= DATA_MIN_GB
35
+ # * otherwise the largest filesystem >= DATA_MIN_GB (main disk, no formatting)
36
+ BLOCK_DEVICE="${BLOCK_DEVICE:-/dev/sdb}"
37
+ DATA_MIN_GB="${DATA_MIN_GB:-1000}"
38
+ DATA_LABEL="${DATA_LABEL:-octra-data}"
39
+ DATA_MARKER="${DATA_MARKER:-.octra-data-volume}"
40
+
41
+ # --- Build profile ---
42
+ BUILD_PROFILE="release"
43
+
44
+ # --- Helpers -------------------------------------------------------
45
+ log() { echo -e "\033[1;32m[octra]\033[0m $*"; }
46
+ warn() { echo -e "\033[1;33m[warn]\033[0m $*"; }
47
+ err() { echo -e "\033[1;31m[err]\033[0m $*" >&2; }
48
+
49
+ # --- tools on the host: check -> install -> continue ------------------
50
+ # A VPS that was just re-installed is a bare image: `jq`, `rsync` or even
51
+ # `curl` may simply not be there yet, and a step that calls them dies half way
52
+ # through with "command not found". Every step therefore asks for the tool it
53
+ # needs instead of assuming it: the check is free, apt only runs when something
54
+ # is really missing, and a retry after a dropped SSH does not re-run a full
55
+ # `apt-get update`/install for tools that are already present.
56
+ OCTRA_APT_STAMP="${OCTRA_APT_STAMP:-/root/.octra-apt-update}"
57
+ OCTRA_APT_TTL="${OCTRA_APT_TTL:-900}" # seconds an apt-get update stays fresh
58
+
59
+ octra_have() { command -v "$1" >/dev/null 2>&1; }
60
+
61
+ octra_pkg_installed() {
62
+ dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -q 'install ok installed'
63
+ }
64
+
65
+ # apt-get update at most once per OCTRA_APT_TTL seconds (checkpoint: a retry of a
66
+ # failed step must not re-sync the package lists).
67
+ apt_refresh() {
68
+ local now last
69
+ now=$(date +%s)
70
+ last=0
71
+ if [[ -f "${OCTRA_APT_STAMP}" ]]; then
72
+ last=$(tr -dc '0-9' < "${OCTRA_APT_STAMP}" 2>/dev/null || true)
73
+ fi
74
+ last="${last:-0}"
75
+ if (( now - last < OCTRA_APT_TTL )); then
76
+ log "apt lists are $(( now - last ))s old - skipping apt-get update"
77
+ return 0
78
+ fi
79
+ log "apt-get update"
80
+ if DEBIAN_FRONTEND=noninteractive apt-get update; then
81
+ echo "${now}" > "${OCTRA_APT_STAMP}" 2>/dev/null || true
82
+ return 0
83
+ fi
84
+ warn "apt-get update failed - trying once more in 10s"
85
+ sleep 10
86
+ DEBIAN_FRONTEND=noninteractive apt-get update || { err "apt-get update failed (network or mirror)"; return 1; }
87
+ echo "${now}" > "${OCTRA_APT_STAMP}" 2>/dev/null || true
88
+ }
89
+
90
+ # install_packages <pkg> ... - only the missing ones, with an apt-lock retry
91
+ # (unattended-upgrades holds the dpkg lock on a freshly booted VPS).
92
+ install_packages() {
93
+ local pkg missing=() attempt out
94
+ for pkg in "$@"; do
95
+ octra_pkg_installed "${pkg}" || missing+=("${pkg}")
96
+ done
97
+ if (( ${#missing[@]} == 0 )); then
98
+ return 0
99
+ fi
100
+ log "apt-get install: ${missing[*]}"
101
+ apt_refresh || return 1
102
+ for attempt in 1 2 3; do
103
+ if out=$(DEBIAN_FRONTEND=noninteractive apt-get install -y "${missing[@]}" 2>&1); then
104
+ printf '%s\n' "${out}"
105
+ return 0
106
+ fi
107
+ printf '%s\n' "${out}"
108
+ if [[ "${out}" == *"Could not get lock"* || "${out}" == *"dpkg frontend lock"* ]]; then
109
+ warn "dpkg is locked (another apt run) - retrying in $(( attempt * 10 ))s"
110
+ sleep $(( attempt * 10 ))
111
+ continue
112
+ fi
113
+ break
114
+ done
115
+ err "could not install: ${missing[*]}"
116
+ return 1
117
+ }
118
+
119
+ # ensure_package <pkg> - present, or installed now
120
+ ensure_package() {
121
+ local pkg="$1"
122
+ if octra_pkg_installed "${pkg}"; then
123
+ log "package ${pkg}: present"
124
+ return 0
125
+ fi
126
+ log "package ${pkg}: missing - installing"
127
+ install_packages "${pkg}" || return 1
128
+ octra_pkg_installed "${pkg}" && log "package ${pkg}: installed"
129
+ }
130
+
131
+ # ensure_tool <binary> [package] - check -> install -> continue
132
+ # returns 0 when the binary answers, 1 when it still does not exist
133
+ ensure_tool() {
134
+ local bin="$1" pkg="${2:-$1}"
135
+ if octra_have "${bin}"; then
136
+ log "tool ${bin}: present ($(command -v "${bin}"))"
137
+ return 0
138
+ fi
139
+ log "tool ${bin}: missing - installing package ${pkg}"
140
+ install_packages "${pkg}" || return 1
141
+ if octra_have "${bin}"; then
142
+ log "tool ${bin}: installed"
143
+ return 0
144
+ fi
145
+ err "tool ${bin} is still missing after installing ${pkg}"
146
+ return 1
147
+ }
148
+
149
+ # ensure_tools <bin[:package]> ... - the whole set or a non-zero return
150
+ ensure_tools() {
151
+ local spec bin pkg rc=0
152
+ for spec in "$@"; do
153
+ bin="${spec%%:*}"
154
+ pkg="${spec#*:}"
155
+ [[ "${pkg}" == "${bin}" ]] && pkg="${bin}"
156
+ ensure_tool "${bin}" "${pkg}" || rc=1
157
+ done
158
+ return "${rc}"
159
+ }
160
+
161
+ # --- Host hardening (MANDATORY, both programs) ----------------------
162
+ # MPTCP is dead weight on a node host and pure attack surface: the node, the
163
+ # dashboard and the SSH pull all speak plain TCP, nothing ever negotiates
164
+ # multipath. So every machine this toolkit prepares must come up with the knob
165
+ # off, persisted for the reboot:
166
+ #
167
+ # net.mptcp.enabled = 0 -> /etc/sysctl.d/99-disable-mptcp.conf
168
+ #
169
+ # Idempotent, and harmless on a kernel without MPTCP (the knob is absent and
170
+ # there is nothing to disable). Verify with: sysctl net.mptcp.enabled
171
+ # Both paths are overridable so the dev harness can drive every branch.
172
+ MPTCP_KNOB="${MPTCP_KNOB:-/proc/sys/net/mptcp/enabled}"
173
+ MPTCP_CONF="${MPTCP_CONF:-/etc/sysctl.d/99-disable-mptcp.conf}"
174
+
175
+ disable_mptcp() {
176
+ if [[ ! -e "${MPTCP_KNOB}" ]]; then
177
+ log "this kernel has no net.mptcp.enabled knob - MPTCP is not built in (nothing to disable)"
178
+ return 0
179
+ fi
180
+ printf 'net.mptcp.enabled = 0\n' > "${MPTCP_CONF}"
181
+ chmod 644 "${MPTCP_CONF}"
182
+ log "wrote ${MPTCP_CONF} (net.mptcp.enabled = 0)"
183
+ # immediate value first, then reload every drop-in so the running kernel and
184
+ # the files on disk agree (a stale 50-*.conf would otherwise win)
185
+ sysctl -w net.mptcp.enabled=0 >/dev/null 2>&1 || true
186
+ sysctl --system >/dev/null 2>&1 || warn "sysctl --system reported an error - the value below decides"
187
+ local value
188
+ value=$(sysctl -n net.mptcp.enabled 2>/dev/null || echo unknown)
189
+ if [[ "${value}" == "0" ]]; then
190
+ log "MPTCP disabled: $(sysctl net.mptcp.enabled 2>/dev/null || echo 'net.mptcp.enabled = 0')"
191
+ return 0
192
+ fi
193
+ err "net.mptcp.enabled is '${value}' - disabling MPTCP is mandatory on a node host"
194
+ return 1
195
+ }
196
+
197
+ # --- the node repo's local OCaml switch ------------------------------
198
+ # controls/install.sh --source-build builds the switch INSIDE the repo
199
+ # <repo>/runtime_data/toolchains/ocaml/_opam (holds .opam-switch)
200
+ # and links it as
201
+ # <repo>/_opam -> runtime_data/toolchains/ocaml/_opam
202
+ #
203
+ # opam 2.3 (Debian 13) CANNOT re-create that link: `opam switch link` calls
204
+ # rmdir(2) on the existing symlink, dies with
205
+ # «"rmdir" failed on <repo>/_opam: Not a directory»
206
+ # and the failed call leaves the switch prefix EMPTY - it throws away a 15-30
207
+ # minute build. Proven on 2026-11-02 with a throw-away switch: the first
208
+ # `switch link` creates the link, the second one destroys the switch.
209
+ #
210
+ # Both scripts of the node repo call `opam switch link` (controls/install.sh and
211
+ # controls/config_val.sh), so this kit unlinks <repo>/_opam first and lets them
212
+ # re-create it.
213
+ OCAML_SWITCH_REL="runtime_data/toolchains/ocaml"
214
+
215
+ ocaml_switch_root() { printf '%s/%s\n' "${NODE_SRC_DIR}" "${OCAML_SWITCH_REL}"; }
216
+
217
+ ocaml_switch_built() { [[ -d "$(ocaml_switch_root)/_opam/.opam-switch" ]]; }
218
+
219
+ unlink_ocaml_switch_link() { # unlink_ocaml_switch_link [<why>]
220
+ local link="${NODE_SRC_DIR}/_opam"
221
+ if [[ -L "${link}" ]]; then
222
+ log "unlinking ${link} (${1:-opam 2.3 cannot relink an existing _opam})"
223
+ rm -f "${link}"
224
+ elif [[ -d "${link}" ]]; then
225
+ if rmdir "${link}" 2>/dev/null; then
226
+ log "removed the empty ${link} directory (${1:-opam needs a free path})"
227
+ else
228
+ warn "${link} is a non-empty directory, not a link - leaving it as it is"
229
+ fi
230
+ fi
231
+ return 0
232
+ }
233
+
234
+ # A cached "<unit>.rc == 0" is NOT proof the toolchain is usable: the failed
235
+ # `opam switch link` above empties the switch prefix and leaves the exit code
236
+ # untouched. Drop what is left, so the next run rebuilds instead of reporting a
237
+ # cached success.
238
+ drop_broken_ocaml_switch() { # drop_broken_ocaml_switch [<rc file to forget>]
239
+ local root rc="${1:-}"
240
+ if ocaml_switch_built; then
241
+ return 0 # never touch a real switch
242
+ fi
243
+ unlink_ocaml_switch_link "the switch has to be rebuilt"
244
+ root=$(ocaml_switch_root)
245
+ if [[ -d "${root}" ]]; then
246
+ warn "dropping the incomplete switch prefix ${root} (no .opam-switch)"
247
+ rm -rf "${root}"
248
+ fi
249
+ if [[ -n "${rc}" ]]; then
250
+ rm -f "${rc}"
251
+ fi
252
+ return 0
253
+ }
254
+
255
+ # Run a command as the octra user
256
+ as_octra() {
257
+ sudo -iu "${NODE_USER}" "$@"
258
+ }
259
+
260
+ # Run a command inside the node source directory as octra
261
+ in_src() {
262
+ sudo -iu "${NODE_USER}" bash -c "cd '${NODE_SRC_DIR}' && $*"
263
+ }
@@ -0,0 +1,197 @@
1
+ #!/usr/bin/env bash
2
+ # ===================================================================
3
+ # extract-data.sh — collect validator node data into a JSON file
4
+ #
5
+ # Gathers all critical files needed for migration to a new machine:
6
+ # - wallet.json (validator wallet + keys)
7
+ # - node.env (node configuration)
8
+ # - network.env (network parameters)
9
+ # - SOURCE_COMMIT (exact build commit)
10
+ # - node status (consensus head, sync state, peer count)
11
+ # - system info (hostname, IP, OS, kernel, uptime)
12
+ #
13
+ # Output: /tmp/octra-node-data.json (also printed to stdout)
14
+ #
15
+ # Usage: sudo bash extract-data.sh
16
+ # ===================================================================
17
+
18
+ set -euo pipefail
19
+ source "$(dirname "$0")/config.sh"
20
+
21
+ if [[ $EUID -ne 0 ]]; then
22
+ err "Run as root: sudo bash $0"
23
+ exit 1
24
+ fi
25
+
26
+ # --- Collect file contents -----------------------------------------
27
+ get_file() {
28
+ local f="$1"
29
+ if [[ -f "$f" ]]; then
30
+ cat "$f" 2>/dev/null || echo ""
31
+ else
32
+ echo ""
33
+ fi
34
+ }
35
+
36
+ # Helper: base64-encode a file (for binary or sensitive content)
37
+ get_file_b64() {
38
+ local f="$1"
39
+ if [[ -f "$f" ]]; then
40
+ base64 -w0 "$f" 2>/dev/null || echo ""
41
+ else
42
+ echo ""
43
+ fi
44
+ }
45
+
46
+ # --- Gather node status (best-effort, may fail if node not running) -
47
+ STAT_OUTPUT=""
48
+ STAT_LOGS=""
49
+ if su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/stat.sh" >/tmp/octra_stat.txt 2>&1; then
50
+ STAT_OUTPUT=$(cat /tmp/octra_stat.txt)
51
+ else
52
+ STAT_OUTPUT="$(cat /tmp/octra_stat.txt 2>/dev/null || echo 'node not running')"
53
+ fi
54
+
55
+ # --- Gather system info --------------------------------------------
56
+ HOSTNAME=$(hostname)
57
+ PUBLIC_IP=$(curl -s --max-time 5 https://api.ipify.org 2>/dev/null || echo "")
58
+ LOCAL_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "")
59
+ OS_INFO=$(. /etc/os-release 2>/dev/null && echo "${PRETTY_NAME:-unknown}" || echo "unknown")
60
+ KERNEL=$(uname -r)
61
+ UPTIME=$(uptime -p 2>/dev/null || echo "")
62
+ CPU_COUNT=$(nproc 2>/dev/null || echo 0)
63
+ MEM_TOTAL=$(free -h 2>/dev/null | awk '/^Mem:/ {print $2}' || echo "")
64
+ DISK_USAGE=$(df -h "${NODE_DATA_ROOT}" 2>/dev/null | tail -1 | awk '{print $3 "/" $2 " (" $5 " used)"}' || echo "")
65
+
66
+ # --- Gather storage info -------------------------------------------
67
+ STORAGE_INFO=""
68
+ if su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/storage.sh" >/tmp/octra_storage.txt 2>&1; then
69
+ STORAGE_INFO=$(cat /tmp/octra_storage.txt)
70
+ fi
71
+
72
+ # --- Gather enroll status -----------------------------------------
73
+ ENROLL_STATUS=""
74
+ if su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh status" >/tmp/octra_enroll.txt 2>&1; then
75
+ ENROLL_STATUS=$(cat /tmp/octra_enroll.txt)
76
+ fi
77
+
78
+ # --- Source commit -------------------------------------------------
79
+ SOURCE_COMMIT=$(get_file "${NODE_SRC_DIR}/SOURCE_COMMIT")
80
+
81
+ # --- Wallet (base64-encoded for safe transport) --------------------
82
+ WALLET_PATH="${NODE_SRC_DIR}/.keys/validator/wallet.json"
83
+ if [[ ! -f "${WALLET_PATH}" ]]; then
84
+ WALLET_PATH="${NODE_DATA_DIR}/wallet.json"
85
+ fi
86
+ WALLET_B64=$(get_file_b64 "${WALLET_PATH}")
87
+
88
+ # --- node.env (base64-encoded) ------------------------------------
89
+ NODE_ENV_PATH="${NODE_SRC_DIR}/.keys/validator/node.env"
90
+ if [[ ! -f "${NODE_ENV_PATH}" ]]; then
91
+ NODE_ENV_PATH="${NODE_SRC_DIR}/config/node.env"
92
+ fi
93
+ NODE_ENV_B64=$(get_file_b64 "${NODE_ENV_PATH}")
94
+
95
+ # --- network.env (base64-encoded) --------------------------------
96
+ NETWORK_ENV_PATH="${NODE_SRC_DIR}/config/network.env"
97
+ NETWORK_ENV_B64=$(get_file_b64 "${NETWORK_ENV_PATH}")
98
+
99
+ # --- network.env.sha256 -------------------------------------------
100
+ NETWORK_SHA256=$(get_file "${NETWORK_ENV_PATH}.sha256")
101
+
102
+ # --- Gather file list in data dir (for inventory) -----------------
103
+ DATA_DIR_FILES=""
104
+ if [[ -d "${NODE_DATA_DIR}" ]]; then
105
+ # NOTE: `| head -50` would SIGPIPE the find under `set -o pipefail` (rc 141)
106
+ # as soon as the data dir holds more than 50 files, so use sed instead.
107
+ DATA_DIR_FILES=$(find "${NODE_DATA_DIR}" -maxdepth 2 -type f 2>/dev/null | sed -n '1,50p' | while read -r f; do
108
+ SIZE=$(stat -c%s "$f" 2>/dev/null || echo 0)
109
+ echo "$(basename "$f"):${SIZE}"
110
+ done | tr '\n' ',' | sed 's/,$//')
111
+ fi
112
+
113
+ # --- Build the JSON ------------------------------------------------
114
+ # Using jq for proper JSON construction
115
+ TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
116
+
117
+ jq -n \
118
+ --arg timestamp "$TIMESTAMP" \
119
+ --arg hostname "$HOSTNAME" \
120
+ --arg public_ip "$PUBLIC_IP" \
121
+ --arg local_ip "$LOCAL_IP" \
122
+ --arg os "$OS_INFO" \
123
+ --arg kernel "$KERNEL" \
124
+ --arg uptime "$UPTIME" \
125
+ --arg cpu "$CPU_COUNT" \
126
+ --arg mem "$MEM_TOTAL" \
127
+ --arg disk "$DISK_USAGE" \
128
+ --arg source_commit "$SOURCE_COMMIT" \
129
+ --arg wallet_b64 "$WALLET_B64" \
130
+ --arg node_env_b64 "$NODE_ENV_B64" \
131
+ --arg network_env_b64 "$NETWORK_ENV_B64" \
132
+ --arg network_sha256 "$NETWORK_SHA256" \
133
+ --arg stat_output "$STAT_OUTPUT" \
134
+ --arg storage_info "$STORAGE_INFO" \
135
+ --arg enroll_status "$ENROLL_STATUS" \
136
+ --arg data_dir "$NODE_DATA_DIR" \
137
+ --arg src_dir "$NODE_SRC_DIR" \
138
+ --arg api_port "$API_PORT" \
139
+ --arg consensus_port "$CONSENSUS_PORT" \
140
+ --arg p2p_port "$P2P_PORT" \
141
+ --arg data_files "$DATA_DIR_FILES" \
142
+ '{
143
+ meta: {
144
+ extracted_at: $timestamp,
145
+ script_version: "1.0"
146
+ },
147
+ system: {
148
+ hostname: $hostname,
149
+ public_ip: $public_ip,
150
+ local_ip: $local_ip,
151
+ os: $os,
152
+ kernel: $kernel,
153
+ uptime: $uptime,
154
+ cpu_cores: ($cpu | tonumber),
155
+ memory_total: $mem,
156
+ disk_usage: $disk
157
+ },
158
+ node: {
159
+ source_dir: $src_dir,
160
+ source_commit: $source_commit,
161
+ data_dir: $data_dir,
162
+ ports: {
163
+ api: ($api_port | tonumber),
164
+ consensus: ($consensus_port | tonumber),
165
+ p2p: ($p2p_port | tonumber)
166
+ },
167
+ status_raw: $stat_output,
168
+ storage_raw: $storage_info,
169
+ enroll_status_raw: $enroll_status
170
+ },
171
+ migration_data: {
172
+ wallet_json_b64: $wallet_b64,
173
+ node_env_b64: $node_env_b64,
174
+ network_env_b64: $network_env_b64,
175
+ network_env_sha256: $network_sha256
176
+ },
177
+ data_files: ($data_files | split(",") | map(split(":") | {(.[0]): (.[1] | tonumber)}) | add // {})
178
+ }' > /tmp/octra-node-data.json
179
+
180
+ # Print summary
181
+ echo
182
+ log "Data extracted to /tmp/octra-node-data.json"
183
+ log "Summary:"
184
+ jq '{
185
+ hostname: .system.hostname,
186
+ public_ip: .system.public_ip,
187
+ source_commit: .node.source_commit,
188
+ has_wallet: (.migration_data.wallet_json_b64 | length > 0),
189
+ has_node_env: (.migration_data.node_env_b64 | length > 0),
190
+ has_network_env: (.migration_data.network_env_b64 | length > 0)
191
+ }' /tmp/octra-node-data.json
192
+
193
+ # Also copy to the source dir for easy access
194
+ cp /tmp/octra-node-data.json "${NODE_SRC_DIR}/config/node-data.json" 2>/dev/null || true
195
+ chown "${NODE_USER}:${NODE_USER}" "${NODE_SRC_DIR}/config/node-data.json" 2>/dev/null || true
196
+
197
+ log "Done. Copy it locally with scp if needed."
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env bash
2
+ # ===================================================================
3
+ # harden-host.sh - mandatory baseline hardening for an Octra node host
4
+ #
5
+ # net.mptcp.enabled = 0
6
+ #
7
+ # MPTCP is not used by the node, the dashboard or the SSH pull, and a host that
8
+ # answers on the MPTCP stack is an avoidable vector of attack on a public VPS.
9
+ # Disabling it is part of this project's baseline, so every program that
10
+ # prepares a machine applies it:
11
+ #
12
+ # * scripts/pre-run.sh (both programs, via disable_mptcp)
13
+ # * deploy/migrate/manage phases (every run, so a resumed run repairs the host)
14
+ # * migrate target, before anything is copied
15
+ #
16
+ # The three operator steps are the ones documented in the runbook:
17
+ #
18
+ # sudo sysctl -w net.mptcp.enabled=0
19
+ # echo "net.mptcp.enabled = 0" | sudo tee /etc/sysctl.d/99-disable-mptcp.conf
20
+ # sudo sysctl --system
21
+ # sysctl net.mptcp.enabled -> net.mptcp.enabled = 0
22
+ #
23
+ # Idempotent. Exits 1 when the kernel offers the knob but it stays enabled.
24
+ #
25
+ # Usage: sudo bash harden-host.sh
26
+ # ===================================================================
27
+
28
+ set -euo pipefail
29
+ source "$(dirname "$0")/config.sh"
30
+
31
+ if [[ $EUID -ne 0 ]]; then
32
+ err "run as root: sudo bash $0"
33
+ exit 1
34
+ fi
35
+
36
+ log "=== hardening $(hostname) (MPTCP off) ==="
37
+ disable_mptcp
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env bash
2
+ # ===================================================================
3
+ # install-deps.sh — install system build dependencies for Octra lite_node
4
+ #
5
+ # Installs: git, ca-certificates, build-essential, GMP, SQLite3, libev,
6
+ # Rust toolchain, OCaml/opam (via install.sh --source-build).
7
+ #
8
+ # Usage: sudo bash install-deps.sh
9
+ # Idempotent — apt skips packages already installed.
10
+ # ===================================================================
11
+
12
+ set -euo pipefail
13
+ source "$(dirname "$0")/config.sh"
14
+
15
+ if [[ $EUID -ne 0 ]]; then
16
+ err "Run as root: sudo bash $0"
17
+ exit 1
18
+ fi
19
+
20
+ log "Updating package lists…"
21
+ apt-get update -qq
22
+
23
+ log "Installing base packages…"
24
+ apt-get install -y -qq \
25
+ ca-certificates \
26
+ git \
27
+ curl \
28
+ wget \
29
+ build-essential \
30
+ pkg-config \
31
+ libgmp-dev \
32
+ libsqlite3-dev \
33
+ libev-dev \
34
+ cmake \
35
+ jq \
36
+ htop \
37
+ > /dev/null
38
+
39
+ log "Base packages installed"
40
+
41
+ # NOTE: Do NOT install Rust separately. The official install.sh
42
+ # --source-build installs a pinned Rust 1.88.0 toolchain into
43
+ # runtime_data/toolchains/ for the octra user. A separate rustup
44
+ # install in /root/.cargo causes permission conflicts.
45
+
46
+ # --- Create the octra system user --------------------------------
47
+ if ! id "${NODE_USER}" &>/dev/null; then
48
+ log "Creating system user '${NODE_USER}'…"
49
+ useradd -r -m -d "/home/${NODE_USER}" -s /bin/bash "${NODE_USER}"
50
+ log "User '${NODE_USER}' created"
51
+ else
52
+ log "User '${NODE_USER}' already exists"
53
+ fi
54
+
55
+ # --- Create the node source directory ------------------------------
56
+ log "Creating ${NODE_SRC_DIR}…"
57
+ mkdir -p "$(dirname "${NODE_SRC_DIR}")"
58
+ chown -R "${NODE_USER}:${NODE_USER}" "$(dirname "${NODE_SRC_DIR}")"
59
+
60
+ # --- Clone the lite_node repo (if not already present) ------------
61
+ if [[ ! -d "${NODE_SRC_DIR}/.git" ]]; then
62
+ log "Cloning lite_node repository…"
63
+ git clone --branch "${NODE_BRANCH}" --single-branch "${NODE_REPO}" "${NODE_SRC_DIR}"
64
+ chown -R "${NODE_USER}:${NODE_USER}" "${NODE_SRC_DIR}"
65
+ log "Repository cloned"
66
+ else
67
+ log "Repository already present at ${NODE_SRC_DIR}"
68
+ fi
69
+
70
+ # --- Run the official install.sh --source-build -------------------
71
+ # install.sh calls `opam switch link` itself, and opam 2.3 wipes the switch when
72
+ # <repo>/_opam already exists (see config.sh) - so clear the link first.
73
+ unlink_ocaml_switch_link "install.sh runs opam switch link next"
74
+ # This installs OCaml, Dune, builds all dependencies from source
75
+ log "Running official install.sh --source-build (this compiles OCaml + deps)…"
76
+ log "This may take 15–30 minutes on a 4-core VPS…"
77
+
78
+ cd "${NODE_SRC_DIR}"
79
+ export OCTRA_OPERATOR_USER="${NODE_USER}"
80
+ export OCTRA_DATA_ROOT="${NODE_DATA_ROOT}"
81
+
82
+ sh controls/install.sh --source-build
83
+
84
+ # --- Verify the build ---------------------------------------------
85
+ log "Running check.sh to verify the build…"
86
+ if su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/check.sh"; then
87
+ log "Build verification passed"
88
+ else
89
+ warn "check.sh reported issues — review the output above"
90
+ fi
91
+
92
+ echo
93
+ log "Dependencies installed. Next: run node-setup.sh to configure the node."
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env bash
2
+ # ===================================================================
3
+ # node-enroll.sh — join the Octra network as a validator
4
+ #
5
+ # Steps:
6
+ # 1. Join the active set (stake OCTRA tokens)
7
+ # 2. Check enrollment status
8
+ # 3. Show node status
9
+ #
10
+ # Usage:
11
+ # sudo bash node-enroll.sh # default stake (1000000)
12
+ # sudo bash node-enroll.sh 500000 # custom stake amount
13
+ # sudo bash node-enroll.sh exit # leave active set
14
+ # sudo bash node-enroll.sh withdraw # withdraw stake
15
+ #
16
+ # IMPORTANT: The node must be fully synced before enrolling.
17
+ # ===================================================================
18
+
19
+ set -euo pipefail
20
+ source "$(dirname "$0")/config.sh"
21
+
22
+ if [[ $EUID -ne 0 ]]; then
23
+ err "Run as root: sudo bash $0"
24
+ exit 1
25
+ fi
26
+
27
+ ACTION="${1:-join}"
28
+ STAKE_AMOUNT="${2:-1000000}"
29
+
30
+ case "${ACTION}" in
31
+ join)
32
+ log "Joining the network with stake ${STAKE_AMOUNT}…"
33
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh join --amount ${STAKE_AMOUNT}"
34
+ log "Checking enrollment status…"
35
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh status"
36
+ echo
37
+ log "Node status:"
38
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/stat.sh"
39
+ ;;
40
+
41
+ status)
42
+ log "Enrollment status:"
43
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh status"
44
+ ;;
45
+
46
+ exit)
47
+ warn "Leaving the active validator set…"
48
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh exit"
49
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh status"
50
+ log "Exited active set."
51
+ ;;
52
+
53
+ withdraw)
54
+ warn "Withdrawing stake…"
55
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh withdraw"
56
+ su - "${NODE_USER}" -c "cd ${NODE_SRC_DIR} && sh controls/enroll.sh status"
57
+ log "Stake withdrawn."
58
+ ;;
59
+
60
+ *)
61
+ err "Unknown action: ${ACTION}"
62
+ echo "Usage: sudo bash node-enroll.sh [join|status|exit|withdraw] [amount]"
63
+ exit 1
64
+ ;;
65
+ esac