@dxos/client-services 0.7.4 → 0.7.5-labs.071a3e2

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 (96) hide show
  1. package/dist/lib/browser/{chunk-423GRVVV.mjs → chunk-SKOL3Q2R.mjs} +379 -267
  2. package/dist/lib/browser/chunk-SKOL3Q2R.mjs.map +7 -0
  3. package/dist/lib/browser/index.mjs +1 -1
  4. package/dist/lib/browser/meta.json +1 -1
  5. package/dist/lib/browser/testing/index.mjs +1 -1
  6. package/dist/lib/node/{chunk-ZS24HRVA.cjs → chunk-XTM3FUCM.cjs} +405 -293
  7. package/dist/lib/node/chunk-XTM3FUCM.cjs.map +7 -0
  8. package/dist/lib/node/index.cjs +47 -47
  9. package/dist/lib/node/meta.json +1 -1
  10. package/dist/lib/node/testing/index.cjs +8 -8
  11. package/dist/lib/node-esm/{chunk-OQOXRHWF.mjs → chunk-EQU6BG5J.mjs} +379 -267
  12. package/dist/lib/node-esm/chunk-EQU6BG5J.mjs.map +7 -0
  13. package/dist/lib/node-esm/index.mjs +1 -1
  14. package/dist/lib/node-esm/meta.json +1 -1
  15. package/dist/lib/node-esm/testing/index.mjs +1 -1
  16. package/dist/types/src/packlets/agents/edge-agent-service.d.ts +1 -1
  17. package/dist/types/src/packlets/agents/edge-agent-service.d.ts.map +1 -1
  18. package/dist/types/src/packlets/devices/devices-service.d.ts +1 -1
  19. package/dist/types/src/packlets/devices/devices-service.d.ts.map +1 -1
  20. package/dist/types/src/packlets/devtools/devtools.d.ts +1 -1
  21. package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
  22. package/dist/types/src/packlets/devtools/feeds.d.ts +1 -1
  23. package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
  24. package/dist/types/src/packlets/devtools/keys.d.ts +1 -1
  25. package/dist/types/src/packlets/devtools/keys.d.ts.map +1 -1
  26. package/dist/types/src/packlets/devtools/metadata.d.ts +1 -1
  27. package/dist/types/src/packlets/devtools/metadata.d.ts.map +1 -1
  28. package/dist/types/src/packlets/devtools/network.d.ts +1 -1
  29. package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
  30. package/dist/types/src/packlets/devtools/spaces.d.ts +1 -1
  31. package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
  32. package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
  33. package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
  34. package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
  35. package/dist/types/src/packlets/identity/identity-manager.d.ts +0 -3
  36. package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
  37. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +12 -4
  38. package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
  39. package/dist/types/src/packlets/identity/identity-service.d.ts +9 -4
  40. package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
  41. package/dist/types/src/packlets/identity/identity.d.ts +3 -1
  42. package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
  43. package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
  44. package/dist/types/src/packlets/invitations/invitations-service.d.ts +1 -1
  45. package/dist/types/src/packlets/invitations/invitations-service.d.ts.map +1 -1
  46. package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
  47. package/dist/types/src/packlets/invitations/utils.d.ts +1 -0
  48. package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
  49. package/dist/types/src/packlets/logging/logging-service.d.ts +1 -1
  50. package/dist/types/src/packlets/logging/logging-service.d.ts.map +1 -1
  51. package/dist/types/src/packlets/network/network-service.d.ts +9 -2
  52. package/dist/types/src/packlets/network/network-service.d.ts.map +1 -1
  53. package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
  54. package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
  55. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +3 -0
  56. package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
  57. package/dist/types/src/packlets/spaces/spaces-service.d.ts +1 -1
  58. package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
  59. package/dist/types/src/packlets/system/system-service.d.ts +1 -1
  60. package/dist/types/src/packlets/system/system-service.d.ts.map +1 -1
  61. package/dist/types/src/version.d.ts +1 -1
  62. package/dist/types/src/version.d.ts.map +1 -1
  63. package/dist/types/tsconfig.tsbuildinfo +1 -0
  64. package/package.json +38 -38
  65. package/src/packlets/agents/edge-agent-service.ts +1 -1
  66. package/src/packlets/devices/devices-service.ts +1 -1
  67. package/src/packlets/devtools/devtools.ts +1 -1
  68. package/src/packlets/devtools/feeds.ts +1 -1
  69. package/src/packlets/devtools/keys.ts +1 -1
  70. package/src/packlets/devtools/metadata.ts +1 -1
  71. package/src/packlets/devtools/network.ts +1 -1
  72. package/src/packlets/devtools/spaces.ts +1 -1
  73. package/src/packlets/diagnostics/diagnostics.ts +17 -13
  74. package/src/packlets/identity/contacts-service.ts +1 -1
  75. package/src/packlets/identity/identity-manager.ts +3 -29
  76. package/src/packlets/identity/identity-recovery-manager.ts +86 -9
  77. package/src/packlets/identity/identity-service.ts +17 -4
  78. package/src/packlets/identity/identity.test.ts +2 -1
  79. package/src/packlets/identity/identity.ts +4 -1
  80. package/src/packlets/invitations/invitations-handler.ts +15 -6
  81. package/src/packlets/invitations/invitations-manager.ts +1 -1
  82. package/src/packlets/invitations/invitations-service.ts +1 -1
  83. package/src/packlets/invitations/space-invitation-protocol.ts +2 -3
  84. package/src/packlets/invitations/utils.ts +7 -0
  85. package/src/packlets/logging/logging-service.ts +1 -1
  86. package/src/packlets/network/network-service.ts +39 -1
  87. package/src/packlets/services/service-context.ts +3 -1
  88. package/src/packlets/spaces/data-space-manager.ts +1 -1
  89. package/src/packlets/spaces/edge-feed-replicator.ts +16 -10
  90. package/src/packlets/spaces/notarization-plugin.ts +32 -17
  91. package/src/packlets/spaces/spaces-service.ts +31 -21
  92. package/src/packlets/system/system-service.ts +1 -1
  93. package/src/version.ts +1 -5
  94. package/dist/lib/browser/chunk-423GRVVV.mjs.map +0 -7
  95. package/dist/lib/node/chunk-ZS24HRVA.cjs.map +0 -7
  96. package/dist/lib/node-esm/chunk-OQOXRHWF.mjs.map +0 -7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/client-services",
