@dxos/credentials 0.5.1-next.638da00 → 0.5.1-next.65aaa36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/lib/browser/index.mjs +167 -740
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +144 -713
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/types/src/credentials/credential-factory.d.ts +1 -3
  8. package/dist/types/src/credentials/credential-factory.d.ts.map +1 -1
  9. package/dist/types/src/credentials/credential-generator.d.ts +2 -12
  10. package/dist/types/src/credentials/credential-generator.d.ts.map +1 -1
  11. package/dist/types/src/credentials/verifier.d.ts +1 -1
  12. package/dist/types/src/credentials/verifier.d.ts.map +1 -1
  13. package/dist/types/src/state-machine/member-state-machine.d.ts +12 -17
  14. package/dist/types/src/state-machine/member-state-machine.d.ts.map +1 -1
  15. package/dist/types/src/state-machine/space-state-machine.d.ts +3 -3
  16. package/dist/types/src/state-machine/space-state-machine.d.ts.map +1 -1
  17. package/package.json +16 -16
  18. package/src/credentials/credential-factory.ts +1 -9
  19. package/src/credentials/credential-generator.ts +2 -15
  20. package/src/credentials/verifier.test.ts +0 -25
  21. package/src/credentials/verifier.ts +1 -5
  22. package/src/state-machine/invitation-state-machine.test.ts +1 -1
  23. package/src/state-machine/member-state-machine.ts +53 -111
  24. package/src/state-machine/space-state-machine.test.ts +16 -20
  25. package/src/state-machine/space-state-machine.ts +33 -12
  26. package/dist/types/src/graph/credential-graph.d.ts +0 -152
  27. package/dist/types/src/graph/credential-graph.d.ts.map +0 -1
  28. package/dist/types/src/state-machine/member-state-machine.test.d.ts +0 -2
  29. package/dist/types/src/state-machine/member-state-machine.test.d.ts.map +0 -1
  30. package/src/graph/credential-graph.ts +0 -557
  31. package/src/state-machine/member-state-machine.test.ts +0 -580
@@ -112,9 +112,6 @@ import { PublicKey as PublicKey2 } from "@dxos/keys";
112
112
  import { verifySignature } from "@dxos/crypto";
113
113
  var SIGNATURE_TYPE_ED25519 = "ED25519Signature";
