@lamalibre/create-lamaste 4.0.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 (270) hide show
  1. package/LICENSE.md +118 -0
  2. package/README.md +114 -0
  3. package/bin/create-lamaste.js +15 -0
  4. package/package.json +54 -0
  5. package/scripts/bundle-vendor.js +110 -0
  6. package/scripts/lamaste-cert-info +62 -0
  7. package/scripts/lamaste-certbot +96 -0
  8. package/scripts/lamaste-priv +693 -0
  9. package/src/index.js +646 -0
  10. package/src/lib/accounts.js +86 -0
  11. package/src/lib/binaries.js +137 -0
  12. package/src/lib/cert-help-page.js +160 -0
  13. package/src/lib/env.js +132 -0
  14. package/src/lib/http-redirect.js +38 -0
  15. package/src/lib/ownership.js +231 -0
  16. package/src/lib/relay-services.js +103 -0
  17. package/src/lib/secrets.js +19 -0
  18. package/src/lib/service-config.js +311 -0
  19. package/src/lib/summary.js +131 -0
  20. package/src/lib/wrappers.js +63 -0
  21. package/src/tasks/gatekeeper.js +233 -0
  22. package/src/tasks/harden.js +293 -0
  23. package/src/tasks/mtls.js +193 -0
  24. package/src/tasks/nginx.js +265 -0
  25. package/src/tasks/node.js +110 -0
  26. package/src/tasks/panel.js +408 -0
  27. package/src/tasks/redeploy.js +438 -0
  28. package/vendor/docs/00-introduction/how-it-works.md +447 -0
  29. package/vendor/docs/00-introduction/quickstart.md +472 -0
  30. package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
  31. package/vendor/docs/01-concepts/authentication.md +464 -0
  32. package/vendor/docs/01-concepts/authorization.md +242 -0
  33. package/vendor/docs/01-concepts/certificates.md +452 -0
  34. package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
  35. package/vendor/docs/01-concepts/mtls.md +490 -0
  36. package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
  37. package/vendor/docs/01-concepts/security-model.md +600 -0
  38. package/vendor/docs/01-concepts/tickets.md +414 -0
  39. package/vendor/docs/01-concepts/tunneling.md +367 -0
  40. package/vendor/docs/02-guides/agent-setup.md +194 -0
  41. package/vendor/docs/02-guides/certificate-management.md +341 -0
  42. package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
  43. package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
  44. package/vendor/docs/02-guides/disaster-recovery.md +464 -0
  45. package/vendor/docs/02-guides/first-tunnel.md +230 -0
  46. package/vendor/docs/02-guides/installation.md +393 -0
  47. package/vendor/docs/02-guides/managing-users.md +255 -0
  48. package/vendor/docs/02-guides/onboarding.md +244 -0
  49. package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
  50. package/vendor/docs/02-guides/static-sites.md +364 -0
  51. package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
  52. package/vendor/docs/03-architecture/installer-flow.md +151 -0
  53. package/vendor/docs/03-architecture/installer.md +445 -0
  54. package/vendor/docs/03-architecture/management-flow.md +288 -0
  55. package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
  56. package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
  57. package/vendor/docs/03-architecture/overview.md +541 -0
  58. package/vendor/docs/03-architecture/panel-client.md +416 -0
  59. package/vendor/docs/03-architecture/panel-server.md +695 -0
  60. package/vendor/docs/03-architecture/state-management.md +529 -0
  61. package/vendor/docs/03-architecture/system-overview.md +167 -0
  62. package/vendor/docs/04-api-reference/certificates.md +1141 -0
  63. package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
  64. package/vendor/docs/04-api-reference/onboarding.md +473 -0
  65. package/vendor/docs/04-api-reference/overview.md +404 -0
  66. package/vendor/docs/04-api-reference/services.md +267 -0
  67. package/vendor/docs/04-api-reference/settings.md +343 -0
  68. package/vendor/docs/04-api-reference/sites.md +716 -0
  69. package/vendor/docs/04-api-reference/system.md +268 -0
  70. package/vendor/docs/04-api-reference/tickets.md +618 -0
  71. package/vendor/docs/04-api-reference/tunnels.md +417 -0
  72. package/vendor/docs/04-api-reference/users.md +333 -0
  73. package/vendor/docs/05-operations/backup-and-restore.md +342 -0
  74. package/vendor/docs/05-operations/monitoring.md +285 -0
  75. package/vendor/docs/05-operations/uninstalling.md +344 -0
  76. package/vendor/docs/05-operations/upgrades.md +270 -0
  77. package/vendor/docs/06-reference/config-files.md +1080 -0
  78. package/vendor/docs/06-reference/glossary.md +528 -0
  79. package/vendor/docs/06-reference/installer-flags.md +260 -0
  80. package/vendor/docs/06-reference/ports-and-services.md +244 -0
  81. package/vendor/docs/06-reference/troubleshooting.md +665 -0
  82. package/vendor/docs/_index.json +233 -0
  83. package/vendor/docs/gatekeeper-design.md +953 -0
  84. package/vendor/docs/package.json +15 -0
  85. package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
  86. package/vendor/gatekeeper/dist/boot.d.ts +1 -0
  87. package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
  88. package/vendor/gatekeeper/dist/boot.js +17 -0
  89. package/vendor/gatekeeper/dist/boot.js.map +1 -0
  90. package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
  91. package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
  92. package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
  93. package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
  94. package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
  95. package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
  96. package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
  97. package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
  98. package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
  99. package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
  100. package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
  101. package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
  102. package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
  103. package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
  104. package/vendor/gatekeeper/dist/cli/index.js +45 -0
  105. package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
  106. package/vendor/gatekeeper/dist/index.d.ts +9 -0
  107. package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
  108. package/vendor/gatekeeper/dist/index.js +13 -0
  109. package/vendor/gatekeeper/dist/index.js.map +1 -0
  110. package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
  111. package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
  112. package/vendor/gatekeeper/dist/lib/authz.js +42 -0
  113. package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
  114. package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
  115. package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
  116. package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
  117. package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
  118. package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
  119. package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
  120. package/vendor/gatekeeper/dist/lib/constants.js +79 -0
  121. package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
  122. package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
  123. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
  124. package/vendor/gatekeeper/dist/lib/grants.js +314 -0
  125. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
  126. package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
  127. package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
  128. package/vendor/gatekeeper/dist/lib/groups.js +247 -0
  129. package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
  130. package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
  131. package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
  132. package/vendor/gatekeeper/dist/lib/migration.js +79 -0
  133. package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
  134. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
  135. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
  136. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
  137. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
  138. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
  139. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
  140. package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
  141. package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
  142. package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
  143. package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
  144. package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
  145. package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
  146. package/vendor/gatekeeper/dist/lib/templates.js +151 -0
  147. package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
  148. package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
  149. package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
  150. package/vendor/gatekeeper/dist/lib/types.js +2 -0
  151. package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
  152. package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
  153. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
  154. package/vendor/gatekeeper/dist/server/index.js +251 -0
  155. package/vendor/gatekeeper/dist/server/index.js.map +1 -0
  156. package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
  157. package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
  158. package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
  159. package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
  160. package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
  161. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
  162. package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
  163. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
  164. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
  165. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
  166. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
  167. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
  168. package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
  169. package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
  170. package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
  171. package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
  172. package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
  173. package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
  174. package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
  175. package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
  176. package/vendor/gatekeeper/package.json +49 -0
  177. package/vendor/server/bin/lamaste-server.js +204 -0
  178. package/vendor/server/package.json +31 -0
  179. package/vendor/server/src/commands/certs.js +224 -0
  180. package/vendor/server/src/commands/chisel.js +132 -0
  181. package/vendor/server/src/commands/logs.js +49 -0
  182. package/vendor/server/src/commands/plugins.js +321 -0
  183. package/vendor/server/src/commands/reset-admin.js +478 -0
  184. package/vendor/server/src/commands/restart.js +94 -0
  185. package/vendor/server/src/commands/sites.js +257 -0
  186. package/vendor/server/src/commands/status.js +158 -0
  187. package/vendor/server/src/commands/tunnels.js +365 -0
  188. package/vendor/server/src/commands/uninstall.js +326 -0
  189. package/vendor/server/src/config.js +38 -0
  190. package/vendor/server/src/exec.js +26 -0
  191. package/vendor/server/src/logger.js +39 -0
  192. package/vendor/server/src/ndjson.js +56 -0
  193. package/vendor/server/src/panel-api.js +85 -0
  194. package/vendor/server/src/state.js +45 -0
  195. package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
  196. package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
  197. package/vendor/server-ui/dist/index.html +13 -0
  198. package/vendor/serverd/package.json +44 -0
  199. package/vendor/serverd/src/boot.js +13 -0
  200. package/vendor/serverd/src/index.js +191 -0
  201. package/vendor/serverd/src/lib/app-error.js +14 -0
  202. package/vendor/serverd/src/lib/authelia.js +85 -0
  203. package/vendor/serverd/src/lib/certbot.js +74 -0
  204. package/vendor/serverd/src/lib/chisel-args.js +6 -0
  205. package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
  206. package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
  207. package/vendor/serverd/src/lib/chisel-users.js +109 -0
  208. package/vendor/serverd/src/lib/chisel.js +81 -0
  209. package/vendor/serverd/src/lib/config.js +190 -0
  210. package/vendor/serverd/src/lib/constants.js +8 -0
  211. package/vendor/serverd/src/lib/csr-signing.js +331 -0
  212. package/vendor/serverd/src/lib/enrollment.js +302 -0
  213. package/vendor/serverd/src/lib/files.js +19 -0
  214. package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
  215. package/vendor/serverd/src/lib/invite-page.js +281 -0
  216. package/vendor/serverd/src/lib/ip-utils.js +79 -0
  217. package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
  218. package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
  219. package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
  220. package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
  221. package/vendor/serverd/src/lib/mtls.js +795 -0
  222. package/vendor/serverd/src/lib/nginx.js +1090 -0
  223. package/vendor/serverd/src/lib/pki-paths.js +41 -0
  224. package/vendor/serverd/src/lib/pki-sign.js +103 -0
  225. package/vendor/serverd/src/lib/plugins.js +538 -0
  226. package/vendor/serverd/src/lib/push-install.js +390 -0
  227. package/vendor/serverd/src/lib/revocation.js +112 -0
  228. package/vendor/serverd/src/lib/services.js +131 -0
  229. package/vendor/serverd/src/lib/session.js +151 -0
  230. package/vendor/serverd/src/lib/state-db.js +130 -0
  231. package/vendor/serverd/src/lib/state.js +140 -0
  232. package/vendor/serverd/src/lib/storage.js +63 -0
  233. package/vendor/serverd/src/lib/system-stats.js +58 -0
  234. package/vendor/serverd/src/lib/tickets.js +1556 -0
  235. package/vendor/serverd/src/lib/totp.js +76 -0
  236. package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
  237. package/vendor/serverd/src/lib/user-access-session.js +142 -0
  238. package/vendor/serverd/src/lib/user-access.js +52 -0
  239. package/vendor/serverd/src/middleware/errors.js +63 -0
  240. package/vendor/serverd/src/middleware/mtls.js +120 -0
  241. package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
  242. package/vendor/serverd/src/middleware/role-guard.js +58 -0
  243. package/vendor/serverd/src/middleware/twofa-session.js +65 -0
  244. package/vendor/serverd/src/middleware/user-access-session.js +52 -0
  245. package/vendor/serverd/src/routes/enrollment.js +120 -0
  246. package/vendor/serverd/src/routes/health.js +22 -0
  247. package/vendor/serverd/src/routes/invite.js +163 -0
  248. package/vendor/serverd/src/routes/management/agents.js +182 -0
  249. package/vendor/serverd/src/routes/management/certs.js +911 -0
  250. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
  251. package/vendor/serverd/src/routes/management/identity.js +107 -0
  252. package/vendor/serverd/src/routes/management/invitations.js +166 -0
  253. package/vendor/serverd/src/routes/management/logs.js +218 -0
  254. package/vendor/serverd/src/routes/management/plugins.js +560 -0
  255. package/vendor/serverd/src/routes/management/services.js +63 -0
  256. package/vendor/serverd/src/routes/management/settings.js +325 -0
  257. package/vendor/serverd/src/routes/management/sites.js +522 -0
  258. package/vendor/serverd/src/routes/management/storage.js +192 -0
  259. package/vendor/serverd/src/routes/management/system.js +201 -0
  260. package/vendor/serverd/src/routes/management/tickets.js +457 -0
  261. package/vendor/serverd/src/routes/management/tunnels.js +709 -0
  262. package/vendor/serverd/src/routes/management/users.js +377 -0
  263. package/vendor/serverd/src/routes/management.js +36 -0
  264. package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
  265. package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
  266. package/vendor/serverd/src/routes/onboarding/index.js +18 -0
  267. package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
  268. package/vendor/serverd/src/routes/onboarding/status.js +12 -0
  269. package/vendor/serverd/src/routes/plugin-router.js +79 -0
  270. package/vendor/serverd/src/routes/user-access.js +389 -0
