cyberia 3.2.9 → 3.2.12

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 (169) hide show
  1. package/.github/workflows/engine-cyberia.cd.yml +6 -0
  2. package/.github/workflows/npmpkg.ci.yml +1 -0
  3. package/.github/workflows/pwa-microservices-template-test.ci.yml +1 -1
  4. package/.github/workflows/release.cd.yml +1 -0
  5. package/.vscode/extensions.json +9 -9
  6. package/.vscode/settings.json +20 -4
  7. package/CHANGELOG.md +213 -1
  8. package/CLI-HELP.md +92 -23
  9. package/README.md +190 -348
  10. package/bin/build.js +24 -8
  11. package/bin/build.template.js +187 -0
  12. package/bin/cyberia.js +229 -52
  13. package/bin/deploy.js +12 -2
  14. package/bin/index.js +229 -52
  15. package/bump.config.js +26 -0
  16. package/conf.js +130 -24
  17. package/deployment.yaml +4 -2
  18. package/hardhat/package-lock.json +113 -144
  19. package/hardhat/package.json +4 -3
  20. package/manifests/cronjobs/dd-cron/dd-cron-backup.yaml +1 -1
  21. package/manifests/cronjobs/dd-cron/dd-cron-dns.yaml +1 -1
  22. package/manifests/deployment/dd-cyberia-development/deployment.yaml +4 -2
  23. package/manifests/deployment/dd-default-development/deployment.yaml +2 -2
  24. package/manifests/deployment/dd-test-development/deployment.yaml +4 -2
  25. package/manifests/kind-config-dev.yaml +8 -0
  26. package/manifests/lxd/lxd-admin-profile.yaml +12 -3
  27. package/manifests/mongodb/pv-pvc.yaml +44 -8
  28. package/manifests/mongodb/statefulset.yaml +55 -68
  29. package/manifests/mongodb-4.4/headless-service.yaml +10 -0
  30. package/manifests/mongodb-4.4/kustomization.yaml +3 -1
  31. package/manifests/mongodb-4.4/mongodb-nodeport.yaml +17 -0
  32. package/manifests/mongodb-4.4/pv-pvc.yaml +10 -14
  33. package/manifests/mongodb-4.4/statefulset.yaml +79 -0
  34. package/manifests/mongodb-4.4/storage-class.yaml +9 -0
  35. package/manifests/valkey/statefulset.yaml +1 -1
  36. package/manifests/valkey/valkey-nodeport.yaml +17 -0
  37. package/package.json +27 -15
  38. package/scripts/ipxe-setup.sh +52 -49
  39. package/scripts/k3s-node-setup.sh +81 -46
  40. package/scripts/lxd-vm-setup.sh +193 -8
  41. package/scripts/maas-nat-firewalld.sh +145 -0
  42. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.router.js +38 -33
  43. package/src/api/atlas-sprite-sheet/atlas-sprite-sheet.service.js +16 -16
  44. package/src/api/core/core.router.js +19 -14
  45. package/src/api/core/core.service.js +5 -5
  46. package/src/api/crypto/crypto.router.js +18 -12
  47. package/src/api/crypto/crypto.service.js +3 -3
  48. package/src/api/cyberia-action/cyberia-action.model.js +1 -1
  49. package/src/api/cyberia-action/cyberia-action.router.js +22 -18
  50. package/src/api/cyberia-action/cyberia-action.service.js +5 -5
  51. package/src/api/cyberia-client-hints/cyberia-client-hints.controller.js +74 -0
  52. package/src/api/cyberia-client-hints/cyberia-client-hints.model.js +99 -0
  53. package/src/api/cyberia-client-hints/cyberia-client-hints.router.js +98 -0
  54. package/src/api/cyberia-client-hints/cyberia-client-hints.service.js +152 -0
  55. package/src/api/cyberia-dialogue/cyberia-dialogue.router.js +25 -20
  56. package/src/api/cyberia-dialogue/cyberia-dialogue.service.js +6 -6
  57. package/src/api/cyberia-entity/cyberia-entity.router.js +22 -18
  58. package/src/api/cyberia-entity/cyberia-entity.service.js +5 -5
  59. package/src/api/cyberia-instance/cyberia-fallback-world.js +79 -4
  60. package/src/api/cyberia-instance/cyberia-instance.router.js +57 -52
  61. package/src/api/cyberia-instance/cyberia-instance.service.js +10 -10
  62. package/src/api/cyberia-instance/cyberia-world-generator.js +3 -3
  63. package/src/api/cyberia-instance-conf/cyberia-instance-conf.model.js +14 -48
  64. package/src/api/cyberia-instance-conf/cyberia-instance-conf.router.js +22 -18
  65. package/src/api/cyberia-instance-conf/cyberia-instance-conf.service.js +5 -5
  66. package/src/api/cyberia-map/cyberia-map.router.js +35 -30
  67. package/src/api/cyberia-map/cyberia-map.service.js +7 -7
  68. package/src/api/cyberia-quest/cyberia-quest.model.js +1 -1
  69. package/src/api/cyberia-quest/cyberia-quest.router.js +22 -18
  70. package/src/api/cyberia-quest/cyberia-quest.service.js +5 -5
  71. package/src/api/cyberia-quest-progress/cyberia-quest-progress.router.js +22 -18
  72. package/src/api/cyberia-quest-progress/cyberia-quest-progress.service.js +5 -5
  73. package/src/api/cyberia-server-defaults/cyberia-server-defaults.js +451 -0
  74. package/src/api/default/default.router.js +22 -18
  75. package/src/api/default/default.service.js +5 -5
  76. package/src/api/document/document.router.js +28 -23
  77. package/src/api/document/document.service.js +100 -23
  78. package/src/api/file/file.router.js +19 -13
  79. package/src/api/file/file.service.js +9 -7
  80. package/src/api/instance/instance.router.js +29 -24
  81. package/src/api/instance/instance.service.js +6 -6
  82. package/src/api/ipfs/ipfs.router.js +21 -16
  83. package/src/api/ipfs/ipfs.service.js +8 -8
  84. package/src/api/object-layer/object-layer.router.js +512 -507
  85. package/src/api/object-layer/object-layer.service.js +17 -14
  86. package/src/api/object-layer-render-frames/object-layer-render-frames.router.js +22 -18
  87. package/src/api/object-layer-render-frames/object-layer-render-frames.service.js +5 -5
  88. package/src/api/test/test.router.js +17 -12
  89. package/src/api/types.js +24 -0
  90. package/src/api/user/guest.service.js +5 -4
  91. package/src/api/user/user.router.js +297 -288
  92. package/src/api/user/user.service.js +100 -35
  93. package/src/cli/baremetal.js +132 -101
  94. package/src/cli/cluster.js +700 -232
  95. package/src/cli/db.js +59 -60
  96. package/src/cli/deploy.js +216 -137
  97. package/src/cli/fs.js +13 -3
  98. package/src/cli/index.js +80 -15
  99. package/src/cli/ipfs.js +4 -6
  100. package/src/cli/kubectl.js +4 -1
  101. package/src/cli/lxd.js +1099 -223
  102. package/src/cli/monitor.js +9 -3
  103. package/src/cli/release.js +334 -140
  104. package/src/cli/repository.js +68 -23
  105. package/src/cli/run.js +193 -49
  106. package/src/cli/secrets.js +11 -2
  107. package/src/cli/test.js +9 -3
  108. package/src/client/Default.index.js +9 -3
  109. package/src/client/components/core/Auth.js +5 -0
  110. package/src/client/components/core/ClientEvents.js +76 -0
  111. package/src/client/components/core/EventBus.js +4 -0
  112. package/src/client/components/core/Modal.js +82 -41
  113. package/src/client/components/core/PanelForm.js +56 -52
  114. package/src/client/components/core/Worker.js +162 -363
  115. package/src/client/components/cyberia/MapEngineCyberia.js +1 -1
  116. package/src/client/components/cyberia/SharedDefaultsCyberia.js +330 -0
  117. package/src/client/public/cyberia-docs/ARCHITECTURE.md +50 -410
  118. package/src/client/public/cyberia-docs/CYBERIA-CLI.md +114 -327
  119. package/src/client/public/cyberia-docs/CYBERIA-CLIENT.md +200 -222
  120. package/src/client/public/cyberia-docs/CYBERIA-SERVER.md +203 -185
  121. package/src/client/public/cyberia-docs/CYBERIA.md +259 -0
  122. package/src/client/public/cyberia-docs/OFF-CHAIN-ECONOMY.md +2 -2
  123. package/src/client/public/cyberia-docs/ROADMAP.md +1 -1
  124. package/src/client/public/cyberia-docs/UNDERPOST-PLATFORM.md +106 -0
  125. package/src/client/public/cyberia-docs/WHITE-PAPER.md +1 -1
  126. package/src/client/services/cyberia-client-hints/cyberia-client-hints.service.js +99 -0
  127. package/src/client/ssr/views/CyberiaServerMetrics.js +982 -0
  128. package/src/client/sw/core.sw.js +174 -112
  129. package/src/db/DataBaseProvider.js +115 -15
  130. package/src/db/mariadb/MariaDB.js +2 -1
  131. package/src/db/mongo/MongoBootstrap.js +657 -0
  132. package/src/db/mongo/MongooseDB.js +129 -21
  133. package/src/grpc/cyberia/grpc-server.js +25 -57
  134. package/src/index.js +1 -1
  135. package/src/runtime/cyberia-client/Dockerfile +24 -3
  136. package/src/runtime/cyberia-client/Dockerfile.dev +82 -0
  137. package/src/runtime/cyberia-server/Dockerfile +29 -4
  138. package/src/runtime/cyberia-server/Dockerfile.dev +71 -0
  139. package/src/runtime/express/Express.js +2 -2
  140. package/src/runtime/wp/Wp.js +8 -5
  141. package/src/server/auth.js +2 -2
  142. package/src/server/client-build-docs.js +1 -1
  143. package/src/server/client-build.js +94 -129
  144. package/src/server/conf.js +86 -83
  145. package/src/server/process.js +180 -19
  146. package/src/server/proxy.js +9 -2
  147. package/src/server/runtime.js +1 -1
  148. package/src/server/start.js +17 -5
  149. package/src/server/valkey.js +2 -0
  150. package/src/ws/IoInterface.js +16 -16
  151. package/src/ws/core/channels/core.ws.chat.js +11 -11
  152. package/src/ws/core/channels/core.ws.mailer.js +29 -29
  153. package/src/ws/core/channels/core.ws.stream.js +19 -19
  154. package/src/ws/core/core.ws.connection.js +8 -8
  155. package/src/ws/core/core.ws.server.js +6 -5
  156. package/src/ws/default/channels/default.ws.main.js +10 -10
  157. package/src/ws/default/default.ws.connection.js +4 -4
  158. package/src/ws/default/default.ws.server.js +4 -3
  159. package/bin/file.js +0 -202
  160. package/bin/vs.js +0 -74
  161. package/bin/zed.js +0 -84
  162. package/src/api/cyberia-instance-conf/cyberia-instance-conf.defaults.js +0 -574
  163. package/src/client/components/cyberia-portal/CommonCyberiaPortal.js +0 -467
  164. package/src/client/ssr/email/DefaultRecoverEmail.js +0 -21
  165. package/src/client/ssr/email/DefaultVerifyEmail.js +0 -17
  166. package/src/client/ssr/pages/CyberiaServerMetrics.js +0 -461
  167. /package/src/client/ssr/{offline → views}/Maintenance.js +0 -0
  168. /package/src/client/ssr/{offline → views}/NoNetworkConnection.js +0 -0
  169. /package/src/client/ssr/{pages → views}/Test.js +0 -0
