@kubun/plugin-p2p 0.10.0 → 0.11.0

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 (136) hide show
  1. package/lib/context/delegation.js +130 -1
  2. package/lib/context/group.js +1319 -1
  3. package/lib/context/hub.js +150 -1
  4. package/lib/context/join.js +80 -1
  5. package/lib/context/sync.js +19 -1
  6. package/lib/context/types.d.ts +12 -3
  7. package/lib/context/types.js +28 -1
  8. package/lib/groups/admin-roster.d.ts +130 -0
  9. package/lib/groups/admin-roster.js +184 -0
  10. package/lib/groups/broadcast-codec.js +6 -1
  11. package/lib/groups/broadcast.d.ts +122 -38
  12. package/lib/groups/broadcast.js +627 -1
  13. package/lib/groups/circle-projection.d.ts +122 -0
  14. package/lib/groups/circle-projection.js +288 -0
  15. package/lib/groups/circle-reducers.d.ts +178 -0
  16. package/lib/groups/circle-reducers.js +198 -0
  17. package/lib/groups/events.d.ts +7 -2
  18. package/lib/groups/events.js +10 -1
  19. package/lib/groups/group-anchor.d.ts +63 -0
  20. package/lib/groups/group-anchor.js +107 -0
  21. package/lib/groups/group-crypto.d.ts +19 -0
  22. package/lib/groups/group-crypto.js +85 -0
  23. package/lib/groups/group-handle-registry.d.ts +69 -2
  24. package/lib/groups/group-handle-registry.js +243 -1
  25. package/lib/groups/group-handlers.d.ts +29 -0
  26. package/lib/groups/group-handlers.js +92 -0
  27. package/lib/groups/group-health-monitor.d.ts +56 -0
  28. package/lib/groups/group-health-monitor.js +139 -0
  29. package/lib/groups/group-mls.d.ts +35 -0
  30. package/lib/groups/group-mls.js +164 -0
  31. package/lib/groups/group-peer-manager.d.ts +108 -0
  32. package/lib/groups/group-peer-manager.js +344 -0
  33. package/lib/groups/group-protocols.d.ts +297 -0
  34. package/lib/groups/group-protocols.js +174 -0
  35. package/lib/groups/invite-payload.d.ts +1 -1
  36. package/lib/groups/invite-payload.js +26 -1
  37. package/lib/groups/join-utils.d.ts +1 -0
  38. package/lib/groups/join-utils.js +104 -1
  39. package/lib/groups/ledger-affected-events.d.ts +15 -0
  40. package/lib/groups/ledger-affected-events.js +96 -0
  41. package/lib/groups/ledger-entry.d.ts +37 -0
  42. package/lib/groups/ledger-entry.js +56 -0
  43. package/lib/groups/ledger-fold.d.ts +56 -0
  44. package/lib/groups/ledger-fold.js +53 -0
  45. package/lib/groups/manager.d.ts +29 -71
  46. package/lib/groups/manager.js +281 -1
  47. package/lib/groups/mls-codec.d.ts +1 -1
  48. package/lib/groups/mls-codec.js +18 -1
  49. package/lib/groups/mls-encryptor.d.ts +2 -2
  50. package/lib/groups/mls-encryptor.js +30 -1
  51. package/lib/groups/mls-group-handle.d.ts +1 -1
  52. package/lib/groups/mls-group-handle.js +10 -1
  53. package/lib/groups/mls-json.js +17 -1
  54. package/lib/groups/mls-receive-errors.d.ts +29 -0
  55. package/lib/groups/mls-receive-errors.js +42 -0
  56. package/lib/groups/mls-state.d.ts +1 -1
  57. package/lib/groups/mls-state.js +56 -1
  58. package/lib/groups/store-received-grant.d.ts +4 -1
  59. package/lib/groups/store-received-grant.js +116 -1
  60. package/lib/groups/store-received-revocation.d.ts +3 -3
  61. package/lib/groups/store-received-revocation.js +82 -1
  62. package/lib/hub/ack-backoff.d.ts +22 -0
  63. package/lib/hub/ack-backoff.js +88 -0
  64. package/lib/hub/did-observing-transport.d.ts +13 -2
  65. package/lib/hub/did-observing-transport.js +126 -1
  66. package/lib/hub/errors.d.ts +4 -8
  67. package/lib/hub/errors.js +33 -1
  68. package/lib/hub/http-client.d.ts +28 -3
  69. package/lib/hub/http-client.js +32 -1
  70. package/lib/hub/hub-like.d.ts +24 -0
  71. package/lib/hub/hub-like.js +535 -0
  72. package/lib/hub/manager.d.ts +5 -4
  73. package/lib/hub/manager.js +198 -1
  74. package/lib/hub/wiring.d.ts +28 -41
  75. package/lib/hub/wiring.js +132 -1
  76. package/lib/index.d.ts +29 -13
  77. package/lib/index.js +392 -1
  78. package/lib/protocol.d.ts +8 -1
  79. package/lib/protocol.js +251 -1
  80. package/lib/schema.d.ts +2 -1
  81. package/lib/schema.js +471 -2
  82. package/lib/sync/authorize.d.ts +25 -0
  83. package/lib/sync/authorize.js +73 -0
  84. package/lib/sync/broadcast-queue.js +183 -1
  85. package/lib/sync/broadcast-sender.js +179 -1
  86. package/lib/sync/catalog-scope.js +68 -1
  87. package/lib/sync/errors.d.ts +12 -0
  88. package/lib/sync/errors.js +19 -0
  89. package/lib/sync/forwarder.js +277 -1
  90. package/lib/sync/handlers.d.ts +3 -22
  91. package/lib/sync/handlers.js +160 -1
  92. package/lib/sync/hub-tunnel-sync-listener.d.ts +9 -3
  93. package/lib/sync/hub-tunnel-sync-listener.js +172 -1
  94. package/lib/sync/hub-tunnel-sync-provider.d.ts +14 -14
  95. package/lib/sync/hub-tunnel-sync-provider.js +64 -1
  96. package/lib/sync/merkle-apply.d.ts +5 -0
  97. package/lib/sync/merkle-apply.js +194 -1
  98. package/lib/sync/merkle-channel.d.ts +19 -0
  99. package/lib/sync/merkle-channel.js +182 -1
  100. package/lib/sync/merkle-tree.js +120 -1
  101. package/lib/sync/peer-registry.d.ts +8 -1
  102. package/lib/sync/peer-registry.js +107 -1
  103. package/lib/sync/receive-access-gate.js +24 -1
  104. package/lib/sync/scope-resolver.js +41 -1
  105. package/lib/sync/sync-client.d.ts +5 -3
  106. package/lib/sync/sync-client.js +192 -1
  107. package/lib/sync/sync-manager.d.ts +2 -2
  108. package/lib/sync/sync-manager.js +272 -1
  109. package/lib/types.d.ts +41 -0
  110. package/lib/types.js +2 -1
  111. package/lib/util/mutex.js +10 -1
  112. package/lib/utils.d.ts +5 -0
  113. package/lib/utils.js +21 -0
  114. package/package.json +48 -41
  115. package/lib/groups/broadcast-service.d.ts +0 -189
  116. package/lib/groups/broadcast-service.js +0 -1
  117. package/lib/groups/wire-frame.d.ts +0 -32
  118. package/lib/groups/wire-frame.js +0 -1
  119. package/lib/hub/connection-pool.d.ts +0 -43
  120. package/lib/hub/connection-pool.js +0 -1
  121. package/lib/hub/forward-remote-broadcast.d.ts +0 -15
  122. package/lib/hub/forward-remote-broadcast.js +0 -1
  123. package/lib/hub/group-channel.d.ts +0 -67
  124. package/lib/hub/group-channel.js +0 -1
  125. package/lib/hub/hub-connection.d.ts +0 -96
  126. package/lib/hub/hub-connection.js +0 -1
  127. package/lib/hub/receive-handler.d.ts +0 -33
  128. package/lib/hub/receive-handler.js +0 -1
  129. package/lib/hub/relay-manager.d.ts +0 -127
  130. package/lib/hub/relay-manager.js +0 -1
  131. package/lib/hub/send-handler.d.ts +0 -40
  132. package/lib/hub/send-handler.js +0 -1
  133. package/lib/hub/tunnel-inbox.d.ts +0 -20
  134. package/lib/hub/tunnel-inbox.js +0 -1
  135. package/lib/hub/wait-for-gate.d.ts +0 -14
  136. package/lib/hub/wait-for-gate.js +0 -1
