@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
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
} from '@lamalibre/lamaste';
|
|
30
30
|
import { readTarballFiles } from '@lamalibre/lamaste/server';
|
|
31
31
|
import { httpError } from './app-error.js';
|
|
32
|
+
import { withRegistryUserconfig } from './registry-credential.js';
|
|
32
33
|
|
|
33
34
|
const STATE_DIR = process.env.LAMALIBRE_LAMASTE_STATE_DIR || '/etc/lamalibre/lamaste';
|
|
34
35
|
|
|
@@ -88,9 +89,11 @@ export async function installedHookEntrySha256(packageName, manifest) {
|
|
|
88
89
|
}
|
|
89
90
|
|
|
90
91
|
/**
|
|
91
|
-
* Fetch `<package>@<version>` from the registry
|
|
92
|
-
*
|
|
93
|
-
*
|
|
92
|
+
* Fetch `<package>@<version>` from the registry the panel installs from
|
|
93
|
+
* (`npm pack` through {@link withRegistryUserconfig}: the configured
|
|
94
|
+
* registry credential, else the machine's npm configuration) and read its
|
|
95
|
+
* manifest and, when the manifest names one, its relay hook module — in
|
|
96
|
+
* memory only.
|
|
94
97
|
*
|
|
95
98
|
* @param {string} packageName bare `@lamalibre/<name>`
|
|
96
99
|
* @param {string} version an exact version or a dist-tag (`latest`)
|
|
@@ -103,22 +106,24 @@ export async function readRegistryPackage(packageName, version) {
|
|
|
103
106
|
try {
|
|
104
107
|
let packed;
|
|
105
108
|
try {
|
|
106
|
-
const { stdout } = await
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
109
|
+
const { stdout } = await withRegistryUserconfig((env) =>
|
|
110
|
+
execa(
|
|
111
|
+
'npm',
|
|
112
|
+
[
|
|
113
|
+
'pack',
|
|
114
|
+
`${packageName}@${version}`,
|
|
115
|
+
'--json',
|
|
116
|
+
'--ignore-scripts',
|
|
117
|
+
'--pack-destination',
|
|
118
|
+
dir,
|
|
119
|
+
],
|
|
120
|
+
{ cwd: STATE_DIR, timeout: 120000, env },
|
|
121
|
+
),
|
|
117
122
|
);
|
|
118
123
|
packed = JSON.parse(stdout);
|
|
119
124
|
} catch (err) {
|
|
120
125
|
throw httpError(
|
|
121
|
-
`Could not fetch ${packageName}@${version} from the registry: ${(err
|
|
126
|
+
`Could not fetch ${packageName}@${version} from the registry: ${npmFailureLine(err)}`,
|
|
122
127
|
502,
|
|
123
128
|
);
|
|
124
129
|
}
|
|
@@ -171,9 +176,9 @@ function npmFailureLine(err) {
|
|
|
171
176
|
|
|
172
177
|
/**
|
|
173
178
|
* The npm Subresource Integrity of `<package>@<version>`'s tarball, as the
|
|
174
|
-
* registry
|
|
175
|
-
*
|
|
176
|
-
* installs use. Only a sha512 SRI is accepted: it is what the approval
|
|
179
|
+
* registry the panel installs from publishes it — `npm view … dist.integrity`
|
|
180
|
+
* through {@link withRegistryUserconfig}, so the registry and credential
|
|
181
|
+
* are the ones plugin installs use. Only a sha512 SRI is accepted: it is what the approval
|
|
177
182
|
* binds and what the agent checks the tarball it fetches against.
|
|
178
183
|
*
|
|
179
184
|
* @param {string} packageName bare `@lamalibre/<name>`
|
|
@@ -186,10 +191,13 @@ export async function readRegistryIntegrity(packageName, version) {
|
|
|
186
191
|
const spec = `${packageName}@${version}`;
|
|
187
192
|
let stdout;
|
|
188
193
|
try {
|
|
189
|
-
({ stdout } = await
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
({ stdout } = await withRegistryUserconfig((env) =>
|
|
195
|
+
execa('npm', ['view', spec, 'dist.integrity', '--json'], {
|
|
196
|
+
cwd: STATE_DIR,
|
|
197
|
+
timeout: 60000,
|
|
198
|
+
env,
|
|
199
|
+
}),
|
|
200
|
+
));
|
|
193
201
|
} catch (err) {
|
|
194
202
|
if (/\bE404\b/.test(`${err.stderr ?? ''}\n${err.stdout ?? ''}`)) {
|
|
195
203
|
throw httpError(
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
import { listRegisteredSubScopeNames } from './tickets.js';
|
|
26
26
|
import { getStateDb } from './state-db.js';
|
|
27
27
|
import { revokePluginCapabilitiesFromAgents } from './mtls.js';
|
|
28
|
+
import { withRegistryUserconfig } from './registry-credential.js';
|
|
29
|
+
import { RegistryCredentialError } from '@lamalibre/lamaste/server';
|
|
28
30
|
import {
|
|
29
31
|
bareLamalibrePackageName,
|
|
30
32
|
installedHookEntrySha256,
|
|
@@ -326,6 +328,17 @@ export async function manifestPolicyError(manifest) {
|
|
|
326
328
|
return null;
|
|
327
329
|
}
|
|
328
330
|
|
|
331
|
+
/**
|
|
332
|
+
* What a failed install of `spec` is reported as. A registry that refused
|
|
333
|
+
* the panel's credential says so (the registry host and the status, from
|
|
334
|
+
* {@link withRegistryUserconfig}); anything else is npm's failure, whose
|
|
335
|
+
* output is in the log.
|
|
336
|
+
*/
|
|
337
|
+
function installFailure(spec, err) {
|
|
338
|
+
const detail = err instanceof RegistryCredentialError ? `: ${err.message}` : '';
|
|
339
|
+
return `Failed to install "${spec}"${detail}`;
|
|
340
|
+
}
|
|
341
|
+
|
|
329
342
|
export function installPlugin(packageName, logger) {
|
|
330
343
|
return withPluginLock(async () => {
|
|
331
344
|
// Validate package scope
|
|
@@ -358,19 +371,25 @@ export function installPlugin(packageName, logger) {
|
|
|
358
371
|
}
|
|
359
372
|
}
|
|
360
373
|
|
|
361
|
-
// Install the npm package
|
|
374
|
+
// Install the npm package — from the configured registry, as the
|
|
375
|
+
// panel's own identity, when a registry credential is set.
|
|
362
376
|
logger.info({ packageName: installSpec }, 'Installing plugin package');
|
|
363
377
|
try {
|
|
364
|
-
await
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
378
|
+
await withRegistryUserconfig(
|
|
379
|
+
(env) =>
|
|
380
|
+
execa('npm', ['install', '--ignore-scripts', installSpec], {
|
|
381
|
+
cwd: STATE_DIR,
|
|
382
|
+
timeout: 120000,
|
|
383
|
+
env,
|
|
384
|
+
}),
|
|
385
|
+
{ log: logger },
|
|
386
|
+
);
|
|
368
387
|
} catch (err) {
|
|
369
388
|
logger.error(
|
|
370
389
|
{ packageName: installSpec, stderr: err.stderr, err: err.message },
|
|
371
390
|
'npm install failed',
|
|
372
391
|
);
|
|
373
|
-
throw Object.assign(new Error(
|
|
392
|
+
throw Object.assign(new Error(installFailure(installSpec, err)), { statusCode: 500 });
|
|
374
393
|
}
|
|
375
394
|
|
|
376
395
|
// Read the plugin manifest from the installed package
|
|
@@ -688,13 +707,18 @@ export function replacePluginPackage(name, packageName, version, logger) {
|
|
|
688
707
|
assertSafeLamalibrePackage(spec);
|
|
689
708
|
logger?.info({ name, spec }, 'Installing plugin package version');
|
|
690
709
|
try {
|
|
691
|
-
await
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
710
|
+
await withRegistryUserconfig(
|
|
711
|
+
(env) =>
|
|
712
|
+
execa('npm', ['install', '--ignore-scripts', spec], {
|
|
713
|
+
cwd: STATE_DIR,
|
|
714
|
+
timeout: 120000,
|
|
715
|
+
env,
|
|
716
|
+
}),
|
|
717
|
+
{ log: logger },
|
|
718
|
+
);
|
|
695
719
|
} catch (err) {
|
|
696
720
|
logger?.error({ spec, stderr: err.stderr, err: err.message }, 'npm install failed');
|
|
697
|
-
throw Object.assign(new Error(
|
|
721
|
+
throw Object.assign(new Error(installFailure(spec, err)), { statusCode: 502 });
|
|
698
722
|
}
|
|
699
723
|
const require = createRequire(`${STATE_DIR}/`);
|
|
700
724
|
const manifestPath = require.resolve(`${bare}/lamaste-plugin.json`);
|
|
@@ -12,6 +12,13 @@
|
|
|
12
12
|
* install within the hour and limited to ten new tokens per agent per
|
|
13
13
|
* hour.
|
|
14
14
|
*
|
|
15
|
+
* The panel's own npm calls — installing a plugin, staging an update with
|
|
16
|
+
* `npm pack`, pinning an agent half with `npm view` — go through
|
|
17
|
+
* {@link withRegistryUserconfig}: with a credential configured, npm is
|
|
18
|
+
* routed to its registry for its scope and authenticated with the service
|
|
19
|
+
* token through a throw-away userconfig; without one, npm runs as it is
|
|
20
|
+
* configured on the machine (the public registry by default).
|
|
21
|
+
*
|
|
15
22
|
* Also the one piece of policy that belongs here: when a user loses a
|
|
16
23
|
* `feria-*` group or is deleted, their registry tokens are revoked (best
|
|
17
24
|
* effort, never blocks the user operation).
|
|
@@ -26,6 +33,7 @@ import {
|
|
|
26
33
|
revokeRegistryTokensForOwner,
|
|
27
34
|
lostFeriaGroups,
|
|
28
35
|
readOidcClientSecret,
|
|
36
|
+
withRegistryUserconfig as withRegistryUserconfigCore,
|
|
29
37
|
RegistryServiceTokenCache,
|
|
30
38
|
AgentTokenBroker,
|
|
31
39
|
RegistryCredentialError,
|
|
@@ -102,6 +110,35 @@ async function serviceToken(config) {
|
|
|
102
110
|
return (await serviceTokens.resolve(config, await oidcDeps(config))).token;
|
|
103
111
|
}
|
|
104
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Run `fn(env)` — one npm call of the panel's own — against the configured
|
|
115
|
+
* registry: with a credential, `env` names an isolated userconfig (0600,
|
|
116
|
+
* removed afterwards) routing the credential's scope to its registry with
|
|
117
|
+
* the panel's service token; a `401` refreshes the token once and retries.
|
|
118
|
+
* Without a credential `fn({})` runs npm as the machine configures it.
|
|
119
|
+
* A credential file that cannot be read fails the call rather than
|
|
120
|
+
* silently installing from the public registry.
|
|
121
|
+
*
|
|
122
|
+
* @template T
|
|
123
|
+
* @param {(env: Record<string, string>) => Promise<T>} fn
|
|
124
|
+
* @param {{ log?: import('pino').Logger | undefined }} [options]
|
|
125
|
+
* @returns {Promise<T>}
|
|
126
|
+
*/
|
|
127
|
+
export async function withRegistryUserconfig(fn, options = {}) {
|
|
128
|
+
const config = await readRegistryCredentialConfig();
|
|
129
|
+
if (!config) return fn({});
|
|
130
|
+
const deps = await oidcDeps(config);
|
|
131
|
+
return withRegistryUserconfigCore(
|
|
132
|
+
config,
|
|
133
|
+
async ({ refresh }) => {
|
|
134
|
+
if (refresh) serviceTokens.invalidate();
|
|
135
|
+
return (await serviceTokens.resolve(config, deps)).token;
|
|
136
|
+
},
|
|
137
|
+
fn,
|
|
138
|
+
{ log: options.log },
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
105
142
|
/**
|
|
106
143
|
* The calling agent's registry token: the cached one when it is still good
|
|
107
144
|
* for an install, otherwise a freshly brokered one-hour token — unless the
|