@amalgm/shell 0.1.162 → 0.1.164-main.34182640231

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 (144) hide show
  1. package/PURPOSE.md +163 -17
  2. package/README.md +74 -11
  3. package/config/releases/trust/runtime/main.root.json +84 -0
  4. package/config/releases/trust/runtime/preview.root.json +84 -0
  5. package/dist/authorization/recovery-host.d.ts +1 -0
  6. package/dist/authorization/recovery-host.js +2 -3
  7. package/dist/authorization/recovery-host.js.map +1 -1
  8. package/dist/boot.js +28 -20
  9. package/dist/boot.js.map +1 -1
  10. package/dist/cli-arguments.d.ts +8 -0
  11. package/dist/cli-arguments.js +47 -0
  12. package/dist/cli-arguments.js.map +1 -0
  13. package/dist/cli.d.ts +6 -4
  14. package/dist/cli.js +91 -73
  15. package/dist/cli.js.map +1 -1
  16. package/dist/compose.js +4 -3
  17. package/dist/compose.js.map +1 -1
  18. package/dist/content-cache-index.js +2 -2
  19. package/dist/content-cache-index.js.map +1 -1
  20. package/dist/detection/runtime.js +2 -2
  21. package/dist/detection/runtime.js.map +1 -1
  22. package/dist/development/profile.d.ts +27 -0
  23. package/dist/development/profile.js +142 -0
  24. package/dist/development/profile.js.map +1 -0
  25. package/dist/entity-apply-host.js +2 -2
  26. package/dist/entity-apply-host.js.map +1 -1
  27. package/dist/entity-apply-store.d.ts +2 -1
  28. package/dist/entity-apply-store.js +52 -37
  29. package/dist/entity-apply-store.js.map +1 -1
  30. package/dist/files-register-host.d.ts +1 -0
  31. package/dist/files-register-host.js +1 -0
  32. package/dist/files-register-host.js.map +1 -1
  33. package/dist/installation/environment.d.ts +30 -0
  34. package/dist/installation/environment.js +28 -0
  35. package/dist/installation/environment.js.map +1 -0
  36. package/dist/installation/lease.d.ts +6 -0
  37. package/dist/installation/lease.js +28 -0
  38. package/dist/installation/lease.js.map +1 -0
  39. package/dist/installation/ownership.d.ts +30 -0
  40. package/dist/installation/ownership.js +156 -0
  41. package/dist/installation/ownership.js.map +1 -0
  42. package/dist/installation/selection.d.ts +8 -0
  43. package/dist/installation/selection.js +29 -0
  44. package/dist/installation/selection.js.map +1 -0
  45. package/dist/machine-host.d.ts +27 -5
  46. package/dist/machine-host.js +244 -96
  47. package/dist/machine-host.js.map +1 -1
  48. package/dist/main.js +56 -2
  49. package/dist/main.js.map +1 -1
  50. package/dist/platform-product-host.d.ts +9 -9
  51. package/dist/platform-product-host.js +17 -17
  52. package/dist/platform-product-host.js.map +1 -1
  53. package/dist/platform-tools.d.ts +4 -3
  54. package/dist/platform-tools.js +34 -37
  55. package/dist/platform-tools.js.map +1 -1
  56. package/dist/process-lifecycle.d.ts +6 -0
  57. package/dist/process-lifecycle.js +42 -0
  58. package/dist/process-lifecycle.js.map +1 -0
  59. package/dist/pty-host.d.ts +1 -1
  60. package/dist/pty-host.js +70 -21
  61. package/dist/pty-host.js.map +1 -1
  62. package/dist/runtime-command-host.d.ts +7 -0
  63. package/dist/runtime-command-host.js +49 -13
  64. package/dist/runtime-command-host.js.map +1 -1
  65. package/dist/service/adapter.d.ts +36 -0
  66. package/dist/service/adapter.js +24 -0
  67. package/dist/service/adapter.js.map +1 -0
  68. package/dist/service/cli.d.ts +8 -0
  69. package/dist/service/cli.js +237 -0
  70. package/dist/service/cli.js.map +1 -0
  71. package/dist/service/control.d.ts +38 -0
  72. package/dist/service/control.js +68 -0
  73. package/dist/service/control.js.map +1 -0
  74. package/dist/service/index.d.ts +9 -0
  75. package/dist/service/index.js +10 -0
  76. package/dist/service/index.js.map +1 -0
  77. package/dist/service/keepalive.d.ts +18 -0
  78. package/dist/service/keepalive.js +418 -0
  79. package/dist/service/keepalive.js.map +1 -0
  80. package/dist/service/launchd.d.ts +9 -0
  81. package/dist/service/launchd.js +105 -0
  82. package/dist/service/launchd.js.map +1 -0
  83. package/dist/service/owner.d.ts +4 -0
  84. package/dist/service/owner.js +14 -0
  85. package/dist/service/owner.js.map +1 -0
  86. package/dist/service/portable.d.ts +10 -0
  87. package/dist/service/portable.js +32 -0
  88. package/dist/service/portable.js.map +1 -0
  89. package/dist/service/recovery.d.ts +27 -0
  90. package/dist/service/recovery.js +58 -0
  91. package/dist/service/recovery.js.map +1 -0
  92. package/dist/service/registration.d.ts +82 -0
  93. package/dist/service/registration.js +114 -0
  94. package/dist/service/registration.js.map +1 -0
  95. package/dist/service/systemd-unit.d.ts +7 -0
  96. package/dist/service/systemd-unit.js +43 -0
  97. package/dist/service/systemd-unit.js.map +1 -0
  98. package/dist/service/systemd.d.ts +5 -0
  99. package/dist/service/systemd.js +72 -0
  100. package/dist/service/systemd.js.map +1 -0
  101. package/dist/service/windows-action.d.ts +9 -0
  102. package/dist/service/windows-action.js +55 -0
  103. package/dist/service/windows-action.js.map +1 -0
  104. package/dist/service/windows-task.d.ts +9 -0
  105. package/dist/service/windows-task.js +46 -0
  106. package/dist/service/windows-task.js.map +1 -0
  107. package/dist/service/windows.d.ts +2 -0
  108. package/dist/service/windows.js +65 -0
  109. package/dist/service/windows.js.map +1 -0
  110. package/dist/sqlite.d.ts +4 -0
  111. package/dist/sqlite.js +16 -0
  112. package/dist/sqlite.js.map +1 -0
  113. package/dist/updates/artifacts.d.ts +40 -0
  114. package/dist/updates/artifacts.js +299 -0
  115. package/dist/updates/artifacts.js.map +1 -0
  116. package/dist/updates/bootstrap.d.ts +48 -0
  117. package/dist/updates/bootstrap.js +384 -0
  118. package/dist/updates/bootstrap.js.map +1 -0
  119. package/dist/updates/controller.d.ts +66 -0
  120. package/dist/updates/controller.js +322 -0
  121. package/dist/updates/controller.js.map +1 -0
  122. package/dist/updates/isolated-command-lifetime.d.ts +1 -0
  123. package/dist/updates/isolated-command-lifetime.js +63 -0
  124. package/dist/updates/isolated-command-lifetime.js.map +1 -0
  125. package/dist/updates/store.d.ts +41 -0
  126. package/dist/updates/store.js +80 -0
  127. package/dist/updates/store.js.map +1 -0
  128. package/dist/updates/trust.d.ts +20 -0
  129. package/dist/updates/trust.js +138 -0
  130. package/dist/updates/trust.js.map +1 -0
  131. package/dist/user-ground-host.d.ts +4 -1
  132. package/dist/user-ground-host.js +16 -3
  133. package/dist/user-ground-host.js.map +1 -1
  134. package/dist/watching/host.js +4 -2
  135. package/dist/watching/host.js.map +1 -1
  136. package/dist/workspace-add-staging.js +2 -2
  137. package/dist/workspace-add-staging.js.map +1 -1
  138. package/examinations/fixtures/machine-control-plane.js +57 -0
  139. package/examinations/fixtures/private-wire-authority.js +1065 -0
  140. package/examinations/native-service.mjs +92 -0
  141. package/examinations/network-view.mjs +202 -0
  142. package/examinations/runtime-upgrade.native-test.mjs +198 -0
  143. package/npm-shrinkwrap.json +2097 -218
  144. package/package.json +95 -14
