@dxos/client-services 0.6.13-main.548ca8d → 0.6.13-staging.1e988a3
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-UEQIHAL2.mjs → chunk-4XKSDWEO.mjs} +5218 -5905
- package/dist/lib/browser/chunk-4XKSDWEO.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +3 -3
- package/dist/lib/browser/index.mjs.map +3 -3
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +7 -8
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/node/{chunk-MA5EWTRH.cjs → chunk-HNOOWB6F.cjs} +5060 -5743
- package/dist/lib/node/chunk-HNOOWB6F.cjs.map +7 -0
- package/dist/lib/node/index.cjs +46 -46
- package/dist/lib/node/index.cjs.map +3 -3
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +13 -14
- package/dist/lib/node/testing/index.cjs.map +3 -3
- package/dist/types/src/packlets/diagnostics/diagnostics-broadcast.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.test.d.ts +2 -0
- package/dist/types/src/packlets/identity/authenticator.test.d.ts.map +1 -0
- package/dist/types/src/packlets/identity/contacts-service.d.ts +1 -1
- package/dist/types/src/packlets/identity/contacts-service.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +7 -19
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +1 -8
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts +1 -2
- package/dist/types/src/packlets/invitations/invitation-guest-extenstion.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts +1 -2
- package/dist/types/src/packlets/invitations/invitation-host-extension.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations-handler.d.ts +8 -8
- 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/services/automerge-host.test.d.ts +2 -0
- package/dist/types/src/packlets/services/automerge-host.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-context.d.ts +9 -9
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +0 -1
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +3 -6
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space.d.ts +3 -4
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts +0 -3
- package/dist/types/src/packlets/spaces/edge-feed-replicator.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts +1 -1
- package/dist/types/src/packlets/spaces/epoch-migrations.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +6 -31
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +1 -1
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/storage/storage.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts +2 -1
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/worker/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/version.d.ts +1 -1
- package/dist/types/src/version.d.ts.map +1 -1
- package/package.json +39 -43
- package/src/packlets/devices/devices-service.test.ts +5 -4
- package/src/packlets/diagnostics/diagnostics-broadcast.ts +0 -1
- package/src/packlets/identity/{authenticator.node.test.ts → authenticator.test.ts} +3 -2
- package/src/packlets/identity/authenticator.ts +2 -5
- package/src/packlets/identity/contacts-service.ts +1 -1
- package/src/packlets/identity/identity-manager.test.ts +6 -5
- package/src/packlets/identity/identity-manager.ts +19 -35
- package/src/packlets/identity/identity-service.test.ts +8 -4
- package/src/packlets/identity/identity.test.ts +239 -128
- package/src/packlets/identity/identity.ts +8 -42
- package/src/packlets/invitations/device-invitation-protocol.test.ts +4 -7
- package/src/packlets/invitations/invitation-guest-extenstion.ts +4 -8
- package/src/packlets/invitations/invitation-host-extension.ts +7 -8
- package/src/packlets/invitations/invitations-handler.test.ts +9 -16
- package/src/packlets/invitations/invitations-handler.ts +92 -23
- package/src/packlets/invitations/space-invitation-protocol.test.ts +3 -4
- package/src/packlets/invitations/space-invitation-protocol.ts +0 -4
- package/src/packlets/logging/logging.test.ts +2 -1
- package/src/packlets/network/network-service.test.ts +3 -2
- package/src/packlets/services/automerge-host.test.ts +60 -0
- package/src/packlets/services/service-context.test.ts +1 -3
- package/src/packlets/services/service-context.ts +31 -68
- package/src/packlets/services/service-host.test.ts +12 -8
- package/src/packlets/services/service-host.ts +6 -8
- package/src/packlets/services/service-registry.test.ts +2 -1
- package/src/packlets/spaces/data-space-manager.test.ts +2 -2
- package/src/packlets/spaces/data-space-manager.ts +5 -40
- package/src/packlets/spaces/data-space.ts +6 -34
- package/src/packlets/spaces/edge-feed-replicator.ts +22 -80
- package/src/packlets/spaces/epoch-migrations.ts +2 -2
- package/src/packlets/spaces/notarization-plugin.test.ts +7 -10
- package/src/packlets/spaces/notarization-plugin.ts +29 -169
- package/src/packlets/spaces/spaces-service.test.ts +9 -5
- package/src/packlets/spaces/spaces-service.ts +1 -6
- package/src/packlets/storage/storage.ts +1 -0
- package/src/packlets/system/system-service.test.ts +2 -1
- package/src/packlets/testing/test-builder.ts +4 -3
- package/src/packlets/worker/worker-runtime.ts +2 -2
- package/src/version.ts +1 -1
- package/dist/lib/browser/chunk-UEQIHAL2.mjs.map +0 -7
- package/dist/lib/node/chunk-MA5EWTRH.cjs.map +0 -7
- package/dist/lib/node-esm/chunk-AIBLDI4U.mjs +0 -8403
- package/dist/lib/node-esm/chunk-AIBLDI4U.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -416
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/testing/index.mjs +0 -420
- package/dist/lib/node-esm/testing/index.mjs.map +0 -7
- package/dist/types/src/packlets/identity/authenticator.node.test.d.ts +0 -2
- package/dist/types/src/packlets/identity/authenticator.node.test.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts +0 -30
- package/dist/types/src/packlets/invitations/edge-invitation-handler.d.ts.map +0 -1
- package/dist/types/src/packlets/invitations/invitation-state.d.ts +0 -19
- package/dist/types/src/packlets/invitations/invitation-state.d.ts.map +0 -1
- package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts +0 -2
- package/dist/types/src/packlets/spaces/edge-feed-replicator.test.d.ts.map +0 -1
- package/dist/types/src/testing/setup.d.ts +0 -3
- package/dist/types/src/testing/setup.d.ts.map +0 -1
- package/src/packlets/invitations/edge-invitation-handler.ts +0 -184
- package/src/packlets/invitations/invitation-state.ts +0 -111
- package/src/packlets/spaces/edge-feed-replicator.test.ts +0 -253
- package/src/testing/setup.ts +0 -11
|
@@ -7,7 +7,6 @@ import { Event } from '@dxos/async';
|
|
|
7
7
|
import { Context } from '@dxos/context';
|
|
8
8
|
import { createCredentialSignerWithKey, CredentialGenerator } from '@dxos/credentials';
|
|
9
9
|
import { type MetadataStore, type SpaceManager, type SwarmIdentity } from '@dxos/echo-pipeline';
|
|
10
|
-
import { type EdgeConnection } from '@dxos/edge-client';
|
|
11
10
|
import { type FeedStore } from '@dxos/feed-store';
|
|
12
11
|
import { invariant } from '@dxos/invariant';
|
|
13
12
|
import { type Keyring } from '@dxos/keyring';
|
|
@@ -15,7 +14,6 @@ import { PublicKey } from '@dxos/keys';
|
|
|
15
14
|
import { log } from '@dxos/log';
|
|
16
15
|
import { trace } from '@dxos/protocols';
|
|
17
16
|
import { Device, DeviceKind } from '@dxos/protocols/proto/dxos/client/services';
|
|
18
|
-
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
19
17
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
20
18
|
import { type IdentityRecord, type SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
21
19
|
import {
|
|
@@ -65,20 +63,9 @@ export type CreateIdentityOptions = {
|
|
|
65
63
|
deviceProfile?: DeviceProfileDocument;
|
|
66
64
|
};
|
|
67
65
|
|
|
68
|
-
export type
|
|
69
|
-
onIdentityConstruction?: (identity: Identity) => void;
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
export type IdentityManagerParams = {
|
|
73
|
-
metadataStore: MetadataStore;
|
|
74
|
-
keyring: Keyring;
|
|
75
|
-
feedStore: FeedStore<FeedMessage>;
|
|
76
|
-
spaceManager: SpaceManager;
|
|
77
|
-
edgeConnection?: EdgeConnection;
|
|
78
|
-
edgeFeatures?: Runtime.Client.EdgeFeatures;
|
|
66
|
+
export type IdentityManagerRuntimeParams = {
|
|
79
67
|
devicePresenceAnnounceInterval?: number;
|
|
80
68
|
devicePresenceOfflineTimeout?: number;
|
|
81
|
-
callbacks?: IdentityManagerCallbacks;
|
|
82
69
|
};
|
|
83
70
|
|
|
84
71
|
// TODO(dmaretskyi): Rename: represents the peer's state machine.
|
|
@@ -86,29 +73,28 @@ export type IdentityManagerParams = {
|
|
|
86
73
|
export class IdentityManager {
|
|
87
74
|
readonly stateUpdate = new Event();
|
|
88
75
|
|
|
89
|
-
private
|
|
90
|
-
private readonly _keyring: Keyring;
|
|
91
|
-
private readonly _feedStore: FeedStore<FeedMessage>;
|
|
92
|
-
private readonly _spaceManager: SpaceManager;
|
|
76
|
+
private _identity?: Identity;
|
|
93
77
|
private readonly _devicePresenceAnnounceInterval: number;
|
|
94
78
|
private readonly _devicePresenceOfflineTimeout: number;
|
|
95
|
-
private readonly _edgeConnection: EdgeConnection | undefined;
|
|
96
|
-
private readonly _edgeFeatures: Runtime.Client.EdgeFeatures | undefined;
|
|
97
|
-
private readonly _callbacks: IdentityManagerCallbacks | undefined;
|
|
98
|
-
|
|
99
|
-
private _identity?: Identity;
|
|
100
79
|
|
|
80
|
+
// TODO(burdon): IdentityManagerParams.
|
|
101
81
|
// TODO(dmaretskyi): Perhaps this should take/generate the peerKey outside of an initialized identity.
|
|
102
|
-
constructor(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
82
|
+
constructor(
|
|
83
|
+
private readonly _metadataStore: MetadataStore,
|
|
84
|
+
private readonly _keyring: Keyring,
|
|
85
|
+
private readonly _feedStore: FeedStore<FeedMessage>,
|
|
86
|
+
private readonly _spaceManager: SpaceManager,
|
|
87
|
+
params?: IdentityManagerRuntimeParams,
|
|
88
|
+
private readonly _callbacks?: {
|
|
89
|
+
onIdentityConstruction?: (identity: Identity) => void;
|
|
90
|
+
},
|
|
91
|
+
) {
|
|
92
|
+
const {
|
|
93
|
+
devicePresenceAnnounceInterval = DEVICE_PRESENCE_ANNOUNCE_INTERVAL,
|
|
94
|
+
devicePresenceOfflineTimeout = DEVICE_PRESENCE_OFFLINE_TIMEOUT,
|
|
95
|
+
} = params ?? {};
|
|
96
|
+
this._devicePresenceAnnounceInterval = devicePresenceAnnounceInterval;
|
|
97
|
+
this._devicePresenceOfflineTimeout = devicePresenceOfflineTimeout;
|
|
112
98
|
}
|
|
113
99
|
|
|
114
100
|
get identity() {
|
|
@@ -374,8 +360,6 @@ export class IdentityManager {
|
|
|
374
360
|
signer: this._keyring,
|
|
375
361
|
identityKey: identityRecord.identityKey,
|
|
376
362
|
deviceKey: identityRecord.deviceKey,
|
|
377
|
-
edgeConnection: this._edgeConnection,
|
|
378
|
-
edgeFeatures: this._edgeFeatures,
|
|
379
363
|
});
|
|
380
364
|
log('done', { identityKey: identityRecord.identityKey });
|
|
381
365
|
this._callbacks?.onIdentityConstruction?.(identity);
|
|
@@ -2,17 +2,21 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import chai, { expect } from 'chai';
|
|
6
|
+
import chaiAsPromised from 'chai-as-promised';
|
|
6
7
|
|
|
7
8
|
import { Trigger } from '@dxos/async';
|
|
8
9
|
import { Context } from '@dxos/context';
|
|
9
10
|
import { PublicKey } from '@dxos/keys';
|
|
10
11
|
import { type Identity, type IdentityService } from '@dxos/protocols/proto/dxos/client/services';
|
|
12
|
+
import { afterEach, afterTest, beforeEach, describe, test } from '@dxos/test';
|
|
11
13
|
|
|
12
14
|
import { IdentityServiceImpl } from './identity-service';
|
|
13
15
|
import { type ServiceContext } from '../services';
|
|
14
16
|
import { createServiceContext } from '../testing';
|
|
15
17
|
|
|
18
|
+
chai.use(chaiAsPromised);
|
|
19
|
+
|
|
16
20
|
describe('IdentityService', () => {
|
|
17
21
|
let serviceContext: ServiceContext;
|
|
18
22
|
let identityService: IdentityService;
|
|
@@ -45,7 +49,7 @@ describe('IdentityService', () => {
|
|
|
45
49
|
|
|
46
50
|
test('fails to create identity if one already exists', async () => {
|
|
47
51
|
await identityService.createIdentity({});
|
|
48
|
-
await expect(identityService.createIdentity({})).
|
|
52
|
+
await expect(identityService.createIdentity({})).to.be.rejectedWith('Identity already exists');
|
|
49
53
|
});
|
|
50
54
|
});
|
|
51
55
|
|
|
@@ -68,7 +72,7 @@ describe('IdentityService', () => {
|
|
|
68
72
|
query.subscribe(({ identity }) => {
|
|
69
73
|
result.wake(identity);
|
|
70
74
|
});
|
|
71
|
-
|
|
75
|
+
afterTest(() => query.close());
|
|
72
76
|
expect(await result.wait()).to.be.undefined;
|
|
73
77
|
});
|
|
74
78
|
|
|
@@ -78,7 +82,7 @@ describe('IdentityService', () => {
|
|
|
78
82
|
query.subscribe(({ identity }) => {
|
|
79
83
|
result.wake(identity);
|
|
80
84
|
});
|
|
81
|
-
|
|
85
|
+
afterTest(() => query.close());
|
|
82
86
|
expect(await result.wait()).to.be.undefined;
|
|
83
87
|
|
|
84
88
|
result = new Trigger<Identity | undefined>();
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// Copyright 2022 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import expect from 'expect';
|
|
6
6
|
|
|
7
|
-
import { Event } from '@dxos/async';
|
|
8
7
|
import { Context } from '@dxos/context';
|
|
9
8
|
import { CredentialGenerator, verifyCredential } from '@dxos/credentials';
|
|
10
9
|
import {
|
|
@@ -16,9 +15,7 @@ import {
|
|
|
16
15
|
SpaceProtocol,
|
|
17
16
|
valueEncoding,
|
|
18
17
|
} from '@dxos/echo-pipeline';
|
|
19
|
-
import { type EdgeConnection, type MessageListener } from '@dxos/edge-client';
|
|
20
18
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
21
|
-
import { type FeedWrapper } from '@dxos/feed-store';
|
|
22
19
|
import { Keyring } from '@dxos/keyring';
|
|
23
20
|
import { type PublicKey } from '@dxos/keys';
|
|
24
21
|
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
@@ -27,6 +24,7 @@ import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
|
27
24
|
import { AdmittedFeed } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
28
25
|
import { createStorage, StorageType } from '@dxos/random-access-storage';
|
|
29
26
|
import { BlobStore } from '@dxos/teleport-extension-object-sync';
|
|
27
|
+
import { afterTest, describe, test } from '@dxos/test';
|
|
30
28
|
|
|
31
29
|
import { Identity } from './identity';
|
|
32
30
|
|
|
@@ -44,108 +42,16 @@ const createStores = () => {
|
|
|
44
42
|
|
|
45
43
|
describe('identity/identity', () => {
|
|
46
44
|
test('create', async () => {
|
|
47
|
-
const setup = await setupIdentity();
|
|
48
|
-
|
|
49
|
-
await writeGenesisCredential(setup);
|
|
50
|
-
|
|
51
|
-
// Wait for identity to be ready.
|
|
52
|
-
await setup.identity.ready();
|
|
53
|
-
const identitySigner = setup.identity.getIdentityCredentialSigner();
|
|
54
|
-
const credential = await identitySigner.createCredential({
|
|
55
|
-
subject: setup.identityKey,
|
|
56
|
-
assertion: {
|
|
57
|
-
'@type': 'dxos.halo.credentials.IdentityProfile',
|
|
58
|
-
profile: {
|
|
59
|
-
displayName: 'Alice',
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(credential.issuer).toEqual(setup.identityKey);
|
|
65
|
-
expect(await verifyCredential(credential)).toEqual({ kind: 'pass' });
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('two devices', async () => {
|
|
69
|
-
const signalContext = new MemorySignalManagerContext();
|
|
70
|
-
|
|
71
|
-
const owner = await setupIdentity({ signalContext });
|
|
72
|
-
await writeGenesisCredential(owner);
|
|
73
|
-
await owner.identity.ready();
|
|
74
|
-
|
|
75
|
-
const secondDevice = (
|
|
76
|
-
await setupIdentity({
|
|
77
|
-
signalContext,
|
|
78
|
-
spaceKey: owner.spaceKey,
|
|
79
|
-
identityKey: owner.identityKey,
|
|
80
|
-
genesisFeedKey: owner.controlFeed.key,
|
|
81
|
-
})
|
|
82
|
-
).identity;
|
|
83
|
-
|
|
84
|
-
//
|
|
85
|
-
// Second device admission
|
|
86
|
-
//
|
|
87
|
-
{
|
|
88
|
-
const signer = owner.identity.getIdentityCredentialSigner();
|
|
89
|
-
void owner.identity.controlPipeline.writer.write({
|
|
90
|
-
credential: {
|
|
91
|
-
credential: await signer.createCredential({
|
|
92
|
-
subject: secondDevice.deviceKey,
|
|
93
|
-
assertion: {
|
|
94
|
-
'@type': 'dxos.halo.credentials.AuthorizedDevice',
|
|
95
|
-
identityKey: owner.identityKey,
|
|
96
|
-
deviceKey: secondDevice.deviceKey,
|
|
97
|
-
},
|
|
98
|
-
}),
|
|
99
|
-
},
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
await secondDevice.ready();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
expect(Array.from(owner.identity.authorizedDeviceKeys.keys())).toEqual([owner.deviceKey, secondDevice.deviceKey]);
|
|
106
|
-
expect(Array.from(secondDevice.authorizedDeviceKeys.keys())).toEqual([owner.deviceKey, secondDevice.deviceKey]);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
test('edge feed replicator', async () => {
|
|
110
|
-
let replicationStarted = false;
|
|
111
|
-
const connectedEvent = new Event();
|
|
112
|
-
const setup = await setupIdentity({
|
|
113
|
-
edgeConnection: {
|
|
114
|
-
connected: connectedEvent,
|
|
115
|
-
open: async () => {},
|
|
116
|
-
close: async () => {},
|
|
117
|
-
addListener: (_: MessageListener): (() => void) => {
|
|
118
|
-
return () => {};
|
|
119
|
-
},
|
|
120
|
-
send: async (_) => {
|
|
121
|
-
replicationStarted = true;
|
|
122
|
-
},
|
|
123
|
-
} as EdgeConnection,
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
await writeGenesisCredential(setup);
|
|
127
|
-
connectedEvent.emit();
|
|
128
|
-
|
|
129
|
-
await expect.poll(() => replicationStarted).toBeTruthy();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const setupIdentity = async (args?: {
|
|
133
|
-
signalContext?: MemorySignalManagerContext;
|
|
134
|
-
spaceKey?: PublicKey;
|
|
135
|
-
identityKey?: PublicKey;
|
|
136
|
-
genesisFeedKey?: PublicKey;
|
|
137
|
-
edgeConnection?: EdgeConnection;
|
|
138
|
-
}): Promise<TestIdentitySetup> => {
|
|
139
45
|
const { storage, metadataStore, blobStore } = createStores();
|
|
140
46
|
|
|
141
47
|
const keyring = new Keyring();
|
|
48
|
+
const identityKey = await keyring.createKey();
|
|
142
49
|
const deviceKey = await keyring.createKey();
|
|
143
|
-
const
|
|
144
|
-
const spaceKey = args?.spaceKey ?? (await keyring.createKey());
|
|
50
|
+
const spaceKey = await keyring.createKey();
|
|
145
51
|
|
|
146
52
|
const feedStore = new FeedStore<FeedMessage>({
|
|
147
53
|
factory: new FeedFactory<FeedMessage>({
|
|
148
|
-
root: storage.createDirectory(),
|
|
54
|
+
root: storage.createDirectory('feeds'),
|
|
149
55
|
signer: keyring,
|
|
150
56
|
hypercore: {
|
|
151
57
|
valueEncoding,
|
|
@@ -171,7 +77,7 @@ describe('identity/identity', () => {
|
|
|
171
77
|
},
|
|
172
78
|
blobStore,
|
|
173
79
|
networkManager: new SwarmNetworkManager({
|
|
174
|
-
signalManager: new MemorySignalManager(
|
|
80
|
+
signalManager: new MemorySignalManager(new MemorySignalManagerContext()),
|
|
175
81
|
transportFactory: MemoryTransportFactory,
|
|
176
82
|
}),
|
|
177
83
|
});
|
|
@@ -181,7 +87,7 @@ describe('identity/identity', () => {
|
|
|
181
87
|
id: await createIdFromSpaceKey(spaceKey),
|
|
182
88
|
spaceKey,
|
|
183
89
|
protocol,
|
|
184
|
-
genesisFeed:
|
|
90
|
+
genesisFeed: controlFeed,
|
|
185
91
|
feedProvider: (feedKey) => feedStore.openFeed(feedKey),
|
|
186
92
|
memberKey: identityKey,
|
|
187
93
|
metadataStore,
|
|
@@ -197,37 +103,242 @@ describe('identity/identity', () => {
|
|
|
197
103
|
identityKey,
|
|
198
104
|
deviceKey,
|
|
199
105
|
space,
|
|
200
|
-
edgeFeatures: args?.edgeConnection && { feedReplicator: true },
|
|
201
|
-
edgeConnection: args?.edgeConnection,
|
|
202
106
|
});
|
|
203
107
|
|
|
204
108
|
await identity.open(new Context());
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
109
|
+
afterTest(() => identity.close(new Context()));
|
|
110
|
+
|
|
111
|
+
//
|
|
112
|
+
// Identity genesis
|
|
113
|
+
//
|
|
114
|
+
{
|
|
115
|
+
const generator = new CredentialGenerator(keyring, identityKey, deviceKey);
|
|
116
|
+
const credentials = [
|
|
117
|
+
...(await generator.createSpaceGenesis(spaceKey, controlFeed.key)),
|
|
118
|
+
await generator.createDeviceAuthorization(deviceKey),
|
|
119
|
+
await generator.createFeedAdmission(spaceKey, dataFeed.key, AdmittedFeed.Designation.DATA),
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
for (const credential of credentials) {
|
|
123
|
+
await identity.controlPipeline.writer.write({
|
|
124
|
+
credential: { credential },
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Wait for identity to be ready.
|
|
130
|
+
await identity.ready();
|
|
131
|
+
const identitySigner = identity.getIdentityCredentialSigner();
|
|
132
|
+
const credential = await identitySigner.createCredential({
|
|
133
|
+
subject: identityKey,
|
|
134
|
+
assertion: {
|
|
135
|
+
'@type': 'dxos.halo.credentials.IdentityProfile',
|
|
136
|
+
profile: {
|
|
137
|
+
displayName: 'Alice',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
expect(credential.issuer).toEqual(identityKey);
|
|
143
|
+
expect(await verifyCredential(credential)).toEqual({ kind: 'pass' });
|
|
144
|
+
});
|
|
208
145
|
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
146
|
+
test('two devices', async () => {
|
|
147
|
+
const signalContext = new MemorySignalManagerContext();
|
|
148
|
+
|
|
149
|
+
let spaceKey: PublicKey;
|
|
150
|
+
let identityKey: PublicKey;
|
|
151
|
+
let genesisFeedKey: PublicKey;
|
|
152
|
+
let identity1: Identity;
|
|
153
|
+
let identity2: Identity;
|
|
154
|
+
|
|
155
|
+
//
|
|
156
|
+
// First device
|
|
157
|
+
//
|
|
158
|
+
{
|
|
159
|
+
const { storage, metadataStore, blobStore } = createStores();
|
|
160
|
+
|
|
161
|
+
const keyring = new Keyring();
|
|
162
|
+
identityKey = await keyring.createKey();
|
|
163
|
+
const deviceKey = await keyring.createKey();
|
|
164
|
+
spaceKey = await keyring.createKey();
|
|
165
|
+
|
|
166
|
+
const feedStore = new FeedStore<FeedMessage>({
|
|
167
|
+
factory: new FeedFactory<FeedMessage>({
|
|
168
|
+
root: storage.createDirectory(),
|
|
169
|
+
signer: keyring,
|
|
170
|
+
hypercore: {
|
|
171
|
+
valueEncoding,
|
|
172
|
+
},
|
|
173
|
+
}),
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
const createFeed = async () => {
|
|
177
|
+
const feedKey = await keyring.createKey();
|
|
178
|
+
return feedStore.openFeed(feedKey, { writable: true });
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const controlFeed = await createFeed();
|
|
182
|
+
genesisFeedKey = controlFeed.key;
|
|
183
|
+
|
|
184
|
+
const dataFeed = await createFeed();
|
|
185
|
+
|
|
186
|
+
const protocol = new SpaceProtocol({
|
|
187
|
+
topic: spaceKey,
|
|
188
|
+
swarmIdentity: {
|
|
189
|
+
peerKey: deviceKey,
|
|
190
|
+
identityKey,
|
|
191
|
+
credentialProvider: MOCK_AUTH_PROVIDER, // createHaloAuthProvider(createCredentialSignerWithKey(keyring, device_key)),
|
|
192
|
+
credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
|
|
193
|
+
},
|
|
194
|
+
blobStore,
|
|
195
|
+
networkManager: new SwarmNetworkManager({
|
|
196
|
+
signalManager: new MemorySignalManager(signalContext),
|
|
197
|
+
transportFactory: MemoryTransportFactory,
|
|
198
|
+
}),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await metadataStore.setIdentityRecord({ haloSpace: { key: spaceKey }, identityKey, deviceKey });
|
|
202
|
+
const space = new Space({
|
|
203
|
+
id: await createIdFromSpaceKey(spaceKey),
|
|
204
|
+
spaceKey,
|
|
205
|
+
protocol,
|
|
206
|
+
genesisFeed: controlFeed,
|
|
207
|
+
feedProvider: (feedKey) => feedStore.openFeed(feedKey),
|
|
208
|
+
memberKey: identityKey,
|
|
209
|
+
metadataStore,
|
|
210
|
+
onDelegatedInvitationStatusChange: async () => {},
|
|
211
|
+
onMemberRolesChanged: async () => {},
|
|
220
212
|
});
|
|
213
|
+
await space.setControlFeed(controlFeed);
|
|
214
|
+
await space.setDataFeed(dataFeed);
|
|
215
|
+
|
|
216
|
+
const identity = (identity1 = new Identity({
|
|
217
|
+
signer: keyring,
|
|
218
|
+
identityKey,
|
|
219
|
+
deviceKey,
|
|
220
|
+
space,
|
|
221
|
+
}));
|
|
222
|
+
|
|
223
|
+
await identity.open(new Context());
|
|
224
|
+
afterTest(() => identity.close(new Context()));
|
|
225
|
+
|
|
226
|
+
//
|
|
227
|
+
// Identity genesis
|
|
228
|
+
//
|
|
229
|
+
{
|
|
230
|
+
const generator = new CredentialGenerator(keyring, identityKey, deviceKey);
|
|
231
|
+
const credentials = [
|
|
232
|
+
...(await generator.createSpaceGenesis(spaceKey, controlFeed.key)),
|
|
233
|
+
await generator.createDeviceAuthorization(deviceKey),
|
|
234
|
+
await generator.createFeedAdmission(spaceKey, dataFeed.key, AdmittedFeed.Designation.DATA),
|
|
235
|
+
];
|
|
236
|
+
|
|
237
|
+
for (const credential of credentials) {
|
|
238
|
+
await identity.controlPipeline.writer.write({
|
|
239
|
+
credential: { credential },
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Wait for identity to be ready.
|
|
245
|
+
await identity.ready();
|
|
221
246
|
}
|
|
222
|
-
};
|
|
223
|
-
});
|
|
224
247
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
248
|
+
//
|
|
249
|
+
// Second device
|
|
250
|
+
//
|
|
251
|
+
{
|
|
252
|
+
const { storage, metadataStore, blobStore } = createStores();
|
|
253
|
+
|
|
254
|
+
const keyring = new Keyring();
|
|
255
|
+
const deviceKey = await keyring.createKey();
|
|
256
|
+
|
|
257
|
+
const feedStore = new FeedStore<FeedMessage>({
|
|
258
|
+
factory: new FeedFactory<FeedMessage>({
|
|
259
|
+
root: storage.createDirectory(),
|
|
260
|
+
signer: keyring,
|
|
261
|
+
hypercore: {
|
|
262
|
+
valueEncoding,
|
|
263
|
+
},
|
|
264
|
+
}),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const createFeed = async () => {
|
|
268
|
+
const feedKey = await keyring.createKey();
|
|
269
|
+
return feedStore.openFeed(feedKey, { writable: true });
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const controlFeed = await createFeed();
|
|
273
|
+
const dataFeed = await createFeed();
|
|
274
|
+
|
|
275
|
+
const protocol = new SpaceProtocol({
|
|
276
|
+
topic: spaceKey,
|
|
277
|
+
swarmIdentity: {
|
|
278
|
+
peerKey: deviceKey,
|
|
279
|
+
identityKey,
|
|
280
|
+
credentialProvider: MOCK_AUTH_PROVIDER, // createHaloAuthProvider(createCredentialSignerWithKey(keyring, device_key)),
|
|
281
|
+
credentialAuthenticator: MOCK_AUTH_VERIFIER, // createHaloAuthVerifier(() => identity.authorizedDeviceKeys),
|
|
282
|
+
},
|
|
283
|
+
blobStore,
|
|
284
|
+
networkManager: new SwarmNetworkManager({
|
|
285
|
+
signalManager: new MemorySignalManager(signalContext),
|
|
286
|
+
transportFactory: MemoryTransportFactory,
|
|
287
|
+
}),
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
await metadataStore.setIdentityRecord({
|
|
291
|
+
haloSpace: { key: spaceKey },
|
|
292
|
+
identityKey: identity1.identityKey,
|
|
293
|
+
deviceKey,
|
|
294
|
+
});
|
|
295
|
+
const space = new Space({
|
|
296
|
+
id: await createIdFromSpaceKey(spaceKey),
|
|
297
|
+
spaceKey,
|
|
298
|
+
protocol,
|
|
299
|
+
genesisFeed: await feedStore.openFeed(genesisFeedKey),
|
|
300
|
+
feedProvider: (feedKey) => feedStore.openFeed(feedKey),
|
|
301
|
+
memberKey: identityKey,
|
|
302
|
+
metadataStore,
|
|
303
|
+
onDelegatedInvitationStatusChange: async () => {},
|
|
304
|
+
onMemberRolesChanged: async () => {},
|
|
305
|
+
});
|
|
306
|
+
await space.setControlFeed(controlFeed);
|
|
307
|
+
await space.setDataFeed(dataFeed);
|
|
308
|
+
|
|
309
|
+
const identity = (identity2 = new Identity({
|
|
310
|
+
signer: keyring,
|
|
311
|
+
identityKey: identity1.identityKey,
|
|
312
|
+
deviceKey,
|
|
313
|
+
space,
|
|
314
|
+
}));
|
|
315
|
+
|
|
316
|
+
await identity.open(new Context());
|
|
317
|
+
afterTest(() => identity.close(new Context()));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
//
|
|
321
|
+
// Second device admission
|
|
322
|
+
//
|
|
323
|
+
{
|
|
324
|
+
const signer = identity1.getIdentityCredentialSigner();
|
|
325
|
+
void identity1.controlPipeline.writer.write({
|
|
326
|
+
credential: {
|
|
327
|
+
credential: await signer.createCredential({
|
|
328
|
+
subject: identity2.deviceKey,
|
|
329
|
+
assertion: {
|
|
330
|
+
'@type': 'dxos.halo.credentials.AuthorizedDevice',
|
|
331
|
+
identityKey: identity1.identityKey,
|
|
332
|
+
deviceKey: identity2.deviceKey,
|
|
333
|
+
},
|
|
334
|
+
}),
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
await identity2.ready();
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
expect(Array.from(identity1.authorizedDeviceKeys.keys())).toEqual([identity1.deviceKey, identity2.deviceKey]);
|
|
342
|
+
expect(Array.from(identity2.authorizedDeviceKeys.keys())).toEqual([identity1.deviceKey, identity2.deviceKey]);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
@@ -14,12 +14,10 @@ import {
|
|
|
14
14
|
} from '@dxos/credentials';
|
|
15
15
|
import { type Signer } from '@dxos/crypto';
|
|
16
16
|
import { type Space } from '@dxos/echo-pipeline';
|
|
17
|
-
import {
|
|
18
|
-
import { writeMessages, type FeedWrapper } from '@dxos/feed-store';
|
|
17
|
+
import { writeMessages } from '@dxos/feed-store';
|
|
19
18
|
import { invariant } from '@dxos/invariant';
|
|
20
19
|
import { PublicKey, type SpaceId } from '@dxos/keys';
|
|
21
20
|
import { log } from '@dxos/log';
|
|
22
|
-
import { type Runtime } from '@dxos/protocols/proto/dxos/config';
|
|
23
21
|
import { type FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
24
22
|
import {
|
|
25
23
|
AdmittedFeed,
|
|
@@ -34,7 +32,6 @@ import { type ComplexMap, ComplexSet } from '@dxos/util';
|
|
|
34
32
|
|
|
35
33
|
import { TrustedKeySetAuthVerifier } from './authenticator';
|
|
36
34
|
import { DefaultSpaceStateMachine } from './default-space-state-machine';
|
|
37
|
-
import { EdgeFeedReplicator } from '../spaces';
|
|
38
35
|
|
|
39
36
|
export type IdentityParams = {
|
|
40
37
|
identityKey: PublicKey;
|
|
@@ -42,9 +39,6 @@ export type IdentityParams = {
|
|
|
42
39
|
signer: Signer;
|
|
43
40
|
space: Space;
|
|
44
41
|
presence?: Presence;
|
|
45
|
-
|
|
46
|
-
edgeConnection?: EdgeConnection;
|
|
47
|
-
edgeFeatures?: Runtime.Client.EdgeFeatures;
|
|
48
42
|
};
|
|
49
43
|
|
|
50
44
|
/**
|
|
@@ -58,8 +52,6 @@ export class Identity {
|
|
|
58
52
|
private readonly _deviceStateMachine: DeviceStateMachine;
|
|
59
53
|
private readonly _profileStateMachine: ProfileStateMachine;
|
|
60
54
|
private readonly _defaultSpaceStateMachine: DefaultSpaceStateMachine;
|
|
61
|
-
private readonly _edgeFeedReplicator?: EdgeFeedReplicator = undefined;
|
|
62
|
-
|
|
63
55
|
public readonly authVerifier: TrustedKeySetAuthVerifier;
|
|
64
56
|
|
|
65
57
|
public readonly identityKey: PublicKey;
|
|
@@ -67,15 +59,15 @@ export class Identity {
|
|
|
67
59
|
|
|
68
60
|
public readonly stateUpdate = new Event();
|
|
69
61
|
|
|
70
|
-
constructor(
|
|
71
|
-
this.space =
|
|
72
|
-
this._signer =
|
|
73
|
-
this._presence =
|
|
62
|
+
constructor({ space, signer, identityKey, deviceKey, presence }: IdentityParams) {
|
|
63
|
+
this.space = space;
|
|
64
|
+
this._signer = signer;
|
|
65
|
+
this._presence = presence;
|
|
74
66
|
|
|
75
|
-
this.identityKey =
|
|
76
|
-
this.deviceKey =
|
|
67
|
+
this.identityKey = identityKey;
|
|
68
|
+
this.deviceKey = deviceKey;
|
|
77
69
|
|
|
78
|
-
log.trace('dxos.halo.device', { deviceKey
|
|
70
|
+
log.trace('dxos.halo.device', { deviceKey });
|
|
79
71
|
|
|
80
72
|
this._deviceStateMachine = new DeviceStateMachine({
|
|
81
73
|
identityKey: this.identityKey,
|
|
@@ -96,10 +88,6 @@ export class Identity {
|
|
|
96
88
|
update: this.stateUpdate,
|
|
97
89
|
authTimeout: AUTH_TIMEOUT,
|
|
98
90
|
});
|
|
99
|
-
|
|
100
|
-
if (params.edgeConnection && params.edgeFeatures?.feedReplicator) {
|
|
101
|
-
this._edgeFeedReplicator = new EdgeFeedReplicator({ messenger: params.edgeConnection, spaceId: this.space.id });
|
|
102
|
-
}
|
|
103
91
|
}
|
|
104
92
|
|
|
105
93
|
// TODO(burdon): Expose state object?
|
|
@@ -117,14 +105,7 @@ export class Identity {
|
|
|
117
105
|
await this.space.spaceState.addCredentialProcessor(this._deviceStateMachine);
|
|
118
106
|
await this.space.spaceState.addCredentialProcessor(this._profileStateMachine);
|
|
119
107
|
await this.space.spaceState.addCredentialProcessor(this._defaultSpaceStateMachine);
|
|
120
|
-
|
|
121
|
-
if (this._edgeFeedReplicator) {
|
|
122
|
-
this.space.protocol.feedAdded.append(this._onFeedAdded);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
108
|
await this.space.open(ctx);
|
|
126
|
-
|
|
127
|
-
await this._edgeFeedReplicator?.open();
|
|
128
109
|
}
|
|
129
110
|
|
|
130
111
|
@trace.span()
|
|
@@ -134,13 +115,6 @@ export class Identity {
|
|
|
134
115
|
await this.space.spaceState.removeCredentialProcessor(this._defaultSpaceStateMachine);
|
|
135
116
|
await this.space.spaceState.removeCredentialProcessor(this._profileStateMachine);
|
|
136
117
|
await this.space.spaceState.removeCredentialProcessor(this._deviceStateMachine);
|
|
137
|
-
|
|
138
|
-
if (this._edgeFeedReplicator) {
|
|
139
|
-
this.space.protocol.feedAdded.remove(this._onFeedAdded);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
await this._edgeFeedReplicator?.close();
|
|
143
|
-
|
|
144
118
|
await this.space.close();
|
|
145
119
|
}
|
|
146
120
|
|
|
@@ -177,10 +151,6 @@ export class Identity {
|
|
|
177
151
|
return this._presence;
|
|
178
152
|
}
|
|
179
153
|
|
|
180
|
-
get signer() {
|
|
181
|
-
return this._signer;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
154
|
/**
|
|
185
155
|
* Issues credentials as identity.
|
|
186
156
|
* Requires identity to be ready.
|
|
@@ -253,8 +223,4 @@ export class Identity {
|
|
|
253
223
|
].map((credential): FeedMessage.Payload => ({ credential: { credential } })),
|
|
254
224
|
);
|
|
255
225
|
}
|
|
256
|
-
|
|
257
|
-
private _onFeedAdded = async (feed: FeedWrapper<any>) => {
|
|
258
|
-
await this._edgeFeedReplicator!.addFeed(feed);
|
|
259
|
-
};
|
|
260
226
|
}
|