@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,230 @@
1
+ # Creating Your First Tunnel
2
+
3
+ > Expose a local web app to the internet through Lamaste in under 5 minutes.
4
+
5
+ ## In Plain English
6
+
7
+ A tunnel connects a web app running on your local machine (behind your home router or office firewall) to a public URL on your Lamaste server. Visitors go to `app.example.com`, and Lamaste forwards the request through a secure WebSocket tunnel to your machine, which serves the response. Your app never leaves your machine — only the traffic flows through the relay.
8
+
9
+ ## Prerequisites
10
+
11
+ - A completed [Lamaste onboarding](onboarding.md) with your domain configured
12
+ - A **web app running locally** on your machine (macOS or Linux) on a port between 1024 and 65535
13
+ - That machine **enrolled as an agent** — with the [Desktop App](desktop-app-setup.md) or `lamaste-agent setup` (see [Agent Setup](agent-setup.md)). Every tunnel is carried by exactly one agent, so the agent must exist before its tunnel
14
+
15
+ For this guide, assume you have a web app running on `http://localhost:3000`.
16
+
17
+ ## Step-by-Step
18
+
19
+ ### 1. Open the Tunnels Page
20
+
21
+ Log in to the Lamaste admin panel at `https://panel.example.com` (or `https://<ip>:9292`).
22
+
23
+ Click **Tunnels** in the sidebar navigation.
24
+
25
+ You see an empty state: "No tunnels configured. Create your first tunnel to get started."
26
+
27
+ ### 2. Click Add Tunnel
28
+
29
+ Click the **Add Tunnel** button in the top right corner.
30
+
31
+ A form appears with these fields:
32
+
33
+ | Field | Description | Example |
34
+ | ------------------------------ | ------------------------------------------------------------------ | --------------------- |
35
+ | **Subdomain** | The subdomain for your tunnel's public URL | `app` |
36
+ | **Port** | The port your app listens on, on the agent | `3000` |
37
+ | **Agent** | The enrolled agent that carries the tunnel (required) | `laptop` |
38
+ | **Largest request body (MiB)** | Biggest upload nginx accepts for this tunnel (1–10240, default 10) | `10` |
39
+ | **Description** | Optional note for your reference | `My React dev server` |
40
+ | **Access** | Restricted (default), All Authelia Users, or Public | Restricted |
41
+
42
+ ### 3. Fill in the Form
43
+
44
+ Enter a subdomain name. This becomes the public URL: if your domain is `example.com` and you enter `app`, the tunnel URL is `app.example.com`.
45
+
46
+ Subdomain rules:
47
+
48
+ - Lowercase letters, numbers, and hyphens only
49
+ - Cannot start or end with a hyphen
50
+ - Maximum 63 characters
51
+ - Cannot use reserved names: `panel`, `auth`, `tunnel`, `www`, `mail`, `ftp`, `api`
52
+ - Cannot start with `agent-` (this prefix is reserved for agent panel tunnels)
53
+
54
+ Enter the port number. This is the port your web app listens on, on `127.0.0.1` of the agent machine. Must be between 1024 and 65535, and unique across all tunnels (Chisel binds the same port on the server). Ports 3100, 9090, 9091, 9292 and 9294 are refused: they belong to Lamaste's own services on the server, and a tunnel on one of them would publish that service.
55
+
56
+ Pick the **Agent** that runs the app. Only that agent is told about the tunnel, and only that agent's Chisel credential may open its port on the server — another agent cannot take over the hostname.
57
+
58
+ Leave **Largest request body** at 10 MiB unless the app takes larger uploads; requests above the limit get HTTP 413. Optionally add a description to help you remember what this tunnel is for.
59
+
60
+ ### 4. Create the Tunnel
61
+
62
+ Click **Add Tunnel**.
63
+
64
+ The panel performs four operations in sequence:
65
+
66
+ 1. **Issues a TLS certificate** — Runs certbot to get a Let's Encrypt certificate for `app.example.com`. This takes a few seconds.
67
+ 2. **Writes an nginx vhost** — Creates a reverse proxy configuration that forwards `app.example.com` traffic through Gatekeeper/Authelia (unless the tunnel is public) to `127.0.0.1:3000`, with the tunnel's `client_max_body_size`.
68
+ 3. **Saves the tunnel** — Writes the tunnel record, including its `agentLabel`, to `/etc/lamalibre/lamaste/tunnels.json`.
69
+ 4. **Syncs Chisel** — Re-renders the Chisel authfile from the saved state so the carrying agent — and only that agent — may bind the port. Chisel picks up the added grant without a restart, so other tunnels are not interrupted.
70
+
71
+ When all four steps succeed, the new tunnel appears in the list with its subdomain, FQDN, port, agent, access mode, and body limit.
72
+
73
+ **If something goes wrong:** The panel rolls back completed steps. If nginx configuration fails, the certificate is left in place (harmless). If saving the state or the Chisel sync fails, the tunnel is removed from the state and the nginx vhost is removed. You see an error message describing what went wrong.
74
+
75
+ ### 5. Wait for the Agent (about 30 seconds)
76
+
77
+ The server now accepts the port from the carrying agent. The agent picks the tunnel up by itself: its sync timer asks the panel for its tunnel configuration (`GET /api/tunnels/agent-config`) every 30 seconds, rewrites its Chisel service (launchd on macOS, a systemd user unit on Linux) when the set changed, and restarts it — or starts it, if this is the agent's first tunnel. To apply the change at once, run on the agent machine:
78
+
79
+ ```bash
80
+ lamaste-agent update
81
+ ```
82
+
83
+ The same happens for every later change — a deleted or disabled tunnel, or one moved to or from this agent. `lamaste-agent status` shows the last sync on its **Sync** line.
84
+
85
+ ### 6. Verify the Tunnel
86
+
87
+ Once the agent has picked up the tunnel, test your tunnel:
88
+
89
+ 1. Make sure your local app is running on the configured port (e.g., `http://localhost:3000`).
90
+ 2. Open `https://app.example.com` in a new browser window (or incognito).
91
+ 3. Authelia redirects you to the login page at `auth.example.com`.
92
+ 4. Enter your Authelia username and password.
93
+ 5. Enter your TOTP code from your authenticator app.
94
+ 6. After authentication, you see your local app.
95
+
96
+ **Expected behavior:** The first visit takes a moment because Authelia intercepts the request for authentication. After logging in, subsequent requests flow through quickly. Authelia sets a session cookie, so you do not need to re-authenticate for each page load.
97
+
98
+ ### 7. Share Access with Users
99
+
100
+ By default a tunnel is **Restricted**: a visitor needs an Authelia account and a grant for this tunnel. To let someone else access the tunnel:
101
+
102
+ 1. Go to the **Users** page in the panel.
103
+ 2. Create a new user with a username, display name, email, and password.
104
+ 3. Grant the user (or a group they belong to) access to the tunnel — see [Authorization](../01-concepts/authorization.md). Tunnels set to **All Authelia Users** skip this step.
105
+ 4. Share the credentials and the tunnel URL with the person.
106
+ 5. On their first login, they set up TOTP with their own authenticator app.
107
+
108
+ See [Managing Users](managing-users.md) for the full guide.
109
+
110
+ ## For Developers
111
+
112
+ ### Tunnel Data Model
113
+
114
+ Each tunnel is stored as a JSON object in `/etc/lamalibre/lamaste/tunnels.json`:
115
+
116
+ ```json
117
+ {
118
+ "id": "550e8400-e29b-41d4-a716-446655440000",
119
+ "subdomain": "app",
120
+ "fqdn": "app.example.com",
121
+ "port": 3000,
122
+ "description": "My React dev server",
123
+ "type": "app",
124
+ "accessMode": "restricted",
125
+ "agentLabel": "laptop",
126
+ "maxBodySizeMb": 10,
127
+ "enabled": true,
128
+ "createdAt": "2024-01-15T10:30:00.000Z"
129
+ }
130
+ ```
131
+
132
+ `agentLabel` is the carrying agent. `maxBodySizeMb` becomes the vhost's `client_max_body_size`; tunnels created before the setting existed have no value and keep nginx's built-in 1 MiB until a body limit is set on them.
133
+
134
+ ### API Endpoints
135
+
136
+ | Method | Path | Purpose |
137
+ | -------- | --------------------------- | ---------------------------------------------------------------------- |
138
+ | `GET` | `/api/tunnels` | List tunnels, newest first (an agent sees only its own) |
139
+ | `POST` | `/api/tunnels` | Create a tunnel (certbot + nginx + state + chisel) |
140
+ | `PATCH` | `/api/tunnels/:id` | Enable/disable, move to another agent, change access mode or body size |
141
+ | `DELETE` | `/api/tunnels/:id` | Remove a tunnel (nginx + state + chisel) |
142
+ | `GET` | `/api/tunnels/agent-config` | One agent's Chisel client configuration (used by `lamaste-agent`) |
143
+
144
+ See the [Tunnels API](../04-api-reference/tunnels.md) for the full reference.
145
+
146
+ ### Create Tunnel Request
147
+
148
+ ```json
149
+ POST /api/tunnels
150
+ {
151
+ "subdomain": "app",
152
+ "port": 3000,
153
+ "agentLabel": "laptop",
154
+ "description": "My React dev server"
155
+ }
156
+ ```
157
+
158
+ Validation rules (Zod schema):
159
+
160
+ - `subdomain`: lowercase alphanumeric with hyphens, max 63 chars, not reserved, not already served by a static site or site alias
161
+ - `port`: integer between 1024 and 65535, unique across tunnels
162
+ - `agentLabel`: an enrolled, non-revoked agent — required for admin certificates; an agent certificate defaults to itself and may not name another agent
163
+ - `accessMode`: `restricted` (default), `authenticated`, or `public` — the last two admin-only
164
+ - `maxBodySizeMb`: optional integer 1–10240, default 10
165
+ - `description`: optional, max 200 characters
166
+
167
+ ### nginx Vhost Structure
168
+
169
+ Each tunnel gets a vhost that:
170
+
171
+ 1. Terminates TLS with a Let's Encrypt certificate
172
+ 2. Sends an auth subrequest to Authelia (`auth_request /authelia`)
173
+ 3. Proxies authenticated traffic to `127.0.0.1:<tunnel-port>`
174
+
175
+ The Chisel server on the VPS accepts a reverse tunnel for the port only from the carrying agent — its authfile grants each `agent-<label>` user exactly `^R:127\.0\.0\.1:<port>$` for the enabled tunnels it owns — and forwards traffic to that agent.
176
+
177
+ ### Traffic Flow
178
+
179
+ ```
180
+ Browser → app.example.com:443
181
+ → nginx (TLS termination)
182
+ → Authelia auth_request (checks session cookie / prompts login)
183
+ → proxy_pass 127.0.0.1:3000
184
+ → Chisel server (reverse tunnel)
185
+ → Chisel client (on your machine)
186
+ → localhost:3000 (your app)
187
+ ```
188
+
189
+ ### Agent Service
190
+
191
+ `lamaste-agent setup`, `update` and the 30-second sync timer render the Chisel client service from the `chiselArgs` returned by `GET /api/tunnels/agent-config` (implementation: `packages/core/lib/src/agent/converge.ts` and `chisel-service.ts`, shared by the CLI and `lamaste-agentd`). The service:
192
+
193
+ - Runs `chisel client --max-retry-interval 30s https://tunnel.example.com:443` and verifies the server's Let's Encrypt certificate — no `--tls-skip-verify`. The server URL must be `tunnel.<the domain the agent enrolled with>`
194
+ - Is stopped while the agent carries no tunnels (Chisel cannot run without a remote), and while the operator has stopped it
195
+ - Adds a `R:127.0.0.1:<port>:127.0.0.1:<port>` argument for each tunnel the agent carries
196
+ - Reads its credential from the environment (`AUTH=`), never from argv: the 0600 `chisel.env` on Linux, the 0600 plist's `EnvironmentVariables` on macOS
197
+ - Restarts automatically (`KeepAlive` on macOS, `Restart=always` on Linux)
198
+
199
+ ### Deletion Rollback
200
+
201
+ When deleting a tunnel, the panel removes components in order:
202
+
203
+ 1. Remove the nginx vhost and reload
204
+ 2. Remove the tunnel from `tunnels.json`
205
+ 3. Sync Chisel — the authfile is re-rendered without the owner's grant for the port, and Chisel restarts so live sessions drop it. The owner's sync timer stops asking for the port within about 30 seconds (until then Chisel refuses the owner's session, so its other tunnels are briefly down too)
206
+
207
+ The TLS certificate is left in place (certbot manages its lifecycle).
208
+
209
+ ## Quick Reference
210
+
211
+ | Action | How |
212
+ | ---------------------- | ----------------------------------------------------------- |
213
+ | **Create tunnel** | Tunnels page, "Add Tunnel" button |
214
+ | **Delete tunnel** | Click delete icon on the tunnel row |
215
+ | **Move / reconfigure** | "Edit" on the tunnel row (agent, access mode, body limit) |
216
+ | **Enable / disable** | Toggle on the tunnel row |
217
+ | **View tunnel URL** | Click the domain link on the tunnel row |
218
+ | **Update client** | Automatic within ~30 s; `lamaste-agent update` to apply now |
219
+
220
+ | Constraint | Value |
221
+ | -------------------- | ----------------------------------------------- |
222
+ | Subdomain format | `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$` |
223
+ | Port range | 1024-65535, except 3100, 9090, 9091, 9292, 9294 |
224
+ | Max subdomain length | 63 characters |
225
+ | Reserved subdomains | panel, auth, tunnel, www, mail, ftp, api |
226
+ | Reserved prefix | `agent-` (used by agent panel tunnels) |
227
+ | Unique port | Yes (one tunnel per port) |
228
+ | Unique subdomain | Yes (one tunnel per subdomain) |
229
+ | Carrying agent | Exactly one (`agentLabel`) |
230
+ | Request body limit | 1-10240 MiB (agents: up to 100), default 10 |
@@ -0,0 +1,393 @@
1
+ # Installation
2
+
3
+ > Complete guide to provisioning a Lamaste server from a fresh DigitalOcean droplet in under 10 minutes.
4
+
5
+ ## In Plain English
6
+
7
+ This guide walks you through creating a cheap cloud server and running a single command that turns it into a secure relay for your local web apps. By the end, you have a browser-accessible admin panel protected by a client certificate — no SSH required after setup.
8
+
9
+ ## Prerequisites
10
+
11
+ Before you begin, make sure you have:
12
+
13
+ - A **DigitalOcean account** (or any VPS provider offering Ubuntu 24.04)
14
+ - A **local terminal** with SSH and SCP available (macOS Terminal, Linux shell, or Windows PowerShell)
15
+ - A **modern browser** (Chrome, Firefox, Safari, or Edge) for importing the client certificate
16
+ - Approximately **10 minutes** of uninterrupted time
17
+
18
+ ## Step-by-Step
19
+
20
+ ### 1. Create a DigitalOcean Droplet
21
+
22
+ 1. Log in to [DigitalOcean](https://cloud.digitalocean.com/).
23
+ 2. Click **Create** and select **Droplets**.
24
+ 3. Choose these settings:
25
+
26
+ | Setting | Value |
27
+ | ------------------ | ----------------------------------------------------- |
28
+ | **Region** | Choose the closest to your users |
29
+ | **Image** | Ubuntu 24.04 (LTS) x64 |
30
+ | **Size** | Basic, Regular, $4/mo (512 MB RAM, 1 vCPU, 10 GB SSD) |
31
+ | **Authentication** | SSH keys (recommended) or password |
32
+ | **Hostname** | Something memorable, e.g. `lamaste-relay` |
33
+
34
+ 4. Click **Create Droplet**.
35
+ 5. Wait for the droplet to boot. Copy its **public IP address** from the dashboard.
36
+
37
+ The $4/month droplet is sufficient. Lamaste is designed for a 512 MB RAM budget — all services together use approximately 245 MB, with a 1 GB swap file as a safety net.
38
+
39
+ ### 2. SSH into the Droplet
40
+
41
+ Open your terminal and connect:
42
+
43
+ ```bash
44
+ ssh root@203.0.113.42
45
+ ```
46
+
47
+ Replace `203.0.113.42` with your droplet's actual IP address.
48
+
49
+ If you used an SSH key during droplet creation, you connect immediately. If you chose a password, enter it when prompted.
50
+
51
+ **Expected output:**
52
+
53
+ ```
54
+ Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.x.x-xxx-generic x86_64)
55
+ ...
56
+ root@lamaste-relay:~#
57
+ ```
58
+
59
+ ### 3. Run the Installer
60
+
61
+ Execute the Lamaste installer:
62
+
63
+ ```bash
64
+ apt install -y npm
65
+ npx @lamalibre/create-lamaste@4.0.0
66
+ ```
67
+
68
+ The installer displays a confirmation banner before making any changes:
69
+
70
+ ```
71
+ ┌─────────────────────────────────────────────────────────────┐
72
+ │ Lamaste Installer │
73
+ ├─────────────────────────────────────────────────────────────┤
74
+ │ │
75
+ │ This will install Lamaste on this machine. │
76
+ │ │
77
+ │ The following changes will be made: │
78
+ │ │
79
+ │ * Reset UFW firewall (allow ports 22, 80, 443, 9292) │
80
+ │ * Harden SSH (disable password authentication) │
81
+ │ * Install fail2ban, Node.js 20, nginx, certbot │
82
+ │ * Generate mTLS certificates for browser access │
83
+ │ * Create lamaste user and systemd service │
84
+ │ * Deploy panel to /opt/lamalibre/lamaste/ │
85
+ │ │
86
+ │ Designed for a fresh Ubuntu 24.04 droplet. │
87
+ │ │
88
+ └─────────────────────────────────────────────────────────────┘
89
+
90
+ Press Enter to continue or Ctrl+C to abort...
91
+ ```
92
+
93
+ Press **Enter** to proceed. To skip the confirmation prompt, use `npx @lamalibre/create-lamaste@4.0.0 --yes`.
94
+
95
+ ### 4. What the Installer Does
96
+
97
+ The installer runs seven sequential phases. Each phase displays real-time progress:
98
+
99
+ **Phase 1 — Environment checks**
100
+
101
+ ```
102
+ ✔ Checking environment
103
+ ✔ Verifying root access
104
+ ✔ Detecting operating system → Ubuntu 24.04 LTS
105
+ ✔ Detecting IP address → 203.0.113.42
106
+ ```
107
+
108
+ The installer verifies you are running as root on Ubuntu 24.04 and detects the droplet's public IP address.
109
+
110
+ **Phase 2 — OS hardening**
111
+
112
+ ```
113
+ ✔ Hardening operating system
114
+ ✔ Creating swap file → Swap file created and activated
115
+ ✔ Configuring UFW firewall → UFW enabled with ports 22, 80, 443, 9292 allowed
116
+ ✔ Installing and configuring fail2ban → fail2ban status: active
117
+ ✔ Hardening SSH configuration → SSH hardened: key-auth only, root with keys only
118
+ ✔ Installing system dependencies → System dependencies installed, nginx stopped
119
+ ```
120
+
121
+ This phase creates a 1 GB swap file, configures the UFW firewall to allow only ports 22 (SSH), 80 (HTTP for Let's Encrypt), 443 (HTTPS), and 9292 (panel), installs fail2ban with SSH and nginx jails, hardens SSH to disable password authentication, and installs nginx, certbot, curl, and openssl.
122
+
123
+ **Phase 3 — Node.js**
124
+
125
+ ```
126
+ ✔ Installing Node.js 20
127
+ ✔ Checking existing Node.js installation → Node.js not found, installing v20 LTS
128
+ ✔ Installing NodeSource repository → NodeSource repository added
129
+ ✔ Installing Node.js 20 LTS → Node.js installed
130
+ ✔ Verifying Node.js installation → Node.js v20.x.x, npm 10.x.x
131
+ ```
132
+
133
+ If Node.js 20 or later is already installed, this phase is skipped.
134
+
135
+ **Phase 4 — mTLS certificates**
136
+
137
+ ```
138
+ ✔ Generating mTLS certificates
139
+ ✔ Creating PKI directory → PKI directory: /etc/lamalibre/lamaste/pki
140
+ ✔ Generating CA private key and certificate → CA key and certificate generated
141
+ ✔ Generating client key and CSR → Client key and CSR generated
142
+ ✔ Signing client certificate with CA → Client certificate signed and CSR removed
143
+ ✔ Creating PKCS12 bundle → PKCS12 bundle created
144
+ ```
145
+
146
+ This phase creates a private certificate authority (CA), signs a client certificate, and bundles it into a `.p12` file for browser import. The CA certificate has a 10-year validity; the client certificate has a 2-year validity.
147
+
148
+ **Phase 5 — nginx**
149
+
150
+ ```
151
+ ✔ Configuring nginx
152
+ ✔ Generating self-signed TLS certificate for IP access → Self-signed TLS certificate generated
153
+ ✔ Writing mTLS snippet → mTLS snippet written
154
+ ✔ Writing IP-based panel vhost → Vhost written
155
+ ✔ Deploying certificate help page → Certificate help page deployed
156
+ ✔ Enabling site and cleaning up defaults → Site enabled, default removed
157
+ ✔ Redirecting plain HTTP to HTTPS → Port 80 answers every host with a redirect to https://
158
+ ✔ Validating and starting nginx → nginx is running and listening on port 9292
159
+ ```
160
+
161
+ nginx is configured to listen on port 9292 with TLS and mTLS client certificate verification. A self-signed certificate is used for the IP-based connection (a proper Let's Encrypt certificate is issued later during onboarding when you configure a domain).
162
+
163
+ Port 80 gets a catch-all site, `lamalibre-lamaste-http-redirect` (`listen 80 default_server`), that answers every plain-HTTP request with a `301` to `https://$host$request_uri`. It does not get in the way of Let's Encrypt: during an HTTP-01 challenge certbot's nginx plugin clones this default server for the name being validated, answers the challenge, and restores the file afterwards — for new certificates and renewals alike. If another site on the machine already owns port 80's default server, the step is skipped and the relay works without the redirect.
164
+
165
+ **Phase 6 — Panel deployment**
166
+
167
+ ```
168
+ ✔ Deploying Lamaste panel
169
+ ✔ Creating system user → Created system user: lamaste
170
+ ✔ Creating directory structure → Directories created: /opt/lamalibre/lamaste, /etc/lamalibre/lamaste
171
+ ✔ Installing chisel and Authelia → chisel and Authelia installed (started during onboarding)
172
+ ✔ Deploying serverd → Panel server deployed
173
+ ✔ Deploying lamaste-server CLI → lamaste-server CLI deployed; lamaste-reset-admin shim installed
174
+ ✔ Deploying server-ui → Panel client deployed from pre-built dist
175
+ ✔ Deploying documentation → Documentation deployed
176
+ ✔ Writing panel configuration → Configuration written to /etc/lamalibre/lamaste/panel.json
177
+ ✔ Writing systemd service unit → Systemd service unit written
178
+ ✔ Installing sudoers wrapper scripts → Sudoers wrappers installed
179
+ ✔ Writing sudoers rules → Sudoers rules written and validated
180
+ ✔ Starting panel service → Panel service running. Health: {"status":"ok"}
181
+ ```
182
+
183
+ The panel server runs as the unprivileged `lamaste` system user. Its code in `/opt/lamalibre/lamaste/` is root-owned and read-only for it. The installer — not onboarding — installs the pinned Chisel (1.12.0) and Authelia (4.39.28) releases, verified against pinned SHA-256 digests, with their systemd units; it creates the `lamaste-chisel` group (Chisel runs as it; the panel is a member so it can hand Chisel its authfile without sudo) and the `lamaste-authelia` account Authelia runs as. `/etc/sudoers.d/lamaste` has no wildcards: fixed `systemctl` command lines, `nginx -t`, and three root-owned programs in `/usr/local/sbin/` that validate every argument — `lamaste-priv` (the panel's nginx vhosts, checked against a directive allow-list; Authelia TOTP secrets; self-update), `lamaste-certbot` and `lamaste-cert-info`. The sudoers file is checked with `visudo -c` before it replaces the old one. See [Security Model](../01-concepts/security-model.md#privilege-boundary). Configuration lives in `/etc/lamalibre/lamaste/panel.json`.
184
+
185
+ **Phase 7 — Gatekeeper deployment** deploys the tunnel authorization service into `/opt/lamalibre/lamaste/gatekeeper` and starts it.
186
+
187
+ ### 5. Read the Summary
188
+
189
+ After all phases complete, the installer prints a summary box:
190
+
191
+ ```
192
+ ╔════════════════════════════════════════════════════════════════╗
193
+ ║ ║
194
+ ║ Lamaste installed successfully! ║
195
+ ║ ║
196
+ ╠════════════════════════════════════════════════════════════════╣
197
+ ║ ║
198
+ ║ 1. Download your client certificate: ║
199
+ ║ ║
200
+ ║ scp root@203.0.113.42:/etc/lamalibre/lamaste/pki/client.p12 . ║
201
+ ║ ║
202
+ ║ 2. Import client.p12 into your browser: ║
203
+ ║ ║
204
+ ║ macOS: Double-click the file → Keychain Access ║
205
+ ║ → select "System" keychain (not "login") ║
206
+ ║ → enter the password below when prompted ║
207
+ ║ → find cert in System keychain → double-click ║
208
+ ║ → Trust → Always Trust ║
209
+ ║ ║
210
+ ║ Linux: Chrome → Settings → Privacy & Security ║
211
+ ║ → Security → Manage certificates → Import ║
212
+ ║ ║
213
+ ║ Windows: Double-click the file → Certificate Import ║
214
+ ║ Wizard → enter the password below ║
215
+ ║ ║
216
+ ║ 3. Certificate password: ║
217
+ ║ ║
218
+ ║ <random-password-here> ║
219
+ ║ ║
220
+ ║ 4. Open the Lamaste panel: ║
221
+ ║ ║
222
+ ║ https://203.0.113.42:9292 ║
223
+ ║ ║
224
+ ║ (Your browser will warn about the self-signed cert. ║
225
+ ║ This is expected — click "Advanced" → "Proceed") ║
226
+ ║ ║
227
+ ║ You can now disconnect from SSH. ║
228
+ ║ The panel is running and accessible from your browser. ║
229
+ ║ ║
230
+ ╚════════════════════════════════════════════════════════════════╝
231
+ ```
232
+
233
+ **Save the certificate password.** You need it to import the `.p12` file into your browser.
234
+
235
+ ### 6. Download the Client Certificate
236
+
237
+ Open a **new terminal window** on your local machine (not the SSH session) and run:
238
+
239
+ ```bash
240
+ scp root@203.0.113.42:/etc/lamalibre/lamaste/pki/client.p12 .
241
+ ```
242
+
243
+ This copies the certificate bundle to your current directory.
244
+
245
+ ### 7. Import the Certificate into Your Browser
246
+
247
+ The `.p12` file contains both your client certificate and private key, protected by the password shown in the summary.
248
+
249
+ **macOS (Chrome, Safari, Firefox):**
250
+
251
+ 1. Double-click `client.p12`.
252
+ 2. Keychain Access opens. Select the **System** keychain (not "login").
253
+ 3. Enter the certificate password when prompted.
254
+ 4. Find the imported certificate in the System keychain (look for "admin" under "My Certificates").
255
+ 5. Double-click the certificate, expand **Trust**, and set "When using this certificate" to **Always Trust**.
256
+ 6. Close the dialog and enter your macOS password to confirm.
257
+
258
+ If you see an error about "Local Items" — ignore it. The System keychain import is what matters.
259
+
260
+ **Linux (Chrome):**
261
+
262
+ 1. Open Chrome and navigate to `chrome://settings/certificates`.
263
+ 2. Click **Import** under "Your Certificates".
264
+ 3. Select `client.p12` and enter the password.
265
+
266
+ **Windows (Chrome, Edge):**
267
+
268
+ 1. Double-click `client.p12`.
269
+ 2. The Certificate Import Wizard opens.
270
+ 3. Choose "Current User" and click Next.
271
+ 4. Enter the certificate password and click Next.
272
+ 5. Let Windows automatically select the certificate store.
273
+ 6. Click Finish.
274
+
275
+ ### 8. Access the Panel
276
+
277
+ Open your browser and navigate to:
278
+
279
+ ```
280
+ https://203.0.113.42:9292
281
+ ```
282
+
283
+ Your browser warns about the self-signed TLS certificate. This is expected — the IP-based connection uses a self-signed cert because Let's Encrypt cannot issue certificates for IP addresses.
284
+
285
+ - **Chrome:** Click "Advanced", then "Proceed to 203.0.113.42 (unsafe)".
286
+ - **Firefox:** Click "Advanced", then "Accept the Risk and Continue".
287
+ - **Safari:** Click "Show Details", then "visit this website".
288
+
289
+ Your browser then presents the client certificate. Select the "admin" certificate and click OK.
290
+
291
+ You see the Lamaste onboarding wizard. Proceed to the [Onboarding Guide](onboarding.md) for the next steps.
292
+
293
+ ### 9. Disconnect SSH
294
+
295
+ You can now close your SSH session:
296
+
297
+ ```bash
298
+ exit
299
+ ```
300
+
301
+ Everything from this point forward happens through the browser-based admin panel. SSH is never needed again for normal operation.
302
+
303
+ ## For Developers
304
+
305
+ ### Installer Internals
306
+
307
+ The installer (`packages/create-lamaste`) is built with Listr2 for progress display and execa for shell commands. It runs as a single orchestrated pipeline:
308
+
309
+ ```
310
+ main() → envTasks → confirmInstallation() → installTasks → printSummary()
311
+ ```
312
+
313
+ Each task module exports a function that returns a Listr2 subtask list:
314
+
315
+ | Module | File | Purpose |
316
+ | ------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------- |
317
+ | `hardenTasks` | `src/tasks/harden.js` | Swap, UFW, fail2ban, SSH, apt packages |
318
+ | `nodeTasks` | `src/tasks/node.js` | Node.js 20 LTS via NodeSource |
319
+ | `mtlsTasks` | `src/tasks/mtls.js` | CA, client cert, PKCS12 bundle |
320
+ | `nginxTasks` | `src/tasks/nginx.js` | Self-signed TLS, mTLS snippet, vhost |
321
+ | `panelTasks` | `src/tasks/panel.js` | System user, root-owned install dir, pinned chisel + Authelia, deploy, config, systemd, sudoers programs + rules |
322
+
323
+ ### Idempotency
324
+
325
+ Every task includes skip guards. Running the installer a second time detects existing state and skips completed steps:
326
+
327
+ - Swap already active: skips creation
328
+ - Node.js 20+ installed: skips NodeSource setup
329
+ - mTLS CA and client.p12 exist: skips all certificate generation
330
+ - UFW active with correct rules: skips firewall configuration
331
+ - SSH already hardened: skips SSH changes
332
+ - Existing `panel.json`: merges rather than overwrites
333
+
334
+ ### Directory Structure on the Server
335
+
336
+ After installation, the server has these Lamaste directories:
337
+
338
+ | Path | Owner | Purpose |
339
+ | ----------------------------------- | --------------------------------- | ----------------------------------------------- |
340
+ | `/etc/lamalibre/lamaste/` | `lamaste:lamaste` | Configuration and state files |
341
+ | `/etc/lamalibre/lamaste/pki/` | `lamaste:lamaste` (700) | CA, client cert, server cert, .p12 |
342
+ | `/etc/lamalibre/lamaste/panel.json` | `lamaste:lamaste` | Panel configuration |
343
+ | `/opt/lamalibre/lamaste/` | `root:root` | Panel server, CLI, UI, docs, gatekeeper (code) |
344
+ | `/var/www/lamaste/` | `lamaste:www-data` (2750, setgid) | Static site files (0640; nginx reads via group) |
345
+ | `/etc/authelia/` | `lamaste:lamaste-authelia` (2770) | Authelia configuration and database |
346
+ | `/var/log/authelia/` | `lamaste-authelia` (750) | Authelia log |
347
+ | `/usr/local/bin/chisel`, `authelia` | `root:root` | Pinned releases |
348
+
349
+ ### CLI Flags
350
+
351
+ | Flag | Effect |
352
+ | --------------- | ------------------------------------------- |
353
+ | `--help`, `-h` | Print help and exit |
354
+ | `--yes`, `-y` | Skip confirmation prompt |
355
+ | `--skip-harden` | Skip swap, UFW, fail2ban, and SSH hardening |
356
+ | `--dev` | Allow private/non-routable IP addresses |
357
+ | `--uninstall` | Print manual removal guide and exit |
358
+
359
+ ### Systemd Service
360
+
361
+ The panel runs as `lamalibre-lamaste-serverd.service`:
362
+
363
+ ```ini
364
+ [Service]
365
+ User=lamaste
366
+ Group=lamaste
367
+ # Lets the panel hand the chisel authfile to the group chisel runs as.
368
+ SupplementaryGroups=lamaste-chisel
369
+ WorkingDirectory=/opt/lamalibre/lamaste/serverd
370
+ ExecStart=/usr/bin/node src/index.js
371
+ Environment=NODE_ENV=production
372
+ Environment=CONFIG_FILE=/etc/lamalibre/lamaste/panel.json
373
+ Restart=always
374
+ RestartSec=5
375
+ ```
376
+
377
+ The service includes security hardening: `ProtectHome=true`, `PrivateTmp=true`, and `ReadWritePaths` for the config and static site directories. `NoNewPrivileges` is intentionally omitted because the panel runs the fixed `sudo` commands in `/etc/sudoers.d/lamaste` (service control, `nginx -t`, `lamaste-priv`, `lamaste-certbot`, `lamaste-cert-info`) — nothing else.
378
+
379
+ ## Quick Reference
380
+
381
+ | Item | Value |
382
+ | ------------------------ | ------------------------------------------------------------ |
383
+ | **Install command** | `npx @lamalibre/create-lamaste@4.0.0` |
384
+ | **Minimum VPS** | Ubuntu 24.04, 512 MB RAM |
385
+ | **Required ports** | 22 (SSH), 80 (HTTP/Let's Encrypt), 443 (HTTPS), 9292 (panel) |
386
+ | **Certificate location** | `/etc/lamalibre/lamaste/pki/client.p12` |
387
+ | **Download command** | `scp root@<ip>:/etc/lamalibre/lamaste/pki/client.p12 .` |
388
+ | **Panel URL** | `https://<ip>:9292` |
389
+ | **Config file** | `/etc/lamalibre/lamaste/panel.json` |
390
+ | **Service name** | `lamalibre-lamaste-serverd` |
391
+ | **Service logs** | `journalctl -u lamalibre-lamaste-serverd -f` |
392
+ | **Re-run safe** | Yes (idempotent) |
393
+ | **Total install time** | ~5-10 minutes |