@@ -14,39 +14,39 @@ REBUILD=false
14
14
  EMBED_SCRIPT=""
15
15
 
16
16
  while [[ $# -gt 0 ]]; do
17
- case $1 in
18
- --rebuild)
19
- REBUILD=true
20
- shift # past argument
21
- ;;
22
- --target-arch)
23
- case "$2" in
24
- arm64)
25
- TARGET_ARCH="aarch64"
26
- ;;
27
- amd64)
28
- TARGET_ARCH="x86_64"
29
- ;;
17
+ case $1 in
18
+ --rebuild)
19
+ REBUILD=true
20
+ shift # past argument
21
+ ;;
22
+ --target-arch)
23
+ case "$2" in
24
+ arm64)
25
+ TARGET_ARCH="aarch64"
26
+ ;;
27
+ amd64)
28
+ TARGET_ARCH="x86_64"
29
+ ;;
30
+ *)
31
+ echo "Error: Unsupported architecture '$2'. Use 'arm64' or 'amd64'."
32
+ exit 1
33
+ ;;
34
+ esac
35
+ shift # past argument
36
+ shift # past value
37
+ ;;
38
+ --embed-script)
39
+ EMBED_SCRIPT="$2"
40
+ shift # past argument
41
+ shift # past value
42
+ ;;
30
43
  *)
