@lamalibre/create-lamaste 4.4.0 → 4.4.1
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.
- package/package.json +2 -2
- package/vendor/docs/00-introduction/how-it-works.md +2 -2
- package/vendor/docs/00-introduction/quickstart.md +5 -5
- package/vendor/docs/00-introduction/what-is-lamaste.md +5 -5
- package/vendor/docs/01-concepts/certificates.md +3 -3
- package/vendor/docs/01-concepts/mtls.md +2 -2
- package/vendor/docs/01-concepts/security-model.md +2 -2
- package/vendor/docs/01-concepts/tickets.md +2 -2
- package/vendor/docs/01-concepts/tunneling.md +4 -4
- package/vendor/docs/02-guides/agent-setup.md +5 -5
- package/vendor/docs/02-guides/certificate-management.md +2 -2
- package/vendor/docs/02-guides/cloud-provisioning.md +3 -3
- package/vendor/docs/02-guides/desktop-app-setup.md +10 -10
- package/vendor/docs/02-guides/installation.md +3 -3
- package/vendor/docs/02-guides/onboarding.md +2 -2
- package/vendor/docs/02-guides/standalone-plugin-with-tickets.md +2 -2
- package/vendor/docs/03-architecture/installer-flow.md +2 -2
- package/vendor/docs/03-architecture/installer.md +4 -4
- package/vendor/docs/03-architecture/management-flow.md +1 -1
- package/vendor/docs/03-architecture/overview.md +5 -5
- package/vendor/docs/03-architecture/panel-client.md +6 -6
- package/vendor/docs/03-architecture/panel-server.md +2 -2
- package/vendor/docs/03-architecture/state-management.md +2 -2
- package/vendor/docs/03-architecture/system-overview.md +1 -1
- package/vendor/docs/04-api-reference/agent-hosted-plugins.md +11 -11
- package/vendor/docs/04-api-reference/app-host.md +3 -3
- package/vendor/docs/04-api-reference/lifecycle-hooks.md +5 -5
- package/vendor/docs/04-api-reference/onboarding.md +1 -1
- package/vendor/docs/04-api-reference/registry-and-oidc.md +3 -1
- package/vendor/docs/04-api-reference/tunnels.md +1 -1
- package/vendor/docs/05-operations/backup-and-restore.md +1 -1
- package/vendor/docs/05-operations/uninstalling.md +1 -1
- package/vendor/docs/05-operations/upgrades.md +5 -5
- package/vendor/docs/06-reference/config-files.md +2 -2
- package/vendor/docs/06-reference/glossary.md +3 -3
- package/vendor/docs/06-reference/installer-flags.md +13 -13
- package/vendor/docs/06-reference/ports-and-services.md +1 -1
- package/vendor/docs/06-reference/troubleshooting.md +8 -8
- package/vendor/docs/gatekeeper-design.md +8 -8
- package/vendor/docs/identity-directory-design.md +2 -2
- package/vendor/docs/package.json +1 -1
- package/vendor/docs/todo/lib-cli-desktop-refactor.md +27 -27
- package/vendor/gatekeeper/package.json +2 -2
- package/vendor/server/package.json +2 -2
- package/vendor/server-ui/dist/assets/{index-CESyyK8V.js → index-roDHz3dM.js} +1 -1
- package/vendor/server-ui/dist/index.html +1 -1
- package/vendor/serverd/package.json +2 -2
- package/vendor/serverd/src/lib/plugin-packages.js +30 -22
- package/vendor/serverd/src/lib/plugins.js +35 -11
- package/vendor/serverd/src/lib/registry-credential.js +37 -0
|
@@ -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.4.
|
|
13
|
+
The management-mode admin pages (Dashboard, Tunnels, Sites, Users, Certificates, Services, Plugins, Tickets, Logs, Settings) live in `@lamalibre/lamaste-server-ui@4.4.1`. 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.4.
|
|
15
|
+
The agent-mode pages (Dashboard, Tunnels, Services, Plugins, Logs, Settings) are extracted into a separate shared package: `@lamalibre/lamaste-agent-ui@4.4.1`. 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.4.
|
|
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.4.1` 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.4.
|
|
354
|
-
3. **ToastProvider** — from `@lamalibre/lamaste-server-ui@4.4.
|
|
355
|
-
4. **TwoFaProvider** — from `@lamalibre/lamaste-server-ui@4.4.
|
|
353
|
+
2. **AdminClientProvider** — from `@lamalibre/lamaste-server-ui@4.4.1`, supplies the host-specific data client (`webAdminClient` for browser fetch, Tauri `invoke` for desktop)
|
|
354
|
+
3. **ToastProvider** — from `@lamalibre/lamaste-server-ui@4.4.1`, notification toast system
|
|
355
|
+
4. **TwoFaProvider** — from `@lamalibre/lamaste-server-ui@4.4.1`, 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.4.
|
|
456
|
+
- Thin shim over `@lamalibre/lamaste@4.4.1/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
|
|
@@ -535,7 +535,7 @@ Agent-to-agent authorization with scopes, instances, tickets, and sessions. Prov
|
|
|
535
535
|
- **Periodic cleanup** — stale/dead instances, expired tickets, dead sessions
|
|
536
536
|
- **Concurrency** — promise-chain mutex with atomic file writes (temp → fsync → rename)
|
|
537
537
|
|
|
538
|
-
Client SDK: `@lamalibre/lamaste-tickets@4.4.
|
|
538
|
+
Client SDK: `@lamalibre/lamaste-tickets@4.4.1` (TypeScript, undici) provides `TicketClient`, `TicketInstanceManager` (source side), and `TicketSessionManager` (target side) for plugin integration.
|
|
539
539
|
|
|
540
540
|
State files: `/etc/lamalibre/lamaste/ticket-scopes.json` (scopes, instances, assignments) and `/etc/lamalibre/lamaste/tickets.json` (tickets, sessions).
|
|
541
541
|
|
|
@@ -331,7 +331,7 @@ Read returns `[]` for missing files (ENOENT), enabling the system to start with
|
|
|
331
331
|
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
|
|
332
332
|
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
|
|
333
333
|
|
|
334
|
-
Every one of these workflows runs under the single tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.4.
|
|
334
|
+
Every one of these workflows runs under the single tunnel lock (`withTunnelLock` in `@lamalibre/lamaste@4.4.1/server`), with its validation inside the lock.
|
|
335
335
|
|
|
336
336
|
## Site State (`sites.json`)
|
|
337
337
|
|
|
@@ -486,7 +486,7 @@ The PKI directory belongs to `lamaste` because the panel is the CA (it signs age
|
|
|
486
486
|
|
|
487
487
|
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:
|
|
488
488
|
|
|
489
|
-
**Tunnel state** is guarded by one promise-chain lock, `withTunnelLock` in `@lamalibre/lamaste@4.4.
|
|
489
|
+
**Tunnel state** is guarded by one promise-chain lock, `withTunnelLock` in `@lamalibre/lamaste@4.4.1/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.
|
|
490
490
|
|
|
491
491
|
**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.
|
|
492
492
|
|
|
@@ -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.4.
|
|
61
|
+
│ │ │ (pages from @lamalibre/lamaste-agent-ui@4.4.1) │ │
|
|
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) │ │
|
|
@@ -45,7 +45,7 @@ There is no port in the manifest: Lamaste assigns it when the plugin is hosted.
|
|
|
45
45
|
|
|
46
46
|
**Routes are unique.** A plugin's route — its name without a trailing `-server` or `-agent` — belongs to one installed plugin: installing another plugin with the same route is refused (`409`, `code: "route_taken"`, with `route` and `owner`). Plugins installed before the rule that share a route keep it for the one installed first; the others hold no route and can be uninstalled but not hosted (`409`, `code: "route_conflict"`) until they are reinstalled under a route of their own.
|
|
47
47
|
|
|
48
|
-
**Pinned at install.** Installing (or staging an update of) a plugin with `agent.package` reads the agent package's integrity from the registry the panel installs from (`npm view <package>@<version> dist.integrity
|
|
48
|
+
**Pinned at install.** Installing (or staging an update of) a plugin with `agent.package` reads the agent package's integrity from the registry the panel installs from (`npm view <package>@<version> dist.integrity`, authenticated with the panel's [registry credential](registry-and-oidc.md) when one is configured) and pins `{ package, version, integrity }` with the plugin: the approval covers it and the agent installs only a tarball that matches. A version that is not semver without build metadata, an agent package not published at the plugin's version (`400`) or a registry without a SHA-512 integrity for it is refused.
|
|
49
49
|
|
|
50
50
|
## Hosting endpoints
|
|
51
51
|
|
|
@@ -133,7 +133,7 @@ Every app job is signed by the relay with its signing key when it is created, an
|
|
|
133
133
|
|
|
134
134
|
### Host facts
|
|
135
135
|
|
|
136
|
-
Each agent reports its host on its sync when the facts change, when the relay does not hold them, and once a day: `PUT /api/agents/me/host-facts` (agent certificates only; 10 per minute, 4 KiB). What the relay holds it tells the agent in every [`GET /api/tunnels/agent-config`](tunnels.md#get-api-tunnels-agent-config) answer: `hostFactsHash`, the SHA-256 of the facts stored on the agent's record (`hostFactsHash` in `@lamalibre/lamaste@4.4.
|
|
136
|
+
Each agent reports its host on its sync when the facts change, when the relay does not hold them, and once a day: `PUT /api/agents/me/host-facts` (agent certificates only; 10 per minute, 4 KiB). What the relay holds it tells the agent in every [`GET /api/tunnels/agent-config`](tunnels.md#get-api-tunnels-agent-config) answer: `hostFactsHash`, the SHA-256 of the facts stored on the agent's record (`hostFactsHash` in `@lamalibre/lamaste@4.4.1`, the same function the agent hashes its own with), `null` when it stores none. When that is not the hash of the agent's facts, the sync reports them whatever it last reported — so an agent enrolled again on its label (a new record, without host facts) or a relay restored from a backup can host again after one sync, not a day later. Behind a relay that does not send the field, the agent reports as before.
|
|
137
137
|
|
|
138
138
|
```json
|
|
139
139
|
{
|
|
@@ -337,7 +337,7 @@ What the agent's host offers — see [Host facts](#host-facts).
|
|
|
337
337
|
|
|
338
338
|
`lamaste-agent sync` (the 30-second timer) fetches a plugin's config when it does not hold the secret issued at `secretIssuedAt`, writes `~/.lamalibre/lamaste/agents/<label>/app-plugins/<route>.json` (0600, atomic), acknowledges it, and removes the file of a plugin the relay no longer lists. For an agent half Lamaste installed it first writes the service's own copy, `/etc/lamalibre/apps/<route>/plugin-config.json`, and acknowledges only once both are written. `sync.log` records `plugin config fetched: <route>` and `plugin config delivered to its service: <route>`.
|
|
339
339
|
|
|
340
|
-
The agent half reads its plugin config — `LAMASTE_APP_CONFIG` for an agent half Lamaste installs, the file above for one installed by hand (`LAMALIBRE_LAMASTE_APP_PLUGIN_CONFIG` by convention) — and verifies every request on `/admin/*` with `createCallerAssertionVerifier` from `@lamalibre/lamaste@4.4.
|
|
340
|
+
The agent half reads its plugin config — `LAMASTE_APP_CONFIG` for an agent half Lamaste installs, the file above for one installed by hand (`LAMALIBRE_LAMASTE_APP_PLUGIN_CONFIG` by convention) — and verifies every request on `/admin/*` with `createCallerAssertionVerifier` from `@lamalibre/lamaste@4.4.1/agent`, which re-reads the file when a signature does not verify, so a rotation needs no restart. It listens where `agentHalfListenTarget` says: for an agent half Lamaste installs, systemd holds the port in a socket unit and passes the listening socket (descriptor 3), so the port stays bound while the agent half restarts or is updated and no other local process can take it in between — such an agent half must listen this way, and one that binds its port itself fails with `EADDRINUSE`; for one installed by hand, the config's port on `127.0.0.1` ([App Host](app-host.md#writing-an-agent-half)). Every answer it gives a verified request carries the responder signature ([below](#authentication-between-the-relay-and-the-agent-half)):
|
|
341
341
|
|
|
342
342
|
```js
|
|
343
343
|
import {
|
|
@@ -345,7 +345,7 @@ import {
|
|
|
345
345
|
createCallerAssertionVerifier,
|
|
346
346
|
createHealthHandler,
|
|
347
347
|
responderHeaderFor,
|
|
348
|
-
} from '@lamalibre/lamaste@4.4.
|
|
348
|
+
} from '@lamalibre/lamaste@4.4.1/agent';
|
|
349
349
|
|
|
350
350
|
const verifier = createCallerAssertionVerifier({ configPath, plugin: 'market-indicators' });
|
|
351
351
|
const health = createHealthHandler({ verifier, check: () => db.isOpen() });
|
|
@@ -411,7 +411,7 @@ Answers given before or instead of a verification — a `401` for a bad assertio
|
|
|
411
411
|
|
|
412
412
|
**Secret settings travel sealed** to the one request that carries them ([Between the relay and the agent half](#between-the-relay-and-the-agent-half)).
|
|
413
413
|
|
|
414
|
-
**Agent halves built against `@lamalibre/lamaste@4.4.
|
|
414
|
+
**Agent halves built against `@lamalibre/lamaste@4.4.1` 4.3 must be rebuilt** against this version: an agent half that does not sign its answers is down (`app`) to the health monitor, its pages and API answer `502`, and its settings and hooks fail as `unauthenticated`; one that expects secrets in clear refuses sealed ones.
|
|
415
415
|
|
|
416
416
|
## Health
|
|
417
417
|
|
|
@@ -555,13 +555,13 @@ The panel checks the change against the installed schema — without `pattern`
|
|
|
555
555
|
| 422 | — | The agent half refused it: `errors` per field — its `pattern`, its own schema, or a sealed secret that did not open ("Could not be decrypted") |
|
|
556
556
|
| 502 | `settings_unsupported` | The agent half does not answer `/admin/settings` (`404`) — it predates settings; update the plugin |
|
|
557
557
|
| 502 | `refused` | Nothing answers on the internal tunnel's port |
|
|
558
|
-
| 502 | `unauthenticated` | The answer did not carry a valid responder signature: something else may hold the tunnel's port, or the agent half was built against an older `@lamalibre/lamaste@4.4.
|
|
558
|
+
| 502 | `unauthenticated` | The answer did not carry a valid responder signature: something else may hold the tunnel's port, or the agent half was built against an older `@lamalibre/lamaste@4.4.1` and must be rebuilt. The change may or may not have been stored — read the settings again |
|
|
559
559
|
| 502 | `invalid`, `other` | The agent half answered too large a body or in another shape (reload the settings to see what it stored), or could not be reached |
|
|
560
560
|
| 502 | `agent_half_error` | The agent half answered another status (`status`) |
|
|
561
561
|
| 503 | `hosting_offline` | The plugin is not hosted, its hosting is not `active`, or its health is `down` — with `hosting` as in `GET` (`null` when not hosted) |
|
|
562
562
|
| 504 | `timeout` | No answer within 15 s — the change may or may not have been stored; read the settings again |
|
|
563
563
|
|
|
564
|
-
Once the plugin and its settings block are found, the checks run in this order: the body (`400`), a running operation (`409`), the hosting (`503`), then the agent half. A refusal answers `{ error, code?, errors?, revision?, operationId?, hosting?, status? }`, built from what Lamaste found and never from the request body. The agent half's per-field messages are passed on only under the keys of the installed schema (or `_`), and only when they are messages Lamaste's own validation produces — the fixed ones (`Is required`, `Does not match the required format`, …) and the templated ones (`Must be at most 64 characters`, `Must be at least 0`, `2 unknown settings`), matched exactly; anything else reads `Invalid value` (`normalizeSettingsErrorMessage` in `@lamalibre/lamaste@4.4.
|
|
564
|
+
Once the plugin and its settings block are found, the checks run in this order: the body (`400`), a running operation (`409`), the hosting (`503`), then the agent half. A refusal answers `{ error, code?, errors?, revision?, operationId?, hosting?, status? }`, built from what Lamaste found and never from the request body. The agent half's per-field messages are passed on only under the keys of the installed schema (or `_`), and only when they are messages Lamaste's own validation produces — the fixed ones (`Is required`, `Does not match the required format`, …) and the templated ones (`Must be at most 64 characters`, `Must be at least 0`, `2 unknown settings`), matched exactly; anything else reads `Invalid value` (`normalizeSettingsErrorMessage` in `@lamalibre/lamaste@4.4.1`). The agent half is Lamaste code that never repeats a value, but the relay does not answer an administrator with text it cannot vouch for.
|
|
565
565
|
|
|
566
566
|
A stored change is recorded in the [audit log](audit-and-notifications.md) as `plugin.settings.updated` with key names only — `changed`, `cleared`, `secretsSet`, `secretsCleared` — the new `revision` and the certificate serial. A change whose answer was lost (`504 timeout`, `502 unauthenticated` or `invalid`) is remembered by its key names; when a later read finds the agent half at exactly its revision, changed in those keys only, it is recorded then, with `confirmedLater: true`.
|
|
567
567
|
|
|
@@ -587,7 +587,7 @@ An update's preview — `pendingUpdate.settings`, in `GET /api/plugins/:name/hoo
|
|
|
587
587
|
|
|
588
588
|
### Between the relay and the agent half
|
|
589
589
|
|
|
590
|
-
The relay calls the agent half with `callAgentHalf` (`@lamalibre/lamaste@4.4.
|
|
590
|
+
The relay calls the agent half with `callAgentHalf` (`@lamalibre/lamaste@4.4.1`, also exported from `@lamalibre/lamaste@4.4.1/server`): one JSON request to `http://127.0.0.1:<port>/admin/…` — the relay end of the internal tunnel — with a caller assertion naming Lamaste itself (`{ "type": "lamaste" }`) that binds the body's SHA-256, answered with whatever status the agent half gives once the answer carries a valid responder signature over that status and its exact body ([Authentication](#authentication-between-the-relay-and-the-agent-half)). It calls only paths under `/admin/` without `.` or `..` segments, with `GET`, `PUT`, `POST` or `DELETE`, and throws before sending anything otherwise; it sends a body as `application/json` — `body` may be a function of the assertion's nonce, chosen before anything is signed, which is how a change seals its secrets to the one call — bounds the time and the response size, and resolves `{ ok: true, status, body }` (the parsed JSON, or `null`) or `{ ok: false, code, error }` — `refused` (nothing listens), `timeout`, `invalid` (a response over the size limit), `unauthenticated` (no valid responder signature: not the agent half, or one built against an older core) or `other` — with an error that never contains the request body. The hook call `callAgentHalfHook` is built on the same exchange.
|
|
591
591
|
|
|
592
592
|
| Call | Body | Answer |
|
|
593
593
|
| ----------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -610,7 +610,7 @@ The state, which every answer of the relay's reads and changes is built from:
|
|
|
610
610
|
|
|
611
611
|
`values` are the explicitly stored non-secret values — no defaults; `secrets` names the secrets that are set, and when — never a value; `setAt` must be an ISO 8601 UTC timestamp as `Date.prototype.toISOString` writes it (`2026-10-16T10:00:00.000Z`), so not even that field can carry free text. In a change, `schemaVersion` is the relay's installed manifest's, `baseRevision` the revision the administrator edited, `values` the non-secret change the panel validated and `secrets` each secret **sealed** (`null` clears). Bodies and responses are 2 MiB at most — the largest valid change, 50 fields of 4096 characters that JSON escapes to six bytes apiece, is about 1.23 MB. The relay parses every state with `parseAgentHalfSettingsState`, which builds a fresh object of the contract's fields: nothing else the agent half sent — a secret's value in particular — survives into the cache.
|
|
612
612
|
|
|
613
|
-
**Sealed secrets.** Each secret of a change crosses the tunnel as `{ "v": 1, "iv": "<base64url, 12 bytes>", "ct": "<base64url ciphertext and 16-byte tag>" }` — AES-256-GCM under a key derived from the installation secret (HKDF-SHA256 over its 32 bytes, salt `lamaste/settings-secrets/v1`, info the plugin's route), a random IV per value, and as additional data the JSON array `[plugin, nonce, key, baseRevision]`: the plugin, the call's assertion nonce, the field and the revision the change was made on. `encryptSettingsSecret` and `decryptSettingsSecret` in `@lamalibre/lamaste@4.4.
|
|
613
|
+
**Sealed secrets.** Each secret of a change crosses the tunnel as `{ "v": 1, "iv": "<base64url, 12 bytes>", "ct": "<base64url ciphertext and 16-byte tag>" }` — AES-256-GCM under a key derived from the installation secret (HKDF-SHA256 over its 32 bytes, salt `lamaste/settings-secrets/v1`, info the plugin's route), a random IV per value, and as additional data the JSON array `[plugin, nonce, key, baseRevision]`: the plugin, the call's assertion nonce, the field and the revision the change was made on. `encryptSettingsSecret` and `decryptSettingsSecret` in `@lamalibre/lamaste@4.4.1` are the two halves; the relay seals with the secret it signs the call with, and the agent half opens with the secret that verified it (`decryptSettingsSecret` on the verify result — the secret never leaves the verifier). A sealed secret opens for no other plugin, call, field or revision; one that does not open is a `422` for its key, and an error never repeats it. Whatever takes the tunnel's port receives no secret in clear — the non-secret values of a change, which the relay stores in clear anyway, are not sealed.
|
|
614
614
|
|
|
615
615
|
Only Lamaste: a caller assertion that does not verify is `401`, one naming an administrator or an agent `403`; another path under `/admin/settings` is `404`, a method a path does not take `405`. The **forwarder reserves `/admin/settings`** and everything under it as it reserves `/admin/hooks/*`: `/api/<route>/settings`, `/api/<route>/Settings` and `/api/<route>/%73ettings/x` all answer `404` and reach nothing. No forwarded caller — an administrator, or an agent holding one of the plugin's capabilities — reads or changes the settings behind the panel's validation, and the relay's cache cannot drift from the agent half. An agent half's own admin routes cannot live there.
|
|
616
616
|
|
|
@@ -618,7 +618,7 @@ Only Lamaste: a caller assertion that does not verify is `401`, one naming an ad
|
|
|
618
618
|
|
|
619
619
|
### In the agent half
|
|
620
620
|
|
|
621
|
-
`@lamalibre/lamaste@4.4.
|
|
621
|
+
`@lamalibre/lamaste@4.4.1/agent` holds the store and the handler, so that no plugin reimplements how a secret is stored:
|
|
622
622
|
|
|
623
623
|
- **`settingsFromManifest(manifest)`** — the `settings` block of the agent half's own copy of its manifest (bundled with it), validated — here each string default is also tested against its field's `pattern`, which the relay never runs; throws `ManifestValidationError` when it is missing or invalid. The agent half checks values against this schema, not the relay's.
|
|
624
624
|
- **`createSettingsStore({ dataDir, settings })`** — the store over two files in `dataDir`, which must exist: for an agent half Lamaste installs, `LAMASTE_APP_DATA_DIR`, the only directory its unit may write ([App Host](app-host.md)). One store per process. `load()` reads and reconciles the files and must run before anything else; then `state()` is what `GET` answers, `values()` the values the plugin runs with (defaults overlaid by the stored values, never a secret), `secret(key)` a secret's value for the agent half's own use (`undefined` when it is not set), `configured()`, `update(change)`, `reload()`, and `onChange(listener)` — called with the new state after every update and reload, returning its unsubscribe function; a listener that throws does not fail the update, which is stored.
|
|
@@ -641,7 +641,7 @@ import {
|
|
|
641
641
|
createSettingsHandler,
|
|
642
642
|
createSettingsStore,
|
|
643
643
|
settingsFromManifest,
|
|
644
|
-
} from '@lamalibre/lamaste@4.4.
|
|
644
|
+
} from '@lamalibre/lamaste@4.4.1/agent';
|
|
645
645
|
import manifest from '../lamaste-plugin.json' with { type: 'json' }; // bundled into the agent half
|
|
646
646
|
|
|
647
647
|
const verifier = createCallerAssertionVerifier({
|
|
@@ -15,12 +15,12 @@ The helper does only what the relay **signed**. When it is enabled it remembers
|
|
|
15
15
|
Once per agent machine (Linux with systemd; macOS agents cannot host an agent half Lamaste installs):
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
sudo npm install -g @lamalibre/lamaste-agent@4.4.
|
|
18
|
+
sudo npm install -g @lamalibre/lamaste-agent@4.4.1 # a root-owned install
|
|
19
19
|
sudo lamaste-agent apphost enable # the agent runs as the user who ran sudo
|
|
20
20
|
sudo lamaste-agent apphost enable --user root # the agent runs as root: no sudoers rule
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
`enable` refuses to act from an install another user could have changed: the package, the helper it ships and the core library it loads — and every directory above them — must be root-owned and writable by no group or other user. A user-level install (`nvm`, a home-directory prefix) is not; install it root-owned with `sudo npm install -g @lamalibre/lamaste-agent@4.4.
|
|
23
|
+
`enable` refuses to act from an install another user could have changed: the package, the helper it ships and the core library it loads — and every directory above them — must be root-owned and writable by no group or other user. A user-level install (`nvm`, a home-directory prefix) is not; install it root-owned with `sudo npm install -g @lamalibre/lamaste-agent@4.4.1` and run `enable` from that install. It also needs `/usr/bin/node` (the helper's interpreter and the Node.js every agent half runs on — from the distribution or NodeSource), systemd as the system manager (`/run/systemd/system`), and `sudo` and `visudo` unless the agent runs as root.
|
|
24
24
|
|
|
25
25
|
What `enable` does:
|
|
26
26
|
|
|
@@ -357,7 +357,7 @@ The contract:
|
|
|
357
357
|
|
|
358
358
|
```js
|
|
359
359
|
import Fastify from 'fastify';
|
|
360
|
-
import { agentHalfListenTarget, createCallerAssertionVerifier } from '@lamalibre/lamaste@4.4.
|
|
360
|
+
import { agentHalfListenTarget, createCallerAssertionVerifier } from '@lamalibre/lamaste@4.4.1/agent'; // bundled
|
|
361
361
|
|
|
362
362
|
const verifier = createCallerAssertionVerifier({
|
|
363
363
|
configPath: process.env.LAMASTE_APP_CONFIG,
|
|
@@ -163,7 +163,7 @@ Approving a pending update approves everything it waits for (`approvalRequired`)
|
|
|
163
163
|
|
|
164
164
|
### `POST /api/plugins/:name/update`
|
|
165
165
|
|
|
166
|
-
`{ "version"?: "1.2.3" | "latest" }` (default `latest`). For agent-hosted plugins. The panel fetches the version with `npm pack` and reads its manifest and relay module **in memory** — nothing of it is installed yet. Then:
|
|
166
|
+
`{ "version"?: "1.2.3" | "latest" }` (default `latest`). For agent-hosted plugins. The panel fetches the version with `npm pack` (from the registry its [registry credential](registry-and-oidc.md) names, authenticated as the panel, when one is configured) and reads its manifest and relay module **in memory** — nothing of it is installed yet. Then:
|
|
167
167
|
|
|
168
168
|
- `200 { upToDate: true }` — nothing to do;
|
|
169
169
|
- `202 { pendingUpdate }` — what it brings (relay hook set, agent half) differs from what was approved and auto-approve is off or withheld, or it turns a secret setting into a plain one (always); `approvalRequired` says which, and the administrators are notified. Approve it with the digest shown, or by its version;
|
|
@@ -250,13 +250,13 @@ Relay hooks run in `lamalibre-lamaste-hooks@<route>.service`, started by its soc
|
|
|
250
250
|
|
|
251
251
|
`lamaste-priv hooks install <route> <sha256|none>` (module on stdin, checked against the approved digest) writes the module root-owned to `/opt/lamalibre/lamaste/hooks/<route>/<sha256>.mjs` and enables the socket; `hooks activate` points back at the previous module (an update's revert); `hooks restart` stops a stuck runner; `hooks remove` disables it and deletes its modules. The panel reinstalls an approved runner at start-up if it is missing.
|
|
252
252
|
|
|
253
|
-
The protocol between the panel and the runner is one length-prefixed JSON frame each way per connection (`@lamalibre/lamaste@4.4.
|
|
253
|
+
The protocol between the panel and the runner is one length-prefixed JSON frame each way per connection (`@lamalibre/lamaste@4.4.1/hooks`, `protocol.ts`).
|
|
254
254
|
|
|
255
255
|
## Writing hooks
|
|
256
256
|
|
|
257
257
|
```js
|
|
258
258
|
// hooks/relay.mjs — bundled into one file (esbuild), Node built-ins only
|
|
259
|
-
import { defineRelayHooks } from '@lamalibre/lamaste@4.4.
|
|
259
|
+
import { defineRelayHooks } from '@lamalibre/lamaste@4.4.1/hooks';
|
|
260
260
|
|
|
261
261
|
export default defineRelayHooks({
|
|
262
262
|
'install:pre-tunnel': async (input) =>
|
|
@@ -285,10 +285,10 @@ Every decision may carry `effects` (at most 10): `{ type: 'audit', event, data?
|
|
|
285
285
|
|
|
286
286
|
### Agent hooks
|
|
287
287
|
|
|
288
|
-
The agent half answers `POST /admin/hooks/<stage>` (body `{ stage, input }`, answer `{ ok: true, output? }` or `{ ok: false, reason }`). `createAgentHookHandler` from `@lamalibre/lamaste@4.4.
|
|
288
|
+
The agent half answers `POST /admin/hooks/<stage>` (body `{ stage, input }`, answer `{ ok: true, output? }` or `{ ok: false, reason }`). `createAgentHookHandler` from `@lamalibre/lamaste@4.4.1/agent` does the checking — the caller assertion must name Lamaste itself (`{ type: 'lamaste' }`), which no forwarded caller can, and bind the SHA-256 of the exact body (`wrong_body` otherwise), so it needs the body **as received** (a `Buffer` or a string; a parsed object is refused `400` "The body must be passed as received"); the forwarder never reaches `/admin/hooks*`. Send the `status`, `headers` and `bodyText` it returns, `bodyText` byte for byte: its `X-Lamalibre-Responder` header signs those bytes, and the relay counts a hook answer without a valid one as a failure (`unauthenticated`) — see [Authentication between the relay and the agent half](agent-hosted-plugins.md#authentication-between-the-relay-and-the-agent-half):
|
|
289
289
|
|
|
290
290
|
```js
|
|
291
|
-
import { createAgentHookHandler, createCallerAssertionVerifier } from '@lamalibre/lamaste@4.4.
|
|
291
|
+
import { createAgentHookHandler, createCallerAssertionVerifier } from '@lamalibre/lamaste@4.4.1/agent';
|
|
292
292
|
|
|
293
293
|
const hooks = createAgentHookHandler({
|
|
294
294
|
verifier: createCallerAssertionVerifier({ configPath, plugin: 'fx' }),
|
|
@@ -255,7 +255,7 @@ The wildcard check probes `test-lamaste-check.<domain>`. Wildcard DNS is optiona
|
|
|
255
255
|
|
|
256
256
|
### `POST /api/onboarding/provision`
|
|
257
257
|
|
|
258
|
-
Starts the full stack provisioning process in the background. This configures and starts Chisel and Authelia, issues certbot certificates, and writes nginx vhosts. The Chisel and Authelia binaries and units are installed by `create-lamaste`, not here: if one is missing, the task fails with the error "Chisel is not installed. Run `npx @lamalibre/create-lamaste@4.4.
|
|
258
|
+
Starts the full stack provisioning process in the background. This configures and starts Chisel and Authelia, issues certbot certificates, and writes nginx vhosts. The Chisel and Authelia binaries and units are installed by `create-lamaste`, not here: if one is missing, the task fails with the error "Chisel is not installed. Run `npx @lamalibre/create-lamaste@4.4.1` on the server to repair the installation." (likewise for Authelia). Task IDs are unchanged from earlier versions; only the titles changed.
|
|
259
259
|
|
|
260
260
|
Provisioning runs asynchronously. This endpoint returns immediately with a 202 status. Use the WebSocket stream endpoint to follow progress in real time.
|
|
261
261
|
|
|
@@ -6,6 +6,8 @@ Two generic mechanisms a plugin can build on, first used by Feria (the Lamalibre
|
|
|
6
6
|
|
|
7
7
|
Agents install plugins with `npm install` from the Lamalibre registry. A networked registry accepts nothing anonymously, and an agent must never hold a long-lived registry credential. So the panel holds one credential for the registry and, each time an agent is about to install a plugin, asks the registry for a one-hour read-only token owned by that agent. The agent writes it to a throw-away npm configuration file for that one install and deletes it afterwards.
|
|
8
8
|
|
|
9
|
+
The panel's own npm calls use the same credential: installing a plugin, fetching a version to stage an update, and reading an agent package's integrity to pin it all run with npm routed to the credential's registry for its scope and authenticated with the panel's service token — through a throw-away userconfig of the same kind (0600 in a private temporary directory, named to npm as `npm_config_userconfig`, removed once npm has run), never the service account's `~/.npmrc`. A registry that answers `401` has the token obtained again once; a token still refused, or `403`, fails the operation naming the registry and the status. Without a credential the panel's npm runs as the machine configures it (the public registry by default).
|
|
10
|
+
|
|
9
11
|
The panel itself can authenticate to the registry in two ways: with a long-lived token an administrator pasted in, or — the recommended way — as its own **service principal**: an OpenID Connect client on the relay's Authelia named `lamaste-server`. In that mode nothing long-lived is pasted anywhere; the panel proves its identity to Authelia with a secret it generated itself, Authelia issues a short-lived JWT, and the registry exchanges that JWT for a registry token.
|
|
10
12
|
|
|
11
13
|
Plugins use the same Authelia to let people and programs log in. A plugin's manifest declares its OIDC clients — a CLI that logs in with the device-code flow, a service that authenticates with client credentials — and the panel writes them into Authelia's configuration when the plugin is enabled.
|
|
@@ -156,7 +158,7 @@ Admin only. What is configured — never a token.
|
|
|
156
158
|
|
|
157
159
|
### `PUT /api/registry-credential`
|
|
158
160
|
|
|
159
|
-
Admin only, 10 per minute. Verifies the whole chain against the registry, then stores the credential at `/etc/lamalibre/lamaste/registry-credential.json` (0600).
|
|
161
|
+
Admin only, 10 per minute. Verifies the whole chain against the registry, then stores the credential at `/etc/lamalibre/lamaste/registry-credential.json` (0600). From then on the panel's own installs, update staging and agent-half pinning authenticate to `registryUrl` for `scope` with the service token (see In Plain English); a package published only there installs from the panel.
|
|
160
162
|
|
|
161
163
|
```json
|
|
162
164
|
{ "registryUrl": "https://feria.example.com", "auth": { "kind": "oidc" } }
|
|
@@ -316,7 +316,7 @@ curl -s --cert client.p12:password \
|
|
|
316
316
|
| `chiselArgs` | `string[]` | Chisel client arguments: `client`, the server URL, and one `R:127.0.0.1:<port>:127.0.0.1:<port>` remote per tunnel. No `--tls-skip-verify` (the agent verifies the relay's certificate) and no credential — the agent supplies its own through the service environment, never argv |
|
|
317
317
|
| `chiselCredentialIssuedAt` | `string \| null` | When the agent's current Chisel credential was issued — a timestamp, never the credential. An agent holding a credential with a different issue time (an administrator rotated it) fetches the current one from `GET /api/agents/me/chisel-credential` |
|
|
318
318
|
| `tunnels` | `array` | The agent's enabled tunnels with port and subdomain |
|
|
319
|
-
| `hostFactsHash` | `string \| null` | Agent certificates only: the SHA-256 of the [host facts](agent-hosted-plugins.md#host-facts) the relay stores for the agent, computed as the agent computes it over its own (`hostFactsHash` in `@lamalibre/lamaste@4.4.
|
|
319
|
+
| `hostFactsHash` | `string \| null` | Agent certificates only: the SHA-256 of the [host facts](agent-hosted-plugins.md#host-facts) the relay stores for the agent, computed as the agent computes it over its own (`hostFactsHash` in `@lamalibre/lamaste@4.4.1`); `null` when it stores none. The agent's sync reports its facts again when this is not the hash of its own, so a record replaced since its last report — the agent enrolled again on its label, the relay restored from a backup — has them again after one sync, not a day. An agent behind a relay without the field reports as before |
|
|
320
320
|
|
|
321
321
|
**Errors:**
|
|
322
322
|
|
|
@@ -197,7 +197,7 @@ sudo chown lamaste-authelia:lamaste-authelia /etc/authelia/db.sqlite3* /etc/auth
|
|
|
197
197
|
sudo chmod 600 /etc/authelia/db.sqlite3* /etc/authelia/notifications.txt
|
|
198
198
|
```
|
|
199
199
|
|
|
200
|
-
A backup taken on a version before the privilege boundary holds root-owned Authelia files and older unit files. After restoring one, re-run `npx @lamalibre/create-lamaste@4.4.
|
|
200
|
+
A backup taken on a version before the privilege boundary holds root-owned Authelia files and older unit files. After restoring one, re-run `npx @lamalibre/create-lamaste@4.4.1 --yes`: the redeploy rewrites the `chisel` and `authelia` units and converts the ownership of `/etc/authelia`, the Chisel key and the PKI directory (see [Upgrades](upgrades.md)).
|
|
201
201
|
|
|
202
202
|
The panel re-applies the authfile's group and mode itself on every start, so a Chisel that fails to read it before the panel is up is started again by the panel's reconciliation.
|
|
203
203
|
|
|
@@ -29,7 +29,7 @@ It stops and disables the panel, gatekeeper, Chisel and Authelia; removes their
|
|
|
29
29
|
The manual guide is also available from the CLI:
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
32
|
+
npx @lamalibre/create-lamaste@4.4.1 --uninstall
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
This prints the same steps listed below.
|
|
@@ -27,7 +27,7 @@ Coming from 3.x, upgrade the **agents first**, as 4.0 required: agents from befo
|
|
|
27
27
|
On each agent machine, after the server:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
sudo npm install -g @lamalibre/lamaste-agent@4.4.
|
|
30
|
+
sudo npm install -g @lamalibre/lamaste-agent@4.4.1
|
|
31
31
|
lamaste-agent update
|
|
32
32
|
```
|
|
33
33
|
|
|
@@ -84,7 +84,7 @@ exit
|
|
|
84
84
|
|
|
85
85
|
### Updating Chisel
|
|
86
86
|
|
|
87
|
-
Chisel is the tunnel server binary at `/usr/local/bin/chisel`. Lamaste pins it: server and agents run Chisel 1.12.0 (`CHISEL_RELEASE` in `@lamalibre/lamaste@4.4.
|
|
87
|
+
Chisel is the tunnel server binary at `/usr/local/bin/chisel`. Lamaste pins it: server and agents run Chisel 1.12.0 (`CHISEL_RELEASE` in `@lamalibre/lamaste@4.4.1`), downloaded from the fixed release URL and verified against a pinned SHA-256 before it is unpacked. **Do not replace the binary by hand.** On the server, `create-lamaste` installs it (as root) and replaces a binary that reports any other version; the panel cannot install binaries, and only logs an error at startup when the installed Chisel is not the pinned release (it then restarts Chisel on every authfile change). Each agent's sync replaces its own copy.
|
|
88
88
|
|
|
89
89
|
A new Chisel version therefore arrives with a Lamaste release that changes the pin: re-run the installer, which installs it and restarts Chisel. To check what is running:
|
|
90
90
|
|
|
@@ -98,7 +98,7 @@ Tunnel clients reconnect on their own after the brief interruption (they retry a
|
|
|
98
98
|
|
|
99
99
|
### Updating Authelia
|
|
100
100
|
|
|
101
|
-
Authelia is the authentication server binary at `/usr/local/bin/authelia`. Lamaste pins it too: Authelia 4.39.28 (`AUTHELIA_RELEASE` in `@lamalibre/lamaste@4.4.
|
|
101
|
+
Authelia is the authentication server binary at `/usr/local/bin/authelia`. Lamaste pins it too: Authelia 4.39.28 (`AUTHELIA_RELEASE` in `@lamalibre/lamaste@4.4.1`), downloaded by `create-lamaste` from its fixed release URL and verified against a pinned SHA-256 per architecture. A new Authelia version arrives with a Lamaste release that changes the pin — re-run the installer. It stops Authelia while it replaces the binary and starts it again.
|
|
102
102
|
|
|
103
103
|
An installed Authelia **newer** than the pin is kept, never downgraded: its database may already use a schema the pinned release cannot read, and Authelia refuses to start on a database newer than itself. If you install a newer release by hand, stop the service, replace the binary as root with a `root:root 0755` file, and start it again; check the [Authelia changelog](https://github.com/authelia/authelia/blob/master/CHANGELOG.md) first, since the configuration format may change between versions.
|
|
104
104
|
|
|
@@ -237,7 +237,7 @@ The panel server, CLI, client, docs and gatekeeper are always redeployed (files
|
|
|
237
237
|
|
|
238
238
|
### Version Pinning
|
|
239
239
|
|
|
240
|
-
Chisel and Authelia are pinned by version and SHA-256 in `packages/core/lib/src/constants.ts` (`CHISEL_RELEASE`, `AUTHELIA_RELEASE`); changing a pin means updating the version and every digest there. The downloads are verified by `downloadVerifiedChisel` / `downloadVerifiedAuthelia` (exported from `@lamalibre/lamaste@4.4.
|
|
240
|
+
Chisel and Authelia are pinned by version and SHA-256 in `packages/core/lib/src/constants.ts` (`CHISEL_RELEASE`, `AUTHELIA_RELEASE`); changing a pin means updating the version and every digest there. The downloads are verified by `downloadVerifiedChisel` / `downloadVerifiedAuthelia` (exported from `@lamalibre/lamaste@4.4.1`) before anything is unpacked; the server installer uses them in `packages/provisioners/server/src/lib/binaries.js`. The configuration the panel writes needs Authelia 4.38 or later.
|
|
241
241
|
|
|
242
242
|
### Automated Updates
|
|
243
243
|
|
|
@@ -259,7 +259,7 @@ sudo dpkg-reconfigure -plow unattended-upgrades
|
|
|
259
259
|
| Panel server | `/opt/lamalibre/lamaste/lamaste-serverd/` | Re-run `npx @lamalibre/create-lamaste@latest` |
|
|
260
260
|
| Panel client | `/opt/lamalibre/lamaste/lamaste-server-ui/` | Re-run `npx @lamalibre/create-lamaste@latest` |
|
|
261
261
|
| Chisel | `/usr/local/bin/chisel` | Pinned; installed by `create-lamaste` |
|
|
262
|
-
| Agents | `sudo npm install -g @lamalibre/lamaste-agent@4.4.
|
|
262
|
+
| Agents | `sudo npm install -g @lamalibre/lamaste-agent@4.4.1` | Upgrade after the server, then `lamaste-agent update` |
|
|
263
263
|
| Authelia | `/usr/local/bin/authelia` | Pinned; installed by `create-lamaste` (newer is kept) |
|
|
264
264
|
| nginx | System package | `sudo apt-get update && sudo apt-get upgrade` |
|
|
265
265
|
| certbot | System package | `sudo apt-get update && sudo apt-get upgrade` |
|
|
@@ -733,7 +733,7 @@ The RSA-4096 private key (PKCS#8) Authelia signs ID and JWT access tokens with.
|
|
|
733
733
|
|
|
734
734
|
## `/etc/lamalibre/lamaste/registry-credential.json`
|
|
735
735
|
|
|
736
|
-
The panel's service credential on the Lamalibre npm registry (Feria), used to broker one-hour install tokens for agents (`GET /api/agents/me/registry-credential`) and to revoke a user's tokens when a `feria-*` group is lost.
|
|
736
|
+
The panel's service credential on the Lamalibre npm registry (Feria), used for the panel's own npm calls (plugin installs, update staging, agent-half pinning — through a throw-away userconfig, never a `.npmrc` here), to broker one-hour install tokens for agents (`GET /api/agents/me/registry-credential`) and to revoke a user's tokens when a `feria-*` group is lost.
|
|
737
737
|
|
|
738
738
|
```json
|
|
739
739
|
{
|
|
@@ -926,7 +926,7 @@ Service files use per-agent names:
|
|
|
926
926
|
- macOS: `com.lamalibre.lamaste.chisel-<label>` (plist label), `~/Library/LaunchAgents/com.lamalibre.lamaste.chisel-<label>.plist` — mode 0600, because it carries `AUTH` in `EnvironmentVariables`
|
|
927
927
|
- Linux: `lamalibre-lamaste-chisel-<label>` (unit name), `~/.config/systemd/user/lamalibre-lamaste-chisel-<label>.service` — a systemd **user** unit with `EnvironmentFile=` pointing at `chisel.env`; enable lingering (`sudo loginctl enable-linger <user>`) for it to start at boot without a login
|
|
928
928
|
|
|
929
|
-
Both run `chisel client --max-retry-interval 30s https://tunnel.<domain>:443 R:127.0.0.1:<port>:127.0.0.1:<port> ...` with TLS verification and no credential in argv. They are generated by `@lamalibre/lamaste@4.4.
|
|
929
|
+
Both run `chisel client --max-retry-interval 30s https://tunnel.<domain>:443 R:127.0.0.1:<port>:127.0.0.1:<port> ...` with TLS verification and no credential in argv. They are generated by `@lamalibre/lamaste@4.4.1/agent` (`chisel-service.ts`) and rewritten by `lamaste-agent setup`, `update` and `sync` — do not edit them by hand. An agent carrying no tunnels has its service unloaded (Chisel cannot run without a remote).
|
|
930
930
|
|
|
931
931
|
The sync timer, which runs `<node> <installed lamaste-agent> sync --label <label> --quiet` (`<node>` is a stable path such as `/opt/homebrew/bin/node` when one on `PATH` resolves to the running binary, so a Node.js upgrade does not break it):
|
|
932
932
|
|
|
@@ -24,7 +24,7 @@ If you are reading documentation and hit an unfamiliar term, this is the place t
|
|
|
24
24
|
|
|
25
25
|
**Where it appears:**
|
|
26
26
|
|
|
27
|
-
- Enrolled from the panel (**Certificates → Enrollment Token**) and `lamaste-agent setup` (installed globally with `npm install -g @lamalibre/lamaste-agent@4.4.
|
|
27
|
+
- Enrolled from the panel (**Certificates → Enrollment Token**) and `lamaste-agent setup` (installed globally with `npm install -g @lamalibre/lamaste-agent@4.4.1`)
|
|
28
28
|
- Agent registry: `/etc/lamalibre/lamaste/agents.json` on the server; `~/.lamalibre/lamaste/agents/<label>/` on the agent
|
|
29
29
|
- The **Agent** column and field on the panel's Tunnels page
|
|
30
30
|
|
|
@@ -242,7 +242,7 @@ See also: [mTLS](#mtls-mutual-tls), [CA](#ca-certificate-authority), [PKCS12](#p
|
|
|
242
242
|
|
|
243
243
|
**Where it appears:**
|
|
244
244
|
|
|
245
|
-
- Generated by `lamaste-agent setup` / `update` (implementation in `@lamalibre/lamaste@4.4.
|
|
245
|
+
- Generated by `lamaste-agent setup` / `update` (implementation in `@lamalibre/lamaste@4.4.1/agent`)
|
|
246
246
|
- Installed to `~/Library/LaunchAgents/com.lamalibre.lamaste.chisel-<label>.plist` on the agent
|
|
247
247
|
- Manages the Chisel client process lifecycle
|
|
248
248
|
|
|
@@ -373,7 +373,7 @@ See also: [Client Certificate](#client-certificate), [CA](#ca-certificate-author
|
|
|
373
373
|
|
|
374
374
|
**Plain English:** A small scheduled job on every agent that checks with the panel every 30 seconds and adjusts the tunnel client when the agent's tunnels changed — so tunnels created, disabled or moved in the panel reach the agent without anyone running a command on it.
|
|
375
375
|
|
|
376
|
-
**Technical:** Runs `lamaste-agent sync --label <label>`, which fetches `GET /api/tunnels/agent-config` and converges the agent's Chisel client with it (relay URL, credential, pinned binary, granted remotes, running or stopped). It exists because Chisel rejects a client's whole session when one requested remote is not granted: an agent must drop a revoked tunnel promptly or all its other tunnels go dark. Requires a global install of `@lamalibre/lamaste-agent@4.4.
|
|
376
|
+
**Technical:** Runs `lamaste-agent sync --label <label>`, which fetches `GET /api/tunnels/agent-config` and converges the agent's Chisel client with it (relay URL, credential, pinned binary, granted remotes, running or stopped). It exists because Chisel rejects a client's whole session when one requested remote is not granted: an agent must drop a revoked tunnel promptly or all its other tunnels go dark. Requires a global install of `@lamalibre/lamaste-agent@4.4.1`.
|
|
377
377
|
|
|
378
378
|
**Where it appears:**
|
|
379
379
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Installer Flags
|
|
2
2
|
|
|
3
|
-
> CLI flags, environment variables, and exit codes for `npx @lamalibre/create-lamaste@4.4.
|
|
3
|
+
> CLI flags, environment variables, and exit codes for `npx @lamalibre/create-lamaste@4.4.1`.
|
|
4
4
|
|
|
5
5
|
## Usage
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
8
|
+
npx @lamalibre/create-lamaste@4.4.1 [flags]
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
The installer runs as root on a fresh Ubuntu 24.04 server. It is completely non-interactive by default — all configuration happens later through the browser-based onboarding wizard.
|
|
@@ -26,7 +26,7 @@ The installer runs as root on a fresh Ubuntu 24.04 server. It is completely non-
|
|
|
26
26
|
Prints a description of what the installer does, lists all flags, and exits with code 0. Does not modify anything on the system.
|
|
27
27
|
|
|
28
28
|
```bash
|
|
29
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
29
|
+
npx @lamalibre/create-lamaste@4.4.1 --help
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
### `--yes`, `-y`
|
|
@@ -34,7 +34,7 @@ npx @lamalibre/create-lamaste@4.4.0 --help
|
|
|
34
34
|
Skips the interactive confirmation prompt that appears before installation begins. Useful for automated deployments or scripts where you cannot press Enter.
|
|
35
35
|
|
|
36
36
|
```bash
|
|
37
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
37
|
+
npx @lamalibre/create-lamaste@4.4.1 --yes
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
Without this flag, the installer displays a banner listing all changes it will make and waits for the user to press Enter or Ctrl+C.
|
|
@@ -51,7 +51,7 @@ Skips all OS hardening tasks:
|
|
|
51
51
|
Everything else (Node.js, nginx, mTLS, panel deployment) still runs.
|
|
52
52
|
|
|
53
53
|
```bash
|
|
54
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
54
|
+
npx @lamalibre/create-lamaste@4.4.1 --skip-harden
|
|
55
55
|
```
|
|
56
56
|
|
|
57
57
|
Use this if you manage your own firewall rules, already have fail2ban configured, or are running on a platform that handles security at a different layer.
|
|
@@ -61,7 +61,7 @@ Use this if you manage your own firewall rules, already have fail2ban configured
|
|
|
61
61
|
Allows the installer to run on a machine with a private or non-routable IP address (e.g., `192.168.x.x`, `10.x.x.x`). By default, the installer requires a public IP.
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
64
|
+
npx @lamalibre/create-lamaste@4.4.1 --dev
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
This is useful for:
|
|
@@ -76,7 +76,7 @@ When `--dev` is active, the installer prints the detected IP with a note: `(dev
|
|
|
76
76
|
Forces the installer to run the complete installation pipeline, ignoring skip guards that would normally detect an existing installation. This is useful when an installation is in an inconsistent state and you want to ensure all components are fully deployed.
|
|
77
77
|
|
|
78
78
|
```bash
|
|
79
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
79
|
+
npx @lamalibre/create-lamaste@4.4.1 --force-full
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
Use this if:
|
|
@@ -90,7 +90,7 @@ Use this if:
|
|
|
90
90
|
Prints a step-by-step guide for manually removing Lamaste from the server, then exits with code 0. Does not modify anything on the system.
|
|
91
91
|
|
|
92
92
|
```bash
|
|
93
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
93
|
+
npx @lamalibre/create-lamaste@4.4.1 --uninstall
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
The printed guide first points to the automated removal, `sudo lamaste-server uninstall`, then covers:
|
|
@@ -207,25 +207,25 @@ Components that are always redeployed on re-run:
|
|
|
207
207
|
|
|
208
208
|
```bash
|
|
209
209
|
ssh root@203.0.113.42
|
|
210
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
210
|
+
npx @lamalibre/create-lamaste@4.4.1
|
|
211
211
|
```
|
|
212
212
|
|
|
213
213
|
**Automated install (no confirmation prompt):**
|
|
214
214
|
|
|
215
215
|
```bash
|
|
216
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
216
|
+
npx @lamalibre/create-lamaste@4.4.1 --yes
|
|
217
217
|
```
|
|
218
218
|
|
|
219
219
|
**Install without OS hardening:**
|
|
220
220
|
|
|
221
221
|
```bash
|
|
222
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
222
|
+
npx @lamalibre/create-lamaste@4.4.1 --yes --skip-harden
|
|
223
223
|
```
|
|
224
224
|
|
|
225
225
|
**Test in a local VM:**
|
|
226
226
|
|
|
227
227
|
```bash
|
|
228
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
228
|
+
npx @lamalibre/create-lamaste@4.4.1 --dev
|
|
229
229
|
```
|
|
230
230
|
|
|
231
231
|
**Update an existing installation:**
|
|
@@ -237,7 +237,7 @@ npx @lamalibre/create-lamaste@latest --yes
|
|
|
237
237
|
**View the uninstall guide:**
|
|
238
238
|
|
|
239
239
|
```bash
|
|
240
|
-
npx @lamalibre/create-lamaste@4.4.
|
|
240
|
+
npx @lamalibre/create-lamaste@4.4.1 --uninstall
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
## Quick Reference
|
|
@@ -34,7 +34,7 @@ A tunnel's port is also a loopback port on the relay: its vhost proxies to `127.
|
|
|
34
34
|
| 9292 | IP-based panel listener |
|
|
35
35
|
| 9294 | Gatekeeper |
|
|
36
36
|
|
|
37
|
-
A tunnel on one of them would publish that internal service on a public hostname. For the panel that is an administrator takeover, not a leak: the panel trusts the client-certificate headers nginx sets. `RESERVED_TUNNEL_PORTS` in `@lamalibre/lamaste@4.4.
|
|
37
|
+
A tunnel on one of them would publish that internal service on a public hostname. For the panel that is an administrator takeover, not a leak: the panel trusts the client-certificate headers nginx sets. `RESERVED_TUNNEL_PORTS` in `@lamalibre/lamaste@4.4.1` is enforced by the request schema, inside the tunnel workflow, and again when the Chisel grants are rendered. Tunnel ports must also be 1024–65535.
|
|
38
38
|
|
|
39
39
|
## Systemd Units
|
|
40
40
|
|