@@ -0,0 +1,138 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { readFileSync, writeFileSync } from 'node:fs';
3
+ import { mkdtemp, readFile, rm } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { createRequire } from 'node:module';
6
+ import { BaseFetcher, Updater } from 'tuf-js';
7
+ import { DownloadHTTPError } from 'tuf-js/dist/error.js';
8
+ import { Metadata, MetadataKind } from '@tufjs/models';
9
+ import { admitManagedRuntimeRelease } from '@amalgm/core/supervision';
10
+ import { packageRoot, runtimeHost } from './store.js';
11
+ export const RELEASE_ORIGIN = 'https://amalgm-desktop-releases.fly.dev';
12
+ const ROLES = ['root', 'targets', 'snapshot', 'timestamp'];
13
+ const digest = (bytes) => createHash('sha256').update(bytes).digest('hex');
14
+ export function runtimeRoot(value, lane) {
15
+ const root = Metadata.fromJSON(MetadataKind.Root, value);
16
+ if (root.signed.unrecognizedFields.amalgm !== `runtime/${lane}` || !root.signed.consistentSnapshot)
17
+ throw new Error('Runtime trust scope differs');
18
+ const used = new Set();
19
+ for (const role of ROLES) {
20
+ const entry = root.signed.roles[role];
21
+ if (!entry || entry.threshold < (role === 'root' ? 2 : 1) || entry.threshold > entry.keyIDs.length)
22
+ throw new Error('Insufficient runtime trust threshold');
23
+ for (const id of entry.keyIDs) {
24
+ const key = root.signed.keys[id];
25
+ if (key?.keyType !== 'ed25519' || key.scheme !== 'ed25519' || used.has(key.keyVal.public))
26
+ throw new Error('Runtime signing roles must be independent');
27
+ used.add(key.keyVal.public);
28
+ }
29
+ }
30
+ root.verifyDelegate('root', root);
31
+ return root;
32
+ }
33
+ class RuntimeFetcher extends BaseFetcher {
34
+ base;
35
+ signal;
36
+ constructor(base, signal) {
37
+ super();
38
+ this.base = base;
39
+ this.signal = signal;
40
+ }
41
+ async fetch(url) {
42
+ const target = new URL(url);
43
+ if (target.origin !== this.base.origin || !target.pathname.startsWith(this.base.pathname))
44
+ throw new Error('Runtime trust request escaped its repository');
45
+ const response = await fetch(target, { signal: this.signal, redirect: 'error', cache: 'no-store' });
46
+ if (!response.ok || !response.body)
47
+ throw new DownloadHTTPError('Runtime trust download failed', response.status);
48
+ return response.body;
49
+ }
50
+ }
51
+ /** Intentionally matches Electron's pinned host adapter: TUF owns verification;
52
+ * SQLite owns the four metadata writes, including partially failed refreshes.
53
+ * Neither consumer resets trust from a disposable filesystem mirror. */
54
+ export class RuntimeTrust {
55
+ store;
56
+ signal;
57
+ repository;
58
+ lane;
59
+ constructor(store, signal, origin = RELEASE_ORIGIN) {
60
+ this.store = store;
61
+ this.signal = signal;
62
+ const lane = store.identity.binding.runtimeLabel;
63
+ if (lane !== 'main' && lane !== 'preview')
64
+ throw new Error('Local development has no managed release authority');
65
+ this.lane = lane;
66
+ this.repository = new URL(`/trust/runtime/${lane}/`, origin);
67
+ if (createRequire(import.meta.url)('tuf-js/package.json').version !== '6.0.0')
68
+ throw new Error('Review the pinned TUF storage interface before upgrading');
69
+ if (store.read('metadata:root') === null) {
70
+ if (['metadata:timestamp', 'metadata:snapshot', 'metadata:targets', 'policy'].some(key => store.read(key) !== null))
71
+ throw new Error('Runtime trust history is incomplete');
72
+ const root = readFileSync(join(packageRoot, 'config/releases/trust/runtime', `${lane}.root.json`), 'utf8');
73
+ runtimeRoot(JSON.parse(root), lane);
74
+ store.write('metadata:root', root);
75
+ }
76
+ runtimeRoot(JSON.parse(store.read('metadata:root')), lane);
77
+ }
78
+ async current(previous) {
79
+ const scratch = await mkdtemp(join(this.store.identity.serviceDir, 'trust-refresh-'));
80
+ try {
81
+ writeFileSync(join(scratch, 'root.json'), this.store.read('metadata:root'));
82
+ const client = new Updater({ metadataDir: scratch, targetDir: scratch,
83
+ metadataBaseUrl: new URL('metadata/', this.repository).href, targetBaseUrl: new URL('targets/', this.repository).href,
84
+ fetcher: new RuntimeFetcher(this.repository, AbortSignal.any([this.signal, AbortSignal.timeout(30_000)])),
85
+ config: { maxDelegations: 1, targetsMaxLength: 4 * 1024 * 1024, fetchRetry: 0 } });
86
+ if (typeof Reflect.get(client, 'loadLocalMetadata') !== 'function' || typeof Reflect.get(client, 'persistMetadata') !== 'function')
87
+ throw new Error('The examined TUF storage interface changed');
88
+ const key = (role) => { if (!ROLES.includes(role))
89
+ throw new Error('Runtime targets do not delegate'); return `metadata:${role}`; };
90
+ Reflect.set(client, 'loadLocalMetadata', (role) => {
91
+ const bytes = this.store.read(key(role));
92
+ if (bytes === null)
93
+ throw new Error('No cached metadata');
94
+ return Buffer.from(bytes);
95
+ });
96
+ Reflect.set(client, 'persistMetadata', (role, bytes) => this.store.write(key(role), bytes.toString('utf8')));
97
+ await client.refresh();
98
+ const root = runtimeRoot(JSON.parse(this.store.read('metadata:root')), this.lane);
99
+ if (Date.parse(root.signed.expires) <= Date.now())
100
+ throw new Error('Runtime root expired');
101
+ const info = await client.getTargetInfo('current.json');
102
+ if (!info || info.custom.amalgm !== `runtime/${this.lane}` || info.length > 4 * 1024 * 1024)
103
+ throw new Error('No authorized runtime target exists');
104
+ const bytes = await readFile(await client.downloadTarget(info));
105
+ return this.admit(JSON.parse(bytes.toString('utf8')), digest(bytes), previous);
106
+ }
107
+ finally {
108
+ await rm(scratch, { recursive: true, force: true });
109
+ }
110
+ }
111
+ /** A historical signature identifies existing code; only current() authorizes
112
+ * a desired release. Expiry cannot retroactively change the installed bytes. */
113
+ async baseline(version) {
114
+ if (!/^[\w.-]+$/.test(version))
115
+ throw new Error('Invalid installed version');
116
+ const fetcher = new RuntimeFetcher(this.repository, AbortSignal.any([this.signal, AbortSignal.timeout(30_000)]));
117
+ const receipt = JSON.parse((await fetcher.downloadBytes(new URL(`releases/shell-${version}/receipt.json`, this.repository).href, 4 * 1024 * 1024)).toString('utf8'));
118
+ const root = runtimeRoot(JSON.parse(this.store.read('metadata:root')), this.lane);
119
+ const authorization = Metadata.fromJSON(MetadataKind.Targets, receipt.authorization);
120
+ root.verifyDelegate('targets', authorization);
121
+ const bytes = Buffer.from(JSON.stringify(receipt.target) + '\n');
122
+ const file = authorization.signed.targets['current.json'];
123
+ if (!file || authorization.signed.delegations || Object.keys(authorization.signed.targets).length !== 1
124
+ || file.custom.amalgm !== `runtime/${this.lane}` || file.length !== bytes.length || file.hashes.sha256 !== digest(bytes))
125
+ throw new Error('Installed receipt does not authenticate its exact target');
126
+ const accepted = this.admit(receipt.target, digest(bytes));
127
+ if (accepted.target.id !== `shell-${version}` || accepted.target.release.units[0].artifact.version !== version)
128
+ throw new Error('Installed receipt identity differs');
129
+ return accepted;
130
+ }
131
+ admit(value, hash, previous) {
132
+ const accepted = admitManagedRuntimeRelease(value, hash, this.store.identity.binding, runtimeHost(), previous);
133
+ if (accepted.target.release.units.length !== 1 || accepted.target.release.units[0].id !== 'shell')
134
+ throw new Error('This controller implements one Shell process unit');
135
+ return accepted;
136
+ }
137
+ }
138
+ //# sourceMappingURL=trust.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trust.js","sourceRoot":"","sources":["../../src/updates/trust.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { type UserGroundPort, type UserHomeDeclaration } from "@amalgm/core/identity";
2
2
  import { type EntityType, type FilesAddPorts, type FilesRegisterPorts, type UserGroundIdentity } from "@amalgm/live";
