@lamalibre/create-lamaste 4.5.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 (178) hide show
  1. package/package.json +2 -2
  2. package/scripts/bundle-vendor.js +20 -3
  3. package/scripts/lamaste-priv +56 -5
  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/02-guides/static-sites.md +4 -0
  30. package/vendor/docs/03-architecture/installer-flow.md +2 -2
  31. package/vendor/docs/03-architecture/installer.md +4 -4
  32. package/vendor/docs/03-architecture/management-flow.md +1 -1
  33. package/vendor/docs/03-architecture/nginx-configuration.md +80 -12
  34. package/vendor/docs/03-architecture/overview.md +5 -5
  35. package/vendor/docs/03-architecture/panel-client.md +6 -6
  36. package/vendor/docs/03-architecture/panel-server.md +2 -2
  37. package/vendor/docs/03-architecture/state-management.md +3 -3
  38. package/vendor/docs/03-architecture/system-overview.md +1 -1
  39. package/vendor/docs/04-api-reference/agent-hosted-plugins.md +116 -37
  40. package/vendor/docs/04-api-reference/app-host.md +3 -3
  41. package/vendor/docs/04-api-reference/domains.md +192 -0
  42. package/vendor/docs/04-api-reference/gatekeeper.md +54 -8
  43. package/vendor/docs/04-api-reference/lifecycle-hooks.md +4 -4
  44. package/vendor/docs/04-api-reference/onboarding.md +1 -1
  45. package/vendor/docs/04-api-reference/overview.md +9 -0
  46. package/vendor/docs/04-api-reference/registry-and-oidc.md +26 -13
  47. package/vendor/docs/04-api-reference/sites.md +9 -0
  48. package/vendor/docs/04-api-reference/tunnels.md +35 -28
  49. package/vendor/docs/04-api-reference/users.md +41 -29
  50. package/vendor/docs/05-operations/backup-and-restore.md +1 -1
  51. package/vendor/docs/05-operations/uninstalling.md +1 -1
  52. package/vendor/docs/05-operations/upgrades.md +15 -6
  53. package/vendor/docs/06-reference/config-files.md +19 -15
  54. package/vendor/docs/06-reference/glossary.md +3 -3
  55. package/vendor/docs/06-reference/installer-flags.md +13 -13
  56. package/vendor/docs/06-reference/ports-and-services.md +1 -1
  57. package/vendor/docs/06-reference/troubleshooting.md +8 -8
  58. package/vendor/docs/_index.json +105 -19
  59. package/vendor/docs/external-members-design.md +816 -0
  60. package/vendor/docs/gatekeeper-design.md +8 -8
  61. package/vendor/docs/identity-directory-design.md +3 -3
  62. package/vendor/docs/multi-domain-relay-design.md +836 -0
  63. package/vendor/docs/package.json +1 -1
  64. package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
  65. package/vendor/gatekeeper/dist/index.d.ts +3 -3
  66. package/vendor/gatekeeper/dist/index.d.ts.map +1 -1
  67. package/vendor/gatekeeper/dist/index.js +2 -2
  68. package/vendor/gatekeeper/dist/index.js.map +1 -1
  69. package/vendor/gatekeeper/dist/lib/authz.d.ts +9 -0
  70. package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -1
  71. package/vendor/gatekeeper/dist/lib/authz.js +31 -0
  72. package/vendor/gatekeeper/dist/lib/authz.js.map +1 -1
  73. package/vendor/gatekeeper/dist/lib/constants.d.ts +5 -2
  74. package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -1
  75. package/vendor/gatekeeper/dist/lib/constants.js +5 -2
  76. package/vendor/gatekeeper/dist/lib/constants.js.map +1 -1
  77. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -1
  78. package/vendor/gatekeeper/dist/lib/grants.js +20 -13
  79. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -1
  80. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0004_unconsume_access_grants.sql +8 -0
  81. package/vendor/gatekeeper/dist/lib/secret.d.ts +6 -0
  82. package/vendor/gatekeeper/dist/lib/secret.d.ts.map +1 -0
  83. package/vendor/gatekeeper/dist/lib/secret.js +12 -0
  84. package/vendor/gatekeeper/dist/lib/secret.js.map +1 -0
  85. package/vendor/gatekeeper/dist/lib/templates.d.ts +20 -0
  86. package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -1
  87. package/vendor/gatekeeper/dist/lib/templates.js +80 -14
  88. package/vendor/gatekeeper/dist/lib/templates.js.map +1 -1
  89. package/vendor/gatekeeper/dist/lib/tunnels.d.ts +3 -6
  90. package/vendor/gatekeeper/dist/lib/tunnels.d.ts.map +1 -1
  91. package/vendor/gatekeeper/dist/lib/tunnels.js +4 -16
  92. package/vendor/gatekeeper/dist/lib/tunnels.js.map +1 -1
  93. package/vendor/gatekeeper/dist/lib/types.d.ts +0 -19
  94. package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -1
  95. package/vendor/gatekeeper/dist/server/index.d.ts +6 -1
  96. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -1
  97. package/vendor/gatekeeper/dist/server/index.js +56 -11
  98. package/vendor/gatekeeper/dist/server/index.js.map +1 -1
  99. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -1
  100. package/vendor/gatekeeper/dist/server/routes/authz.js +325 -55
  101. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -1
  102. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -1
  103. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +5 -2
  104. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -1
  105. package/vendor/gatekeeper/package.json +2 -2
  106. package/vendor/server/bin/lamaste-server.js +7 -0
  107. package/vendor/server/package.json +2 -2
  108. package/vendor/server/src/commands/domains.js +214 -0
  109. package/vendor/server/src/commands/plugin-identity.js +2 -16
  110. package/vendor/server/src/commands/plugin-web-surface.js +269 -0
  111. package/vendor/server/src/commands/plugins.js +175 -14
  112. package/vendor/server/src/commands/registry-credential.js +10 -3
  113. package/vendor/server/src/commands/sites.js +18 -1
  114. package/vendor/server/src/commands/status.js +19 -0
  115. package/vendor/server/src/commands/tunnels.js +4 -1
  116. package/vendor/server/src/commands/users.js +8 -19
  117. package/vendor/server/src/domains.js +92 -0
  118. package/vendor/server/src/fail.js +21 -0
  119. package/vendor/server/src/oidc-client-grants.js +63 -0
  120. package/vendor/server/src/panel-api.js +15 -0
  121. package/vendor/server/src/plugin-approval.js +80 -1
  122. package/vendor/server/src/plugin-web-surface.js +307 -0
  123. package/vendor/server-ui/dist/assets/index-B8Tv_ocF.js +84 -0
  124. package/vendor/server-ui/dist/assets/index-Cb-ADsbB.css +1 -0
  125. package/vendor/server-ui/dist/index.html +2 -2
  126. package/vendor/serverd/package.json +2 -2
  127. package/vendor/serverd/src/index.js +50 -19
  128. package/vendor/serverd/src/lib/app-error.js +24 -0
  129. package/vendor/serverd/src/lib/audience.js +12 -3
  130. package/vendor/serverd/src/lib/authelia.js +341 -35
  131. package/vendor/serverd/src/lib/chisel-runtime.js +18 -6
  132. package/vendor/serverd/src/lib/chisel.js +10 -2
  133. package/vendor/serverd/src/lib/directory.js +67 -52
  134. package/vendor/serverd/src/lib/domains.js +1792 -0
  135. package/vendor/serverd/src/lib/gatekeeper-client.js +34 -7
  136. package/vendor/serverd/src/lib/hook-pipelines.js +8 -3
  137. package/vendor/serverd/src/lib/identity-sync.js +35 -0
  138. package/vendor/serverd/src/lib/migrations/state/0015_plugin_web_surface.sql +14 -0
  139. package/vendor/serverd/src/lib/migrations/state/0016_domains.sql +53 -0
  140. package/vendor/serverd/src/lib/migrations/state/0017_membership_kind.sql +15 -0
  141. package/vendor/serverd/src/lib/migrations/state/0018_domain_group_keys.sql +23 -0
  142. package/vendor/serverd/src/lib/nginx.js +576 -131
  143. package/vendor/serverd/src/lib/owned-groups.js +326 -0
  144. package/vendor/serverd/src/lib/plugin-domain.js +86 -0
  145. package/vendor/serverd/src/lib/plugin-hooks.js +40 -11
  146. package/vendor/serverd/src/lib/plugin-lifecycle.js +129 -5
  147. package/vendor/serverd/src/lib/plugin-oidc.js +144 -42
  148. package/vendor/serverd/src/lib/plugin-packages.js +74 -2
  149. package/vendor/serverd/src/lib/plugins.js +82 -9
  150. package/vendor/serverd/src/lib/registry-credential.js +39 -6
  151. package/vendor/serverd/src/lib/services.js +9 -1
  152. package/vendor/serverd/src/lib/state.js +29 -0
  153. package/vendor/serverd/src/lib/tunnel-deps.js +2 -4
  154. package/vendor/serverd/src/lib/user-surface.js +77 -31
  155. package/vendor/serverd/src/lib/users-lock.js +23 -0
  156. package/vendor/serverd/src/lib/vhost-reconcile.js +151 -41
  157. package/vendor/serverd/src/lib/web-surface.js +783 -0
  158. package/vendor/serverd/src/middleware/user-access-session.js +11 -4
  159. package/vendor/serverd/src/routes/invite.js +15 -5
  160. package/vendor/serverd/src/routes/management/domains.js +154 -0
  161. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +44 -8
  162. package/vendor/serverd/src/routes/management/identity.js +21 -7
  163. package/vendor/serverd/src/routes/management/invitations.js +33 -4
  164. package/vendor/serverd/src/routes/management/plugin-web-surface.js +151 -0
  165. package/vendor/serverd/src/routes/management/plugins.js +6 -2
  166. package/vendor/serverd/src/routes/management/registry-credential.js +86 -57
  167. package/vendor/serverd/src/routes/management/sites.js +188 -71
  168. package/vendor/serverd/src/routes/management/tunnels.js +37 -22
  169. package/vendor/serverd/src/routes/management/users.js +184 -64
  170. package/vendor/serverd/src/routes/management.js +4 -0
  171. package/vendor/serverd/src/routes/onboarding/dns.js +3 -17
  172. package/vendor/serverd/src/routes/onboarding/provision.js +22 -13
  173. package/vendor/serverd/src/routes/user-access.js +13 -1
  174. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +0 -7
  175. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +0 -14
  176. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +0 -9
  177. package/vendor/server-ui/dist/assets/index-D-fer5ef.css +0 -1
  178. package/vendor/server-ui/dist/assets/index-DSaDPJaw.js +0 -84
