@lamalibre/create-lamaste 4.6.0 → 4.7.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 (149) hide show
  1. package/package.json +2 -2
  2. package/scripts/bundle-vendor.js +20 -3
  3. package/scripts/lamaste-priv +22 -3
  4. package/scripts/pin-release.js +98 -0
  5. package/src/index.js +8 -1
  6. package/src/lib/env.js +19 -0
  7. package/src/lib/service-config.js +7 -0
  8. package/src/tasks/redeploy.js +8 -0
  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 +6 -2
  13. package/vendor/docs/01-concepts/authorization.md +4 -0
  14. package/vendor/docs/01-concepts/certificates.md +3 -3
  15. package/vendor/docs/01-concepts/dns-and-domains.md +9 -0
  16. package/vendor/docs/01-concepts/mtls.md +2 -2
  17. package/vendor/docs/01-concepts/security-model.md +28 -3
  18. package/vendor/docs/01-concepts/tickets.md +2 -2
  19. package/vendor/docs/01-concepts/tunneling.md +4 -4
  20. package/vendor/docs/02-guides/agent-setup.md +5 -5
  21. package/vendor/docs/02-guides/certificate-management.md +2 -2
  22. package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
  23. package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
  24. package/vendor/docs/02-guides/installation.md +3 -3
  25. package/vendor/docs/02-guides/managing-users.md +5 -11
  26. package/vendor/docs/02-guides/onboarding.md +2 -2
  27. package/vendor/docs/02-guides/second-domain.md +82 -0
  28. package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
  29. package/vendor/docs/03-architecture/installer-flow.md +2 -2
  30. package/vendor/docs/03-architecture/installer.md +4 -4
  31. package/vendor/docs/03-architecture/management-flow.md +1 -1
  32. package/vendor/docs/03-architecture/nginx-configuration.md +15 -5
  33. package/vendor/docs/03-architecture/overview.md +5 -5
  34. package/vendor/docs/03-architecture/panel-client.md +6 -6
  35. package/vendor/docs/03-architecture/panel-server.md +2 -2
  36. package/vendor/docs/03-architecture/state-management.md +3 -3
  37. package/vendor/docs/03-architecture/system-overview.md +1 -1
  38. package/vendor/docs/04-api-reference/agent-hosted-plugins.md +17 -13
  39. package/vendor/docs/04-api-reference/app-host.md +3 -3
  40. package/vendor/docs/04-api-reference/domains.md +192 -0
  41. package/vendor/docs/04-api-reference/gatekeeper.md +35 -7
  42. package/vendor/docs/04-api-reference/lifecycle-hooks.md +4 -4
  43. package/vendor/docs/04-api-reference/onboarding.md +1 -1
  44. package/vendor/docs/04-api-reference/overview.md +9 -0
  45. package/vendor/docs/04-api-reference/registry-and-oidc.md +26 -13
  46. package/vendor/docs/04-api-reference/sites.md +5 -0
  47. package/vendor/docs/04-api-reference/tunnels.md +15 -10
  48. package/vendor/docs/04-api-reference/users.md +41 -29
  49. package/vendor/docs/05-operations/backup-and-restore.md +1 -1
  50. package/vendor/docs/05-operations/uninstalling.md +1 -1
  51. package/vendor/docs/05-operations/upgrades.md +6 -6
  52. package/vendor/docs/06-reference/config-files.md +1 -1
  53. package/vendor/docs/06-reference/glossary.md +3 -3
  54. package/vendor/docs/06-reference/installer-flags.md +13 -13
  55. package/vendor/docs/06-reference/ports-and-services.md +1 -1
  56. package/vendor/docs/06-reference/troubleshooting.md +8 -8
  57. package/vendor/docs/_index.json +105 -19
  58. package/vendor/docs/external-members-design.md +816 -0
  59. package/vendor/docs/gatekeeper-design.md +8 -8
  60. package/vendor/docs/identity-directory-design.md +3 -3
  61. package/vendor/docs/multi-domain-relay-design.md +836 -0
  62. package/vendor/docs/package.json +1 -1
  63. package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
  64. package/vendor/gatekeeper/dist/cli/index.js +0 -0
  65. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -1
  66. package/vendor/gatekeeper/dist/lib/grants.js +20 -13
  67. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -1
  68. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0004_unconsume_access_grants.sql +8 -0
  69. package/vendor/gatekeeper/dist/lib/secret.d.ts +6 -0
  70. package/vendor/gatekeeper/dist/lib/secret.d.ts.map +1 -0
  71. package/vendor/gatekeeper/dist/lib/secret.js +12 -0
  72. package/vendor/gatekeeper/dist/lib/secret.js.map +1 -0
  73. package/vendor/gatekeeper/dist/lib/templates.d.ts +20 -0
  74. package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -1
  75. package/vendor/gatekeeper/dist/lib/templates.js +80 -14
  76. package/vendor/gatekeeper/dist/lib/templates.js.map +1 -1
  77. package/vendor/gatekeeper/dist/server/index.d.ts +5 -1
  78. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
  79. package/vendor/gatekeeper/dist/server/index.js +28 -2
  80. package/vendor/gatekeeper/dist/server/index.js.map +1 -1
  81. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
  82. package/vendor/gatekeeper/dist/server/routes/authz.js +225 -36
  83. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
  84. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -1
  85. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +5 -2
  86. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -1
  87. package/vendor/gatekeeper/package.json +2 -2
  88. package/vendor/server/bin/lamaste-server.js +7 -0
  89. package/vendor/server/package.json +2 -2
  90. package/vendor/server/src/commands/domains.js +214 -0
  91. package/vendor/server/src/commands/plugin-identity.js +2 -16
  92. package/vendor/server/src/commands/plugins.js +112 -8
  93. package/vendor/server/src/commands/registry-credential.js +10 -3
  94. package/vendor/server/src/commands/sites.js +3 -0
  95. package/vendor/server/src/commands/status.js +19 -0
  96. package/vendor/server/src/commands/tunnels.js +4 -1
  97. package/vendor/server/src/commands/users.js +8 -19
  98. package/vendor/server/src/domains.js +92 -0
  99. package/vendor/server/src/fail.js +21 -0
  100. package/vendor/server/src/oidc-client-grants.js +63 -0
  101. package/vendor/server/src/panel-api.js +15 -0
  102. package/vendor/server-ui/dist/assets/index-B8Tv_ocF.js +84 -0
  103. package/vendor/server-ui/dist/assets/{index-BZwQ_aQc.css → index-Cb-ADsbB.css} +1 -1
  104. package/vendor/server-ui/dist/index.html +2 -2
  105. package/vendor/serverd/package.json +2 -2
  106. package/vendor/serverd/src/index.js +42 -16
  107. package/vendor/serverd/src/lib/app-error.js +24 -0
  108. package/vendor/serverd/src/lib/audience.js +12 -3
  109. package/vendor/serverd/src/lib/authelia.js +341 -35
  110. package/vendor/serverd/src/lib/directory.js +67 -52
  111. package/vendor/serverd/src/lib/domains.js +1792 -0
  112. package/vendor/serverd/src/lib/gatekeeper-client.js +20 -4
  113. package/vendor/serverd/src/lib/identity-sync.js +35 -0
  114. package/vendor/serverd/src/lib/migrations/state/0016_domains.sql +53 -0
  115. package/vendor/serverd/src/lib/migrations/state/0017_membership_kind.sql +15 -0
  116. package/vendor/serverd/src/lib/migrations/state/0018_domain_group_keys.sql +23 -0
  117. package/vendor/serverd/src/lib/nginx.js +318 -140
  118. package/vendor/serverd/src/lib/owned-groups.js +326 -0
  119. package/vendor/serverd/src/lib/plugin-domain.js +86 -0
  120. package/vendor/serverd/src/lib/plugin-lifecycle.js +1 -0
  121. package/vendor/serverd/src/lib/plugin-oidc.js +144 -42
  122. package/vendor/serverd/src/lib/plugins.js +45 -5
  123. package/vendor/serverd/src/lib/registry-credential.js +39 -6
  124. package/vendor/serverd/src/lib/services.js +9 -1
  125. package/vendor/serverd/src/lib/state.js +11 -0
  126. package/vendor/serverd/src/lib/tunnel-deps.js +2 -4
  127. package/vendor/serverd/src/lib/user-surface.js +29 -4
  128. package/vendor/serverd/src/lib/users-lock.js +23 -0
  129. package/vendor/serverd/src/lib/vhost-reconcile.js +149 -41
  130. package/vendor/serverd/src/lib/web-surface.js +49 -32
  131. package/vendor/serverd/src/middleware/user-access-session.js +11 -4
  132. package/vendor/serverd/src/routes/invite.js +15 -5
  133. package/vendor/serverd/src/routes/management/domains.js +154 -0
  134. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +44 -8
  135. package/vendor/serverd/src/routes/management/identity.js +21 -7
  136. package/vendor/serverd/src/routes/management/invitations.js +33 -4
  137. package/vendor/serverd/src/routes/management/plugins.js +4 -2
  138. package/vendor/serverd/src/routes/management/registry-credential.js +86 -57
  139. package/vendor/serverd/src/routes/management/sites.js +50 -11
  140. package/vendor/serverd/src/routes/management/tunnels.js +29 -5
  141. package/vendor/serverd/src/routes/management/users.js +184 -64
  142. package/vendor/serverd/src/routes/management.js +2 -0
  143. package/vendor/serverd/src/routes/onboarding/dns.js +3 -17
  144. package/vendor/serverd/src/routes/onboarding/provision.js +22 -13
  145. package/vendor/serverd/src/routes/user-access.js +13 -1
  146. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +0 -7
  147. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +0 -14
  148. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +0 -9
  149. package/vendor/server-ui/dist/assets/index-BvEfCZYS.js +0 -84
