@dxos/echo-db 2.33.2 → 2.33.3-dev.5c4af82a

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 (37) hide show
  1. package/dist/src/echo.test.js +10 -10
  2. package/dist/src/echo.test.js.map +1 -1
  3. package/dist/src/halo/halo.d.ts +1 -0
  4. package/dist/src/halo/halo.d.ts.map +1 -1
  5. package/dist/src/halo/halo.js +10 -1
  6. package/dist/src/halo/halo.js.map +1 -1
  7. package/dist/src/halo/identity-manager.d.ts +0 -1
  8. package/dist/src/halo/identity-manager.d.ts.map +1 -1
  9. package/dist/src/halo/identity-manager.js +11 -11
  10. package/dist/src/halo/identity-manager.js.map +1 -1
  11. package/dist/src/halo/identity.d.ts +15 -11
  12. package/dist/src/halo/identity.d.ts.map +1 -1
  13. package/dist/src/halo/identity.js +17 -20
  14. package/dist/src/halo/identity.js.map +1 -1
  15. package/dist/src/invitations/offline-invitation-claimer.d.ts.map +1 -1
  16. package/dist/src/invitations/offline-invitation-claimer.js +1 -3
  17. package/dist/src/invitations/offline-invitation-claimer.js.map +1 -1
  18. package/dist/src/parties/party-factory.d.ts.map +1 -1
  19. package/dist/src/parties/party-factory.js +1 -10
  20. package/dist/src/parties/party-factory.js.map +1 -1
  21. package/dist/src/parties/party-manager.d.ts.map +1 -1
  22. package/dist/src/parties/party-manager.js +2 -1
  23. package/dist/src/parties/party-manager.js.map +1 -1
  24. package/dist/src/protocol/credentials-signer.d.ts +4 -4
  25. package/dist/src/protocol/credentials-signer.d.ts.map +1 -1
  26. package/dist/src/protocol/credentials-signer.js +8 -8
  27. package/dist/src/protocol/credentials-signer.js.map +1 -1
  28. package/dist/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +17 -17
  30. package/src/echo.test.ts +10 -10
  31. package/src/halo/halo.ts +8 -1
  32. package/src/halo/identity-manager.ts +13 -14
  33. package/src/halo/identity.ts +32 -35
  34. package/src/invitations/offline-invitation-claimer.ts +3 -4
  35. package/src/parties/party-factory.ts +1 -8
  36. package/src/parties/party-manager.ts +2 -1
  37. package/src/protocol/credentials-signer.ts +9 -9
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,12 +2,12 @@
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
12
  import { ContactManager } from './contact-manager';
13
13
  import { HaloParty } from './halo-party';
@@ -21,9 +21,9 @@ const log = debug('dxos:echo-db:identity');
21
21
  * Acts as a read-only view into IdentityManager.
22
22
  */
