@ebowwa/hetzner 0.1.0

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 (46) hide show
  1. package/actions.js +802 -0
  2. package/actions.ts +1053 -0
  3. package/auth.js +35 -0
  4. package/auth.ts +37 -0
  5. package/bootstrap/FIREWALL.md +326 -0
  6. package/bootstrap/KERNEL-HARDENING.md +258 -0
  7. package/bootstrap/SECURITY-INTEGRATION.md +281 -0
  8. package/bootstrap/TESTING.md +301 -0
  9. package/bootstrap/cloud-init.js +279 -0
  10. package/bootstrap/cloud-init.ts +394 -0
  11. package/bootstrap/firewall.js +279 -0
  12. package/bootstrap/firewall.ts +342 -0
  13. package/bootstrap/genesis.js +406 -0
  14. package/bootstrap/genesis.ts +518 -0
  15. package/bootstrap/index.js +35 -0
  16. package/bootstrap/index.ts +71 -0
  17. package/bootstrap/kernel-hardening.js +266 -0
  18. package/bootstrap/kernel-hardening.test.ts +230 -0
  19. package/bootstrap/kernel-hardening.ts +272 -0
  20. package/bootstrap/security-audit.js +118 -0
  21. package/bootstrap/security-audit.ts +124 -0
  22. package/bootstrap/ssh-hardening.js +182 -0
  23. package/bootstrap/ssh-hardening.ts +192 -0
  24. package/client.js +137 -0
  25. package/client.ts +177 -0
  26. package/config.js +5 -0
  27. package/config.ts +5 -0
  28. package/errors.js +270 -0
  29. package/errors.ts +371 -0
  30. package/index.js +28 -0
  31. package/index.ts +55 -0
  32. package/package.json +56 -0
  33. package/pricing.js +284 -0
  34. package/pricing.ts +422 -0
  35. package/schemas.js +660 -0
  36. package/schemas.ts +765 -0
  37. package/server-status.ts +81 -0
  38. package/servers.js +424 -0
  39. package/servers.ts +568 -0
  40. package/ssh-keys.js +90 -0
  41. package/ssh-keys.ts +122 -0
  42. package/ssh-setup.ts +218 -0
  43. package/types.js +96 -0
  44. package/types.ts +389 -0
  45. package/volumes.js +172 -0
  46. package/volumes.ts +229 -0
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Cloud-Init Bootstrap Generator
3
+ *
4
+ * Generates cloud-init YAML scripts for first-boot server provisioning.
5
+ * Handles seed repository installation and initial setup.
6
+ *
7
+ * Security Integration:
8
+ * This module integrates all security modules in the correct order:
9
+ * 1. UFW Firewall (network-level defense)
10
+ * 2. Kernel Hardening (system-level hardening)
11
+ * 3. SSH Hardening (service-level hardening)
12
+ * 4. Security Audit (verification and reporting)
13
+ */
14
+ import { sshdHardeningPackages, sshdHardeningWriteFiles, sshdHardeningRunCmd, } from "./ssh-hardening";
15
+ import { ufwFirewallPackages, ufwFirewallWriteFiles, ufwFirewallRunCmd, DEFAULT_UFW_WORKER_OPTIONS, } from "./firewall";
16
+ import { kernelHardeningPackages, kernelHardeningWriteFiles, kernelHardeningRunCmd, } from "./kernel-hardening";
17
+ import { securityAuditPackages, securityAuditWriteFiles, securityAuditRunCmd, } from "./security-audit";
18
+ /**
19
+ * Generate a cloud-init YAML script for seed installation
20
+ *
21
+ * @param options - Bootstrap configuration options
22
+ * @returns Cloud-init YAML string
23
+ */
24
+ export function generateSeedBootstrap(options = {}) {
25
+ const { seedRepo = "https://github.com/ebowwa/seed", seedBranch = "dev", seedPath = "/root/seed", runSetup = true, setupEnv = {}, packages = [], additionalCommands = [], enableSecurity = true, } = options;
26
+ const lines = [];
27
+ // Cloud-config header
28
+ lines.push("#cloud-config");
29
+ lines.push("");
30
+ // System updates
31
+ lines.push("# Update system packages");
32
+ lines.push("package_update: true");
33
+ lines.push("package_upgrade: true");
34
+ lines.push("");
35
+ // Required packages
36
+ lines.push("# Install required packages");
37
+ lines.push("packages:");
38
+ lines.push(" - git");
39
+ lines.push(" - curl");
40
+ lines.push(" - jq");
41
+ lines.push(" - unzip");
42
+ lines.push(" - tmux");
43
+ // Security Module 1: UFW Firewall packages
44
+ if (enableSecurity) {
45
+ lines.push(" # Security: UFW Firewall");
46
+ lines.push(...ufwFirewallPackages());
47
+ }
48
+ // Security Module 2: Kernel hardening packages
49
+ if (enableSecurity) {
50
+ lines.push(" # Security: Kernel hardening");
51
+ lines.push(...kernelHardeningPackages());
52
+ }
53
+ // Security Module 3: SSH hardening packages (fail2ban)
54
+ if (enableSecurity) {
55
+ lines.push(" # Security: SSH hardening");
56
+ lines.push(...sshdHardeningPackages());
57
+ }
58
+ // Security Module 4: Security audit packages (lynis)
59
+ if (enableSecurity) {
60
+ lines.push(" # Security: Security audit");
61
+ lines.push(...securityAuditPackages());
62
+ }
63
+ // Add additional packages
64
+ for (const pkg of packages) {
65
+ lines.push(` - ${pkg}`);
66
+ }
67
+ lines.push("");
68
+ // Status tracking file
69
+ lines.push("# Write bootstrap status file");
70
+ lines.push("write_files:");
71
+ lines.push(" - path: /root/.bootstrap-status");
72
+ lines.push(" owner: root:root");
73
+ lines.push(" permissions: '0644'");
74
+ lines.push(" content: |");
75
+ lines.push(" status=started");
76
+ lines.push(" started_at=$(date -Iseconds)");
77
+ lines.push(" source=cloud-init");
78
+ if (enableSecurity) {
79
+ lines.push(" security=enabled");
80
+ }
81
+ lines.push("");
82
+ // Add bun to system-wide PATH via /etc/environment
83
+ // NOTE: /etc/environment uses simple KEY="value" format (no variables, no comments)
84
+ // We need to replace PATH rather than append, and include all standard paths
85
+ lines.push(" # Add bun to /etc/environment for all users/shells");
86
+ lines.push(" # Format: Simple KEY=\"value\" pairs, no variable expansion");
87
+ lines.push(" - path: /etc/environment");
88
+ lines.push(" owner: root:root");
89
+ lines.push(" permissions: '0644'");
90
+ lines.push(" content: |");
91
+ lines.push(" PATH=\"/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"");
92
+ lines.push("");
93
+ // Security Module 1: UFW Firewall configuration files
94
+ if (enableSecurity) {
95
+ lines.push(" # Security Module 1: UFW Firewall configuration");
96
+ lines.push(...ufwFirewallWriteFiles(DEFAULT_UFW_WORKER_OPTIONS));
97
+ }
98
+ // Security Module 2: Kernel hardening configuration files
99
+ if (enableSecurity) {
100
+ lines.push(" # Security Module 2: Kernel hardening");
101
+ lines.push(...kernelHardeningWriteFiles());
102
+ }
103
+ // Security Module 3: SSH hardening configuration files
104
+ if (enableSecurity) {
105
+ lines.push(" # Security Module 3: SSH hardening");
106
+ lines.push(...sshdHardeningWriteFiles());
107
+ }
108
+ // Security Module 4: Security audit script
109
+ if (enableSecurity) {
110
+ lines.push(" # Security Module 4: Security audit");
111
+ lines.push(...securityAuditWriteFiles());
112
+ }
113
+ // Node-agent systemd service
114
+ lines.push(" # Node-agent systemd service for Ralph Loop orchestration");
115
+ lines.push(" - path: /etc/systemd/system/node-agent.service");
116
+ lines.push(" owner: root:root");
117
+ lines.push(" permissions: '0644'");
118
+ lines.push(" content: |");
119
+ lines.push(" [Unit]");
120
+ lines.push(" Description=Node Agent for Ralph Loop Orchestration");
121
+ lines.push(" Documentation=https://github.com/ebowwa/seed");
122
+ lines.push(" After=network-online.target");
123
+ lines.push(" Wants=network-online.target");
124
+ lines.push("");
125
+ lines.push(" [Service]");
126
+ lines.push(" Type=simple");
127
+ lines.push(" User=root");
128
+ lines.push(` WorkingDirectory=${seedPath}/node-agent`);
129
+ lines.push(" ExecStart=/root/.bun/bin/bun run src/index.ts");
130
+ lines.push(` EnvironmentFile=-${seedPath}/node-agent/.env`);
131
+ lines.push(" Environment=PORT=8911");
132
+ lines.push(" Restart=always");
133
+ lines.push(" RestartSec=10");
134
+ lines.push(" StandardOutput=journal");
135
+ lines.push(" StandardError=journal");
136
+ lines.push(" SyslogIdentifier=node-agent");
137
+ lines.push("");
138
+ // Security hardening for node-agent service
139
+ if (enableSecurity) {
140
+ lines.push(" # Security hardening");
141
+ lines.push(" NoNewPrivileges=true");
142
+ lines.push(" PrivateTmp=true");
143
+ lines.push(" ProtectSystem=strict");
144
+ lines.push(" ProtectHome=true");
145
+ lines.push(" ReadOnlyPaths=/");
146
+ lines.push(" ReadWritePaths=/var/log " + seedPath + "/node-agent");
147
+ }
148
+ lines.push("");
149
+ lines.push(" [Install]");
150
+ lines.push(" WantedBy=multi-user.target");
151
+ lines.push("");
152
+ // Run commands
153
+ lines.push("# Bootstrap commands");
154
+ lines.push("runcmd:");
155
+ // Install Bun and create node symlink
156
+ lines.push(" # Install Bun");
157
+ lines.push(" - curl -fsSL https://bun.sh/install | bash");
158
+ lines.push(" - ln -sf /root/.bun/bin/bun /root/.bun/bin/node # Create 'node' symlink to bun");
159
+ lines.push("");
160
+ // Clone seed repository
161
+ lines.push(` # Clone seed repository`);
162
+ lines.push(` - git clone --depth 1 --branch ${seedBranch} ${seedRepo} ${seedPath}`);
163
+ lines.push("");
164
+ if (runSetup) {
165
+ // Build environment variables
166
+ const envVars = ["NONINTERACTIVE=1", ...Object.entries(setupEnv).map(([k, v]) => `${k}=${v}`)];
167
+ const envString = envVars.join(" ");
168
+ lines.push(` # Run seed setup non-interactively`);
169
+ lines.push(` - cd ${seedPath} && ${envString} bash ./setup.sh 2>&1 | tee /var/log/seed-setup.log`);
170
+ lines.push("");
171
+ // Create completion marker
172
+ lines.push(` # Mark setup complete`);
173
+ lines.push(` - touch ${seedPath}/.seed-setup-complete`);
174
+ lines.push("");
175
+ }
176
+ // Additional commands
177
+ if (additionalCommands.length > 0) {
178
+ lines.push(` # Additional custom commands`);
179
+ for (const cmd of additionalCommands) {
180
+ lines.push(` - ${cmd}`);
181
+ }
182
+ lines.push("");
183
+ }
184
+ // Security Module 1: UFW Firewall activation (runs first)
185
+ if (enableSecurity) {
186
+ lines.push(" # Security Module 1: Activate UFW Firewall");
187
+ lines.push(...ufwFirewallRunCmd(DEFAULT_UFW_WORKER_OPTIONS));
188
+ }
189
+ // Security Module 2: Kernel hardening activation
190
+ if (enableSecurity) {
191
+ lines.push(" # Security Module 2: Apply kernel hardening");
192
+ lines.push(...kernelHardeningRunCmd());
193
+ }
194
+ // Security Module 3: SSH hardening activation
195
+ if (enableSecurity) {
196
+ lines.push(" # Security Module 3: Activate SSH hardening");
197
+ lines.push(...sshdHardeningRunCmd());
198
+ }
199
+ // Security Module 4: Security audit (runs last)
200
+ if (enableSecurity) {
201
+ lines.push(" # Security Module 4: Run security audit");
202
+ lines.push(...securityAuditRunCmd());
203
+ }
204
+ // Mark bootstrap complete
205
+ lines.push(` # Mark bootstrap complete`);
206
+ lines.push(` - echo "status=complete" >> /root/.bootstrap-status`);
207
+ lines.push(` - echo "completed_at=$(date -Iseconds)" >> /root/.bootstrap-status`);
208
+ if (enableSecurity) {
209
+ lines.push(` - echo "security_hardening=applied" >> /root/.bootstrap-status`);
210
+ }
211
+ lines.push("");
212
+ lines.push(" # Start node-agent service");
213
+ lines.push(" - systemctl daemon-reload");
214
+ lines.push(" - systemctl enable node-agent");
215
+ lines.push(" - systemctl start node-agent");
216
+ return lines.join("\n");
217
+ }
218
+ /**
219
+ * Generate a minimal cloud-init script that uses #include to fetch from a URL
220
+ *
221
+ * This is useful for larger bootstrap scripts or when you want to update
222
+ * the bootstrap without code changes.
223
+ *
224
+ * @param url - URL to fetch the cloud-init config from
225
+ * @returns Cloud-init YAML string with #include directive
226
+ */
227
+ export function generateRemoteBootstrap(url) {
228
+ return `#include\n${url}`;
229
+ }
230
+ /**
231
+ * Bootstrap configuration presets for common scenarios
232
+ */
233
+ export const BootstrapPresets = {
234
+ /**
235
+ * Default seed installation with setup.sh and full security hardening
236
+ */
237
+ default: () => generateSeedBootstrap(),
238
+ /**
239
+ * Seed installation with full security hardening and verbose logging
240
+ */
241
+ secure: () => generateSeedBootstrap({
242
+ setupEnv: {
243
+ DEBUG: "1",
244
+ VERBOSE: "1",
245
+ },
246
+ }),
247
+ /**
248
+ * Seed installation without running setup.sh (useful for debugging)
249
+ */
250
+ cloneOnly: () => generateSeedBootstrap({ runSetup: false }),
251
+ /**
252
+ * Development bootstrap without security hardening (for testing)
253
+ */
254
+ development: () => generateSeedBootstrap({
255
+ enableSecurity: false,
256
+ packages: ["htop", "vim", "strace"],
257
+ }),
258
+ /**
259
+ * Verbose bootstrap with logging enabled
260
+ */
261
+ verbose: () => generateSeedBootstrap({
262
+ setupEnv: {
263
+ DEBUG: "1",
264
+ VERBOSE: "1",
265
+ },
266
+ }),
267
+ };
268
+ // Re-export Genesis bootstrap functions
269
+ export { generateGenesisBootstrap, generateRemoteGenesisBootstrap, GenesisBootstrapPresets, } from "./genesis";
270
+ // Re-export SSH hardening components so callers can compose custom
271
+ // cloud-init scripts with hardening baked in (e.g. for non-standard node types)
272
+ export { sshdHardeningPackages, sshdHardeningWriteFiles, sshdHardeningRunCmd, } from "./ssh-hardening";
273
+ // Re-export UFW firewall components
274
+ export { ufwFirewallPackages, ufwFirewallWriteFiles, ufwFirewallRunCmd, DEFAULT_UFW_WORKER_OPTIONS, DEFAULT_UFW_GENESIS_OPTIONS, generateUFWFirewallForGenesis, generateUFWFirewallForWorker, } from "./firewall";
275
+ // Re-export kernel hardening components
276
+ export { kernelHardeningPackages, kernelHardeningWriteFiles, kernelHardeningRunCmd, } from "./kernel-hardening";
277
+ // Re-export security audit components
278
+ export { securityAuditPackages, securityAuditWriteFiles, securityAuditRunCmd, } from "./security-audit";
279
+ //# sourceMappingURL=cloud-init.js.map
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Cloud-Init Bootstrap Generator
3
+ *
4
+ * Generates cloud-init YAML scripts for first-boot server provisioning.
5
+ * Handles seed repository installation and initial setup.
6
+ *
7
+ * Security Integration:
8
+ * This module integrates all security modules in the correct order:
9
+ * 1. UFW Firewall (network-level defense)
10
+ * 2. Kernel Hardening (system-level hardening)
11
+ * 3. SSH Hardening (service-level hardening)
12
+ * 4. Security Audit (verification and reporting)
13
+ */
14
+
15
+ import {
16
+ sshdHardeningPackages,
17
+ sshdHardeningWriteFiles,
18
+ sshdHardeningRunCmd,
19
+ } from "./ssh-hardening";
20
+ import {
21
+ ufwFirewallPackages,
22
+ ufwFirewallWriteFiles,
23
+ ufwFirewallRunCmd,
24
+ DEFAULT_UFW_WORKER_OPTIONS,
25
+ } from "./firewall";
26
+ import {
27
+ kernelHardeningPackages,
28
+ kernelHardeningWriteFiles,
29
+ kernelHardeningRunCmd,
30
+ } from "./kernel-hardening";
31
+ import {
32
+ securityAuditPackages,
33
+ securityAuditWriteFiles,
34
+ securityAuditRunCmd,
35
+ } from "./security-audit";
36
+
37
+ export interface BootstrapOptions {
38
+ /** Seed repository URL (default: https://github.com/ebowwa/seed) */
39
+ seedRepo?: string;
40
+ /** Seed repository branch (default: dev) */
41
+ seedBranch?: string;
42
+ /** Installation path (default: /root/seed) */
43
+ seedPath?: string;
44
+ /** Whether to run setup.sh non-interactively (default: true) */
45
+ runSetup?: boolean;
46
+ /** Additional environment variables for setup.sh */
47
+ setupEnv?: Record<string, string>;
48
+ /** Additional packages to install */
49
+ packages?: string[];
50
+ /** Additional commands to run after seed installation */
51
+ additionalCommands?: string[];
52
+ /** Enable security hardening (default: true) */
53
+ enableSecurity?: boolean;
54
+ }
55
+
56
+ /**
57
+ * Generate a cloud-init YAML script for seed installation
58
+ *
59
+ * @param options - Bootstrap configuration options
60
+ * @returns Cloud-init YAML string
61
+ */
62
+ export function generateSeedBootstrap(options: BootstrapOptions = {}): string {
63
+ const {
64
+ seedRepo = "https://github.com/ebowwa/seed",
65
+ seedBranch = "dev",
66
+ seedPath = "/root/seed",
67
+ runSetup = true,
68
+ setupEnv = {},
69
+ packages = [],
70
+ additionalCommands = [],
71
+ enableSecurity = true,
72
+ } = options;
73
+
74
+ const lines: string[] = [];
75
+
76
+ // Cloud-config header
77
+ lines.push("#cloud-config");
78
+ lines.push("");
79
+
80
+ // System updates
81
+ lines.push("# Update system packages");
82
+ lines.push("package_update: true");
83
+ lines.push("package_upgrade: true");
84
+ lines.push("");
85
+
86
+ // Required packages
87
+ lines.push("# Install required packages");
88
+ lines.push("packages:");
89
+ lines.push(" - git");
90
+ lines.push(" - curl");
91
+ lines.push(" - jq");
92
+ lines.push(" - unzip");
93
+ lines.push(" - tmux");
94
+
95
+ // Security Module 1: UFW Firewall packages
96
+ if (enableSecurity) {
97
+ lines.push(" # Security: UFW Firewall");
98
+ lines.push(...ufwFirewallPackages());
99
+ }
100
+
101
+ // Security Module 2: Kernel hardening packages
102
+ if (enableSecurity) {
103
+ lines.push(" # Security: Kernel hardening");
104
+ lines.push(...kernelHardeningPackages());
105
+ }
106
+
107
+ // Security Module 3: SSH hardening packages (fail2ban)
108
+ if (enableSecurity) {
109
+ lines.push(" # Security: SSH hardening");
110
+ lines.push(...sshdHardeningPackages());
111
+ }
112
+
113
+ // Security Module 4: Security audit packages (lynis)
114
+ if (enableSecurity) {
115
+ lines.push(" # Security: Security audit");
116
+ lines.push(...securityAuditPackages());
117
+ }
118
+
119
+ // Add additional packages
120
+ for (const pkg of packages) {
121
+ lines.push(` - ${pkg}`);
122
+ }
123
+ lines.push("");
124
+
125
+ // Status tracking file
126
+ lines.push("# Write bootstrap status file");
127
+ lines.push("write_files:");
128
+ lines.push(" - path: /root/.bootstrap-status");
129
+ lines.push(" owner: root:root");
130
+ lines.push(" permissions: '0644'");
131
+ lines.push(" content: |");
132
+ lines.push(" status=started");
133
+ lines.push(" started_at=$(date -Iseconds)");
134
+ lines.push(" source=cloud-init");
135
+ if (enableSecurity) {
136
+ lines.push(" security=enabled");
137
+ }
138
+ lines.push("");
139
+
140
+ // Add bun to system-wide PATH via /etc/environment
141
+ // NOTE: /etc/environment uses simple KEY="value" format (no variables, no comments)
142
+ // We need to replace PATH rather than append, and include all standard paths
143
+ lines.push(" # Add bun to /etc/environment for all users/shells");
144
+ lines.push(" # Format: Simple KEY=\"value\" pairs, no variable expansion");
145
+ lines.push(" - path: /etc/environment");
146
+ lines.push(" owner: root:root");
147
+ lines.push(" permissions: '0644'");
148
+ lines.push(" content: |");
149
+ lines.push(" PATH=\"/root/.bun/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"");
150
+ lines.push("");
151
+
152
+ // Security Module 1: UFW Firewall configuration files
153
+ if (enableSecurity) {
154
+ lines.push(" # Security Module 1: UFW Firewall configuration");
155
+ lines.push(...ufwFirewallWriteFiles(DEFAULT_UFW_WORKER_OPTIONS));
156
+ }
157
+
158
+ // Security Module 2: Kernel hardening configuration files
159
+ if (enableSecurity) {
160
+ lines.push(" # Security Module 2: Kernel hardening");
161
+ lines.push(...kernelHardeningWriteFiles());
162
+ }
163
+
164
+ // Security Module 3: SSH hardening configuration files
165
+ if (enableSecurity) {
166
+ lines.push(" # Security Module 3: SSH hardening");
167
+ lines.push(...sshdHardeningWriteFiles());
168
+ }
169
+
170
+ // Security Module 4: Security audit script
171
+ if (enableSecurity) {
172
+ lines.push(" # Security Module 4: Security audit");
173
+ lines.push(...securityAuditWriteFiles());
174
+ }
175
+
176
+ // Node-agent systemd service
177
+ lines.push(" # Node-agent systemd service for Ralph Loop orchestration");
178
+ lines.push(" - path: /etc/systemd/system/node-agent.service");
179
+ lines.push(" owner: root:root");
180
+ lines.push(" permissions: '0644'");
181
+ lines.push(" content: |");
182
+ lines.push(" [Unit]");
183
+ lines.push(" Description=Node Agent for Ralph Loop Orchestration");
184
+ lines.push(" Documentation=https://github.com/ebowwa/seed");
185
+ lines.push(" After=network-online.target");
186
+ lines.push(" Wants=network-online.target");
187
+ lines.push("");
188
+ lines.push(" [Service]");
189
+ lines.push(" Type=simple");
190
+ lines.push(" User=root");
191
+ lines.push(` WorkingDirectory=${seedPath}/node-agent`);
192
+ lines.push(" ExecStart=/root/.bun/bin/bun run src/index.ts");
193
+ lines.push(` EnvironmentFile=-${seedPath}/node-agent/.env`);
194
+ lines.push(" Environment=PORT=8911");
195
+ lines.push(" Restart=always");
196
+ lines.push(" RestartSec=10");
197
+ lines.push(" StandardOutput=journal");
198
+ lines.push(" StandardError=journal");
199
+ lines.push(" SyslogIdentifier=node-agent");
200
+ lines.push("");
201
+ // Security hardening for node-agent service
202
+ if (enableSecurity) {
203
+ lines.push(" # Security hardening");
204
+ lines.push(" NoNewPrivileges=true");
205
+ lines.push(" PrivateTmp=true");
206
+ lines.push(" ProtectSystem=strict");
207
+ lines.push(" ProtectHome=true");
208
+ lines.push(" ReadOnlyPaths=/");
209
+ lines.push(" ReadWritePaths=/var/log " + seedPath + "/node-agent");
210
+ }
211
+ lines.push("");
212
+ lines.push(" [Install]");
213
+ lines.push(" WantedBy=multi-user.target");
214
+ lines.push("");
215
+
216
+ // Run commands
217
+ lines.push("# Bootstrap commands");
218
+ lines.push("runcmd:");
219
+
220
+ // Install Bun and create node symlink
221
+ lines.push(" # Install Bun");
222
+ lines.push(" - curl -fsSL https://bun.sh/install | bash");
223
+ lines.push(" - ln -sf /root/.bun/bin/bun /root/.bun/bin/node # Create 'node' symlink to bun");
224
+ lines.push("");
225
+
226
+ // Clone seed repository
227
+ lines.push(` # Clone seed repository`);
228
+ lines.push(` - git clone --depth 1 --branch ${seedBranch} ${seedRepo} ${seedPath}`);
229
+ lines.push("");
230
+
231
+ if (runSetup) {
232
+ // Build environment variables
233
+ const envVars = ["NONINTERACTIVE=1", ...Object.entries(setupEnv).map(([k, v]) => `${k}=${v}`)];
234
+ const envString = envVars.join(" ");
235
+
236
+ lines.push(` # Run seed setup non-interactively`);
237
+ lines.push(` - cd ${seedPath} && ${envString} bash ./setup.sh 2>&1 | tee /var/log/seed-setup.log`);
238
+ lines.push("");
239
+
240
+ // Create completion marker
241
+ lines.push(` # Mark setup complete`);
242
+ lines.push(` - touch ${seedPath}/.seed-setup-complete`);
243
+ lines.push("");
244
+ }
245
+
246
+ // Additional commands
247
+ if (additionalCommands.length > 0) {
248
+ lines.push(` # Additional custom commands`);
249
+ for (const cmd of additionalCommands) {
250
+ lines.push(` - ${cmd}`);
251
+ }
252
+ lines.push("");
253
+ }
254
+
255
+ // Security Module 1: UFW Firewall activation (runs first)
256
+ if (enableSecurity) {
257
+ lines.push(" # Security Module 1: Activate UFW Firewall");
258
+ lines.push(...ufwFirewallRunCmd(DEFAULT_UFW_WORKER_OPTIONS));
259
+ }
260
+
261
+ // Security Module 2: Kernel hardening activation
262
+ if (enableSecurity) {
263
+ lines.push(" # Security Module 2: Apply kernel hardening");
264
+ lines.push(...kernelHardeningRunCmd());
265
+ }
266
+
267
+ // Security Module 3: SSH hardening activation
268
+ if (enableSecurity) {
269
+ lines.push(" # Security Module 3: Activate SSH hardening");
270
+ lines.push(...sshdHardeningRunCmd());
271
+ }
272
+
273
+ // Security Module 4: Security audit (runs last)
274
+ if (enableSecurity) {
275
+ lines.push(" # Security Module 4: Run security audit");
276
+ lines.push(...securityAuditRunCmd());
277
+ }
278
+
279
+ // Mark bootstrap complete
280
+ lines.push(` # Mark bootstrap complete`);
281
+ lines.push(` - echo "status=complete" >> /root/.bootstrap-status`);
282
+ lines.push(` - echo "completed_at=$(date -Iseconds)" >> /root/.bootstrap-status`);
283
+ if (enableSecurity) {
284
+ lines.push(` - echo "security_hardening=applied" >> /root/.bootstrap-status`);
285
+ }
286
+ lines.push("");
287
+ lines.push(" # Start node-agent service");
288
+ lines.push(" - systemctl daemon-reload");
289
+ lines.push(" - systemctl enable node-agent");
290
+ lines.push(" - systemctl start node-agent");
291
+
292
+ return lines.join("\n");
293
+ }
294
+
295
+ /**
296
+ * Generate a minimal cloud-init script that uses #include to fetch from a URL
297
+ *
298
+ * This is useful for larger bootstrap scripts or when you want to update
299
+ * the bootstrap without code changes.
300
+ *
301
+ * @param url - URL to fetch the cloud-init config from
302
+ * @returns Cloud-init YAML string with #include directive
303
+ */
304
+ export function generateRemoteBootstrap(url: string): string {
305
+ return `#include\n${url}`;
306
+ }
307
+
308
+ /**
309
+ * Bootstrap configuration presets for common scenarios
310
+ */
311
+ export const BootstrapPresets = {
312
+ /**
313
+ * Default seed installation with setup.sh and full security hardening
314
+ */
315
+ default: () => generateSeedBootstrap(),
316
+
317
+ /**
318
+ * Seed installation with full security hardening and verbose logging
319
+ */
320
+ secure: () =>
321
+ generateSeedBootstrap({
322
+ setupEnv: {
323
+ DEBUG: "1",
324
+ VERBOSE: "1",
325
+ },
326
+ }),
327
+
328
+ /**
329
+ * Seed installation without running setup.sh (useful for debugging)
330
+ */
331
+ cloneOnly: () => generateSeedBootstrap({ runSetup: false }),
332
+
333
+ /**
334
+ * Development bootstrap without security hardening (for testing)
335
+ */
336
+ development: () =>
337
+ generateSeedBootstrap({
338
+ enableSecurity: false,
339
+ packages: ["htop", "vim", "strace"],
340
+ }),
341
+
342
+ /**
343
+ * Verbose bootstrap with logging enabled
344
+ */
345
+ verbose: () =>
346
+ generateSeedBootstrap({
347
+ setupEnv: {
348
+ DEBUG: "1",
349
+ VERBOSE: "1",
350
+ },
351
+ }),
352
+ } as const;
353
+
354
+ // Re-export Genesis bootstrap functions
355
+ export {
356
+ generateGenesisBootstrap,
357
+ generateRemoteGenesisBootstrap,
358
+ GenesisBootstrapPresets,
359
+ type GenesisBootstrapOptions,
360
+ } from "./genesis";
361
+
362
+ // Re-export SSH hardening components so callers can compose custom
363
+ // cloud-init scripts with hardening baked in (e.g. for non-standard node types)
364
+ export {
365
+ sshdHardeningPackages,
366
+ sshdHardeningWriteFiles,
367
+ sshdHardeningRunCmd,
368
+ } from "./ssh-hardening";
369
+
370
+ // Re-export UFW firewall components
371
+ export {
372
+ ufwFirewallPackages,
373
+ ufwFirewallWriteFiles,
374
+ ufwFirewallRunCmd,
375
+ DEFAULT_UFW_WORKER_OPTIONS,
376
+ DEFAULT_UFW_GENESIS_OPTIONS,
377
+ generateUFWFirewallForGenesis,
378
+ generateUFWFirewallForWorker,
379
+ type UFWFirewallOptions,
380
+ } from "./firewall";
381
+
382
+ // Re-export kernel hardening components
383
+ export {
384
+ kernelHardeningPackages,
385
+ kernelHardeningWriteFiles,
386
+ kernelHardeningRunCmd,
387
+ } from "./kernel-hardening";
388
+
389
+ // Re-export security audit components
390
+ export {
391
+ securityAuditPackages,
392
+ securityAuditWriteFiles,
393
+ securityAuditRunCmd,
394
+ } from "./security-audit";