31
- echo "Error: Unsupported architecture '$2'. Use 'arm64' or 'amd64'."
32
- exit 1
33
- ;;
34
- esac
35
- shift # past argument
36
- shift # past value
37
- ;;
38
- --embed-script)
39
- EMBED_SCRIPT="$2"
40
- shift # past argument
41
- shift # past value
42
- ;;
43
- *)
44
- if [ -z "$TARGET_DIR_ARG" ]; then
45
- TARGET_DIR_ARG="$1"
46
- fi
47
- shift # past argument
48
- ;;
49
- esac
44
+ if [ -z "$TARGET_DIR_ARG" ]; then
45
+ TARGET_DIR_ARG="$1"
46
+ fi
47
+ shift # past argument
48
+ ;;
49
+ esac
50
50
  done
51
51
 
52
52
  # Use argument if provided, otherwise env var, otherwise current dir
@@ -64,7 +64,7 @@ echo "Embed Script: ${EMBED_SCRIPT:-none}"
64
64
  # Determine iPXE build target based on requested architecture
65
65
  if [ "$TARGET_ARCH" = "aarch64" ]; then
66
66
  BUILD_TARGET="bin-arm64-efi/ipxe.efi"
67
- elif [ "$TARGET_ARCH" = "x86_64" ]; then
67
+ elif [ "$TARGET_ARCH" = "x86_64" ]; then
68
68
  BUILD_TARGET="bin-x86_64-efi/ipxe.efi"
69
69
  else
70
70
  echo "Error: Unsupported target architecture '$TARGET_ARCH'"
