@lamalibre/create-lamaste 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/LICENSE.md +118 -0
  2. package/README.md +114 -0
  3. package/bin/create-lamaste.js +15 -0
  4. package/package.json +54 -0
  5. package/scripts/bundle-vendor.js +110 -0
  6. package/scripts/lamaste-cert-info +62 -0
  7. package/scripts/lamaste-certbot +96 -0
  8. package/scripts/lamaste-priv +693 -0
  9. package/src/index.js +646 -0
  10. package/src/lib/accounts.js +86 -0
  11. package/src/lib/binaries.js +137 -0
  12. package/src/lib/cert-help-page.js +160 -0
  13. package/src/lib/env.js +132 -0
  14. package/src/lib/http-redirect.js +38 -0
  15. package/src/lib/ownership.js +231 -0
  16. package/src/lib/relay-services.js +103 -0
  17. package/src/lib/secrets.js +19 -0
  18. package/src/lib/service-config.js +311 -0
  19. package/src/lib/summary.js +131 -0
  20. package/src/lib/wrappers.js +63 -0
  21. package/src/tasks/gatekeeper.js +233 -0
  22. package/src/tasks/harden.js +293 -0
  23. package/src/tasks/mtls.js +193 -0
  24. package/src/tasks/nginx.js +265 -0
  25. package/src/tasks/node.js +110 -0
  26. package/src/tasks/panel.js +408 -0
  27. package/src/tasks/redeploy.js +438 -0
  28. package/vendor/docs/00-introduction/how-it-works.md +447 -0
  29. package/vendor/docs/00-introduction/quickstart.md +472 -0
  30. package/vendor/docs/00-introduction/what-is-lamaste.md +112 -0
  31. package/vendor/docs/01-concepts/authentication.md +464 -0
  32. package/vendor/docs/01-concepts/authorization.md +242 -0
  33. package/vendor/docs/01-concepts/certificates.md +452 -0
  34. package/vendor/docs/01-concepts/dns-and-domains.md +343 -0
  35. package/vendor/docs/01-concepts/mtls.md +490 -0
  36. package/vendor/docs/01-concepts/nginx-reverse-proxy.md +540 -0
  37. package/vendor/docs/01-concepts/security-model.md +600 -0
  38. package/vendor/docs/01-concepts/tickets.md +414 -0
  39. package/vendor/docs/01-concepts/tunneling.md +367 -0
  40. package/vendor/docs/02-guides/agent-setup.md +194 -0
  41. package/vendor/docs/02-guides/certificate-management.md +341 -0
  42. package/vendor/docs/02-guides/cloud-provisioning.md +406 -0
  43. package/vendor/docs/02-guides/desktop-app-setup.md +226 -0
  44. package/vendor/docs/02-guides/disaster-recovery.md +464 -0
  45. package/vendor/docs/02-guides/first-tunnel.md +230 -0
  46. package/vendor/docs/02-guides/installation.md +393 -0
  47. package/vendor/docs/02-guides/managing-users.md +255 -0
  48. package/vendor/docs/02-guides/onboarding.md +244 -0
  49. package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +375 -0
  50. package/vendor/docs/02-guides/static-sites.md +364 -0
  51. package/vendor/docs/03-architecture/e2e-three-vm-sequences.md +457 -0
  52. package/vendor/docs/03-architecture/installer-flow.md +151 -0
  53. package/vendor/docs/03-architecture/installer.md +445 -0
  54. package/vendor/docs/03-architecture/management-flow.md +288 -0
  55. package/vendor/docs/03-architecture/nginx-configuration.md +830 -0
  56. package/vendor/docs/03-architecture/onboarding-flow.md +164 -0
  57. package/vendor/docs/03-architecture/overview.md +541 -0
  58. package/vendor/docs/03-architecture/panel-client.md +416 -0
  59. package/vendor/docs/03-architecture/panel-server.md +695 -0
  60. package/vendor/docs/03-architecture/state-management.md +529 -0
  61. package/vendor/docs/03-architecture/system-overview.md +167 -0
  62. package/vendor/docs/04-api-reference/certificates.md +1141 -0
  63. package/vendor/docs/04-api-reference/gatekeeper.md +592 -0
  64. package/vendor/docs/04-api-reference/onboarding.md +473 -0
  65. package/vendor/docs/04-api-reference/overview.md +404 -0
  66. package/vendor/docs/04-api-reference/services.md +267 -0
  67. package/vendor/docs/04-api-reference/settings.md +343 -0
  68. package/vendor/docs/04-api-reference/sites.md +716 -0
  69. package/vendor/docs/04-api-reference/system.md +268 -0
  70. package/vendor/docs/04-api-reference/tickets.md +618 -0
  71. package/vendor/docs/04-api-reference/tunnels.md +417 -0
  72. package/vendor/docs/04-api-reference/users.md +333 -0
  73. package/vendor/docs/05-operations/backup-and-restore.md +342 -0
  74. package/vendor/docs/05-operations/monitoring.md +285 -0
  75. package/vendor/docs/05-operations/uninstalling.md +344 -0
  76. package/vendor/docs/05-operations/upgrades.md +270 -0
  77. package/vendor/docs/06-reference/config-files.md +1080 -0
  78. package/vendor/docs/06-reference/glossary.md +528 -0
  79. package/vendor/docs/06-reference/installer-flags.md +260 -0
  80. package/vendor/docs/06-reference/ports-and-services.md +244 -0
  81. package/vendor/docs/06-reference/troubleshooting.md +665 -0
  82. package/vendor/docs/_index.json +233 -0
  83. package/vendor/docs/gatekeeper-design.md +953 -0
  84. package/vendor/docs/package.json +15 -0
  85. package/vendor/docs/todo/lib-cli-desktop-refactor.md +641 -0
  86. package/vendor/gatekeeper/dist/boot.d.ts +1 -0
  87. package/vendor/gatekeeper/dist/boot.d.ts.map +1 -0
  88. package/vendor/gatekeeper/dist/boot.js +17 -0
  89. package/vendor/gatekeeper/dist/boot.js.map +1 -0
  90. package/vendor/gatekeeper/dist/cli/commands/access.d.ts +2 -0
  91. package/vendor/gatekeeper/dist/cli/commands/access.d.ts.map +1 -0
  92. package/vendor/gatekeeper/dist/cli/commands/access.js +33 -0
  93. package/vendor/gatekeeper/dist/cli/commands/access.js.map +1 -0
  94. package/vendor/gatekeeper/dist/cli/commands/grants.d.ts +2 -0
  95. package/vendor/gatekeeper/dist/cli/commands/grants.d.ts.map +1 -0
  96. package/vendor/gatekeeper/dist/cli/commands/grants.js +110 -0
  97. package/vendor/gatekeeper/dist/cli/commands/grants.js.map +1 -0
  98. package/vendor/gatekeeper/dist/cli/commands/groups.d.ts +2 -0
  99. package/vendor/gatekeeper/dist/cli/commands/groups.d.ts.map +1 -0
  100. package/vendor/gatekeeper/dist/cli/commands/groups.js +98 -0
  101. package/vendor/gatekeeper/dist/cli/commands/groups.js.map +1 -0
  102. package/vendor/gatekeeper/dist/cli/index.d.ts +3 -0
  103. package/vendor/gatekeeper/dist/cli/index.d.ts.map +1 -0
  104. package/vendor/gatekeeper/dist/cli/index.js +45 -0
  105. package/vendor/gatekeeper/dist/cli/index.js.map +1 -0
  106. package/vendor/gatekeeper/dist/index.d.ts +9 -0
  107. package/vendor/gatekeeper/dist/index.d.ts.map +1 -0
  108. package/vendor/gatekeeper/dist/index.js +13 -0
  109. package/vendor/gatekeeper/dist/index.js.map +1 -0
  110. package/vendor/gatekeeper/dist/lib/authz.d.ts +16 -0
  111. package/vendor/gatekeeper/dist/lib/authz.d.ts.map +1 -0
  112. package/vendor/gatekeeper/dist/lib/authz.js +42 -0
  113. package/vendor/gatekeeper/dist/lib/authz.js.map +1 -0
  114. package/vendor/gatekeeper/dist/lib/cache-bust.d.ts +15 -0
  115. package/vendor/gatekeeper/dist/lib/cache-bust.d.ts.map +1 -0
  116. package/vendor/gatekeeper/dist/lib/cache-bust.js +19 -0
  117. package/vendor/gatekeeper/dist/lib/cache-bust.js.map +1 -0
  118. package/vendor/gatekeeper/dist/lib/constants.d.ts +82 -0
  119. package/vendor/gatekeeper/dist/lib/constants.d.ts.map +1 -0
  120. package/vendor/gatekeeper/dist/lib/constants.js +79 -0
  121. package/vendor/gatekeeper/dist/lib/constants.js.map +1 -0
  122. package/vendor/gatekeeper/dist/lib/grants.d.ts +32 -0
  123. package/vendor/gatekeeper/dist/lib/grants.d.ts.map +1 -0
  124. package/vendor/gatekeeper/dist/lib/grants.js +314 -0
  125. package/vendor/gatekeeper/dist/lib/grants.js.map +1 -0
  126. package/vendor/gatekeeper/dist/lib/groups.d.ts +10 -0
  127. package/vendor/gatekeeper/dist/lib/groups.d.ts.map +1 -0
  128. package/vendor/gatekeeper/dist/lib/groups.js +247 -0
  129. package/vendor/gatekeeper/dist/lib/groups.js.map +1 -0
  130. package/vendor/gatekeeper/dist/lib/migration.d.ts +17 -0
  131. package/vendor/gatekeeper/dist/lib/migration.d.ts.map +1 -0
  132. package/vendor/gatekeeper/dist/lib/migration.js +79 -0
  133. package/vendor/gatekeeper/dist/lib/migration.js.map +1 -0
  134. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0001_groups.sql +7 -0
  135. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0002_access_grants.sql +14 -0
  136. package/vendor/gatekeeper/dist/lib/migrations/gatekeeper/0003_access_request_log.sql +9 -0
  137. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0001_groups.sql +7 -0
  138. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0002_access_grants.sql +14 -0
  139. package/vendor/gatekeeper/dist/lib/migrations/migrations/gatekeeper/0003_access_request_log.sql +9 -0
  140. package/vendor/gatekeeper/dist/lib/state-db.d.ts +16 -0
  141. package/vendor/gatekeeper/dist/lib/state-db.d.ts.map +1 -0
  142. package/vendor/gatekeeper/dist/lib/state-db.js +154 -0
  143. package/vendor/gatekeeper/dist/lib/state-db.js.map +1 -0
  144. package/vendor/gatekeeper/dist/lib/templates.d.ts +21 -0
  145. package/vendor/gatekeeper/dist/lib/templates.d.ts.map +1 -0
  146. package/vendor/gatekeeper/dist/lib/templates.js +151 -0
  147. package/vendor/gatekeeper/dist/lib/templates.js.map +1 -0
  148. package/vendor/gatekeeper/dist/lib/types.d.ts +178 -0
  149. package/vendor/gatekeeper/dist/lib/types.d.ts.map +1 -0
  150. package/vendor/gatekeeper/dist/lib/types.js +2 -0
  151. package/vendor/gatekeeper/dist/lib/types.js.map +1 -0
  152. package/vendor/gatekeeper/dist/server/index.d.ts +15 -0
  153. package/vendor/gatekeeper/dist/server/index.d.ts.map +1 -0
  154. package/vendor/gatekeeper/dist/server/index.js +251 -0
  155. package/vendor/gatekeeper/dist/server/index.js.map +1 -0
  156. package/vendor/gatekeeper/dist/server/routes/access-request.d.ts +7 -0
  157. package/vendor/gatekeeper/dist/server/routes/access-request.d.ts.map +1 -0
  158. package/vendor/gatekeeper/dist/server/routes/access-request.js +17 -0
  159. package/vendor/gatekeeper/dist/server/routes/access-request.js.map +1 -0
  160. package/vendor/gatekeeper/dist/server/routes/authz.d.ts +3 -0
  161. package/vendor/gatekeeper/dist/server/routes/authz.d.ts.map +1 -0
  162. package/vendor/gatekeeper/dist/server/routes/authz.js +279 -0
  163. package/vendor/gatekeeper/dist/server/routes/authz.js.map +1 -0
  164. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts +13 -0
  165. package/vendor/gatekeeper/dist/server/routes/diagnostic.d.ts.map +1 -0
  166. package/vendor/gatekeeper/dist/server/routes/diagnostic.js +249 -0
  167. package/vendor/gatekeeper/dist/server/routes/diagnostic.js.map +1 -0
  168. package/vendor/gatekeeper/dist/server/routes/grants.d.ts +3 -0
  169. package/vendor/gatekeeper/dist/server/routes/grants.d.ts.map +1 -0
  170. package/vendor/gatekeeper/dist/server/routes/grants.js +112 -0
  171. package/vendor/gatekeeper/dist/server/routes/grants.js.map +1 -0
  172. package/vendor/gatekeeper/dist/server/routes/groups.d.ts +3 -0
  173. package/vendor/gatekeeper/dist/server/routes/groups.d.ts.map +1 -0
  174. package/vendor/gatekeeper/dist/server/routes/groups.js +139 -0
  175. package/vendor/gatekeeper/dist/server/routes/groups.js.map +1 -0
  176. package/vendor/gatekeeper/package.json +49 -0
  177. package/vendor/server/bin/lamaste-server.js +204 -0
  178. package/vendor/server/package.json +31 -0
  179. package/vendor/server/src/commands/certs.js +224 -0
  180. package/vendor/server/src/commands/chisel.js +132 -0
  181. package/vendor/server/src/commands/logs.js +49 -0
  182. package/vendor/server/src/commands/plugins.js +321 -0
  183. package/vendor/server/src/commands/reset-admin.js +478 -0
  184. package/vendor/server/src/commands/restart.js +94 -0
  185. package/vendor/server/src/commands/sites.js +257 -0
  186. package/vendor/server/src/commands/status.js +158 -0
  187. package/vendor/server/src/commands/tunnels.js +365 -0
  188. package/vendor/server/src/commands/uninstall.js +326 -0
  189. package/vendor/server/src/config.js +38 -0
  190. package/vendor/server/src/exec.js +26 -0
  191. package/vendor/server/src/logger.js +39 -0
  192. package/vendor/server/src/ndjson.js +56 -0
  193. package/vendor/server/src/panel-api.js +85 -0
  194. package/vendor/server/src/state.js +45 -0
  195. package/vendor/server-ui/dist/assets/index-B0nWuUyL.js +257 -0
  196. package/vendor/server-ui/dist/assets/index-BqWWFlTU.css +1 -0
  197. package/vendor/server-ui/dist/index.html +13 -0
  198. package/vendor/serverd/package.json +44 -0
  199. package/vendor/serverd/src/boot.js +13 -0
  200. package/vendor/serverd/src/index.js +191 -0
  201. package/vendor/serverd/src/lib/app-error.js +14 -0
  202. package/vendor/serverd/src/lib/authelia.js +85 -0
  203. package/vendor/serverd/src/lib/certbot.js +74 -0
  204. package/vendor/serverd/src/lib/chisel-args.js +6 -0
  205. package/vendor/serverd/src/lib/chisel-reconcile.js +232 -0
  206. package/vendor/serverd/src/lib/chisel-runtime.js +46 -0
  207. package/vendor/serverd/src/lib/chisel-users.js +109 -0
  208. package/vendor/serverd/src/lib/chisel.js +81 -0
  209. package/vendor/serverd/src/lib/config.js +190 -0
  210. package/vendor/serverd/src/lib/constants.js +8 -0
  211. package/vendor/serverd/src/lib/csr-signing.js +331 -0
  212. package/vendor/serverd/src/lib/enrollment.js +302 -0
  213. package/vendor/serverd/src/lib/files.js +19 -0
  214. package/vendor/serverd/src/lib/gatekeeper-client.js +135 -0
  215. package/vendor/serverd/src/lib/invite-page.js +281 -0
  216. package/vendor/serverd/src/lib/ip-utils.js +79 -0
  217. package/vendor/serverd/src/lib/migrations/state/0001_enrollment_tokens.sql +17 -0
  218. package/vendor/serverd/src/lib/migrations/state/0002_tickets.sql +70 -0
  219. package/vendor/serverd/src/lib/migrations/state/0003_plugins.sql +15 -0
  220. package/vendor/serverd/src/lib/migrations/state/0004_agents.sql +19 -0
  221. package/vendor/serverd/src/lib/mtls.js +795 -0
  222. package/vendor/serverd/src/lib/nginx.js +1090 -0
  223. package/vendor/serverd/src/lib/pki-paths.js +41 -0
  224. package/vendor/serverd/src/lib/pki-sign.js +103 -0
  225. package/vendor/serverd/src/lib/plugins.js +538 -0
  226. package/vendor/serverd/src/lib/push-install.js +390 -0
  227. package/vendor/serverd/src/lib/revocation.js +112 -0
  228. package/vendor/serverd/src/lib/services.js +131 -0
  229. package/vendor/serverd/src/lib/session.js +151 -0
  230. package/vendor/serverd/src/lib/state-db.js +130 -0
  231. package/vendor/serverd/src/lib/state.js +140 -0
  232. package/vendor/serverd/src/lib/storage.js +63 -0
  233. package/vendor/serverd/src/lib/system-stats.js +58 -0
  234. package/vendor/serverd/src/lib/tickets.js +1556 -0
  235. package/vendor/serverd/src/lib/totp.js +76 -0
  236. package/vendor/serverd/src/lib/tunnel-deps.js +41 -0
  237. package/vendor/serverd/src/lib/user-access-session.js +142 -0
  238. package/vendor/serverd/src/lib/user-access.js +52 -0
  239. package/vendor/serverd/src/middleware/errors.js +63 -0
  240. package/vendor/serverd/src/middleware/mtls.js +120 -0
  241. package/vendor/serverd/src/middleware/onboarding-guard.js +30 -0
  242. package/vendor/serverd/src/middleware/role-guard.js +58 -0
  243. package/vendor/serverd/src/middleware/twofa-session.js +65 -0
  244. package/vendor/serverd/src/middleware/user-access-session.js +52 -0
  245. package/vendor/serverd/src/routes/enrollment.js +120 -0
  246. package/vendor/serverd/src/routes/health.js +22 -0
  247. package/vendor/serverd/src/routes/invite.js +163 -0
  248. package/vendor/serverd/src/routes/management/agents.js +182 -0
  249. package/vendor/serverd/src/routes/management/certs.js +911 -0
  250. package/vendor/serverd/src/routes/management/gatekeeper-proxy.js +160 -0
  251. package/vendor/serverd/src/routes/management/identity.js +107 -0
  252. package/vendor/serverd/src/routes/management/invitations.js +166 -0
  253. package/vendor/serverd/src/routes/management/logs.js +218 -0
  254. package/vendor/serverd/src/routes/management/plugins.js +560 -0
  255. package/vendor/serverd/src/routes/management/services.js +63 -0
  256. package/vendor/serverd/src/routes/management/settings.js +325 -0
  257. package/vendor/serverd/src/routes/management/sites.js +522 -0
  258. package/vendor/serverd/src/routes/management/storage.js +192 -0
  259. package/vendor/serverd/src/routes/management/system.js +201 -0
  260. package/vendor/serverd/src/routes/management/tickets.js +457 -0
  261. package/vendor/serverd/src/routes/management/tunnels.js +709 -0
  262. package/vendor/serverd/src/routes/management/users.js +377 -0
  263. package/vendor/serverd/src/routes/management.js +36 -0
  264. package/vendor/serverd/src/routes/onboarding/dns.js +101 -0
  265. package/vendor/serverd/src/routes/onboarding/domain.js +42 -0
  266. package/vendor/serverd/src/routes/onboarding/index.js +18 -0
  267. package/vendor/serverd/src/routes/onboarding/provision.js +327 -0
  268. package/vendor/serverd/src/routes/onboarding/status.js +12 -0
  269. package/vendor/serverd/src/routes/plugin-router.js +79 -0
  270. package/vendor/serverd/src/routes/user-access.js +389 -0