3
- import Database from "better-sqlite3";
3
+ import type Database from "better-sqlite3";
4
4
  import { type FilesStageObserver } from "./files-observability.js";
5
5
  import { type EntityApplyHostOptions, type EntityApplyStageEvidence } from "./entity-apply-host.js";
6
6
  import { type DurabilityBarrier } from "./durability-barrier.js";
@@ -68,6 +68,8 @@ export interface UserGroundHostOptions {
68
68
  readonly tunnelUrl: string;
69
69
  readonly tunnelToken: string;
70
70
  readonly runtimeVersion: string;
71
+ /** Installation-wide host admission, before any writer touches ground. */
72
+ readonly reserveWritableRoot?: (path: string) => void;
71
73
  readonly declaration?: UserHomeDeclaration;
72
74
  readonly now?: () => Date;
73
75
  /** Product diagnostics consume this; Core's `/healthz` remains liveness-only. */
@@ -256,6 +258,7 @@ export declare class UserGroundHost {
256
258
  * stay one read each. A lone artifact is a set of one. */
257
259
  private downloadContentSet;
258
260
  private downloadContent;
261
+ private reserveGroundRoots;
259
262
  private ensureWatchers;
260
263
  private scheduleRescan;
261
264
  /** A finite cold command may suppress Detect while it owns publication, but
@@ -4,7 +4,7 @@ import { open, readFile } from "node:fs/promises";
4
4
  import { basename, dirname, extname, isAbsolute, join, relative, resolve, sep } from "node:path";
5
5
  import { buildUserHomeManifest, buildUserManifest, liveMachineStateDir, scopedAmalgmDir, shippedUserHomeDeclaration, } from "@amalgm/core/identity";
6
6
  import { CHUNK_BYTES, CONTENT_CONTRACT, INLINE_CARGO_MAX_BYTES, ENTITY_CLOUD_CONTRACT, ENTITY_CLOUD_SCHEMA_VERSION, artifactForRecord, canonicalVersion, checkContentManifest, classifyDirectory, classifyFile, classifyRegisteredRoot, containedRecords, createLocalEntityRecord, createEntityRecordAuthorityPort, convergeUserGround, decodeAcceptedEntityRecord, deriveUploadManifest, createUserGroundEnrollmentPolicy, isRepositoryMetadataEntry, isContentPlacementChangedError, indexRepositoryTerritory, downloadAll as downloadAllArtifacts, encodeEntityRecord, EntityApplyRail, EntityRecordRail, membershipHash, materializedSlotNames, pathIsSuspect, CONTENT_REFS_BATCH_OBJECTS, privateEntityResourceId, repositoryIdentityHash, sameRecords, snapshotFromRecords, stableJson, streamCargo, travelingRecords, travelingRecordUUIDs, upload as uploadArtifact, userGroundRecords, portableFileMode, GRAPH_HEAD_OPERATION_KIND, } from "@amalgm/live";
7
- import Database from "better-sqlite3";
7
+ import { openFileDatabase } from "./sqlite.js";
8
8
  import { atomicCopy, atomicWrite, ensurePrivateDir, ensureUserDir } from "./filesystem.js";
9
9
  import { emitFilesStage, } from "./files-observability.js";
10
10
  import { EntityApplyHost, } from "./entity-apply-host.js";
@@ -171,6 +171,7 @@ export class UserGroundHost {
171
171
  converge: async (identity) => {
172
172
  this.converged = false;
173
173
  this.activeIdentity = normalizedIdentity(identity);
174
+ this.reserveGroundRoots(this.activeIdentity);
174
175
  const local = this.localPort(this.activeIdentity);
175
176
  const result = await convergeUserGround(this.activeIdentity, {
176
177
  sha256Hex,
@@ -312,6 +313,7 @@ export class UserGroundHost {
312
313
  const register = createFilesRegisterPorts({
313
314
  userRoot,
314
315
  deviceId: identity.deviceId,
316
+ reserveWritableRoot: this.options.reserveWritableRoot,
315
317
  onReferenceDeclared: () => this.scheduleRescan(),
316
318
  findKnownWorkspaceId: ({ absolutePath, deviceNumber, inode }) => findKnownEntityId(this.databasePath(identity), absolutePath, deviceNumber, inode),
317
319
  });
@@ -363,6 +365,7 @@ export class UserGroundHost {
363
365
  return current;
364
366
  },
365
367
  materializeWorkspace: async (input) => {
368
+ this.options.reserveWritableRoot?.(join(input.destinationParent, input.workspace.name));
366
369
  const groundWaitStarted = performance.now();
367
370
  this.stage({
368
371
  primitive: "download",
@@ -2060,9 +2063,19 @@ export class UserGroundHost {
2060
2063
  throw new Error(`download set did not seal ${artifact.contentHash}`);
2061
2064
  return local;
2062
2065
  }
2066
+ reserveGroundRoots(identity) {
2067
+ if (!this.options.reserveWritableRoot)
2068
+ return;
2069
+ const userRoot = this.userRoot(identity);
2070
+ this.options.reserveWritableRoot(userRoot);
2071
+ for (const { directory } of materializedWorkspaceBindings(workspaceBindingDir(userRoot, identity.deviceId))) {
2072
+ this.options.reserveWritableRoot(directory);
2073
+ }
2074
+ }
2063
2075
  ensureWatchers(identity, authoritativeBaselineRootIds = []) {
2064
2076
  if (this.closing)
2065
2077
  return this.watchHost.health();
2078
+ this.reserveGroundRoots(identity);
2066
2079
  const started = performance.now();
2067
2080
  const root = this.userRoot(identity);
2068
2081
  const wanted = new Map([[root, {
@@ -3023,7 +3036,7 @@ function convertLegacySnapshotJournal(database, shape, columns) {
3023
3036
  * heap-capped child process. */
3024
3037
  export function initializeDatabase(file) {
3025
3038
  ensurePrivateDir(dirname(file));
3026
- const database = new Database(file);
3039
+ const database = openFileDatabase(file);
3027
3040
  database.pragma("busy_timeout = 5000");
3028
3041
  database.pragma("journal_mode = WAL");
3029
3042
  database.pragma("synchronous = FULL");
@@ -4292,7 +4305,7 @@ function refreshRevealedRootEvidence(file, workspaceId, absolutePath) {
4292
4305
  }
4293
4306
  }
4294
4307
  function assertDatabaseIdentity(file, identity) {
4295
- const database = new Database(file, { readonly: true });
4308
+ const database = openFileDatabase(file, { readonly: true });
4296
4309
  try {
4297
4310
  const found = database.prepare("SELECT user_id AS userId, user_email AS userEmail, device_id AS deviceId FROM ground_identity WHERE singleton = 1").get();
4298
4311
  if (!found || found.userId !== identity.userId || found.userEmail !== identity.userEmail