@@ -79,49 +79,50 @@ DO_BUILD=false
79
79
 
80
80
  if [ "$REBUILD" = true ]; then
81
81
  DO_BUILD=true
82
- elif [ ! -f "$COMPILED_SRC_PATH" ]; then
82
+ elif [ ! -f "$COMPILED_SRC_PATH" ]; then
83
83
  echo "Binary not found at $COMPILED_SRC_PATH. Initiating build..."
84
84
  DO_BUILD=true
85
+
85
86
  else
86
- echo "Binary found at $COMPILED_SRC_PATH. Skipping build."
87
+ echo "Binary found at $COMPILED_SRC_PATH with matching embedded script. Skipping build."
87
88
  fi
88
89
 
89
90
  if [ "$DO_BUILD" = true ]; then
90
-
91
+
91
92
  # Helper function for package manager
92
93
  if command -v dnf &> /dev/null; then
93
94
  PKG_MGR="dnf"
94
95
  else
95
96
  PKG_MGR="yum"
96
97
  fi
97
-
98
+
98
99
  # --- 2. Install Dependencies (RHEL/CentOS/Fedora) ---
99
100
  echo ""
100
101
  echo "--- Installing Build Dependencies ---"
101
102
  echo "Requesting sudo permissions..."
102
-
103
+
103
104
  COMMON_PKGS="git make binutils-devel xz-devel perl"
104
-
105
+
105
106
  # Logic to determine if we need native or cross-compilers
106
107
  if [ "$HOST_ARCH" = "$TARGET_ARCH" ]; then
107
108
  # Native compilation
108
109
  echo "Architecture match ($HOST_ARCH). Installing native GCC..."
109
110
  sudo $PKG_MGR install -y $COMMON_PKGS gcc
110
111
  CROSS_COMPILE_PREFIX=""
111
-
112
- elif [ "$HOST_ARCH" = "x86_64" ] && [ "$TARGET_ARCH" = "aarch64" ]; then
112
+
113
+ elif [ "$HOST_ARCH" = "x86_64" ] && [ "$TARGET_ARCH" = "aarch64" ]; then
113
114
  # Cross-compilation: x86_64 host -> aarch64 target
114
115
  echo "Cross-compiling for $TARGET_ARCH on $HOST_ARCH..."
115
116
  # Note: Ensure EPEL repo is enabled on RHEL/CentOS for this package
116
117
  sudo $PKG_MGR install -y $COMMON_PKGS gcc-aarch64-linux-gnu
117
118
  CROSS_COMPILE_PREFIX="aarch64-linux-gnu-"
118
-
119
+
119
120
  else
120
121
  echo "Error: No automated path defined for Host: $HOST_ARCH -> Target: $TARGET_ARCH"
121
122
  echo "You may need to install specific cross-compilers manually."
122
123
  exit 1
123
124
  fi
124
-
125
+
125
126
  # --- 3. Clone iPXE Source ---
126
127
  echo ""
127
128
  echo "--- Downloading iPXE Source Code ---"
@@ -133,15 +134,15 @@ if [ "$DO_BUILD" = true ]; then
133
134
  git clone https://github.com/ipxe/ipxe.git $IPXE_SRC_DIR
134
135
  cd $IPXE_SRC_DIR
135
136
  fi
136
-
137
+
137
138
  # --- 4. Compile the Binary ---
138
139
  echo ""
139
140
  echo "--- Compiling $EFI_FILENAME for $TARGET_ARCH ---"
140
141
  cd src
141
-
142
+
142
143
  # Clean previous builds to ensure no arch mismatch
143
144
  make clean
144
-
145
+
145
146
  # Build with embedded script if provided
146
147
  if [ -n "$EMBED_SCRIPT" ]; then
147
148
  echo "Embedding script into iPXE binary..."
@@ -155,7 +156,7 @@ if [ "$DO_BUILD" = true ]; then
155
156
  echo "Running make for target: $BUILD_TARGET..."
156
157
  make CROSS_COMPILE=$CROSS_COMPILE_PREFIX $BUILD_TARGET
157
158
  fi
158
-
159
+
159
160
  if [ $? -ne 0 ]; then
160
161
  echo "Error: Compilation failed."
161
162
  if [ -n "$CROSS_COMPILE_PREFIX" ]; then
@@ -163,6 +164,8 @@ if [ "$DO_BUILD" = true ]; then
163
164
  fi
164
165
  exit 1
165
166
  fi
167
+
168
+
166
169
  fi
167
170
 
168
171
  # --- 5. Deploy Binary ---
@@ -176,10 +179,10 @@ if [ -f "$COMPILED_SRC_PATH" ]; then
176
179
  echo "Creating target directory: $TARGET_DIR"
177
180
  mkdir -p "$TARGET_DIR"
178
181
  fi
179
-
182
+
180
183
  echo "Copying $COMPILED_SRC_PATH to $TARGET_DIR/$EFI_FILENAME..."
