@dxos/echo-db 2.33.2 → 2.33.3-dev.0cff904f

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 (51) hide show
  1. package/dist/src/echo.js +2 -2
  2. package/dist/src/echo.js.map +1 -1
  3. package/dist/src/echo.test.js +10 -10
  4. package/dist/src/echo.test.js.map +1 -1
  5. package/dist/src/halo/halo.d.ts +1 -0
  6. package/dist/src/halo/halo.d.ts.map +1 -1
  7. package/dist/src/halo/halo.js +10 -1
  8. package/dist/src/halo/halo.js.map +1 -1
  9. package/dist/src/halo/identity-manager.d.ts +0 -1
  10. package/dist/src/halo/identity-manager.d.ts.map +1 -1
  11. package/dist/src/halo/identity-manager.js +11 -11
  12. package/dist/src/halo/identity-manager.js.map +1 -1
  13. package/dist/src/halo/identity.d.ts +18 -13
  14. package/dist/src/halo/identity.d.ts.map +1 -1
  15. package/dist/src/halo/identity.js +24 -27
  16. package/dist/src/halo/identity.js.map +1 -1
  17. package/dist/src/invitations/offline-invitation-claimer.d.ts +2 -2
  18. package/dist/src/invitations/offline-invitation-claimer.d.ts.map +1 -1
  19. package/dist/src/invitations/offline-invitation-claimer.js +2 -4
  20. package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
  21. package/dist/src/parties/party-factory.d.ts +2 -2
  22. package/dist/src/parties/party-factory.d.ts.map +1 -1
  23. package/dist/src/parties/party-factory.js +11 -20
  24. package/dist/src/parties/party-factory.js.map +1 -1
  25. package/dist/src/parties/party-manager.d.ts +2 -2
  26. package/dist/src/parties/party-manager.d.ts.map +1 -1
  27. package/dist/src/parties/party-manager.js +2 -1
  28. package/dist/src/parties/party-manager.js.map +1 -1
  29. package/dist/src/parties/party-manager.test.js +7 -22
  30. package/dist/src/parties/party-manager.test.js.map +1 -1
  31. package/dist/src/protocol/credentials-signer.d.ts +4 -4
  32. package/dist/src/protocol/credentials-signer.d.ts.map +1 -1
  33. package/dist/src/protocol/credentials-signer.js +8 -8
  34. package/dist/src/protocol/credentials-signer.js.map +1 -1
  35. package/dist/src/protocol/identity-credentials.d.ts +21 -0
  36. package/dist/src/protocol/identity-credentials.d.ts.map +1 -0
  37. package/dist/src/protocol/identity-credentials.js +35 -0
  38. package/dist/src/protocol/identity-credentials.js.map +1 -0
  39. package/dist/tsconfig.tsbuildinfo +1 -1
  40. package/package.json +17 -17
  41. package/src/echo.test.ts +10 -10
  42. package/src/echo.ts +1 -1
  43. package/src/halo/halo.ts +8 -1
  44. package/src/halo/identity-manager.ts +13 -14
  45. package/src/halo/identity.ts +39 -41
  46. package/src/invitations/offline-invitation-claimer.ts +6 -7
  47. package/src/parties/party-factory.ts +13 -20
  48. package/src/parties/party-manager.test.ts +8 -42
  49. package/src/parties/party-manager.ts +4 -3
  50. package/src/protocol/credentials-signer.ts +9 -9
  51. package/src/protocol/identity-credentials.ts +56 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/echo-db",
3
- "version": "2.33.2",
3
+ "version": "2.33.3-dev.0cff904f",
4
4
  "description": "ECHO database.",
5
5
  "license": "MIT",
6
6
  "main": "dist/src/index.js",
@@ -10,22 +10,22 @@
10
10
  "src"
11
11
  ],