@@ -264,9 +264,9 @@ The integration flow:
264
264
  }
265
265
  ```
266
266
 
267
- ### Client SDK (`@lamalibre/lamaste-tickets@4.6.0`)
267
+ ### Client SDK (`@lamalibre/lamaste-tickets@4.7.0`)
268
268
 
269
- The `@lamalibre/lamaste-tickets@4.6.0` package is a TypeScript SDK that provides the client-side ticket lifecycle for plugins and agents. It uses `undici` for HTTP with mTLS dispatcher support and has no other runtime dependencies.
269
+ The `@lamalibre/lamaste-tickets@4.7.0` package is a TypeScript SDK that provides the client-side ticket lifecycle for plugins and agents. It uses `undici` for HTTP with mTLS dispatcher support and has no other runtime dependencies.
270
270
 
271
271
  The SDK exports three main classes:
272
272
 
@@ -144,7 +144,7 @@ On the API side the same rule holds: `GET /api/tunnels/agent-config` returns onl
144
144
 
145
145
  ### Client configuration
146
146
 
147
- The Chisel client runs as a user-level service managed by `lamaste-agent` (and `lamaste-agentd`): a LaunchAgent plist on macOS, a systemd **user** unit on Linux. Both are generated by one implementation in `@lamalibre/lamaste@4.6.0/agent`. The client command looks like:
147
+ The Chisel client runs as a user-level service managed by `lamaste-agent` (and `lamaste-agentd`): a LaunchAgent plist on macOS, a systemd **user** unit on Linux. Both are generated by one implementation in `@lamalibre/lamaste@4.7.0/agent`. The client command looks like:
148
148
 
149
149
  ```bash
150
150
  chisel client \
@@ -208,7 +208,7 @@ location / {
208
208
 
209
209
  ### Concurrency and locking
210
210
 
211
- Every tunnel workflow — create, delete, toggle, assign, reconfigure, the combined `PATCH`, releasing a revoked agent's tunnels, and startup reconciliation — runs under one tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.6.0/server`'s `tunnels.ts`), with its validation inside the lock. Two concurrent requests therefore cannot both pass a uniqueness check for the same subdomain or port. Authfile renders are additionally serialized with a keyed promise-chain mutex in `chisel-users.ts`, so enrollments and credential rotations never interleave authfile writes with tunnel changes.
211
+ Every tunnel workflow — create, delete, toggle, assign, reconfigure, the combined `PATCH`, releasing a revoked agent's tunnels, and startup reconciliation — runs under one tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.7.0/server`'s `tunnels.ts`), with its validation inside the lock. Two concurrent requests therefore cannot both pass a uniqueness check for the same subdomain or port. Authfile renders are additionally serialized with a keyed promise-chain mutex in `chisel-users.ts`, so enrollments and credential rotations never interleave authfile writes with tunnel changes.
212
212
 
213
213
  ### Startup reconciliation
214
214
 
@@ -223,7 +223,7 @@ It **fails closed**: if the authfile cannot be established, Chisel is stopped **
223
223
 
224
224
  ### Installation
225
225
 