181
184
  cp "$COMPILED_SRC_PATH" "$TARGET_DIR/$EFI_FILENAME"
182
-
185
+
183
186
  if [ $? -eq 0 ]; then
184
187
  echo "✓ Success!"
185
188
  echo "---------------------------------------------------"
@@ -1,39 +1,70 @@
1
1
  #!/bin/bash
2
- set -e
2
+ set -euo pipefail
3
3
 
4
4
  # ---------------------------------------------------------------------------
5
5
  # Underpost K3s Node Setup
6
+ #
7
+ # This script runs INSIDE an LXD VM. It assumes the host has already mirrored
8
+ # the project source into $ENGINE_ROOT (default /home/dd/engine) via the
9
+ # `underpost lxd [vm-id] --vm-init` flow in `src/cli/lxd.js`. There is no
10
+ # fallback to a globally installed `underpost`: every operational command
11
+ # resolves from the local project so the latest local changes are always what
12
+ # runs in the VM.
13
+ #
6
14
  # Usage:
7
- # --control Initialize as K3s control plane node (default)
8
- # --worker Initialize as K3s worker node
9
- # --control-ip=<ip> Control plane IP (required for --worker)
10
- # --token=<token> K3s node token (required for --worker)
15
+ # --engine-root=<path> Path to the mirrored engine source (default: /home/dd/engine)
16
+ # --control Initialize as K3s control plane node (default)
17
+ # --worker Initialize as K3s worker node
18
+ # --control-ip=<ip> Control plane IP (required for --worker)
19
+ # --token=<token> K3s node token (required for --worker)
11
20
  # ---------------------------------------------------------------------------
12
21
 
13
22
  ROLE="control"
14
23
  CONTROL_IP=""
15
24
  K3S_TOKEN=""
25
+ ENGINE_ROOT="/home/dd/engine"
16
26
 
17
27
  for arg in "$@"; do
18
- case $arg in
19
- --worker) ROLE="worker" ;;
20
- --control) ROLE="control" ;;
21
- --control-ip=*) CONTROL_IP="${arg#*=}" ;;
22
- --token=*) K3S_TOKEN="${arg#*=}" ;;
23
- esac
28
+ case $arg in
29
+ --worker) ROLE="worker" ;;
30
+ --control) ROLE="control" ;;
31
+ --control-ip=*) CONTROL_IP="${arg#*=}" ;;
32
+ --token=*) K3S_TOKEN="${arg#*=}" ;;
33
+ --engine-root=*) ENGINE_ROOT="${arg#*=}" ;;
34
+ esac
24
35
  done
36
+
37
+ # Fail fast if the bootstrap step did not run / left the directory empty.
38
+ # Split into two checks so `ls -A` only runs against a path that exists; this
39
+ # avoids needing an error-swallowing redirect.
40
+ if [ ! -d "$ENGINE_ROOT" ]; then
41
+ echo "ERROR: engine source directory $ENGINE_ROOT does not exist."
42
+ echo "The LXD [vm-id] --vm-init flow must mirror the project here before running this script."
43
+ exit 1
44
+ fi
45
+ if [ -z "$(ls -A "$ENGINE_ROOT")" ]; then
46
+ echo "ERROR: engine source directory $ENGINE_ROOT is empty."
47
+ echo "The LXD [vm-id] --vm-init flow must mirror the project here before running this script."
48
+ exit 1
49
+ fi
50
+
25
51
  # ---------------------------------------------------------------------------
26
- # NVM and Node.js
52
+ # NVM and Node.js — required for `node bin ...` entrypoints
27
53
  # ---------------------------------------------------------------------------
28
- echo "Installing NVM and Node.js v24.10.0..."
54
+ echo "Installing NVM and Node.js v24.15.0..."
29
55
 
30
56
  curl -o- https://cdn.jsdelivr.net/gh/nvm-sh/nvm@v0.40.1/install.sh | bash
31
57
 
32
58
  export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
59
+ # shellcheck disable=SC1090
33
60
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
34
61
 
35
62
  nvm install 24.15.0
36
63
  nvm use 24.15.0
64
+ nvm alias default 24.15.0
65
+ ln -sf "$(command -v node)" /usr/local/bin/node
66
+ ln -sf "$(command -v npm)" /usr/local/bin/npm
67
+ ln -sf "$(command -v npx)" /usr/local/bin/npx
37
68
 
38
69
  echo "
39
70
  ██╗░░░██╗███╗░░██╗██████╗░███████╗██████╗░██████╗░░█████╗░░██████╗████████╗
@@ -43,47 +74,51 @@ echo "
43
74
  ╚██████╔╝██║░╚███║██████╔╝███████╗██║░░██║██║░░░░░╚█████╔╝██████╔╝░░░██║░░░
44
75
  ░╚═════╝░╚═╝░░╚══╝╚═════╝░╚══════╝╚═╝░░╚═╝╚═╝░░░░░░╚════╝░╚═════╝░░░░╚═╝░░░