@@ -0,0 +1,445 @@
1
+ # Installer Architecture
2
+
3
+ > The `create-lamaste` package is a zero-prompt CLI that provisions a fresh Ubuntu 24.04 droplet with everything needed to run the Lamaste admin panel — in a single command.
4
+
5
+ ## In Plain English
6
+
7
+ When you run `npx @lamalibre/create-lamaste@4.0.0` on a fresh VPS, the installer takes over and does everything automatically: hardens the OS, installs dependencies, generates security certificates, configures nginx, deploys the panel, and starts the service. It prints a summary with instructions for downloading your client certificate and accessing the panel. No prompts, no questions, no configuration files to edit.
8
+
9
+ The installer is deliberately limited in scope. It sets up just enough to serve the panel UI over a secure connection. Everything else — domain configuration, tunnel setup, user management — happens through the browser-based onboarding wizard after the installer finishes.
10
+
11
+ ## Overview
12
+
13
+ ```
14
+ npx @lamalibre/create-lamaste@4.0.0
15
+ │
16
+ ├── bin/create-lamaste.js ← CLI entry point
17
+ │ └── calls main() from src/index.js
18
+ │
19
+ └── src/index.js ← Listr2 orchestrator
20
+ │
21
+ ├── Phase 1: Environment Checks
22
+ │ ├── checkRoot() ← Verify running as root
23
+ │ ├── detectOS() ← Verify Ubuntu 24.04
24
+ │ └── detectIP() ← Find public IP (DO metadata or hostname)
25
+ │
26
+ ├── detectExistingState() ← Pre-flight detection
27
+ │ ├── Existing Lamaste? ← Check /etc/lamalibre/lamaste/panel.json
28
+ │ ├── Existing nginx? ← List non-lamaste sites-enabled
29
+ │ ├── Port 3100 in use? ← Check with ss
30
+ │ └── UFW active? ← Count existing rules
31
+ │
32
+ ├── confirmInstallation() ← Show banner + warnings, wait for Enter
33
+ │
34
+ ├── Phase 2: Installation Tasks
35
+ │ ├── hardenTasks() ← Swap, UFW, fail2ban, SSH hardening
36
+ │ ├── nodeTasks() ← Node.js 20 LTS installation
37
+ │ ├── mtlsTasks() ← CA + client cert + PKCS12 generation
38
+ │ ├── nginxTasks() ← Self-signed TLS, mTLS snippet, vhost, nginx start
39
+ │ ├── panelTasks() ← User, dirs, chisel + Authelia, deploy, config, systemd, sudoers, start
40
+ │ └── gatekeeperTasks() ← Deploy gatekeeper, state files, systemd, start
41
+ │
42
+ └── printSummary() ← Formatted box with SCP command + password + URL
43
+ ```
44
+
45
+ ## Task Execution Order
46
+
47
+ The installer runs tasks in a strict sequential order. Each task group depends on the outputs of the previous one.
48
+
49
+ ### Phase 1: Environment Checks
50
+
51
+ Three checks run as subtasks of a single Listr2 group:
52
+
53
+ 1. **Verify root access** — `process.getuid() !== 0` → error
54
+ 2. **Detect operating system** — parse `/etc/os-release`, require Ubuntu 24.04
55
+ 3. **Detect IP address** — try DigitalOcean metadata API (`169.254.169.254`), fall back to `hostname -I`. The `--dev` flag accepts private IPs for local testing.
56
+
57
+ ### Pre-flight Detection
58
+
59
+ Between Phase 1 and Phase 2, the installer runs `detectExistingState()` to gather warnings for the confirmation banner. All checks are wrapped in try/catch — detection failures never block the installer.
60
+
61
+ Detected conditions:
62
+
63
+ - Existing `/etc/lamalibre/lamaste/panel.json` → "Re-running will update the installation but preserve your configuration"
64
+ - Non-lamaste nginx sites in `sites-enabled` → "Existing nginx sites will be affected"
65
+ - Port 3100 in use (checked via `ss`) → "The panel may fail to start"
66
+ - Active UFW with existing rules → "Existing UFW firewall rules will be reset"
67
+
68
+ ### Phase 2: Installation Tasks
69
+
70
+ Six task groups run sequentially through Listr2:
71
+
72
+ | Order | Task Group | Source | Key Operations |
73
+ | ----- | ---------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
74
+ | 1 | Hardening | `tasks/harden.js` | Swap file, UFW firewall, fail2ban, SSH hardening, system packages |
75
+ | 2 | Node.js | `tasks/node.js` | Check existing, add NodeSource repo, install, verify |
76
+ | 3 | mTLS | `tasks/mtls.js` | CA key + cert, client key + CSR, sign, PKCS12 bundle |
77
+ | 4 | nginx | `tasks/nginx.js` | Self-signed TLS cert, mTLS snippet, IP vhost, port-80 redirect, enable site, start |
78
+ | 5 | Panel | `tasks/panel.js` | System user, root-owned install dir + cert help page, pinned chisel + Authelia (binaries, units, accounts, ownership), deploy server + CLI + client + docs, config, systemd, `lamaste-priv` and wrappers, sudoers, start + health check |
79
+ | 6 | Gatekeeper | `tasks/gatekeeper.js` | Deploy gatekeeper (root-owned), state files, systemd unit, start |
80
+
81
+ ## Task Implementation Details
82
+
83
+ ### Hardening (`tasks/harden.js`)
84
+
85
+ **Swap file creation:**
86
+
87
+ - Checks if swap is already active via `swapon --show`
88
+ - Creates 1 GB swap at `/swapfile` with `fallocate`, `mkswap`, `swapon`
89
+ - Adds to `/etc/fstab` if not already present
90
+ - Sets `vm.swappiness=10` via sysctl (reduces swap aggressiveness)
91
+
92
+ **UFW firewall:**
93
+
94
+ - If UFW is already active, only adds missing port rules (22, 443, 9292)
95
+ - If inactive, sets defaults (deny incoming, allow outgoing), allows ports, enables
96
+ - Never resets an active firewall — preserves existing rules
97
+
98
+ **fail2ban:**
99
+
100
+ - Installs via `apt-get`, writes drop-in config at `/etc/fail2ban/jail.d/lamaste.conf`
101
+ - Configures SSH jail (5 attempts, 1 hour ban) and nginx-http-auth jail
102
+ - Skip guard: checks if config already exists with expected content and service is active
103
+
104
+ **SSH hardening:**
105
+
106
+ - Sets `PasswordAuthentication no`, `PermitRootLogin prohibit-password`, `ChallengeResponseAuthentication no`
107
+ - Uses write-validate-swap pattern: writes to temp file, validates with `sshd -t -f`, then moves into place
108
+ - Creates backup at `/etc/ssh/sshd_config.pre-lamaste` before first modification
109
+ - If validation fails, temp file is deleted and original config remains untouched
110
+
111
+ **System dependencies:**
112
+
113
+ - Runs `apt-get update` then installs `curl`, `openssl`, `nginx`, `certbot`, `python3-certbot-nginx`
114
+ - Stops nginx after installation (will be configured and started by the nginx task)
115
+ - Removes the default nginx site from `sites-enabled`
116
+
117
+ ### mTLS Certificates (`tasks/mtls.js`)
118
+
119
+ **CA generation:**
120
+
121
+ - 4096-bit RSA key at `/etc/lamalibre/lamaste/pki/ca.key` (mode 600)
122
+ - Self-signed CA certificate at `/etc/lamalibre/lamaste/pki/ca.crt` (mode 644) with 10-year validity
123
+ - Subject: `CN=Lamaste CA, O=Lamaste`
124
+
125
+ **Client certificate:**
126
+
127
+ - 4096-bit RSA key at `/etc/lamalibre/lamaste/pki/client.key` (mode 600)
128
+ - CSR signed by CA with 2-year validity
129
+ - Subject: `CN=admin, O=Lamaste`
130
+ - CSR is deleted after signing (no longer needed)
131
+
132
+ **PKCS12 bundle:**
133
+
134
+ - Combines client key + cert + CA cert into `/etc/lamalibre/lamaste/pki/client.p12` (mode 600)
135
+ - Uses legacy algorithms (`PBE-SHA1-3DES`, `sha1` MAC) for maximum browser compatibility — modern PKCS12 defaults are not supported by macOS Keychain Access
136
+ - Password generated via `crypto.randomBytes(24).toString('base64url')`
137
+ - Password saved to `/etc/lamalibre/lamaste/pki/.p12-password` (mode 600)
138
+
139
+ ### nginx Configuration (`tasks/nginx.js`)
140
+
141
+ **Self-signed TLS certificate:**
142
+
143
+ - 2048-bit RSA key for the IP-based vhost
144
+ - Includes `subjectAltName=IP:<detected-ip>` for browser compatibility
145
+ - 10-year validity — this cert is only for IP access, not public-facing
146
+
147
+ **mTLS snippet:**
148
+
149
+ - Written to `/etc/nginx/snippets/lamalibre-lamaste-mtls.conf`
150
+ - Contains two lines: `ssl_client_certificate` pointing to CA cert, and `ssl_verify_client on`
151
+ - Shared by the IP vhost and the domain panel vhost (after onboarding)
152
+
153
+ **IP-based panel vhost:**
154
+
155
+ - Listens on port 9292 with SSL
156
+ - Includes the mTLS snippet for client certificate enforcement
157
+ - Proxies all traffic to `127.0.0.1:3100` (Panel Server)
158
+ - WebSocket upgrade support for `/api` paths
159
+ - Custom error pages (495/496) serve a certificate help page for visitors without certs
160
+
161
+ **Certificate help page** (written by the panel task, and regenerated on every redeploy):
162
+
163
+ - Static HTML at `/opt/lamalibre/lamaste/lamaste-server-ui/cert-help.html`
164
+ - Styled with the same dark terminal aesthetic as the panel
165
+ - Shows step-by-step instructions for downloading and importing the client certificate
166
+ - Served by nginx when SSL client verification fails (error codes 495, 496)
167
+
168
+ ### Panel Deployment (`tasks/panel.js`)
169
+
170
+ **System user:**
171
+
172
+ - Creates `lamaste` user with `--system --no-create-home --shell /usr/sbin/nologin`
173
+ - Idempotent: checks if user exists first with `id lamaste`
174
+
175
+ **Directory structure:**
176
+
177
+ - `/opt/lamalibre/lamaste/` — code (panel server, `lamaste-server` CLI, UI, docs, gatekeeper, cert help page), **root-owned** and read-only for `lamaste`. The `lamaste-server` CLI is run as root by administrators, so the panel must not be able to change it. A tree owned by anyone but root (earlier versions gave it to `lamaste`) is moved aside, every component is deployed fresh, and the old tree is removed.
178
+ - `/etc/lamalibre/lamaste/` — configuration and state, owned by `lamaste`
179
+ - `/var/www/lamaste/` — static site roots, `lamaste:www-data`, directories `2750` (setgid), files `0640`
180
+
181
+ **Chisel and Authelia** (`lib/relay-services.js`, run on install and on every redeploy):
182
+
183
+ - Creates the `lamaste-chisel` group (and adds `lamaste` to it) and the `lamaste-authelia` system account (no login shell, no home)
184
+ - Installs Chisel 1.12.0 and Authelia 4.39.28 to `/usr/local/bin` (`lib/binaries.js`): downloaded from fixed release URLs, verified against the SHA-256 digests pinned in `@lamalibre/lamaste@4.0.0` (`CHISEL_RELEASE`, `AUTHELIA_RELEASE`) before anything is unpacked, staged in a `mkdtemp` directory and renamed into place. A binary already at the pinned version is left alone; an Authelia **newer** than the pin is kept, never downgraded (its database may use a schema the pinned release cannot read)
185
+ - Writes `/etc/systemd/system/chisel.service` (`User=nobody`, `Group=lamaste-chisel`) and `/etc/systemd/system/authelia.service` (`User=lamaste-authelia`, `UMask=027`, `ProtectSystem=strict` with `ReadWritePaths=/etc/authelia /var/log/authelia`, `SystemCallFilter=@system-service`, empty `CapabilityBoundingSet`, `NoNewPrivileges`); both are enabled and started later by onboarding
186
+ - Sets ownership (`lib/ownership.js`): `/var/www/lamaste` `lamaste:www-data 2750`; `/etc/authelia` `lamaste:lamaste-authelia 2770` (setgid) with `configuration.yml` `0640`, `users.yml` `0660`, `.secrets.json` `0600` (lamaste), `db.sqlite3*` and `notifications.txt` `lamaste-authelia 0600`; `/var/log/authelia` `lamaste-authelia 0750`; `chisel-server.key` (if present) `lamaste:lamaste-chisel 0640`; the PKI directory `lamaste:lamaste` (the panel is the CA)
187
+
188
+ Every write root makes in a directory `lamaste` can write to (its config directory, the web root, `/etc/authelia`) avoids following symlinks the panel could have planted: new files are created `O_EXCL | O_NOFOLLOW` and renamed into place, ownership is changed through the file descriptor, recursive changes use `chown -R -P` / `rm -rf --one-file-system`.
189
+
190
+ **Server deployment:**
191
+
192
+ - Copies `package.json` and `src/` from `vendor/lamaste-serverd/`
193
+ - Runs `npm install --production --ignore-scripts` in the deployment directory (root-owned)
194
+
195
+ **Client deployment:**
196
+
197
+ - Prefers pre-built `dist/` directory from vendor (avoids Vite build on low-RAM VPS)
198
+ - Falls back to building from source in a fresh `mkdtemp` directory if no pre-built dist exists
199
+ - Only the `dist/` output is deployed to the lamaste-server-ui directory
200
+
201
+ **Configuration:**
202
+
203
+ - Writes `/etc/lamalibre/lamaste/panel.json` with detected IP, directory paths, and `onboarding.status: "FRESH"`
204
+ - On re-run: merges with existing config, preserving user/onboarding state
205
+ - File mode 0640, owned by `lamaste:lamaste`, written as a fresh file renamed into place
206
+
207
+ **Systemd service:**
208
+
209
+ - Unit file at `/etc/systemd/system/lamalibre-lamaste-serverd.service`
210
+ - Runs as the `lamaste` user with `SupplementaryGroups=lamaste-chisel`, `ProtectHome=true`, `PrivateTmp=true`, `ReadWritePaths=/etc/lamalibre/lamaste /var/www/lamaste`
211
+ - `NoNewPrivileges` is omitted: the panel runs the fixed `sudo` commands below
212
+ - Restart on failure with 5-second delay
213
+
214
+ **Root-owned programs** (`lib/wrappers.js`), installed before the sudoers file:
215
+
216
+ - `/usr/local/sbin/lamaste-priv` (Node.js, syntax-checked with `node --check`), `/usr/local/sbin/lamaste-certbot` and `/usr/local/sbin/lamaste-cert-info` (bash, checked with `bash -n`), installed `root:root 0755`
217
+ - The retired `lamaste-sign-csr` and `lamaste-pki-rename` are removed
218
+ - See [Security Model](../01-concepts/security-model.md#privilege-boundary) for what each accepts
219
+
220
+ **Sudoers rules:**
221
+
222
+ - `/etc/sudoers.d/lamaste` contains no wildcard: only fixed `systemctl` command lines (nginx; chisel including `try-restart`, `enable`, `disable`; authelia; `certbot.timer`; the panel and gatekeeper services), `nginx -t`, and the three programs above
223
+ - Installed atomically: written as `/etc/sudoers.d/lamaste.lamaste-new` (sudo ignores files with a dot in their name), checked with `visudo -c`, then renamed into place — an invalid file is never live and the previous rules stay in force
224
+ - No blanket root access
225
+
226
+ **Health check:**
227
+
228
+ - After starting the service, waits 3 seconds, checks `systemctl is-active`
229
+ - Sends HTTP request to `http://127.0.0.1:3100/api/health`
230
+ - If either check fails, captures recent journal logs and reports them in the error message
231
+
232
+ ## Idempotency
233
+
234
+ The installer is designed to be safely re-run on the same machine. Every task group implements skip guards that detect existing state:
235
+
236
+ | Task | Skip Condition | Behavior |
237
+ | --------------- | --------------------------------------------------------- | ----------------------------------------- |
238
+ | Swap creation | `swapon --show` returns data | Skip silently |
239
+ | UFW firewall | All required ports already allowed and active | Skip silently |
240
+ | fail2ban | Config exists with expected content and service is active | Skip silently |
241
+ | SSH hardening | All settings already correct in `sshd_config` | Skip silently |
242
+ | Node.js install | `node --version` returns v20+ | Skip NodeSource + apt-get |
243
+ | mTLS certs | `ca.key` and `client.p12` both exist | Skip entire group, read existing password |
244
+ | Panel config | `panel.json` exists | Merge instead of overwrite |
245
+ | System user | `id lamaste` succeeds | Skip `useradd` |
246
+
247
+ The mTLS certificate skip guard is especially important: regenerating certificates would invalidate the admin's already-imported client certificate, locking them out of the panel.
248
+
249
+ ## Redeploy Mode
250
+
251
+ When the installer detects an existing Lamaste installation (`/etc/lamalibre/lamaste/panel.json` exists) and the `--force-full` flag is not set, it enters **redeploy mode** instead of running the full installation.
252
+
253
+ Redeploy mode stops the panel and gatekeeper, secures the install directory (a non-root-owned tree is moved aside and removed at the end), redeploys the panel server, `lamaste-server` CLI, UI, docs, gatekeeper and cert help page, merges `panel.json`, runs the same Chisel/Authelia step as a fresh install (stopping a running Authelia while its files change hands), rewrites the panel's systemd unit, the root-owned programs and the sudoers rules, ensures the port-80 redirect, then restarts Chisel if its binary or unit changed, starts Authelia and the gatekeeper if they were running, and restarts the panel. It does not touch OS hardening, mTLS certificates or the panel's vhosts. The panel's self-update (`sudo lamaste-priv self-update <version>`) runs this path as root, so it never writes through a path the `lamaste` user controls.
254
+
255
+ This provides a fast upgrade path: re-running `npx @lamalibre/create-lamaste@4.0.0` on an existing installation updates the code and the pinned services while preserving all configuration and certificates. Use `--force-full` to bypass this and run the complete installer.
256
+
257
+ The redeploy logic lives in `tasks/redeploy.js`, with shared systemd unit and sudoers content generators in `lib/service-config.js`.
258
+
259
+ ## Vendor Bundling
260
+
261
+ The `create-lamaste` package ships with the lamalibre-lamaste-serverd and lamaste-server-ui code bundled in a `vendor/` directory. This is necessary because:
262
+
263
+ 1. The installer runs on a fresh VPS with no access to the monorepo
264
+ 2. `npm install` on the server only installs the `create-lamaste` package
265
+ 3. The vendor directory contains the exact code that was built and tested
266
+
267
+ The vendor directory structure:
268
+
269
+ ```
270
+ packages/create-lamaste/vendor/
271
+ ├── lamaste-serverd/
272
+ │ ├── package.json
273
+ │ └── src/ ← Server source code
274
+ └── lamaste-server-ui/
275
+ └── dist/ ← Pre-built Vite output (preferred)
276
+ ├── index.html
277
+ └── assets/
278
+ ```
279
+
280
+ When `dist/` contains a pre-built `index.html`, the installer copies it directly. This avoids running `vite build` on the VPS, which can OOM-kill on a 512 MB droplet.
281
+
282
+ ## Error Handling
283
+
284
+ The installer follows a fail-fast philosophy with safe re-run guarantees:
285
+
286
+ **At the orchestrator level:**
287
+
288
+ - `exitOnError: true` on both Listr2 task groups — any failure stops the entire pipeline
289
+ - The error handler prints a formatted box with the error message and a "You can safely re-run this installer to retry" note
290
+ - Exit code 1 on any failure
291
+
292
+ **At the task level:**
293
+
294
+ - Every `execa` call includes descriptive error messages that explain what failed and suggest recovery steps
295
+ - Network errors (apt-get, curl, npm) include "Check your internet connection" hints
296
+ - Validation errors (sshd, nginx, sudoers) clean up temp files before throwing
297
+
298
+ **At the subtask level:**
299
+
300
+ - `subtask.output` provides real-time progress for long-running operations (e.g., "Downloading NodeSource setup script...")
301
+ - `rendererOptions: { persistentOutput: true }` keeps the last status visible in the terminal
302
+
303
+ ## CLI Flags
304
+
305
+ | Flag | Purpose |
306
+ | --------------- | ---------------------------------------------------------------------------- |
307
+ | `--help`, `-h` | Print help message describing what the installer does and exit |
308
+ | `--yes`, `-y` | Skip the confirmation prompt (for automated installs) |
309
+ | `--skip-harden` | Skip OS hardening tasks (swap, UFW, fail2ban, SSH) |
310
+ | `--dev` | Accept private/non-routable IP addresses (for VM testing) |
311
+ | `--force-full` | Run the full installation even on existing installs (bypasses redeploy mode) |
312
+ | `--uninstall` | Print manual removal guide and exit |
313
+
314
+ ## Shared Context Object
315
+
316
+ All tasks share a `ctx` object that accumulates state through the pipeline:
317
+
318
+ ```javascript
319
+ const ctx = {
320
+ ip: null, // Detected public IP
321
+ osRelease: null, // { id, versionId, prettyName }
322
+ skipHarden: false, // --skip-harden flag
323
+ nodeAlreadyInstalled: false, // Skip Node.js install if v20+
324
+ nodeVersion: null, // e.g., "v20.11.1"
325
+ npmVersion: null, // e.g., "10.2.4"
326
+ p12Password: null, // Generated PKCS12 password
327
+ pkiDir: '/etc/lamalibre/lamaste/pki', // Certificate directory
328
+ configDir: '/etc/lamalibre/lamaste', // Configuration directory
329
+ installDir: '/opt/lamalibre/lamaste', // Deployment directory
330
+ };
331
+ ```
332
+
333
+ ## Key Files
334
+
335
+ | File | Role |
336
+ | --------------------------------------------------------------------------- | -------------------------------------------------------------------- |
337
+ | `packages/provisioners/server/bin/create-lamaste.js` | CLI entry point (`#!/usr/bin/env node`) |
338
+ | `packages/provisioners/server/src/index.js` | Main orchestrator with Listr2 pipeline |
339
+ | `packages/provisioners/server/src/tasks/harden.js` | OS hardening subtasks |
340
+ | `packages/provisioners/server/src/tasks/node.js` | Node.js 20 installation subtasks |
341
+ | `packages/provisioners/server/src/tasks/mtls.js` | mTLS certificate generation subtasks |
342
+ | `packages/provisioners/server/src/tasks/nginx.js` | nginx IP-based configuration subtasks |
343
+ | `packages/provisioners/server/src/tasks/panel.js` | Panel deployment subtasks |
344
+ | `packages/provisioners/server/src/tasks/gatekeeper.js` | Gatekeeper deployment subtasks |
345
+ | `packages/provisioners/server/src/tasks/redeploy.js` | Redeployment (upgrade and self-update) subtasks |
346
+ | `packages/provisioners/server/src/lib/relay-services.js` | Accounts, pinned binaries, units and ownership for chisel/Authelia |
347
+ | `packages/provisioners/server/src/lib/binaries.js` | Verified install of the pinned chisel and Authelia releases |
348
+ | `packages/provisioners/server/src/lib/accounts.js` | `lamaste-chisel` group and `lamaste-authelia` account |
349
+ | `packages/provisioners/server/src/lib/ownership.js` | Symlink-safe root writes, directory ownership migrations |
350
+ | `packages/provisioners/server/src/lib/wrappers.js` | Installs the root-owned programs and the sudoers file |
351
+ | `packages/provisioners/server/src/lib/env.js` | OS detection, IP detection, root check |
352
+ | `packages/provisioners/server/src/lib/secrets.js` | `crypto.randomBytes` wrappers |
353
+ | `packages/provisioners/server/src/lib/summary.js` | Post-install summary box printer |
354
+ | `packages/provisioners/server/src/lib/cert-help-page.js` | HTML help page generator |
355
+ | `packages/provisioners/server/src/lib/service-config.js` | Systemd unit + sudoers content generators |
356
+ | `packages/provisioners/server/scripts/lamaste-priv` | Root helper: panel vhosts (allow-listed), Authelia TOTP, self-update |
357
+ | `packages/provisioners/server/scripts/lamaste-certbot`, `lamaste-cert-info` | Validating certbot / certificate-read wrappers |
358
+
359
+ ## Confirmation Banner
360
+
361
+ Between environment checks and installation, the installer displays a confirmation banner. This is the only user interaction point (unless `--yes` is passed).
362
+
363
+ The banner:
364
+
365
+ - Shows a formatted box listing all system modifications that will be made
366
+ - Displays detection warnings below the box (existing Lamaste install, UFW rules, port conflicts, existing nginx sites)
367
+ - Waits for the user to press Enter or Ctrl+C
368
+
369
+ ```
370
+ ┌─────────────────────────────────────────────────────────────┐
371
+ │ Lamaste Installer │
372
+ ├─────────────────────────────────────────────────────────────┤
373
+ │ │
374
+ │ This will install Lamaste on this machine. │
375
+ │ │
376
+ │ The following changes will be made: │
377
+ │ │
378
+ │ • Reset UFW firewall (allow ports 22, 443, 9292 only) │
379
+ │ • Harden SSH (disable password authentication) │
380
+ │ • Install fail2ban, Node.js 20, nginx, certbot │
381
+ │ • Generate mTLS certificates for browser access │
382
+ │ • Create lamaste user and systemd service │
383
+ │ • Deploy panel to /opt/lamalibre/lamaste/ │
384
+ │ │
385
+ │ Designed for a fresh Ubuntu 24.04 droplet. │
386
+ │ │
387
+ └─────────────────────────────────────────────────────────────┘
388
+
389
+ ! An existing Lamaste installation was detected (onboarding: COMPLETED).
390
+ Re-running will update the installation but preserve your configuration.
391
+ ! Existing UFW firewall rules (6 rules) will be reset.
392
+ ```
393
+
394
+ Warning messages are only shown when relevant conditions are detected. On a fresh droplet with no existing state, no warnings appear.
395
+
396
+ ## Post-Install Summary
397
+
398
+ After all tasks complete, `printSummary()` reads the saved PKCS12 password and displays a formatted box with everything the user needs:
399
+
400
+ 1. **SCP command** to download the client certificate: `scp root@<ip>:/etc/lamalibre/lamaste/pki/client.p12 .`
401
+ 2. **Import instructions** for macOS, Linux, and Windows browsers
402
+ 3. **Certificate password** (highlighted in yellow for visibility)
403
+ 4. **Panel URL**: `https://<ip>:9292`
404
+ 5. **Reassurance** that they can disconnect from SSH
405
+
406
+ The summary box uses dynamic width calculation, stripping ANSI color codes to measure actual character widths, and builds a Unicode box-drawing border around the content.
407
+
408
+ ## Listr2 Rendering
409
+
410
+ The installer uses Listr2's default renderer with `collapseSubtasks: false`, which means all subtask output remains visible as the installer progresses. Each subtask uses `rendererOptions: { persistentOutput: true }` to keep the final status line visible after the subtask completes.
411
+
412
+ The rendering structure looks like this during execution:
413
+
414
+ ```
415
+ ✔ Checking environment
416
+ ✔ Verifying root access
417
+ ✔ Detecting operating system [Ubuntu 24.04.1 LTS]
418
+ ✔ Detecting IP address [203.0.113.42]
419
+ ◼ Hardening operating system
420
+ ✔ Creating swap file [Swap file created and activated]
421
+ ◼ Configuring UFW firewall [Allowing ports 22, 443, 9292...]
422
+ ◼ Installing and configuring fail2ban
423
+ ◼ Hardening SSH configuration
424
+ ◼ Installing system dependencies
425
+ ```
426
+
427
+ This gives the operator clear visibility into what is happening, which is important when modifying a production server.
428
+
429
+ ## Design Decisions
430
+
431
+ ### Why zero prompts?
432
+
433
+ The installer collects no input. Domain, email, and all other configuration happen through the browser-based onboarding wizard after the panel is running. This separation means the installer can be fully automated (`--yes` flag) and the user gets a graphical interface for the complex decisions.
434
+
435
+ ### Why Listr2?
436
+
437
+ Listr2 provides structured task output with progress indicators, persistent output lines, and nested subtask rendering. This makes it clear what the installer is doing at every step — important for a script that modifies the OS.
438
+
439
+ ### Why vendor bundling instead of npm workspace install?
440
+
441
+ The installer runs via `npx` on a fresh server. `npx` installs only the `create-lamaste` package, not the full monorepo. The vendor approach ships a self-contained bundle with all code needed, avoiding network-dependent monorepo installs on the target server.
442
+
443
+ ### Why PBE-SHA1-3DES for PKCS12?
444
+
445
+ Modern OpenSSL defaults produce PKCS12 bundles that macOS Keychain Access cannot import. The legacy algorithm flags (`-keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1`) ensure compatibility across macOS, Windows, and Linux browsers. Security of the PKCS12 bundle is not critical — it is a one-time transport mechanism protected by a random password.