@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,333 @@
1
+ # Users API
2
+
3
+ > Manage Authelia users who authenticate to tunneled applications with TOTP two-factor authentication.
4
+
5
+ ## In Plain English
6
+
7
+ When someone visits one of your tunneled apps (like `app.example.com`), they are prompted to log in through Authelia with a username, password, and a TOTP code from an authenticator app. The users API lets you create those login accounts, update their details, reset their TOTP secrets, and delete accounts you no longer need.
8
+
9
+ These are not admin panel users — the panel itself is protected by mTLS client certificates with no login screen. These users are for the apps you expose through tunnels.
10
+
11
+ ## Authentication
12
+
13
+ All user endpoints require a valid mTLS client certificate and a completed onboarding. See the [API Overview](./overview.md) for details.
14
+
15
+ If onboarding is not complete, all endpoints return `503 Service Unavailable`.
16
+
17
+ ## Important Behavior
18
+
19
+ - **Password hashing:** Passwords are hashed with bcrypt (not argon2id — argon2id uses ~93MB per hash and causes OOM kills on a 512MB droplet).
20
+ - **Authelia restart:** After every user change that modifies `users.yml` (create, update, delete), Authelia is restarted via `systemctl restart authelia` to pick up the changes. If the restart fails, the operation still succeeds — a warning is logged but no error is returned to the client. Note: TOTP reset does **not** restart Authelia — it writes directly to Authelia's SQLite database via the `authelia storage user totp generate` CLI command, which takes effect immediately.
21
+ - **Atomic writes:** The `users.yml` file is written atomically (write to temp file, then rename) to prevent Authelia from reading a partially written file.
22
+ - **Last user protection:** You cannot delete the last remaining user. At least one user must always exist.
23
+
24
+ ## Endpoints
25
+
26
+ ### `GET /api/users`
27
+
28
+ Returns all Authelia users, sorted alphabetically by username. Sensitive fields (password hash, TOTP secret) are excluded from the response.
29
+
30
+ **Request:**
31
+
32
+ No request body.
33
+
34
+ ```bash
35
+ curl -s --cert client.p12:password \
36
+ https://203.0.113.42:9292/api/users | jq
37
+ ```
38
+
39
+ **Response (200):**
40
+
41
+ ```json
42
+ {
43
+ "users": [
44
+ {
45
+ "username": "admin",
46
+ "displayname": "Admin User",
47
+ "email": "admin@example.com",
48
+ "groups": []
49
+ },
50
+ {
51
+ "username": "alice",
52
+ "displayname": "Alice Smith",
53
+ "email": "alice@example.com",
54
+ "groups": ["developers"]
55
+ }
56
+ ]
57
+ }
58
+ ```
59
+
60
+ | Field | Type | Description |
61
+ | ------------- | ---------- | --------------------------- |
62
+ | `username` | `string` | Unique login identifier |
63
+ | `displayname` | `string` | Human-readable display name |
64
+ | `email` | `string` | User's email address |
65
+ | `groups` | `string[]` | Authelia group memberships |
66
+
67
+ **Errors:**
68
+
69
+ | Status | Body | When |
70
+ | ------ | ------------------------------------------ | -------------------------------- |
71
+ | 500 | `{"error":"Failed to read user database"}` | Cannot read or parse `users.yml` |
72
+
73
+ ---
74
+
75
+ ### `POST /api/users`
76
+
77
+ Creates a new Authelia user. The password is hashed with bcrypt before being stored in `users.yml`.
78
+
79
+ **Request:**
80
+
81
+ ```json
82
+ {
83
+ "username": "alice",
84
+ "displayname": "Alice Smith",
85
+ "email": "alice@example.com",
86
+ "password": "s3cur3p4ssw0rd!",
87
+ "groups": ["developers"]
88
+ }
89
+ ```
90
+
91
+ | Field | Type | Validation | Description |
92
+ | ------------- | ---------- | ------------------------------------------------------------------------------------------- | ------------------------------------------- |
93
+ | `username` | `string` | 2-32 chars, lowercase alphanumeric + underscores + hyphens, starting with a letter or digit | Login identifier |
94
+ | `displayname` | `string` | 1-100 chars | Display name |
95
+ | `email` | `string` | Valid email format | Email address |
96
+ | `password` | `string` | 8-128 chars | Plain-text password (hashed before storage) |
97
+ | `groups` | `string[]` | Optional, defaults to `[]` | Authelia group memberships |
98
+
99
+ **Username regex** (the same rule applies to invitations — a leading `-` would read as a flag to Authelia's CLI when a TOTP secret is stored):
100
+
101
+ ```
102
+ ^[a-z0-9][a-z0-9_-]*$
103
+ ```
104
+
105
+ ```bash
106
+ curl -s --cert client.p12:password \
107
+ -X POST \
108
+ -H "Content-Type: application/json" \
109
+ -d '{"username":"alice","displayname":"Alice Smith","email":"alice@example.com","password":"s3cur3p4ssw0rd!"}' \
110
+ https://203.0.113.42:9292/api/users | jq
111
+ ```
112
+
113
+ **Response (201):**
114
+
115
+ ```json
116
+ {
117
+ "ok": true,
118
+ "user": {
119
+ "username": "alice",
120
+ "displayname": "Alice Smith",
121
+ "email": "alice@example.com",
122
+ "groups": []
123
+ }
124
+ }
125
+ ```
126
+
127
+ **Errors:**
128
+
129
+ | Status | Body | When |
130
+ | ------ | ---------------------------------------------------------- | ------------------------------------------- |
131
+ | 400 | `{"error":"Validation failed","details":{"issues":[...]}}` | Invalid username, email, or password length |
132
+ | 409 | `{"error":"Username already exists"}` | A user with this username already exists |
133
+ | 500 | `{"error":"Failed to read user database"}` | Cannot read `users.yml` |
134
+ | 500 | `{"error":"Failed to hash password"}` | bcrypt hashing failed |
135
+ | 500 | `{"error":"Failed to update user database"}` | Cannot write `users.yml` |
136
+
137
+ ---
138
+
139
+ ### `PUT /api/users/:username`
140
+
141
+ Updates one or more fields of an existing user. At least one field must be provided. Only the fields included in the request body are updated — omitted fields remain unchanged.
142
+
143
+ If a new password is provided, it is hashed with bcrypt before storage.
144
+
145
+ **Request:**
146
+
147
+ ```json
148
+ {
149
+ "displayname": "Alice J. Smith",
150
+ "email": "alice.smith@example.com",
151
+ "password": "n3wP4ssw0rd!",
152
+ "groups": ["developers", "admins"]
153
+ }
154
+ ```
155
+
156
+ | Field | Type | Validation | Description |
157
+ | ------------- | ---------- | --------------------- | -------------------------------------------- |
158
+ | `displayname` | `string` | 1-100 chars, optional | New display name |
159
+ | `email` | `string` | Valid email, optional | New email address |
160
+ | `password` | `string` | 8-128 chars, optional | New password (hashed before storage) |
161
+ | `groups` | `string[]` | Optional | New group memberships (replaces entire list) |
162
+
163
+ All fields are optional, but at least one must be provided (validated by a Zod `.refine()`).
164
+
165
+ ```bash
166
+ curl -s --cert client.p12:password \
167
+ -X PUT \
168
+ -H "Content-Type: application/json" \
169
+ -d '{"displayname":"Alice J. Smith","groups":["developers","admins"]}' \
170
+ https://203.0.113.42:9292/api/users/alice | jq
171
+ ```
172
+
173
+ **Response (200):**
174
+
175
+ ```json
176
+ {
177
+ "ok": true,
178
+ "user": {
179
+ "username": "alice",
180
+ "displayname": "Alice J. Smith",
181
+ "email": "alice.smith@example.com",
182
+ "groups": ["developers", "admins"]
183
+ }
184
+ }
185
+ ```
186
+
187
+ **Errors:**
188
+
189
+ | Status | Body | When |
190
+ | ------ | ---------------------------------------------------------- | ------------------------------------------------ |
191
+ | 400 | `{"error":"Validation failed","details":{"issues":[...]}}` | No fields provided, or invalid field values |
192
+ | 404 | `{"error":"User not found"}` | No user with this username |
193
+ | 500 | `{"error":"Failed to read user database"}` | Cannot read `users.yml` |
194
+ | 500 | `{"error":"Failed to hash password"}` | bcrypt hashing failed (when password is updated) |
195
+ | 500 | `{"error":"Failed to update user database"}` | Cannot write `users.yml` |
196
+
197
+ ---
198
+
199
+ ### `DELETE /api/users/:username`
200
+
201
+ Deletes an Authelia user. The user is removed from `users.yml` and Authelia is reloaded.
202
+
203
+ You cannot delete the last remaining user. This safety check prevents accidentally locking out all users from tunneled applications.
204
+
205
+ **Request:**
206
+
207
+ No request body.
208
+
209
+ ```bash
210
+ curl -s --cert client.p12:password \
211
+ -X DELETE \
212
+ https://203.0.113.42:9292/api/users/alice | jq
213
+ ```
214
+
215
+ **Response (200):**
216
+
217
+ ```json
218
+ {
219
+ "ok": true
220
+ }
221
+ ```
222
+
223
+ **Errors:**
224
+
225
+ | Status | Body | When |
226
+ | ------ | -------------------------------------------- | -------------------------- |
227
+ | 400 | `{"error":"Cannot delete the last user"}` | Only one user remains |
228
+ | 404 | `{"error":"User not found"}` | No user with this username |
229
+ | 500 | `{"error":"Failed to read user database"}` | Cannot read `users.yml` |
230
+ | 500 | `{"error":"Failed to update user database"}` | Cannot write `users.yml` |
231
+
232
+ ---
233
+
234
+ ### `POST /api/users/:username/reset-totp`
235
+
236
+ Generates a new TOTP secret for a user and writes it to Authelia's SQLite database via the `authelia storage user totp generate` CLI command. Returns an `otpauth://` URI that can be used to enroll an authenticator app. The old TOTP secret is immediately replaced — the user's existing authenticator enrollment becomes invalid.
237
+
238
+ **Request:**
239
+
240
+ No request body.
241
+
242
+ ```bash
243
+ curl -s --cert client.p12:password \
244
+ -X POST \
245
+ https://203.0.113.42:9292/api/users/alice/reset-totp | jq
246
+ ```
247
+
248
+ **Response (200):**
249
+
250
+ ```json
251
+ {
252
+ "ok": true,
253
+ "totpUri": "otpauth://totp/Lamaste:alice?secret=JBSWY3DPEHPK3PXP&issuer=Lamaste&algorithm=SHA1&digits=6&period=30"
254
+ }
255
+ ```
256
+
257
+ | Field | Type | Description |
258
+ | --------- | --------- | ------------------------------------------------------------------------------- |
259
+ | `ok` | `boolean` | Always `true` on success |
260
+ | `totpUri` | `string` | `otpauth://` URI for QR code generation or manual entry in an authenticator app |
261
+
262
+ The `totpUri` follows the [Key Uri Format](https://github.com/google/google-authenticator/wiki/Key-Uri-Format) standard. The panel client renders this as a QR code for scanning with Google Authenticator, Authy, or similar apps.
263
+
264
+ **Errors:**
265
+
266
+ | Status | Body | When |
267
+ | ------ | ------------------------------------------------ | ------------------------------------------------------ |
268
+ | 404 | `{"error":"User not found"}` | No user with this username |
269
+ | 500 | `{"error":"Failed to read user database"}` | Cannot read `users.yml` |
270
+ | 500 | `{"error":"Failed to write TOTP configuration"}` | Cannot write TOTP secret to Authelia's SQLite database |
271
+
272
+ ## Quick Reference
273
+
274
+ | Method | Path | Description |
275
+ | ------ | --------------------------------- | -------------------------------------------------- |
276
+ | GET | `/api/users` | List all users (alphabetical, no sensitive fields) |
277
+ | POST | `/api/users` | Create user (bcrypt hash + Authelia reload) |
278
+ | PUT | `/api/users/:username` | Update user fields (partial update) |
279
+ | DELETE | `/api/users/:username` | Delete user (not the last one) |
280
+ | POST | `/api/users/:username/reset-totp` | Generate new TOTP secret |
281
+
282
+ ### User Object Shape
283
+
284
+ Returned by list and mutation endpoints (sensitive fields excluded):
285
+
286
+ ```json
287
+ {
288
+ "username": "alice",
289
+ "displayname": "Alice Smith",
290
+ "email": "alice@example.com",
291
+ "groups": ["developers"]
292
+ }
293
+ ```
294
+
295
+ ### Validation Summary
296
+
297
+ | Field | Rules |
298
+ | ------------- | --------------------------------------------- |
299
+ | `username` | 2-32 chars, `/^[a-z0-9][a-z0-9_-]*$/`, unique |
300
+ | `displayname` | 1-100 chars |
301
+ | `email` | Valid email format |
302
+ | `password` | 8-128 chars |
303
+ | `groups` | Array of strings, optional |
304
+
305
+ ### curl Cheat Sheet
306
+
307
+ ```bash
308
+ # List users
309
+ curl -s --cert client.p12:password \
310
+ https://203.0.113.42:9292/api/users | jq
311
+
312
+ # Create user
313
+ curl -s --cert client.p12:password \
314
+ -X POST -H "Content-Type: application/json" \
315
+ -d '{"username":"bob","displayname":"Bob","email":"bob@example.com","password":"mypassword"}' \
316
+ https://203.0.113.42:9292/api/users | jq
317
+
318
+ # Update user (change display name and groups)
319
+ curl -s --cert client.p12:password \
320
+ -X PUT -H "Content-Type: application/json" \
321
+ -d '{"displayname":"Robert","groups":["admins"]}' \
322
+ https://203.0.113.42:9292/api/users/bob | jq
323
+
324
+ # Delete user
325
+ curl -s --cert client.p12:password \
326
+ -X DELETE \
327
+ https://203.0.113.42:9292/api/users/bob | jq
328
+
329
+ # Reset TOTP
330
+ curl -s --cert client.p12:password \
331
+ -X POST \
332
+ https://203.0.113.42:9292/api/users/bob/reset-totp | jq
333
+ ```
@@ -0,0 +1,342 @@
1
+ # Backup and Restore
2
+
3
+ > Protect your Lamaste installation by backing up critical configuration, state, and certificates.
4
+
5
+ ## In Plain English
6
+
7
+ A backup is a copy of everything that makes your Lamaste server work the way you set it up — your domain configuration, user accounts, encryption certificates, and tunnel definitions. If something goes wrong (accidental deletion, server failure, or provider issue), a backup lets you rebuild everything on a fresh server instead of starting from scratch.
8
+
9
+ Lamaste stores all its important data in a handful of directories. There is no database to dump. Everything is plain files: JSON, YAML, and certificate files. Backing up means copying these directories to a safe location.
10
+
11
+ ## For Users
12
+
13
+ ### Critical Files to Back Up
14
+
15
+ Lamaste's state is spread across four directories. All of them matter.
16
+
17
+ #### `/etc/lamalibre/lamaste/` — Configuration, State, and PKI
18
+
19
+ This is the most important directory. It contains:
20
+
21
+ | File | Purpose |
22
+ | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | `panel.json` | Server configuration (IP, domain, email, onboarding status) |
24
+ | `tunnels.json` | Tunnel definitions (subdomain, port, carrying agent, enabled state) |
25
+ | `sites.json` | Static site definitions (including custom-domain aliases) |
26
+ | `chisel-credentials.json` | Per-agent Chisel credentials. If lost, the panel mints a new credential for every active agent at its next start, and each agent's sync timer fetches it within about 30 seconds (the issue time changed) |
27
+ | `chisel-users` | Chisel authfile — derived; rewritten from the two files above, with mode `0640` and group `lamaste-chisel`, at every panel start |
28
+ | `chisel-sentinel` | Password of the no-grants sentinel Chisel user |
29
+ | `invitations.json` | Pending user invitations |
30
+ | `pki/revoked.json` | Revoked certificate tracking |
31
+ | `pki/ca.key` | Certificate Authority private key |
32
+ | `pki/ca.crt` | Certificate Authority certificate |
33
+ | `pki/client.key` | Client certificate private key |
34
+ | `pki/client.crt` | Client certificate |
35
+ | `pki/client.p12` | PKCS12 bundle (imported into browsers) |
36
+ | `pki/.p12-password` | Password for the PKCS12 bundle |
37
+ | `pki/self-signed.pem` | Self-signed TLS cert for IP:9292 access |
38
+ | `pki/self-signed-key.pem` | Self-signed TLS key for IP:9292 access |
39
+
40
+ **If you lose the PKI files**, every browser that imported the client certificate will need a new one. This is the single most important directory to back up.
41
+
42
+ #### `/etc/authelia/` — User Database and Authentication Config
43
+
44
+ | File | Purpose |
45
+ | ------------------- | --------------------------------------------------------- |
46
+ | `configuration.yml` | Authelia server configuration |
47
+ | `users.yml` | User accounts (usernames, bcrypt password hashes, groups) |
48
+ | `.secrets.json` | JWT secret, session secret, storage encryption key |
49
+ | `db.sqlite3` | Authelia session/state database (TOTP registrations) |
50
+
51
+ **If you lose the users database**, all user accounts and TOTP secrets are gone. Users will need to be recreated and re-enroll their authenticator apps.
52
+
53
+ #### `/etc/letsencrypt/` — TLS Certificates
54
+
55
+ | Path | Purpose |
56
+ | ---------------------- | ---------------------------------------- |
57
+ | `live/*/fullchain.pem` | Active certificate chain for each domain |
58
+ | `live/*/privkey.pem` | Private key for each domain |
59
+ | `renewal/*.conf` | Auto-renewal configuration per domain |
60
+ | `accounts/` | Let's Encrypt account credentials |
61
+
62
+ **If you lose Let's Encrypt certificates**, you can re-issue them (free, no limit for different domains), but it takes a few minutes per domain and is subject to rate limits (50 certificates per registered domain per week).
63
+
64
+ #### `/etc/nginx/` — Reverse Proxy Configuration
65
+
66
+ | Path | Purpose |
67
+ | -------------------------------------- | --------------------------------------------- |
68
+ | `sites-available/lamalibre-lamaste-*` | Lamaste vhost configurations |
69
+ | `sites-enabled/lamalibre-lamaste-*` | Symlinks to enabled vhosts |
70
+ | `snippets/lamalibre-lamaste-mtls.conf` | mTLS configuration snippet |
71
+ | `nginx.conf` | Main nginx configuration (usually unmodified) |
72
+
73
+ **If you lose nginx configs**, the panel can regenerate vhosts for tunnels and sites, but you will need to re-run the provisioning or manually recreate the base vhosts (panel, auth, tunnel).
74
+
75
+ ### Backup Commands
76
+
77
+ #### Full Backup (Recommended)
78
+
79
+ Run this on the droplet to create a single compressed archive of everything:
80
+
81
+ ```bash
82
+ # Create a timestamped backup
83
+ BACKUP_FILE="/root/lamaste-backup-$(date +%Y%m%d-%H%M%S).tar.gz"
84
+
85
+ sudo tar czf "$BACKUP_FILE" \
86
+ /etc/lamalibre/lamaste/ \
87
+ /etc/authelia/ \
88
+ /etc/letsencrypt/ \
89
+ /etc/nginx/sites-available/lamalibre-lamaste-* \
90
+ /etc/nginx/sites-enabled/lamalibre-lamaste-* \
91
+ /etc/nginx/snippets/lamalibre-lamaste-mtls.conf \
92
+ /etc/systemd/system/lamalibre-lamaste-serverd.service \
93
+ /etc/systemd/system/chisel.service \
94
+ /etc/systemd/system/authelia.service \
95
+ /etc/fail2ban/jail.d/lamaste.conf \
96
+ /etc/sudoers.d/lamaste \
97
+ 2>/dev/null
98
+
99
+ echo "Backup created: $BACKUP_FILE"
100
+ ls -lh "$BACKUP_FILE"
101
+ ```
102
+
103
+ #### Download the Backup to Your Local Machine
104
+
105
+ ```bash
106
+ # From your local machine
107
+ scp root@203.0.113.42:/root/lamaste-backup-*.tar.gz ~/backups/
108
+ ```
109
+
110
+ #### Minimal Backup (Essential Files Only)
111
+
112
+ If you just want the irreplaceable data (certificates and state):
113
+
114
+ ```bash
115
+ BACKUP_FILE="/root/lamaste-essential-$(date +%Y%m%d-%H%M%S).tar.gz"
116
+
117
+ sudo tar czf "$BACKUP_FILE" \
118
+ /etc/lamalibre/lamaste/ \
119
+ /etc/authelia/users.yml \
120
+ /etc/authelia/.secrets.json \
121
+ /etc/authelia/configuration.yml
122
+
123
+ echo "Essential backup created: $BACKUP_FILE"
124
+ ```
125
+
126
+ #### Automated Daily Backup
127
+
128
+ Create a cron job that runs daily and keeps the last 7 backups:
129
+
130
+ ```bash
131
+ sudo tee /etc/cron.daily/lamaste-backup << 'SCRIPT'
132
+ #!/bin/bash
133
+ BACKUP_DIR="/root/lamaste-backups"
134
+ mkdir -p "$BACKUP_DIR"
135
+
136
+ tar czf "$BACKUP_DIR/lamaste-$(date +%Y%m%d).tar.gz" \
137
+ /etc/lamalibre/lamaste/ \
138
+ /etc/authelia/ \
139
+ /etc/letsencrypt/ \
140
+ /etc/nginx/sites-available/lamalibre-lamaste-* \
141
+ /etc/nginx/snippets/lamalibre-lamaste-mtls.conf \
142
+ 2>/dev/null
143
+
144
+ # Keep only the last 7 backups
145
+ ls -t "$BACKUP_DIR"/lamaste-*.tar.gz | tail -n +8 | xargs -r rm --
146
+ SCRIPT
147
+
148
+ sudo chmod +x /etc/cron.daily/lamaste-backup
149
+ ```
150
+
151
+ ### Restore Procedure
152
+
153
+ #### Restoring to the Same Server
154
+
155
+ If you need to restore files on the same server (e.g., after accidental deletion):
156
+
157
+ **Step 1: Stop all services**
158
+
159
+ ```bash
160
+ sudo systemctl stop lamalibre-lamaste-serverd
161
+ sudo systemctl stop chisel
162
+ sudo systemctl stop authelia
163
+ sudo systemctl stop nginx
164
+ ```
165
+
166
+ **Step 2: Extract the backup**
167
+
168
+ ```bash
169
+ # Preview the contents first
170
+ tar tzf /root/lamaste-backup-20260313-103045.tar.gz | head -30
171
+
172
+ # Extract (overwrites existing files)
173
+ sudo tar xzf /root/lamaste-backup-20260313-103045.tar.gz -C /
174
+ ```
175
+
176
+ **Step 3: Fix permissions**
177
+
178
+ ```bash
179
+ sudo chown -R lamaste:lamaste /etc/lamalibre/lamaste/
180
+ sudo chmod 700 /etc/lamalibre/lamaste/pki/
181
+ sudo chmod 600 /etc/lamalibre/lamaste/pki/ca.key
182
+ sudo chmod 600 /etc/lamalibre/lamaste/pki/client.key
183
+ sudo chmod 600 /etc/lamalibre/lamaste/pki/client.p12
184
+ sudo chmod 600 /etc/lamalibre/lamaste/pki/.p12-password
185
+ sudo chmod 640 /etc/lamalibre/lamaste/panel.json
186
+ # Files the Chisel server (User=nobody, Group=lamaste-chisel) must read
187
+ sudo chgrp lamaste-chisel /etc/lamalibre/lamaste/chisel-users /etc/lamalibre/lamaste/chisel-server.key
188
+ sudo chmod 640 /etc/lamalibre/lamaste/chisel-users /etc/lamalibre/lamaste/chisel-server.key
189
+ # Authelia: the panel owns its configuration; Authelia (lamaste-authelia) reads it
190
+ # through the group and owns its database and notification file
191
+ sudo chown lamaste:lamaste-authelia /etc/authelia /etc/authelia/configuration.yml /etc/authelia/users.yml /etc/authelia/.secrets.json
192
+ sudo chmod 2770 /etc/authelia
193
+ sudo chmod 640 /etc/authelia/configuration.yml
194
+ sudo chmod 660 /etc/authelia/users.yml
195
+ sudo chmod 600 /etc/authelia/.secrets.json
196
+ sudo chown lamaste-authelia:lamaste-authelia /etc/authelia/db.sqlite3* /etc/authelia/notifications.txt
197
+ sudo chmod 600 /etc/authelia/db.sqlite3* /etc/authelia/notifications.txt
198
+ ```
199
+
200
+ A backup taken on a version before the privilege boundary holds root-owned Authelia files and older unit files. After restoring one, re-run `npx @lamalibre/create-lamaste@4.0.0 --yes`: the redeploy rewrites the `chisel` and `authelia` units and converts the ownership of `/etc/authelia`, the Chisel key and the PKI directory (see [Upgrades](upgrades.md)).
201
+
202
+ The panel re-applies the authfile's group and mode itself on every start, so a Chisel that fails to read it before the panel is up is started again by the panel's reconciliation.
203
+
204
+ **Step 4: Validate nginx configuration**
205
+
206
+ ```bash
207
+ sudo nginx -t
208
+ ```
209
+
210
+ If the test fails, check for missing certificate files referenced in the vhosts.
211
+
212
+ **Step 5: Restart services**
213
+
214
+ ```bash
215
+ sudo systemctl start nginx
216
+ sudo systemctl start authelia
217
+ sudo systemctl start chisel
218
+ sudo systemctl start lamalibre-lamaste-serverd
219
+ ```
220
+
221
+ **Step 6: Verify**
222
+
223
+ ```bash
224
+ sudo systemctl status nginx chisel authelia lamalibre-lamaste-serverd
225
+ curl -s http://127.0.0.1:3100/api/health
226
+ ```
227
+
228
+ #### Restoring to a New Server (Migration)
229
+
230
+ To move Lamaste to a new droplet:
231
+
232
+ **Step 1: Create a fresh droplet** (Ubuntu 24.04, same region if possible)
233
+
234
+ **Step 2: Run the installer**
235
+
236
+ ```bash
237
+ npx @lamalibre/create-lamaste@latest --yes
238
+ ```
239
+
240
+ **Step 3: Stop services**
241
+
242
+ ```bash
243
+ sudo systemctl stop lamalibre-lamaste-serverd chisel authelia nginx
244
+ ```
245
+
246
+ **Step 4: Upload and extract the backup**
247
+
248
+ ```bash
249
+ # From your local machine
250
+ scp ~/backups/lamaste-backup-20260313-103045.tar.gz root@<new-ip>:/root/
251
+
252
+ # On the new server
253
+ sudo tar xzf /root/lamaste-backup-20260313-103045.tar.gz -C /
254
+ ```
255
+
256
+ **Step 5: Update the IP address in panel.json**
257
+
258
+ The new server has a different IP. Update it:
259
+
260
+ ```bash
261
+ # Replace old IP with new IP
262
+ sudo sed -i 's/OLD_IP/NEW_IP/g' /etc/lamalibre/lamaste/panel.json
263
+ ```
264
+
265
+ **Step 6: Regenerate the self-signed certificate for the new IP**
266
+
267
+ ```bash
268
+ sudo openssl req -x509 -nodes -days 3650 \
269
+ -newkey rsa:2048 \
270
+ -keyout /etc/lamalibre/lamaste/pki/self-signed-key.pem \
271
+ -out /etc/lamalibre/lamaste/pki/self-signed.pem \
272
+ -subj "/CN=NEW_IP/O=Lamaste" \
273
+ -addext "subjectAltName=IP:NEW_IP"
274
+ ```
275
+
276
+ **Step 7: Update DNS records** to point to the new IP
277
+
278
+ **Step 8: Fix permissions (same as Step 3 above)**
279
+
280
+ **Step 9: Restart services**
281
+
282
+ ```bash
283
+ sudo systemctl daemon-reload
284
+ sudo systemctl start nginx authelia chisel lamalibre-lamaste-serverd
285
+ ```
286
+
287
+ **Step 10: Re-issue Let's Encrypt certificates** (they are bound to the server that responded to the ACME challenge)
288
+
289
+ If DNS is already pointing to the new server, the existing certificates will still work. When they are due for renewal, certbot will handle it automatically. If you need to force re-issuance:
290
+
291
+ ```bash
292
+ sudo certbot renew --force-renewal
293
+ ```
294
+
295
+ **Step 11: Verify everything works** through the browser at `https://<new-ip>:9292`
296
+
297
+ The existing client certificate (`.p12` file) will still work because the CA key was restored from the backup.
298
+
299
+ ## For Developers
300
+
301
+ ### State File Locations
302
+
303
+ All Lamaste state is stored as flat files with atomic writes:
304
+
305
+ | File | Format | Write Pattern |
306
+ | ----------------------------------------- | ------ | ------------------------------------------------------------- |
307
+ | `/etc/lamalibre/lamaste/panel.json` | JSON | Write `.tmp` then `rename()` |
308
+ | `/etc/lamalibre/lamaste/tunnels.json` | JSON | Write `.tmp`, `fsync()`, then `rename()` |
309
+ | `/etc/lamalibre/lamaste/sites.json` | JSON | Write `.tmp`, `fsync()`, then `rename()` |
310
+ | `/etc/lamalibre/lamaste/invitations.json` | JSON | Write `.tmp`, `fsync()`, then `rename()` |
311
+ | `/etc/authelia/users.yml` | YAML | Temp file in `/etc/authelia`, `fsync()`, `rename()` (no sudo) |
312
+
313
+ The atomic write pattern (write to temporary file, sync, rename) ensures that a crash during a write does not corrupt the primary file. The `rename()` system call is atomic on POSIX filesystems.
314
+
315
+ ### Backup Considerations
316
+
317
+ - **`/etc/lamalibre/lamaste/pki/ca.key`** is the root of trust. All client certificates are signed by this CA. If the CA key changes, all previously issued client certificates become invalid.
318
+ - **`/etc/authelia/.secrets.json`** contains the JWT secret. If this changes, all active Authelia sessions are invalidated and users must re-authenticate.
319
+ - **`/etc/authelia/db.sqlite3`** contains Authelia session data and TOTP registration states. It can be regenerated (users re-enroll TOTP), but this is disruptive.
320
+ - **Let's Encrypt account credentials** in `/etc/letsencrypt/accounts/` are tied to the email address used during registration. If lost, certbot creates a new account on next run.
321
+
322
+ ## Quick Reference
323
+
324
+ | Directory | Contains | Criticality |
325
+ | ----------------------------- | -------------------------- | --------------------------------- |
326
+ | `/etc/lamalibre/lamaste/` | Config, state, PKI | Critical — irreplaceable PKI keys |
327
+ | `/etc/authelia/` | User database, auth config | High — user accounts and secrets |
328
+ | `/etc/letsencrypt/` | TLS certificates | Medium — can be re-issued |
329
+ | `/etc/nginx/sites-available/` | Vhost configs | Low — can be regenerated |
330
+
331
+ | Backup Command | What It Does |
332
+ | -------------------------------------------------------------------------------- | ------------------- |
333
+ | `tar czf backup.tar.gz /etc/lamalibre/lamaste/ /etc/authelia/ /etc/letsencrypt/` | Full config backup |
334
+ | `scp root@IP:/root/backup.tar.gz ~/backups/` | Download backup |
335
+ | `tar xzf backup.tar.gz -C /` | Restore from backup |
336
+
337
+ | Post-Restore Step | Command |
338
+ | -------------------- | ------------------------------------------------------------------------------------------------ |
339
+ | Fix PKI permissions | `sudo chmod 700 /etc/lamalibre/lamaste/pki/ && sudo chmod 600 /etc/lamalibre/lamaste/pki/ca.key` |
340
+ | Validate nginx | `sudo nginx -t` |
341
+ | Restart all services | `sudo systemctl restart nginx authelia chisel lamalibre-lamaste-serverd` |
342
+ | Verify health | `curl -s http://127.0.0.1:3100/api/health` |