@volter/twin-catalog 0.2.33 → 0.2.34

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/catalog.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "sourceCommit": "b8885b66d78a6277c6fa1ed2883aa28f6d499b62",
3
+ "sourceCommit": "6974ffe917b9d91212225eed921b735428cf07ec",
4
4
  "digest": "b21e251b1ff51975a212e56872423830967cd3a1b6a258af4eca0db7287574fe",
5
5
  "versions": [
6
6
  {
package/docs/process.md CHANGED
@@ -47,7 +47,7 @@ does not change that pin. Removing a recommendation is not uninstalling a runnin
47
47
  Each assessed release exposes its artifact's description, license, Node requirements, kernel peer requirements,
48
48
  source directory and issue tracker beside its integrity, provenance and measured behavior. README and license links
49
49
  point to the source commit verified by npm provenance. They describe that version, not the publisher's current branch.
50
- The JSON-only reader also projects installed customer scope, pinned SDK dependencies, actual CLI/kernel/runtime versions, clock conditions, retention and teardown when the bound report measured them. An older report exposes `consumer: null`; it cannot acquire a passing workflow from publisher trust or a later test.
50
+ The JSON-only reader also projects installed customer scope, pinned SDK dependencies, actual CLI/kernel/runtime versions, app-local dependency identity, clock conditions, retention and teardown when the bound report measured them. An older report without a customer workflow exposes `consumer: null`; one without app-local dependency evidence exposes `application: null`. Neither acquires newer installation evidence from publisher trust or a later test.
51
51
 
52
52
  Historical assessments without these retained fields show them as unavailable; current registry metadata does not
53
53
  fill missing historical evidence. A provenance link establishes source and build identity, not vendor fidelity.
@@ -120,6 +120,10 @@ never the contributor's head. Source-registration and maintenance PRs do not exe
120
120
  extraction, dependency installation and evaluation occur in disposable containers with no repository, npm, cloud,
121
121
  moderation or publication credentials, no Docker socket and no host project mounts. Install scripts are disabled.
122
122
  Dependencies are resolved once into a recorded lock; evaluation reuses that installation with network disabled.
123
+ The customer app has its own candidate, CLI-matching kernel and SDK installation in a separate owned volume,
124
+ outside the evaluator's dependency tree. Both volumes survive preparation and offline evaluation; temporary
125
+ container files do not hold prepared inputs. The controller retains both dependency locks and verifies cleanup
126
+ of every volume it created. This proves an app-local installation rather than ancestor dependency resolution.
123
127
  Before dependency installation, preparation confirms the candidate's exact version and integrity in npm's
124
128
  installation metadata as well as its exact-version metadata. These registry views may propagate separately.
125
129
  Tarball downloads also confirm availability and integrity before extraction. Only HTTP 404 or an otherwise valid package document without that version is retried, using the existing measured
package/lib/browse.d.mts CHANGED
@@ -14,6 +14,12 @@ export type Measurements = {
14
14
  fixture: string | null;
15
15
  sdkDependencies: Record<string, string> | null;
16
16
  tools: Record<string, string> | null;
17
+ application: null | {
18
+ appLocal: boolean;
19
+ candidate: { package: string; version: string; integrity: string };
20
+ kernel: { package: string; version: string };
21
+ dependencyLockSha256: string;
22
+ };
17
23
  clock: Record<string, unknown> | null;
18
24
  retainedReadback: boolean | null;
19
25
  retentionNotApplicable: string | null;
package/lib/browse.mjs CHANGED
@@ -135,6 +135,7 @@ export function browse(root, { vendor, package: packageName, integrity = null }
135
135
  scope: bound.report.consumer.scope ?? null, fixture: bound.report.consumer.fixture ?? null,
136
136
  sdkDependencies: bound.envelope.prepared?.consumerFixture?.dependencies ?? null,
137
137
  tools: bound.report.consumer.tools ?? null, clock: bound.report.consumer.clock ?? null,
138
+ application: bound.report.consumer.application ?? null,
138
139
  retainedReadback: bound.report.consumer.retainedReadback ?? null,
139
140
  retentionNotApplicable: bound.report.consumer.retentionNotApplicable ?? null,
140
141
  teardownVerified: bound.report.consumer.teardownVerified ?? null,
package/lib/runner.mjs CHANGED
@@ -22,23 +22,28 @@ export function withContainer(root, input, output, execute, { spawn = spawnSync
22
22
  if (!allowFailure) requireThat(r.status === 0, `container ${args[0]} failed: ${r.error?.message ?? r.stderr}`);
23
23
  return r;
24
24
  };
25
- let volume = false;
25
+ const volumes = [];
26
+ const customerVolume = `${id}-customer`;
26
27
  try {
27
28
  const tools = input.policy.tools;
28
29
  requireThat(tools['@volter/world'], 'policy must pin the product CLI for installed customer workflows');
29
30
  run(['build', '--iidfile', imageFile, '--build-arg', `NODE_IMAGE=${input.policy.nodeImage}`, '--build-arg', `BUN_VERSION=${input.policy.bun}`, '--build-arg', `STANDARD_VERSION=${tools['@volter/twin-standard']}`, '--build-arg', `CORE_VERSION=${tools['@volter/world-core']}`, '--build-arg', `RUNTIME_VERSION=${tools['@volter/world-runtime']}`, '--build-arg', `CLI_VERSION=${tools['@volter/world']}`, '--build-arg', `PLAYWRIGHT_VERSION=${tools.playwright}`, '-f', join(root, 'runner/Dockerfile'), root]);
30
31
  const image = readFileSync(imageFile, 'utf8').trim();
31
32
  requireThat(/^sha256:[a-f0-9]{64}$/.test(image), 'container image has no immutable identity');
32
- run(['volume', 'create', '--label', `twin-catalog.owner=${id}`, '--label', `twin-catalog.run=${runOwner}`, id]); volume = true;
33
- // Docker volumes start root-owned. Only this task's new volume is changed.
34
- run(['run', '--rm', '--label', `twin-catalog.owner=${id}`, '--label', `twin-catalog.run=${runOwner}`, '--network', 'none', '--user', '0', '--entrypoint', 'chown', '-v', `${id}:/work`, image, '1000:1000', '/work']);
35
- const common = ['run', '--rm', '--label', `twin-catalog.owner=${id}`, '--label', `twin-catalog.run=${runOwner}`, '--init', '--shm-size', '256m', '--read-only', '--cap-drop', 'ALL', '--security-opt', 'no-new-privileges', '--tmpfs', '/tmp:rw,nosuid,nodev', '-e', 'HOME=/tmp/home', '-e', 'npm_config_cache=/tmp/npm-cache', '-v', `${id}:/work`, '-v', `${work}:/input:ro`];
33
+ for (const volume of [id, customerVolume]) {
34
+ run(['volume', 'create', '--label', `twin-catalog.owner=${id}`, '--label', `twin-catalog.run=${runOwner}`, volume]);
35
+ volumes.push(volume);
36
+ }
37
+ // Only these task-owned new volumes are changed; both persist across container phases.
38
+ run(['run', '--rm', '--label', `twin-catalog.owner=${id}`, '--label', `twin-catalog.run=${runOwner}`, '--network', 'none', '--user', '0', '--entrypoint', 'chown', '-v', `${id}:/work`, '-v', `${customerVolume}:/customer`, image, '1000:1000', '/work', '/customer']);
39
+ const common = ['run', '--rm', '--label', `twin-catalog.owner=${id}`, '--label', `twin-catalog.run=${runOwner}`, '--init', '--shm-size', '256m', '--read-only', '--cap-drop', 'ALL', '--security-opt', 'no-new-privileges', '--tmpfs', '/tmp:rw,nosuid,nodev', '-e', 'HOME=/tmp/home', '-e', 'npm_config_cache=/tmp/npm-cache', '-v', `${id}:/work`, '-v', `${customerVolume}:/customer`, '-v', `${work}:/input:ro`];
36
40
  return execute({ image, common, run, owner: id });
37
41
  } finally {
38
- const cleanup = volume ? run(['volume', 'rm', id], true) : null;
42
+ const cleanup = volumes.map(volume => ({ volume, result: run(['volume', 'rm', volume], true) }));
39
43
  write(`${output}.logs.json`, logs);
40
44
  // Retain input identity and diagnostics on failure; no other actor's resources are touched.
41
- requireThat(!cleanup || cleanup.status === 0, `owned volume teardown could not be verified: ${id}`);
45
+ const failed = cleanup.filter(({ result }) => result.status !== 0);
46
+ requireThat(failed.length === 0, `owned volume teardown could not be verified: ${failed.map(({ volume }) => volume).join(', ')}`);
42
47
  }
43
48
  }
44
49
 
@@ -57,7 +62,8 @@ export function evaluate(root, input, output, options = {}) {
57
62
  const lock = run([...common, '--network', 'none', image, '-e', 'process.stdout.write(require("fs").readFileSync("/work/package-lock.json","utf8"))']).stdout;
58
63
  requireThat(digest(lock) === prepared.dependencyLockSha256, 'dependency lock identity changed');
59
64
  write(`${output}.lock.json`, JSON.parse(lock));
60
- const consumerLock = run([...common, '--network', 'none', image, '-e', 'process.stdout.write(require("fs").readFileSync("/work/customer-app/package-lock.json","utf8"))']).stdout;
65
+ requireThat(prepared.consumer?.appDir === '/customer/app', 'prepared customer app is outside its owned volume');
66
+ const consumerLock = run([...common, '--network', 'none', image, '-e', 'process.stdout.write(require("fs").readFileSync("/customer/app/package-lock.json","utf8"))']).stdout;
61
67
  requireThat(digest(consumerLock) === prepared.consumerDependencyLockSha256, 'customer SDK lock identity changed');
62
68
  write(`${output}.consumer-lock.json`, JSON.parse(consumerLock));
63
69
  phase = 'offline-assessment';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/twin-catalog",
3
- "version": "0.2.33",
3
+ "version": "0.2.34",
4
4
  "description": "The moderated twin distribution: approved versions and submission tooling for independent publishers.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -38,7 +38,7 @@
38
38
  "evidence"
39
39
  ],
40
40
  "catalog": {
41
- "sourceCommit": "b8885b66d78a6277c6fa1ed2883aa28f6d499b62",
41
+ "sourceCommit": "6974ffe917b9d91212225eed921b735428cf07ec",
42
42
  "digest": "b21e251b1ff51975a212e56872423830967cd3a1b6a258af4eca0db7287574fe"
43
43
  }
44
44
  }
package/policy.json CHANGED
@@ -2,11 +2,11 @@
2
2
  "schemaVersion": 1,
3
3
  "registry": "https://registry.npmjs.org",
4
4
  "tools": {
5
- "@volter/world-core": "3.0.108",
6
- "@volter/world-runtime": "3.0.108",
7
- "@volter/twin-standard": "1.0.100",
5
+ "@volter/world-core": "3.0.113",
6
+ "@volter/world-runtime": "3.0.113",
7
+ "@volter/twin-standard": "1.0.105",
8
8
  "playwright": "1.63.0",
9
- "@volter/world": "3.0.108"
9
+ "@volter/world": "3.0.113"
10
10
  },
11
11
  "bun": "1.4.2",
12
12
  "nodeImage": "node:24-bookworm-slim",
@@ -82,23 +82,25 @@ const provenance = await verifiedProvenance(await response.json(), source, bytes
82
82
  mkdirSync('/work/packs', { recursive: true });
83
83
  cpSync(join('/work/node_modules', s.package), join('/work/packs', s.vendor), { recursive: true });
84
84
  const { prepareConsumerApp } = await import(evaluatorRequire.resolve('@volter/twin-standard'));
85
- const appDir = '/work/customer-app';
86
- const consumerFixture = prepareConsumerApp(join('/work/packs', s.vendor), appDir);
85
+ const productRoot = '/work/node_modules/@volter/world';
86
+ const product = read(join(productRoot, 'package.json'));
87
+ requireThat(product.name === '@volter/world' && product.version === policy.tools['@volter/world'], 'customer CLI differs from the product pin');
88
+ requireThat(typeof product.bin?.volter === 'string', 'product has no declared volter entry');
89
+ const cli = realpathSync(join(productRoot, product.bin.volter));
90
+ requireThat(!relative(realpathSync(productRoot), cli).startsWith('..'), 'CLI entry is outside the installed product');
91
+ // Keep the app outside /work's evaluator dependency tree.
92
+ const appDir = '/customer/app';
93
+ const consumerFixture = prepareConsumerApp(join('/work/packs', s.vendor), appDir, { cli });
87
94
  const appInstall = spawnSync('npm', ['install', '--ignore-scripts', '--no-audit', '--no-fund', '--registry', policy.registry], { cwd: appDir, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 });
88
95
  requireThat(appInstall.status === 0, `customer SDK preparation failed: ${appInstall.stderr}`);
89
96
  const consumerLock = read(join(appDir, 'package-lock.json'));
97
+ requireThat(consumerLock.packages[`node_modules/${s.package}`]?.integrity === s.integrity, 'customer candidate differs from the admitted artifact');
98
+ requireThat(consumerLock.packages['node_modules/@volter/world-core']?.version === policy.tools['@volter/world-core'], 'customer kernel differs from the CLI pin');
90
99
  for (const [path, entry] of Object.entries(consumerLock.packages ?? {})) {
91
100
  if (!path) continue;
92
101
  requireThat(!entry.link && entry.resolved && entry.integrity, `${path}: customer dependency is not a locked registry artifact`);
93
102
  registryURL(entry.resolved, policy.registry);
94
103
  }
95
- // Other dependencies can own the flattened volter shortcut; use the exact product's declared entry.
96
- const productRoot = '/work/node_modules/@volter/world';
97
- const product = read(join(productRoot, 'package.json'));
98
- requireThat(product.name === '@volter/world' && product.version === policy.tools['@volter/world'], 'customer CLI differs from the product pin');
99
- requireThat(typeof product.bin?.volter === 'string', 'product has no declared volter entry');
100
- const cli = realpathSync(join(productRoot, product.bin.volter));
101
- requireThat(!relative(realpathSync(productRoot), cli).startsWith('..'), 'CLI entry is outside the installed product');
102
104
  const consumer = { appDir, cli, artifactIntegrity: s.integrity };
103
105
  // Retain listing facts from these exact bytes, never the registry's moving latest metadata.
104
106
  const packageMetadata = {