226
- On the server, Chisel is installed by `npx @lamalibre/create-lamaste@4.6.0` — as root, together with its hardened systemd unit (`User=nobody`, `Group=lamaste-chisel`, `NoNewPrivileges`, `ProtectSystem=strict`, empty `CapabilityBoundingSet`) — and kept at the pinned release by every redeploy. The panel never installs a binary or writes the unit; onboarding only checks the binary exists, creates the server key and authfile, and starts the service. Both the server installer and the agents download the pinned release (`CHISEL_RELEASE` in `@lamalibre/lamaste@4.6.0`'s `constants.ts`) from its fixed URL, with no GitHub API lookup and no "latest":
226
+ On the server, Chisel is installed by `npx @lamalibre/create-lamaste@4.7.0` — as root, together with its hardened systemd unit (`User=nobody`, `Group=lamaste-chisel`, `NoNewPrivileges`, `ProtectSystem=strict`, empty `CapabilityBoundingSet`) — and kept at the pinned release by every redeploy. The panel never installs a binary or writes the unit; onboarding only checks the binary exists, creates the server key and authfile, and starts the service. Both the server installer and the agents download the pinned release (`CHISEL_RELEASE` in `@lamalibre/lamaste@4.7.0`'s `constants.ts`) from its fixed URL, with no GitHub API lookup and no "latest":
227
227
 
228
228
  ```
229
229
  https://github.com/jpillora/chisel/releases/download/v1.12.0/chisel_1.12.0_<os>_<arch>.gz
@@ -320,7 +320,7 @@ sudo systemctl restart chisel
320
320
 
321
321
  ```bash
322
322
  # Install globally, then set up the agent (interactive)
323
- npm install -g @lamalibre/lamaste-agent@4.6.0
323
+ npm install -g @lamalibre/lamaste-agent@4.7.0
324
324
  lamaste-agent setup --label <label>
325
325
 
326
326
  # Apply tunnel changes now (the sync timer does it within 30 s anyway)
@@ -59,7 +59,7 @@ The token is single-use and expires after 10 minutes. `tunnels:read` is enough f
59
59
  Install the agent **globally** first:
60
60
 
61
61
  ```bash
62
- npm install -g @lamalibre/lamaste-agent@4.6.0
62
+ npm install -g @lamalibre/lamaste-agent@4.7.0
63
63
  ```
64
64
 
65
65
  A global install is required, not a convenience. Setup installs a sync timer (see [How tunnel changes reach the agent](#how-tunnel-changes-reach-the-agent)) that runs the installed `lamaste-agent` every 30 seconds for as long as the agent exists. An `npx` copy lives in npm's cache, which npm may clear at any time — the timer would silently stop. `setup` and `update` therefore refuse to run from `npx`, and they check this **before** an enrollment token is spent. For an installation npm does not manage, set `LAMALIBRE_LAMASTE_AGENT_CLI_PATH` to the absolute path of the `lamaste-agent` script the timer should run.
@@ -148,7 +148,7 @@ Each agent authenticates to the relay with its own chisel credential, stored in
148
148
  Upgrade agents **before** the server. Agents from before this version reject the new shape of the relay's Chisel arguments; a current agent works against an older server, keeping its credential until the server offers rotation.
149
149
 
150
150
  ```bash
151
- npm install -g @lamalibre/lamaste-agent@4.6.0
151
+ npm install -g @lamalibre/lamaste-agent@4.7.0
152
152
  lamaste-agent update
153
153
  ```
154
154
 
@@ -172,7 +172,7 @@ lamaste-agent uninstall --label <l> # stop and remove one agent, including its
172
172
  To let the panel install the agent halves of app plugins on this machine — their accounts, units and directories — enable the app host helper once, from a root-owned install:
173
173
 
174
174
  ```bash
175
- sudo npm install -g @lamalibre/lamaste-agent@4.6.0
175
+ sudo npm install -g @lamalibre/lamaste-agent@4.7.0
176
176
  sudo lamaste-agent apphost enable # the agent runs as the user who ran sudo
177
177
  lamaste-agent apphost status # helper, pinned relay keys, installed agent halves
178
178
  sudo lamaste-agent apphost pin # after the relay rotated its signing key
@@ -184,7 +184,7 @@ The agent reports it on its next sync, and the panel can then host plugins whose
184
184
 
185
185
  | Symptom | Cause and fix |
186
186
  | ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
187
- | `setup` says the agent must be installed globally | It was run through `npx`. Run `npm install -g @lamalibre/lamaste-agent@4.6.0`, then `lamaste-agent setup ...` again. The enrollment token was not spent. |
187
+ | `setup` says the agent must be installed globally | It was run through `npx`. Run `npm install -g @lamalibre/lamaste-agent@4.7.0`, then `lamaste-agent setup ...` again. The enrollment token was not spent. |
188
188
  | Log shows a TLS / x509 error | `tunnel.<domain>` does not present a valid certificate for its name. Check the relay's certificate (`Certificates` page) and that the agent's clock is correct. |
189
189
  | Log shows authentication failed | The credential was rotated on the panel. The next sync picks it up; `lamaste-agent chisel refresh-credential` applies it now. |
190
190
  | Log shows `access denied` for a remote | The relay no longer grants this agent that port: the tunnel was disabled, deleted, or moved to another agent. The next sync drops it. If it persists, check `lamaste-agent status` — the sync timer may not be running. |
@@ -196,7 +196,7 @@ The agent reports it on its next sync, and the panel can then host plugins whose
196
196
  | New tunnel returns 502 | The agent has not synced yet (up to ~30 s), or nothing listens on `127.0.0.1:<port>` on the agent. Run `lamaste-agent update` to apply at once. |
197
197
  | Upload fails with 413 | The request is larger than the tunnel's body limit. Raise **Largest request body** on the tunnel (`maxBodySizeMb`). |
198
198
  | `uninstall` refuses: agent halves are installed | Agent halves run here for this agent. Unhost or uninstall those plugins on the panel (the next sync removes them), or `sudo lamaste-agent apphost disable --force` (data is kept). |
199
- | `apphost enable` says the install is not root-owned | Root installs the helper only from code no other user can change: `sudo npm install -g @lamalibre/lamaste-agent@4.6.0`, then `sudo lamaste-agent apphost enable` from that install. |
199
+ | `apphost enable` says the install is not root-owned | Root installs the helper only from code no other user can change: `sudo npm install -g @lamalibre/lamaste-agent@4.7.0`, then `sudo lamaste-agent apphost enable` from that install. |
200
200
  | `apphost enable` says the helper serves another user | App hosting serves one OS user per machine. Unhost or uninstall the plugins that user's agents host (`sudo lamaste-agent apphost status` lists their agent halves), then `sudo lamaste-agent apphost enable --user <name> --force`. |
201
201
  | `apphost pin` says a label is not an agent of the user | Root pins only labels the agent user's registry lists. Pin as the user the agent runs as (`--user <name>`; `--user root` for an agent running as root). |
202
202
  | Hosting refused: the helper trusts another relay key | The relay's signing key was replaced, or the agent enrolled again. Run `sudo lamaste-agent apphost pin`; compare the key id with the relay's ([App Host](../04-api-reference/app-host.md#pinning-the-relay-key)). |
@@ -196,7 +196,7 @@ The admin certificate (`CN=admin`) has unrestricted access to all panel endpoint
196
196
 
197
197
  ### Distribute to the Agent Machine
198
198
 
199
- Send the `.p12` file and password to the agent machine (macOS or Linux) securely, and install the agent there with `npm install -g @lamalibre/lamaste-agent@4.6.0` and run `lamaste-agent setup --label <label>` — or import it into the Desktop App. An enrollment token is preferable where possible, because the private key is then generated on the agent and never travels. See [Agent Setup](agent-setup.md) for details.
199
+ Send the `.p12` file and password to the agent machine (macOS or Linux) securely, and install the agent there with `npm install -g @lamalibre/lamaste-agent@4.7.0` and run `lamaste-agent setup --label <label>` — or import it into the Desktop App. An enrollment token is preferable where possible, because the private key is then generated on the agent and never travels. See [Agent Setup](agent-setup.md) for details.
200
200
 
201
201
  ### Update Capabilities
202
202
 
@@ -293,7 +293,7 @@ Revocation is immediate — the certificate serial is added to a revocation list
293
293
 
294
294
  ### mTLS Rotation Internals
295
295
 
296
- Admin certificate rotation lives only in `lamaste-server reset-admin` (`packages/server/cli/src/commands/reset-admin.js`), run as root on the server. The panel's `rotateClientCert()` in `lib/mtls.js` is a stub that returns `503` with that instruction; `@lamalibre/lamaste@4.6.0/server` no longer exports a rotation function. The PKI directory belongs to `lamaste`, so `reset-admin` runs every file operation inside it (`openssl genrsa`/`req`/`x509 -req`/`pkcs12`, `cp`, `chmod`) as `lamaste`, and writes `panel.json` and `revoked.json` as fresh files renamed into place — root never writes through a path the panel could have replaced with a symlink.
296
+ Admin certificate rotation lives only in `lamaste-server reset-admin` (`packages/server/cli/src/commands/reset-admin.js`), run as root on the server. The panel's `rotateClientCert()` in `lib/mtls.js` is a stub that returns `503` with that instruction; `@lamalibre/lamaste@4.7.0/server` no longer exports a rotation function. The PKI directory belongs to `lamaste`, so `reset-admin` runs every file operation inside it (`openssl genrsa`/`req`/`x509 -req`/`pkcs12`, `cp`, `chmod`) as `lamaste`, and writes `panel.json` and `revoked.json` as fresh files renamed into place — root never writes through a path the panel could have replaced with a symlink.
297
297
 
298
298
  The PKCS12 bundle uses legacy encryption settings (`-keypbe PBE-SHA1-3DES -certpbe PBE-SHA1-3DES -macalg sha1`) because newer OpenSSL defaults produce files that macOS Keychain Access cannot import.
299
299
 
@@ -14,7 +14,7 @@ Before you start, make sure you have:
14
14
 
15
15
  | Requirement | Details |
16
16
  | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
17
- | **Desktop app installed** | `npx @lamalibre/create-lamaste-desktop@4.6.0` ([setup guide](desktop-app-setup.md)) |
17
+ | **Desktop app installed** | `npx @lamalibre/create-lamaste-desktop@4.7.0` ([setup guide](desktop-app-setup.md)) |
18
18
  | **DigitalOcean account** | A free account at [cloud.digitalocean.com](https://cloud.digitalocean.com). **If you have other infrastructure on DO, create a dedicated team first** (see below) |
19
19
  | **Payment method** | A credit card or PayPal on file in DigitalOcean (required to create droplets) |
20
20
  | **Domain name** | Optional for initial setup — you can add a domain later through the panel |
@@ -221,7 +221,7 @@ The wizard shows up to 12 steps with live progress indicators. Each step shows a
221
221
  | **Waiting for boot** | Polls DigitalOcean until the droplet has a public IP and is ready (up to 5 minutes) |
222
222
  | **Setting up DNS records** | Creates A and wildcard A records in DigitalOcean DNS (only if a domain was selected in the Domain step) |
223
223
  | **Connecting via SSH** | Establishes an SSH connection using the temporary key |
224
- | **Installing Lamaste** | Runs `npx @lamalibre/create-lamaste@4.6.0` on the droplet (up to 10 minutes) |
224
+ | **Installing Lamaste** | Runs `npx @lamalibre/create-lamaste@4.7.0` on the droplet (up to 10 minutes) |
225
225
  | **Retrieving credentials** | Downloads the admin certificate (`.p12` file) and reads the panel's TLS key pin from the droplet |
226
226
  | **Enrolling admin certificate** | Imports the certificate into your OS credential store |
227
227
  | **Saving configuration** | Writes the server entry to `~/.lamalibre/lamaste/servers.json` |
@@ -315,7 +315,7 @@ If you already have a Lamaste server set up (via the manual SSH method), you can
315
315
 
316
316
  The app checks that the panel is reachable before adding it. The server is added with `active: false` — click **Set Active** on its card to start using it.
317
317
 
318
- > **Note:** Adding an existing server registers it in the app but does not configure authentication. You will still need to set up your agent certificate separately using `lamaste-agent setup` (after `npm install -g @lamalibre/lamaste-agent@4.6.0`) or by importing a certificate manually.
318
+ > **Note:** Adding an existing server registers it in the app but does not configure authentication. You will still need to set up your agent certificate separately using `lamaste-agent setup` (after `npm install -g @lamalibre/lamaste-agent@4.7.0`) or by importing a certificate manually.
319
319
 
320
320
  ---
321
321
 
@@ -26,7 +26,7 @@ The Lamaste Desktop app is a native application (built with Tauri) that wraps th
26
26
  Run the installer:
27
27
 
28
28
  ```bash
29
- npx @lamalibre/create-lamaste-desktop@4.6.0
29
+ npx @lamalibre/create-lamaste-desktop@4.7.0
30
30
  ```
31
31
 
32
32
  The installer:
@@ -56,11 +56,11 @@ After the app launches, it shows a setup screen with two options:
56
56
  Install the agent globally and run its setup command:
57
57
 
58
58
  ```bash
59
- npm install -g @lamalibre/lamaste-agent@4.6.0
59
+ npm install -g @lamalibre/lamaste-agent@4.7.0
60
60
  lamaste-agent setup --label my-server
61
61
  ```
62
62
 
63
- The agent must be a global install — setup installs a sync timer that runs it every 30 seconds to pick up tunnel changes, and refuses to run from `npx`. (When the app sets up an agent itself, it installs `@lamalibre/lamaste-agent@4.6.0` globally for you.) The `--label` flag names this agent connection (used for multi-agent support). This command connects to your VPS panel using the agent certificate and configures the local Chisel client. During setup, the client certificate and key are extracted from the P12 bundle for mTLS client authentication. Once setup completes, the app detects the configuration and switches to the main interface.
63
+ The agent must be a global install — setup installs a sync timer that runs it every 30 seconds to pick up tunnel changes, and refuses to run from `npx`. (When the app sets up an agent itself, it installs `@lamalibre/lamaste-agent@4.7.0` globally for you.) The `--label` flag names this agent connection (used for multi-agent support). This command connects to your VPS panel using the agent certificate and configures the local Chisel client. During setup, the client certificate and key are extracted from the P12 bundle for mTLS client authentication. Once setup completes, the app detects the configuration and switches to the main interface.
64
64
 
65
65
  **Option B: Create a new server**
66
66
 
@@ -72,7 +72,7 @@ The desktop app has two modes, toggled via a pill switch in the sidebar header.
72
72
 
73
73
  ### Agent Mode (default)
74
74
 
75
- Agent mode provides local agent management. The agent-mode pages (Dashboard, Tunnels, Services, Plugins, Logs, Settings) are imported from the shared `@lamalibre/lamaste-agent-ui@4.6.0` package. The landing page is the **Agents** list, showing all configured agents with their connection status. Click an agent to drill into its management pages:
75
+ Agent mode provides local agent management. The agent-mode pages (Dashboard, Tunnels, Services, Plugins, Logs, Settings) are imported from the shared `@lamalibre/lamaste-agent-ui@4.7.0` package. The landing page is the **Agents** list, showing all configured agents with their connection status. Click an agent to drill into its management pages:
76
76
 
77
77
  - **Agents** (landing) — list of all configured agents with start/stop controls and status
78
78
  - **Dashboard** — per-agent connection status, Chisel version, start/stop/restart controls
@@ -84,7 +84,7 @@ Agent mode provides local agent management. The agent-mode pages (Dashboard, Tun
84
84
 
85
85
  ### Server Mode
86
86
 
87
- Server mode provides the full admin panel — the same management pages available in the browser-based panel UI. These pages are imported from the shared `@lamalibre/lamaste-server-ui@4.6.0` package:
87
+ Server mode provides the full admin panel — the same management pages available in the browser-based panel UI. These pages are imported from the shared `@lamalibre/lamaste-server-ui@4.7.0` package:
88
88
 
89
89
  - **Dashboard** — system stats (CPU, RAM, disk, uptime) + service health
90
90
  - **Tunnels** — tunnel CRUD, carrying agent, access mode, body limit
@@ -170,7 +170,7 @@ Stopping an agent's tunnels from the app is remembered: the agent's sync timer k
170
170
  Run the installer again to get the latest version:
171
171
 
172
172
  ```bash
173
- npx @lamalibre/create-lamaste-desktop@4.6.0
173
+ npx @lamalibre/create-lamaste-desktop@4.7.0
174
174
  ```
175
175
 
176
176
  If a new release is available, it downloads and installs the update. The previous version is replaced.
@@ -203,9 +203,9 @@ Deleting `~/.lamalibre/lamaste/` by hand instead would leave the agents' service
203
203
 
204
204
  | Action | Command / Location |
205
205
  | -------------------- | ------------------------------------------------------------------------------------ |
206
- | **Install** | `npx @lamalibre/create-lamaste-desktop@4.6.0` |
207
- | **Update** | `npx @lamalibre/create-lamaste-desktop@4.6.0` |
208
- | **Agent setup** | `npm install -g @lamalibre/lamaste-agent@4.6.0`, then `lamaste-agent setup --label <name>` |
206
+ | **Install** | `npx @lamalibre/create-lamaste-desktop@4.7.0` |
207
+ | **Update** | `npx @lamalibre/create-lamaste-desktop@4.7.0` |
208
+ | **Agent setup** | `npm install -g @lamalibre/lamaste-agent@4.7.0`, then `lamaste-agent setup --label <name>` |
209
209
  | **App location** | `/Applications/Lamaste.app` (macOS) |
210
210
  | **Agent registry** | `~/.lamalibre/lamaste/agents.json` |
211
211
  | **Per-agent data** | `~/.lamalibre/lamaste/agents/<label>/` |
@@ -216,7 +216,7 @@ Deleting `~/.lamalibre/lamaste/` by hand instead would leave the agents' service
216
216
  | **Chisel logs** | `~/.lamalibre/lamaste/agents/<label>/logs/chisel.log` |
217
217
  | **Cloud tokens** | OS credential store (`com.lamalibre.cloud`) |
218
218
  | **P12 passwords** | OS credential store (`com.lamalibre.lamaste.server`) |
219
- | **npm package** | `@lamalibre/create-lamaste-desktop@4.6.0` |
219
+ | **npm package** | `@lamalibre/create-lamaste-desktop@4.7.0` |
220
220
 
221
221
  ### Related Documentation
222
222
 
@@ -62,7 +62,7 @@ Execute the Lamaste installer:
62
62
 
63
63
  ```bash
64
64
  apt install -y npm
65
- npx @lamalibre/create-lamaste@4.6.0
65
+ npx @lamalibre/create-lamaste@4.7.0
66
66
  ```
67
67
 
68
68
  The installer displays a confirmation banner before making any changes:
@@ -90,7 +90,7 @@ The installer displays a confirmation banner before making any changes:
90
90
  Press Enter to continue or Ctrl+C to abort...
91
91
  ```
92
92
 
93
- Press **Enter** to proceed. To skip the confirmation prompt, use `npx @lamalibre/create-lamaste@4.6.0 --yes`.
93
+ Press **Enter** to proceed. To skip the confirmation prompt, use `npx @lamalibre/create-lamaste@4.7.0 --yes`.
94
94
 
95
95
  ### 4. What the Installer Does
96
96
 
@@ -380,7 +380,7 @@ The service includes security hardening: `ProtectHome=true`, `PrivateTmp=true`,
380
380
 
381
381
  | Item | Value |
382
382
  | ------------------------ | ------------------------------------------------------------ |
383
- | **Install command** | `npx @lamalibre/create-lamaste@4.6.0` |
383
+ | **Install command** | `npx @lamalibre/create-lamaste@4.7.0` |
384
384
  | **Minimum VPS** | Ubuntu 24.04, 512 MB RAM |
385
385
  | **Required ports** | 22 (SSH), 80 (HTTP/Let's Encrypt), 443 (HTTPS), 9292 (panel) |
386
386
  | **Certificate location** | `/etc/lamalibre/lamaste/pki/client.p12` |
@@ -117,13 +117,13 @@ When someone leaves, **disable** them first — it is immediate and reversible:
117
117
  The user's status turns **disabled**. From that moment:
118
118
 
119
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;
120
+ - every agent-hosted plugin's user surface refuses them (`403`) — the panel's directory is updated in the same step;
121
121
  - every plugin they use sees them as `disabled` in its audience (its agent half is told within seconds while it is online);
122
122
  - their desktop app sessions end and their registry (Feria) tokens are revoked.
123
123
 
124
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
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.
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 when it reads the file back 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
127
 
128
128
  ```bash
129
129
  lamaste-server users disable alice
@@ -141,7 +141,7 @@ lamaste-server users enable alice
141
141
 
142
142
  **What happens behind the scenes:**
143
143
 
144
- 1. The panel removes the user from `users.yml` (atomic write, under the panel's one lock for user changes) and restarts Authelia.
144
+ 1. The panel removes the user from `users.yml` (atomic write, under the panel's one lock for user changes); Authelia reloads it and ends the user's session at their next request.
145
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
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
147
  4. Authelia deletes the user's second factors — their TOTP configuration and WebAuthn security keys.
@@ -227,7 +227,7 @@ Details: [Users API](../04-api-reference/users.md).
227
227
 
228
228
  ### The User Directory
229
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.
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 in the same step. 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
231
 
232
232
  ### The CLI
233
233
 
@@ -310,13 +310,7 @@ Passwords are hashed with bcrypt at cost factor 12. The `hashPassword()` functio
310
310
 
311
311
  ### Authelia Reload
312
312
 
313
- After every write to `users.yml`, the panel restarts Authelia:
314
-
315
- ```bash
316
- sudo systemctl restart authelia
317
- ```
318
-
319
- Authelia reads `users.yml` on restart. If the restart fails, a warning is logged but the API request still succeeds — the user data is persisted and the next Authelia restart picks it up.
313
+ Authelia watches `users.yml` (`authentication_backend.file.watch: true`) and reloads it in place; with the watch it re-reads a user's details at each of their requests, so a change applies at the user's next request without a restart and without ending anyone else's session. It skips a reload within 0.5 s of the previous one, so the panel writes the file once more 0.6 s after its last write. Who may reach which domain is part of the file too: the groups Lamaste owns (`lamaste:m:<key>`, `lamaste:x:<key>`, `lamaste:s:<site id>`, hostnames, `lamaste:oidc@<client>` — never shown as groups). Authelia restarts when its configuration changes — a domain or a protected static site added or removed, the OIDC clients changed — and when a change takes a device-code OIDC client (`feria login`) away from someone, because Authelia's device flow checks the groups a session last read. Authelia's own password change and reset are disabled (they cannot work on a relay: its notifier is a file); passwords are set by invitation or by an administrator.
320
314
 
321
315
  ## Quick Reference
322
316
 
@@ -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.6.0` 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.7.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.6.0`)
216
+ 1. `panel.example.com`, `auth.example.com`, `tunnel.example.com` — must resolve to the server IP (required; `CORE_SUBDOMAINS` in `@lamalibre/lamaste@4.7.0`)
217
217
  2. `test-lamaste-check.example.com` — tests wildcard resolution (optional)
218
218
  3. `example.com` — reported, not required
219
219
 
@@ -0,0 +1,82 @@
1
+ # Adding a Second Domain
2
+
3
+ > Serve another base domain from the same relay, with its own sign-in and its own members — `codelama.com.tr` beside `samplepark.tr`, or a customer's domain.
4
+
5
+ ## In Plain English
6
+
7
+ One relay can serve several domains. Each gets a sign-in page at `auth.<domain>` and a member list; a person signs in to each domain once with the same account and reaches only the domains they are a member of. The panel and the tunnel endpoint stay on the primary domain, so agents change nothing.
8
+
9
+ ## 1. Add the domain
10
+
11
+ Panel → **Domains** → enter the name → **Add domain**, or:
12
+
13
+ ```bash
14
+ sudo lamaste-server domains add codelama.com.tr
15
+ ```
16
+
17
+ It is `pending`: nothing is served on it yet.
18
+
19
+ ## 2. Create the DNS records
20
+
21
+ At the domain's DNS host, point the sign-in at the relay:
22
+
23
+ | Type | Name | Value |
24
+ | ---- | ---------------------- | ---------------- |
25
+ | A | `auth.codelama.com.tr` | the relay's IPv4 |
26
+
27
+ Then either one wildcard for every later hostname, or one record per hostname:
28
+
29
+ | Type | Name | Value |
30
+ | ---- | ------------------- | ---------------- |
31
+ | A | `*.codelama.com.tr` | the relay's IPv4 |
32
+
33
+ Explicit records keep precedence over a wildcard, so an apex, `www` or mail (MX, SPF, DKIM) record elsewhere stays as it is. Some DNS hosts (Turhost among them) take a wildcard as a plain record named `*`; if yours refuses one, create an A record per hostname as you add tunnels and sites. Certificates are Let's Encrypt HTTP-01 per hostname, issued by the relay — the DNS host does not matter.
34
+
35
+ **Check DNS** on the domain's card (or `lamaste-server domains verify-dns codelama.com.tr`) lists the records the panel expects and whether each resolves here.
36
+
37
+ ## 3. Choose the members
38
+
39
+ Add members on the domain's card (**Members**), or `lamaste-server domains members codelama.com.tr add <username>`. Members of a pending domain are let in the moment it goes live. From now on new users and invitations name their domains.
40
+
41
+ ## 4. Activate
42
+
43
+ **Activate** (or `lamaste-server domains activate codelama.com.tr`) issues `auth.codelama.com.tr`, serves its sign-in and admits its members. It is one operation: if the certificate, nginx or Authelia step fails, nothing changes.
44
+
45
+ ## 5. Put hostnames on it
46
+
47
+ Tunnel and managed-site forms gain a **Domain** picker; the CLI takes `--domain`. A plugin belongs to one domain: move it on its card (or `lamaste-server plugins domain <name> <domain>`) before giving it a user or web surface there.
48
+
49
+ ## What members see
50
+
51
+ - A member signs in at `auth.<domain>` once per domain (browsers never share cookies between domains), with the same password and TOTP.
52
+ - A signed-in non-member gets a "not a member of this domain" page with your contact and a sign-out link.
53
+ - `authenticated` means "any member of this domain"; `restricted` still needs a Gatekeeper grant.
54
+
55
+ ## External members
56
+
57
+ A client's people can sign in on your domain without reaching all of it: make them **external** members. An external member reaches only what names them:
58
+
59
+ - a `restricted` tunnel, user surface or web surface — with a Gatekeeper grant (to them, or a Lamaste group they are in);
60
+ - a protected static site that lists them in its allowed users;
61
+ - an OIDC client of a plugin on the domain that is granted to them (the plugin card's **OIDC sign-in**, or `lamaste-server plugins oidc-clients <plugin> grant <client> --user <name>`).
62
+
63
+ Every `authenticated` host stays closed to them — they get the access-request page. They hold no Authelia groups (`admins`, `feria-*`): remove a user's groups before making them external. A plugin's audience counts them when they are entitled to it, so the plugin sees them with their roles.
64
+
65
+ Make someone external when you create or invite them (the membership's kind, or **External member** on a one-domain relay), on the domain's **Members** list (**Make external**), or with the CLI:
66
+
67
+ ```
68
+ lamaste-server domains members codelama.com.tr add client-ali --external
69
+ lamaste-server domains members codelama.com.tr kind onur external
70
+ ```
71
+
72
+ None of this restarts Authelia or signs anyone out: a change applies at the person's next request.
73
+
74
+ ## Removing a domain
75
+
76
+ Delete or move what uses it first (the card lists it), then **Remove**. Its memberships end; the primary can never be removed.
77
+
78
+ ## Related
79
+
80
+ - [Domains API](../04-api-reference/domains.md)
81
+ - [DNS & Domains](../01-concepts/dns-and-domains.md#multiple-domains)
82
+ - [Authorization](../01-concepts/authorization.md)
@@ -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.6.0` to create the droplet
30
+ - The wizard calls `@lamalibre/lamaste-cloud@4.7.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.6.0` — 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.7.0` — the admin does not need to do anything.
100
100
 
101
101
  ```mermaid
102
102
  sequenceDiagram
@@ -1,6 +1,6 @@
1
1
  # Installer Flow
2
2
 
3
- ## What `npx @lamalibre/create-lamaste@4.6.0` Does
3
+ ## What `npx @lamalibre/create-lamaste@4.7.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.6.0
16
+ $ npx @lamalibre/create-lamaste@4.7.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.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.
7
+ When you run `npx @lamalibre/create-lamaste@4.7.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.6.0
14
+ npx @lamalibre/create-lamaste@4.7.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.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)
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.7.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.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.
256
+ This provides a fast upgrade path: re-running `npx @lamalibre/create-lamaste@4.7.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
 
@@ -278,7 +278,7 @@ Everything else the panel does needs no rule: the PKI directory (agent CSR signi
278
278
 
279
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.
280
280
 
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.
281
+ Redeploying (`npx @lamalibre/create-lamaste@4.7.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.
282
282
 
283
283
  ## File Operation Safety
284
284
 
@@ -204,7 +204,7 @@ Identical to the IP vhost except:
204
204
  - `server_name` is `panel.<domain>`
205
205
  - Requires the `map $http_upgrade $connection_upgrade` block from the IP vhost (or a shared config) to be present
206
206
 
207
- ### 3. Auth Vhost (`lamalibre-lamaste-auth`)
207
+ ### 3. Auth Vhost (`lamalibre-lamaste-auth-<domain>`)
208
208
 
209
209
  Created during onboarding provisioning. Serves the Authelia authentication portal.
210
210
 
@@ -282,7 +282,7 @@ Key characteristics:
282
282
  - 24-hour read/send timeouts to keep tunnel connections alive without nginx closing them
283
283
  - No mTLS and no Authelia — Chisel handles its own authentication
284
284
 
285
- ### 5. App Vhost (`lamalibre-lamaste-app-<name>`)
285
+ ### 5. App Vhost (`lamalibre-lamaste-app-<fqdn>`)
286
286
 
287
287
  Created when a tunnel is added via the management UI or `POST /api/tunnels`. Each tunnel gets its own vhost, rendered by one of three writers according to the tunnel's `accessMode`:
288
288
 
@@ -468,7 +468,7 @@ server {
468
468
 
469
469
  Moving a bound tunnel's host to a web surface hands the hostname over in **one reload**: the tunnel's vhost is unlinked, the site's written and linked, `nginx -t` run, nginx reloaded once; the tunnel's file stays installed, unlinked, so the tunnel can be enabled again once the site is deleted. On a failed test both are put back.
470
470
 
471
- ### 7. Public Tunnel Vhost (`lamalibre-lamaste-app-<name>`, access mode: public)
471
+ ### 7. Public Tunnel Vhost (`lamalibre-lamaste-app-<fqdn>`, access mode: public)
472
472
 
473
473
  Created when a tunnel is configured with public access. Direct proxy with no authentication.
474
474
 
@@ -505,7 +505,7 @@ server {
505
505
 
506
506
  No `auth_request`, no Authelia, no Gatekeeper. Traffic is proxied directly to the tunneled app. Use only for services that handle their own authentication or are intentionally public.
507
507
 
508
- ### 8. Authenticated Tunnel Vhost (`lamalibre-lamaste-app-<name>`, access mode: authenticated)
508
+ ### 8. Authenticated Tunnel Vhost (`lamalibre-lamaste-app-<fqdn>`, access mode: authenticated)
509
509
 
510
510
  Created when a tunnel requires Authelia login but no per-user grant checking. Any authenticated Authelia user can access the tunnel.
511
511
 
@@ -582,7 +582,7 @@ Key characteristics:
582
582
 
583
583
  > **Note:** The `lamalibre-lamaste-authz-cache.conf` nginx snippet is installed by the Gatekeeper installer task, but the generated vhosts do not currently include `proxy_cache` directives. Authorization caching relies on the Gatekeeper's in-memory session cache (30-second TTL) rather than nginx-level proxy caching.
584
584
 
585
- ### 9. Restricted Tunnel Vhost (`lamalibre-lamaste-app-<name>`, access mode: restricted)
585
+ ### 9. Restricted Tunnel Vhost (`lamalibre-lamaste-app-<fqdn>`, access mode: restricted)
586
586
 
587
587
  Created when a tunnel requires both Authelia login and an explicit access grant. Only users with a matching grant in Gatekeeper (`gatekeeper.db`) can access the tunnel.
588
588
 
@@ -757,6 +757,16 @@ Gatekeeper authorizes the tunnel named in the path, and refuses a check whose `X
757
757
 
758
758
  Authelia evaluates the raw `$request_uri`, while nginx serves the normalized `$uri` (`/a/../b` is `/b`, `%2e` is `.`). Lamaste writes no path (`resources`) rules into Authelia's `access_control`, only hostname rules; a path rule added by hand would see a different path than nginx serves.
759
759
 
760
+ ## Vhosts on several domains
761
+
762
+ A relay may serve several base domains ([Domains API](../04-api-reference/domains.md)). Vhost files are therefore named by full hostname — `lamalibre-lamaste-app-<fqdn>`, `lamalibre-lamaste-agent-panel-<fqdn>`, `lamalibre-lamaste-auth-<domain>` — so the same subdomain on two domains never shares a file. At start the panel renames vhosts written under the earlier names (`lamalibre-lamaste-app-<subdomain>`, `lamalibre-lamaste-agent-panel-<subdomain>`, `lamalibre-lamaste-auth`): the new file is written with the old one's link state, the old file and link removed, then one `nginx -t` and reload, with the old files restored if the test fails. `lamaste-priv` accepts the hostname names for every action and the legacy names for everything but `write`.
763
+
764
+ - **One auth vhost per active domain** (`auth.<domain>`, its own certificate). Only the primary's carries `/api/user-access/authorize` and the `user_access_auth` rate-limit zone (desktop user access is primary-only); every domain's serves `/`, `/invite/` and `/api/invite/`.
765
+ - **The 401 redirect** of tunnel and web-surface vhosts goes to `https://auth.<the host's domain>/?rd=…`; static sites follow Authelia's `$redirection_url`, which Authelia builds for the host's cookie domain.
766
+ - **A 403** from Gatekeeper (a signed-in non-member, or a missing grant) is served inline; Authelia-protected static sites route `error_page 403` to Gatekeeper's `/authz/refusal/<fqdn>`.
767
+ - **Authenticated and restricted tunnels share one vhost** (`writeGatekeeperVhost`): Gatekeeper decides by the access mode in `tunnels.json`.
768
+ - **Certificates** stay HTTP-01 per hostname; a wildcard is used when one for the host's domain is already present.
769
+
760
770
  ## The backend cookie map
761
771
 
762
772
  Authelia's session cookie (`authelia_session`, the name core configures in `server/authelia.ts` and exports as `AUTHELIA_SESSION_COOKIE`) is scoped to the base domain, so a browser sends it to every host under it. No tunnel or agent backend may receive it: every location that proxies to one sends
@@ -6,7 +6,7 @@
6
6
 
7
7
  Lamaste has three main pieces:
8
8
 
9
- 1. **The Installer** (`npx @lamalibre/create-lamaste@4.6.0`) — a one-shot script you run once on a fresh VPS. It sets up nginx, generates security certificates, and deploys a web-based control panel. After it finishes, you never SSH into the server again.
9
+ 1. **The Installer** (`npx @lamalibre/create-lamaste@4.7.0`) — a one-shot script you run once on a fresh VPS. It sets up nginx, generates security certificates, and deploys a web-based control panel. After it finishes, you never SSH into the server again.
10
10
 
11
11
  2. **The Panel** (browser UI + backend API) — a management interface protected by a client certificate. Through it, you configure your domain, create tunnels, manage users, and monitor services.
12
12
 
@@ -318,7 +318,7 @@ lamaste/
318
318
  │ │ ├── files.js ← Static site file operations (upload, delete, list)
319
319
  │ │ └── app-error.js ← Operational error class (AppError)
320
320
  │ │
321
- │ ├── lamaste-server-ui/ ← React 18 + Vite + Tailwind SPA + shared admin components (@lamalibre/lamaste-server-ui@4.6.0)
321
+ │ ├── lamaste-server-ui/ ← React 18 + Vite + Tailwind SPA + shared admin components (@lamalibre/lamaste-server-ui@4.7.0)
322
322
  │ │ └── src/
323
323
  │ │ ├── index.js ← Package exports (pages, context, utilities)
324
324
  │ │ ├── context/
@@ -334,7 +334,7 @@ lamaste/
334
334
  │ │ │ └── formatters.js ← Byte, uptime, relative time formatters
335
335
  │ │ └── pages/ ← Admin pages (Dashboard, Tunnels, Sites, Users, Certs, Services, Plugins, Tickets, Settings)
336
336
  │ │
337
- │ ├── lamaste-agent-ui/ ← Shared React agent UI (pages, context, components) (@lamalibre/lamaste-agent-ui@4.6.0)
337
+ │ ├── lamaste-agent-ui/ ← Shared React agent UI (pages, context, components) (@lamalibre/lamaste-agent-ui@4.7.0)
338
338
  │ │
339
339
  │ ├── lamaste-tickets/ ← Agent-to-agent authorization SDK (mTLS, undici)
340
340
  │ │ └── src/
@@ -374,7 +374,7 @@ lamaste/
374
374
  │ │ │ └── CompleteStep.jsx ← Credentials + TOTP QR + next steps
375
375
  │ │ ├── management/ ← Legacy standalone implementations (kept for reference)
376
376
  │ │ │ ├── Certificates.jsx ← Standalone certificates page (dead code)
377
- │ │ │ ├── Dashboard.jsx ← Standalone dashboard (not imported — App.jsx uses @lamalibre/lamaste-server-ui@4.6.0)
377
+ │ │ │ ├── Dashboard.jsx ← Standalone dashboard (not imported — App.jsx uses @lamalibre/lamaste-server-ui@4.7.0)
378
378
  │ │ │ ├── Plugins.jsx ← Standalone plugins page (dead code)
379
379
  │ │ │ ├── Services.jsx ← Standalone services page (dead code)
380
380
  │ │ │ ├── Settings.jsx ← Standalone settings page (dead code)
@@ -483,7 +483,7 @@ The Panel Server runs as the `lamaste` user with minimal privileges and cannot b
483
483
  The full lifecycle of a Lamaste installation:
484
484
 
485
485
  ```
486
- Phase 1: Installation (npx @lamalibre/create-lamaste@4.6.0)
486
+ Phase 1: Installation (npx @lamalibre/create-lamaste@4.7.0)
487
487
  └─ OS hardening, Node.js, mTLS certs, nginx, panel deploy
488
488
  └─ Result: Panel accessible at https://<ip>:9292
489
489