@dxos/client-services 0.1.22 → 0.1.24-next.6b1f434
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-ZPZBLOQW.mjs +4086 -0
- package/dist/lib/browser/chunk-ZPZBLOQW.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +68 -3027
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/packlets/testing/index.mjs +198 -0
- package/dist/lib/browser/packlets/testing/index.mjs.map +7 -0
- package/dist/lib/node/index.cjs +1694 -667
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/packlets/testing/index.cjs +3806 -0
- package/dist/lib/node/packlets/testing/index.cjs.map +7 -0
- package/dist/types/src/packlets/deprecated/profile.d.ts +3 -1
- package/dist/types/src/packlets/deprecated/profile.d.ts.map +1 -1
- package/dist/types/src/packlets/deprecated/space.d.ts.map +1 -1
- package/dist/types/src/packlets/deprecated/system.d.ts +15 -9
- package/dist/types/src/packlets/deprecated/system.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/devtools.d.ts +2 -2
- package/dist/types/src/packlets/devtools/devtools.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/feeds.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/network.d.ts +3 -3
- package/dist/types/src/packlets/devtools/network.d.ts.map +1 -1
- package/dist/types/src/packlets/devtools/spaces.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/authenticator.d.ts +9 -9
- package/dist/types/src/packlets/identity/authenticator.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity-manager.d.ts +8 -2
- package/dist/types/src/packlets/identity/identity-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/identity/identity.d.ts +5 -4
- package/dist/types/src/packlets/identity/identity.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts +11 -3
- package/dist/types/src/packlets/invitations/halo-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts +1 -1
- package/dist/types/src/packlets/invitations/invitations.d.ts.map +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts +1 -1
- package/dist/types/src/packlets/invitations/space-invitations-handler.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.d.ts +3 -1
- package/dist/types/src/packlets/services/service-context.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-context.test.d.ts +2 -0
- package/dist/types/src/packlets/services/service-context.test.d.ts.map +1 -0
- package/dist/types/src/packlets/services/service-definitions.d.ts +1 -1
- package/dist/types/src/packlets/services/service-definitions.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-host.d.ts +9 -6
- package/dist/types/src/packlets/services/service-host.d.ts.map +1 -1
- package/dist/types/src/packlets/services/service-registry.d.ts +4 -3
- package/dist/types/src/packlets/services/service-registry.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts +9 -2
- package/dist/types/src/packlets/spaces/data-space-manager.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/data-space-manager.test.d.ts +2 -0
- package/dist/types/src/packlets/spaces/data-space-manager.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/data-space.d.ts +20 -4
- package/dist/types/src/packlets/spaces/data-space.d.ts.map +1 -1
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts +50 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts +2 -0
- package/dist/types/src/packlets/spaces/notarization-plugin.test.d.ts.map +1 -0
- package/dist/types/src/packlets/spaces/spaces-service.d.ts +3 -2
- package/dist/types/src/packlets/spaces/spaces-service.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/credential-utils.d.ts +8 -0
- package/dist/types/src/packlets/testing/credential-utils.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/index.d.ts +2 -0
- package/dist/types/src/packlets/testing/index.d.ts.map +1 -1
- package/dist/types/src/packlets/testing/invitation-utils.d.ts +3 -0
- package/dist/types/src/packlets/testing/invitation-utils.d.ts.map +1 -0
- package/dist/types/src/packlets/testing/test-builder.d.ts +31 -2
- package/dist/types/src/packlets/testing/test-builder.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts +35 -0
- package/dist/types/src/packlets/vault/iframe-host-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/{iframe-runtime.d.ts → iframe-proxy-runtime.d.ts} +9 -4
- package/dist/types/src/packlets/vault/iframe-proxy-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/index.d.ts +6 -1
- package/dist/types/src/packlets/vault/index.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/services.d.ts +9 -1
- package/dist/types/src/packlets/vault/services.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/shell-controller.d.ts +24 -0
- package/dist/types/src/packlets/vault/shell-controller.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/shell-runtime.d.ts +45 -0
- package/dist/types/src/packlets/vault/shell-runtime.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/vault-resource-lock.d.ts +19 -0
- package/dist/types/src/packlets/vault/vault-resource-lock.d.ts.map +1 -0
- package/dist/types/src/packlets/vault/worker-runtime.d.ts +2 -1
- package/dist/types/src/packlets/vault/worker-runtime.d.ts.map +1 -1
- package/dist/types/src/packlets/vault/worker-session.d.ts +7 -4
- package/dist/types/src/packlets/vault/worker-session.d.ts.map +1 -1
- package/package.json +30 -29
- package/src/packlets/deprecated/profile.ts +20 -2
- package/src/packlets/deprecated/space.ts +41 -37
- package/src/packlets/deprecated/system.ts +26 -13
- package/src/packlets/devtools/devtools.ts +4 -4
- package/src/packlets/devtools/feeds.ts +11 -6
- package/src/packlets/devtools/network.ts +20 -9
- package/src/packlets/devtools/spaces.ts +8 -7
- package/src/packlets/identity/authenticator.test.ts +4 -4
- package/src/packlets/identity/authenticator.ts +27 -17
- package/src/packlets/identity/identity-manager.test.ts +25 -16
- package/src/packlets/identity/identity-manager.ts +25 -13
- package/src/packlets/identity/identity.test.ts +22 -23
- package/src/packlets/identity/identity.ts +15 -10
- package/src/packlets/invitations/halo-invitations-handler.test.ts +4 -45
- package/src/packlets/invitations/halo-invitations-handler.ts +33 -17
- package/src/packlets/invitations/invitations.ts +2 -1
- package/src/packlets/invitations/space-invitations-handler.test.ts +43 -26
- package/src/packlets/invitations/space-invitations-handler.ts +9 -12
- package/src/packlets/services/service-context.test.ts +56 -0
- package/src/packlets/services/service-context.ts +62 -10
- package/src/packlets/services/service-definitions.ts +1 -1
- package/src/packlets/services/service-host.test.ts +74 -5
- package/src/packlets/services/service-host.ts +86 -24
- package/src/packlets/services/service-registry.test.ts +2 -1
- package/src/packlets/services/service-registry.ts +5 -1
- package/src/packlets/spaces/data-space-manager.test.ts +131 -0
- package/src/packlets/spaces/data-space-manager.ts +61 -15
- package/src/packlets/spaces/data-space.ts +125 -14
- package/src/packlets/spaces/notarization-plugin.test.ts +81 -0
- package/src/packlets/spaces/notarization-plugin.ts +211 -0
- package/src/packlets/spaces/spaces-service.ts +10 -2
- package/src/packlets/testing/credential-utils.ts +28 -0
- package/src/packlets/testing/index.ts +2 -0
- package/src/packlets/testing/invitation-utils.ts +44 -0
- package/src/packlets/testing/test-builder.ts +96 -14
- package/src/packlets/vault/iframe-host-runtime.ts +131 -0
- package/src/packlets/vault/{iframe-runtime.ts → iframe-proxy-runtime.ts} +18 -3
- package/src/packlets/vault/index.ts +6 -1
- package/src/packlets/vault/services.ts +17 -1
- package/src/packlets/vault/shell-controller.ts +77 -0
- package/src/packlets/vault/shell-runtime.ts +133 -0
- package/src/packlets/vault/vault-resource-lock.ts +76 -0
- package/src/packlets/vault/worker-runtime.ts +16 -4
- package/src/packlets/vault/worker-session.ts +36 -26
- package/dist/types/src/packlets/vault/iframe-runtime.d.ts.map +0 -1
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
import assert from 'node:assert';
|
|
6
6
|
|
|
7
|
-
import { EventSubscriptions } from '@dxos/async';
|
|
8
7
|
import { Stream } from '@dxos/codec-protobuf';
|
|
9
8
|
import { todo } from '@dxos/debug';
|
|
10
9
|
import { log } from '@dxos/log';
|
|
@@ -94,29 +93,29 @@ export class SpaceServiceImpl implements SpaceService {
|
|
|
94
93
|
}
|
|
95
94
|
|
|
96
95
|
subscribeSpaces() {
|
|
97
|
-
return new Stream<SubscribeSpacesResponse>(({ next }) => {
|
|
98
|
-
const subscriptions = new EventSubscriptions();
|
|
99
|
-
|
|
96
|
+
return new Stream<SubscribeSpacesResponse>(({ next, ctx }) => {
|
|
100
97
|
const onUpdate = () => {
|
|
101
|
-
const spaces = Array.from(this.serviceContext.dataSpaceManager!.spaces.values())
|
|
102
|
-
(space)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
const spaces = Array.from(this.serviceContext.dataSpaceManager!.spaces.values())
|
|
99
|
+
.filter((space) => this.serviceContext.dataServiceSubscriptions.getDataService(space.key)) // Skip spaces without data service available.
|
|
100
|
+
.map(
|
|
101
|
+
(space): Space => ({
|
|
102
|
+
publicKey: space.key,
|
|
103
|
+
isOpen: true,
|
|
104
|
+
isActive: true,
|
|
105
|
+
members: Array.from(space.inner.spaceState.members.values()).map((member) => ({
|
|
109
106
|
identityKey: member.key,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
107
|
+
profile: {
|
|
108
|
+
identityKey: member.key,
|
|
109
|
+
displayName: member.assertion.profile?.displayName ?? humanize(member.key)
|
|
110
|
+
},
|
|
111
|
+
presence:
|
|
112
|
+
this.serviceContext.identityManager.identity?.identityKey.equals(member.key) ||
|
|
113
|
+
space.presence.getPeersOnline().filter(({ identityKey }) => identityKey.equals(member.key)).length > 0
|
|
114
|
+
? SpaceMember.PresenceState.ONLINE
|
|
115
|
+
: SpaceMember.PresenceState.OFFLINE
|
|
116
|
+
}))
|
|
117
|
+
})
|
|
118
|
+
);
|
|
120
119
|
log('update', { spaces });
|
|
121
120
|
next({ spaces });
|
|
122
121
|
};
|
|
@@ -128,25 +127,27 @@ export class SpaceServiceImpl implements SpaceService {
|
|
|
128
127
|
|
|
129
128
|
await this.serviceContext.initialized.wait();
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
130
|
+
// TODO(dmaretskyi): Create a pattern for subscribing to a set of objects.
|
|
131
|
+
const subscribeSpaces = () => {
|
|
132
|
+
for (const space of this.serviceContext.dataSpaceManager!.spaces.values()) {
|
|
133
|
+
if (!this.serviceContext.dataServiceSubscriptions.getDataService(space.key)) {
|
|
134
|
+
// Skip spaces without data service available.
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
space.stateUpdate.on(ctx, onUpdate);
|
|
139
|
+
space.presence.updated.on(ctx, onUpdate);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
this.serviceContext.dataSpaceManager!.updated.on(ctx, () => {
|
|
144
|
+
subscribeSpaces();
|
|
145
|
+
onUpdate();
|
|
144
146
|
});
|
|
145
147
|
|
|
148
|
+
subscribeSpaces();
|
|
146
149
|
onUpdate();
|
|
147
150
|
});
|
|
148
|
-
|
|
149
|
-
return () => subscriptions.clear();
|
|
150
151
|
});
|
|
151
152
|
}
|
|
152
153
|
|
|
@@ -159,6 +160,9 @@ export class SpaceServiceImpl implements SpaceService {
|
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
async createSpace(): Promise<Space> {
|
|
163
|
+
if (!this.serviceContext.identityManager.identity) {
|
|
164
|
+
throw new Error('This device has no HALO identity available. See https://docs.dxos.org/guide/halo');
|
|
165
|
+
}
|
|
162
166
|
await this.serviceContext.initialized.wait();
|
|
163
167
|
const space = await this.serviceContext.dataSpaceManager!.createSpace();
|
|
164
168
|
return {
|
|
@@ -3,33 +3,46 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Config } from '@dxos/config';
|
|
6
|
-
import { SystemService } from '@dxos/protocols/proto/dxos/client';
|
|
6
|
+
import { StatusResponse, SystemService } from '@dxos/protocols/proto/dxos/client';
|
|
7
|
+
import { MaybePromise } from '@dxos/util';
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
export type SystemServiceOptions = {
|
|
10
|
+
config: Config;
|
|
11
|
+
onInit: () => MaybePromise<void>;
|
|
12
|
+
onStatus: () => MaybePromise<StatusResponse>;
|
|
13
|
+
onReset: () => MaybePromise<void>;
|
|
14
|
+
};
|
|
9
15
|
|
|
10
16
|
/**
|
|
11
17
|
* @deprecated
|
|
12
18
|
*/
|
|
13
19
|
export class SystemServiceImpl implements SystemService {
|
|
14
|
-
|
|
20
|
+
private readonly _config: Config;
|
|
21
|
+
private readonly _onInit: () => MaybePromise<void>;
|
|
22
|
+
private readonly _onStatus: () => MaybePromise<StatusResponse>;
|
|
23
|
+
private readonly _onReset: () => MaybePromise<void>;
|
|
15
24
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
constructor({ config, onInit, onStatus, onReset }: SystemServiceOptions) {
|
|
26
|
+
this._config = config;
|
|
27
|
+
this._onInit = onInit;
|
|
28
|
+
this._onStatus = onStatus;
|
|
29
|
+
this._onReset = onReset;
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
async getConfig(
|
|
32
|
+
async getConfig() {
|
|
22
33
|
return this._config.values;
|
|
23
34
|
}
|
|
24
35
|
|
|
25
36
|
// TODO(burdon): Connect to iframe RPC heartbeat for network status?
|
|
26
|
-
async getStatus(
|
|
27
|
-
return
|
|
28
|
-
|
|
29
|
-
|
|
37
|
+
async getStatus() {
|
|
38
|
+
return this._onStatus();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async initSession() {
|
|
42
|
+
await this._onInit();
|
|
30
43
|
}
|
|
31
44
|
|
|
32
|
-
async reset(
|
|
33
|
-
await this.
|
|
45
|
+
async reset() {
|
|
46
|
+
await this._onReset();
|
|
34
47
|
}
|
|
35
48
|
}
|
|
@@ -33,14 +33,14 @@ import {
|
|
|
33
33
|
SubscribeToSpacesRequest,
|
|
34
34
|
SubscribeToSpacesResponse,
|
|
35
35
|
SubscribeToSignalStatusResponse,
|
|
36
|
-
|
|
36
|
+
SignalResponse,
|
|
37
37
|
SubscribeToSwarmInfoResponse
|
|
38
38
|
} from '@dxos/protocols/proto/dxos/devtools/host';
|
|
39
39
|
|
|
40
40
|
import { ServiceContext } from '../services';
|
|
41
41
|
import { subscribeToFeedBlocks, subscribeToFeeds } from './feeds';
|
|
42
42
|
import { subscribeToKeyringKeys } from './keys';
|
|
43
|
-
import { subscribeToNetworkStatus,
|
|
43
|
+
import { subscribeToNetworkStatus, subscribeToSignal, subscribeToSwarmInfo } from './network';
|
|
44
44
|
import { subscribeToSpaces } from './spaces';
|
|
45
45
|
|
|
46
46
|
export class DevtoolsHostEvents {
|
|
@@ -133,8 +133,8 @@ export class DevtoolsServiceImpl implements DevtoolsHost {
|
|
|
133
133
|
return subscribeToNetworkStatus({ networkManager: this.params.context.networkManager });
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
|
|
137
|
-
return
|
|
136
|
+
subscribeToSignal(): Stream<SignalResponse> {
|
|
137
|
+
return subscribeToSignal({ networkManager: this.params.context.networkManager });
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
subscribeToSwarmInfo(): Stream<SubscribeToSwarmInfoResponse> {
|
|
@@ -18,17 +18,17 @@ import { ComplexMap } from '@dxos/util';
|
|
|
18
18
|
export const subscribeToFeeds = (
|
|
19
19
|
{ feedStore }: { feedStore: FeedStore<FeedMessage> },
|
|
20
20
|
{ feedKeys }: SubscribeToFeedsRequest
|
|
21
|
-
) =>
|
|
22
|
-
new Stream<SubscribeToFeedsResponse>(({ next }) => {
|
|
21
|
+
) => {
|
|
22
|
+
return new Stream<SubscribeToFeedsResponse>(({ next }) => {
|
|
23
23
|
if (feedKeys?.length === 0) {
|
|
24
24
|
return;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
const feedMap = new ComplexMap<PublicKey, FeedWrapper<FeedMessage>>(PublicKey.hash);
|
|
28
27
|
const subscriptions = new EventSubscriptions();
|
|
28
|
+
const feedMap = new ComplexMap<PublicKey, FeedWrapper<FeedMessage>>(PublicKey.hash);
|
|
29
29
|
|
|
30
30
|
const update = () => {
|
|
31
|
-
const feeds = feedStore
|
|
31
|
+
const { feeds } = feedStore;
|
|
32
32
|
feeds
|
|
33
33
|
.filter((feed) => !feedKeys?.length || feedKeys.some((feedKey) => feedKey.equals(feed.key)))
|
|
34
34
|
.forEach((feed) => {
|
|
@@ -38,6 +38,7 @@ export const subscribeToFeeds = (
|
|
|
38
38
|
subscriptions.add(feed.off('close', update));
|
|
39
39
|
}
|
|
40
40
|
});
|
|
41
|
+
|
|
41
42
|
next({
|
|
42
43
|
feeds: Array.from(feedMap.values()).map((feed) => ({ feedKey: feed.key, length: feed.properties.length }))
|
|
43
44
|
});
|
|
@@ -50,12 +51,13 @@ export const subscribeToFeeds = (
|
|
|
50
51
|
subscriptions.clear();
|
|
51
52
|
};
|
|
52
53
|
});
|
|
54
|
+
};
|
|
53
55
|
|
|
54
56
|
export const subscribeToFeedBlocks = (
|
|
55
57
|
{ feedStore }: { feedStore: FeedStore<FeedMessage> },
|
|
56
58
|
{ feedKey, maxBlocks = 10 }: SubscribeToFeedBlocksRequest
|
|
57
|
-
) =>
|
|
58
|
-
new Stream<SubscribeToFeedBlocksResponse>(({ next }) => {
|
|
59
|
+
) => {
|
|
60
|
+
return new Stream<SubscribeToFeedBlocksResponse>(({ next }) => {
|
|
59
61
|
if (!feedKey) {
|
|
60
62
|
return;
|
|
61
63
|
}
|
|
@@ -77,9 +79,11 @@ export const subscribeToFeedBlocks = (
|
|
|
77
79
|
break;
|
|
78
80
|
}
|
|
79
81
|
}
|
|
82
|
+
|
|
80
83
|
next({
|
|
81
84
|
blocks: blocks.slice(-maxBlocks)
|
|
82
85
|
});
|
|
86
|
+
|
|
83
87
|
await iterator.close();
|
|
84
88
|
};
|
|
85
89
|
|
|
@@ -96,3 +100,4 @@ export const subscribeToFeedBlocks = (
|
|
|
96
100
|
clearTimeout(timeout);
|
|
97
101
|
};
|
|
98
102
|
});
|
|
103
|
+
};
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Stream } from '@dxos/codec-protobuf';
|
|
6
|
+
import { Context } from '@dxos/context';
|
|
6
7
|
import { PublicKey } from '@dxos/keys';
|
|
7
|
-
import { CommandTrace } from '@dxos/messaging';
|
|
8
8
|
import { NetworkManager } from '@dxos/network-manager';
|
|
9
9
|
import {
|
|
10
10
|
GetNetworkPeersRequest,
|
|
11
11
|
GetNetworkPeersResponse,
|
|
12
12
|
SubscribeToNetworkTopicsResponse,
|
|
13
13
|
SubscribeToSignalStatusResponse,
|
|
14
|
-
|
|
14
|
+
SignalResponse,
|
|
15
15
|
SubscribeToSwarmInfoResponse
|
|
16
16
|
} from '@dxos/protocols/proto/dxos/devtools/host';
|
|
17
17
|
|
|
@@ -30,14 +30,25 @@ export const subscribeToNetworkStatus = ({ networkManager }: { networkManager: N
|
|
|
30
30
|
update();
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
-
export const
|
|
34
|
-
new Stream<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
export const subscribeToSignal = ({ networkManager }: { networkManager: NetworkManager }) =>
|
|
34
|
+
new Stream<SignalResponse>(({ next }) => {
|
|
35
|
+
const ctx = new Context();
|
|
36
|
+
networkManager.signalManager.onMessage.on(ctx, (message) => {
|
|
37
|
+
next({
|
|
38
|
+
message: {
|
|
39
|
+
author: message.author.asUint8Array(),
|
|
40
|
+
recipient: message.recipient.asUint8Array(),
|
|
41
|
+
payload: message.payload
|
|
42
|
+
},
|
|
43
|
+
receivedAt: new Date()
|
|
44
|
+
});
|
|
40
45
|
});
|
|
46
|
+
networkManager.signalManager.swarmEvent.on(ctx, (swarmEvent) => {
|
|
47
|
+
next({ swarmEvent: swarmEvent.swarmEvent, receivedAt: new Date() });
|
|
48
|
+
});
|
|
49
|
+
return () => {
|
|
50
|
+
return ctx.dispose();
|
|
51
|
+
};
|
|
41
52
|
});
|
|
42
53
|
|
|
43
54
|
export const subscribeToNetworkTopics = ({ networkManager }: { networkManager: NetworkManager }) =>
|
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { Stream } from '@dxos/codec-protobuf';
|
|
6
|
-
import { Space } from '@dxos/echo-
|
|
6
|
+
import { Space } from '@dxos/echo-pipeline';
|
|
7
7
|
import { SubscribeToSpacesRequest, SubscribeToSpacesResponse } from '@dxos/protocols/proto/dxos/devtools/host';
|
|
8
8
|
import { SpaceMetadata } from '@dxos/protocols/proto/dxos/echo/metadata';
|
|
9
9
|
|
|
10
10
|
import { ServiceContext } from '../services';
|
|
11
11
|
|
|
12
|
-
export const subscribeToSpaces = (context: ServiceContext, { spaceKeys = [] }: SubscribeToSpacesRequest) =>
|
|
13
|
-
new Stream<SubscribeToSpacesResponse>(({ next }) => {
|
|
12
|
+
export const subscribeToSpaces = (context: ServiceContext, { spaceKeys = [] }: SubscribeToSpacesRequest) => {
|
|
13
|
+
return new Stream<SubscribeToSpacesResponse>(({ next }) => {
|
|
14
14
|
let unsubscribe: () => void;
|
|
15
15
|
|
|
16
16
|
const update = async () => {
|
|
@@ -20,7 +20,7 @@ export const subscribeToSpaces = (context: ServiceContext, { spaceKeys = [] }: S
|
|
|
20
20
|
);
|
|
21
21
|
|
|
22
22
|
next({
|
|
23
|
-
spaces: filteredSpaces.map((space) => {
|
|
23
|
+
spaces: filteredSpaces.map((space): SubscribeToSpacesResponse.SpaceInfo => {
|
|
24
24
|
const spaceMetadata = context.metadataStore.spaces.find((spaceMetadata: SpaceMetadata) =>
|
|
25
25
|
spaceMetadata.key.equals(space.key)
|
|
26
26
|
);
|
|
@@ -28,10 +28,10 @@ export const subscribeToSpaces = (context: ServiceContext, { spaceKeys = [] }: S
|
|
|
28
28
|
return {
|
|
29
29
|
key: space.key,
|
|
30
30
|
isOpen: space.isOpen,
|
|
31
|
-
timeframe: spaceMetadata?.
|
|
31
|
+
timeframe: spaceMetadata?.dataTimeframe,
|
|
32
32
|
genesisFeed: space.genesisFeedKey,
|
|
33
|
-
controlFeed: space.controlFeedKey
|
|
34
|
-
dataFeed: space.dataFeedKey
|
|
33
|
+
controlFeed: space.controlFeedKey!, // TODO(dmaretskyi): Those keys may be missing.
|
|
34
|
+
dataFeed: space.dataFeedKey!
|
|
35
35
|
};
|
|
36
36
|
})
|
|
37
37
|
});
|
|
@@ -50,3 +50,4 @@ export const subscribeToSpaces = (context: ServiceContext, { spaceKeys = [] }: S
|
|
|
50
50
|
clearTimeout(timeout);
|
|
51
51
|
};
|
|
52
52
|
});
|
|
53
|
+
};
|
|
@@ -12,16 +12,16 @@ import { PublicKey } from '@dxos/keys';
|
|
|
12
12
|
import { describe, test } from '@dxos/test';
|
|
13
13
|
import { ComplexSet } from '@dxos/util';
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { createAuthProvider, TrustedKeySetAuthVerifier } from './authenticator';
|
|
16
16
|
|
|
17
17
|
describe('identity/authenticator', () => {
|
|
18
18
|
test('verifies credentials', async () => {
|
|
19
19
|
const keyring = new Keyring();
|
|
20
20
|
const deviceKey = await keyring.createKey();
|
|
21
21
|
const signer = createCredentialSignerWithKey(keyring, deviceKey);
|
|
22
|
-
const authProvider =
|
|
23
|
-
const authVerifier = new
|
|
24
|
-
|
|
22
|
+
const authProvider = createAuthProvider(signer);
|
|
23
|
+
const authVerifier = new TrustedKeySetAuthVerifier({
|
|
24
|
+
trustedKeysProvider: () => new ComplexSet(PublicKey.hash, [deviceKey]),
|
|
25
25
|
update: new Event(),
|
|
26
26
|
authTimeout: 10
|
|
27
27
|
});
|
|
@@ -5,13 +5,13 @@
|
|
|
5
5
|
import { Event, Trigger } from '@dxos/async';
|
|
6
6
|
import { Context } from '@dxos/context';
|
|
7
7
|
import { verifyCredential, CredentialSigner } from '@dxos/credentials';
|
|
8
|
-
import { AuthProvider, AuthVerifier } from '@dxos/echo-
|
|
8
|
+
import { AuthProvider, AuthVerifier } from '@dxos/echo-pipeline';
|
|
9
9
|
import { PublicKey } from '@dxos/keys';
|
|
10
10
|
import { log } from '@dxos/log';
|
|
11
11
|
import { schema } from '@dxos/protocols';
|
|
12
12
|
import { ComplexSet } from '@dxos/util';
|
|
13
13
|
|
|
14
|
-
export const
|
|
14
|
+
export const createAuthProvider =
|
|
15
15
|
(signer: CredentialSigner): AuthProvider =>
|
|
16
16
|
async (nonce) => {
|
|
17
17
|
const credential = await signer.createCredential({
|
|
@@ -25,8 +25,9 @@ export const createHaloAuthProvider =
|
|
|
25
25
|
return schema.getCodecForType('dxos.halo.credentials.Credential').encode(credential);
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
export type
|
|
29
|
-
|
|
28
|
+
export type TrustedKeySetAuthVerifierParams = {
|
|
29
|
+
// TODO(dmaretskyi): Change to `isTrusted: (key) => bool`.
|
|
30
|
+
trustedKeysProvider: () => ComplexSet<PublicKey>;
|
|
30
31
|
update: Event<void>;
|
|
31
32
|
/**
|
|
32
33
|
* Timeout to wait for the device key to be added to the trusted set.
|
|
@@ -35,26 +36,25 @@ export type HaloAuthVerifierParams = {
|
|
|
35
36
|
};
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
|
-
* Verifies credentials of another
|
|
39
|
-
* Will wait up to `authTimeout` for the
|
|
39
|
+
* Verifies credentials of another member in the space based on a set of trusted key.
|
|
40
|
+
* Will wait up to `authTimeout` for the key to be added to the trusted set.
|
|
40
41
|
*/
|
|
41
|
-
export class
|
|
42
|
+
export class TrustedKeySetAuthVerifier {
|
|
42
43
|
private _ctx = new Context();
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
// prettier-ignore
|
|
46
|
+
constructor(
|
|
47
|
+
private readonly _params: TrustedKeySetAuthVerifierParams
|
|
48
|
+
) {}
|
|
45
49
|
|
|
46
50
|
async close() {
|
|
47
51
|
await this._ctx.dispose();
|
|
48
52
|
}
|
|
49
53
|
|
|
50
|
-
private _isTrustedDevice(deviceKey: PublicKey) {
|
|
51
|
-
const deviceSet = this._params.trustedDevicesProvider();
|
|
52
|
-
return deviceSet.has(deviceKey);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
54
|
get verifier(): AuthVerifier {
|
|
56
55
|
return async (nonce, auth) => {
|
|
57
56
|
const credential = schema.getCodecForType('dxos.halo.credentials.Credential').decode(auth);
|
|
57
|
+
log('authenticating...', { credential });
|
|
58
58
|
|
|
59
59
|
const result = await verifyCredential(credential);
|
|
60
60
|
if (result.kind === 'fail') {
|
|
@@ -67,7 +67,8 @@ export class HaloAuthVerifier {
|
|
|
67
67
|
return false;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
if (this.
|
|
70
|
+
if (this._isTrustedKey(credential.issuer)) {
|
|
71
|
+
log('key is not currently in trusted set, waiting...', { key: credential.issuer });
|
|
71
72
|
return true;
|
|
72
73
|
}
|
|
73
74
|
|
|
@@ -75,14 +76,18 @@ export class HaloAuthVerifier {
|
|
|
75
76
|
this._ctx.onDispose(() => {
|
|
76
77
|
trigger.wake(false);
|
|
77
78
|
});
|
|
79
|
+
|
|
78
80
|
const clear = this._params.update.on(this._ctx, () => {
|
|
79
|
-
if (this.
|
|
81
|
+
if (this._isTrustedKey(credential.issuer)) {
|
|
82
|
+
log('auth success', { key: credential.issuer });
|
|
80
83
|
trigger.wake(true);
|
|
84
|
+
} else {
|
|
85
|
+
log('key is not currently in trusted set, waiting...', { key: credential.issuer });
|
|
81
86
|
}
|
|
82
87
|
});
|
|
88
|
+
|
|
83
89
|
try {
|
|
84
|
-
await trigger.wait({ timeout: this._params.authTimeout });
|
|
85
|
-
return true;
|
|
90
|
+
return await trigger.wait({ timeout: this._params.authTimeout });
|
|
86
91
|
} catch {
|
|
87
92
|
return false;
|
|
88
93
|
} finally {
|
|
@@ -90,4 +95,9 @@ export class HaloAuthVerifier {
|
|
|
90
95
|
}
|
|
91
96
|
};
|
|
92
97
|
}
|
|
98
|
+
|
|
99
|
+
private _isTrustedKey(deviceKey: PublicKey) {
|
|
100
|
+
const deviceSet = this._params.trustedKeysProvider();
|
|
101
|
+
return deviceSet.has(deviceKey);
|
|
102
|
+
}
|
|
93
103
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { expect } from 'chai';
|
|
6
6
|
|
|
7
|
-
import { valueEncoding, MetadataStore, SpaceManager, AuthStatus } from '@dxos/echo-
|
|
7
|
+
import { valueEncoding, MetadataStore, SpaceManager, AuthStatus } from '@dxos/echo-pipeline';
|
|
8
8
|
import { FeedFactory, FeedStore } from '@dxos/feed-store';
|
|
9
9
|
import { Keyring } from '@dxos/keyring';
|
|
10
10
|
import { MemorySignalManager, MemorySignalManagerContext } from '@dxos/messaging';
|
|
@@ -46,11 +46,12 @@ describe('identity/identity-manager', () => {
|
|
|
46
46
|
feedStore,
|
|
47
47
|
networkManager
|
|
48
48
|
});
|
|
49
|
-
const identityManager = new IdentityManager(metadataStore, keyring, spaceManager);
|
|
49
|
+
const identityManager = new IdentityManager(metadataStore, keyring, feedStore, spaceManager);
|
|
50
50
|
|
|
51
51
|
return {
|
|
52
52
|
identityManager,
|
|
53
|
-
feedStore
|
|
53
|
+
feedStore,
|
|
54
|
+
keyring
|
|
54
55
|
};
|
|
55
56
|
};
|
|
56
57
|
|
|
@@ -90,23 +91,31 @@ describe('identity/identity-manager', () => {
|
|
|
90
91
|
|
|
91
92
|
const peer2 = await setupPeer({ signalContext });
|
|
92
93
|
|
|
94
|
+
const deviceKey = await peer2.keyring.createKey();
|
|
95
|
+
const controlFeedKey = await peer2.keyring.createKey();
|
|
96
|
+
const dataFeedKey = await peer2.keyring.createKey();
|
|
97
|
+
|
|
98
|
+
await identity1.controlPipeline.writer.write({
|
|
99
|
+
credential: {
|
|
100
|
+
credential: await identity1.getIdentityCredentialSigner().createCredential({
|
|
101
|
+
subject: deviceKey,
|
|
102
|
+
assertion: {
|
|
103
|
+
'@type': 'dxos.halo.credentials.AuthorizedDevice',
|
|
104
|
+
identityKey: identity1.identityKey,
|
|
105
|
+
deviceKey
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
93
111
|
// Identity2 is not yet ready at this point. Peer1 needs to admit peer2 device key and feed keys.
|
|
94
112
|
const identity2 = await peer2.identityManager.acceptIdentity({
|
|
95
113
|
identityKey: identity1.identityKey,
|
|
114
|
+
deviceKey,
|
|
96
115
|
haloSpaceKey: identity1.haloSpaceKey,
|
|
97
|
-
haloGenesisFeedKey: identity1.haloGenesisFeedKey
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
await identity1.controlPipeline.writer.write({
|
|
101
|
-
'@type': 'dxos.echo.feed.CredentialsMessage',
|
|
102
|
-
credential: await identity1.getIdentityCredentialSigner().createCredential({
|
|
103
|
-
subject: identity2.deviceKey,
|
|
104
|
-
assertion: {
|
|
105
|
-
'@type': 'dxos.halo.credentials.AuthorizedDevice',
|
|
106
|
-
identityKey: identity2.identityKey,
|
|
107
|
-
deviceKey: identity2.deviceKey
|
|
108
|
-
}
|
|
109
|
-
})
|
|
116
|
+
haloGenesisFeedKey: identity1.haloGenesisFeedKey,
|
|
117
|
+
controlFeedKey,
|
|
118
|
+
dataFeedKey
|
|
110
119
|
});
|
|
111
120
|
|
|
112
121
|
// TODO(dmaretskyi): We'd also need to admit device2's feeds otherwise messages from them won't be processed by the pipeline.
|
|
@@ -6,15 +6,17 @@ import assert from 'node:assert';
|
|
|
6
6
|
|
|
7
7
|
import { Event } from '@dxos/async';
|
|
8
8
|
import { createCredentialSignerWithKey, CredentialGenerator } from '@dxos/credentials';
|
|
9
|
-
import { MetadataStore, SpaceManager, SwarmIdentity } from '@dxos/echo-
|
|
9
|
+
import { MetadataStore, SpaceManager, SwarmIdentity } from '@dxos/echo-pipeline';
|
|
10
|
+
import { FeedStore } from '@dxos/feed-store';
|
|
10
11
|
import { Keyring } from '@dxos/keyring';
|
|
11
12
|
import { PublicKey } from '@dxos/keys';
|
|
12
13
|
import { log } from '@dxos/log';
|
|
14
|
+
import { FeedMessage } from '@dxos/protocols/proto/dxos/echo/feed';
|
|
13
15
|
import { AdmittedFeed, IdentityRecord, SpaceRecord } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
14
16
|
import { deferFunction } from '@dxos/util';
|
|
15
17
|
|
|
16
18
|
import { Identity } from '../identity';
|
|
17
|
-
import {
|
|
19
|
+
import { createAuthProvider } from './authenticator';
|
|
18
20
|
|
|
19
21
|
interface ConstructSpaceParams {
|
|
20
22
|
spaceRecord: SpaceRecord;
|
|
@@ -24,8 +26,11 @@ interface ConstructSpaceParams {
|
|
|
24
26
|
|
|
25
27
|
export type JoinIdentityParams = {
|
|
26
28
|
identityKey: PublicKey;
|
|
29
|
+
deviceKey: PublicKey;
|
|
27
30
|
haloSpaceKey: PublicKey;
|
|
28
31
|
haloGenesisFeedKey: PublicKey;
|
|
32
|
+
controlFeedKey: PublicKey;
|
|
33
|
+
dataFeedKey: PublicKey;
|
|
29
34
|
};
|
|
30
35
|
|
|
31
36
|
export type CreateIdentityOptions = {
|
|
@@ -43,6 +48,7 @@ export class IdentityManager {
|
|
|
43
48
|
constructor(
|
|
44
49
|
private readonly _metadataStore: MetadataStore,
|
|
45
50
|
private readonly _keyring: Keyring,
|
|
51
|
+
private readonly _feedStore: FeedStore<FeedMessage>,
|
|
46
52
|
private readonly _spaceManager: SpaceManager
|
|
47
53
|
) {}
|
|
48
54
|
|
|
@@ -113,8 +119,7 @@ export class IdentityManager {
|
|
|
113
119
|
|
|
114
120
|
for (const credential of credentials) {
|
|
115
121
|
await identity.controlPipeline.writer.write({
|
|
116
|
-
|
|
117
|
-
credential
|
|
122
|
+
credential: { credential }
|
|
118
123
|
});
|
|
119
124
|
}
|
|
120
125
|
}
|
|
@@ -141,12 +146,12 @@ export class IdentityManager {
|
|
|
141
146
|
|
|
142
147
|
const identityRecord: IdentityRecord = {
|
|
143
148
|
identityKey: params.identityKey,
|
|
144
|
-
deviceKey:
|
|
149
|
+
deviceKey: params.deviceKey,
|
|
145
150
|
haloSpace: {
|
|
146
151
|
spaceKey: params.haloSpaceKey,
|
|
147
152
|
genesisFeedKey: params.haloGenesisFeedKey,
|
|
148
|
-
writeControlFeedKey:
|
|
149
|
-
writeDataFeedKey:
|
|
153
|
+
writeControlFeedKey: params.controlFeedKey,
|
|
154
|
+
writeDataFeedKey: params.dataFeedKey
|
|
150
155
|
}
|
|
151
156
|
};
|
|
152
157
|
const identity = await this._constructIdentity(identityRecord);
|
|
@@ -154,7 +159,9 @@ export class IdentityManager {
|
|
|
154
159
|
await identity.open();
|
|
155
160
|
this._identity = identity;
|
|
156
161
|
await this._metadataStore.setIdentityRecord(identityRecord);
|
|
162
|
+
await this._identity.ready();
|
|
157
163
|
this.stateUpdate.emit();
|
|
164
|
+
log('accepted identity', { identityKey: identity.identityKey, deviceKey: identity.deviceKey });
|
|
158
165
|
return identity;
|
|
159
166
|
}
|
|
160
167
|
|
|
@@ -162,17 +169,24 @@ export class IdentityManager {
|
|
|
162
169
|
assert(!this._identity);
|
|
163
170
|
log('constructing identity', { identityRecord });
|
|
164
171
|
|
|
172
|
+
// Must be created before the space so the feeds are writable.
|
|
173
|
+
const controlFeed = await this._feedStore.openFeed(identityRecord.haloSpace.writeControlFeedKey, {
|
|
174
|
+
writable: true
|
|
175
|
+
});
|
|
176
|
+
const dataFeed = await this._feedStore.openFeed(identityRecord.haloSpace.writeDataFeedKey, { writable: true });
|
|
177
|
+
|
|
165
178
|
const space = await this._constructSpace({
|
|
166
179
|
spaceRecord: identityRecord.haloSpace,
|
|
167
180
|
swarmIdentity: {
|
|
168
181
|
peerKey: identityRecord.deviceKey,
|
|
169
|
-
credentialProvider:
|
|
170
|
-
createCredentialSignerWithKey(this._keyring, identityRecord.deviceKey)
|
|
171
|
-
),
|
|
182
|
+
credentialProvider: createAuthProvider(createCredentialSignerWithKey(this._keyring, identityRecord.deviceKey)),
|
|
172
183
|
credentialAuthenticator: deferFunction(() => identity.authVerifier.verifier)
|
|
173
184
|
},
|
|
174
185
|
identityKey: identityRecord.identityKey
|
|
175
186
|
});
|
|
187
|
+
space.setControlFeed(controlFeed);
|
|
188
|
+
space.setDataFeed(dataFeed);
|
|
189
|
+
|
|
176
190
|
const identity: Identity = new Identity({
|
|
177
191
|
space,
|
|
178
192
|
signer: this._keyring,
|
|
@@ -188,9 +202,7 @@ export class IdentityManager {
|
|
|
188
202
|
return this._spaceManager.constructSpace({
|
|
189
203
|
metadata: {
|
|
190
204
|
key: spaceRecord.spaceKey,
|
|
191
|
-
genesisFeedKey: spaceRecord.genesisFeedKey
|
|
192
|
-
controlFeedKey: spaceRecord.writeControlFeedKey,
|
|
193
|
-
dataFeedKey: spaceRecord.writeDataFeedKey
|
|
205
|
+
genesisFeedKey: spaceRecord.genesisFeedKey
|
|
194
206
|
},
|
|
195
207
|
swarmIdentity,
|
|
196
208
|
onNetworkConnection: () => {}
|