@@ -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
 
@@ -410,7 +410,65 @@ Optional Authelia protection is controlled by the `autheliaProtected` flag on th
410
410
 
411
411
  A custom-domain site may list `aliases` (e.g. `www.myblog.net`). They are served by a second server block that answers with a 301 to the primary host. One certificate lineage, named after the site's domain (`certbot certonly --nginx --cert-name <fqdn> -d <fqdn> -d <alias> ...`), covers the primary name and every alias, so both blocks point at the same `live/<fqdn>/` directory.
412
412
 
413
- ### 7. Public Tunnel Vhost (`lamalibre-lamaste-app-<name>`, access mode: public)
413
+ ### Web-surface vhosts
414
+
415
+ A site a plugin owns for its [web surface](../04-api-reference/agent-hosted-plugins.md#web-surface) has a vhost of its own kind under the site name (`lamalibre-lamaste-site-<id>`), rendered by the panel from the plugin's state (`renderWebSurfaceVhost`) — never by the static-site generator. While the surface is served:
416
+
417
+ ```nginx
418
+ server {
419
+ listen 443 ssl;
420
+ server_name piyasa.example.com;
421
+ ssl_certificate /etc/letsencrypt/live/piyasa.example.com/fullchain.pem;
422
+ ssl_certificate_key /etc/letsencrypt/live/piyasa.example.com/privkey.pem;
423
+
424
+ root /var/www/lamaste/apps/market-indicators/current;
425
+ index index.html;
426
+
427
+ # Every path of the shell: Gatekeeper first (the API's locations ask on their own)
428
+ auth_request /internal/lamaste/shell-authz;
429
+ error_page 401 =302 https://auth.example.com/?rd=https://piyasa.example.com$request_uri;
430
+ error_page 403 = /internal/lamaste/shell-authz;
431
+
432
+ location = /internal/lamaste/shell-authz {
433
+ internal;
434
+ auth_request off;
435
+ proxy_pass http://127.0.0.1:9294/authz/user-surface-shell/piyasa.example.com;
436
+ # ... as every Gatekeeper subrequest
437
+ }
438
+
439
+ # /internal/lamaste/user-surface-authz, -401 and the /api/market-indicators/app
440
+ # locations — exactly the user-surface locations of a bound tunnel
441
+
442
+ # A script, style, font or image the current tree lacks: the previous
443
+ # tree's (a page still running the old shell), else 404
444
+ location ~* [.](?:js|mjs|css|woff2?|ttf|otf|png|jpe?g|gif|webp|avif|ico)$ {
445
+ if ($request_method !~ ^(GET|HEAD)$) { return 405; }
446
+ if ($http_service_worker) { return 404; }
447
+ add_header Content-Security-Policy "default-src 'self'; script-src 'self'; ..." always;
448
+ # X-Content-Type-Options, X-Frame-Options DENY, Referrer-Policy, COOP, CORP, Cache-Control no-cache
449
+ types { text/javascript js mjs; text/css css; ... } # core WEB_TREE_CONTENT_TYPES
450
+ try_files $uri @previous;
451
+ }
452
+
453
+ # Everything else: the current tree's file, else the shell
454
+ location / {
455
+ # the same method check, worker refusal, headers and types
456
+ try_files $uri /index.html;
457
+ }
458
+
459
+ location @previous {
460
+ # the same headers and types
461
+ root /var/www/lamaste/apps/market-indicators/previous;
462
+ try_files $uri =404;
463
+ }
464
+ }
465
+ ```
466
+
467
+ `auth_request` sits at server level so every location of the shell asks Gatekeeper — the API's locations name their own subrequest, and the internal ones turn it `off`. The API's prefix location is `^~`, so the subresource regex never takes an API path. The shell's headers are set in its locations only: the API's answers are the panel's to head. A document, an SVG or a manifest is never served from the previous tree, and a service worker's script (`Service-Worker: script`) is always `404` — a worker would outlive the tree that installed it. The `types` block names each extension's content type from core's list (`lamaste-priv` accepts a `types` block in a location — `media/type ext…` entries only, no `include`) rather than trust the host's `mime.types`, which differs between nginx versions. `current` and `previous` are links the panel switches with a rename (see [Serving the tree](../04-api-reference/agent-hosted-plugins.md#serving-the-tree)); nginx follows them per request. While the surface is not served, the vhost answers everything with `503` (`Cache-Control: no-store`, a worker's script `404`) — and, once the plugin is uninstalled, `Clear-Site-Data: "storage"`.
468
+
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
+
471
+ ### 7. Public Tunnel Vhost (`lamalibre-lamaste-app-<fqdn>`, access mode: public)
414
472
 