45
76
 
46
- Installing underpost VM node...
77
+ Bringing up underpost VM node from $ENGINE_ROOT (role=$ROLE)
47
78
  "
48
79
 
49
- npm install -g underpost
50
-
51
- cd /home/dd/engine
52
-
53
- echo "Applying host configuration..."
80
+ cd "$ENGINE_ROOT"
54
81
 
82
+ # Install JS deps and generate secrets using the local engine entrypoint only.
55
83
  npm install
56
-
57
84
  node bin run secret
58
85
 
59
- node bin cluster --dev --config
60
-
61
86
  if [ "$ROLE" = "control" ]; then
62
- echo "Initializing K3s control plane..."
63
- node bin cluster --dev --k3s
64
-
65
- echo ""
66
- echo "K3s control plane is ready."
67
- echo "Node token (share with workers to join this cluster):"
68
- sudo cat /var/lib/rancher/k3s/server/node-token
69
- echo ""
70
- echo "Control plane IP addresses:"
71
- ip -4 addr show scope global | grep inet | awk '{print $2}' | cut -d/ -f1
72
-
73
- elif [ "$ROLE" = "worker" ]; then
74
- if [ -z "$CONTROL_IP" ] || [ -z "$K3S_TOKEN" ]; then
75
- echo "ERROR: --control-ip and --token are required for worker role."
76
- echo "Usage: bash k3s-node-setup.sh --worker --control-ip=<ip> --token=<token>"
77
- exit 1
78
- fi
79
-
80
- echo "Joining K3s cluster at https://${CONTROL_IP}:6443..."
81
- curl -sfL https://get.k3s.io | \
87
+ echo "Installing underpost CLI..."
88
+ npm install -g underpost
89
+ underpost --version
90
+ echo "Initializing K3s control plane via local engine..."
91
+ node bin cluster --dev --k3s
92
+ ln -s /usr/local/bin/k3s /bin/k3s
93
+ ln -s /usr/local/bin/kubectl /bin/kubectl
94
+
95
+ echo ""
96
+ echo "K3s control plane is ready."
97
+ echo "Node token (share with workers to join this cluster):"
98
+ sudo cat /var/lib/rancher/k3s/server/node-token
99
+ echo ""
100
+ echo "Control plane IP addresses:"
101
+ ip -4 addr show scope global | grep inet | awk '{print $2}' | cut -d/ -f1
102
+
103
+ elif [ "$ROLE" = "worker" ]; then
104
+ if [ -z "$CONTROL_IP" ] || [ -z "$K3S_TOKEN" ]; then
105
+ echo "ERROR: --control-ip and --token are required for worker role."
106
+ echo "Usage: bash k3s-node-setup.sh --worker --control-ip=<ip> --token=<token>"
107
+ exit 1
108
+ fi
109
+
110
+ # Worker nodes still need the minimal K3s host prep even though they join via
111
+ # the upstream installer rather than `node bin cluster --k3s`.
112
+ echo "Applying minimal K3s host configuration via local engine..."
113
+ node bin cluster --dev --config --k3s
114
+
115
+ echo "Joining K3s cluster at https://${CONTROL_IP}:6443..."
116
+ curl -sfL https://get.k3s.io | \
82
117
  K3S_URL="https://${CONTROL_IP}:6443" \
83
118
  K3S_TOKEN="${K3S_TOKEN}" \
84
119
  sh -s - agent
85
-
86
- echo ""
87
- echo "K3s worker node joined the cluster at https://${CONTROL_IP}:6443 successfully."
88
- sudo systemctl status k3s-agent --no-pager
120
+
121
+ echo ""
122
+ echo "K3s worker joined https://${CONTROL_IP}:6443 successfully."
123
+ sudo systemctl status k3s-agent --no-pager
89
124
  fi
@@ -1,23 +1,208 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
1
3
 
2
- echo "Expanding /dev/sda2 and resizing filesystem..."
4
+ # ---------------------------------------------------------------------------
5
+ # LXD VM OS base setup. Runs inside the VM via `lxc exec`. Idempotent.
6
+ # ---------------------------------------------------------------------------
3
7
 