@@ -0,0 +1,375 @@
1
+ # Standalone Plugin with Lamaste Tickets
2
+
3
+ > This guide walks through deploying a plugin (e.g., Sync) as a standalone server that uses Lamaste's tunneling and ticket system for authorization — without running the plugin as a Lamaste panel plugin.
4
+
5
+ ## Scenario
6
+
7
+ You are an admin who has:
8
+
9
+ 1. A **Lamaste server** running on a DigitalOcean droplet
10
+ 2. A **Mac** with a Lamaste agent enrolled and a standalone Sync server installed
11
+ 3. A **Raspberry Pi** running a Sync agent that needs to sync files with the Sync server
12
+
13
+ The Sync server is **not** a Lamaste plugin — it runs as its own process on its own port. However, it leverages Lamaste for:
14
+
15
+ - **Tunneling** — making the Sync server reachable through a Lamaste subdomain
16
+ - **Tickets** — authorizing which agents can connect to the Sync server
17
+
18
+ ## Step-by-Step Walkthrough
19
+
20
+ ### 1. Install the Lamaste Desktop App
21
+
22
+ The desktop app is a management shell for existing CLI tools — it does not install them. If the Lamaste agent CLI or Sync CLI are not already installed, install them first.
23
+
24
+ ### 2. Provision a Lamaste Server
25
+
26
+ Using the desktop app's cloud provisioning wizard:
27
+
28
+ - Enter your DigitalOcean API token
29
+ - Select a region and droplet size
30
+ - The wizard calls `@lamalibre/lamaste-cloud@4.0.0` to create the droplet
31
+ - Progress streams back as NDJSON events in the desktop UI
32
+ - Complete onboarding in the browser panel
33
+
34
+ ### 3. Enroll a Lamaste Agent on the Mac
35
+
36
+ From the Lamaste panel, create an enrollment token with the `plugin:sync:connect` capability (in addition to the default `tunnels:read`). On the Mac, run `lamaste-agent setup` with that token.
37
+
38
+ ```mermaid
39
+ sequenceDiagram
40
+ participant Admin as Admin (Desktop)
41
+ participant Panel as Lamaste Panel
42
+ participant Mac as Mac (Agent CLI)
43
+
44
+ Admin->>Panel: POST /api/certs/agent/enroll<br/>{label: "macbook-pro",<br/>capabilities: ["tunnels:read", "plugin:sync:connect"]}
45
+ Note over Panel: Generate 32-byte token<br/>10-minute expiry<br/>Store in enrollment-tokens.json
46
+ Panel-->>Admin: {ok, token, expiresAt}
47
+
48
+ Admin-->>Mac: Out-of-band: token + panel URL
49
+
50
+ Mac->>Panel: POST /api/enroll<br/>{token, csr: "<PEM CSR>"}
51
+ Note over Panel: Validate token (HMAC-SHA256 timing-safe)<br/>Consume token (mark used)<br/>Sign CSR with panel CA<br/>Subject: CN=agent:macbook-pro/O=Lamaste<br/>Validity: 730 days<br/>Add to agent registry with capabilities:<br/>["tunnels:read", "plugin:sync:connect"]
52
+ Panel-->>Mac: {ok, cert, caCert, label, serial, expiresAt}
53
+ Note over Mac: Save cert + CA to ~/.lamalibre/lamaste/<br/>Agent is now enrolled
54
+ ```
55
+
56
+ ### 4. Install the Sync Server Standalone on the Mac
57
+
58
+ Run the Sync installer on the Mac:
59
+
60
+ ```
61
+ npx @lamalibre/create-sync
62
+ ```
63
+
64
+ The installer walks through an interactive setup wizard. On first run, the Sync server generates a one-time **setup token** (printed to console, never persisted). The admin enters this token to generate an **API key** that secures all subsequent API access.
65
+
66
+ ```mermaid
67
+ sequenceDiagram
68
+ participant Admin as Admin (Mac terminal)
69
+ participant Installer as create-sync wizard
70
+ participant Server as Sync Server (Mac)
71
+
72
+ Admin->>Installer: npx @lamalibre/create-sync
73
+
74
+ Note over Installer: Check Node.js version<br/>Check rclone installed<br/>Select: Standalone mode
75
+
76
+ Installer->>Server: GET /api/sync/health
77
+ Note over Server: First startup:<br/>No API key exists yet<br/>Generate one-time setup token<br/>Print to console (in-memory only)
78
+ Server-->>Installer: {ok: true}
79
+
80
+ Admin-->>Installer: Enter setup token (from console)
81
+
82
+ Installer->>Server: POST /api/sync/setup/api-key<br/>X-Setup-Token: <token>
83
+ Note over Server: Timing-safe token verification<br/>Generate API key: sync_<32-byte-hex><br/>Store SHA-256 hash in sync-config.json<br/>Discard setup token (one-time use)
84
+ Server-->>Installer: {ok, apiKey: "sync_..."}
85
+
86
+ Note over Admin: Admin must save this API key<br/>It cannot be retrieved again
87
+
88
+ Installer->>Server: PATCH /api/sync/storage<br/>Authorization: Bearer <apiKey><br/>{provider, bucket, accessKey, secretKey, region}
89
+ Note over Server: Test connection to storage<br/>Encrypt credentials with master key<br/>Save to sync-config.json
90
+ Server-->>Installer: {ok}
91
+
92
+ Note over Installer: Optionally: create first project<br/>Install launchd/systemd service<br/>Server is now running
93
+ ```
94
+
95
+ The Sync server now runs as a local service on the Mac. It operates independently of Lamaste — it has its own API key, its own storage config, and its own agent registry at `~/.sync/agents.json`.
96
+
97
+ ### 5. Register Sync Scopes and Instance on Lamaste
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.0.0` — the admin does not need to do anything.
100
+
101
+ ```mermaid
102
+ sequenceDiagram
103
+ participant Sync as Sync Server (Mac)
104
+ participant Panel as Lamaste Panel
105
+
106
+ Note over Sync: Startup: detect Lamaste agent cert<br/>at ~/.lamalibre/lamaste/ → enter plugin mode<br/>mTLS auth: CN=agent:macbook-pro
107
+
108
+ Sync->>Panel: POST /api/tickets/scopes<br/>{name: "sync", version: "1.0.0",<br/>scopes: [{name: "plugin:sync:connect",<br/>instanceScoped: true}],<br/>transport: {strategies: ["tunnel"],<br/>preferred: "tunnel"}}
109
+ Note over Panel: Store scope in ticket-scopes.json<br/>Refresh capability set<br/>(plugin:sync:connect now assignable)
110
+ Panel-->>Sync: {ok, registered: ["plugin:sync:connect"]}
111
+
112
+ Sync->>Panel: POST /api/tickets/instances<br/>{scope: "plugin:sync:connect",<br/>transport: {strategies: ["tunnel"],<br/>preferred: "tunnel"}}
113
+ Note over Panel: Generate instanceId (16 bytes hex)<br/>Bind to agent label: macbook-pro<br/>Status: active
114
+ Panel-->>Sync: {ok, instanceId,<br/>instanceScope: "plugin:sync:connect:<id>"}
115
+
116
+ loop Every 60 seconds
117
+ Sync->>Panel: POST /api/tickets/instances/<instanceId>/heartbeat
118
+ Note over Panel: Update lastHeartbeat<br/>Keep status: active
119
+ Panel-->>Sync: {ok}
120
+ end
121
+ ```
122
+
123
+ ### 6. Create a Tunnel for the Sync Server
124
+
125
+ The admin creates a tunnel through Lamaste so the Sync server is reachable from the internet at a subdomain (e.g., `sync.your-domain.com`). The tunnel forwards traffic from the Lamaste droplet through the Mac's Lamaste agent to the local Sync server port. All Lamaste tunnel subdomains are protected by Authelia — unauthenticated requests are redirected to the login portal.
126
+
127
+ ### 7. Admin Prepares Agent Enrollment
128
+
129
+ Before the Raspberry Pi can connect, the admin must prepare its enrollment. This is a two-part process: the admin creates the enrollment on the Sync server, and the Sync server pre-announces it to the Lamaste panel.
130
+
131
+ The admin creates a new agent enrollment on the Sync server (e.g., via the Sync CLI or admin API). Because the Sync server detects it is running as a Lamaste agent, it automatically pre-announces to the Lamaste panel, requesting a delegated enrollment token for the new agent.
132
+
133
+ ```mermaid
134
+ sequenceDiagram
135
+ participant Admin as Admin (Mac)
136
+ participant Sync as Sync Server (Mac)
137
+ participant Panel as Lamaste Panel
138
+
139
+ Admin->>Sync: Create enrollment for agent "rpi-sync"<br/>Authorization: Bearer <apiKey>
140
+
141
+ Note over Sync: Detect: has Lamaste agent cert<br/>→ pre-announce to Lamaste
142
+
143
+ rect rgb(40, 40, 60)
144
+ Note over Sync,Panel: Pre-announcement (mTLS: CN=agent:macbook-pro)
145
+ Sync->>Panel: POST /api/certs/agent/enroll-delegated<br/>{pluginAgentLabel: "rpi-sync",<br/>scope: "plugin:sync:connect"}
146
+ Note over Panel: Verify caller is agent (not plugin-agent)<br/>Verify plugin:sync:connect is a registered ticket scope<br/>Verify plugin:sync:connect is NOT a base capability<br/>Verify agent owns active instance for scope<br/>Generate delegated token (32 bytes, 10-min expiry)<br/>Store with type: "delegated",<br/>delegatedBy: "macbook-pro",<br/>label: "plugin-agent:macbook-pro:rpi-sync",<br/>capabilities: ["plugin:sync:connect"]
147
+ Panel-->>Sync: {ok, enrollmentToken,<br/>expiresAt, pluginAgentLabel}
148
+ end
149
+
150
+ Sync-->>Admin: Enrollment prepared:<br/>- Sync API key (already known)<br/>- Sync server URL: sync.your-domain.com<br/>- Lamaste delegated token<br/>- Lamaste panel URL
151
+
152
+ Note over Admin: Admin now has everything needed<br/>to set up the Raspberry Pi
153
+ ```
154
+
155
+ The admin now has four pieces of information to give to the Raspberry Pi:
156
+
157
+ | Credential | Purpose | Source |
158
+ | ----------------------- | ---------------------------------------- | --------------------------------------------------- |
159
+ | Sync API key | Authenticate with the Sync server | From step 4 (already known) |
160
+ | Sync server URL | Where to reach the Sync server | `https://sync.your-domain.com` (the tunnel) |
161
+ | Lamaste delegated token | Enroll with Lamaste to get a certificate | From the pre-announcement (one-time, 10-min expiry) |
162
+ | Lamaste panel URL | Where to submit the enrollment | `https://your-domain.com` |
163
+
164
+ ### 8. Raspberry Pi Enrolls with Lamaste
165
+
166
+ The Raspberry Pi must enroll with Lamaste **before** it can access the tunnel. The Lamaste panel's `/api/enroll` endpoint is public — no mTLS or Authelia required. The delegated token is the sole authentication gate.
167
+
168
+ ```mermaid
169
+ sequenceDiagram
170
+ participant Admin as Admin
171
+ participant RPi as Raspberry Pi
172
+ participant Panel as Lamaste Panel
173
+
174
+ Admin-->>RPi: Out-of-band: delegated token +<br/>panel URL + API key + tunnel URL
175
+
176
+ Note over RPi: Step 1: Enroll with Lamaste FIRST<br/>(before accessing the tunnel)<br/>Generate RSA 4096-bit keypair<br/>Create CSR via openssl req
177
+
178
+ rect rgb(40, 60, 40)
179
+ Note over RPi,Panel: Lamaste enrollment (public endpoint — no mTLS, no Authelia)
180
+ RPi->>Panel: POST /api/enroll<br/>{token: delegatedToken,<br/>csr: "<PEM CSR>"}
181
+ Note over Panel: Validate token (HMAC-SHA256 timing-safe)<br/>Verify delegating agent (macbook-pro)<br/>still registered and not revoked<br/>Consume token (mark used)<br/>Sign CSR with panel CA<br/>Subject: CN=plugin-agent:macbook-pro:rpi-sync<br/>Validity: 730 days<br/>Add to agent registry:<br/>enrollmentMethod: "delegated"<br/>delegatedBy: "macbook-pro"<br/>capabilities: ["plugin:sync:connect"]
182
+ Panel-->>RPi: {ok, cert, caCert,<br/>label: "plugin-agent:macbook-pro:rpi-sync",<br/>serial, expiresAt}
183
+ end
184
+
185
+ Note over RPi: Save Lamaste cert files to<br/>~/.sync-agent/lamaste-cert/<br/>client.key (mode 0600)<br/>client.crt (mode 0644)<br/>ca.crt (mode 0644)
186
+
187
+ Note over RPi: RPi now has a Lamaste identity<br/>and can access Authelia-protected tunnels
188
+ ```
189
+
190
+ ### 9. Raspberry Pi Registers with the Sync Server
191
+
192
+ Now that the RPi has a Lamaste certificate, Authelia can identify it and grant access to the tunnel subdomain. The RPi connects to `sync.your-domain.com` through the Authelia-protected tunnel and registers with the Sync server using the API key.
193
+
194
+ ```mermaid
195
+ sequenceDiagram
196
+ participant RPi as Sync Agent (RPi)
197
+ participant Authelia as Authelia
198
+ participant Tunnel as Lamaste Tunnel<br/>sync.your-domain.com
199
+ participant Sync as Sync Server (Mac)
200
+
201
+ Note over RPi: Daemon starts<br/>Has Lamaste cert → can access tunnel<br/>No agentId persisted → must register
202
+
203
+ RPi->>Authelia: Access sync.your-domain.com<br/>(authenticated via Lamaste identity)
204
+ Authelia-->>RPi: Access granted
205
+
206
+ RPi->>Tunnel: POST /api/sync/agents<br/>Authorization: Bearer <apiKey><br/>{name: "rpi-sync",<br/>hostname: "raspberrypi",<br/>os: "Linux",<br/>nodeVersion, agentVersion}
207
+ Tunnel->>Sync: Forward request
208
+
209
+ Note over Sync: Verify API key (SHA-256 + timingSafeEqual)<br/>Generate Sync agent token: agent_<32 hex bytes><br/>Store SHA-256 hash in ~/.sync/agents.json
210
+
211
+ Sync-->>Tunnel: {ok,<br/>agent: {id, name, hostname, status},<br/>agentToken: "agent_..."}
212
+ Tunnel-->>RPi: Forward response
213
+
214
+ Note over RPi: Persist registration:<br/>Save agentId + agentToken<br/>to ~/.sync-agent/agent-settings.json<br/>(AES-256-GCM encrypted at rest)
215
+ ```
216
+
217
+ After registration, the Sync agent starts its normal operation — fetching config, heartbeating, and syncing files:
218
+
219
+ ```mermaid
220
+ sequenceDiagram
221
+ participant RPi as Sync Agent (RPi)
222
+ participant Tunnel as Lamaste Tunnel
223
+ participant Sync as Sync Server (Mac)
224
+
225
+ RPi->>Tunnel: GET /api/sync/agent-config<br/>Authorization: Bearer <apiKey>
226
+ Tunnel->>Sync: Forward
227
+ Note over Sync: Return projects, storage config,<br/>encryption passwords
228
+ Sync-->>Tunnel: {ok, projects, storage, ...}
229
+ Tunnel-->>RPi: Forward
230
+ Note over RPi: Cache config encrypted at<br/>~/.sync-agent/cached-config.json
231
+
232
+ loop Every 15 seconds
233
+ RPi->>Tunnel: POST /api/sync/agents/<agentId>/heartbeat<br/>X-Agent-Token: agent_...<br/>{activeSyncs, diskUsage}
234
+ Tunnel->>Sync: Forward
235
+ Note over Sync: Verify token (SHA-256 + timingSafeEqual)<br/>Update agent status: online
236
+ Sync-->>Tunnel: {ok}
237
+ Tunnel-->>RPi: Forward
238
+ end
239
+ ```
240
+
241
+ The Sync agent now has three credentials:
242
+
243
+ | Credential | Purpose | Stored at |
244
+ | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------- |
245
+ | Lamaste plugin-agent cert (`CN=plugin-agent:macbook-pro:rpi-sync`) | Authenticate with Authelia to access the tunnel; participate in the Lamaste ticket system | `~/.sync-agent/lamaste-cert/` (PEM files) |
246
+ | Sync API key (`Authorization: Bearer`) | Authenticate with the Sync server for config fetch and registration | `~/.sync-agent/agent-settings.json` (AES-256-GCM encrypted) |
247
+ | Sync agent token (`X-Agent-Token`) | Authenticate with the Sync server for heartbeats and sync reports | `~/.sync-agent/agent-settings.json` (AES-256-GCM encrypted) |
248
+
249
+ ### 10. Admin Grants Ticket Access
250
+
251
+ The delegated enrollment gives the Sync agent a Lamaste identity, but it still needs to be authorized in the ticket system. The admin assigns the `plugin:sync:connect` capability and creates a ticket assignment.
252
+
253
+ ```mermaid
254
+ sequenceDiagram
255
+ participant Admin as Admin (Panel UI)
256
+ participant Panel as Lamaste Panel
257
+
258
+ Admin->>Panel: PATCH /api/certs/agent/<br/>plugin-agent:macbook-pro:rpi-sync/capabilities<br/>{capabilities: ["plugin:sync:connect"]}
259
+ Note over Panel: Update registry entry<br/>for plugin-agent
260
+ Panel-->>Admin: {ok}
261
+
262
+ Admin->>Panel: POST /api/tickets/assignments<br/>{agentLabel: "plugin-agent:macbook-pro:rpi-sync",<br/>instanceScope: "plugin:sync:connect:<instanceId>"}
263
+ Note over Panel: Store assignment<br/>in ticket-scopes.json
264
+ Panel-->>Admin: {ok}
265
+
266
+ Note over Admin: Plugin-agent can now<br/>participate in plugin:sync:connect tickets
267
+ ```
268
+
269
+ The plugin-agent cert starts with only the delegated scope capability (`plugin:sync:connect`). The admin can grant additional capabilities via the Certificates page if needed — this is a deliberate opt-in with minimal privilege as the default.
270
+
271
+ ### 11. Ticket Authorization in Action
272
+
273
+ With both agents enrolled and the assignment in place, the ticket system authorizes file sync sessions. The Mac (source, instance owner) requests a ticket targeting the RPi's plugin-agent. The RPi polls its inbox, validates the ticket, and establishes a session with heartbeat-based re-validation.
274
+
275
+ ```mermaid
276
+ sequenceDiagram
277
+ participant Mac as Sync Server (Mac)<br/>SOURCE: owns instance
278
+ participant Panel as Lamaste Panel
279
+ participant RPi as Sync Agent (RPi)<br/>TARGET: assigned to instance
280
+
281
+ Mac->>Panel: POST /api/tickets<br/>(mTLS: CN=agent:macbook-pro)<br/>{scope: "plugin:sync:connect",<br/>instanceId: "<id>",<br/>target: "plugin-agent:<br/>macbook-pro:rpi-sync"}
282
+ Note over Panel: Rate limit (max 10/min per agent)<br/>Verify source exists, not revoked<br/>Verify source has plugin:sync:connect<br/>Verify target exists, not revoked<br/>Verify target has plugin:sync:connect<br/>Verify source owns instance (not stale)<br/>Verify source ≠ target<br/>Verify target assigned to instance<br/>Generate ticket (32 bytes, 30-sec TTL)
283
+ Panel-->>Mac: {ok, ticket: {id, scope,<br/>instanceId, source, target, expiresAt}}
284
+
285
+ RPi->>Panel: GET /api/tickets/inbox<br/>(mTLS: CN=plugin-agent:<br/>macbook-pro:rpi-sync)
286
+ Panel-->>RPi: {tickets: [{id, scope,<br/>source, expiresAt}]}
287
+
288
+ RPi->>Panel: POST /api/tickets/validate<br/>{ticketId: "<id>"}
289
+ Note over Panel: HMAC-SHA256 timing-safe compare<br/>Verify not expired (30-sec TTL)<br/>Verify not already used<br/>Verify caller is target<br/>Mark ticket as used
290
+ Panel-->>RPi: {valid: true, scope,<br/>instanceId, source, target, transport}
291
+
292
+ RPi->>Panel: POST /api/tickets/sessions<br/>{ticketId: "<id>"}
293
+ Note over Panel: Generate sessionId<br/>(16 bytes, server-side)<br/>Status: active
294
+ Panel-->>RPi: {ok, session: {sessionId,<br/>scope, source, target,<br/>status: "active"}}
295
+
296
+ loop Every 60 seconds
297
+ RPi->>Panel: POST /api/tickets/sessions/<sessionId>/heartbeat
298
+ Note over Panel: Re-validate:<br/>Source not revoked?<br/>Source has capability?<br/>Target not revoked?<br/>Target has capability?<br/>Assignment still valid?
299
+ Panel-->>RPi: {authorized: true}
300
+ end
301
+
302
+ Mac<-->RPi: Sync session authorized — file sync proceeds
303
+ ```
304
+
305
+ ### Revocation Cascade
306
+
307
+ If the admin revokes the Mac's Lamaste agent cert (`CN=agent:macbook-pro`), the panel automatically cascade-revokes all plugin-agents delegated by that agent. The Sync agent's cert is revoked in the same atomic operation, and the next session heartbeat terminates the active sync session.
308
+
309
+ ```mermaid
310
+ sequenceDiagram
311
+ participant Admin
312
+ participant Panel as Lamaste Panel
313
+ participant RPi as Sync Agent (RPi)
314
+
315
+ Admin->>Panel: DELETE /api/certs/agent/macbook-pro
316
+
317
+ Note over Panel: Revoke agent:macbook-pro<br/>Add serial to revoked.json
318
+
319
+ rect rgb(80, 40, 40)
320
+ Note over Panel: Cascade revocation
321
+ Note over Panel: Find all plugin-agents where<br/>delegatedBy = "macbook-pro"<br/>→ plugin-agent:macbook-pro:rpi-sync
322
+ Note over Panel: Revoke each plugin-agent<br/>Add their serials to revoked.json<br/>Clean up cert files
323
+ end
324
+
325
+ Note over Panel: Save registry atomically
326
+ Panel-->>Admin: {ok}
327
+
328
+ Note over RPi: Next heartbeat...
329
+ RPi->>Panel: POST /api/tickets/sessions/<sessionId>/heartbeat
330
+ Note over Panel: Target cert is revoked
331
+ Panel-->>RPi: {authorized: false,<br/>reason: "target_revoked"}
332
+ Note over RPi: Session terminated<br/>Active syncs cancelled
333
+ ```
334
+
335
+ ### Standalone vs Tunneled Behavior
336
+
337
+ The delegated enrollment flow depends on whether the Sync server is reachable through a Lamaste tunnel or directly by IP:
338
+
339
+ | Access method | Lamaste cert? | Ticket authorization |
340
+ | --------------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
341
+ | Direct IP (`192.168.1.x:port`) | No — no Lamaste involvement | Sync server mediates tickets on behalf of its agents using its own cert. Two-party model collapses to single party. Sufficient for trusted local networks. |
342
+ | Lamaste tunnel (`sync.your-domain.com`) | Yes — delegated enrollment | Each Sync agent has its own Lamaste identity (`CN=plugin-agent:...`). Full two-party ticket authorization with independent source and target identities. |
343
+
344
+ When the Sync server detects it has a Lamaste agent cert, the admin can prepare agent enrollments that include delegated Lamaste tokens. When running standalone without a Lamaste agent, only the Sync API key is needed and ticket authorization is self-mediated.
345
+
346
+ ```mermaid
347
+ flowchart TD
348
+ A[Admin prepares<br/>new agent enrollment<br/>on Sync Server] --> B{Sync Server has<br/>Lamaste agent cert?}
349
+
350
+ B -->|Yes| C[Tunneled Mode]
351
+ B -->|No| D[Standalone Mode]
352
+
353
+ C --> C1[1. Pre-announce to Lamaste panel<br/>POST /api/certs/agent/enroll-delegated]
354
+ C1 --> C2[2. Admin gives RPi: API key +<br/>tunnel URL + delegated token + panel URL]
355
+ C2 --> C3[3. RPi enrolls with Lamaste FIRST<br/>POST /api/enroll — public endpoint]
356
+ C3 --> C4[4. RPi has Lamaste cert<br/>→ can access Authelia-protected tunnel]
357
+ C4 --> C5[5. RPi registers with Sync server<br/>through the tunnel]
358
+ C5 --> C6[Full two-party<br/>ticket authorization]
359
+
360
+ D --> D1[1. Admin gives RPi:<br/>API key + server IP/URL]
361
+ D1 --> D2[2. RPi registers with Sync server<br/>directly via IP]
362
+ D2 --> D3[No Lamaste identity]
363
+ D3 --> D4[Sync server self-mediates<br/>tickets using its own cert]
364
+
365
+ style C fill:#1a3a2a,stroke:#4ade80
366
+ style D fill:#3a2a1a,stroke:#fbbf24
367
+ style C6 fill:#1a3a2a,stroke:#4ade80
368
+ style D4 fill:#3a2a1a,stroke:#fbbf24
369
+ ```
370
+
371
+ ## Related Documentation
372
+
373
+ - [Tickets (Agent-to-Agent)](../01-concepts/tickets.md) — ticket system concepts and data model
374
+ - [mTLS & Client Certificates](../01-concepts/mtls.md) — certificate scoping and capabilities
375
+ - [Certificate Management](certificate-management.md) — managing agent certificates