3
- "version": "0.7.4",
3
+ "version": "0.7.5-labs.071a3e2",
4
4
  "description": "DXOS client services implementation",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -42,48 +42,48 @@
42
42
  "dependencies": {
43
43
  "cbor-x": "^1.5.4",
44
44
  "platform": "^1.3.6",
45
- "@dxos/async": "0.7.4",
46
- "@dxos/client-protocol": "0.7.4",
47
- "@dxos/automerge": "0.7.4",
48
- "@dxos/codec-protobuf": "0.7.4",
49
- "@dxos/context": "0.7.4",
50
- "@dxos/config": "0.7.4",
51
- "@dxos/credentials": "0.7.4",
52
- "@dxos/debug": "0.7.4",
53
- "@dxos/crypto": "0.7.4",
54
- "@dxos/echo-db": "0.7.4",
55
- "@dxos/echo-pipeline": "0.7.4",
56
- "@dxos/echo-schema": "0.7.4",
57
- "@dxos/edge-client": "0.7.4",
58
- "@dxos/echo-protocol": "0.7.4",
59
- "@dxos/indexing": "0.7.4",
60
- "@dxos/invariant": "0.7.4",
61
- "@dxos/keyring": "0.7.4",
62
- "@dxos/feed-store": "0.7.4",
63
- "@dxos/kv-store": "0.7.4",
64
- "@dxos/keys": "0.7.4",
65
- "@dxos/lock-file": "0.7.4",
66
- "@dxos/log": "0.7.4",
67
- "@dxos/network-manager": "0.7.4",
68
- "@dxos/node-std": "0.7.4",
69
- "@dxos/protocols": "0.7.4",
70
- "@dxos/messaging": "0.7.4",
71
- "@dxos/rpc": "0.7.4",
72
- "@dxos/random-access-storage": "0.7.4",
73
- "@dxos/teleport": "0.7.4",
74
- "@dxos/teleport-extension-gossip": "0.7.4",
75
- "@dxos/teleport-extension-object-sync": "0.7.4",
76
- "@dxos/tracing": "0.7.4",
77
- "@dxos/timeframe": "0.7.4",
78
- "@dxos/websocket-rpc": "0.7.4",
79
- "@dxos/util": "0.7.4"
45
+ "@dxos/async": "0.7.5-labs.071a3e2",
46
+ "@dxos/automerge": "0.7.5-labs.071a3e2",
47
+ "@dxos/client-protocol": "0.7.5-labs.071a3e2",
48
+ "@dxos/codec-protobuf": "0.7.5-labs.071a3e2",
49
+ "@dxos/config": "0.7.5-labs.071a3e2",
50
+ "@dxos/context": "0.7.5-labs.071a3e2",
51
+ "@dxos/credentials": "0.7.5-labs.071a3e2",
52
+ "@dxos/debug": "0.7.5-labs.071a3e2",
53
+ "@dxos/crypto": "0.7.5-labs.071a3e2",
54
+ "@dxos/echo-db": "0.7.5-labs.071a3e2",
55
+ "@dxos/echo-pipeline": "0.7.5-labs.071a3e2",
56
+ "@dxos/echo-protocol": "0.7.5-labs.071a3e2",
57
+ "@dxos/echo-schema": "0.7.5-labs.071a3e2",
58
+ "@dxos/edge-client": "0.7.5-labs.071a3e2",
59
+ "@dxos/feed-store": "0.7.5-labs.071a3e2",
60
+ "@dxos/invariant": "0.7.5-labs.071a3e2",
61
+ "@dxos/keyring": "0.7.5-labs.071a3e2",
62
+ "@dxos/indexing": "0.7.5-labs.071a3e2",
63
+ "@dxos/keys": "0.7.5-labs.071a3e2",
64
+ "@dxos/kv-store": "0.7.5-labs.071a3e2",
65
+ "@dxos/lock-file": "0.7.5-labs.071a3e2",
66
+ "@dxos/log": "0.7.5-labs.071a3e2",
67
+ "@dxos/messaging": "0.7.5-labs.071a3e2",
68
+ "@dxos/node-std": "0.7.5-labs.071a3e2",
69
+ "@dxos/protocols": "0.7.5-labs.071a3e2",
70
+ "@dxos/network-manager": "0.7.5-labs.071a3e2",
71
+ "@dxos/random-access-storage": "0.7.5-labs.071a3e2",
72
+ "@dxos/rpc": "0.7.5-labs.071a3e2",
73
+ "@dxos/teleport": "0.7.5-labs.071a3e2",
74
+ "@dxos/teleport-extension-gossip": "0.7.5-labs.071a3e2",
75
+ "@dxos/teleport-extension-object-sync": "0.7.5-labs.071a3e2",
76
+ "@dxos/timeframe": "0.7.5-labs.071a3e2",
77
+ "@dxos/util": "0.7.5-labs.071a3e2",
78
+ "@dxos/tracing": "0.7.5-labs.071a3e2",
79
+ "@dxos/websocket-rpc": "0.7.5-labs.071a3e2"
80
80
  },