4
- if ! command -v parted &>/dev/null; then
5
- sudo dnf install -y parted
8
+ # lxdbr0 is a plain L2 bridge: LXD runs no DHCP/DNS on it (MAAS owns
9
+ # provisioning), so VMs configure their NIC statically and deterministically.
10
+ # The host pins the gateway (10.250.250.1) on the bridge and provides NAT; no
11
+ # resolver listens there, so DNS must use public/MAAS resolvers, not the gateway.
12
+ # LXD_NET_MODE=dhcp opts back into DHCP-first for VMs on a MAAS-served segment.
13
+ LXD_NET_MODE="${LXD_NET_MODE:-static}"
14
+ LXD_FALLBACK_IPV4_CIDR="${LXD_FALLBACK_IPV4_CIDR:-10.250.250.100/24}"
15
+ LXD_FALLBACK_GATEWAY="${LXD_FALLBACK_GATEWAY:-10.250.250.1}"
16
+ LXD_FALLBACK_DNS="${LXD_FALLBACK_DNS:-1.1.1.1 8.8.8.8}"
17
+ ROCKY_MIRROR_HOST="${ROCKY_MIRROR_HOST:-mirrors.rockylinux.org}"
18
+
19
+ current_ipv4() {
20
+ ip -4 addr show "$IFACE" | awk '/inet /{print $2; exit}'
21
+ }
22
+
23
+ wait_for_ipv4() {
24
+ local current_ip=""
25
+
26
+ for _ in $(seq 1 10); do
27
+ current_ip="$(current_ipv4)"
28
+ if [ -n "$current_ip" ]; then
29
+ echo "$current_ip"
30
+ return 0
31
+ fi
32
+ sleep 1
33
+ done
34
+
35
+ return 1
36
+ }
37
+
38
+ refresh_resolver_state() {
39
+ if command -v resolvectl >/dev/null 2>&1; then
40
+ sudo resolvectl flush-caches || true
41
+ fi
42
+
43
+ if [ -f /run/NetworkManager/resolv.conf ]; then
44
+ sudo ln -sf /run/NetworkManager/resolv.conf /etc/resolv.conf || true
45
+ fi
46
+ }
47
+
48
+ verify_name_resolution() {
49
+ getent ahostsv4 "$ROCKY_MIRROR_HOST" >/dev/null 2>&1
50
+ }
51
+
52
+ retry_dnf() {
53
+ local attempt=1
54
+ local max_attempts=3
55
+
56
+ until "$@"; do
57
+ if [ "$attempt" -ge "$max_attempts" ]; then
58
+ return 1
59
+ fi
60
+
61
+ echo "DNF command failed (attempt $attempt/$max_attempts): $*"
62
+ echo "Refreshing resolver state before retry..."
63
+ refresh_resolver_state
64
+ verify_name_resolution || true
65
+ attempt=$((attempt + 1))
66
+ done
67
+ }
68
+
69
+ # k3s derives the node name from the system hostname. The Rocky image keeps
70
+ # "localhost.localdomain" for every VM, so without this every node would try to
71
+ # register under the same name — the second one is rejected ("Node password
72
+ # rejected, duplicate hostname"). Pin the hostname to the LXD instance name so
73
+ # node names are unique and deterministic (control plane labeling relies on it).
74
+ if [ -n "${LXD_NODE_NAME:-}" ] && [ "$(hostname)" != "$LXD_NODE_NAME" ]; then
75
+ echo "--- Hostname ---"
76
+ echo "Setting hostname to ${LXD_NODE_NAME} (k3s node name)..."
77
+ sudo hostnamectl set-hostname "$LXD_NODE_NAME" 2>/dev/null || sudo hostname "$LXD_NODE_NAME"
78
+ fi
79
+
80
+ echo "--- Network Configuration ---"
81
+
82
+ # 1. Detect primary non-loopback interface
83
+ IFACE=$(ip -o link show up | awk -F': ' '$2!="lo"{print $2; exit}')
84
+ echo "Using network interface: ${IFACE:-none}"
85
+
86
+ if [ -z "$IFACE" ]; then
87
+ echo "CRITICAL ERROR: No network interface detected."
88
+ exit 1
89
+ fi
90
+
91
+ # 2. Force NetworkManager initialization if interface lacks an IP address
92
+ CURRENT_IP="$(current_ipv4 || true)"
93
+
94
+ if command -v nmcli >/dev/null 2>&1 && [ -z "$CURRENT_IP" ]; then
95
+ echo "Inspecting NetworkManager profiles..."
96
+
97
+ # Check if a profile is tracking this device
98
+ NM_CON=$(nmcli -t -f NAME,DEVICE connection show | awk -F: -v iface="$IFACE" '$2==iface{print $1; exit}')
99
+
100
+ if [ -z "$NM_CON" ]; then
101
+ echo "No connection profile matches $IFACE. Forcing generation of profile 'k3s-net'..."
102
+ nmcli connection add type ethernet con-name k3s-net ifname "$IFACE"
103
+ NM_CON="k3s-net"
104
+ fi
105
+
106
+ apply_static_ipv4() {
107
+ echo "Applying static LXD bridge address ${LXD_FALLBACK_IPV4_CIDR} (gw ${LXD_FALLBACK_GATEWAY}, dns ${LXD_FALLBACK_DNS})..."
108
+ nmcli connection modify "$NM_CON" \
109
+ connection.autoconnect yes \
110
+ connection.interface-name "$IFACE" \
111
+ ipv4.method manual \
112
+ ipv4.addresses "$LXD_FALLBACK_IPV4_CIDR" \
113
+ ipv4.gateway "$LXD_FALLBACK_GATEWAY" \
114
+ ipv4.dns "$LXD_FALLBACK_DNS" \
115
+ ipv4.ignore-auto-dns yes \
116
+ ipv6.method ignore
117
+ nmcli connection up "$NM_CON"
118
+ CURRENT_IP="$(wait_for_ipv4 || true)"
119
+ }
120
+
121
+ # Static-first: lxdbr0 has no DHCP, so a DHCP attempt only adds a ~45s
122
+ # activation timeout per boot. DHCP is opt-in for VMs on a MAAS-served
123
+ # segment (LXD_NET_MODE=dhcp), with a static fallback if no lease appears.
124
+ if [ "$LXD_NET_MODE" = "dhcp" ]; then
125
+ echo "Configuring DHCP-first NetworkManager profile '$NM_CON'..."
126
+ nmcli connection modify "$NM_CON" \
127
+ connection.autoconnect yes \
128
+ connection.interface-name "$IFACE" \
129
+ ipv4.method auto \
130
+ ipv4.dhcp-client-id mac \
131
+ ipv4.ignore-auto-dns no \
132
+ ipv6.method ignore
133
+ echo "Bringing network interface up with DHCP..."
134
+ nmcli connection up "$NM_CON" || echo "DHCP activation failed; static fallback will be applied."
135
+ CURRENT_IP="$(wait_for_ipv4 || true)"
136
+ if [ -z "$CURRENT_IP" ]; then
137
+ echo "No DHCP lease on $IFACE."
138
+ apply_static_ipv4
139
+ fi
140
+ else
141
+ echo "Configuring static NetworkManager profile '$NM_CON' for the plain LXD bridge..."
142
+ apply_static_ipv4
143
+ fi
144
+ fi
145
+
146
+ # 3. Give the network interface a short window to settle and lease an IP address
147
+ echo "Waiting for IP address allocation..."
148
+ CURRENT_IP="${CURRENT_IP:-$(wait_for_ipv4 || true)}"
149
+ if [ -n "$CURRENT_IP" ]; then
150
+ echo "Interface $IFACE successfully initialized with IP: $CURRENT_IP"
151
+ fi
152
+
153
+ # 4. Verify DNS and outbound connectivity before proceeding (Fail Closed)
154
+ echo "Verifying internet and DNS connectivity..."
155
+ refresh_resolver_state
156
+ if ! verify_name_resolution; then
157
+ echo "CRITICAL ERROR: DNS lookup failed for $ROCKY_MIRROR_HOST. Diagnostic snapshot:"
158
+ echo "=== /etc/resolv.conf ==="
159
+ cat /etc/resolv.conf || true
160
+ echo "=== Interface State ==="
161
+ ip -4 addr show "$IFACE" || true
162
+ echo "=== Routing Table ==="
163
+ ip route show || true
164
+ echo "=== NetworkManager Status ==="
165
+ nmcli device status || true
166
+ exit 1
167
+ fi
168
+
169
+ if ! timeout 15 curl -fsSL --max-time 10 "https://${ROCKY_MIRROR_HOST}" -o /dev/null; then
170
+ echo "CRITICAL ERROR: Network/DNS remains unreachable. Diagnostic snapshot:"
171
+ echo "=== /etc/resolv.conf ==="
172
+ cat /etc/resolv.conf || true
173
+ echo "=== Interface State ==="
174
+ ip -4 addr show "$IFACE" || true
175
+ echo "=== Routing Table ==="
176
+ ip route show || true
177
+ echo "=== NetworkManager Status ==="
178
+ nmcli device status || true
179
+ exit 1
6
180
  fi