415
473
  Created when a tunnel is configured with public access. Direct proxy with no authentication.
416
474
 
@@ -447,7 +505,7 @@ server {
447
505
 
448
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.
449
507
 
450
- ### 8. Authenticated Tunnel Vhost (`lamalibre-lamaste-app-<name>`, access mode: authenticated)
508
+ ### 8. Authenticated Tunnel Vhost (`lamalibre-lamaste-app-<fqdn>`, access mode: authenticated)
451
509
 
452
510
  Created when a tunnel requires Authelia login but no per-user grant checking. Any authenticated Authelia user can access the tunnel.
453
511
 
@@ -524,7 +582,7 @@ Key characteristics:
524
582
 
525
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.
526
584
 
527
- ### 9. Restricted Tunnel Vhost (`lamalibre-lamaste-app-<name>`, access mode: restricted)
585
+ ### 9. Restricted Tunnel Vhost (`lamalibre-lamaste-app-<fqdn>`, access mode: restricted)
528
586
 
529
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.
530
588
 
@@ -642,7 +700,7 @@ An `authenticated` or `restricted` app tunnel bound as the user surface of agent
642
700
  # The user surface of plugin "market-indicators"
643
701
  location = /api/market-indicators/app { ... } # the same block as below
644
702
 
645
- location /api/market-indicators/app/ {
703
+ location ^~ /api/market-indicators/app/ {
646
704
  auth_request /internal/lamaste/user-surface-authz;
647
705
  auth_request_set $relay_assertion $upstream_http_x_lamalibre_relay_assertion;
648
706
 
@@ -699,6 +757,16 @@ Gatekeeper authorizes the tunnel named in the path, and refuses a check whose `X
699
757
 
700
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.
701
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
+
702
770
  ## The backend cookie map
703
771
 
704
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
@@ -852,12 +920,12 @@ nginx's master process runs as root and opens the files a configuration names
852
920
  - its **name** is one the panel manages: `lamalibre-lamaste-panel-domain`, `-auth`, `-tunnel`, `-app-<subdomain>`, `-agent-panel-<label>` or `-site-<uuid>`. `lamalibre-lamaste-panel-ip` (written by the installer) may only be enabled or disabled — which is how panel 2FA turns the IP vhost off;
853
921
  - every **directive** is on the allow-list for its context, with arguments that cannot name a file outside what Lamaste serves:
854
922
 
855
- | Context | Allowed |
856
- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
857
- | top level | `server`, `map` (plain `key value;` entries, no `include`), `limit_req_zone` |
858
- | `server` | `listen 443` / `[::]:443` with optional `ssl` / `http2`; `server_name` (hostnames); `ssl_certificate(_key)` only `/etc/letsencrypt/live/<name>/{fullchain,privkey}.pem`; `include` only `/etc/nginx/snippets/lamalibre-lamaste-*.conf`; `root` only under `/var/www/lamaste/`; `ssl_protocols`, `ssl_ciphers`, `ssl_prefer_server_ciphers`, `client_max_body_size`, `index`, `add_header`, `proxy_set_header`, `error_page`, `return`, `rewrite`, `location` |
859
- | `location` | `proxy_pass` only `http://127.0.0.1:<port>[/path]`; `root` / `alias` only under `/var/www/lamaste/`; `proxy_set_header`, `proxy_http_version`, `proxy_read_timeout`, `proxy_send_timeout`, `proxy_buffers`, `proxy_next_upstream`, `proxy_pass_request_body`, `auth_request`, `auth_request_set`, `limit_req`, `try_files`, `index`, `add_header`, `error_page`, `client_max_body_size`, `internal`, `return`, `rewrite`, `if`, nested `location` |
860
- | `if` | `return`, `rewrite` |
923
+ | Context | Allowed |
924
+ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
925
+ | top level | `server`, `map` (plain `key value;` entries, no `include`), `limit_req_zone` |
926
+ | `server` | `listen 443` / `[::]:443` with optional `ssl` / `http2`; `server_name` (hostnames); `ssl_certificate(_key)` only `/etc/letsencrypt/live/<name>/{fullchain,privkey}.pem`; `include` only `/etc/nginx/snippets/lamalibre-lamaste-*.conf`; `root` only under `/var/www/lamaste/`; `auth_request` only a URI of the server or `off`; `ssl_protocols`, `ssl_ciphers`, `ssl_prefer_server_ciphers`, `client_max_body_size`, `index`, `add_header`, `proxy_set_header`, `error_page`, `return`, `rewrite`, `location` (a path, or a named location `@name`) |
927
+ | `location` | `proxy_pass` only `http://127.0.0.1:<port>[/path]`; `root` / `alias` only under `/var/www/lamaste/`; `proxy_set_header`, `proxy_http_version`, `proxy_read_timeout`, `proxy_send_timeout`, `proxy_buffers`, `proxy_next_upstream`, `proxy_pass_request_body`, `auth_request`, `auth_request_set`, `limit_req`, `try_files` (the last argument may name a location, `@name`), `index`, `add_header`, `error_page`, `client_max_body_size`, `internal`, `return`, `rewrite`, `if`, nested `location` |
928
+ | `if` | `return`, `rewrite` |
861
929
 
862
930
  Port 80 is not allowed: it belongs to the installer's HTTPS redirect and to certbot's HTTP-01 challenges. Backslashes, `${`, control characters and non-ASCII text outside comments are refused rather than interpreted, as is any directive not listed — including ones nginx would accept (`access_log`, `log_format`, `ssl_*` files elsewhere, `proxy_pass` to another host, ...). A refused `write` fails with `nginx site write failed for <name>: lamaste-priv: vhost line <n>: ...` and leaves the previous vhost in place.
863
931
 
@@ -6,7 +6,7 @@
6
6
 
7
7
  Lamaste has three main pieces:
8
8
 
9
- 1. **The Installer** (`npx @lamalibre/create-lamaste@4.5.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.5.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.5.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.5.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.5.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
 
@@ -10,9 +10,9 @@ The client has two modes. On first visit, it shows an onboarding wizard that wal
10
10
 
11
11
  ## Shared Panel Packages
12
12
 
13
- The management-mode admin pages (Dashboard, Tunnels, Sites, Users, Certificates, Services, Plugins, Tickets, Logs, Settings) live in `@lamalibre/lamaste-server-ui@4.5.0`. This package provides both the browser SPA build and the shared admin components consumed by `lamaste-desktop` (Tauri app).
13
+ The management-mode admin pages (Dashboard, Tunnels, Sites, Users, Certificates, Services, Plugins, Tickets, Logs, Settings) live in `@lamalibre/lamaste-server-ui@4.7.0`. This package provides both the browser SPA build and the shared admin components consumed by `lamaste-desktop` (Tauri app).
14
14
 
15
- The agent-mode pages (Dashboard, Tunnels, Services, Plugins, Logs, Settings) are extracted into a separate shared package: `@lamalibre/lamaste-agent-ui@4.5.0`. This package is consumed by `lamaste-desktop` for agent mode.
15
+ The agent-mode pages (Dashboard, Tunnels, Services, Plugins, Logs, Settings) are extracted into a separate shared package: `@lamalibre/lamaste-agent-ui@4.7.0`. This package is consumed by `lamaste-desktop` for agent mode.
16
16
 
17
17
  The shared package exposes an `AdminClientContext` abstraction that decouples admin UI pages from their data transport. Each host provides its own client implementation:
18
18
 
@@ -118,7 +118,7 @@ The shell renders the appropriate step component and shows a progress indicator
118
118
 
119
119
  ### Management Mode
120
120
 
121
- React Router handles navigation within the `Layout` component (sidebar + content area). Management page components are imported from the shared `@lamalibre/lamaste-server-ui@4.5.0` package and wrapped with the `AdminClientProvider` (which supplies the web-specific data client):
121
+ React Router handles navigation within the `Layout` component (sidebar + content area). Management page components are imported from the shared `@lamalibre/lamaste-server-ui@4.7.0` package and wrapped with the `AdminClientProvider` (which supplies the web-specific data client):
122
122
 
123
123
  | Path | Component | Description |
124
124
  | ------------------------ | ------------------- | ------------------------------------------------------------------- |
@@ -350,9 +350,9 @@ The app root wraps all content in five providers:
350
350
  ```
351
351
 
352
352
  1. **QueryClientProvider** — react-query cache with global defaults (`retry: 1`, `refetchOnWindowFocus: false`)
353
- 2. **AdminClientProvider** — from `@lamalibre/lamaste-server-ui@4.5.0`, supplies the host-specific data client (`webAdminClient` for browser fetch, Tauri `invoke` for desktop)
354
- 3. **ToastProvider** — from `@lamalibre/lamaste-server-ui@4.5.0`, notification toast system
355
- 4. **TwoFaProvider** — from `@lamalibre/lamaste-server-ui@4.5.0`, handles 2FA verification prompts when the panel has 2FA enabled
353
+ 2. **AdminClientProvider** — from `@lamalibre/lamaste-server-ui@4.7.0`, supplies the host-specific data client (`webAdminClient` for browser fetch, Tauri `invoke` for desktop)
354
+ 3. **ToastProvider** — from `@lamalibre/lamaste-server-ui@4.7.0`, notification toast system
355
+ 4. **TwoFaProvider** — from `@lamalibre/lamaste-server-ui@4.7.0`, handles 2FA verification prompts when the panel has 2FA enabled
356
356
  5. **BrowserRouter** — React Router for client-side navigation
357
357
 
358
358
  ## Documentation Viewer
@@ -453,7 +453,7 @@ See [nginx-configuration.md](./nginx-configuration.md) for detailed coverage.
453
453
 
454
454
  ### chisel-users.js — Chisel Credentials and Grants
455
455
 
456
- - Thin shim over `@lamalibre/lamaste@4.5.0/server`: per-agent credentials in `chisel-credentials.json` (0600)
456
+ - Thin shim over `@lamalibre/lamaste@4.7.0/server`: per-agent credentials in `chisel-credentials.json` (0600)
457
457
  - Renders the authfile `chisel-users` from the credentials and `tunnels.json`: each `agent-<label>` may bind exactly `^R:127\.0\.0\.1:<port>$` for its enabled tunnels
458
458
  - Adds the `lamaste-no-grants` sentinel user (password in `chisel-sentinel`, 0600) so the authfile is never empty — Chisel disables authentication on an empty authfile
459
459
  - Never grants a reserved port (3100, 9090, 9091, 9292, 9294) or a port claimed by two tunnels
@@ -536,7 +536,7 @@ Agent-to-agent authorization with scopes, instances, tickets, and sessions. Prov
536
536
  - **Periodic cleanup** — stale/dead instances, expired tickets, dead sessions
537
537
  - **Concurrency** — promise-chain mutex with atomic file writes (temp → fsync → rename)
538
538
 
539
- Client SDK: `@lamalibre/lamaste-tickets@4.5.0` (TypeScript, undici) provides `TicketClient`, `TicketInstanceManager` (source side), and `TicketSessionManager` (target side) for plugin integration.
539
+ Client SDK: `@lamalibre/lamaste-tickets@4.7.0` (TypeScript, undici) provides `TicketClient`, `TicketInstanceManager` (source side), and `TicketSessionManager` (target side) for plugin integration.
540
540
 
541
541
  State files: `/etc/lamalibre/lamaste/ticket-scopes.json` (scopes, instances, assignments) and `/etc/lamalibre/lamaste/tickets.json` (tickets, sessions).
542
542
 
@@ -329,7 +329,7 @@ Read returns `[]` for missing files (ENOENT), enabling the system to start with
329
329
  5. `DELETE /api/certs/agent/:label` (revocation) — the agent's app and plugin tunnels lose their `agentLabel` (unassigned), and its `agent-<label>` panel tunnel is removed
330
330
  6. At startup (background, fail closed): an ownerless `agent-<label>` panel tunnel is bound to the agent it names when that agent is active; other ownerless tunnels (from versions before tunnel ownership) are bound to the sole active agent, or left unassigned when there are several. Then the pinned Chisel binary, the unit and the authfile are re-established — and Chisel is stopped if they cannot be
331
331
 
332
- Every one of these workflows runs under the single tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.5.0/server`), with its validation inside the lock.
332
+ Every one of these workflows runs under the single tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.7.0/server`), with its validation inside the lock.
333
333
 
334
334
  ## Site State (`sites.json`)
335
335
 
@@ -391,7 +391,7 @@ The Panel Server writes Authelia's files itself, without sudo (`writeAutheliaFil
391
391
  2. `rename()`s it over the destination — atomic on the same filesystem, so Authelia never reads a partial file
392
392
  3. The mode decides Authelia's access: `users.yml` `0660` (Authelia rewrites it when a user changes a password), `configuration.yml` `0640`, `.secrets.json` `0600` (the panel only)
393
393
 
394
- **Critical invariant:** After writing `users.yml`, the Authelia service must be restarted to pick up changes. Authelia does not watch the file for changes. The Panel Server calls `reloadAuthelia()` (which runs `systemctl restart authelia`) after every user modification.
394
+ **Critical invariant:** Authelia must see every write of `users.yml`. 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. Every write goes through one file lock in the panel (`lib/authelia.js`), which also keeps the groups Lamaste owns as the last rendering decided.
395
395
 
396
396
  **Safety rule:** The Panel Server prevents deleting the last user in the Authelia database. Without at least one user, no one could authenticate to access tunneled apps.
397
397
 
@@ -482,7 +482,7 @@ The PKI directory belongs to `lamaste` because the panel is the CA (it signs age
482
482
 
483
483
  The Panel Server is a single-process Node.js application, but a single-threaded event loop does not serialize asynchronous workflows: two requests that each read `tunnels.json`, await a certificate or an nginx test, and then write it back would interleave, and both could pass the same uniqueness check. Serialization is therefore explicit:
484
484
 
485
- **Tunnel state** is guarded by one promise-chain lock, `withTunnelLock` in `@lamalibre/lamaste@4.5.0/server`'s `tunnels.ts`. Every tunnel workflow — create, delete, toggle, assign, reconfigure, the combined `PATCH /api/tunnels/:id`, releasing a revoked agent's tunnels, and startup reconciliation — takes it for its whole duration, and does its validation (subdomain and port uniqueness, reserved ports, agent checks, authorization against the current entry) inside it.
485
+ **Tunnel state** is guarded by one promise-chain lock, `withTunnelLock` in `@lamalibre/lamaste@4.7.0/server`'s `tunnels.ts`. Every tunnel workflow — create, delete, toggle, assign, reconfigure, the combined `PATCH /api/tunnels/:id`, releasing a revoked agent's tunnels, and startup reconciliation — takes it for its whole duration, and does its validation (subdomain and port uniqueness, reserved ports, agent checks, authorization against the current entry) inside it.
486
486
 
487
487
  **Chisel authfile renders** are serialized by a promise-chain mutex in `chisel-users.ts`, keyed by the credentials file, around every render (enroll, revoke, rotate, tunnel change). The daemon's `syncChisel()` renders after the tunnel state is written and restarts Chisel (`systemctl try-restart`) only when the render withdrew something.
488
488
 
@@ -58,7 +58,7 @@
58
58
  │ │ │ │
59
59
  │ │ Lamaste Desktop (Tauri v2) — dual mode │ │
60
60
  │ │ ├─ Agent mode: service discovery + tunnel management │ │
61
- │ │ │ (pages from @lamalibre/lamaste-agent-ui@4.5.0) │ │
61
+ │ │ │ (pages from @lamalibre/lamaste-agent-ui@4.7.0) │ │
62
62
  │ │ ├─ Server mode: full admin panel (via lamaste-server-ui) │ │
63
63
  │ │ ├─ Multi-server registry (~/.lamalibre/lamaste/servers.json) │ │
64
64
  │ │ ├─ Cloud provisioning (DigitalOcean via lamaste-cloud) │ │