114
114
  var verifyCredential = async (credential) => {
115
- if (credential.parentCredentialIds?.length === 0) {
116
- delete credential.parentCredentialIds;
117
- }
118
115
  if (!credential.issuer.equals(credential.proof.signer)) {
119
116
  if (!credential.proof.chain) {
120
117
  return {
@@ -179,10 +176,10 @@ var verifyChain = async (chain, authority, subject) => {
179
176
 
180
177
  // packages/core/halo/credentials/src/credentials/credential-factory.ts
181
178
  var __dxlog_file = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/credentials/credential-factory.ts";
182
- var createCredential = async ({ signer, issuer, subject, assertion, signingKey, chain, nonce, parentCredentialIds }) => {
179
+ var createCredential = async ({ signer, issuer, subject, assertion, signingKey, chain, nonce }) => {
183
180
  invariant(assertion["@type"], "Invalid assertion.", {
184
181
  F: __dxlog_file,
185
- L: 48,
182
+ L: 45,
186
183
  S: void 0,
187
184
  A: [
188
185
  "assertion['@type']",
@@ -191,7 +188,7 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
191
188
  });
192
189
  invariant(!!signingKey === !!chain, "Chain must be provided if and only if the signing key differs from the issuer.", {
193
190
  F: __dxlog_file,
194
- L: 49,
191
+ L: 46,
195
192
  S: void 0,
196
193
  A: [
197
194
  "!!signingKey === !!chain",
@@ -202,7 +199,7 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
202
199
  const result = await verifyChain(chain, issuer, signingKey);
203
200
  invariant(result.kind === "pass", "Invalid chain.", {
204
201
  F: __dxlog_file,
205
- L: 52,
202
+ L: 49,
206
203
  S: void 0,
207
204
  A: [
208
205
  "result.kind === 'pass'",
@@ -225,9 +222,6 @@ var createCredential = async ({ signer, issuer, subject, assertion, signingKey,
225
222
  nonce
226
223
  }
227
224
  };
228
- if ((parentCredentialIds?.length ?? 0) > 0) {
229
- credential.parentCredentialIds = parentCredentialIds;
230
- }
231
225
  const signedPayload = getCredentialProofPayload(credential);
232
226
  credential.proof.value = await signer.sign(signingKey ?? issuer, signedPayload);
233
227
  if (chain) {
@@ -244,13 +238,12 @@ var createCredentialMessage = (credential) => {
244
238
  };
245
239
  var createCredentialSignerWithKey = (signer, issuer) => ({
246
240
  getIssuer: () => issuer,
247
- createCredential: ({ subject, assertion, nonce, parentCredentialIds }) => createCredential({
241
+ createCredential: ({ subject, assertion, nonce }) => createCredential({
248
242
  signer,
249
243
  issuer,
250
244
  subject,
251
245
  assertion,
252
- nonce,
253
- parentCredentialIds
246
+ nonce
254
247
  })
255
248
  });
256
249
  var createCredentialSignerWithChain = (signer, chain, signingKey) => ({
@@ -318,7 +311,7 @@ var CredentialGenerator = class {
318
311
  assertion: {
319
312
  "@type": "dxos.halo.credentials.SpaceMember",
320
313
  spaceKey,
321
- role: SpaceMember.Role.EDITOR,
314
+ role: SpaceMember.Role.MEMBER,
322
315
  genesisFeedKey
323
316
  }
324
317
  }),
@@ -413,15 +406,14 @@ var createDeviceAuthorization = async (signer, identityKey, deviceKey) => {
413
406
  credential
414
407
  }));
415
408
  };
416
- var createAdmissionCredentials = async (signer, identityKey, spaceKey, genesisFeedKey, role = SpaceMember.Role.ADMIN, membershipChainHeads = [], profile, invitationCredentialId) => {
409
+ var createAdmissionCredentials = async (signer, identityKey, spaceKey, genesisFeedKey, profile, invitationCredentialId) => {
417
410
  const credentials = await Promise.all([
418
411
  await signer.createCredential({
419
412
  subject: identityKey,
420
- parentCredentialIds: membershipChainHeads,
421
413
  assertion: {
422
414
  "@type": "dxos.halo.credentials.SpaceMember",
423
415
  spaceKey,
424
- role,
416
+ role: SpaceMember.Role.ADMIN,
425
417
  profile,
426
418
  genesisFeedKey,
427
419
  invitationCredentialId
@@ -579,10 +571,10 @@ var verifyPresentationSignature = async (presentation, proof) => {
579
571
  // packages/core/halo/credentials/src/state-machine/space-state-machine.ts
580
572
  import { runInContextAsync } from "@dxos/async";
581
573
  import { Context } from "@dxos/context";
582
- import { PublicKey as PublicKey7 } from "@dxos/keys";
583
- import { log as log3 } from "@dxos/log";
584
- import { SpaceMember as SpaceMember3 } from "@dxos/protocols/proto/dxos/halo/credentials";
585
- import { Callback as Callback4, ComplexMap as ComplexMap5, ComplexSet as ComplexSet3 } from "@dxos/util";
574
+ import { PublicKey as PublicKey6 } from "@dxos/keys";
575
+ import { log as log2 } from "@dxos/log";
576
+ import { SpaceMember as SpaceMember2 } from "@dxos/protocols/proto/dxos/halo/credentials";
577
+ import { Callback as Callback4, ComplexMap as ComplexMap4, ComplexSet as ComplexSet2 } from "@dxos/util";
586
578
 
587
579
  // packages/core/halo/credentials/src/state-machine/feed-state-machine.ts
588
580
  import { invariant as invariant2 } from "@dxos/invariant";
@@ -706,587 +698,25 @@ var InvitationStateMachine = class {
706
698
  };
707
699
 
708
700
  // packages/core/halo/credentials/src/state-machine/member-state-machine.ts
709
- import { invariant as invariant4 } from "@dxos/invariant";
710
- import { PublicKey as PublicKey6 } from "@dxos/keys";
711
- import { log as log2 } from "@dxos/log";
712
- import { SpaceMember as SpaceMember2 } from "@dxos/protocols/proto/dxos/halo/credentials";
713
- import { ComplexMap as ComplexMap4 } from "@dxos/util";
714
-
715
- // packages/core/halo/credentials/src/graph/credential-graph.ts
716
701
  import { invariant as invariant3 } from "@dxos/invariant";
717
702
  import { PublicKey as PublicKey5 } from "@dxos/keys";
718
703
  import { log } from "@dxos/log";
719
- import { Callback as Callback3, ComplexMap as ComplexMap3, ComplexSet as ComplexSet2 } from "@dxos/util";
720
- var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/graph/credential-graph.ts";
721
- var CredentialGraph = class {
722
- constructor(_stateHandler) {
723
- this._stateHandler = _stateHandler;
724
- this._vertexIdGenerator = 1;
725
- this._root = {
726
- id: -1,
727
- parents: [],
728
- children: []
729
- };
730
- this._sentinel = {
731
- id: -2,
732
- parents: [],
733
- children: []
734
- };
735
- this._vertexByCredentialId = new ComplexMap3(PublicKey5.hash);
736
- this._subjectToVertex = new ComplexMap3(PublicKey5.hash);
737
- this._subjectToState = new ComplexMap3(PublicKey5.hash);
738
- this.onSubjectStateChanged = new Callback3();
739
- }
740
- getSubjectState(subjectId) {
741
- return this._subjectToState.get(subjectId);
742
- }
743
- getState() {
744
- return this._subjectToState;
745
- }
746
- getLeafIds() {
747
- return this._sentinel.parents.map((v) => v.credential.id);
748
- }
749
- getGlobalStateScope() {
750
- return {
751
- state: this._subjectToVertex
752
- };
753
- }
754
- addVertex(credential, assertion) {
755
- const newVertex = {
756
- id: this._vertexIdGenerator++,
757
- credential,
758
- assertion,
759
- parents: [],
760
- children: []
761
- };
762
- this._vertexByCredentialId.set(credential.id, newVertex);
763
- const parentIds = credential.parentCredentialIds ?? [];
764
- if (parentIds.length === 0) {
765
- this._root.children.push(newVertex);
766
- newVertex.parents.push(this._root);
767
- } else {
768
- for (const parentId of parentIds) {
769
- const parentVertex = this._vertexByCredentialId.get(parentId);
770
- if (parentVertex == null) {
771
- log.error("credential skipped because of the unknown parent", {
772
- credential,
773
- parentId
774
- }, {
775
- F: __dxlog_file3,
776
- L: 72,
777
- S: this,
778
- C: (f, a) => f(...a)
779
- });
780
- continue;
781
- }
782
- parentVertex.children.push(newVertex);
783
- newVertex.parents.push(parentVertex);
784
- this._removeSentinelConnection(parentVertex);
785
- }
786
- }
787
- newVertex.children.push(this._sentinel);
788
- this._sentinel.parents.push(newVertex);
789
- return this._onVertexInserted(newVertex);
790
- }
791
- _removeSentinelConnection(vertex) {
792
- const sentinelIdx = vertex.children.indexOf(this._sentinel);
793
- if (sentinelIdx >= 0) {
794
- vertex.children.splice(sentinelIdx, 1);
795
- const vertexInSentinelIdx = this._sentinel.parents.indexOf(vertex);
796
- invariant3(vertexInSentinelIdx >= 0, void 0, {
797
- F: __dxlog_file3,
798
- L: 90,
799
- S: this,
800
- A: [
801
- "vertexInSentinelIdx >= 0",
802
- ""
803
- ]
804
- });
805
- this._sentinel.parents.splice(vertexInSentinelIdx, 1);
806
- }
807
- }
808
- async _onVertexInserted(newVertex) {
809
- const { credential, assertion } = newVertex;
810
- invariant3(credential, void 0, {
811
- F: __dxlog_file3,
812
- L: 97,
813
- S: this,
814
- A: [
815
- "credential",
816
- ""
817
- ]
818
- });
819
- let changedSubjects = [];
820
- const isUpdateAppliedOnTopOfThePreviousState = this._sentinel.parents.length === 1;
821
- if (isUpdateAppliedOnTopOfThePreviousState) {
822
- const subjectId = credential.subject.id;
823
- if (this._stateHandler.isUpdateAllowed(this.getGlobalStateScope(), credential, assertion)) {
824
- const newSubjectState = this._stateHandler.createState(credential, newVertex.assertion);
825
- const prevSubjectState = this._subjectToState.get(subjectId);
826
- this._subjectToState.set(subjectId, newSubjectState);
827
- this._subjectToVertex.set(subjectId, newVertex);
828
- if (this._stateHandler.hasStateChanged(newSubjectState, prevSubjectState)) {
829
- changedSubjects.push(newSubjectState);
830
- }
831
- }
832
- } else {
833
- changedSubjects = this._recomputeState();
834
- }
835
- if (changedSubjects.length > 0) {
836
- await this.onSubjectStateChanged.callIfSet(changedSubjects);
837
- }
838
- }
839
- /**
840
- * DFS the graph from root to sentinel pausing on merge points (nodes with multiple parents).
841
- * Continue after all paths leading to a merge point converge by merging their states.
842
- * In case of a concurrent update paths are replayed taking into account the state set
843
- * by the winning branch.
844
- */
845
- _recomputeState() {
846
- const pendingPaths = /* @__PURE__ */ new Map();
847
- const paths = [
848
- this._createRootPath()
849
- ];
850
- let lastPath = null;
851
- while (lastPath == null) {
852
- const path = paths.pop();
853
- log("visit vertex", {
854
- id: path.head.id
855
- }, {
856
- F: __dxlog_file3,
857
- L: 132,
858
- S: this,
859
- C: (f, a) => f(...a)
860
- });
861
- this._updatePathState(path);
862
- const convergedPaths = this._handleMergePoint(paths, pendingPaths, path);
863
- if (convergedPaths == null) {
864
- log("waiting for other paths", void 0, {
865
- F: __dxlog_file3,
866
- L: 136,
867
- S: this,
868
- C: (f, a) => f(...a)
869
- });
870
- continue;
871
- }
872
- const mergeResult = this._mergePaths(convergedPaths);
873
- if (mergeResult.type === "replay_required") {
874
- this._replayFailedPaths(paths, pendingPaths, mergeResult, convergedPaths);
875
- continue;
876
- }
877
- const merged = mergeResult.path;
878
- if (merged.head.children.length === 0) {
879
- lastPath = merged;
880
- } else if (merged.head.children.length === 1) {
881
- merged.head = merged.head.children[0];
882
- paths.push(merged);
883
- } else {
884
- this._forkTraversal(paths, merged);
885
- }
886
- }
887
- if (paths.length > 0) {
888
- log.error("traversal finished while there were active paths", {
889
- paths: paths.map((p) => ({
890
- path: toChosenPath(p),
891
- head: p.head.id
892
- }))
893
- }, {
894
- F: __dxlog_file3,
895
- L: 155,
896
- S: this,
897
- C: (f, a) => f(...a)
898
- });
899
- }
900
- return this._setCurrentState(lastPath);
901
- }
902
- _replayFailedPaths(paths, pendingPaths, mergeResult, convergedPaths) {
903
- paths.push(...mergeResult.replay.map((path) => {
904
- const stateOverrides = path.stateOverrides ?? new ComplexMap3(PublicKey5.hash);
905
- mergeResult.stateOverrides.forEach((value, key) => stateOverrides.set(key, value));
906
- return {
907
- ...mergeResult.from,
908
- chosenPath: path.chosenPath,
909
- stateOverrides
910
- };
911
- }));
912
- log("replay paths", () => ({
913
- count: paths.length,
914
- paths: paths.map((path) => ({
915
- from: mergeResult.from.head.id,
916
- path: toChosenPath(path),
917
- overrides: path?.stateOverrides?.mapValues((v) => this._stateHandler.toLogString(v.assertion))
918
- }))
919
- }), {
920
- F: __dxlog_file3,
921
- L: 175,
922
- S: this,
923
- C: (f, a) => f(...a)
924
- });
925
- const clearedPending = convergedPaths.filter((l) => !mergeResult.replay.includes(l));
926
- pendingPaths.set(convergedPaths[0].head.id, clearedPending);
927
- }
928
- _handleMergePoint(paths, pendingPaths, path) {
929
- const pendingList = pendingPaths.get(path.head.id) ?? [];
930
- pendingPaths.set(path.head.id, pendingList);
931
- pendingList.push(path);
932
- if (pendingList.length < path.head.parents.length) {
933
- return null;
934
- }
935
- if (path.head.id === this._sentinel.id && paths.length > 0) {
936
- log("waiting for all the active paths to converge on the sentinel", void 0, {
937
- F: __dxlog_file3,
938
- L: 199,
939
- S: this,
940
- C: (f, a) => f(...a)
941
- });
942
- return null;
943
- }
944
- pendingPaths.delete(path.head.id);
945
- return pendingList;
946
- }
947
- _updatePathState(path) {
948
- const headCredential = path.head.credential;
949
- if (headCredential == null) {
950
- return;
951
- }
952
- const updatedSubject = headCredential.subject.id;
953
- path.credentials.add(headCredential.id);
954
- let isUpdateAllowed = this._stateHandler.isUpdateAllowed(path, headCredential, path.head.assertion);
955
- if (!isUpdateAllowed && path.head.parents[0]?.id === this._root.id) {
956
- const globalState = this.getGlobalStateScope();
957
- isUpdateAllowed = this._stateHandler.isUpdateAllowed(globalState, headCredential, path.head.assertion);
958
- }
959
- if (isUpdateAllowed) {
960
- path.forkChangedSubjects.add(updatedSubject);
961
- path.forkIssuers.add(headCredential.issuer);
962
- path.state.set(updatedSubject, path.head);
963
- log("path state updated", () => ({
964
- subject: updatedSubject,
965
- newState: this._stateHandler.toLogString(path.head.assertion)
966
- }), {
967
- F: __dxlog_file3,
968
- L: 223,
969
- S: this,
970
- C: (f, a) => f(...a)
971
- });
972
- }
973
- }
974
- _forkTraversal(paths, path) {
975
- const replayChoice = path.chosenPath?.[path.head.id];
976
- const choices = replayChoice ?? path.head.children;
977
- for (const choice of choices) {
978
- log("edge traversal", {
979
- from: path.head.id,
980
- to: choice.id
981
- }, {
982
- F: __dxlog_file3,
983
- L: 234,
984
- S: this,
985
- C: (f, a) => f(...a)
986
- });
987
- const fork = {
988
- forkPoint: path,
989
- chosenPath: {
990
- ...path.chosenPath,
991
- [path.head.id]: [
992
- choice
993
- ]
994
- },
995
- head: choice,
996
- credentials: new ComplexSet2(PublicKey5.hash, path.credentials),
997
- state: new ComplexMap3(PublicKey5.hash, [
998
- ...path.state.entries()
999
- ]),
1000
- forkIssuers: new ComplexSet2(PublicKey5.hash),
1001
- forkChangedSubjects: new ComplexSet2(PublicKey5.hash),
1002
- stateOverrides: path.stateOverrides
1003
- };
1004
- paths.push(fork);
1005
- }
1006
- }
1007
- /**
1008
- * Updates the current graph state.
1009
- * @returns changed states.
1010
- */
1011
- _setCurrentState(path) {
1012
- const changedSubjects = [];
1013
- const newStateMap = new ComplexMap3(PublicKey5.hash);
1014
- const newVertexMap = new ComplexMap3(PublicKey5.hash);
1015
- for (const [subjectKey, subjectVertex] of path.state.entries()) {
1016
- const newState = this._stateHandler.createState(subjectVertex.credential, subjectVertex.assertion);
1017
- const prevState = this._subjectToState.get(subjectKey);
1018
- newStateMap.set(subjectKey, newState);
1019
- newVertexMap.set(subjectKey, subjectVertex);
1020
- if (this._stateHandler.hasStateChanged(newState, prevState)) {
1021
- changedSubjects.push(newState);
1022
- }
1023
- }
1024
- this._subjectToState = newStateMap;
1025
- this._subjectToVertex = newVertexMap;
1026
- return changedSubjects;
1027
- }
1028
- /*
1029
- * Walk up all the fork points and return the first one present in all the paths.
1030
- * We use local id to determine vertex position in the graph, because nodes can't
1031
- * be inserted in the middle (between a parent and a child) and ids are monotonically increasing.
1032
- */
1033
- _leastCommonAncestor(paths) {
1034
- const uniqueForkPoints = paths.reduce((acc, path) => {
1035
- let it = path.forkPoint;
1036
- while (it) {
1037
- acc.set(it.head.id, it);
1038
- it = it.forkPoint;
1039
- }
1040
- return acc;
1041
- }, /* @__PURE__ */ new Map());
1042
- let maxId = this._root.id;
1043
- let maxState = null;
1044
- for (const [id, state] of uniqueForkPoints.entries()) {
1045
- const headCredential = state.head.credential;
1046
- if (headCredential != null) {
1047
- const isPointInEveryPath = paths.every((p) => p.credentials.has(headCredential.id));
1048
- if (isPointInEveryPath && id > maxId) {
1049
- maxId = id;
1050
- maxState = state;
1051
- }
1052
- }
1053
- }
1054
- return maxState ?? this._createRootPath();
1055
- }
1056
- /**
1057
- * We might be merging paths where some of them had fork points after the initial forking.
1058
- * We need all the paths to point to the least common fork point and contain all the changes
1059
- * that happened after it.
1060
- */
1061
- _moveUpToForkPoint(forkPoint, path) {
1062
- const isForkPointInPath = path.chosenPath[forkPoint.head.id] == null || path.forkPoint == null;
1063
- if (isForkPointInPath) {
1064
- return path;
1065
- }
1066
- if (forkPoint.head.id === path.forkPoint?.head.id) {
1067
- return path;
1068
- }
1069
- let it = path.forkPoint;
1070
- while (it.head.id !== forkPoint.head.id) {
1071
- it.forkIssuers.forEach((iss) => path.forkIssuers.add(iss));
1072
- it.forkChangedSubjects.forEach((m) => path.forkChangedSubjects.add(m));
1073
- it = it.forkPoint;
1074
- path.forkPoint = it;
1075
- }
1076
- return path;
1077
- }
1078
- _mergePaths(convergedPaths) {
1079
- invariant3(convergedPaths.length >= 1, void 0, {
1080
- F: __dxlog_file3,
1081
- L: 324,
1082
- S: this,
1083
- A: [
1084
- "convergedPaths.length >= 1",
1085
- ""
1086
- ]
1087
- });
1088
- if (convergedPaths.length === 1) {
1089
- return {
1090
- type: "merged",
1091
- path: convergedPaths[0]
1092
- };
1093
- }
1094
- const forkPoint = this._leastCommonAncestor(convergedPaths);
1095
- log("merging paths", () => ({
1096
- forkPointId: forkPoint.head.id,
1097
- pathCount: convergedPaths.length,
1098
- forkPoints: convergedPaths.map((fp) => fp.forkPoint?.head.id)
1099
- }), {
1100
- F: __dxlog_file3,
1101
- L: 329,
1102
- S: this,
1103
- C: (f, a) => f(...a)
1104
- });
1105
- const paths = convergedPaths.map((p) => this._moveUpToForkPoint(forkPoint, p));
1106
- invariant3(forkPoint, void 0, {
1107
- F: __dxlog_file3,
1108
- L: 335,
1109
- S: this,
1110
- A: [
1111
- "forkPoint",
1112
- ""
1113
- ]
1114
- });
1115
- const result = {
1116
- forkPoint: forkPoint.forkPoint,
1117
- chosenPath: {
1118
- ...forkPoint.chosenPath,
1119
- [forkPoint.head.id]: []
1120
- },
1121
- stateOverrides: forkPoint.stateOverrides,
1122
- credentials: new ComplexSet2(PublicKey5.hash, forkPoint.credentials),
1123
- forkIssuers: new ComplexSet2(PublicKey5.hash, forkPoint.forkIssuers),
1124
- forkChangedSubjects: new ComplexSet2(PublicKey5.hash, forkPoint.forkChangedSubjects),
1125
- state: forkPoint.state.mapValues((v) => v),
1126
- head: paths[0].head
1127
- };
1128
- const subjectToBranch = new ComplexMap3(PublicKey5.hash);
1129
- for (const path of paths) {
1130
- log("processing a path", () => ({
1131
- choices: toChosenPath(path),
1132
- modified: path.forkChangedSubjects,
1133
- forkIssuers: path.forkIssuers,
1134
- state: path.state.mapValues((v) => this._stateHandler.toLogString(v.assertion))
1135
- }), {
1136
- F: __dxlog_file3,
1137
- L: 348,
1138
- S: this,
1139
- C: (f, a) => f(...a)
1140
- });
1141
- path.forkIssuers.forEach((iss) => result.forkIssuers.add(iss));
1142
- path.credentials.forEach((cred) => result.credentials.add(cred));
1143
- result.chosenPath[forkPoint.head.id].push(...path.chosenPath[forkPoint.head.id] ?? []);
1144
- for (const modifiedSubject of path.forkChangedSubjects) {
1145
- const existingBranch = subjectToBranch.get(modifiedSubject);
1146
- if (existingBranch == null || this._shouldOverrideCredential(existingBranch, path, modifiedSubject)) {
1147
- subjectToBranch.set(modifiedSubject, path);
1148
- }
1149
- }
1150
- }
1151
- const replayPaths = /* @__PURE__ */ new Set();
1152
- const addReplayPath = replayPaths.add.bind(replayPaths);
1153
- for (const [subject, branch] of subjectToBranch.entries()) {
1154
- result.forkChangedSubjects.add(subject);
1155
- const vertex = branch.state.get(subject);
1156
- result.state.set(subject, vertex);
1157
- log("set subject state", () => ({
1158
- subject,
1159
- state: this._stateHandler.toLogString(vertex.assertion)
1160
- }), {
1161
- F: __dxlog_file3,
1162
- L: 370,
1163
- S: this,
1164
- C: (f, a) => f(...a)
1165
- });
1166
- const otherPaths = paths.filter((p) => p !== branch);
1167
- this._stateHandler.getConflictingPaths(otherPaths, vertex).forEach(addReplayPath);
1168
- }
1169
- if (replayPaths.size > 0) {
1170
- return {
1171
- type: "replay_required",
1172
- replay: [
1173
- ...replayPaths.values()
1174
- ],
1175
- from: forkPoint,
1176
- stateOverrides: subjectToBranch.mapValues((v, key) => v.state.get(key))
1177
- };
1178
- }
1179
- return {
1180
- type: "merged",
1181
- path: result
1182
- };
1183
- }
1184
- /**
1185
- * A candidate credential is preferred over the existing credential if:
1186
- * 1. It is the merge-point, because it's the last credential that was issued in awareness of all
1187
- * the previously existing ones.
1188
- * 2. A path where candidate was set contains existing credential in it, which means that the candidate
1189
- * was issued after the existing credential by a legitimate issuer.
1190
- * 3. A state-specific logic (_stateHandler) is able to justify using the candidate credential.
1191
- * 4. The path where candidate was set has more issuers than the existing path (longer branch).
1192
- * 5. The issuance time of the candidate is after the issuance time of the existing credential (LWW).
1193
- */
1194
- _shouldOverrideCredential(existing, candidate, modifiedSubject) {
1195
- const candidateVertex = candidate.state.get(modifiedSubject);
1196
- const currentVertex = existing.state.get(modifiedSubject);
1197
- if (candidateVertex.id === currentVertex.id) {
1198
- return false;
1199
- }
1200
- const mergePointId = existing.head.id;
1201
- if (candidateVertex.id === mergePointId || currentVertex.id === mergePointId) {
1202
- log("merge point chosen to break the tie", {
1203
- mergePointId: existing.head.id
1204
- }, {
1205
- F: __dxlog_file3,
1206
- L: 408,
1207
- S: this,
1208
- C: (f, a) => f(...a)
1209
- });
1210
- return mergePointId === candidateVertex.id;
1211
- }
1212
- const candidateCredential = candidateVertex.credential;
1213
- const currentCredential = currentVertex.credential;
1214
- if (existing.credentials.has(candidateCredential.id) !== candidate.credentials.has(currentCredential.id)) {
1215
- log("one of the credentials was overridden in another branch", {
1216
- current: currentVertex.id,
1217
- candidate: candidateVertex.id
1218
- }, {
1219
- F: __dxlog_file3,
1220
- L: 415,
1221
- S: this,
1222
- C: (f, a) => f(...a)
1223
- });
1224
- return candidate.credentials.has(currentCredential.id);
1225
- }
1226
- const winningCredential = this._stateHandler.tryPickWinningUpdate(existing, currentCredential, candidate, candidateCredential);
1227
- if (winningCredential != null) {
1228
- return winningCredential === candidateCredential;
1229
- }
1230
- if (candidate.forkIssuers.size !== existing.forkIssuers.size) {
1231
- log("longer issuers branch used to break the tie", {
1232
- issuerCount: [
1233
- existing.forkIssuers.size,
1234
- candidate.forkIssuers.size
1235
- ]
1236
- }, {
1237
- F: __dxlog_file3,
1238
- L: 432,
1239
- S: this,
1240
- C: (f, a) => f(...a)
1241
- });
1242
- return candidate.forkIssuers.size > existing.forkIssuers.size;
1243
- }
1244
- log("issuance date used to break the tie", void 0, {
1245
- F: __dxlog_file3,
1246
- L: 437,
1247
- S: this,
1248
- C: (f, a) => f(...a)
1249
- });
1250
- return candidateCredential.issuanceDate.getTime() > currentCredential.issuanceDate.getTime();
1251
- }
1252
- _createRootPath() {
1253
- return {
1254
- head: this._root,
1255
- chosenPath: {},
1256
- forkIssuers: new ComplexSet2(PublicKey5.hash),
1257
- forkChangedSubjects: new ComplexSet2(PublicKey5.hash),
1258
- state: new ComplexMap3(PublicKey5.hash),
1259
- credentials: new ComplexSet2(PublicKey5.hash)
1260
- };
1261
- }
1262
- };
1263
- var toChosenPath = (path) => {
1264
- return Object.fromEntries(Object.entries(path.chosenPath).map(([k, vs]) => [
1265
- k,
1266
- vs.map((v) => v.id)
1267
- ]));
1268
- };
1269
-
1270
- // packages/core/halo/credentials/src/state-machine/member-state-machine.ts
1271
- var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/member-state-machine.ts";
704
+ import { Callback as Callback3, ComplexMap as ComplexMap3 } from "@dxos/util";
705
+ var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/member-state-machine.ts";
1272
706
  var MemberStateMachine = class {
1273
707
  constructor(_spaceKey) {
1274
708
  this._spaceKey = _spaceKey;
1275
- this._memberProfiles = new ComplexMap4(PublicKey6.hash);
1276
- this._hashgraph = new CredentialGraph(this);
1277
- this.onMemberRoleChanged = this._hashgraph.onSubjectStateChanged;
709
+ this._members = new ComplexMap3(PublicKey5.hash);
710
+ this.onMemberAdmitted = new Callback3();
1278
711
  }
1279
712
  get creator() {
1280
- return this._ownerKey && this._hashgraph.getSubjectState(this._ownerKey);
713
+ return this._creator;
1281
714
  }
1282
715
  get members() {
1283
- return this._hashgraph.getState();
1284
- }
1285
- get membershipChainHeads() {
1286
- return this._hashgraph.getLeafIds();
716
+ return this._members;
1287
717
  }
1288
718
  getRole(member) {
1289
- return this._getRole(this._hashgraph.getGlobalStateScope(), member);
719
+ return this._members.get(member)?.assertion.role;
1290
720
  }
1291
721
  /**
1292
722
  * Processes the SpaceMember credential.
@@ -1296,125 +726,87 @@ var MemberStateMachine = class {
1296
726
  const assertion = getCredentialAssertion(credential);
1297
727
  switch (assertion["@type"]) {
1298
728
  case "dxos.halo.credentials.SpaceMember": {
1299
- invariant4(assertion.spaceKey.equals(this._spaceKey), void 0, {
1300
- F: __dxlog_file4,
1301
- L: 66,
729
+ invariant3(assertion.spaceKey.equals(this._spaceKey), void 0, {
730
+ F: __dxlog_file3,
731
+ L: 58,
1302
732
  S: this,
1303
733
  A: [
1304
734
  "assertion.spaceKey.equals(this._spaceKey)",
1305
735
  ""
1306
736
  ]
1307
737
  });
1308
- if (this._ownerKey == null && credential.issuer === this._spaceKey) {
1309
- this._ownerKey = credential.subject.id;
738
+ if (this._members.has(credential.subject.id)) {
739
+ return;
1310
740
  }
1311
- if (assertion.profile != null) {
1312
- this._memberProfiles.set(credential.subject.id, assertion.profile);
741
+ const info = {
742
+ key: credential.subject.id,
743
+ credential,
744
+ assertion,
745
+ removed: false,
746
+ profile: assertion.profile
747
+ };
748
+ if (!this._creator) {
749
+ this._creator = info;
1313
750
  }
1314
- await this._hashgraph.addVertex(credential, assertion);
751
+ this._members.set(credential.subject.id, info);
752
+ log("member added", {
753
+ member: credential.subject.id,
754
+ space: this._spaceKey,
755
+ role: assertion.role,
756
+ profile: assertion.profile
757
+ }, {
758
+ F: __dxlog_file3,
759
+ L: 79,
760
+ S: this,
761
+ C: (f, a) => f(...a)
762
+ });
763
+ await this.onMemberAdmitted.callIfSet(info);
1315
764
  break;
1316
765
  }
1317
766
  case "dxos.halo.credentials.MemberProfile": {
1318
- const member = this._hashgraph.getSubjectState(credential.subject.id);
1319
- if (member) {
1320
- member.profile = assertion.profile;
1321
- } else {
1322
- log2.warn("Member not found", {
767
+ const member = this._members.get(credential.subject.id);
768
+ if (!member) {
769
+ log.warn("Member not found", {
1323
770
  id: credential.subject.id
1324
771
  }, {
1325
- F: __dxlog_file4,
1326
- L: 81,
772
+ F: __dxlog_file3,
773
+ L: 93,
1327
774
  S: this,
1328
775
  C: (f, a) => f(...a)
1329
776
  });
777
+ } else {
778
+ member.profile = assertion.profile;
1330
779
  }
1331
- this._memberProfiles.set(credential.subject.id, assertion.profile);
1332
780
  break;
1333
781
  }
1334
782
  default:
1335
783
  throw new Error("Invalid assertion type");
1336
784
  }
1337
785
  }
1338
- createState(credential, assertion) {
1339
- const memberKey = credential.subject.id;
1340
- return {
1341
- key: memberKey,
1342
- role: assertion.role,
1343
- credential,
1344
- assertion,
1345
- profile: this._memberProfiles.get(memberKey)
1346
- };
1347
- }
1348
- isUpdateAllowed(scope, credential, assertion) {
1349
- if (assertion.role === SpaceMember2.Role.OWNER) {
1350
- return credential.issuer.equals(this._spaceKey);
1351
- }
1352
- const issuer = credential.issuer;
1353
- const isChangingOwnRole = issuer.equals(credential.subject.id);
1354
- if (isChangingOwnRole) {
1355
- return false;
1356
- }
1357
- if (issuer.equals(assertion.spaceKey)) {
1358
- return true;
1359
- }
1360
- const issuerRole = this._getRole(scope, issuer);
1361
- return issuerRole === SpaceMember2.Role.ADMIN || issuerRole === SpaceMember2.Role.OWNER;
1362
- }
1363
- getConflictingPaths(paths, update) {
1364
- if (update.assertion.role !== SpaceMember2.Role.REMOVED && update.assertion.role !== SpaceMember2.Role.EDITOR) {
1365
- return [];
1366
- }
1367
- const memberId = update.credential.subject.id;
1368
- return paths.filter((p) => p.forkIssuers.has(memberId));
1369
- }
1370
- tryPickWinningUpdate(scope1, update1, scope2, update2) {
1371
- const path1IssuerRole = this._getRole(scope1, update1.issuer);
1372
- const path2IssuerRole = this._getRole(scope2, update2.issuer);
1373
- if (path2IssuerRole === SpaceMember2.Role.OWNER !== (path1IssuerRole === SpaceMember2.Role.OWNER)) {
1374
- log2("owner decision used to break the tie", void 0, {
1375
- F: __dxlog_file4,
1376
- L: 139,
1377
- S: this,
1378
- C: (f, a) => f(...a)
1379
- });
1380
- return path1IssuerRole === SpaceMember2.Role.OWNER ? update1 : update2;
1381
- }
1382
- return null;
1383
- }
1384
- toLogString(assertion) {
1385
- const role = assertion?.role ?? SpaceMember2.Role.REMOVED;
1386
- return Object.entries(SpaceMember2.Role).find(([_, value]) => value === role)[0];
1387
- }
1388
- hasStateChanged(s1, s2) {
1389
- return s1?.role !== s2?.role;
1390
- }
1391
- _getRole(scope, memberId) {
1392
- if (this._ownerKey?.equals(memberId)) {
1393
- return SpaceMember2.Role.OWNER;
1394
- }
1395
- const realRole = scope.state.get(memberId)?.assertion?.role ?? SpaceMember2.Role.REMOVED;
1396
- if (scope.stateOverrides != null) {
1397
- const override = scope.stateOverrides.get(memberId);
1398
- if (override != null) {
1399
- log2("member role overridden in path", () => ({
1400
- headId: scope.head?.id,
1401
- roleOverride: this.toLogString(override.assertion),
1402
- realRole: this.toLogString(scope.state.get(memberId)?.assertion)
1403
- }), {
1404
- F: __dxlog_file4,
1405
- L: 162,
1406
- S: this,
1407
- C: (f, a) => f(...a)
1408
- });
1409
- return override.assertion.role;
786
+ async onRevoked(revoked, revocation) {
787
+ invariant3(revoked.id, void 0, {
788
+ F: __dxlog_file3,
789
+ L: 106,
790
+ S: this,
791
+ A: [
792
+ "revoked.id",
793
+ ""
794
+ ]
795
+ });
796
+ for (const member of this._members.values()) {
797
+ if (member.credential.id?.equals(revoked.id)) {
798
+ member.removed = true;
799
+ break;
1410
800
  }
1411
801
  }
1412
- return realRole;
1413
802
  }
1414
803
  };
1415
804
 
1416
805
  // packages/core/halo/credentials/src/state-machine/space-state-machine.ts
1417
- var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/space-state-machine.ts";
806
+ var __dxlog_file4 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/state-machine/space-state-machine.ts";
807
+ var REVOCABLE_CREDENTIALS = [
808
+ "dxos.halo.credentials.SpaceMember"
809
+ ];
1418
810
  var SpaceStateMachine = class {
1419
811
  constructor(_spaceKey) {
1420
812
  this._spaceKey = _spaceKey;
@@ -1422,11 +814,11 @@ var SpaceStateMachine = class {
1422
814
  this._feeds = new FeedStateMachine(this._spaceKey);
1423
815
  this._invitations = new InvitationStateMachine();
1424
816
  this._credentials = [];
1425
- this._credentialsById = new ComplexMap5(PublicKey7.hash);
1426
- this._processedCredentials = new ComplexSet3(PublicKey7.hash);
817
+ this._credentialsById = new ComplexMap4(PublicKey6.hash);
818
+ this._processedCredentials = new ComplexSet2(PublicKey6.hash);
1427
819
  this._credentialProcessors = [];
1428
820
  this.onCredentialProcessed = new Callback4();
1429
- this.onMemberRoleChanged = this._members.onMemberRoleChanged;
821
+ this.onMemberAdmitted = this._members.onMemberAdmitted;
1430
822
  this.onFeedAdmitted = this._feeds.onFeedAdmitted;
1431
823
  this.onDelegatedInvitation = this._invitations.onDelegatedInvitation;
1432
824
  this.onDelegatedInvitationRemoved = this._invitations.onDelegatedInvitationRemoved;
@@ -1437,9 +829,6 @@ var SpaceStateMachine = class {
1437
829
  get members() {
1438
830
  return this._members.members;
1439
831
  }
1440
- get membershipChainHeads() {
1441
- return this._members.membershipChainHeads;
1442
- }
1443
832
  get feeds() {
1444
833
  return this._feeds.feeds;
1445
834
  }
@@ -1491,9 +880,9 @@ var SpaceStateMachine = class {
1491
880
  if (!skipVerification) {
1492
881
  const result = await verifyCredential(credential);
1493
882
  if (result.kind !== "pass") {
1494
- log3.warn(`Invalid credential: ${result.errors.join(", ")}`, void 0, {
1495
- F: __dxlog_file5,
1496
- L: 152,
883
+ log2.warn(`Invalid credential: ${result.errors.join(", ")}`, void 0, {
884
+ F: __dxlog_file4,
885
+ L: 149,
1497
886
  S: this,
1498
887
  C: (f, a) => f(...a)
1499
888
  });
@@ -1502,29 +891,61 @@ var SpaceStateMachine = class {
1502
891
  }
1503
892
  const assertion = getCredentialAssertion(credential);
1504
893
  switch (assertion["@type"]) {
894
+ case "dxos.halo.credentials.Revocation": {
895
+ if (!this._canRevokeCredentials(credential.issuer)) {
896
+ log2.warn(`Space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
897
+ F: __dxlog_file4,
898
+ L: 158,
899
+ S: this,
900
+ C: (f, a) => f(...a)
901
+ });
902
+ return false;
903
+ }
904
+ const toBeRevoked = this._credentialsById.get(credential.subject.id);
905
+ if (!toBeRevoked) {
906
+ log2.warn(`Credential to revoke not found: ${credential.subject.id}`, void 0, {
907
+ F: __dxlog_file4,
908
+ L: 163,
909
+ S: this,
910
+ C: (f, a) => f(...a)
911
+ });
912
+ return false;
913
+ }
914
+ if (!this._credentialCanBeRevoked(toBeRevoked)) {
915
+ log2.warn(`Credential cannot be revoked: ${credential.subject.id}`, void 0, {
916
+ F: __dxlog_file4,
917
+ L: 167,
918
+ S: this,
919
+ C: (f, a) => f(...a)
920
+ });
921
+ }
922
+ toBeRevoked.revoked = true;
923
+ await this._members.onRevoked(toBeRevoked.credential, credential);
924
+ break;
925
+ }
1505
926
  case "dxos.halo.credentials.SpaceGenesis": {
1506
927
  if (this._genesisCredential) {
1507
- log3.warn("Space already has a genesis credential.", void 0, {
1508
- F: __dxlog_file5,
1509
- L: 161,
928
+ log2.warn("Space already has a genesis credential.", void 0, {
929
+ F: __dxlog_file4,
930
+ L: 176,
1510
931
  S: this,
1511
932
  C: (f, a) => f(...a)
1512
933
  });
1513
934
  return false;
1514
935
  }
1515
936
  if (!credential.issuer.equals(this._spaceKey)) {
1516
- log3.warn("Space genesis credential must be issued by space.", void 0, {
1517
- F: __dxlog_file5,
1518
- L: 165,
937
+ log2.warn("Space genesis credential must be issued by space.", void 0, {
938
+ F: __dxlog_file4,
939
+ L: 180,
1519
940
  S: this,
1520
941
  C: (f, a) => f(...a)
1521
942
  });
1522
943
  return false;
1523
944
  }
1524
945
  if (!credential.subject.id.equals(this._spaceKey)) {
1525
- log3.warn("Space genesis credential must be issued to space.", void 0, {
1526
- F: __dxlog_file5,
1527
- L: 169,
946
+ log2.warn("Space genesis credential must be issued to space.", void 0, {
947
+ F: __dxlog_file4,
948
+ L: 184,
1528
949
  S: this,
1529
950
  C: (f, a) => f(...a)
1530
951
  });
@@ -1538,18 +959,18 @@ var SpaceStateMachine = class {
1538
959
  break;
1539
960
  }
1540
961
  if (!this._genesisCredential) {
1541
- log3.warn("Space must have a genesis credential before adding members.", void 0, {
1542
- F: __dxlog_file5,
1543
- L: 182,
962
+ log2.warn("Space must have a genesis credential before adding members.", void 0, {
963
+ F: __dxlog_file4,
964
+ L: 198,
1544
965
  S: this,
1545
966
  C: (f, a) => f(...a)
1546
967
  });
1547
968
  return false;
1548
969
  }
1549
970
  if (!this._canInviteNewMembers(credential.issuer)) {
1550
- log3.warn(`Space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
1551
- F: __dxlog_file5,
1552
- L: 186,
971
+ log2.warn(`Space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
972
+ F: __dxlog_file4,
973
+ L: 202,
1553
974
  S: this,
1554
975
  C: (f, a) => f(...a)
1555
976
  });
@@ -1561,9 +982,9 @@ var SpaceStateMachine = class {
1561
982
  }
1562
983
  case "dxos.halo.credentials.MemberProfile": {
1563
984
  if (!this._genesisCredential) {
1564
- log3.warn("Space must have a genesis credential before adding members.", void 0, {
1565
- F: __dxlog_file5,
1566
- L: 197,
985
+ log2.warn("Space must have a genesis credential before adding members.", void 0, {
986
+ F: __dxlog_file4,
987
+ L: 213,
1567
988
  S: this,
1568
989
  C: (f, a) => f(...a)
1569
990
  });
@@ -1574,18 +995,18 @@ var SpaceStateMachine = class {
1574
995
  }
1575
996
  case "dxos.halo.credentials.AdmittedFeed": {
1576
997
  if (!this._genesisCredential) {
1577
- log3.warn("Space must have a genesis credential before admitting feeds.", void 0, {
1578
- F: __dxlog_file5,
1579
- L: 207,
998
+ log2.warn("Space must have a genesis credential before admitting feeds.", void 0, {
999
+ F: __dxlog_file4,
1000
+ L: 223,
1580
1001
  S: this,
1581
1002
  C: (f, a) => f(...a)
1582
1003
  });
1583
1004
  return false;
1584
1005
  }
1585
1006
  if (!this._canAdmitFeeds(credential.issuer)) {
1586
- log3.warn(`Space member is not authorized to admit feeds: ${credential.issuer}`, void 0, {
1587
- F: __dxlog_file5,
1588
- L: 211,
1007
+ log2.warn(`Space member is not authorized to admit feeds: ${credential.issuer}`, void 0, {
1008
+ F: __dxlog_file4,
1009
+ L: 227,
1589
1010
  S: this,
1590
1011
  C: (f, a) => f(...a)
1591
1012
  });
@@ -1597,9 +1018,9 @@ var SpaceStateMachine = class {
1597
1018
  case "dxos.halo.invitations.CancelDelegatedInvitation":
1598
1019
  case "dxos.halo.invitations.DelegateSpaceInvitation": {
1599
1020
  if (!this._canInviteNewMembers(credential.issuer)) {
1600
- log3.warn(`Invalid invitation, space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
1601
- F: __dxlog_file5,
1602
- L: 222,
1021
+ log2.warn(`Invalid invitation, space member is not authorized to invite new members: ${credential.issuer}`, void 0, {
1022
+ F: __dxlog_file4,
1023
+ L: 238,
1603
1024
  S: this,
1604
1025
  C: (f, a) => f(...a)
1605
1026
  });
@@ -1627,11 +1048,17 @@ var SpaceStateMachine = class {
1627
1048
  return true;
1628
1049
  }
1629
1050
  _canInviteNewMembers(key) {
1630
- return key.equals(this._spaceKey) || this._members.getRole(key) === SpaceMember3.Role.ADMIN || this._members.getRole(key) === SpaceMember3.Role.OWNER;
1051
+ return key.equals(this._spaceKey) || this._members.getRole(key) === SpaceMember2.Role.ADMIN;
1631
1052
  }
1632
1053
  _canAdmitFeeds(key) {
1633
1054
  const role = this._members.getRole(key);
1634
- return role === SpaceMember3.Role.EDITOR || role === SpaceMember3.Role.ADMIN || role === SpaceMember3.Role.OWNER;
1055
+ return role === SpaceMember2.Role.MEMBER || role === SpaceMember2.Role.ADMIN;
1056
+ }
1057
+ _canRevokeCredentials(key) {
1058
+ return key.equals(this._spaceKey) || this._members.getRole(key) === SpaceMember2.Role.ADMIN;
1059
+ }
1060
+ _credentialCanBeRevoked(entry) {
1061
+ return REVOCABLE_CREDENTIALS.includes(entry.credential.subject.assertion["@type"]);
1635
1062
  }
1636
1063
  };
1637
1064
  var CredentialConsumer = class {
@@ -1664,24 +1091,24 @@ var CredentialConsumer = class {
1664
1091
 
1665
1092
  // packages/core/halo/credentials/src/processor/device-state-machine.ts
1666
1093
  import { Trigger } from "@dxos/async";
1667
- import { invariant as invariant5 } from "@dxos/invariant";
1668
- import { PublicKey as PublicKey8 } from "@dxos/keys";
1669
- import { log as log4 } from "@dxos/log";
1670
- import { ComplexMap as ComplexMap6 } from "@dxos/util";
1671
- var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/device-state-machine.ts";
1094
+ import { invariant as invariant4 } from "@dxos/invariant";
1095
+ import { PublicKey as PublicKey7 } from "@dxos/keys";
1096
+ import { log as log3 } from "@dxos/log";
1097
+ import { ComplexMap as ComplexMap5 } from "@dxos/util";
1098
+ var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/device-state-machine.ts";
1672
1099
  var DeviceStateMachine = class {
1673
1100
  constructor(_params) {
1674
1101
  this._params = _params;
1675
- this.authorizedDeviceKeys = new ComplexMap6(PublicKey8.hash);
1102
+ this.authorizedDeviceKeys = new ComplexMap5(PublicKey7.hash);
1676
1103
  this.deviceChainReady = new Trigger();
1677
1104
  }
1678
1105
  async processCredential(credential) {
1679
- log4("processing credential...", {
1106
+ log3("processing credential...", {
1680
1107
  identityKey: this._params.identityKey,
1681
1108
  deviceKey: this._params.deviceKey,
1682
1109
  credential
1683
1110
  }, {
1684
- F: __dxlog_file6,
1111
+ F: __dxlog_file5,
1685
1112
  L: 35,
1686
1113
  S: this,
1687
1114
  C: (f, a) => f(...a)
@@ -1695,8 +1122,8 @@ var DeviceStateMachine = class {
1695
1122
  const assertion = getCredentialAssertion(credential);
1696
1123
  switch (assertion["@type"]) {
1697
1124
  case "dxos.halo.credentials.AuthorizedDevice": {
1698
- invariant5(!this.authorizedDeviceKeys.has(assertion.deviceKey), "Device already added.", {
1699
- F: __dxlog_file6,
1125
+ invariant4(!this.authorizedDeviceKeys.has(assertion.deviceKey), "Device already added.", {
1126
+ F: __dxlog_file5,
1700
1127
  L: 51,
1701
1128
  S: this,
1702
1129
  A: [
@@ -1705,12 +1132,12 @@ var DeviceStateMachine = class {
1705
1132
  ]
1706
1133
  });
1707
1134
  this.authorizedDeviceKeys.set(assertion.deviceKey, {});
1708
- log4("added device", {
1135
+ log3("added device", {
1709
1136
  localDeviceKey: this._params.deviceKey,
1710
1137
  deviceKey: assertion.deviceKey,
1711
1138
  size: this.authorizedDeviceKeys.size
1712
1139
  }, {
1713
- F: __dxlog_file6,
1140
+ F: __dxlog_file5,
1714
1141
  L: 54,
1715
1142
  S: this,
1716
1143
  C: (f, a) => f(...a)
@@ -1719,8 +1146,8 @@ var DeviceStateMachine = class {
1719
1146
  break;
1720
1147
  }
1721
1148
  case "dxos.halo.credentials.DeviceProfile": {
1722
- invariant5(this.authorizedDeviceKeys.has(credential.subject.id), "Device not found.", {
1723
- F: __dxlog_file6,
1149
+ invariant4(this.authorizedDeviceKeys.has(credential.subject.id), "Device not found.", {
1150
+ F: __dxlog_file5,
1724
1151
  L: 64,
1725
1152
  S: this,
1726
1153
  A: [
@@ -1729,11 +1156,11 @@ var DeviceStateMachine = class {
1729
1156
  ]
1730
1157
  });
1731
1158
  if (assertion && credential.subject.id.equals(this._params.deviceKey)) {
1732
- log4.trace("dxos.halo.device", {
1159
+ log3.trace("dxos.halo.device", {
1733
1160
  deviceKey: credential.subject.id,
1734
1161
  profile: assertion.profile
1735
1162
  }, {
1736
- F: __dxlog_file6,
1163
+ F: __dxlog_file5,
1737
1164
  L: 67,
1738
1165
  S: this,
1739
1166
  C: (f, a) => f(...a)
@@ -1748,8 +1175,8 @@ var DeviceStateMachine = class {
1748
1175
  };
1749
1176
 
1750
1177
  // packages/core/halo/credentials/src/processor/profile-state-machine.ts
1751
- import { log as log5 } from "@dxos/log";
1752
- var __dxlog_file7 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/profile-state-machine.ts";
1178
+ import { log as log4 } from "@dxos/log";
1179
+ var __dxlog_file6 = "/home/runner/work/dxos/dxos/packages/core/halo/credentials/src/processor/profile-state-machine.ts";
1753
1180
  var ProfileStateMachine = class {
1754
1181
  constructor(_params) {
1755
1182
  this._params = _params;
@@ -1759,11 +1186,11 @@ var ProfileStateMachine = class {
1759
1186
  switch (assertion["@type"]) {
1760
1187
  case "dxos.halo.credentials.IdentityProfile": {
1761
1188
  if (!credential.issuer.equals(this._params.identityKey) || !credential.subject.id.equals(this._params.identityKey)) {
1762
- log5.warn("Invalid profile credential", {
1189
+ log4.warn("Invalid profile credential", {
1763
1190
  expectedIdentity: this._params.identityKey,
1764
1191
  credential
1765
1192
  }, {
1766
- F: __dxlog_file7,
1193
+ F: __dxlog_file6,
1767
1194
  L: 34,
1768
1195
  S: this,
1769
1196
  C: (f, a) => f(...a)
@@ -1771,11 +1198,11 @@ var ProfileStateMachine = class {
1771
1198
  return;
1772
1199
  }
1773
1200
  this.profile = assertion.profile;
1774
- log5("updated profile", {
1201
+ log4("updated profile", {
1775
1202
  identityKey: this._params.identityKey,
1776
1203
  profile: this.profile
1777
1204
  }, {
1778
- F: __dxlog_file7,
1205
+ F: __dxlog_file6,
1779
1206
  L: 40,
1780
1207
  S: this,
1781
1208
  C: (f, a) => f(...a)