amalgm 0.1.255 → 0.1.257

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.
@@ -20,7 +20,7 @@ const REAUTHORIZE_MS = 30_000;
20
20
  const REPO_OBJECT_REQUEST_TIMEOUT_MS = 15_000;
21
21
  const COOKIE_PRIVATE_CONTRACT = 'browser-cookie-jar@1';
22
22
  const USER_HOME_PRIVATE_CONTRACT = 'amalgm-user-home@1';
23
- const ENTITY_REGISTRY_PRIVATE_CONTRACT = 'amalgm-entity-registry@1';
23
+ const ENTITY_REGISTRY_PRIVATE_CONTRACT = 'amalgm-entity-registry@2';
24
24
 
25
25
  function createSharedRealtimeTunnel({
26
26
  send,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "amalgm",
3
- "version": "0.1.255",
3
+ "version": "0.1.257",
4
4
  "description": "Amalgm local computer runtime: login, MCP, chat, events, previews, and tunnels.",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,
@@ -11,7 +11,12 @@
11
11
 
12
12
  const crypto = require('crypto');
13
13
 
14
- const ENTITY_CLOUD_CONTRACT = 'amalgm-entity-registry@1';
14
+ // `@1` was an unreleased bootstrap experiment which could project arbitrary
15
+ // pre-cloud local registrations into the first private head. A resource
16
+ // contract names immutable authority semantics, so it is quarantined rather
17
+ // than interpreted or overwritten. `@2` begins with only the verified user
18
+ // home; later user ground enters exclusively through `amalgm register`.
19
+ const ENTITY_CLOUD_CONTRACT = 'amalgm-entity-registry@2';
15
20
  const ENTITY_CLOUD_SCHEMA_VERSION = 1;
16
21
  const BOOTSTRAP_RECONCILIATION_STATE = 'reconciling-bootstrap';
17
22
  const SNAPSHOT_REPLACE_OPERATION = 'entity.snapshot.replace';
@@ -424,14 +429,23 @@ function createEntityCloud({ db, userId, deviceId, content = null, repoStates =
424
429
  }
425
430
  }
426
431
 
427
- function bootstrap(registry) {
432
+ /**
433
+ * Create the first private entity-resource snapshot from the *already
434
+ * verified* bootstrap graph. This deliberately accepts records rather than
435
+ * a registry: login owns the declared user-home graph, but an older device
436
+ * can also hold unrelated local registrations which have never had cloud
437
+ * authority. Publishing those as an implicit local-wins merge would make
438
+ * sign-in a hidden `register` command.
439
+ */
440
+ function bootstrap(records) {
428
441
  const currentIdentity = identity();
429
442
  if (!currentIdentity.enabled) return currentIdentity;
430
443
  const existing = replica();
431
444
  if (existing?.state === 'active') {
432
445
  throw new Error('entity cloud registry already exists locally; cloud lookup must not create it again');
433
446
  }
434
- const snapshot = cloudSnapshot(registry.syncRecords());
447
+ if (!Array.isArray(records)) throw new Error('entity cloud bootstrap requires verified records');
448
+ const snapshot = cloudSnapshot(records);
435
449
  const cutoff = db.prepare('SELECT COALESCE(MAX(sequence), 0) AS sequence FROM entity_cloud_mutations').get().sequence;
436
450
  saveReplica({
437
451
  resourceId: currentIdentity.resourceId,
@@ -471,7 +485,6 @@ function createEntityCloud({ db, userId, deviceId, content = null, repoStates =
471
485
  || !Number.isInteger(Number(headVersion)) || Number(headVersion) < 0) {
472
486
  throw new Error('entity cloud registry authority is invalid');
473
487
  }
474
- const local = cloudSnapshot(registry.syncRecords());
475
488
  const current = replica();
476
489
  const bootstrapping = current?.state === 'bootstrapping';
477
490
  const reconciling = current?.state === BOOTSTRAP_RECONCILIATION_STATE;
@@ -486,7 +499,12 @@ function createEntityCloud({ db, userId, deviceId, content = null, repoStates =
486
499
  // exactly the already-verified declared user home. This is not a general
487
500
  // local-wins merge: user-selected ground remains refused until an active
488
501
  // authoritative snapshot exists.
489
- if (!current && emptyCloudGraph && trustedBootstrap && sameRecords(local.records, trustedBootstrap)) {
502
+ // The first entity resource contains only declared user-home truth. An
503
+ // old installation may also have local, pre-cloud user ground; that is
504
+ // deliberately excluded here and will join only through an explicit
505
+ // later `amalgm register`. The empty cloud resource is therefore repaired
506
+ // from the trusted bootstrap records, never from the whole local registry.
507
+ if (emptyCloudGraph && trustedBootstrap && (!current || bootstrapping)) {
490
508
  const next = {
491
509
  resourceId,
492
510
  deviceId: currentIdentity.deviceId,
@@ -515,8 +533,8 @@ function createEntityCloud({ db, userId, deviceId, content = null, repoStates =
515
533
  }
516
534
 
517
535
  if (reconciling) {
518
- if (!trustedBootstrap || !sameRecords(local.records, trustedBootstrap)) {
519
- throw new Error('entity bootstrap reconciliation no longer matches the verified user-home graph');
536
+ if (!trustedBootstrap) {
537
+ throw new Error('entity bootstrap reconciliation requires the verified user-home graph');
520
538
  }
521
539
  if (current.resourceId !== resourceId || Number(authorityEpoch) !== current.authorityEpoch
522
540
  || Number(headVersion) < current.headVersion || !recordsAreSubset(snapshot.records, trustedBootstrap)) {
@@ -547,13 +565,17 @@ function createEntityCloud({ db, userId, deviceId, content = null, repoStates =
547
565
  reconciling: !complete,
548
566
  };
549
567
  }
550
- if (bootstrapping && !sameRecord(local, snapshot)) {
551
- throw new Error('first cloud entity snapshot does not match the locally bootstrapped identity graph');
568
+ if (bootstrapping) {
569
+ const expected = trustedBootstrap ?? cloudSnapshot(registry.syncRecords()).records;
570
+ if (!sameRecords(expected, snapshot.records)) {
571
+ throw new Error('first cloud entity snapshot does not match the locally bootstrapped identity graph');
572
+ }
552
573
  }
553
574
  if (!bootstrapping) {
554
575
  const cloudByUuid = new Map(snapshot.records.map((record) => [record.uuid, record]));
555
- for (const record of local.records) {
556
- if (!sameRecord(record, cloudByUuid.get(record.uuid))) {
576
+ for (const local of registry.syncRecords()) {
577
+ const remote = cloudByUuid.get(local.uuid);
578
+ if (remote && !sameRecord(cloudRecord(local), remote)) {
557
579
  throw new Error('cloud entity graph conflicts with locally bound ground; resolve the local mutation before importing a new cloud head');
558
580
  }
559
581
  }
@@ -381,10 +381,10 @@ function createRegistrationService({
381
381
  };
382
382
  }
383
383
 
384
- /** The only local graph allowed to repair an old empty entity resource is
385
- * the exact home that the user-home handshake has already verified. Any
386
- * extra local entity would be user-selected ground and must wait for an
387
- * active entity-cloud authority instead. */
384
+ /** The only local graph eligible to repair an old empty entity resource is
385
+ * the exact home that the user-home handshake has already verified. Extra
386
+ * local entities are deliberately excluded rather than treated as a login
387
+ * merge: they become cloud-backed only through a later explicit register. */
388
388
  async function exactUserHomeCloudRecords() {
389
389
  const home = await bootstrapUserHome();
390
390
  const expected = new Map();
@@ -395,12 +395,9 @@ function createRegistrationService({
395
395
  // containers, with the registry's sync representation.
396
396
  expected.set(entry.record.uuid, boundary.current.registry.syncRecord(entry.record.uuid));
397
397
  }
398
- const records = boundary.current.registry.syncRecords();
399
- if (records.length !== expected.size) {
400
- throw new Error('local entity graph contains ground outside the declared user home');
401
- }
398
+ const records = [...expected.values()];
402
399
  for (const record of records) {
403
- const declared = expected.get(record.uuid);
400
+ const declared = boundary.current.registry.syncRecord(record.uuid);
404
401
  if (!declared || ![
405
402
  'uuid', 'type', 'parentUUID', 'name', 'status', 'version', 'payloadVersion',
406
403
  ].every((field) => record[field] === declared[field])) {
@@ -135,9 +135,11 @@ async function handleEntityRoutes(ctx) {
135
135
  return true;
136
136
  }
137
137
  // A missing entity resource is first created from the same declared
138
- // user-home graph that login owns. This is not an alternate registrar.
139
- await service.bootstrapUserHome();
140
- ctx.sendJson(200, service.entityCloud.bootstrap(service.registration.registry));
138
+ // user-home graph that login owns. This is not an alternate registrar:
139
+ // pre-cloud user registrations are deliberately excluded until their
140
+ // caller explicitly registers them after the bootstrap head is active.
141
+ const bootstrapRecords = await service.exactUserHomeCloudRecords();
142
+ ctx.sendJson(200, service.entityCloud.bootstrap(bootstrapRecords));
141
143
  return true;
142
144
  }
143
145
 
@@ -147,7 +149,8 @@ async function handleEntityRoutes(ctx) {
147
149
  const service = registrationService();
148
150
  try {
149
151
  const existing = service.entityCloud.replica();
150
- let requiresBootstrapProof = existing?.state === 'reconciling-bootstrap';
152
+ let requiresBootstrapProof = existing?.state === 'bootstrapping'
153
+ || existing?.state === 'reconciling-bootstrap';
151
154
  try {
152
155
  const snapshot = JSON.parse(Buffer.from(String(body?.snapshotBase64 || ''), 'base64').toString('utf8'));
153
156
  requiresBootstrapProof ||= Number(body?.headVersion) === 0
@@ -25,7 +25,7 @@ test('the entity cloud snapshot establishes identity, then each later entity com
25
25
  assert.equal(db.prepare('SELECT COUNT(*) AS count FROM entity_cloud_mutations').get().count, 0,
26
26
  'initial bootstrap identity is carried by the first immutable snapshot, not an unscoped tail');
27
27
 
28
- const bootstrap = cloud.bootstrap(registry);
28
+ const bootstrap = cloud.bootstrap(registry.syncRecords());
29
29
  const snapshot = parseSnapshot(Buffer.from(bootstrap.snapshotBase64, 'base64'));
30
30
  assert.deepEqual(snapshot.records.map((record) => record.uuid), [root.uuid]);
31
31
  cloud.install({
@@ -64,7 +64,7 @@ test('a signed-in registration is refused until the cloud entity resource is act
64
64
  const registry = createRegistry({ db, ephemeral: true, emit: () => {}, journal: cloud.journal });
65
65
  assert.throws(() => cloud.assertRegistrationReady(), /entity cloud registry is not ready/);
66
66
  registry.enrollRoot({ type: 'workspace', name: 'bootstrap-only' });
67
- const bootstrap = cloud.bootstrap(registry);
67
+ const bootstrap = cloud.bootstrap(registry.syncRecords());
68
68
  cloud.install({
69
69
  registry,
70
70
  resourceId: bootstrap.resourceId,
@@ -81,7 +81,7 @@ test('a large registration replaces the complete graph in one cloud operation an
81
81
  const cloud = createEntityCloud({ db, userId: 'user-1', deviceId: 'device-1' });
82
82
  const registry = createRegistry({ db, ephemeral: true, emit: () => {}, journal: cloud.journal });
83
83
  const root = registry.enrollRoot({ type: 'workspace', name: 'large-ground' });
84
- const bootstrap = cloud.bootstrap(registry);
84
+ const bootstrap = cloud.bootstrap(registry.syncRecords());
85
85
  cloud.install({
86
86
  registry,
87
87
  resourceId: bootstrap.resourceId,
@@ -126,7 +126,7 @@ test('a large registration replaces the complete graph in one cloud operation an
126
126
  recipientDb.close();
127
127
  });
128
128
 
129
- test('only an exact verified bootstrap graph can repair a legacy empty entity resource', () => {
129
+ test('a legacy empty entity resource repairs only the verified home and excludes unrelated local ground', () => {
130
130
  const db = new Database(':memory:');
131
131
  const cloud = createEntityCloud({ db, userId: 'user-1', deviceId: 'device-1' });
132
132
  const registry = createRegistry({ db, ephemeral: true, emit: () => {}, journal: cloud.journal });
@@ -172,21 +172,28 @@ test('only an exact verified bootstrap graph can repair a legacy empty entity re
172
172
  assert.equal(cloud.replica().state, 'active');
173
173
  assert.doesNotThrow(() => cloud.assertRegistrationReady());
174
174
 
175
- const unsafeDb = new Database(':memory:');
176
- const unsafeCloud = createEntityCloud({ db: unsafeDb, userId: 'user-1', deviceId: 'unsafe-device' });
177
- const unsafeRegistry = createRegistry({ db: unsafeDb, ephemeral: true, emit: () => {}, journal: unsafeCloud.journal });
178
- const safeRoot = unsafeRegistry.enrollRoot({ type: 'workspace', name: 'agents' });
179
- unsafeRegistry.create({ type: 'file.text', parentUUID: safeRoot.uuid, name: 'not-a-default.md', payloadVersion: 'a'.repeat(64) });
180
- assert.throws(() => unsafeCloud.install({
181
- registry: unsafeRegistry,
175
+ const legacyDb = new Database(':memory:');
176
+ const legacyCloud = createEntityCloud({ db: legacyDb, userId: 'user-1', deviceId: 'legacy-device' });
177
+ const legacyRegistry = createRegistry({ db: legacyDb, ephemeral: true, emit: () => {}, journal: legacyCloud.journal });
178
+ const safeRoot = legacyRegistry.enrollRoot({ type: 'workspace', name: 'agents' });
179
+ const legacyRepo = legacyRegistry.create({ type: 'repo.git', parentUUID: safeRoot.uuid, name: 'old-local-repo' });
180
+ const verifiedHome = legacyRegistry.syncRecords().filter((record) => record.uuid === safeRoot.uuid);
181
+ const repaired = legacyCloud.install({
182
+ registry: legacyRegistry,
182
183
  resourceId,
183
184
  authorityEpoch: 1,
184
185
  headVersion: 0,
185
186
  snapshotBase64: empty.toString('base64'),
186
187
  snapshotChecksum: require('crypto').createHash('sha256').update(empty).digest('hex'),
187
- bootstrapRecords: unsafeRegistry.syncRecords().filter((record) => record.uuid === safeRoot.uuid),
188
- }), /conflicts with locally bound ground/,
189
- 'an empty cloud resource never authorizes arbitrary local ground');
188
+ bootstrapRecords: verifiedHome,
189
+ });
190
+ assert.equal(repaired.reconciling, true);
191
+ const repair = legacyCloud.claim();
192
+ assert.equal(repair.length, 1);
193
+ assert.equal(repair[0].operation.record.uuid, safeRoot.uuid,
194
+ 'only the verified home node is queued into the automatic cloud graph');
195
+ assert.equal(legacyRegistry.syncRecord(legacyRepo.uuid).uuid, legacyRepo.uuid,
196
+ 'unrelated local ground remains local until its caller explicitly registers it');
190
197
  });
191
198
 
192
199
  test('a compacted remote snapshot enters the cloud catalog, not locally watched identity, and add claims its immutable content', () => {
@@ -270,7 +277,7 @@ test('add accepts only a catalog installed by the active compacted snapshot, nev
270
277
  envelope: {
271
278
  resourceId,
272
279
  mutationId: 'ent_remote_change',
273
- contract: 'amalgm-entity-registry@1',
280
+ contract: 'amalgm-entity-registry@2',
274
281
  schemaVersion: 1,
275
282
  operationKind: 'entity.upsert',
276
283
  operation: { kind: 'entity.upsert', record: changed },
@@ -32,7 +32,7 @@ test('file bytes are captured as bounded immutable chunks, then acknowledged bef
32
32
  const cloud = createEntityCloud({ db, userId: 'user-1', deviceId: 'device-1', content });
33
33
  const registry = createRegistry({ db, ephemeral: true, emit: () => {}, journal: cloud.journal });
34
34
  const root = registry.enrollRoot({ type: 'workspace', name: 'ground' });
35
- const bootstrap = cloud.bootstrap(registry);
35
+ const bootstrap = cloud.bootstrap(registry.syncRecords());
36
36
  cloud.install({
37
37
  registry,
38
38
  resourceId: bootstrap.resourceId,
@@ -189,7 +189,7 @@ test('an added ordinary workspace stays live: a later cloud file head replaces l
189
189
  envelope: {
190
190
  resourceId: sourceCloud.identity().resourceId,
191
191
  mutationId: 'ent_remote_file_change',
192
- contract: 'amalgm-entity-registry@1',
192
+ contract: 'amalgm-entity-registry@2',
193
193
  schemaVersion: 1,
194
194
  operationKind: 'entity.upsert',
195
195
  operation: { kind: 'entity.upsert', record: remoteRecord },
@@ -296,7 +296,7 @@ test('add preserves a symlink UUID and later retargets the same literal link wit
296
296
  const ingested = target.ingestCloudMutation({
297
297
  envelope: {
298
298
  resourceId: sourceCloud.identity().resourceId, mutationId: 'ent_remote_link_change',
299
- contract: 'amalgm-entity-registry@1', schemaVersion: 1,
299
+ contract: 'amalgm-entity-registry@2', schemaVersion: 1,
300
300
  operationKind: 'entity.upsert', operation: { kind: 'entity.upsert', record: sourceRegistry.syncRecord(changed.uuid) },
301
301
  },
302
302
  version: 42, committedAt: '2026-08-05T00:00:00.000Z',
@@ -344,7 +344,7 @@ test('add recreates an actual Git repository from verified Repocard cargo, then
344
344
  cloudUserId: 'user-1', cloudDeviceId: 'target-device',
345
345
  });
346
346
  try {
347
- const bootstrap = source.entityCloud.bootstrap(source.registration.registry);
347
+ const bootstrap = source.entityCloud.bootstrap(source.registration.registry.syncRecords());
348
348
  source.entityCloud.install({
349
349
  registry: source.registration.registry, resourceId: bootstrap.resourceId,
350
350
  authorityEpoch: 1, headVersion: 0, snapshotBase64: bootstrap.snapshotBase64,
@@ -420,7 +420,7 @@ test('add recreates an actual Git repository from verified Repocard cargo, then
420
420
  const ingested = target.ingestCloudMutation({
421
421
  envelope: {
422
422
  resourceId: bootstrap.resourceId, mutationId: 'ent_remote_repo_change',
423
- contract: 'amalgm-entity-registry@1', schemaVersion: 1,
423
+ contract: 'amalgm-entity-registry@2', schemaVersion: 1,
424
424
  operationKind: 'entity.upsert', operation: { kind: 'entity.upsert', record: remoteRecord },
425
425
  },
426
426
  version: 2, committedAt: '2026-08-05T00:00:00.000Z',
@@ -233,6 +233,40 @@ test('user-home bootstrap creates only declared roots and registers them through
233
233
  );
234
234
  });
235
235
 
236
+ test('entity-cloud bootstrap publishes only the verified user home when an old local repo exists', async () => {
237
+ const service = createRegistrationService({
238
+ dir: path.join(tempRoot, 'state-user-home-cloud-boundary'),
239
+ homeDir: path.join(tempRoot, 'user-home-cloud-boundary'),
240
+ cloudUserId: 'user-home-cloud-boundary',
241
+ cloudDeviceId: 'computer-user-home-cloud-boundary',
242
+ });
243
+ services.push(service);
244
+ const home = await service.bootstrapUserHome();
245
+ const workspaces = home.roots.find((entry) => entry.key === 'workspaces').record;
246
+ const oldRepo = service.registration.registry.create({
247
+ type: 'repo.git', parentUUID: workspaces.uuid, name: 'old-local-repo', payloadVersion: null,
248
+ });
249
+
250
+ const records = await service.exactUserHomeCloudRecords();
251
+ assert.equal(records.length, 9);
252
+ assert.equal(records.some((record) => record.uuid === oldRepo.uuid), false,
253
+ 'login never turns existing user-selected ground into an implicit cloud registration');
254
+
255
+ const bootstrap = service.entityCloud.bootstrap(records);
256
+ const snapshot = JSON.parse(Buffer.from(bootstrap.snapshotBase64, 'base64').toString('utf8'));
257
+ assert.equal(snapshot.records.length, 9);
258
+ service.entityCloud.install({
259
+ registry: service.registration.registry,
260
+ resourceId: bootstrap.resourceId,
261
+ authorityEpoch: 1,
262
+ headVersion: 1,
263
+ snapshotBase64: bootstrap.snapshotBase64,
264
+ snapshotChecksum: bootstrap.snapshotChecksum,
265
+ bootstrapRecords: records,
266
+ });
267
+ assert.doesNotThrow(() => service.entityCloud.assertRegistrationReady());
268
+ });
269
+
236
270
  test('a fresh machine binds existing user-home UUIDs before it starts watching', async () => {
237
271
  const first = createRegistrationService({
238
272
  dir: path.join(tempRoot, 'state-user-home-origin'),
@@ -326,7 +360,7 @@ test('a signed-in register call writes every entity delivery intent in identity.
326
360
  cloudDeviceId: 'computer-cloud-register',
327
361
  });
328
362
  const home = await service.bootstrapUserHome();
329
- const bootstrap = service.entityCloud.bootstrap(service.registration.registry);
363
+ const bootstrap = service.entityCloud.bootstrap(service.registration.registry.syncRecords());
330
364
  service.entityCloud.install({
331
365
  registry: service.registration.registry,
332
366
  resourceId: bootstrap.resourceId,
@@ -366,7 +400,7 @@ test('a large signed-in register call publishes one complete cloud graph before
366
400
  cloudUserId: 'user-cloud-register-large',
367
401
  cloudDeviceId: 'computer-cloud-register-large',
368
402
  });
369
- const bootstrap = service.entityCloud.bootstrap(service.registration.registry);
403
+ const bootstrap = service.entityCloud.bootstrap(service.registration.registry.syncRecords());
370
404
  service.entityCloud.install({
371
405
  registry: service.registration.registry,
372
406
  resourceId: bootstrap.resourceId,