7
181
 
182
+ echo "--- Disk Resizing ---"
183
+ if ! command -v parted >/dev/null 2>&1; then
184
+ sudo dnf install -y parted
185
+ fi
186
+
187
+ set +e
8
188
  sudo parted /dev/sda ---pretend-input-tty <<EOF
9
189
  unit s
10
190
  resizepart 2 100%
11
191
  Yes
12
192
  quit
13
193
  EOF
194
+ set -e
14
195
 
15
196
  sudo resize2fs /dev/sda2
16
- echo "Disk resized."
197
+ echo "Disk partition and filesystem resized successfully."
17
198
 
18
- echo "Installing essential packages..."
19
- sudo dnf install -y tar bzip2 git curl jq epel-release
20
- sudo dnf -y update
199
+ echo "--- Package Installation ---"
200
+ retry_dnf sudo dnf install -y epel-release
201
+ retry_dnf sudo dnf install -y tar bzip2 git curl jq
202
+ retry_dnf sudo dnf -y update
21
203
 
22
- echo "Loading br_netfilter module..."
204
+ echo "--- Kernel Modules for K3s ---"
23
205
  sudo modprobe br_netfilter
206
+ echo "br_netfilter" | sudo tee /etc/modules-load.d/k3s-br_netfilter.conf > /dev/null
207
+
208
+ echo "Setup complete. System is ready for k3s-node-setup.sh"