12
12
  "dependencies": {
13
- "@dxos/async": "2.33.2",
14
- "@dxos/codec-protobuf": "2.33.2",
15
- "@dxos/credentials": "2.33.2",
16
- "@dxos/crypto": "2.33.2",
17
- "@dxos/debug": "2.33.2",
18
- "@dxos/echo-protocol": "2.33.2",
19
- "@dxos/feed-store": "2.33.2",
20
- "@dxos/mesh-protocol": "2.33.2",
21
- "@dxos/model-factory": "2.33.2",
22
- "@dxos/network-manager": "2.33.2",
23
- "@dxos/object-model": "2.33.2",
24
- "@dxos/protocol-plugin-presence": "2.33.2",
25
- "@dxos/protocol-plugin-replicator": "2.33.2",
26
- "@dxos/protocols": "2.33.2",
27
- "@dxos/random-access-multi-storage": "2.33.2",
28
- "@dxos/util": "2.33.2",
13
+ "@dxos/async": "2.33.3-dev.0cff904f",
14
+ "@dxos/codec-protobuf": "2.33.3-dev.0cff904f",
15
+ "@dxos/credentials": "2.33.3-dev.0cff904f",
16
+ "@dxos/crypto": "2.33.3-dev.0cff904f",
17
+ "@dxos/debug": "2.33.3-dev.0cff904f",
18
+ "@dxos/echo-protocol": "2.33.3-dev.0cff904f",
19
+ "@dxos/feed-store": "2.33.3-dev.0cff904f",
20
+ "@dxos/mesh-protocol": "2.33.3-dev.0cff904f",
21
+ "@dxos/model-factory": "2.33.3-dev.0cff904f",
22
+ "@dxos/network-manager": "2.33.3-dev.0cff904f",
23
+ "@dxos/object-model": "2.33.3-dev.0cff904f",
24
+ "@dxos/protocol-plugin-presence": "2.33.3-dev.0cff904f",
25
+ "@dxos/protocol-plugin-replicator": "2.33.3-dev.0cff904f",
26
+ "@dxos/protocols": "2.33.3-dev.0cff904f",
27
+ "@dxos/random-access-multi-storage": "2.33.3-dev.0cff904f",
28
+ "@dxos/util": "2.33.3-dev.0cff904f",
29
29
  "base-x": "~3.0.9",
30
30
  "buffer-json-encoding": "^1.0.2",
31
31
  "debug": "^4.3.3",
package/src/echo.test.ts CHANGED
@@ -264,8 +264,8 @@ describe('ECHO', () => {
264
264
  const a = await setup({ createProfile: true });
265
265
  const b = await setup();
266
266
 
267
- expect(a.halo.isInitialized).toEqual(true);
268
- expect(b.halo.isInitialized).toEqual(false);
267
+ expect(!!a.halo.identity).toEqual(true);
268
+ expect(!!b.halo.identity).toEqual(false);
269
269
 
270
270
  expect(a.queryParties().value.length).toBe(0);
271
271
  await a.createParty();
@@ -430,11 +430,11 @@ describe('ECHO', () => {
430
430
  const b1 = await setup({ createProfile: true });
431
431
  const b2 = await setup();
432
432
 
433
- expect(a1.halo.isInitialized).toBeTruthy();
434
- expect(a2.halo.isInitialized).toBeFalsy();
433
+ expect(a1.halo.identity).toBeTruthy();
434
+ expect(a2.halo.identity).toBeFalsy();
435
435
 
436
- expect(b1.halo.isInitialized).toBeTruthy();
437
- expect(b2.halo.isInitialized).toBeFalsy();
436
+ expect(b1.halo.identity).toBeTruthy();
437
+ expect(b2.halo.identity).toBeFalsy();
438
438
 
439
439
  await Promise.all([
440
440
  (async () => {
@@ -529,13 +529,13 @@ describe('ECHO', () => {
529
529
 
530
530
  const b = await setup();
531
531
 
532
- expect(a.halo.isInitialized).toBeTruthy();
533
- expect(b.halo.isInitialized).toBeFalsy();
532
+ expect(a.halo.identity).toBeTruthy();
533
+ expect(b.halo.identity).toBeFalsy();
534
534
 
535
535
  // And then redeem it on nodeB.
536
536
  await b.halo.recover(seedPhrase);
537
- expect(a.halo.isInitialized).toBeTruthy();
538
- expect(b.halo.isInitialized).toBeTruthy();
537
+ expect(a.halo.identity).toBeTruthy();
538
+ expect(b.halo.identity).toBeTruthy();
539
539
 
540
540
  // Now create a Party on A and make sure it gets opened on both A and B.
541
541
  expect(a.queryParties().value.length).toBe(0);
package/src/echo.ts CHANGED
@@ -376,7 +376,7 @@ export class ECHO {
376
376
  assert(this._partyManager.isOpen, new InvalidStateError());
377
377
 
378
378
  const actualSecretProvider =
379
- secretProvider ?? OfflineInvitationClaimer.createSecretProvider(this.halo.identity ?? raise(new IdentityNotInitializedError()));
379
+ secretProvider ?? OfflineInvitationClaimer.createSecretProvider(this.halo.identity?.createCredentialsSigner() ?? raise(new IdentityNotInitializedError()));
380
380
 
381
381
  return this._partyManager.joinParty(invitationDescriptor, actualSecretProvider);
382
382
  }
package/src/halo/halo.ts CHANGED
@@ -46,6 +46,8 @@ export class HALO {
46
46
  private readonly _keyring: Keyring;
47
47
  private readonly _identityManager: IdentityManager;
48
48
 
49
+ private _isOpen = false;
50
+
49
51
  constructor ({
50
52
  keyring,
51
53
  networkManager,
@@ -85,7 +87,7 @@ export class HALO {
85
87
  * Whether the current identity manager has been initialized.
86
88
  */
87
89
  get isInitialized (): boolean {
88
- return this._identityManager.initialized;
90
+ return this._isOpen;
89
91
  }
90
92
 
91
93
  /**
@@ -129,6 +131,7 @@ export class HALO {
129
131
  *
130
132
  * Loads the saved identity from disk. Is called by client.
131
133
  */
134
+ @synchronized
132
135
  async open (onProgressCallback?: ((progress: OpenProgress) => void) | undefined) {
133
136
  // TODO(burdon): Replace with events.
134
137
  onProgressCallback?.({ haloOpened: false });
@@ -137,12 +140,16 @@ export class HALO {
137
140
  await this._identityManager.loadFromStorage();
138
141
 
139
142
  onProgressCallback?.({ haloOpened: true });
143
+
144
+ this._isOpen = true;
140
145
  }
141
146
 
142
147
  /**
143
148
  * Closes HALO. Automatically called when client is destroyed.
144
149
  */
150
+ @synchronized
145
151
  async close () {
152
+ this._isOpen = false;
146
153
  await this._identityManager.close();
147
154
  }
148
155
 
@@ -7,6 +7,7 @@ import debug from 'debug';
7
7
 
8
8
  import { Event, synchronized, waitForCondition } from '@dxos/async';
9
9
  import { Filter, KeyRecord, Keyring, KeyType, SecretProvider } from '@dxos/credentials';
10
+ import { failUndefined } from '@dxos/debug';
10
11
 
11
12
  import { InvitationDescriptor } from '../invitations';
12
13
  import { MetadataStore } from '../pipeline';
@@ -34,25 +35,23 @@ export class IdentityManager {
34
35
  return this._identity;
35
36
  }
36
37
 
37
- get initialized (): boolean {
38
- return this._identity !== undefined &&
39
- !!this._identity.halo &&
40
- this._identity.halo.isOpen &&
41
- !!this._identity.halo!.memberKeys.length &&
42
- !!this._identity.halo!.identityGenesis &&
43
- !!this._identity.deviceKeyChain;
44
- }
45
-
46
38
  private async _initialize (halo: HaloParty) {
47
39
  assert(halo.isOpen, 'HALO must be open.');
48
40
 
49
- this._identity = new Identity(this._keyring, halo);
50
-
51
- // Wait for the minimum set of keys and messages we need for proper function.
52
- await waitForCondition(() => this.initialized);
41
+ // Wait for the minimum set of keys and messages we need for proper function:
42
+ //
43
+ // - KeyAdmit message for the current device so we can build the device KeyChain.
44
+ // - Identity genesis so it can be copied into newly joined parties.
45
+ //
46
+ const deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE })) ?? failUndefined();
47
+ await waitForCondition(() =>
48
+ halo.processor.isMemberKey(deviceKey.publicKey) &&
49
+ halo.identityGenesis
50
+ );
53
51
 
54
- log('HALO initialized.');
52
+ this._identity = new Identity(this._keyring, halo);
55
53
  this.ready.emit();
54
+ log('HALO initialized.');
56
55
  }
57
56
 
58
57
  async close () {
@@ -2,13 +2,14 @@
2
2
  // Copyright 2021 DXOS.org
3
3
  //
4
4
 
5
+ import assert from 'assert';
5
6
  import debug from 'debug';
6
7
 
7
- import { Filter, KeyChain, KeyRecord, Keyring, KeyType, Signer } from '@dxos/credentials';
8
- import { raise } from '@dxos/debug';
8
+ import { Filter, KeyChain, KeyRecord, Keyring, KeyType, SignedMessage, Signer } from '@dxos/credentials';
9
+ import { failUndefined } from '@dxos/debug';
9
10
 
10
- import { IdentityNotInitializedError } from '../errors';
11
11
  import { CredentialsSigner } from '../protocol/credentials-signer';
12
+ import { IdentityCredentials } from '../protocol/identity-credentials';
12
13
  import { ContactManager } from './contact-manager';
13
14
  import { HaloParty } from './halo-party';
14
15
  import { Preferences } from './preferences';
@@ -20,10 +21,10 @@ const log = debug('dxos:echo-db:identity');
20
21
  *
21
22
  * Acts as a read-only view into IdentityManager.
22
23
  */
23
- export class Identity {
24
- private _identityKey?: KeyRecord;
25
- private _deviceKey?: KeyRecord;
26
- private _deviceKeyChain?: KeyChain;
24
+ export class Identity implements IdentityCredentials {
25
+ private readonly _identityKey: KeyRecord;
26
+ private readonly _deviceKey: KeyRecord;
27
+ private readonly _deviceKeyChain: KeyChain;
27
28
 
28
29
  /**
29
30
  * @param _halo HALO party. Must be open.
@@ -31,54 +32,55 @@ export class Identity {
31
32
  constructor (
32
33
  private readonly _keyring: Keyring,
33
34
  private readonly _halo: HaloParty
34
- ) {}
35
+ ) {
36
+ this._identityKey = this._keyring.findKey(Filter.matches({ type: KeyType.IDENTITY, own: true, trusted: true })) ?? failUndefined();
37
+ this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE })) ?? failUndefined();
38
+ this._deviceKeyChain = getDeviceKeyChainFromHalo(this._halo, this.deviceKey);
39
+ assert(this._halo.identityGenesis);
40
+ }
35
41
 
36
42
  get signer (): Signer {
37
43
  return this._keyring;
38
44
  }
39
45
 
40
- get identityKey (): KeyRecord | undefined {
41
- if (!this._identityKey) {
42
- this._identityKey = this._keyring.findKey(Filter.matches({ type: KeyType.IDENTITY, own: true, trusted: true }));
43
- }
46
+ get keyring (): Keyring {
47
+ return this._keyring;
48
+ }
44
49
 
50
+ get identityKey (): KeyRecord {
45
51
  return this._identityKey;
46
52
  }
47
53
 
48
- get deviceKey (): KeyRecord | undefined {
49
- if (!this._deviceKey) {
50
- this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE }));
51
- }
52
-
54
+ get deviceKey (): KeyRecord {
53
55
  return this._deviceKey;
54
56
  }
55
57
 
56
- get deviceKeyChain (): KeyChain | undefined {
57
- if (!this._deviceKeyChain) {
58
- this._deviceKeyChain = this.deviceKey && this._halo ? getDeviceKeyChainFromHalo(this._halo, this.deviceKey) : undefined;
59
- }
60
-
58
+ get deviceKeyChain (): KeyChain {
61
59
  return this._deviceKeyChain;
62
60
  }
63
61
 
64
- get preferences (): Preferences | undefined {
65
- return this._halo?.preferences;
62
+ get displayName (): string | undefined {
63
+ return this.identityInfo?.signed.payload.displayName;
66
64
  }
67
65
 
68
- get contacts (): ContactManager | undefined {
69
- return this._halo?.contacts;
66
+ /**
67
+ * Contains profile username.
68
+ * Can be missing if the username wasn't provided when profile was created.
69
+ */
70
+ get identityInfo (): SignedMessage | undefined {
71
+ return this._halo.identityInfo;
70
72
  }
71
73
 
72
- get displayName (): string | undefined {
73
- return this.identityInfo?.signed.payload.displayName;
74
+ get identityGenesis (): SignedMessage {
75
+ return this._halo.identityGenesis ?? failUndefined();
74
76
  }
75
77
 
76
- get identityInfo () {
77
- return this._halo?.identityInfo;
78
+ get preferences (): Preferences {
79
+ return this._halo.preferences;
78
80
  }
79
81
 
80
- get identityGenesis () {
81
- return this._halo?.identityGenesis;
82
+ get contacts (): ContactManager {
83
+ return this._halo.contacts;
82
84
  }
83
85
 
84
86
  /**
@@ -88,16 +90,12 @@ export class Identity {
88
90
  return this._halo;
89
91
  }
90
92
 
91
- get keyring () {
92
- return this._keyring;
93
- }
94
-
95
93
  createCredentialsSigner (): CredentialsSigner {
96
94
  return new CredentialsSigner(
97
95
  this._keyring,
98
- () => this.identityKey ?? raise(new IdentityNotInitializedError()),
99
- () => this.deviceKey ?? raise(new IdentityNotInitializedError()),
100
- () => this.deviceKeyChain ?? this.deviceKey ?? raise(new IdentityNotInitializedError())
96
+ this.identityKey,
97
+ this.deviceKey,
98
+ this.deviceKeyChain
101
99
  );
102
100
  }
103
101
  }
@@ -112,7 +110,7 @@ function getDeviceKeyChainFromHalo (halo: HaloParty, deviceKey: KeyRecord) {
112
110
  halo.feedKeys
113
111
  );
114
112
  } catch (err: any) {
115
- log('Unable to locate device KeyChain:', err); // TODO(burdon): ???
116
- return undefined;
113
+ log('Unable to locate device KeyChain:', err);
114
+ throw err;
117
115
  }
118
116
  }
@@ -21,11 +21,10 @@ import {
21
21
  codec
22
22
  } from '@dxos/credentials';
23
23
  import { keyToBuffer, keyToString, PublicKey, randomBytes } from '@dxos/crypto';
24
- import { raise } from '@dxos/debug';
25
24
  import { FullyConnectedTopology, NetworkManager } from '@dxos/network-manager';
26
25
 
27
- import { IdentityNotInitializedError, InvalidInvitationError } from '../errors';
28
- import { Identity } from '../halo';
26
+ import { InvalidInvitationError } from '../errors';
27
+ import { CredentialsSigner } from '../protocol';
29
28
  import { greetingProtocolProvider } from './greeting-protocol-provider';
30
29
  import { GreetingState } from './greeting-responder';
31
30
  import { InvitationDescriptor, InvitationDescriptorType } from './invitation-descriptor';
@@ -170,17 +169,17 @@ export class OfflineInvitationClaimer {
170
169
  }
171
170
 
172
171
  // The secretProvider should provide an `Auth` message signed directly by the Identity key.
173
- static createSecretProvider (identity: Identity): SecretProvider {
172
+ static createSecretProvider (credentials: CredentialsSigner): SecretProvider {
174
173
  return async (info?: SecretInfo) => {
175
174
  return Buffer.from(codec.encode(
176
175
  /* The signed portion of the Auth message includes the ID and authNonce provided
177
176
  * by the `info` object. These values will be validated on the other end.
178
177
  */
179
178
  createAuthMessage(
180
- identity.signer,
179
+ credentials.signer,
181
180
  info!.id.value,
182
- identity.identityKey ?? raise(new IdentityNotInitializedError()),
183
- identity.deviceKeyChain ?? raise(new IdentityNotInitializedError()),
181
+ credentials.getIdentityKey(),
182
+ credentials.getDeviceSigningKeys(),
184
183
  undefined,
185
184
  info!.authNonce.value)
186
185
  ));
@@ -22,12 +22,12 @@ import { NetworkManager } from '@dxos/network-manager';
22
22
  import { ObjectModel } from '@dxos/object-model';
23
23
 
24
24
  import { IdentityNotInitializedError } from '../errors';
25
- import { IdentityProvider } from '../halo';
26
25
  import {
27
26
  createDataPartyAdmissionMessages,
28
27
  GreetingInitiator, InvitationDescriptor, InvitationDescriptorType, OfflineInvitationClaimer
29
28
  } from '../invitations';
30
29
  import { PartyFeedProvider, PartyOptions } from '../pipeline';
30
+ import { IdentityCredentialsProvider } from '../protocol/identity-credentials';
31
31
  import { SnapshotStore } from '../snapshots';
32
32
  import { DataParty, PARTY_ITEM_TYPE } from './data-party';
33
33
 
@@ -38,7 +38,7 @@ const log = debug('dxos:echo-db:party-factory');
38
38
  */
39
39
  export class PartyFactory {
40
40
  constructor (
41
- private readonly _identityProvider: IdentityProvider,
41
+ private readonly _identityProvider: IdentityCredentialsProvider,
42
42
  private readonly _networkManager: NetworkManager,
43
43
  private readonly _modelFactory: ModelFactory,
44
44
  private readonly _snapshotStore: SnapshotStore,
@@ -52,8 +52,6 @@ export class PartyFactory {
52
52
  @timed(5_000)
53
53
  async createParty (): Promise<DataParty> {
54
54
  const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
55
- assert(identity.identityGenesis, 'HALO not initialized.');
56
- assert(identity.deviceKeyChain, 'Device KeyChain not initialized.');
57
55
  assert(!this._options.readOnly, 'PartyFactory is read-only.');
58
56
 
59
57
  const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
@@ -66,7 +64,7 @@ export class PartyFactory {
66
64
 
67
65
  // PartyGenesis (self-signed by Party).
68
66
  await party.processor.writeHaloMessage(createPartyGenesisMessage(
69
- identity.signer,
67
+ identity.keyring,
70
68
  partyKey,
71
69
  writableFeed.key,
72
70
  partyKey)
@@ -74,7 +72,7 @@ export class PartyFactory {
74
72
 
75
73
  // KeyAdmit (IdentityGenesis in an Envelope signed by Party).
76
74
  await party.processor.writeHaloMessage(createEnvelopeMessage(
77
- identity.signer,
75
+ identity.keyring,
78
76
  partyKey.publicKey,
79
77
  wrapMessage(identity.identityGenesis),
80
78
  [partyKey])
@@ -82,7 +80,7 @@ export class PartyFactory {
82
80
 
83
81
  // FeedAdmit (signed by the Device KeyChain).
84
82
  await party.processor.writeHaloMessage(createFeedAdmitMessage(
85
- identity.signer,
83
+ identity.keyring,
86
84
  partyKey.publicKey,
87
85
  writableFeed.key,
88
86
  [identity.deviceKeyChain]
@@ -91,7 +89,7 @@ export class PartyFactory {
91
89
  // IdentityInfo in an Envelope signed by the Device KeyChain.
92
90
  if (identity.identityInfo) {
93
91
  await party.processor.writeHaloMessage(createEnvelopeMessage(
94
- identity.signer,
92
+ identity.keyring,
95
93
  partyKey.publicKey,
96
94
  wrapMessage(identity.identityInfo),
97
95
  [identity.deviceKeyChain]
@@ -138,13 +136,12 @@ export class PartyFactory {
138
136
  );
139
137
 
140
138
  await party.open();
141
- assert(identity.identityKey, 'No identity key.');
142
139
  const isHalo = identity.identityKey.publicKey.equals(partyKey);
143
140
  const signingKey = isHalo ? identity.deviceKey : identity.deviceKeyChain;
144
141
  assert(signingKey, 'No device key or keychain.');
145
142
  // Write the Feed genesis message.
146
143
  await party.processor.writeHaloMessage(createFeedAdmitMessage(
147
- identity.signer,
144
+ identity.keyring,
148
145
  partyKey,
149
146
  feedKeyPair.publicKey,
150
147
  [signingKey]
@@ -213,7 +210,7 @@ export class PartyFactory {
213
210
  async (partyKey, nonce) => [createDataPartyAdmissionMessages(
214
211
  identity.createCredentialsSigner(),
215
212
  partyKey,
216
- identity.identityGenesis ?? raise(new IdentityNotInitializedError()),
213
+ identity.identityGenesis,
217
214
  nonce
218
215
  )]
219
216
  );
@@ -223,13 +220,11 @@ export class PartyFactory {
223
220
  const party = await this.addParty(partyKey, hints);
224
221
  await initiator.destroy();
225
222
  if (!haloInvitation) {
226
- assert(identity.deviceKeyChain);
227
-
228
223
  // Copy our signed IdentityInfo into the new Party.
229
224
  const infoMessage = identity.identityInfo;
230
225
  if (infoMessage) {
231
226
  await party.processor.writeHaloMessage(createEnvelopeMessage(
232
- identity.signer,
227
+ identity.keyring,
233
228
  partyKey,
234
229
  wrapMessage(infoMessage),
235
230
  [identity.deviceKeyChain]
@@ -244,8 +239,6 @@ export class PartyFactory {
244
239
  const identity = this._identityProvider() ?? raise(new IdentityNotInitializedError());
245
240
 
246
241
  assert(!this._options.readOnly, 'PartyFactory is read-only');
247
- assert(identity.identityGenesis, 'IdentityGenesis must exist');
248
- assert(identity.deviceKeyChain, 'Device KeyChain must exist');
249
242
 
250
243
  const partyKey = await identity.keyring.createKeyRecord({ type: KeyType.PARTY });
251
244
  const party = await this.constructParty(partyKey.publicKey);
@@ -257,7 +250,7 @@ export class PartyFactory {
257
250
 
258
251
  // PartyGenesis (self-signed by Party).
259
252
  await party.processor.writeHaloMessage(createPartyGenesisMessage(
260
- identity.signer,
253
+ identity.keyring,
261
254
  partyKey,
262
255
  writableFeed.key,
263
256
  partyKey)
@@ -265,7 +258,7 @@ export class PartyFactory {
265
258
 
266
259
  // KeyAdmit (IdentityGenesis in an Envelope signed by Party).
267
260
  await party.processor.writeHaloMessage(createEnvelopeMessage(
268
- identity.signer,
261
+ identity.keyring,
269
262
  partyKey.publicKey,
270
263
  wrapMessage(identity.identityGenesis),
271
264
  [partyKey]
@@ -273,7 +266,7 @@ export class PartyFactory {
273
266
 
274
267
  // FeedAdmit (signed by the Device KeyChain).
275
268
  await party.processor.writeHaloMessage(createFeedAdmitMessage(
276
- identity.signer,
269
+ identity.keyring,
277
270
  partyKey.publicKey,
278
271
  writableFeed.key,
279
272
  [identity.deviceKeyChain]
@@ -282,7 +275,7 @@ export class PartyFactory {
282
275
  // IdentityInfo in an Envelope signed by the Device KeyChain.
283
276
  if (identity.identityInfo) {
284
277
  await party.processor.writeHaloMessage(createEnvelopeMessage(
285
- identity.signer,
278
+ identity.keyring,
286
279
  partyKey.publicKey,
287
280
  wrapMessage(identity.identityInfo),
288
281
  [identity.deviceKeyChain]
@@ -33,9 +33,9 @@ import { createStorage, StorageType } from '@dxos/random-access-multi-storage';
33
33
  import { afterTest, testTimeout } from '@dxos/testutils';
34
34
 
35
35
  import { Item } from '../api';
36
- import { HaloFactory, Identity, IdentityManager } from '../halo';
37
36
  import { defaultInvitationAuthenticator, OfflineInvitationClaimer } from '../invitations';
38
37
  import { MetadataStore, PartyFeedProvider } from '../pipeline';
38
+ import { createTestIdentityCredentials } from '../protocol/identity-credentials';
39
39
  import { SnapshotStore } from '../snapshots';
40
40
  import { messageLogger } from '../testing';
41
41
  import { createRamStorage } from '../util';
@@ -58,33 +58,12 @@ const setup = async () => {
58
58
  const keyring = new Keyring();
59
59
  const metadataStore = new MetadataStore(createRamStorage());
60
60
  const feedStore = new FeedStore(createStorage('', StorageType.RAM), { valueEncoding: codec });
61
-
62
- const identityKey = await keyring.createKeyRecord({ type: KeyType.IDENTITY });
63
-
64
- assert(keyring.keys.length === 1);
65
-
66
61
  const snapshotStore = new SnapshotStore(createStorage('', StorageType.RAM));
67
62
  const modelFactory = new ModelFactory().registerModel(ObjectModel);
68
63
  const networkManager = new NetworkManager();
69
64
  const feedProviderFactory = (partyKey: PublicKey) => new PartyFeedProvider(metadataStore, keyring, feedStore, partyKey);
70
65
 
71
- const haloFactory = new HaloFactory(
72
- networkManager,
73
- modelFactory,
74
- snapshotStore,
75
- feedProviderFactory,
76
- keyring,
77
- {
78
- writeLogger: messageLogger('<<<'),
79
- readLogger: messageLogger('>>>')
80
- }
81
- );
82
- const haloParty = await haloFactory.createHalo({
83
- identityDisplayName: identityKey.publicKey.humanize()
84
- });
85
- afterTest(() => haloParty.close());
86
- const identity = new Identity(keyring, haloParty);
87
-
66
+ const identity = await createTestIdentityCredentials(keyring);
88
67
  const partyFactory = new PartyFactory(
89
68
  () => identity,
90
69
  networkManager,
@@ -171,33 +150,22 @@ describe('Party manager', () => {
171
150
  test('Create from cold start', async () => {
172
151
  const storage = createStorage('', StorageType.RAM);
173
152
  const feedStore = new FeedStore(storage, { valueEncoding: codec });
174
-
175
153
  const keyring = new Keyring();
176
154
  const metadataStore = new MetadataStore(createRamStorage());
177
-
178
- const identityKey = await keyring.createKeyRecord({ type: KeyType.IDENTITY });
179
- await keyring.createKeyRecord({ type: KeyType.DEVICE });
180
-
181
155
  const modelFactory = new ModelFactory().registerModel(ObjectModel);
182
156
  const snapshotStore = new SnapshotStore(createStorage('', StorageType.RAM));
183
157
  const networkManager = new NetworkManager();
184
158
  const feedProviderFactory = (partyKey: PublicKey) => new PartyFeedProvider(metadataStore, keyring, feedStore, partyKey);
159
+
160
+ const identity = await createTestIdentityCredentials(keyring);
185
161
  const partyFactory = new PartyFactory(
186
- () => identityManager.identity,
162
+ () => identity,
187
163
  networkManager,
188
164
  modelFactory,
189
165
  snapshotStore,
190
166
  feedProviderFactory
191
167
  );
192
- const haloFactory: HaloFactory = new HaloFactory(
193
- networkManager,
194
- modelFactory,
195
- snapshotStore,
196
- feedProviderFactory,
197
- keyring
198
- );
199
- const identityManager = new IdentityManager(keyring, haloFactory, metadataStore);
200
- const partyManager = new PartyManager(metadataStore, snapshotStore, () => identityManager.identity, partyFactory);
168
+ const partyManager = new PartyManager(metadataStore, snapshotStore, () => identity, partyFactory);
201
169
 
202
170
  /* TODO(telackey): Injecting "raw" Parties into the feeds behind the scenes seems fishy to me, as it writes the
203
171
  * Party messages in a slightly different way than the code inside PartyFactory does, and so could easily diverge
@@ -221,7 +189,7 @@ describe('Party manager', () => {
221
189
  assert(feedKey);
222
190
 
223
191
  const feedStream = createWritableFeedStream(feed);
224
- feedStream.write({ halo: createPartyGenesisMessage(keyring, partyKey, feedKey.publicKey, identityKey) });
192
+ feedStream.write({ halo: createPartyGenesisMessage(keyring, partyKey, feedKey.publicKey, identity.identityKey) });
225
193
  feedStream.write({
226
194
  echo: checkType<EchoEnvelope>({
227
195
  itemId: 'foo',
@@ -235,8 +203,6 @@ describe('Party manager', () => {
235
203
  }
236
204
 
237
205
  // Open.
238
- await identityManager.createHalo();
239
- afterTest(() => identityManager.close());
240
206
  await partyManager.open();
241
207
  expect(partyManager.parties).toHaveLength(numParties);
242
208
  await partyManager.close();
@@ -450,7 +416,7 @@ describe('Party manager', () => {
450
416
  // Redeem the invitation on B.
451
417
  expect(partyManagerB.parties).toHaveLength(0);
452
418
  const partyB = await partyManagerB.joinParty(invitationDescriptor,
453
- OfflineInvitationClaimer.createSecretProvider(identityB));
419
+ OfflineInvitationClaimer.createSecretProvider(identityB.createCredentialsSigner()));
454
420
  expect(partyB).toBeDefined();
455
421
  log(`Joined ${partyB.key.toHex()}`);
456
422
 
@@ -13,9 +13,9 @@ import { timed } from '@dxos/debug';
13
13
  import { PartyKey, PartySnapshot } from '@dxos/echo-protocol';
14
14
  import { ComplexMap, boolGuard } from '@dxos/util';
15
15
 
16
- import { IdentityProvider } from '../halo';
17
16
  import { InvitationDescriptor } from '../invitations';
18
17
  import { MetadataStore } from '../pipeline';
18
+ import { IdentityCredentialsProvider } from '../protocol/identity-credentials';
19
19
  import { SnapshotStore } from '../snapshots';
20
20
  import { DataParty, PARTY_ITEM_TYPE, PARTY_TITLE_PROPERTY } from './data-party';
21
21
  import { PartyFactory } from './party-factory';
@@ -51,7 +51,7 @@ export class PartyManager {
51
51
  constructor (
52
52
  private readonly _metadataStore: MetadataStore,
53
53
  private readonly _snapshotStore: SnapshotStore,
54
- private readonly _identityProvider: IdentityProvider,
54
+ private readonly _identityProvider: IdentityCredentialsProvider,
55
55
  private readonly _partyFactory: PartyFactory
56
56
  ) {}
57
57
 
@@ -74,10 +74,11 @@ export class PartyManager {
74
74
 
75
75
  let partyKeys = this._metadataStore.parties.map(party => party.key).filter(boolGuard);
76
76
 
77
+ // Identity may be undefined, for example, on the first start.
77
78
  const identity = this._identityProvider();
78
79
 
79
80
  // TODO(telackey): Does it make any sense to load other parties if we don't have an HALO?
80
- if (identity?.identityKey) {
81
+ if (identity) {
81
82
  partyKeys = partyKeys.filter(partyKey => !partyKey.equals(identity.identityKey!.publicKey));
82
83
  }
83
84