23
23
  export class Identity {
24
- private _identityKey?: KeyRecord;
25
- private _deviceKey?: KeyRecord;
26
- private _deviceKeyChain?: KeyChain;
24
+ private readonly _identityKey: KeyRecord;
25
+ private readonly _deviceKey: KeyRecord;
26
+ private readonly _deviceKeyChain: KeyChain;
27
27
 
28
28
  /**
29
29
  * @param _halo HALO party. Must be open.
@@ -31,54 +31,51 @@ export class Identity {
31
31
  constructor (
32
32
  private readonly _keyring: Keyring,
33
33
  private readonly _halo: HaloParty
34
- ) {}
34
+ ) {
35
+ this._identityKey = this._keyring.findKey(Filter.matches({ type: KeyType.IDENTITY, own: true, trusted: true })) ?? failUndefined();
36
+ this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE })) ?? failUndefined();
37
+ this._deviceKeyChain = getDeviceKeyChainFromHalo(this._halo, this.deviceKey);
38
+ assert(this._halo.identityGenesis);
39
+ }
35
40
 
36
41
  get signer (): Signer {
37
42
  return this._keyring;
38
43
  }
39
44
 
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
- }
44
-
45
+ get identityKey (): KeyRecord {
45
46
  return this._identityKey;
46
47
  }
47
48
 
48
- get deviceKey (): KeyRecord | undefined {
49
- if (!this._deviceKey) {
50
- this._deviceKey = this._keyring.findKey(Keyring.signingFilter({ type: KeyType.DEVICE }));
51
- }
52
-
49
+ get deviceKey (): KeyRecord {
53
50
  return this._deviceKey;
54
51
  }
55
52
 
56
- get deviceKeyChain (): KeyChain | undefined {
57
- if (!this._deviceKeyChain) {
58
- this._deviceKeyChain = this.deviceKey && this._halo ? getDeviceKeyChainFromHalo(this._halo, this.deviceKey) : undefined;
59
- }
60
-
53
+ get deviceKeyChain (): KeyChain {
61
54
  return this._deviceKeyChain;
62
55
  }
63
56
 
64
- get preferences (): Preferences | undefined {
65
- return this._halo?.preferences;
57
+ get preferences (): Preferences {
58
+ return this._halo.preferences;
66
59
  }
67
60
 
68
- get contacts (): ContactManager | undefined {
69
- return this._halo?.contacts;
61
+ get contacts (): ContactManager {
62
+ return this._halo.contacts;
70
63
  }
71
64
 
72
65
  get displayName (): string | undefined {
73
66
  return this.identityInfo?.signed.payload.displayName;
74
67
  }
75
68
 
76
- get identityInfo () {
77
- return this._halo?.identityInfo;
69
+ /**
70
+ * Contains profile username.
71
+ * Can be missing if the username wasn't provided when profile was created.
72
+ */
73
+ get identityInfo (): SignedMessage | undefined {
74
+ return this._halo.identityInfo;
78
75
  }
79
76
 
80
- get identityGenesis () {
81
- return this._halo?.identityGenesis;
77
+ get identityGenesis (): SignedMessage {
78
+ return this._halo.identityGenesis ?? failUndefined();
82
79
  }
83
80
 
84
81
  /**
@@ -95,9 +92,9 @@ export class Identity {
95
92
  createCredentialsSigner (): CredentialsSigner {
96
93
  return new CredentialsSigner(
97
94
  this._keyring,
98
- () => this.identityKey ?? raise(new IdentityNotInitializedError()),
99
- () => this.deviceKey ?? raise(new IdentityNotInitializedError()),
100
- () => this.deviceKeyChain ?? this.deviceKey ?? raise(new IdentityNotInitializedError())
95
+ this.identityKey,
96
+ this.deviceKey,
97
+ this.deviceKeyChain
101
98
  );
102
99
  }
103
100
  }
@@ -112,7 +109,7 @@ function getDeviceKeyChainFromHalo (halo: HaloParty, deviceKey: KeyRecord) {
112
109
  halo.feedKeys
113
110
  );
114
111
  } catch (err: any) {
115
- log('Unable to locate device KeyChain:', err); // TODO(burdon): ???
116
- return undefined;
112
+ log('Unable to locate device KeyChain:', err);
113
+ throw err;
117
114
  }
118
115
  }
@@ -21,10 +21,9 @@ 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';
26
+ import { InvalidInvitationError } from '../errors';
28
27
  import { Identity } from '../halo';
29
28
  import { greetingProtocolProvider } from './greeting-protocol-provider';
30
29
  import { GreetingState } from './greeting-responder';
@@ -179,8 +178,8 @@ export class OfflineInvitationClaimer {
179
178
  createAuthMessage(
180
179
  identity.signer,
181
180
  info!.id.value,
182
- identity.identityKey ?? raise(new IdentityNotInitializedError()),
183
- identity.deviceKeyChain ?? raise(new IdentityNotInitializedError()),
181
+ identity.identityKey,
182
+ identity.deviceKeyChain,
184
183
  undefined,
185
184
  info!.authNonce.value)
186
185
  ));
@@ -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 });
@@ -138,7 +136,6 @@ 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.');
@@ -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,8 +220,6 @@ 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) {
@@ -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);
@@ -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
 
@@ -19,17 +19,17 @@ export class CredentialsSigner {
19
19
 
20
20
  return new CredentialsSigner(
21
21
  keyring,
22
- () => identityKey,
23
- () => deviceKey,
24
- () => deviceKey
22
+ identityKey,
23
+ deviceKey,
24
+ deviceKey
25
25
  );
26
26
  }
27
27
 
28
28
  constructor (
29
29
  private readonly _signer: Signer,
30
- private readonly _getIdentityKey: () => KeyRecord,
31
- private readonly _getDeviceKey: () => KeyRecord,
32
- private readonly _getDeviceSigningKeys: () => KeyRecord | KeyChain
30
+ private readonly _identityKey: KeyRecord,
31
+ private readonly _deviceKey: KeyRecord,
32
+ private readonly _signingKeys: KeyRecord | KeyChain
33
33
  ) {}
34
34
 
35
35
  get signer (): Signer {
@@ -37,11 +37,11 @@ export class CredentialsSigner {
37
37
  }
38
38
 
39
39
  getIdentityKey (): KeyRecord {
40
- return this._getIdentityKey();
40
+ return this._identityKey;
41
41
  }
42
42
 
43
43
  getDeviceKey (): KeyRecord {
44
- return this._getDeviceKey();
44
+ return this._deviceKey;
45
45
  }
46
46
 
47
47
  /**
@@ -55,6 +55,6 @@ export class CredentialsSigner {
55
55
  * Devices need to sign with their keyChain including the device key admission credential in the signature.
56
56
  */
57
57
  getDeviceSigningKeys (): KeyRecord | KeyChain {
58
- return this._getDeviceSigningKeys();
58
+ return this._signingKeys;
59
59
  }
60
60
  }