@lamalibre/create-lamaste 4.4.1 → 4.6.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 (170) hide show
  1. package/package.json +2 -2
  2. package/scripts/lamaste-priv +234 -18
  3. package/src/index.js +1 -0
  4. package/src/lib/backend-cookie.js +65 -0
  5. package/src/lib/ip-panel-vhost.js +208 -0
  6. package/src/lib/service-config.js +5 -3
  7. package/src/tasks/nginx.js +18 -135
  8. package/src/tasks/redeploy.js +39 -1
  9. package/vendor/docs/00-introduction/how-it-works.md +2 -2
  10. package/vendor/docs/00-introduction/quickstart.md +5 -5
  11. package/vendor/docs/00-introduction/what-is-lamaste.md +5 -5
  12. package/vendor/docs/01-concepts/authentication.md +1 -1
  13. package/vendor/docs/01-concepts/authorization.md +66 -48
  14. package/vendor/docs/01-concepts/certificates.md +3 -3
  15. package/vendor/docs/01-concepts/mtls.md +2 -2
  16. package/vendor/docs/01-concepts/security-model.md +70 -33
  17. package/vendor/docs/01-concepts/tickets.md +2 -2
  18. package/vendor/docs/01-concepts/tunneling.md +4 -4
  19. package/vendor/docs/02-guides/agent-setup.md +5 -5
  20. package/vendor/docs/02-guides/certificate-management.md +2 -2
  21. package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
  22. package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
  23. package/vendor/docs/02-guides/installation.md +3 -3
  24. package/vendor/docs/02-guides/managing-users.md +113 -25
  25. package/vendor/docs/02-guides/onboarding.md +2 -2
  26. package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
  27. package/vendor/docs/02-guides/static-sites.md +4 -0
  28. package/vendor/docs/03-architecture/installer-flow.md +2 -2
  29. package/vendor/docs/03-architecture/installer.md +4 -4
  30. package/vendor/docs/03-architecture/management-flow.md +3 -1
  31. package/vendor/docs/03-architecture/nginx-configuration.md +229 -24
  32. package/vendor/docs/03-architecture/overview.md +13 -17
  33. package/vendor/docs/03-architecture/panel-client.md +6 -6
  34. package/vendor/docs/03-architecture/panel-server.md +3 -2
  35. package/vendor/docs/03-architecture/state-management.md +4 -8
  36. package/vendor/docs/03-architecture/system-overview.md +1 -1
  37. package/vendor/docs/04-api-reference/agent-hosted-plugins.md +378 -34
  38. package/vendor/docs/04-api-reference/app-host.md +3 -3
  39. package/vendor/docs/04-api-reference/gatekeeper.md +78 -5
  40. package/vendor/docs/04-api-reference/lifecycle-hooks.md +14 -12
  41. package/vendor/docs/04-api-reference/onboarding.md +1 -1
  42. package/vendor/docs/04-api-reference/registry-and-oidc.md +3 -3
  43. package/vendor/docs/04-api-reference/sites.md +4 -0
  44. package/vendor/docs/04-api-reference/tunnels.md +87 -47
  45. package/vendor/docs/04-api-reference/users.md +241 -45
  46. package/vendor/docs/05-operations/backup-and-restore.md +35 -26
  47. package/vendor/docs/05-operations/monitoring.md +39 -0
  48. package/vendor/docs/05-operations/uninstalling.md +2 -1
  49. package/vendor/docs/05-operations/upgrades.md +24 -6
  50. package/vendor/docs/06-reference/config-files.md +100 -202
  51. package/vendor/docs/06-reference/glossary.md +3 -3
  52. package/vendor/docs/06-reference/installer-flags.md +13 -13
  53. package/vendor/docs/06-reference/ports-and-services.md +25 -26
  54. package/vendor/docs/06-reference/troubleshooting.md +8 -8
  55. package/vendor/docs/gatekeeper-design.md +10 -8
  56. package/vendor/docs/identity-directory-design.md +48 -14
  57. package/vendor/docs/package.json +1 -1
  58. package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
  59. package/vendor/gatekeeper/dist/cli/index.js +0 -0
  60. package/vendor/gatekeeper/dist/index.d.ts +4 -3
  61. package/vendor/gatekeeper/dist/index.d.ts.map +1 -1
  62. package/vendor/gatekeeper/dist/index.js +4 -2
  63. package/vendor/gatekeeper/dist/index.js.map +1 -1
  64. package/vendor/gatekeeper/dist/lib/authz.d.ts +9 -0
  65. package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -1
  66. package/vendor/gatekeeper/dist/lib/authz.js +31 -0
  67. package/vendor/gatekeeper/dist/lib/authz.js.map +1 -1
  68. package/vendor/gatekeeper/dist/lib/constants.d.ts +5 -2
  69. package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -1
  70. package/vendor/gatekeeper/dist/lib/constants.js +5 -2
  71. package/vendor/gatekeeper/dist/lib/constants.js.map +1 -1
  72. package/vendor/gatekeeper/dist/lib/grants.d.ts +6 -0
  73. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -1
  74. package/vendor/gatekeeper/dist/lib/grants.js +6 -1
  75. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -1
  76. package/vendor/gatekeeper/dist/lib/groups.d.ts +5 -0
  77. package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -1
  78. package/vendor/gatekeeper/dist/lib/groups.js +5 -1
  79. package/vendor/gatekeeper/dist/lib/groups.js.map +1 -1
  80. package/vendor/gatekeeper/dist/lib/principals.d.ts +29 -0
  81. package/vendor/gatekeeper/dist/lib/principals.d.ts.map +1 -0
  82. package/vendor/gatekeeper/dist/lib/principals.js +67 -0
  83. package/vendor/gatekeeper/dist/lib/principals.js.map +1 -0
  84. package/vendor/gatekeeper/dist/lib/tunnels.d.ts +10 -0
  85. package/vendor/gatekeeper/dist/lib/tunnels.d.ts.map +1 -0
  86. package/vendor/gatekeeper/dist/lib/tunnels.js +56 -0
  87. package/vendor/gatekeeper/dist/lib/tunnels.js.map +1 -0
  88. package/vendor/gatekeeper/dist/server/index.d.ts +2 -1
  89. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
  90. package/vendor/gatekeeper/dist/server/index.js +49 -56
  91. package/vendor/gatekeeper/dist/server/index.js.map +1 -1
  92. package/vendor/gatekeeper/dist/server/routes/authz.d.ts +15 -1
  93. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
  94. package/vendor/gatekeeper/dist/server/routes/authz.js +332 -104
  95. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
  96. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -1
  97. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +61 -4
  98. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -1
  99. package/vendor/gatekeeper/dist/server/routes/principals.d.ts +3 -0
  100. package/vendor/gatekeeper/dist/server/routes/principals.d.ts.map +1 -0
  101. package/vendor/gatekeeper/dist/server/routes/principals.js +27 -0
  102. package/vendor/gatekeeper/dist/server/routes/principals.js.map +1 -0
  103. package/vendor/gatekeeper/package.json +2 -2
  104. package/vendor/server/bin/lamaste-server.js +15 -1
  105. package/vendor/server/package.json +2 -2
  106. package/vendor/server/src/commands/plugin-identity.js +335 -0
  107. package/vendor/server/src/commands/plugin-web-surface.js +269 -0
  108. package/vendor/server/src/commands/plugins.js +156 -11
  109. package/vendor/server/src/commands/registry-credential.js +2 -1
  110. package/vendor/server/src/commands/sites.js +15 -1
  111. package/vendor/server/src/commands/tunnels.js +102 -2
  112. package/vendor/server/src/commands/uninstall.js +3 -1
  113. package/vendor/server/src/commands/users.js +358 -0
  114. package/vendor/server/src/plugin-approval.js +186 -5
  115. package/vendor/server/src/plugin-identity.js +382 -0
  116. package/vendor/server/src/plugin-web-surface.js +307 -0
  117. package/vendor/server-ui/dist/assets/index-BZwQ_aQc.css +1 -0
  118. package/vendor/server-ui/dist/assets/index-BvEfCZYS.js +84 -0
  119. package/vendor/server-ui/dist/index.html +2 -2
  120. package/vendor/serverd/package.json +2 -2
  121. package/vendor/serverd/src/index.js +34 -5
  122. package/vendor/serverd/src/lib/audience.js +576 -0
  123. package/vendor/serverd/src/lib/authelia.js +78 -7
  124. package/vendor/serverd/src/lib/chisel-runtime.js +18 -6
  125. package/vendor/serverd/src/lib/chisel.js +10 -2
  126. package/vendor/serverd/src/lib/config.js +68 -43
  127. package/vendor/serverd/src/lib/directory.js +1113 -0
  128. package/vendor/serverd/src/lib/entitlements.js +517 -0
  129. package/vendor/serverd/src/lib/gatekeeper-client.js +226 -10
  130. package/vendor/serverd/src/lib/hook-pipelines.js +21 -3
  131. package/vendor/serverd/src/lib/identity-sync.js +905 -0
  132. package/vendor/serverd/src/lib/migrations/state/0013_identity_directory.sql +158 -0
  133. package/vendor/serverd/src/lib/migrations/state/0014_identity_ledger.sql +20 -0
  134. package/vendor/serverd/src/lib/migrations/state/0015_plugin_web_surface.sql +14 -0
  135. package/vendor/serverd/src/lib/mtls.js +6 -2
  136. package/vendor/serverd/src/lib/nginx.js +546 -48
  137. package/vendor/serverd/src/lib/plugin-hooks.js +127 -45
  138. package/vendor/serverd/src/lib/plugin-hosting.js +33 -5
  139. package/vendor/serverd/src/lib/plugin-lifecycle.js +227 -13
  140. package/vendor/serverd/src/lib/plugin-packages.js +74 -2
  141. package/vendor/serverd/src/lib/plugins.js +95 -7
  142. package/vendor/serverd/src/lib/registry-credential.js +9 -6
  143. package/vendor/serverd/src/lib/request-logging.js +19 -0
  144. package/vendor/serverd/src/lib/retained-data.js +50 -4
  145. package/vendor/serverd/src/lib/state.js +36 -0
  146. package/vendor/serverd/src/lib/tunnel-hooks.js +4 -1
  147. package/vendor/serverd/src/lib/user-access-session.js +21 -11
  148. package/vendor/serverd/src/lib/user-caller.js +67 -0
  149. package/vendor/serverd/src/lib/user-surface.js +754 -0
  150. package/vendor/serverd/src/lib/vhost-reconcile.js +137 -33
  151. package/vendor/serverd/src/lib/web-surface.js +766 -0
  152. package/vendor/serverd/src/middleware/user-access-session.js +9 -0
  153. package/vendor/serverd/src/routes/agent-forwarder.js +4 -2
  154. package/vendor/serverd/src/routes/invite.js +53 -12
  155. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +107 -16
  156. package/vendor/serverd/src/routes/management/identity-directory.js +79 -0
  157. package/vendor/serverd/src/routes/management/invitations.js +78 -68
  158. package/vendor/serverd/src/routes/management/plugin-identity.js +221 -0
  159. package/vendor/serverd/src/routes/management/plugin-web-surface.js +151 -0
  160. package/vendor/serverd/src/routes/management/plugins.js +2 -0
  161. package/vendor/serverd/src/routes/management/registry-credential.js +1 -1
  162. package/vendor/serverd/src/routes/management/sites.js +143 -65
  163. package/vendor/serverd/src/routes/management/tunnels.js +109 -22
  164. package/vendor/serverd/src/routes/management/users.js +290 -208
  165. package/vendor/serverd/src/routes/management.js +6 -0
  166. package/vendor/serverd/src/routes/onboarding/provision.js +14 -1
  167. package/vendor/serverd/src/routes/user-access.js +17 -4
  168. package/vendor/serverd/src/routes/user-surface.js +69 -0
  169. package/vendor/server-ui/dist/assets/index-25wo_8av.css +0 -1
  170. package/vendor/server-ui/dist/assets/index-roDHz3dM.js +0 -84
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## In Plain English
6
6
 