81
81
  "devDependencies": {
82
82
  "@types/platform": "^1.3.4",
83
83
  "@types/readable-stream": "^2.3.9",
84
84
  "get-port-please": "^3.1.1",
85
- "@dxos/signal": "0.7.4",
86
- "@dxos/test-utils": "0.7.4"
85
+ "@dxos/signal": "0.7.5-labs.071a3e2",
86
+ "@dxos/test-utils": "0.7.5-labs.071a3e2"
87
87
  },
88
88
  "publishConfig": {
89
89
  "access": "public"
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { Stream } from '@dxos/codec-protobuf';
5
+ import { Stream } from '@dxos/codec-protobuf/stream';
6
6
  import { type EdgeConnection } from '@dxos/edge-client';
7
7
  import { EdgeAgentStatus } from '@dxos/protocols';
8
8
  import {
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { EventSubscriptions } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf';
6
+ import { Stream } from '@dxos/codec-protobuf/stream';
7
7
  import { type EdgeConnection } from '@dxos/edge-client';
8
8
  import { invariant } from '@dxos/invariant';
9
9
  import {
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Event as AsyncEvent } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf';
6
+ import { Stream } from '@dxos/codec-protobuf/stream';
7
7
  import { type Config } from '@dxos/config';
8
8
  import {
9
9
  type ClearSnapshotsRequest,
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { EventSubscriptions } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf';
6
+ import { Stream } from '@dxos/codec-protobuf/stream';
7
7
  import { type SpaceManager } from '@dxos/echo-pipeline';
8
8
  import { FeedIterator, type FeedStore, type FeedWrapper } from '@dxos/feed-store';
9
9
  import { PublicKey } from '@dxos/keys';
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { scheduleTask } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf';
6
+ import { Stream } from '@dxos/codec-protobuf/stream';
7
7
  import { type Keyring } from '@dxos/keyring';
8
8
  import { type SubscribeToKeyringKeysResponse } from '@dxos/protocols/proto/dxos/devtools/host';
9
9
 
@@ -2,7 +2,7 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { Stream } from '@dxos/codec-protobuf';
5
+ import { Stream } from '@dxos/codec-protobuf/stream';
6
6
  import { type SubscribeToMetadataResponse } from '@dxos/protocols/proto/dxos/devtools/host';
7
7
 
8
8
  import { type ServiceContext } from '../services';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2020 DXOS.org
3
3
  //
4
4
 
5
- import { Stream } from '@dxos/codec-protobuf';
5
+ import { Stream } from '@dxos/codec-protobuf/stream';
6
6
  import { Context } from '@dxos/context';
7
7
  import { PublicKey } from '@dxos/keys';
8
8
  import { type SignalManager } from '@dxos/messaging';
@@ -2,7 +2,7 @@
2
2
  // Copyright 2020 DXOS.org
3
3
  //
4
4
 
5
- import { Stream } from '@dxos/codec-protobuf';
5
+ import { Stream } from '@dxos/codec-protobuf/stream';
6
6
  import { type Space } from '@dxos/echo-pipeline';
7
7
  import {
8
8
  type SubscribeToSpacesRequest,
@@ -6,7 +6,7 @@ import { asyncTimeout } from '@dxos/async';
6
6
  import { type ClientServices } from '@dxos/client-protocol';
7
7
  import { getFirstStreamValue } from '@dxos/codec-protobuf';
8
8
  import { type Config, type ConfigProto } from '@dxos/config';
9
- import { credentialTypeFilter } from '@dxos/credentials';
9
+ import { createDidFromIdentityKey, credentialTypeFilter } from '@dxos/credentials';
10
10
  import { invariant } from '@dxos/invariant';
11
11
  import { type PublicKey } from '@dxos/keys';
12
12
  import { STORAGE_VERSION } from '@dxos/protocols';
@@ -120,6 +120,7 @@ export const createDiagnostics = async (
120
120
  if (identity) {
121
121
  // Identity.
122
122
  diagnostics.identity = {
123
+ did: identity.did,
123
124
  identityKey: identity.identityKey,
124
125
  spaceKey: identity.space.key,
125
126
  profile: identity.profileDocument,
@@ -179,19 +180,22 @@ const getSpaceStats = async (space: DataSpace): Promise<SpaceStats> => {
179
180
  id: credential.id,
180
181
  })),
181
182
 
182
- members: Array.from(space.inner.spaceState.members.values()).map((member) => ({
183
- role: member.role,
184
- identity: {
185
- identityKey: member.key,
186
- profile: {
187
- displayName: member.assertion.profile?.displayName,
183
+ members: await Promise.all(
184
+ Array.from(space.inner.spaceState.members.values()).map(async (member) => ({
185
+ role: member.role,
186
+ identity: {
187
+ did: await createDidFromIdentityKey(member.key),
188
+ identityKey: member.key,
189
+ profile: {
190
+ displayName: member.assertion.profile?.displayName,
191
+ },
188
192
  },
189
- },
190
- presence:
191
- space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key)).length > 0
192
- ? SpaceMember.PresenceState.ONLINE
193
- : SpaceMember.PresenceState.OFFLINE,
194
- })),
193
+ presence:
194
+ space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key)).length > 0
195
+ ? SpaceMember.PresenceState.ONLINE
196
+ : SpaceMember.PresenceState.OFFLINE,
197
+ })),
198
+ ),
195
199
 
196
200
  pipeline: {
197
201
  // TODO(burdon): Pick properties from credentials if needed.
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { EventSubscriptions, scheduleTask, UpdateScheduler } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf';
6
+ import { Stream } from '@dxos/codec-protobuf/stream';
7
7
  import { type MemberInfo } from '@dxos/credentials';
8
8
  import { type SpaceManager } from '@dxos/echo-pipeline';
9
9
  import { PublicKey } from '@dxos/keys';
@@ -5,12 +5,7 @@ import platform from 'platform';
5
5
 
6
6
  import { Event } from '@dxos/async';
7
7
  import { Context } from '@dxos/context';
8
- import {
9
- createCredentialSignerWithKey,
10
- CredentialGenerator,
11
- generateSeedPhrase,
12
- keyPairFromSeedPhrase,
13
- } from '@dxos/credentials';
8
+ import { createCredentialSignerWithKey, createDidFromIdentityKey, CredentialGenerator } from '@dxos/credentials';
14
9
  import { type MetadataStore, type SpaceManager, type SwarmIdentity } from '@dxos/echo-pipeline';
15
10
  import { type EdgeConnection } from '@dxos/edge-client';
16
11
  import { type FeedStore } from '@dxos/feed-store';
@@ -335,29 +330,6 @@ export class IdentityManager {
335
330
  };
336
331
  }
337
332
 
338
- async createRecoveryPhrase() {
339
- const identity = this._identity;
340
- invariant(identity);
341
-
342
- const seedphrase = generateSeedPhrase();
343
- const keypair = keyPairFromSeedPhrase(seedphrase);
344
- const recoveryKey = PublicKey.from(keypair.publicKey);
345
- const identityKey = identity.identityKey;
346
- const credential = await identity.getIdentityCredentialSigner().createCredential({
347
- subject: identityKey,
348
- assertion: {
349
- '@type': 'dxos.halo.credentials.IdentityRecovery',
350
- recoveryKey,
351
- identityKey,
352
- },
353
- });
354
-
355
- const receipt = await identity.controlPipeline.writer.write({ credential: { credential } });
356
- await identity.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
357
-
358
- return { seedphrase };
359
- }
360
-
361
333
  private async _constructIdentity(identityRecord: IdentityRecord) {
362
334
  invariant(!this._identity);
363
335
  log('constructing identity', { identityRecord });
@@ -397,10 +369,12 @@ export class IdentityManager {
397
369
  await space.setControlFeed(controlFeed);
398
370
  await space.setDataFeed(dataFeed);
399
371
 
372
+ const did = await createDidFromIdentityKey(identityRecord.identityKey);
400
373
  const identity: Identity = new Identity({
401
374
  space,
402
375
  presence,
403
376
  signer: this._keyring,
377
+ did,
404
378
  identityKey: identityRecord.identityKey,
405
379
  deviceKey: identityRecord.deviceKey,
406
380
  edgeConnection: this._edgeConnection,
@@ -9,8 +9,16 @@ import { invariant } from '@dxos/invariant';
9
9
  import { type Keyring } from '@dxos/keyring';
10
10
  import { PublicKey } from '@dxos/keys';
11
11
  import { log } from '@dxos/log';
12
- import { EdgeAuthChallengeError, type RecoverIdentityRequest, type RecoverIdentityResponseBody } from '@dxos/protocols';
12
+ import {
13
+ EdgeAuthChallengeError,
14
+ type RecoverIdentityRequest as EdgeRecoverIdentityRequest,
15
+ type RecoverIdentityResponseBody,
16
+ } from '@dxos/protocols';
13
17
  import { schema } from '@dxos/protocols/proto';
18
+ import {
19
+ type CreateRecoveryCredentialRequest,
20
+ type RecoverIdentityRequest,
21
+ } from '@dxos/protocols/proto/dxos/client/services';
14
22
  import { Timeframe } from '@dxos/timeframe';
15
23
 
16
24
  import { type Identity } from './identity';
@@ -24,13 +32,19 @@ export class EdgeIdentityRecoveryManager {
24
32
  private readonly _acceptRecoveredIdentity: (params: JoinIdentityParams) => Promise<Identity>,
25
33
  ) {}
26
34
 
27
- public async createRecoveryPhrase() {
35
+ public async createRecoveryCredential({ recoveryKey, algorithm }: CreateRecoveryCredentialRequest) {
28
36
  const identity = this._identityProvider();
29
37
  invariant(identity);
30
38
 
31
- const seedphrase = generateSeedPhrase();
32
- const keypair = keyPairFromSeedPhrase(seedphrase);
33
- const recoveryKey = PublicKey.from(keypair.publicKey);
39
+ let recoveryCode: string | undefined;
40
+ if (!recoveryKey) {
41
+ recoveryCode = generateSeedPhrase();
42
+ const keypair = keyPairFromSeedPhrase(recoveryCode);
43
+ recoveryKey = PublicKey.from(keypair.publicKey);
44
+ algorithm = -8; // Ed25519
45
+ }
46
+
47
+ invariant(algorithm, 'Algorithm is required.');
34
48
  const identityKey = identity.identityKey;
35
49
  const credential = await identity.getIdentityCredentialSigner().createCredential({
36
50
  subject: identityKey,
@@ -38,23 +52,86 @@ export class EdgeIdentityRecoveryManager {
38
52
  '@type': 'dxos.halo.credentials.IdentityRecovery',
39
53
  recoveryKey,
40
54
  identityKey,
55
+ algorithm,
41
56
  },
42
57
  });
43
58
 
44
59
  const receipt = await identity.controlPipeline.writer.write({ credential: { credential } });
45
60
  await identity.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
46
61
 
47
- return { seedphrase };
62
+ return { recoveryCode };
63
+ }
64
+
65
+ public async requestRecoveryChallenge() {
66
+ invariant(this._edgeClient, 'Not connected to EDGE.');
67
+
68
+ const deviceKey = await this._keyring.createKey();
69
+ const controlFeedKey = await this._keyring.createKey();
70
+ const request: EdgeRecoverIdentityRequest = {
71
+ deviceKey: deviceKey.toHex(),
72
+ controlFeedKey: controlFeedKey.toHex(),
73
+ };
74
+
75
+ try {
76
+ await this._edgeClient.recoverIdentity(request);
77
+ throw new Error('No challenge received.');
78
+ } catch (error: any) {
79
+ if (!(error instanceof EdgeAuthChallengeError)) {
80
+ throw error;
81
+ }
82
+ return {
83
+ deviceKey,
84
+ controlFeedKey,
85
+ challenge: error.challenge,
86
+ };
87
+ }
88
+ }
89
+
90
+ public async recoverIdentityWithExternalSignature({
91
+ identityDid,
92
+ deviceKey,
93
+ controlFeedKey,
94
+ signature,
95
+ clientDataJson,
96
+ authenticatorData,
97
+ }: RecoverIdentityRequest.ExternalSignature) {
98
+ invariant(this._edgeClient, 'Not connected to EDGE.');
99
+
100
+ const request: EdgeRecoverIdentityRequest = {
101
+ identityDid,
102
+ deviceKey: deviceKey.toHex(),
103
+ controlFeedKey: controlFeedKey.toHex(),
104
+ signature:
105
+ clientDataJson && authenticatorData
106
+ ? {
107
+ signature: Buffer.from(signature).toString('base64'),
108
+ clientDataJson: Buffer.from(clientDataJson).toString('base64'),
109
+ authenticatorData: Buffer.from(authenticatorData).toString('base64'),
110
+ }
111
+ : Buffer.from(signature).toString('base64'),
112
+ };
113
+
114
+ const response = await this._edgeClient.recoverIdentity(request);
115
+
116
+ await this._acceptRecoveredIdentity({
117
+ authorizedDeviceCredential: decodeCredential(response.deviceAuthCredential),
118
+ haloGenesisFeedKey: PublicKey.fromHex(response.genesisFeedKey),
119
+ haloSpaceKey: PublicKey.fromHex(response.haloSpaceKey),
120
+ identityKey: PublicKey.fromHex(response.identityKey),
121
+ deviceKey,
122
+ controlFeedKey,
123
+ dataFeedKey: await this._keyring.createKey(),
124
+ });
48
125
  }
49
126
 
50
- public async recoverIdentity(args: { seedphrase: string }) {
127
+ public async recoverIdentity({ recoveryCode }: { recoveryCode: string }) {
51
128
  invariant(this._edgeClient, 'Not connected to EDGE.');
52
129
 
53
- const recoveryKeypair = keyPairFromSeedPhrase(args.seedphrase);
130
+ const recoveryKeypair = keyPairFromSeedPhrase(recoveryCode);
54
131
  const recoveryKey = PublicKey.from(recoveryKeypair.publicKey);
55
132
  const deviceKey = await this._keyring.createKey();
56
133
  const controlFeedKey = await this._keyring.createKey();
57
- const request: RecoverIdentityRequest = {
134
+ const request: EdgeRecoverIdentityRequest = {
58
135
  recoveryKey: recoveryKey.toHex(),
59
136
  deviceKey: deviceKey.toHex(),
60
137
  controlFeedKey: controlFeedKey.toHex(),
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Trigger, sleep } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf';
6
+ import { Stream } from '@dxos/codec-protobuf/stream';
7
7
  import { Resource } from '@dxos/context';
8
8
  import { createCredential, signPresentation } from '@dxos/credentials';
9
9
  import { invariant } from '@dxos/invariant';
@@ -11,6 +11,7 @@ import { type Keyring } from '@dxos/keyring';
11
11
  import { log } from '@dxos/log';
12
12
  import {
13
13
  type CreateIdentityRequest,
14
+ type CreateRecoveryCredentialRequest,
14
15
  type Identity as IdentityProto,
15
16
  type IdentityService,
16
17
  type QueryIdentityResponse,
@@ -76,6 +77,7 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
76
77
  }
77
78
 
78
79
  return {
80
+ did: this._identityManager.identity.did,
79
81
  identityKey: this._identityManager.identity.identityKey,
80
82
  spaceKey: this._identityManager.identity.space.key,
81
83
  profile: this._identityManager.identity.profileDocument,
@@ -89,12 +91,23 @@ export class IdentityServiceImpl extends Resource implements IdentityService {
89
91
  return this._getIdentity()!;
90
92
  }
91
93
 
92
- async createRecoveryPhrase() {
93
- return this._recoveryManager.createRecoveryPhrase();
94
+ async createRecoveryCredential(request: CreateRecoveryCredentialRequest) {
95
+ return this._recoveryManager.createRecoveryCredential(request);
96
+ }
97
+
98
+ async requestRecoveryChallenge() {
99
+ return this._recoveryManager.requestRecoveryChallenge();
94
100
  }
95
101
 
96
102
  async recoverIdentity(request: RecoverIdentityRequest): Promise<IdentityProto> {
97
- await this._recoveryManager.recoverIdentity(request);
103
+ if (request.recoveryCode) {
104
+ await this._recoveryManager.recoverIdentity({ recoveryCode: request.recoveryCode });
105
+ } else if (request.external) {
106
+ await this._recoveryManager.recoverIdentityWithExternalSignature(request.external);
107
+ } else {
108
+ throw new Error('Invalid request.');
109
+ }
110
+
98
111
  return this._getIdentity()!;
99
112
  }
100
113
 
@@ -5,7 +5,7 @@
5
5
  import { onTestFinished, describe, expect, test } from 'vitest';
6
6
 
7
7
  import { Context } from '@dxos/context';
8
- import { CredentialGenerator, verifyCredential } from '@dxos/credentials';
8
+ import { createDidFromIdentityKey, CredentialGenerator, verifyCredential } from '@dxos/credentials';
9
9
  import {
10
10
  createIdFromSpaceKey,
11
11
  MetadataStore,
@@ -206,6 +206,7 @@ describe('identity/identity', () => {
206
206
 
207
207
  const identity = new Identity({
208
208
  signer: keyring,
209
+ did: await createDidFromIdentityKey(identityKey),
209
210
  identityKey,
210
211
  deviceKey,
211
212
  space,
@@ -17,7 +17,7 @@ import { type Space } from '@dxos/echo-pipeline';
17
17
  import { type EdgeConnection } from '@dxos/edge-client';
18
18
  import { writeMessages, type FeedWrapper } from '@dxos/feed-store';
19
19
  import { invariant } from '@dxos/invariant';
20
- import { PublicKey, type SpaceId } from '@dxos/keys';
20
+ import { type IdentityDid, PublicKey, type SpaceId } from '@dxos/keys';
21
21
  import { log } from '@dxos/log';
22
22
  import { type Runtime } from '@dxos/protocols/proto/dxos/config';
23
23
  import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
@@ -38,6 +38,7 @@ import { DefaultSpaceStateMachine } from './default-space-state-machine';
38
38
  import { EdgeFeedReplicator } from '../spaces';
39
39
 
40
40
  export type IdentityParams = {
41
+ did: IdentityDid;
41
42
  identityKey: PublicKey;
42
43
  deviceKey: PublicKey;
43
44
  signer: Signer;
@@ -63,6 +64,7 @@ export class Identity {
63
64
 
64
65
  public readonly authVerifier: TrustedKeySetAuthVerifier;
65
66
 
67
+ public readonly did: IdentityDid;
66
68
  public readonly identityKey: PublicKey;
67
69
  public readonly deviceKey: PublicKey;
68
70
 
@@ -73,6 +75,7 @@ export class Identity {
73
75
  this._signer = params.signer;
74
76
  this._presence = params.presence;
75
77
 
78
+ this.did = params.did;
76
79
  this.identityKey = params.identityKey;
77
80
  this.deviceKey = params.deviceKey;
78
81
 
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { type PushStream, scheduleTask, TimeoutError, type Trigger } from '@dxos/async';
6
- import { INVITATION_TIMEOUT } from '@dxos/client-protocol';
6
+ import { INVITATION_TIMEOUT, getExpirationTime } from '@dxos/client-protocol';
7
7
  import { type Context, ContextDisposedError } from '@dxos/context';
8
8
  import { createKeyPair, sign } from '@dxos/crypto';
9
9
  import { type EdgeHttpClient } from '@dxos/edge-client';
@@ -11,7 +11,7 @@ import { invariant } from '@dxos/invariant';
11
11
  import { PublicKey } from '@dxos/keys';
12
12
  import { log } from '@dxos/log';
13
13
  import { createTeleportProtocolFactory, type SwarmNetworkManager, type SwarmConnection } from '@dxos/network-manager';
14
- import { InvalidInvitationExtensionRoleError, trace } from '@dxos/protocols';
14
+ import { InvalidInvitationError, InvalidInvitationExtensionRoleError, trace } from '@dxos/protocols';
15
15
  import { type AdmissionKeypair, Invitation } from '@dxos/protocols/proto/dxos/client/services';
16
16
  import { type DeviceProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
17
17
  import { AuthenticationResponse, type IntroductionResponse } from '@dxos/protocols/proto/dxos/halo/invitations';
@@ -188,8 +188,9 @@ export class InvitationsHandler {
188
188
  return extension;
189
189
  };
190
190
 
191
- if (invitation.lifetime && invitation.created) {
192
- if (invitation.created.getTime() + invitation.lifetime * 1000 < Date.now()) {
191
+ const expiresOn = getExpirationTime(invitation);
192
+ if (expiresOn) {
193
+ if (expiresOn.getTime() < Date.now()) {
193
194
  log.warn('invitation has already expired');
194
195
  guardedState.set(null, Invitation.State.EXPIRED);
195
196
  void ctx.dispose().catch((err) => log.catch(err));
@@ -204,7 +205,7 @@ export class InvitationsHandler {
204
205
  metrics.increment('dxos.invitation.expired');
205
206
  await ctx.dispose();
206
207
  },
207
- invitation.created.getTime() + invitation.lifetime * 1000 - Date.now(),
208
+ expiresOn.getTime() - Date.now(),
208
209
  );
209
210
  }
210
211
 
@@ -397,7 +398,7 @@ export class InvitationsHandler {
397
398
  edgeInvitationHandler.handle(ctx, guardedState, protocol, deviceProfile);
398
399
 
399
400
  scheduleTask(ctx, async () => {
400
- const error = protocol.checkInvitation(invitation);
401
+ const error = checkInvitation(protocol, invitation);
401
402
  if (error) {
402
403
  stream.error(error);
403
404
  await ctx.dispose();
@@ -499,6 +500,14 @@ export class InvitationsHandler {
499
500
  }
500
501
  }
501
502
 
503
+ const checkInvitation = (protocol: InvitationProtocol, invitation: Partial<Invitation>) => {
504
+ const expiresOn = getExpirationTime(invitation);
505
+ if (expiresOn && expiresOn.getTime() < Date.now()) {
506
+ return new InvalidInvitationError('Invitation already expired.');
507
+ }
508
+ return protocol.checkInvitation(invitation);
509
+ };
510
+
502
511
  export const createAdmissionKeypair = (): AdmissionKeypair => {
503
512
  const keypair = createKeyPair();
504
513
  return { publicKey: PublicKey.from(keypair.publicKey), privateKey: keypair.secretKey };
@@ -202,7 +202,7 @@ export class InvitationsManager {
202
202
  created = new Date(),
203
203
  guestKeypair = undefined,
204
204
  role = SpaceMember.Role.ADMIN,
205
- lifetime = 86400, // 1 day,
205
+ lifetime = 86400 * 7, // 7 days,
206
206
  multiUse = false,
207
207
  ...options
208
208
  } = _options ?? {};
@@ -2,7 +2,7 @@
2
2
  // Copyright 2022 DXOS.org
3
3
  //
4
4
 
5
- import { Stream } from '@dxos/codec-protobuf';
5
+ import { Stream } from '@dxos/codec-protobuf/stream';
6
6
  import {
7
7
  type AuthenticationRequest,
8
8
  type AcceptInvitationRequest,
@@ -28,6 +28,7 @@ import {
28
28
  } from '@dxos/protocols/proto/dxos/halo/invitations';
29
29
 
30
30
  import { type InvitationProtocol } from './invitation-protocol';
31
+ import { computeExpirationTime } from './utils';
31
32
  import { type DataSpaceManager, type SigningContext } from '../spaces';
32
33
 
33
34
  export class SpaceInvitationProtocol implements InvitationProtocol {
@@ -113,9 +114,7 @@ export class SpaceInvitationProtocol implements InvitationProtocol {
113
114
  authMethod: invitation.authMethod,
114
115
  swarmKey: invitation.swarmKey,
115
116
  role: invitation.role ?? SpaceMember.Role.ADMIN,
116
- expiresOn: invitation.lifetime
117
- ? new Date((invitation.created?.getTime() ?? Date.now()) + invitation.lifetime)
118
- : undefined,
117
+ expiresOn: computeExpirationTime(invitation),
119
118
  multiUse: invitation.multiUse ?? false,
120
119
  guestKey:
121
120
  invitation.authMethod === Invitation.AuthMethod.KNOWN_PUBLIC_KEY
@@ -10,6 +10,13 @@ export const stateToString = (state: Invitation.State): string => {
10
10
  return Object.entries(Invitation.State).find(([key, val]) => val === state)?.[0] ?? 'unknown';
11
11
  };
12
12
 
13
+ export const computeExpirationTime = (invitation: Partial<Invitation>): Date | undefined => {
14
+ if (!invitation.lifetime) {
15
+ return;
16
+ }
17
+ return new Date((invitation.created?.getTime() ?? Date.now()) + invitation.lifetime * 1000);
18
+ };
19
+
13
20
  export const tryAcquireBeforeContextDisposed = async (ctx: Context, mutex: Mutex): Promise<MutexGuard> => {
14
21
  let guard: MutexGuard | undefined;
15
22
  return cancelWithContext(
@@ -3,7 +3,7 @@
3
3
  //
4
4
 
5
5
  import { Event } from '@dxos/async';
6
- import { Stream } from '@dxos/codec-protobuf';
6
+ import { Stream } from '@dxos/codec-protobuf/stream';
7
7
  import { PublicKey } from '@dxos/keys';
8
8
  import {
9
9
  type LogLevel,