@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,327 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import crypto from 'node:crypto';
3
+ import { execa } from 'execa';
4
+ import { CHISEL_RELEASE } from '@lamalibre/lamaste';
5
+ import { getConfig, updateConfig } from '../../lib/config.js';
6
+ import * as chisel from '../../lib/chisel.js';
7
+ import * as authelia from '../../lib/authelia.js';
8
+ import * as certbot from '../../lib/certbot.js';
9
+ import * as nginx from '../../lib/nginx.js';
10
+ import { writeInvitePage } from '../../lib/invite-page.js';
11
+ import { migrateChiselCredentialsIfNeeded } from '../../lib/chisel-users.js';
12
+ import { loadAgentRegistry } from '../../lib/mtls.js';
13
+ import { setChiselRunningPinnedRelease } from '../../lib/chisel-runtime.js';
14
+
15
+ // Module-level state for provisioning
16
+ const emitter = new EventEmitter();
17
+ emitter.setMaxListeners(50);
18
+
19
+ const TASK_DEFINITIONS = [
20
+ { id: 'install-chisel', title: 'Starting Chisel' },
21
+ { id: 'install-authelia', title: 'Configuring Authelia' },
22
+ { id: 'issue-certs', title: 'Issuing TLS certificates' },
23
+ { id: 'configure-nginx', title: 'Configuring nginx' },
24
+ { id: 'verify-services', title: 'Verifying services' },
25
+ { id: 'finalize', title: 'Finalizing setup' },
26
+ ];
27
+
28
+ let provisioningState = {
29
+ isRunning: false,
30
+ tasks: TASK_DEFINITIONS.map((t) => ({ ...t, status: 'pending', message: null, log: null })),
31
+ error: null,
32
+ result: null,
33
+ };
34
+
35
+ /**
36
+ * Reset provisioning state for a fresh run.
37
+ */
38
+ function resetState() {
39
+ provisioningState = {
40
+ isRunning: true,
41
+ tasks: TASK_DEFINITIONS.map((t) => ({ ...t, status: 'pending', message: null, log: null })),
42
+ error: null,
43
+ result: null,
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Emit a progress event and update the provisioning state.
49
+ */
50
+ function emitProgress(taskId, status, message, log = null) {
51
+ const taskIndex = provisioningState.tasks.findIndex((t) => t.id === taskId);
52
+ if (taskIndex !== -1) {
53
+ provisioningState.tasks[taskIndex].status = status;
54
+ provisioningState.tasks[taskIndex].message = message;
55
+ if (log !== null) {
56
+ provisioningState.tasks[taskIndex].log = log;
57
+ }
58
+ }
59
+
60
+ const current =
61
+ provisioningState.tasks.filter((t) => t.status === 'done').length +
62
+ (status === 'running' ? 1 : 0);
63
+
64
+ const payload = {
65
+ task: taskId,
66
+ title: provisioningState.tasks[taskIndex]?.title || taskId,
67
+ status,
68
+ message,
69
+ log,
70
+ progress: { current, total: TASK_DEFINITIONS.length },
71
+ };
72
+
73
+ emitter.emit('progress', payload);
74
+ }
75
+
76
+ /**
77
+ * Run the full provisioning sequence.
78
+ */
79
+ async function runProvisioning(log) {
80
+ const config = getConfig();
81
+ const { domain, email } = config;
82
+
83
+ let adminPassword;
84
+
85
+ try {
86
+ // Step 1: Chisel. create-lamaste installed the pinned binary and its
87
+ // unit (as root); the panel creates the key and the authfile and starts
88
+ // the service.
89
+ emitProgress('install-chisel', 'running', 'Checking the Chisel installation...');
90
+ if (!(await chisel.isChiselInstalled())) {
91
+ throw new Error(
92
+ 'Chisel is not installed. Run `npx @lamalibre/create-lamaste` on the server to repair the installation.',
93
+ );
94
+ }
95
+ const chiselVersion = await chisel.getInstalledChiselVersion();
96
+ await chisel.ensureChiselKey();
97
+ // Initialise the chisel-users authfile before starting the service.
98
+ // On a fresh install the registry is empty, so it holds only the
99
+ // no-grants sentinel user (chisel authenticates, and rejects every
100
+ // connection until an agent is enrolled). On re-provisioning over an
101
+ // existing install it preserves any prior credentials.
102
+ emitProgress('install-chisel', 'running', 'Initialising chisel-users authfile...');
103
+ await migrateChiselCredentialsIfNeeded(loadAgentRegistry, log);
104
+ emitProgress('install-chisel', 'running', 'Starting Chisel service...');
105
+ await chisel.startChisel();
106
+ setChiselRunningPinnedRelease(chiselVersion === CHISEL_RELEASE.version);
107
+ emitProgress('install-chisel', 'done', `Chisel ${chiselVersion ?? ''} running`.trim());
108
+
109
+ // Step 2: Authelia. Installed by create-lamaste (it runs as its own
110
+ // unprivileged account); the panel writes its configuration and starts it.
111
+ emitProgress('install-authelia', 'running', 'Checking the Authelia installation...');
112
+ if (!(await authelia.isAutheliaInstalled())) {
113
+ throw new Error(
114
+ 'Authelia is not installed. Run `npx @lamalibre/create-lamaste` on the server to repair the installation.',
115
+ );
116
+ }
117
+ emitProgress('install-authelia', 'running', 'Writing configuration...');
118
+
119
+ const secrets = {
120
+ jwtSecret: crypto.randomBytes(32).toString('hex'),
121
+ sessionSecret: crypto.randomBytes(32).toString('hex'),
122
+ storageEncryptionKey: crypto.randomBytes(32).toString('hex'),
123
+ };
124
+ await authelia.writeAutheliaConfig(domain, secrets);
125
+
126
+ emitProgress('install-authelia', 'running', 'Creating admin user...');
127
+ adminPassword = crypto.randomBytes(16).toString('base64url');
128
+ await authelia.createUser('admin', adminPassword);
129
+
130
+ emitProgress('install-authelia', 'running', 'Starting Authelia service...');
131
+ await authelia.startAuthelia();
132
+ emitProgress('install-authelia', 'done', 'Authelia configured and running');
133
+
134
+ // Step 3: Issue certificates
135
+ emitProgress('issue-certs', 'running', `Issuing certificate for panel.${domain}...`);
136
+ await certbot.issueCoreCerts(domain, email);
137
+ emitProgress('issue-certs', 'running', 'Setting up auto-renewal...');
138
+ await certbot.setupAutoRenew();
139
+ emitProgress('issue-certs', 'done', 'TLS certificates issued');
140
+
141
+ // Step 4: Configure nginx
142
+ emitProgress('configure-nginx', 'running', 'Writing panel vhost...');
143
+ await nginx.writePanelVhost(domain);
144
+
145
+ emitProgress('configure-nginx', 'running', 'Writing auth vhost...');
146
+ await nginx.writeAuthVhost(domain);
147
+
148
+ emitProgress('configure-nginx', 'running', 'Writing tunnel vhost...');
149
+ await nginx.writeTunnelVhost(domain);
150
+
151
+ emitProgress('configure-nginx', 'running', 'Writing invitation page...');
152
+ await writeInvitePage();
153
+
154
+ emitProgress('configure-nginx', 'running', 'Enabling sites...');
155
+ await nginx.enableSite('lamalibre-lamaste-panel-domain');
156
+ await nginx.enableSite('lamalibre-lamaste-auth');
157
+ await nginx.enableSite('lamalibre-lamaste-tunnel');
158
+
159
+ emitProgress('configure-nginx', 'running', 'Testing nginx configuration...');
160
+ const testResult = await nginx.testConfig();
161
+ if (!testResult.valid) {
162
+ throw new Error(`nginx configuration test failed: ${testResult.error}`);
163
+ }
164
+
165
+ emitProgress('configure-nginx', 'running', 'Reloading nginx...');
166
+ await nginx.reload();
167
+ emitProgress('configure-nginx', 'done', 'nginx configured and reloaded');
168
+
169
+ // Step 5: Verify services
170
+ emitProgress('verify-services', 'running', 'Checking Chisel...');
171
+ const chiselRunning = await chisel.isChiselRunning();
172
+ if (!chiselRunning) {
173
+ throw new Error('Chisel service is not running after provisioning');
174
+ }
175
+
176
+ emitProgress('verify-services', 'running', 'Checking Authelia...');
177
+ const autheliaRunning = await authelia.isAutheliaRunning();
178
+ if (!autheliaRunning) {
179
+ throw new Error('Authelia service is not running after provisioning');
180
+ }
181
+
182
+ emitProgress('verify-services', 'running', 'Checking nginx...');
183
+ try {
184
+ const { stdout } = await execa('systemctl', ['is-active', 'nginx']);
185
+ if (stdout.trim() !== 'active') {
186
+ throw new Error('nginx is not active');
187
+ }
188
+ } catch {
189
+ throw new Error('nginx service is not running after provisioning');
190
+ }
191
+
192
+ emitProgress('verify-services', 'running', 'Checking panel server...');
193
+ try {
194
+ const { stdout } = await execa('curl', ['-s', 'http://127.0.0.1:3100/api/health']);
195
+ const health = JSON.parse(stdout);
196
+ if (health.status !== 'ok') {
197
+ throw new Error('Panel server health check returned unexpected status');
198
+ }
199
+ } catch (err) {
200
+ throw new Error(`Panel server health check failed: ${err.message}`);
201
+ }
202
+ emitProgress('verify-services', 'done', 'All services running');
203
+
204
+ // Step 6: Finalize
205
+ emitProgress('finalize', 'running', 'Updating configuration...');
206
+ await updateConfig({ onboarding: { status: 'COMPLETED' } });
207
+
208
+ const result = {
209
+ adminUsername: 'admin',
210
+ adminPassword,
211
+ panelUrl: `https://panel.${domain}`,
212
+ authUrl: `https://auth.${domain}`,
213
+ };
214
+
215
+ provisioningState.result = result;
216
+ provisioningState.isRunning = false;
217
+ emitProgress('finalize', 'done', 'Provisioning complete');
218
+
219
+ // Send the completion event
220
+ emitter.emit('progress', {
221
+ task: 'complete',
222
+ status: 'done',
223
+ message: 'Provisioning complete',
224
+ result,
225
+ progress: { current: TASK_DEFINITIONS.length, total: TASK_DEFINITIONS.length },
226
+ });
227
+ } catch (err) {
228
+ const failedTask = provisioningState.tasks.find((t) => t.status === 'running');
229
+ const failedTaskId = failedTask?.id || 'unknown';
230
+
231
+ if (failedTask) {
232
+ failedTask.status = 'error';
233
+ failedTask.message = err.message;
234
+ }
235
+
236
+ provisioningState.error = { task: failedTaskId, message: err.message };
237
+ provisioningState.isRunning = false;
238
+
239
+ emitter.emit('progress', {
240
+ task: failedTaskId,
241
+ status: 'error',
242
+ message: `Failed: ${failedTask?.title || failedTaskId}`,
243
+ error: err.message,
244
+ progress: {
245
+ current: provisioningState.tasks.filter((t) => t.status === 'done').length,
246
+ total: TASK_DEFINITIONS.length,
247
+ },
248
+ });
249
+
250
+ log.error({ err, task: failedTaskId }, 'Provisioning failed');
251
+ } finally {
252
+ // Clear sensitive data from memory after clients have had time to receive it.
253
+ // This runs on both success and error paths to prevent password leaks.
254
+ setTimeout(() => {
255
+ adminPassword = undefined;
256
+ if (provisioningState.result) {
257
+ provisioningState.result.adminPassword = null;
258
+ }
259
+ }, 5000);
260
+ }
261
+ }
262
+
263
+ export default async function provisionRoute(fastify, _opts) {
264
+ // POST /provision — start provisioning
265
+ fastify.post('/provision', async (request, reply) => {
266
+ const config = getConfig();
267
+ const { status } = config.onboarding;
268
+
269
+ if (status === 'FRESH' || status === 'DOMAIN_SET') {
270
+ return reply.code(409).send({
271
+ error: 'DNS must be verified before provisioning',
272
+ });
273
+ }
274
+
275
+ if (status === 'COMPLETED') {
276
+ return reply.code(410).send({
277
+ error: 'Onboarding already completed',
278
+ });
279
+ }
280
+
281
+ // If provisioning is actively running, return 409
282
+ if (provisioningState.isRunning) {
283
+ return reply.code(409).send({
284
+ error: 'Provisioning already in progress',
285
+ });
286
+ }
287
+
288
+ // Set status to PROVISIONING
289
+ await updateConfig({ onboarding: { status: 'PROVISIONING' } });
290
+
291
+ // Reset state and start provisioning in the background
292
+ resetState();
293
+ runProvisioning(request.log);
294
+
295
+ return reply.code(202).send({ ok: true, message: 'Provisioning started' });
296
+ });
297
+
298
+ // WebSocket /provision/stream — real-time progress
299
+ fastify.get('/provision/stream', { websocket: true }, (socket, _request) => {
300
+ // Send current state immediately for late-joining clients
301
+ socket.send(
302
+ JSON.stringify({
303
+ type: 'state',
304
+ ...provisioningState,
305
+ }),
306
+ );
307
+
308
+ // Subscribe to progress events
309
+ function onProgress(payload) {
310
+ try {
311
+ socket.send(JSON.stringify(payload));
312
+ } catch {
313
+ // Client disconnected — handled by close event
314
+ }
315
+ }
316
+
317
+ emitter.on('progress', onProgress);
318
+
319
+ socket.on('close', () => {
320
+ emitter.off('progress', onProgress);
321
+ });
322
+
323
+ socket.on('error', () => {
324
+ emitter.off('progress', onProgress);
325
+ });
326
+ });
327
+ }
@@ -0,0 +1,12 @@
1
+ import { getConfig } from '../../lib/config.js';
2
+
3
+ export default async function statusRoute(fastify, _opts) {
4
+ fastify.get('/status', async (_request, _reply) => {
5
+ const config = getConfig();
6
+ return {
7
+ status: config.onboarding.status,
8
+ domain: config.domain ?? null,
9
+ ip: config.ip,
10
+ };
11
+ });
12
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Server-side plugin router — delegates to pluginHostPlugin from @lamalibre/lamaste.
3
+ *
4
+ * Configures the core plugin host with:
5
+ * - mTLS auth strategy (two-level encapsulation)
6
+ * - Per-plugin auth hook: admits admin certs unconditionally, or
7
+ * agent / plugin-agent certs that hold at least one
8
+ * `plugin:<pluginName>:*` capability. Per-route capability specifics
9
+ * (e.g. `plugin:rodeo:run` vs `plugin:rodeo:submit`) remain each
10
+ * plugin's responsibility via its own preHandlers.
11
+ * - Server paths (/etc/lamalibre/lamaste/)
12
+ * - Storage config resolution per plugin
13
+ * - Onboarding guard (503 before onboarding completes)
14
+ */
15
+ import { pluginHostPlugin } from '@lamalibre/lamaste';
16
+ import { readPlugins } from '../lib/plugins.js';
17
+ import { getPluginStorageConfig } from '../lib/storage.js';
18
+ import { getConfig } from '../lib/config.js';
19
+ import { getCaPaths } from '../lib/pki-paths.js';
20
+ import { managementOnly } from '../middleware/onboarding-guard.js';
21
+
22
+ const STATE_DIR = process.env.LAMALIBRE_LAMASTE_STATE_DIR || '/etc/lamalibre/lamaste';
23
+ const PLUGINS_DIR = `${STATE_DIR}/plugins`;
24
+
25
+ export default async function pluginRouter(fastify) {
26
+ // Block all plugin routes until onboarding is complete
27
+ fastify.addHook('onRequest', managementOnly());
28
+
29
+ await fastify.register(pluginHostPlugin, {
30
+ baseDir: STATE_DIR,
31
+ pluginsDataDir: PLUGINS_DIR,
32
+ readRegistry: readPlugins,
33
+ auth: 'mtls',
34
+ authHookFactory: (pluginName) => {
35
+ const allowedPrefix = `plugin:${pluginName}:`;
36
+ return async (request, reply) => {
37
+ const role = request.certRole;
38
+ if (role === 'admin') return;
39
+
40
+ if (role === 'agent' || role === 'plugin-agent') {
41
+ const caps = request.certCapabilities || [];
42
+ if (caps.some((c) => c.startsWith(allowedPrefix))) {
43
+ return;
44
+ }
45
+ }
46
+
47
+ return reply.code(403).send({
48
+ error: 'Insufficient scope for plugin route',
49
+ details: {
50
+ required: `admin OR agent with at least one "${allowedPrefix}*" capability`,
51
+ current: role ?? 'unknown',
52
+ },
53
+ });
54
+ };
55
+ },
56
+
57
+ /**
58
+ * Resolve per-plugin register options: always injects `lamasteCa`
59
+ * ({ certPath, keyPath }) so plugins (e.g. rodeo) can reuse Lamaste's
60
+ * CA for mTLS; adds `storage` only when the plugin has bound storage.
61
+ */
62
+ resolvePluginOptions: async (plugin) => {
63
+ const panelConfig = getConfig();
64
+ const base = {
65
+ lamasteCa: getCaPaths(),
66
+ };
67
+
68
+ const storageConfig = await getPluginStorageConfig(plugin.name);
69
+ if (storageConfig) {
70
+ base.storage = {
71
+ ...storageConfig,
72
+ prefix: panelConfig.serverId,
73
+ };
74
+ }
75
+
76
+ return base;
77
+ },
78
+ });
79
+ }