7
- Every tunneled app in Lamaste is protected by Authelia, a login system that requires a username, password, and a one-time code from an authenticator app. When you create a user in the Lamaste panel, that person can log in to any of your tunneled apps. This guide covers the full lifecycle: creating users, enrolling them in two-factor authentication, changing passwords, and safely removing users.
7
+ Every tunneled app in Lamaste is protected by Authelia, a login system that requires a username, password, and a one-time code from an authenticator app. When you create a user in the Lamaste panel, that person can log in to any of your tunneled apps. This guide covers the full lifecycle: creating users, enrolling them in two-factor authentication, changing passwords, and offboarding them — disabling, deleting and erasing users. The same users can be given roles in agent-hosted plugins ([Agent-Hosted Plugins](../04-api-reference/agent-hosted-plugins.md#users-and-identity)), which know them by a stable user ID.
8
8
 
9
9
  ## Prerequisites
10
10
 
@@ -20,7 +20,7 @@ Log in to the Lamaste admin panel at `https://<ip>:9292`.
20
20
 
21
21
  Click **Users** in the sidebar navigation.
22
22
 
23
- You see a list of existing users. After onboarding, there is one user: `admin` — the account created during provisioning.
23
+ You see a list of existing users — each with its status (**active** or **disabled**) and its **user ID**, the stable id plugins know the user by (click it to copy). After onboarding, there is one user: `admin` — the account created during provisioning.
24
24
 
25
25
  ### 2. Add a New User
26
26
 
@@ -43,7 +43,7 @@ Fill in the form and click **Create User**.
43
43
  1. The panel validates all fields against the schema.
44
44
  2. The password is hashed with **bcrypt** (not argon2id — argon2id uses ~93 MB per hash and causes OOM kills on 512 MB droplets).
45
45
  3. The user record is written to `/etc/authelia/users.yml` using an atomic write (write to temp file, then rename) to prevent corruption.
46
- 4. Authelia is reloaded to pick up the new user.
46
+ 4. The user directory gives the user a `userId` (a UUID v4), then Authelia is reloaded to pick up the new user.
47
47
 
48
48
  **Expected result:** The new user appears in the list. Share the username and password with the person — they need these to log in to your tunneled apps.
49
49
 
@@ -107,22 +107,85 @@ To change a user's display name, email, or group membership:
107
107
 
108
108
  Only the fields you change are updated — the password and TOTP secret remain the same unless explicitly changed.
109
109
 
110
- ### 7. Remove a User
110
+ ### 7. Disable a User (Offboarding)
111
+
112
+ When someone leaves, **disable** them first — it is immediate and reversible:
113
+
114
+ 1. Go to the **Users** page.
115
+ 2. Click **Disable** on the user and confirm.
116
+
117
+ The user's status turns **disabled**. From that moment:
118
+
119
+ - they can no longer sign in to Authelia (Authelia's `disabled: true`);
120
+ - every agent-hosted plugin's user surface refuses them (`403`) — the panel's directory is updated before Authelia restarts;
121
+ - every plugin they use sees them as `disabled` in its audience (its agent half is told within seconds while it is online);
122
+ - their desktop app sessions end and their registry (Feria) tokens are revoked.
123
+
124
+ Their `userId`, grants and group memberships stay, so **Enable** brings everything back as it was (their desktop app signs in again). The last enabled member of the `admins` group cannot be disabled — the panel refuses with "Cannot disable the last enabled administrator". A user disabled by editing `users.yml` directly is offboarded the same way within a minute, when the panel notices.
125
+
126
+ If Authelia rewrites `users.yml` at the very moment you disable someone (a password reset it handles writes the whole file), the panel notices after Authelia restarts and disables the user again; should that not stick either, the panel says the change did not persist instead of reporting success — check the user and disable them again.
127
+
128
+ ```bash
129
+ lamaste-server users disable alice
130
+ lamaste-server users enable alice
131
+ ```
132
+
133
+ ### 8. Delete a User
111
134
 
112
135
  1. Go to the **Users** page.
113
136
  2. Click the **Delete** button next to the user you want to remove.
114
- 3. Confirm the deletion.
137
+ 3. Leave **Also erase personal data** unchecked to keep a tombstone (see below), or check it to erase at once.
138
+ 4. Confirm the deletion.
115
139
 
116
140
  **Safety rule:** You cannot delete the last user. Lamaste enforces this to prevent locking everyone out of the tunneled apps. If you try to delete the last user, the panel returns an error: "Cannot delete the last user."
117
141
 
118
142
  **What happens behind the scenes:**
119
143
 
120
- 1. The panel removes the user from `users.yml` (atomic write).
121
- 2. Authelia is reloaded.
122
- 3. Any active sessions for that user expire naturally (Authelia does not have a "force logout" mechanism, but sessions have a timeout).
144
+ 1. The panel removes the user from `users.yml` (atomic write, under the panel's one lock for user changes) and restarts Authelia.
145
+ 2. The user directory keeps a **tombstone**: the user's `userId` and their last username, display name and email, marked `removed`. Plugins that had the user as a member are told the user was deleted and keep what they last received, so a plugin's history — "approved by Alice Smith" — stays readable.
146
+ 3. Gatekeeper drops every grant naming the username and its membership of every Lamaste group. If Gatekeeper is unreachable, the panel says so and retries every minute; until it succeeds, the username holds no role in any plugin and cannot be given to a new user.
147
+ 4. Authelia deletes the user's second factors — their TOTP configuration and WebAuthn security keys.
148
+ 5. The user's registry tokens, desktop sessions and legacy plugin-access grants end.
149
+
150
+ A user removed from `users.yml` by hand is offboarded the same way within a minute, when the panel notices.
123
151
 
124
152
  After deletion, the user can no longer log in to any tunneled app.
125
153
 
154
+ ```bash
155
+ lamaste-server users delete alice # keeps the tombstone
156
+ lamaste-server users delete alice --erase # and erases it in the same step
157
+ ```
158
+
159
+ ### 9. Erase a Deleted User
160
+
161
+ A tombstone keeps a name and an address. When a person asks for their personal data to be removed, **erase** it:
162
+
163
+ 1. Go to the **Users** page and scroll to **Deleted users** — every tombstone the directory keeps, with its `userId`, when it was removed and whether Gatekeeper's cleanup is still pending.
164
+ 2. Click **Erase** on the user and confirm.
165
+
166
+ Erasing deletes the user's invitations, Gatekeeper's access-request log entries for them and any second factors Authelia still holds, then scrubs the tombstone to the bare `userId`. Every plugin that ever had the user as a member is sent an erasure: its agent half scrubs its own row and anonymises what the plugin keeps about the user — and never takes the user back afterwards, whatever it is sent later. Until each plugin has acknowledged it, **Deleted users** lists that plugin under the user as a pending erasure — seconds while the hosting agent is online, or when it reconnects. A plugin that was uninstalled while an agent keeps its data, or that no longer declares users, is listed too, marked as not taking the feed: the erasure reaches its data when it comes back, or goes with the data when you purge it (Plugins page, Retained data).
167
+
168
+ Erasure cannot be undone. It cannot reach what Authelia keeps on its own — the user's OpenID Connect identity (the reason a username that ever signed in to an OpenID Connect application cannot be given to anyone else) and its authentication log — nor nginx's access logs, the system journal's older entries or backups taken before it ([what the logs keep](../05-operations/monitoring.md#logs-and-personal-data)).
169
+
170
+ ```bash
171
+ lamaste-server users directory # every user the directory knows, tombstones and pending erasures
172
+ lamaste-server users erase 7d9e1a2b-3c4d-4e5f-8a6b-7c8d9e0f1a2b
173
+ ```
174
+
175
+ ### Disable, delete or erase?
176
+
177
+ | Action | Can sign in | Plugins see | Reversible | Use when |
178
+ | ----------- | ----------- | -------------------------------------- | ---------- | --------------------------------------------- |
179
+ | **Disable** | No | The user, `disabled` | Yes | Someone leaves or must be locked out now |
180
+ | **Delete** | No | A removed user, last name kept | No | The account is gone for good; history matters |
181
+ | **Erase** | — | A bare `userId`; their data anonymised | No | The person asked for their data to be removed |
182
+
183
+ ### Re-created usernames
184
+
185
+ A username that comes back after a deletion — re-created by hand, through an invitation, or edited into `users.yml` — is a **new user with a new `userId`**. Plugins key their data on the `userId`, so the new person sees nothing of the old one's. Before the panel creates the user, Gatekeeper drops everything it holds under the username and Authelia deletes any second factors under it — every time, even when nothing should be left: no grant, group membership, plugin role, TOTP or security key passes from one person to the next, nor a desktop session. If that cannot be done now (Gatekeeper or Authelia's storage unavailable), creating the user is refused with a message saying so — try again later. While a deleted user's username is not held by anyone, nothing can be granted to it (refused with "username_deleted").
186
+
187
+ **A username that has signed in to an OpenID Connect application cannot be reused.** When a user signs in to an application through Lamaste's OpenID Connect provider (a plugin's login, a registry), Authelia gives them an identifier tied to their username, which it cannot delete. A new person under that username would be the old person to every such application — so the panel refuses to create a user, or accept an invitation, under it (`username_has_oidc_identity`): choose another username, such as `alice2`.
188
+
126
189
  ## For Developers
127
190
 
128
191
  ### User Storage
@@ -150,13 +213,34 @@ All writes to this file are atomic: write to a temp file, then rename. This prev
150
213
 
151
214
  ### API Endpoints
152
215
 
153
- | Method | Path | Purpose |
154
- | -------- | --------------------------------- | ----------------------------------------------------------- |
155
- | `GET` | `/api/users` | List all users (sorted alphabetically, no sensitive fields) |
156
- | `POST` | `/api/users` | Create a new user |
157
- | `PUT` | `/api/users/:username` | Update user fields |
158
- | `DELETE` | `/api/users/:username` | Delete a user (not the last one) |
159
- | `POST` | `/api/users/:username/reset-totp` | Generate a new TOTP secret |
216
+ | Method | Path | Purpose |
217
+ | -------- | --------------------------------------- | --------------------------------------------------------------------------------------- |
218
+ | `GET` | `/api/users` | List all users (sorted alphabetically, no sensitive fields), with `userId` and `status` |
219
+ | `POST` | `/api/users` | Create a new user |
220
+ | `PUT` | `/api/users/:username` | Update user fields; `{ "disabled": true \| false }` disables or enables |
221
+ | `DELETE` | `/api/users/:username[?erase=true]` | Delete a user (not the last one); a tombstone stays unless erased |
222
+ | `POST` | `/api/users/:username/reset-totp` | Generate a new TOTP secret |
223
+ | `GET` | `/api/identity/directory` | Every user the directory knows, tombstones and pending erasures included |
224
+ | `POST` | `/api/identity/directory/:userId/erase` | Erase a removed user |
225
+
226
+ Details: [Users API](../04-api-reference/users.md).
227
+
228
+ ### The User Directory
229
+
230
+ Authelia's `users.yml` stays Authelia's credential file; the panel's `state.db` holds the **directory** beside it: a `userId` per user, the status, and the tombstones of deleted users. Every write of `users.yml` the panel makes goes through one lock that brings the directory in line before Authelia restarts. The panel also reads `users.yml` at start and every minute — never writing it — and adopts what changed outside Lamaste (Authelia's own password reset rewrites the file): a new username gets a new `userId`, a changed name, address or `disabled` flag is taken over, and a user gone from the file (seen in two reads) becomes a tombstone marked `deleted-outside`. Audit entries name users by `userId` only.
231
+
232
+ ### The CLI
233
+
234
+ Every command goes through the running panel with the admin certificate (`--json` prints NDJSON):
235
+
236
+ ```bash
237
+ lamaste-server users list # username, status, userId, groups
238
+ lamaste-server users disable <username>
239
+ lamaste-server users enable <username>
240
+ lamaste-server users delete <username> [--erase]
241
+ lamaste-server users erase <userId>
242
+ lamaste-server users directory # tombstones, erasures, pending cleanups and erasures
243
+ ```
160
244
 
161
245
  ### Create User Request
162
246
 
@@ -242,14 +326,18 @@ Authelia reads `users.yml` on restart. If the restart fails, a warning is logged
242
326
  | **Edit user** | Users page, "Edit" on user, modify fields, "Save" |
243
327
  | **Change password** | Edit user, enter new password, "Save" |
244
328
  | **Reset TOTP** | Users page, "Reset TOTP" on user, share QR code |
329
+ | **Disable user** | Users page, "Disable" on user, confirm |
245
330
  | **Delete user** | Users page, "Delete" on user, confirm |
246
-
247
- | Constraint | Value |
248
- | ---------------- | ----------------------------------------------- |
249
- | Username format | `^[a-z0-9][a-z0-9_-]*$` (users and invitations) |
250
- | Username length | 2-32 characters |
251
- | Password length | 8-128 characters |
252
- | Password hashing | bcrypt (cost 12) |
253
- | Minimum users | 1 (cannot delete last user) |
254
- | TOTP algorithm | SHA1, 6 digits, 30-second period |
255
- | User file | `/etc/authelia/users.yml` |
331
+ | **Erase user** | Users page, Deleted users, "Erase", confirm |
332
+
333
+ | Constraint | Value |
334
+ | ---------------- | ----------------------------------------------------------- |
335
+ | Username format | `^[a-z0-9][a-z0-9_-]*$` (users and invitations) |
336
+ | Username length | 2-32 characters |
337
+ | Password length | 8-128 characters |
338
+ | Password hashing | bcrypt (cost 12) |
339
+ | Minimum users | 1 (cannot delete last user) |
340
+ | Administrators | The last enabled `admins` member cannot be disabled |
341
+ | User ID | UUID v4, never reused; a re-created username gets a new one |
342
+ | TOTP algorithm | SHA1, 6 digits, 30-second period |
343
+ | User file | `/etc/authelia/users.yml` |
@@ -113,7 +113,7 @@ Chisel is the tunneling engine. The installer already put the pinned Chisel rele
113
113
 
114
114
  Authelia provides two-factor authentication for your tunneled apps. The installer already put the pinned Authelia release, its systemd unit and its `lamaste-authelia` service account in place; the provisioner checks the binary is there, writes the configuration file (using your domain), creates the initial admin user with a random password, and starts the service. Authelia listens on `127.0.0.1:9091`.
115
115
 
116
- Onboarding downloads nothing. If either task fails with "… is not installed. Run `npx @lamalibre/create-lamaste@4.4.1` on the server to repair the installation.", re-run the installer on the server (it is idempotent) and start provisioning again.
116
+ Onboarding downloads nothing. If either task fails with "… is not installed. Run `npx @lamalibre/create-lamaste@4.6.0` on the server to repair the installation.", re-run the installer on the server (it is idempotent) and start provisioning again.
117
117
 
118
118
  Authelia uses **bcrypt** for password hashing (not argon2id) because argon2id uses approximately 93 MB per hash, which causes out-of-memory kills on a 512 MB droplet.
119
119
 
@@ -213,7 +213,7 @@ Each provisioning task calls library functions in `packages/lamaste-serverd/src/
213
213
 
214
214
  The DNS check resolves these hostnames using Node's `dns.resolve4()`:
215
215
 
216
- 1. `panel.example.com`, `auth.example.com`, `tunnel.example.com` — must resolve to the server IP (required; `CORE_SUBDOMAINS` in `@lamalibre/lamaste@4.4.1`)
216
+ 1. `panel.example.com`, `auth.example.com`, `tunnel.example.com` — must resolve to the server IP (required; `CORE_SUBDOMAINS` in `@lamalibre/lamaste@4.6.0`)
217
217
  2. `test-lamaste-check.example.com` — tests wildcard resolution (optional)
218
218
  3. `example.com` — reported, not required
219
219
 
@@ -27,7 +27,7 @@ Using the desktop app's cloud provisioning wizard:
27
27
 
28
28
  - Enter your DigitalOcean API token
29
29
  - Select a region and droplet size
30
- - The wizard calls `@lamalibre/lamaste-cloud@4.4.1` to create the droplet
30
+ - The wizard calls `@lamalibre/lamaste-cloud@4.6.0` to create the droplet
31
31
  - Progress streams back as NDJSON events in the desktop UI
32
32
  - Complete onboarding in the browser panel
33
33
 
@@ -96,7 +96,7 @@ The Sync server now runs as a local service on the Mac. It operates independentl
96
96
 
97
97
  ### 5. Register Sync Scopes and Instance on Lamaste
98
98
 
99
- The Sync server detects that the Mac has a Lamaste agent certificate. Using that cert (mTLS), it registers a ticket scope and instance with the Lamaste panel. This is handled automatically by `TicketInstanceManager` from `@lamalibre/lamaste-tickets@4.4.1` — the admin does not need to do anything.
99
+ The Sync server detects that the Mac has a Lamaste agent certificate. Using that cert (mTLS), it registers a ticket scope and instance with the Lamaste panel. This is handled automatically by `TicketInstanceManager` from `@lamalibre/lamaste-tickets@4.6.0` — the admin does not need to do anything.
100
100
 
101
101
  ```mermaid
102
102
  sequenceDiagram
@@ -181,6 +181,10 @@ The file browser lets you:
181
181
 
182
182
  The Let's Encrypt certificate is left in place (certbot manages its lifecycle).
183
183
 
184
+ ### Sites that belong to a plugin
185
+
186
+ An agent-hosted plugin can own a site: its **web surface**, where the relay serves the application its users open, from the files its approved version ships ([Web surface](../04-api-reference/agent-hosted-plugins.md#web-surface)). Such a site is listed on the Sites page with the plugin it belongs to, but it is managed on the plugin's card — no uploads, no settings, no deletion here, and `lamaste-agent deploy` refuses it: its files change only with an approved version of the plugin.
187
+
184
188
  ## For Developers
185
189
 
186
190
  ### Site Data Model
@@ -1,6 +1,6 @@
1
1
  # Installer Flow
2
2
 
3
- ## What `npx @lamalibre/create-lamaste@4.4.1` Does
3
+ ## What `npx @lamalibre/create-lamaste@4.6.0` Does
4
4
 
5
5
  The installer is a **zero-prompt, fire-and-forget** command. It assumes:
6
6
 
@@ -13,7 +13,7 @@ It requires **no user input**. All configuration happens later through the brows
13
13
  ## Execution Flow
14
14
 
15
15
  ```
16
- $ npx @lamalibre/create-lamaste@4.4.1
16
+ $ npx @lamalibre/create-lamaste@4.6.0
17
17
 
18
18
  Lamaste Installer v1.0.0
19
19
 
@@ -4,14 +4,14 @@
4
4
 
5
5
  ## In Plain English
6
6
 
7
- When you run `npx @lamalibre/create-lamaste@4.4.1` on a fresh VPS, the installer takes over and does everything automatically: hardens the OS, installs dependencies, generates security certificates, configures nginx, deploys the panel, and starts the service. It prints a summary with instructions for downloading your client certificate and accessing the panel. No prompts, no questions, no configuration files to edit.
7
+ When you run `npx @lamalibre/create-lamaste@4.6.0` on a fresh VPS, the installer takes over and does everything automatically: hardens the OS, installs dependencies, generates security certificates, configures nginx, deploys the panel, and starts the service. It prints a summary with instructions for downloading your client certificate and accessing the panel. No prompts, no questions, no configuration files to edit.
8
8
 
9
9
  The installer is deliberately limited in scope. It sets up just enough to serve the panel UI over a secure connection. Everything else — domain configuration, tunnel setup, user management — happens through the browser-based onboarding wizard after the installer finishes.
10
10
 
11
11
  ## Overview
12
12
 
13
13
  ```
14
- npx @lamalibre/create-lamaste@4.4.1
14
+ npx @lamalibre/create-lamaste@4.6.0
15
15
  │
16
16
  ├── bin/create-lamaste.js ← CLI entry point
17
17
  │ └── calls main() from src/index.js
@@ -181,7 +181,7 @@ Six task groups run sequentially through Listr2:
181
181
  **Chisel and Authelia** (`lib/relay-services.js`, run on install and on every redeploy):
182
182
 
183
183
  - Creates the `lamaste-chisel` group (and adds `lamaste` to it) and the `lamaste-authelia` system account (no login shell, no home)
184
- - Installs Chisel 1.12.0 and Authelia 4.39.28 to `/usr/local/bin` (`lib/binaries.js`): downloaded from fixed release URLs, verified against the SHA-256 digests pinned in `@lamalibre/lamaste@4.4.1` (`CHISEL_RELEASE`, `AUTHELIA_RELEASE`) before anything is unpacked, staged in a `mkdtemp` directory and renamed into place. A binary already at the pinned version is left alone; an Authelia **newer** than the pin is kept, never downgraded (its database may use a schema the pinned release cannot read)
184
+ - Installs Chisel 1.12.0 and Authelia 4.39.28 to `/usr/local/bin` (`lib/binaries.js`): downloaded from fixed release URLs, verified against the SHA-256 digests pinned in `@lamalibre/lamaste@4.6.0` (`CHISEL_RELEASE`, `AUTHELIA_RELEASE`) before anything is unpacked, staged in a `mkdtemp` directory and renamed into place. A binary already at the pinned version is left alone; an Authelia **newer** than the pin is kept, never downgraded (its database may use a schema the pinned release cannot read)
185
185
  - Writes `/etc/systemd/system/chisel.service` (`User=nobody`, `Group=lamaste-chisel`) and `/etc/systemd/system/authelia.service` (`User=lamaste-authelia`, `UMask=027`, `Environment=X_AUTHELIA_CONFIG_FILTERS=template`, `ProtectSystem=strict` with `ReadWritePaths=/etc/authelia /var/log/authelia`, `SystemCallFilter=@system-service`, empty `CapabilityBoundingSet`, `NoNewPrivileges`); both are enabled and started later by onboarding
186
186
  - Generates the OpenID Connect issuer key `/etc/authelia/oidc-issuer.pem` if it is missing (`lib/oidc-issuer-key.js`): RSA 4096 generated in the installer process, written by a child process running as `lamaste-authelia` (`wx`, 0600, renamed into place) so root never writes through `/etc/authelia`. The panel references it from Authelia's configuration with a `{{ secret "…" }}` template expression — which is why the unit and `lamaste-priv authelia-totp` set the `template` configuration filter — and can never read it. A key owned by anyone else is replaced; one owned by the Authelia account is kept (its mode fixed through a descriptor if it drifted)
187
187
  - Sets ownership (`lib/ownership.js`): `/var/www/lamaste` `lamaste:www-data 2750`; `/etc/authelia` `lamaste:lamaste-authelia 2770` (setgid) with `configuration.yml` `0640`, `users.yml` `0660`, `.secrets.json` `0600` (lamaste), `db.sqlite3*` and `notifications.txt` `lamaste-authelia 0600`; `/var/log/authelia` `lamaste-authelia 0750`; `chisel-server.key` (if present) `lamaste:lamaste-chisel 0640`; the PKI directory `lamaste:lamaste` (the panel is the CA)
@@ -253,7 +253,7 @@ When the installer detects an existing Lamaste installation (`/etc/lamalibre/lam
253
253
 
254
254
  Redeploy mode stops the panel and gatekeeper, secures the install directory (a non-root-owned tree is moved aside and removed at the end), redeploys the panel server, `lamaste-server` CLI, UI, docs, gatekeeper and cert help page, merges `panel.json`, runs the same Chisel/Authelia step as a fresh install (stopping a running Authelia while its files change hands), rewrites the panel's systemd unit, the root-owned programs and the sudoers rules, ensures the port-80 redirect, then restarts Chisel if its binary or unit changed, starts Authelia and the gatekeeper if they were running, and restarts the panel. It does not touch OS hardening, mTLS certificates or the panel's vhosts. The panel's self-update (`sudo lamaste-priv self-update <version>`) runs this path as root, so it never writes through a path the `lamaste` user controls.
255
255
 
256
- This provides a fast upgrade path: re-running `npx @lamalibre/create-lamaste@4.4.1` on an existing installation updates the code and the pinned services while preserving all configuration and certificates. Use `--force-full` to bypass this and run the complete installer.
256
+ This provides a fast upgrade path: re-running `npx @lamalibre/create-lamaste@4.6.0` on an existing installation updates the code and the pinned services while preserving all configuration and certificates. Use `--force-full` to bypass this and run the complete installer.
257
257
 
258
258
  The redeploy logic lives in `tasks/redeploy.js`, with shared systemd unit and sudoers content generators in `lib/service-config.js`.
259
259
 
@@ -263,6 +263,8 @@ lamaste ALL=(root) NOPASSWD: /usr/sbin/nginx -t
263
263
  # --- root-owned programs that validate their own arguments ---
264
264
  # lamaste-priv nginx-site write|backup|restore|discard-backup|enable|disable|remove <name>
265
265
  # lamaste-priv authelia-totp <username> secret on stdin
266
+ # lamaste-priv authelia-user purge-second-factors <username> TOTP + WebAuthn deleted
267
+ # lamaste-priv authelia-user has-oidc-identifier <username> prints yes or no
266
268
  # lamaste-priv self-update <version> create-lamaste in a transient unit
267
269
  # lamaste-certbot issue <email> <cert-name> <domain>... certbot certonly --nginx
268
270
  # lamaste-certbot renew <cert-name> [force] | renew-all | list
@@ -276,7 +278,7 @@ Everything else the panel does needs no rule: the PKI directory (agent CSR signi
276
278
 
277
279
  The certbot wrappers accept only lowercase DNS hostnames (no wildcards or path characters, at most 100 per certificate) and a syntactically valid email address. Renewals the panel triggers pass `--no-random-sleep-on-renew`, because certbot otherwise sleeps up to about 8 minutes before a non-interactive renewal (a delay meant for `certbot.timer`) while an operator waits.
278
280
 
279
- Redeploying (`npx @lamalibre/create-lamaste@4.4.1` on an existing install) reinstalls the programs, removes retired ones (`lamaste-sign-csr`, `lamaste-pki-rename`), and replaces this file atomically after `visudo -c` accepts it, so upgrades pick up new rules.
281
+ Redeploying (`npx @lamalibre/create-lamaste@4.6.0` on an existing install) reinstalls the programs, removes retired ones (`lamaste-sign-csr`, `lamaste-pki-rename`), and replaces this file atomically after `visudo -c` accepts it, so upgrades pick up new rules.
280
282
 
281
283
  ## File Operation Safety
282
284