@dxos/client-services 0.7.5-main.9d2a38b → 0.7.5-main.e94eead
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.
- package/dist/lib/browser/{chunk-RFZHCLFI.mjs → chunk-5QA4IAAU.mjs} +262 -181
- package/dist/lib/browser/chunk-5QA4IAAU.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +1 -1
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +1 -1
- package/dist/lib/node/{chunk-WCTGAZLA.cjs → chunk-FQ5WZIGO.cjs} +255 -174
- package/dist/lib/node/chunk-FQ5WZIGO.cjs.map +7 -0
- package/dist/lib/node/index.cjs +47 -47
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +8 -8
- package/dist/lib/node-esm/{chunk-RSOLUJE6.mjs → chunk-YAOQ7FPR.mjs} +262 -181
- package/dist/lib/node-esm/chunk-YAOQ7FPR.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +1 -1
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +1 -1
- package/dist/types/src/packlets/agents/edge-agent-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics-collector.d.ts.map +1 -1
- package/dist/types/src/packlets/diagnostics/diagnostics.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +0 -3
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts +12 -4
- package/dist/types/src/packlets/identity/identity-recovery-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-service.d.ts +8 -3
- package/dist/types/src/packlets/identity/identity-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +3 -1
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitation-protocol.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/utils.d.ts +1 -0
- package/dist/types/src/packlets/invitations/utils.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/package.json +39 -38
- package/src/packlets/agents/edge-agent-manager.ts +6 -0
- package/src/packlets/diagnostics/diagnostics-collector.ts +2 -2
- package/src/packlets/diagnostics/diagnostics.ts +17 -13
- package/src/packlets/identity/identity-manager.ts +3 -29
- package/src/packlets/identity/identity-recovery-manager.ts +95 -10
- package/src/packlets/identity/identity-service.ts +16 -3
- package/src/packlets/identity/identity.test.ts +2 -1
- package/src/packlets/identity/identity.ts +4 -1
- package/src/packlets/invitations/invitations-handler.ts +15 -6
- package/src/packlets/invitations/invitations-manager.ts +1 -1
- package/src/packlets/invitations/space-invitation-protocol.ts +2 -3
- package/src/packlets/invitations/utils.ts +7 -0
- package/src/packlets/spaces/data-space-manager.ts +3 -3
- package/src/packlets/spaces/data-space.ts +2 -2
- package/src/packlets/spaces/edge-feed-replicator.ts +8 -8
- package/src/packlets/spaces/spaces-service.ts +30 -20
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-RFZHCLFI.mjs.map +0 -7
- package/dist/lib/node/chunk-WCTGAZLA.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-RSOLUJE6.mjs.map +0 -7
|
@@ -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 {
|
|
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,26 @@ export class EdgeIdentityRecoveryManager {
|
|
|
24
32
|
private readonly _acceptRecoveredIdentity: (params: JoinIdentityParams) => Promise<Identity>,
|
|
25
33
|
) {}
|
|
26
34
|
|
|
27
|
-
public async
|
|
35
|
+
public async createRecoveryCredential({ data }: CreateRecoveryCredentialRequest) {
|
|
28
36
|
const identity = this._identityProvider();
|
|
29
37
|
invariant(identity);
|
|
30
38
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
let recoveryKey: PublicKey;
|
|
40
|
+
let lookupKey: PublicKey;
|
|
41
|
+
let algorithm: string;
|
|
42
|
+
let recoveryCode: string | undefined;
|
|
43
|
+
if (data) {
|
|
44
|
+
recoveryKey = data.recoveryKey;
|
|
45
|
+
lookupKey = data.lookupKey;
|
|
46
|
+
algorithm = data.algorithm;
|
|
47
|
+
} else {
|
|
48
|
+
recoveryCode = generateSeedPhrase();
|
|
49
|
+
const keypair = keyPairFromSeedPhrase(recoveryCode);
|
|
50
|
+
recoveryKey = PublicKey.from(keypair.publicKey);
|
|
51
|
+
lookupKey = PublicKey.from(keypair.publicKey);
|
|
52
|
+
algorithm = 'ED25519';
|
|
53
|
+
}
|
|
54
|
+
|
|
34
55
|
const identityKey = identity.identityKey;
|
|
35
56
|
const credential = await identity.getIdentityCredentialSigner().createCredential({
|
|
36
57
|
subject: identityKey,
|
|
@@ -38,24 +59,88 @@ export class EdgeIdentityRecoveryManager {
|
|
|
38
59
|
'@type': 'dxos.halo.credentials.IdentityRecovery',
|
|
39
60
|
recoveryKey,
|
|
40
61
|
identityKey,
|
|
62
|
+
algorithm,
|
|
63
|
+
lookupKey,
|
|
41
64
|
},
|
|
42
65
|
});
|
|
43
66
|
|
|
44
67
|
const receipt = await identity.controlPipeline.writer.write({ credential: { credential } });
|
|
45
68
|
await identity.controlPipeline.state.waitUntilTimeframe(new Timeframe([[receipt.feedKey, receipt.seq]]));
|
|
46
69
|
|
|
47
|
-
return {
|
|
70
|
+
return { recoveryCode };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public async requestRecoveryChallenge() {
|
|
74
|
+
invariant(this._edgeClient, 'Not connected to EDGE.');
|
|
75
|
+
|
|
76
|
+
const deviceKey = await this._keyring.createKey();
|
|
77
|
+
const controlFeedKey = await this._keyring.createKey();
|
|
78
|
+
const request: EdgeRecoverIdentityRequest = {
|
|
79
|
+
deviceKey: deviceKey.toHex(),
|
|
80
|
+
controlFeedKey: controlFeedKey.toHex(),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
await this._edgeClient.recoverIdentity(request);
|
|
85
|
+
throw new Error('No challenge received.');
|
|
86
|
+
} catch (error: any) {
|
|
87
|
+
if (!(error instanceof EdgeAuthChallengeError)) {
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
deviceKey,
|
|
92
|
+
controlFeedKey,
|
|
93
|
+
challenge: error.challenge,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public async recoverIdentityWithExternalSignature({
|
|
99
|
+
lookupKey,
|
|
100
|
+
deviceKey,
|
|
101
|
+
controlFeedKey,
|
|
102
|
+
signature,
|
|
103
|
+
clientDataJson,
|
|
104
|
+
authenticatorData,
|
|
105
|
+
}: RecoverIdentityRequest.ExternalSignature) {
|
|
106
|
+
invariant(this._edgeClient, 'Not connected to EDGE.');
|
|
107
|
+
|
|
108
|
+
const request: EdgeRecoverIdentityRequest = {
|
|
109
|
+
lookupKey: lookupKey.toHex(),
|
|
110
|
+
deviceKey: deviceKey.toHex(),
|
|
111
|
+
controlFeedKey: controlFeedKey.toHex(),
|
|
112
|
+
signature:
|
|
113
|
+
clientDataJson && authenticatorData
|
|
114
|
+
? {
|
|
115
|
+
signature: Buffer.from(signature).toString('base64'),
|
|
116
|
+
clientDataJson: Buffer.from(clientDataJson).toString('base64'),
|
|
117
|
+
authenticatorData: Buffer.from(authenticatorData).toString('base64'),
|
|
118
|
+
}
|
|
119
|
+
: Buffer.from(signature).toString('base64'),
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
const response = await this._edgeClient.recoverIdentity(request);
|
|
123
|
+
|
|
124
|
+
await this._acceptRecoveredIdentity({
|
|
125
|
+
authorizedDeviceCredential: decodeCredential(response.deviceAuthCredential),
|
|
126
|
+
haloGenesisFeedKey: PublicKey.fromHex(response.genesisFeedKey),
|
|
127
|
+
haloSpaceKey: PublicKey.fromHex(response.haloSpaceKey),
|
|
128
|
+
identityKey: PublicKey.fromHex(response.identityKey),
|
|
129
|
+
deviceKey,
|
|
130
|
+
controlFeedKey,
|
|
131
|
+
dataFeedKey: await this._keyring.createKey(),
|
|
132
|
+
});
|
|
48
133
|
}
|
|
49
134
|
|
|
50
|
-
public async recoverIdentity(
|
|
135
|
+
public async recoverIdentity({ recoveryCode }: { recoveryCode: string }) {
|
|
51
136
|
invariant(this._edgeClient, 'Not connected to EDGE.');
|
|
52
137
|
|
|
53
|
-
const recoveryKeypair = keyPairFromSeedPhrase(
|
|
138
|
+
const recoveryKeypair = keyPairFromSeedPhrase(recoveryCode);
|
|
54
139
|
const recoveryKey = PublicKey.from(recoveryKeypair.publicKey);
|
|
55
140
|
const deviceKey = await this._keyring.createKey();
|
|
56
141
|
const controlFeedKey = await this._keyring.createKey();
|
|
57
|
-
const request:
|
|
58
|
-
|
|
142
|
+
const request: EdgeRecoverIdentityRequest = {
|
|
143
|
+
lookupKey: recoveryKey.toHex(),
|
|
59
144
|
deviceKey: deviceKey.toHex(),
|
|
60
145
|
controlFeedKey: controlFeedKey.toHex(),
|
|
61
146
|
};
|
|
@@ -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
|
|
93
|
-
return this._recoveryManager.
|
|
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
|
-
|
|
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
|
-
|
|
192
|
-
|
|
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
|
-
|
|
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 =
|
|
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, //
|
|
205
|
+
lifetime = 86400 * 7, // 7 days,
|
|
206
206
|
multiUse = false,
|
|
207
207
|
...options
|
|
208
208
|
} = _options ?? {};
|
|
@@ -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
|
|
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(
|
|
@@ -503,7 +503,7 @@ export class DataSpaceManager extends Resource {
|
|
|
503
503
|
});
|
|
504
504
|
dataSpace.postOpen.append(async () => {
|
|
505
505
|
const setting = dataSpace.getEdgeReplicationSetting();
|
|
506
|
-
if (setting === EdgeReplicationSetting.ENABLED) {
|
|
506
|
+
if (!setting || setting === EdgeReplicationSetting.ENABLED) {
|
|
507
507
|
await this._echoEdgeReplicator?.connectToSpace(dataSpace.id);
|
|
508
508
|
} else if (this._echoEdgeReplicator) {
|
|
509
509
|
log('not connecting EchoEdgeReplicator because of EdgeReplicationSetting', { spaceId: dataSpace.id });
|
|
@@ -511,7 +511,7 @@ export class DataSpaceManager extends Resource {
|
|
|
511
511
|
});
|
|
512
512
|
dataSpace.preClose.append(async () => {
|
|
513
513
|
const setting = dataSpace.getEdgeReplicationSetting();
|
|
514
|
-
if (setting === EdgeReplicationSetting.ENABLED) {
|
|
514
|
+
if (!setting || setting === EdgeReplicationSetting.ENABLED) {
|
|
515
515
|
await this._echoEdgeReplicator?.disconnectFromSpace(dataSpace.id);
|
|
516
516
|
}
|
|
517
517
|
});
|
|
@@ -609,7 +609,7 @@ export class DataSpaceManager extends Resource {
|
|
|
609
609
|
invitationId: invitation.invitationId,
|
|
610
610
|
swarmKey: invitation.swarmKey,
|
|
611
611
|
guestKeypair: invitation.guestKey ? { publicKey: invitation.guestKey } : undefined,
|
|
612
|
-
lifetime: invitation.expiresOn ? invitation.expiresOn.getTime() - Date.now() : undefined,
|
|
612
|
+
lifetime: invitation.expiresOn ? (invitation.expiresOn.getTime() - Date.now()) / 1000 : undefined,
|
|
613
613
|
multiUse: invitation.multiUse,
|
|
614
614
|
delegationCredentialId: credentialId,
|
|
615
615
|
persistent: false,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
type MetadataStore,
|
|
15
15
|
type Space,
|
|
16
16
|
} from '@dxos/echo-pipeline';
|
|
17
|
-
import { SpaceDocVersion } from '@dxos/echo-protocol';
|
|
17
|
+
import { SpaceDocVersion, type SpaceDoc } from '@dxos/echo-protocol';
|
|
18
18
|
import type { EdgeConnection, EdgeHttpClient } from '@dxos/edge-client';
|
|
19
19
|
import { type FeedStore, type FeedWrapper } from '@dxos/feed-store';
|
|
20
20
|
import { failedInvariant } from '@dxos/invariant';
|
|
@@ -446,7 +446,7 @@ export class DataSpace {
|
|
|
446
446
|
private _onNewAutomergeRoot(rootUrl: string) {
|
|
447
447
|
log('loading automerge root doc for space', { space: this.key, rootUrl });
|
|
448
448
|
|
|
449
|
-
const handle = this._echoHost.automergeRepo.find(rootUrl as any);
|
|
449
|
+
const handle = this._echoHost.automergeRepo.find<SpaceDoc>(rootUrl as any);
|
|
450
450
|
|
|
451
451
|
// TODO(dmaretskyi): Make this single-threaded (but doc loading should still be parallel to not block epoch processing).
|
|
452
452
|
queueMicrotask(async () => {
|
|
@@ -117,7 +117,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
async addFeed(feed: FeedWrapper<any>) {
|
|
120
|
-
log
|
|
120
|
+
log('addFeed', { key: feed.key, connected: this._connected, hasConnectionCtx: !!this._connectionCtx });
|
|
121
121
|
this._feeds.set(feed.key, feed);
|
|
122
122
|
|
|
123
123
|
if (this._connected && this._connectionCtx) {
|
|
@@ -143,12 +143,12 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
143
143
|
|
|
144
144
|
private async _sendMessage(message: ProtocolMessage) {
|
|
145
145
|
if (!this._connectionCtx) {
|
|
146
|
-
log
|
|
146
|
+
log('message dropped because connection was disposed');
|
|
147
147
|
return;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
if (message.type === 'data') {
|
|
151
|
-
log
|
|
151
|
+
log('sending blocks', {
|
|
152
152
|
feedKey: message.feedKey,
|
|
153
153
|
blocks: message.blocks.map((b) => b.index),
|
|
154
154
|
});
|
|
@@ -191,7 +191,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
191
191
|
|
|
192
192
|
const logMeta = { localLength: feed.length, remoteLength: message.length, feedKey };
|
|
193
193
|
if (message.length > feed.length) {
|
|
194
|
-
log
|
|
194
|
+
log('requesting missing blocks', logMeta);
|
|
195
195
|
|
|
196
196
|
await this._sendMessage({
|
|
197
197
|
type: 'request',
|
|
@@ -199,7 +199,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
199
199
|
range: { from: feed.length, to: message.length },
|
|
200
200
|
});
|
|
201
201
|
} else if (message.length < feed.length) {
|
|
202
|
-
log
|
|
202
|
+
log('pushing blocks to remote', logMeta);
|
|
203
203
|
|
|
204
204
|
await this._pushBlocks(feed, message.length, feed.length);
|
|
205
205
|
}
|
|
@@ -208,7 +208,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
208
208
|
}
|
|
209
209
|
|
|
210
210
|
case 'data': {
|
|
211
|
-
log
|
|
211
|
+
log('received data', { feed: message.feedKey, blocks: message.blocks.map((b) => b.index) });
|
|
212
212
|
|
|
213
213
|
const feedKey = PublicKey.fromHex(message.feedKey);
|
|
214
214
|
const feed = this._feeds.get(feedKey);
|
|
@@ -229,7 +229,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
private async _pushBlocks(feed: FeedWrapper<any>, from: number, to: number) {
|
|
232
|
-
log
|
|
232
|
+
log('pushing blocks', { feed: feed.key.toHex(), from, to });
|
|
233
233
|
|
|
234
234
|
const blocks: FeedBlock[] = await Promise.all(
|
|
235
235
|
rangeFromTo(from, to).map(async (index) => {
|
|
@@ -255,7 +255,7 @@ export class EdgeFeedReplicator extends Resource {
|
|
|
255
255
|
}
|
|
256
256
|
|
|
257
257
|
private async _integrateBlocks(feed: FeedWrapper<any>, blocks: FeedBlock[]) {
|
|
258
|
-
log
|
|
258
|
+
log('integrating blocks', { feed: feed.key.toHex(), blocks: blocks.length });
|
|
259
259
|
|
|
260
260
|
for (const block of blocks) {
|
|
261
261
|
if (feed.has(block.index)) {
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
import { EventSubscriptions, UpdateScheduler, scheduleTask } from '@dxos/async';
|
|
6
6
|
import { Stream } from '@dxos/codec-protobuf/stream';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
createAdmissionCredentials,
|
|
9
|
+
createDidFromIdentityKey,
|
|
10
|
+
type CredentialProcessor,
|
|
11
|
+
getCredentialAssertion,
|
|
12
|
+
} from '@dxos/credentials';
|
|
8
13
|
import { raise } from '@dxos/debug';
|
|
9
14
|
import { type SpaceManager } from '@dxos/echo-pipeline';
|
|
10
15
|
import { writeMessages } from '@dxos/feed-store';
|
|
@@ -115,7 +120,9 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
115
120
|
ctx,
|
|
116
121
|
async () => {
|
|
117
122
|
const dataSpaceManager = await this._getDataSpaceManager();
|
|
118
|
-
const spaces =
|
|
123
|
+
const spaces = await Promise.all(
|
|
124
|
+
Array.from(dataSpaceManager.spaces.values()).map((space) => this._serializeSpace(space)),
|
|
125
|
+
);
|
|
119
126
|
log('update', () => ({ ids: spaces.map((space) => space.id) }));
|
|
120
127
|
await this._updateMetrics();
|
|
121
128
|
next({ spaces });
|
|
@@ -265,10 +272,10 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
265
272
|
await myIdentity.controlPipeline.writer.write({ credential: { credential } });
|
|
266
273
|
}
|
|
267
274
|
|
|
268
|
-
return { space: this._serializeSpace(dataSpace) };
|
|
275
|
+
return { space: await this._serializeSpace(dataSpace) };
|
|
269
276
|
}
|
|
270
277
|
|
|
271
|
-
private _serializeSpace(space: DataSpace): Space {
|
|
278
|
+
private async _serializeSpace(space: DataSpace): Promise<Space> {
|
|
272
279
|
return {
|
|
273
280
|
id: space.id,
|
|
274
281
|
spaceKey: space.key,
|
|
@@ -291,24 +298,27 @@ export class SpacesServiceImpl implements SpacesService {
|
|
|
291
298
|
|
|
292
299
|
spaceRootUrl: space.databaseRoot?.url,
|
|
293
300
|
},
|
|
294
|
-
members:
|
|
295
|
-
|
|
296
|
-
|
|
301
|
+
members: await Promise.all(
|
|
302
|
+
Array.from(space.inner.spaceState.members.values()).map(async (member) => {
|
|
303
|
+
const peers = space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key));
|
|
304
|
+
const isMe = this._identityManager.identity?.identityKey.equals(member.key);
|
|
297
305
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
306
|
+
if (isMe) {
|
|
307
|
+
peers.push(space.presence.getLocalState());
|
|
308
|
+
}
|
|
301
309
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
310
|
+
return {
|
|
311
|
+
identity: {
|
|
312
|
+
did: await createDidFromIdentityKey(member.key),
|
|
313
|
+
identityKey: member.key,
|
|
314
|
+
profile: member.profile ?? {},
|
|
315
|
+
},
|
|
316
|
+
role: member.role,
|
|
317
|
+
presence: peers.length > 0 ? SpaceMember.PresenceState.ONLINE : SpaceMember.PresenceState.OFFLINE,
|
|
318
|
+
peerStates: peers,
|
|
319
|
+
};
|
|
320
|
+
}),
|
|
321
|
+
),
|
|
312
322
|
creator: space.inner.spaceState.creator?.key,
|
|
313
323
|
cache: space.cache,
|
|
314
324
|
metrics: space.metrics,
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const DXOS_VERSION = "0.7.5-main.
|
|
1
|
+
export const DXOS_VERSION = "0.7.5-main.e94eead";
|