@@ -1,11 +1,14 @@
1
1
  import type { DefaultAccessLevel, GraphInternals } from '@kubun/engine';
2
+ import { HLC } from '@kubun/hlc';
2
3
  import type { Logger } from '@kubun/logger';
3
- import type { CatalogRecord, CircleMemberRecord, CircleRecord } from '@kubun/protocol';
4
- import type { Catalog, GraphStoreAPI } from '@kubun/store-graph';
4
+ import type { CatalogRecord } from '@kubun/protocol';
5
+ import type { DelegationStoreAPI } from '@kubun/store-delegation';
6
+ import type { GraphStoreAPI } from '@kubun/store-graph';
5
7
  import type { Circle, CircleMember, Group, P2PStoreAPI } from '@kubun/store-p2p';
6
8
  import { type ForwardingConfig } from '../sync/forwarder.js';
7
9
  import type { StoreUnreadableMode } from '../types.js';
8
10
  import type { P2PEventEmitter } from './events.js';
11
+ import type { GroupAnchor } from './group-anchor.js';
9
12
  export type MutationApplyEntry = {
10
13
  mutationJWT: string;
11
14
  docID: string;
@@ -13,33 +16,18 @@ export type MutationApplyEntry = {
13
16
  version: string;
14
17
  modelID: string;
15
18
  };
19
+ /**
20
+ * Optional signed token over a control operation. Its issuer (`iss`) is the
21
+ * authenticated author; receivers verify it and authorize that author as a
22
+ * group admin, dropping the operation when the token is absent or invalid. The
23
+ * signed payload is the whole message minus this field, so the signature covers
24
+ * every operation field, not just the author.
25
+ */
26
+ type ControlAuth = string;
16
27
  export type GroupBroadcastMessage = {
17
- type: 'circle:create';
18
- circle: CircleRecord;
19
- } | {
20
- type: 'circle:update';
21
- circleID: string;
22
- update: {
23
- name?: string;
24
- description?: string;
25
- catalogIDs?: Array<string>;
26
- hlc: string;
27
- };
28
- } | {
29
- type: 'circle:delete';
30
- circleID: string;
31
- hlc: string;
32
- } | {
33
- type: 'member:add';
34
- member: CircleMemberRecord;
35
- } | {
36
- type: 'member:remove';
37
- circleID: string;
38
- memberDID: string;
39
- hlc: string;
40
- } | {
41
28
  type: 'catalog:create';
42
29
  catalog: CatalogRecord;
30
+ auth?: ControlAuth;
43
31
  } | {
44
32
  type: 'catalog:update';
45
33
  catalogID: string;
@@ -49,10 +37,12 @@ export type GroupBroadcastMessage = {
49
37
  filterCriteria?: CatalogRecord['filterCriteria'];
50
38
  hlc: string;
51
39
  };
40
+ auth?: ControlAuth;
52
41
  } | {
53
42
  type: 'catalog:delete';
54
43
  catalogID: string;
55
44
  hlc: string;
45
+ auth?: ControlAuth;
56
46
  } | {
57
47
  /**
58
48
  * Distributes a minted `document/write` capability token to group
@@ -85,13 +75,9 @@ export type GroupBroadcastMessage = {
85
75
  /** HLC stamped by the revoker; mirrors the revoker-side stored row. */
86
76
  hlc: string;
87
77
  } | {
88
- type: 'group:update';
78
+ type: 'ledger:entry';
89
79
  groupID: string;
90
- update: {
91
- name?: string;
92
- description?: string;
93
- hlc: string;
94
- };
80
+ token: string;
95
81
  } | {
96
82
  /**
97
83
  * A member announces a voluntary leave. MLS forbids self-removal, so this
@@ -109,20 +95,62 @@ export type GroupBroadcastMessage = {
109
95
  entries: Array<MutationApplyEntry>;
110
96
  /** DID of the peer that authored this broadcast — used for loopback drop. */
111
97
  senderPeerDID: string;
98
+ } | {
99
+ /**
100
+ * A rejoining peer asks current members for the group's full control
101
+ * ledger so it can backfill entries it missed while absent. `requestID`
102
+ * correlates the eventual batched reply.
103
+ */
104
+ type: 'ledger-catchup:request';
105
+ requestID: string;
106
+ } | {
107
+ /**
108
+ * A member's reply to a `ledger-catchup:request`: the group's control
109
+ * ledger as an array of signed entry tokens. The responder is UNTRUSTED —
110
+ * each token self-authenticates, so the requester re-verifies every one
111
+ * independently and a forged or unsigned token is dropped without
112
+ * poisoning the rest. Idempotent + order-independent on the token digest.
113
+ */
114
+ type: 'ledger-catchup:reply';
115
+ requestID: string;
116
+ entries: Array<string>;
112
117
  };
113
- export type AffectedRow = {
114
- kind: 'circle';
118
+ /** Narrow {@link GroupBroadcastMessage} to the variant(s) carrying the given `type`. */
119
+ export type BroadcastOf<TType extends GroupBroadcastMessage['type']> = Extract<GroupBroadcastMessage, {
120
+ type: TType;
121
+ }>;
122
+ /**
123
+ * The local-event descriptor for an applied `ledger:entry`. The ingest arm
124
+ * captures it after reprojecting, while the store still holds the post-state,
125
+ * so {@link forwardRemoteBroadcast} can emit the matching local event for a
126
+ * remote-driven change without re-reading or re-verifying. Each variant carries
127
+ * the projected row plus the create-vs-change / add-vs-remove discriminators the
128
+ * ingest derived by comparing the pre-state it read before reprojecting.
129
+ */
130
+ export type LedgerAffected = {
131
+ entry: 'circle.def';
132
+ row: Circle;
133
+ /** True when an active circle row existed before this entry (changed, not created). */
134
+ preExisted: boolean;
135
+ /** True when the entry's catalog set differs from the pre-state's (catalogs changed). */
136
+ catalogsChanged: boolean;
137
+ } | {
138
+ entry: 'circle.def.removed';
115
139
  row: Circle;
116
140
  } | {
117
- kind: 'member';
141
+ entry: 'circle.member';
118
142
  row: CircleMember;
119
143
  } | {
120
- kind: 'catalog';
121
- row: Catalog;
144
+ entry: 'circle.member.removed';
145
+ row: CircleMember;
122
146
  } | {
123
- kind: 'group';
147
+ entry: 'group.settings';
124
148
  row: Group;
125
149
  };
150
+ export type AffectedRow = {
151
+ kind: 'ledger';
152
+ ledger: LedgerAffected;
153
+ };
126
154
  export type ProcessBroadcastResult = {
127
155
  applied: boolean;
128
156
  /**
@@ -150,9 +178,21 @@ export type ProcessBroadcastResult = {
150
178
  * (possibly `0`) on `mutation:apply`.
151
179
  */
152
180
  duplicates?: number;
181
+ /**
182
+ * Set to `'unknown-type'` when the message carried a `type` outside the
183
+ * known discriminated union (version skew). The frame is not applied and is
184
+ * logged; other branches never set this.
185
+ */
186
+ reason?: 'unknown-type';
153
187
  };
154
188
  export type ProcessBroadcastParams = {
155
189
  p2pStore: P2PStoreAPI;
190
+ /**
191
+ * Delegation store for received-grant / received-revocation handling. The
192
+ * delegation token and revocation rows live here (group-agnostic); the p2p
193
+ * store keeps only the `jti` → group edge.
194
+ */
195
+ delegationStore: DelegationStoreAPI;
156
196
  graphStore: GraphStoreAPI;
157
197
  /**
158
198
  * Engine graph internals — required to apply `mutation:apply` entries via
@@ -198,6 +238,21 @@ export type ProcessBroadcastParams = {
198
238
  * Absent in tests that don't exercise the subscription path.
199
239
  */
200
240
  emitter?: P2PEventEmitter;
241
+ /**
242
+ * Device-wide monotonic clock. On a newly-appended `ledger:entry` the
243
+ * receiver merges the entry's HLC into this clock so its own later entries
244
+ * sort strictly after observed ones. Optional: callers that never receive
245
+ * ledger entries (and existing tests) omit it and the merge is skipped.
246
+ */
247
+ hlc?: HLC;
248
+ /**
249
+ * Resolve a group's genesis anchor, or `null` when none is available yet. A
250
+ * `ledger:entry` is always stored durably, but the admin-role overlay can
251
+ * only be projected once the authenticated epoch-0 root is known; a `null`
252
+ * anchor defers projection without dropping the entry. Optional so callers
253
+ * that never project (and existing tests) can omit it.
254
+ */
255
+ getGroupAnchor?: (groupID: string) => Promise<GroupAnchor | null>;
201
256
  /** Optional logger — warns on per-entry apply failures. */
202
257
  logger?: Logger;
203
258
  };
@@ -219,3 +274,32 @@ export type ProcessBroadcastParams = {
219
274
  * payload fidelity.
220
275
  */
221
276
  export declare function processBroadcast(params: ProcessBroadcastParams, message: GroupBroadcastMessage, groupID: string): Promise<ProcessBroadcastResult>;
277
+ /**
278
+ * Summary of a batched ledger catch-up apply.
279
+ */
280
+ export type ApplyLedgerCatchupReplyResult = {
281
+ /** Count of entries whose verified token was newly appended to the ledger. */
282
+ applied: number;
283
+ /** Count of entries dropped because their token failed verification. */
284
+ rejected: number;
285
+ };
286
+ /**
287
+ * Apply a batched ledger catch-up reply from an UNTRUSTED responder. Each entry
288
+ * carries its own signed token, so the responder's identity is irrelevant: every
289
+ * token is verified independently and a forged or unsigned one is dropped without
290
+ * aborting the batch. Surviving entries are appended idempotently on the token
291
+ * digest, so a replayed or duplicated reply is a harmless no-op.
292
+ *
293
+ * A catch-up reply delivers the ledger's history out of order by construction, so
294
+ * after any new entry lands the projection is rebuilt authoritatively from the
295
+ * whole ledger (a full reproject) rather than applied incrementally. All four
296
+ * control projections — admin roster, circle defs, circle members, group
297
+ * settings — rebuild here, so a circle or settings entry delivered via catch-up
298
+ * materializes immediately rather than waiting for a later live entry. Projection
299
+ * is deferred when no genesis anchor is available yet — the entries are still
300
+ * stored durably.
301
+ *
302
+ * Never throws out of the per-token loop: one bad token must not abort the batch.
303
+ */
304
+ export declare function applyLedgerCatchupReply(params: ProcessBroadcastParams, reply: BroadcastOf<'ledger-catchup:reply'>, groupID: string): Promise<ApplyLedgerCatchupReplyResult>;
305
+ export {};