@kubun/plugin-p2p 0.10.1 → 0.12.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 (201) hide show
  1. package/lib/context/delegation.js +130 -1
  2. package/lib/context/group.js +1646 -1
  3. package/lib/context/hub.js +151 -1
  4. package/lib/context/join.js +115 -1
  5. package/lib/context/peer.d.ts +92 -0
  6. package/lib/context/peer.js +1216 -0
  7. package/lib/context/require-admin.d.ts +57 -0
  8. package/lib/context/require-admin.js +57 -0
  9. package/lib/context/sync.d.ts +12 -0
  10. package/lib/context/sync.js +252 -1
  11. package/lib/context/types.d.ts +58 -22
  12. package/lib/context/types.js +48 -1
  13. package/lib/groups/access-default-token.d.ts +44 -0
  14. package/lib/groups/access-default-token.js +103 -0
  15. package/lib/groups/admin-roster.d.ts +55 -0
  16. package/lib/groups/admin-roster.js +69 -0
  17. package/lib/groups/anchor-store.d.ts +15 -0
  18. package/lib/groups/anchor-store.js +37 -0
  19. package/lib/groups/app-cursor-store.d.ts +17 -0
  20. package/lib/groups/app-cursor-store.js +34 -0
  21. package/lib/groups/broadcast-codec.js +6 -1
  22. package/lib/groups/broadcast.d.ts +238 -106
  23. package/lib/groups/broadcast.js +702 -1
  24. package/lib/groups/catalog-token.d.ts +50 -0
  25. package/lib/groups/catalog-token.js +96 -0
  26. package/lib/groups/circle-projection.d.ts +90 -0
  27. package/lib/groups/circle-projection.js +202 -0
  28. package/lib/groups/circle-reducers.d.ts +139 -0
  29. package/lib/groups/circle-reducers.js +128 -0
  30. package/lib/groups/commit-adoption.d.ts +77 -0
  31. package/lib/groups/commit-adoption.js +108 -0
  32. package/lib/groups/commit-journal.d.ts +14 -0
  33. package/lib/groups/commit-journal.js +65 -0
  34. package/lib/groups/control-request.d.ts +66 -0
  35. package/lib/groups/control-request.js +101 -0
  36. package/lib/groups/events.d.ts +19 -2
  37. package/lib/groups/events.js +10 -1
  38. package/lib/groups/group-anchor.d.ts +24 -0
  39. package/lib/groups/group-anchor.js +22 -0
  40. package/lib/groups/group-crypto.d.ts +42 -0
  41. package/lib/groups/group-crypto.js +159 -0
  42. package/lib/groups/group-handle-registry.d.ts +138 -9
  43. package/lib/groups/group-handle-registry.js +533 -1
  44. package/lib/groups/group-handlers.d.ts +30 -0
  45. package/lib/groups/group-handlers.js +218 -0
  46. package/lib/groups/group-health-monitor.d.ts +11 -0
  47. package/lib/groups/group-health-monitor.js +139 -1
  48. package/lib/groups/group-mls.d.ts +66 -0
  49. package/lib/groups/group-mls.js +696 -0
  50. package/lib/groups/group-peer-manager.d.ts +301 -0
  51. package/lib/groups/group-peer-manager.js +1336 -0
  52. package/lib/groups/group-protocols.d.ts +754 -0
  53. package/lib/groups/group-protocols.js +448 -0
  54. package/lib/groups/invite-payload.d.ts +49 -1
  55. package/lib/groups/invite-payload.js +26 -1
  56. package/lib/groups/join-request-identity.d.ts +21 -0
  57. package/lib/groups/join-request-identity.js +43 -0
  58. package/lib/groups/join-utils.d.ts +61 -0
  59. package/lib/groups/join-utils.js +366 -1
  60. package/lib/groups/ledger-affected-events.d.ts +20 -0
  61. package/lib/groups/ledger-affected-events.js +85 -0
  62. package/lib/groups/ledger-commit-fold.d.ts +107 -0
  63. package/lib/groups/ledger-commit-fold.js +167 -0
  64. package/lib/groups/ledger-ingest.d.ts +124 -0
  65. package/lib/groups/ledger-ingest.js +212 -0
  66. package/lib/groups/ledger.d.ts +44 -0
  67. package/lib/groups/ledger.js +30 -0
  68. package/lib/groups/manager.d.ts +217 -85
  69. package/lib/groups/manager.js +798 -1
  70. package/lib/groups/mls-codec.d.ts +28 -7
  71. package/lib/groups/mls-codec.js +33 -1
  72. package/lib/groups/mls-encryptor.d.ts +2 -2
  73. package/lib/groups/mls-encryptor.js +33 -1
  74. package/lib/groups/mls-group-handle.d.ts +37 -2
  75. package/lib/groups/mls-group-handle.js +18 -1
  76. package/lib/groups/mls-json.js +17 -1
  77. package/lib/groups/mls-receive-errors.d.ts +27 -0
  78. package/lib/groups/mls-receive-errors.js +39 -0
  79. package/lib/groups/mls-state.d.ts +6 -4
  80. package/lib/groups/mls-state.js +79 -1
  81. package/lib/groups/peer-presence.d.ts +90 -0
  82. package/lib/groups/peer-presence.js +295 -0
  83. package/lib/groups/peer-selection.d.ts +37 -0
  84. package/lib/groups/peer-selection.js +72 -0
  85. package/lib/groups/roster-projection.d.ts +53 -0
  86. package/lib/groups/roster-projection.js +65 -0
  87. package/lib/groups/store-received-grant.d.ts +7 -3
  88. package/lib/groups/store-received-grant.js +161 -1
  89. package/lib/groups/store-received-revocation.d.ts +19 -16
  90. package/lib/groups/store-received-revocation.js +83 -1
  91. package/lib/hub/ack-backoff.d.ts +22 -0
  92. package/lib/hub/ack-backoff.js +88 -0
  93. package/lib/hub/http-client.d.ts +18 -10
  94. package/lib/hub/http-client.js +15 -1
  95. package/lib/hub/hub-like.d.ts +75 -0
  96. package/lib/hub/hub-like.js +1022 -0
  97. package/lib/hub/loopback-log-hub.d.ts +20 -0
  98. package/lib/hub/loopback-log-hub.js +178 -0
  99. package/lib/hub/manager.d.ts +25 -40
  100. package/lib/hub/manager.js +155 -1
  101. package/lib/hub/peer-scoped-hub-view.d.ts +35 -0
  102. package/lib/hub/peer-scoped-hub-view.js +92 -0
  103. package/lib/hub/sender-scoped-hub-view.d.ts +41 -0
  104. package/lib/hub/sender-scoped-hub-view.js +59 -0
  105. package/lib/hub/server-did.d.ts +67 -0
  106. package/lib/hub/server-did.js +131 -0
  107. package/lib/hub/wiring.d.ts +77 -51
  108. package/lib/hub/wiring.js +189 -1
  109. package/lib/index.d.ts +58 -22
  110. package/lib/index.js +563 -1
  111. package/lib/peer/connection-registry.d.ts +45 -0
  112. package/lib/peer/connection-registry.js +52 -0
  113. package/lib/peer/handlers.d.ts +69 -0
  114. package/lib/peer/handlers.js +110 -0
  115. package/lib/protocol.d.ts +214 -1
  116. package/lib/protocol.js +519 -1
  117. package/lib/schema.d.ts +3 -1
  118. package/lib/schema.js +1150 -39
  119. package/lib/sync/access-default-sender.d.ts +23 -0
  120. package/lib/sync/access-default-sender.js +80 -0
  121. package/lib/sync/authorize.d.ts +58 -4
  122. package/lib/sync/authorize.js +114 -1
  123. package/lib/sync/broadcast-queue.d.ts +3 -4
  124. package/lib/sync/broadcast-queue.js +182 -1
  125. package/lib/sync/broadcast-sender.js +179 -1
  126. package/lib/sync/catalog-scope.d.ts +8 -7
  127. package/lib/sync/catalog-scope.js +84 -1
  128. package/lib/sync/errors.d.ts +12 -2
  129. package/lib/sync/errors.js +29 -1
  130. package/lib/sync/forwarder.d.ts +13 -19
  131. package/lib/sync/forwarder.js +271 -1
  132. package/lib/sync/handlers.js +200 -1
  133. package/lib/sync/hub-tunnel-sync-listener.d.ts +19 -5
  134. package/lib/sync/hub-tunnel-sync-listener.js +261 -1
  135. package/lib/sync/hub-tunnel-sync-provider.d.ts +34 -14
  136. package/lib/sync/hub-tunnel-sync-provider.js +113 -1
  137. package/lib/sync/merkle-apply.d.ts +5 -0
  138. package/lib/sync/merkle-apply.js +194 -1
  139. package/lib/sync/merkle-channel.d.ts +11 -3
  140. package/lib/sync/merkle-channel.js +0 -0
  141. package/lib/sync/merkle-tree.js +120 -1
  142. package/lib/sync/peer-registry.d.ts +15 -11
  143. package/lib/sync/peer-registry.js +82 -1
  144. package/lib/sync/receive-access-gate.js +24 -1
  145. package/lib/sync/scope-resolver.js +41 -1
  146. package/lib/sync/sync-client.d.ts +14 -4
  147. package/lib/sync/sync-client.js +254 -1
  148. package/lib/sync/sync-manager.d.ts +24 -6
  149. package/lib/sync/sync-manager.js +301 -1
  150. package/lib/sync/tunnel-listeners.d.ts +52 -0
  151. package/lib/sync/tunnel-listeners.js +175 -0
  152. package/lib/sync/tunnel-observability.d.ts +25 -0
  153. package/lib/sync/tunnel-observability.js +63 -0
  154. package/lib/sync/tunnel-topics.d.ts +20 -0
  155. package/lib/sync/tunnel-topics.js +28 -0
  156. package/lib/types.d.ts +704 -22
  157. package/lib/types.js +1 -1
  158. package/lib/util/handler-error.d.ts +16 -0
  159. package/lib/util/handler-error.js +64 -0
  160. package/lib/util/mutex.d.ts +14 -1
  161. package/lib/util/mutex.js +44 -1
  162. package/lib/utils.js +21 -1
  163. package/package.json +58 -47
  164. package/lib/groups/broadcast-service.d.ts +0 -263
  165. package/lib/groups/broadcast-service.js +0 -1
  166. package/lib/groups/rejoin-codec.d.ts +0 -14
  167. package/lib/groups/rejoin-codec.js +0 -1
  168. package/lib/groups/wire-frame.d.ts +0 -34
  169. package/lib/groups/wire-frame.js +0 -1
  170. package/lib/hub/circle-catchup-requester.d.ts +0 -58
  171. package/lib/hub/circle-catchup-requester.js +0 -1
  172. package/lib/hub/circle-catchup-responder.d.ts +0 -50
  173. package/lib/hub/circle-catchup-responder.js +0 -1
  174. package/lib/hub/connection-pool.d.ts +0 -43
  175. package/lib/hub/connection-pool.js +0 -1
  176. package/lib/hub/did-observing-transport.d.ts +0 -64
  177. package/lib/hub/did-observing-transport.js +0 -1
  178. package/lib/hub/epoch-stale-detector.d.ts +0 -18
  179. package/lib/hub/epoch-stale-detector.js +0 -1
  180. package/lib/hub/errors.d.ts +0 -30
  181. package/lib/hub/errors.js +0 -1
  182. package/lib/hub/forward-remote-broadcast.d.ts +0 -15
  183. package/lib/hub/forward-remote-broadcast.js +0 -1
  184. package/lib/hub/group-channel.d.ts +0 -89
  185. package/lib/hub/group-channel.js +0 -1
  186. package/lib/hub/hub-connection.d.ts +0 -96
  187. package/lib/hub/hub-connection.js +0 -1
  188. package/lib/hub/receive-handler.d.ts +0 -51
  189. package/lib/hub/receive-handler.js +0 -1
  190. package/lib/hub/rejoin-manager.d.ts +0 -78
  191. package/lib/hub/rejoin-manager.js +0 -1
  192. package/lib/hub/rejoin-responder.d.ts +0 -32
  193. package/lib/hub/rejoin-responder.js +0 -1
  194. package/lib/hub/relay-manager.d.ts +0 -142
  195. package/lib/hub/relay-manager.js +0 -1
  196. package/lib/hub/send-handler.d.ts +0 -40
  197. package/lib/hub/send-handler.js +0 -1
  198. package/lib/hub/tunnel-inbox.d.ts +0 -20
  199. package/lib/hub/tunnel-inbox.js +0 -1
  200. package/lib/hub/wait-for-gate.d.ts +0 -14
  201. package/lib/hub/wait-for-gate.js +0 -1
@@ -1 +1,151 @@
1
- import{getP2PStore as e}from"@kubun/store-p2p";import{bindHubToGroup as t,createHub as r,deleteHub as s,getHub as a,listHubs as o,listHubsByGroup as u,unbindHubFromGroup as i,updateHub as n}from"../hub/manager.js";import{toISO as l}from"./types.js";function d(e){return{id:e.id,label:e.label,url:e.url,serverDID:e.serverDID,createdAt:e.createdAt.toISOString(),updatedAt:null==e.updatedAt?null:e.updatedAt.toISOString()}}export function createHubContext(c,p){return{list:async()=>(await o({stores:p.stores})).map(d),get:async e=>{let t=await a({stores:p.stores,id:e});return null==t?null:d(t)},create:async e=>d(await r({stores:p.stores,url:e.url,label:e.label,serverDID:e.serverDID})),update:async e=>d(await n({stores:p.stores,id:e.id,url:e.url,label:e.label,serverDID:e.serverDID})),delete:async(e,t)=>{let r=await s({stores:p.stores,id:e,force:t});return{success:r.success,boundGroupIDs:r.boundGroupIDs}},bind:async({hubID:e,groupID:r})=>{let s=await t({stores:p.stores,hubID:e,groupID:r});if(s){let t=await a({stores:p.stores,id:e});null!=t&&await p.emitter.emit("hubBound",{groupID:r,hubID:e,hubURL:t.url,serverDID:t.serverDID})}return s},unbind:async({hubID:e,groupID:t})=>{let r=await u({stores:p.stores,groupID:t}),s=r.find(t=>t.id===e)?.url??null,a=await i({stores:p.stores,hubID:e,groupID:t});return a&&null!=s&&await p.emitter.emit("hubUnbound",{groupID:t,hubID:e,hubURL:s}),a},listByGroup:async e=>(await u({stores:p.stores,groupID:e})).map(d),listGroups:async t=>{let r=await e(p.stores);return(await r.listGroupsByHubID(t)).map(e=>({id:e.id,name:e.name,description:e.description,createdBy:e.created_by,createdAt:l(e.created_at)}))},getGroupHealth:e=>p.monitor.getHealth(e),rejoinGroup:e=>p.rejoinGroup(e)}}
1
+ import { getP2PStore } from '@kubun/store-p2p';
2
+ import { bindHubToGroup, createHub, deleteHub, getHub, listHubs, listHubsByGroup, unbindHubFromGroup, updateHub } from '../hub/manager.js';
3
+ import { toISO } from './types.js';
4
+ function toHubData(hub) {
5
+ return {
6
+ id: hub.id,
7
+ label: hub.label,
8
+ url: hub.url,
9
+ serverDID: hub.serverDID,
10
+ createdAt: hub.createdAt.toISOString(),
11
+ updatedAt: hub.updatedAt == null ? null : hub.updatedAt.toISOString()
12
+ };
13
+ }
14
+ export function createHubContext(_ctx, deps) {
15
+ return {
16
+ list: async ()=>{
17
+ const hubs = await listHubs({
18
+ stores: deps.stores
19
+ });
20
+ return hubs.map(toHubData);
21
+ },
22
+ get: async (id)=>{
23
+ const hub = await getHub({
24
+ stores: deps.stores,
25
+ id
26
+ });
27
+ return hub == null ? null : toHubData(hub);
28
+ },
29
+ create: async (input)=>{
30
+ const hub = await createHub({
31
+ stores: deps.stores,
32
+ url: input.url,
33
+ label: input.label,
34
+ serverDID: input.serverDID,
35
+ runtime: deps.runtime
36
+ });
37
+ return toHubData(hub);
38
+ },
39
+ update: async (input)=>{
40
+ const hub = await updateHub({
41
+ stores: deps.stores,
42
+ id: input.id,
43
+ url: input.url,
44
+ label: input.label,
45
+ serverDID: input.serverDID
46
+ });
47
+ return toHubData(hub);
48
+ },
49
+ delete: async (id, force)=>{
50
+ const result = await deleteHub({
51
+ stores: deps.stores,
52
+ id,
53
+ force
54
+ });
55
+ return {
56
+ success: result.success,
57
+ boundGroupIDs: result.boundGroupIDs
58
+ };
59
+ },
60
+ bind: async ({ hubID, groupID })=>{
61
+ const inserted = await bindHubToGroup({
62
+ stores: deps.stores,
63
+ hubID,
64
+ groupID
65
+ });
66
+ if (inserted) {
67
+ const hub = await getHub({
68
+ stores: deps.stores,
69
+ id: hubID
70
+ });
71
+ if (hub != null) {
72
+ const hubURL = hub.url;
73
+ const serverDID = hub.serverDID;
74
+ // Defer past the outermost commit: the `hubBound` listener reads the P2P
75
+ // store on the main connection (deadlocks single-conn SQLite / uncommitted on PG).
76
+ deps.stores.onCommit(()=>{
77
+ deps.emitter.emit('hubBound', {
78
+ groupID,
79
+ hubID,
80
+ hubURL,
81
+ serverDID
82
+ }).catch((error)=>{
83
+ deps.logger.error('hubBound emit failed', {
84
+ groupID,
85
+ hubID,
86
+ error
87
+ });
88
+ });
89
+ });
90
+ }
91
+ }
92
+ return inserted;
93
+ },
94
+ unbind: async ({ hubID, groupID })=>{
95
+ // Capture URL from the (group, hub) binding row itself so a concurrent
96
+ // `deleteHub({ force: true })` racing this unbind can't strand us with
97
+ // a `removed === true` but no URL to emit. If the binding does not
98
+ // exist for this group/hub pair, `unbindHubFromGroup` returns false
99
+ // and no event fires.
100
+ const boundHubs = await listHubsByGroup({
101
+ stores: deps.stores,
102
+ groupID
103
+ });
104
+ const hubURL = boundHubs.find((h)=>h.id === hubID)?.url ?? null;
105
+ const removed = await unbindHubFromGroup({
106
+ stores: deps.stores,
107
+ hubID,
108
+ groupID
109
+ });
110
+ if (removed && hubURL != null) {
111
+ const boundURL = hubURL;
112
+ // Defer past the outermost commit: the `hubUnbound` listener reads the P2P
113
+ // store on the main connection (deadlocks single-conn SQLite / uncommitted on PG).
114
+ deps.stores.onCommit(()=>{
115
+ deps.emitter.emit('hubUnbound', {
116
+ groupID,
117
+ hubID,
118
+ hubURL: boundURL
119
+ }).catch((error)=>{
120
+ deps.logger.error('hubUnbound emit failed', {
121
+ groupID,
122
+ hubID,
123
+ error
124
+ });
125
+ });
126
+ });
127
+ }
128
+ return removed;
129
+ },
130
+ listByGroup: async (groupID)=>{
131
+ const hubs = await listHubsByGroup({
132
+ stores: deps.stores,
133
+ groupID
134
+ });
135
+ return hubs.map(toHubData);
136
+ },
137
+ listGroups: async (hubID)=>{
138
+ const store = await getP2PStore(deps.stores);
139
+ const rows = await store.listGroupsByHubID(hubID);
140
+ return rows.map((row)=>({
141
+ id: row.id,
142
+ name: row.name,
143
+ description: row.description,
144
+ createdBy: row.created_by,
145
+ createdAt: toISO(row.created_at)
146
+ }));
147
+ },
148
+ getGroupHealth: (groupID)=>deps.monitor.getHealth(groupID),
149
+ rejoinGroup: (groupID)=>deps.rejoinGroup(groupID)
150
+ };
151
+ }
@@ -1 +1,115 @@
1
- import{createKeyPackageBundle as e}from"@enkaku/group";import{decodeInvitePayload as t,encodeJoinRequest as r}from"../groups/invite-payload.js";import{applyInviteGrants as i,applyInviteRevocations as o,finalizeJoinedGroup as a}from"../groups/join-utils.js";import{checkPeerAccess as s}from"./types.js";export function createJoinContext(g,n){return{prepareRequest:async()=>{s(g.viewerDID,n.identity.id,n.autoAcceptPeers);let t=n.identity,i=await e(t);return n.pendingJoinRequests.set(g.viewerDID,{publicPackage:i.publicPackage,privatePackage:i.privatePackage,did:t.id}),{joinRequest:r({did:t.id,publicPackage:i.publicPackage})}},complete:async e=>{s(g.viewerDID,n.identity.id,n.autoAcceptPeers);let r=n.pendingJoinRequests.get(g.viewerDID);if(null==r)throw Error("No pending join request found — call prepareJoinRequest first");let p=n.identity,u=t(e),c={publicPackage:r.publicPackage,privatePackage:r.privatePackage,ownerDID:p.id};await n.groupManager.joinGroup({stores:n.stores,identity:p,groupID:u.groupID,groupName:u.groupName,hubs:u.suggestedHubs,invite:u.invite,keyPackageBundle:c,welcomeMessage:u.welcomeMessage,ratchetTree:u.ratchetTree}),await i({stores:n.stores,selfDID:p.id,groupID:u.groupID,grants:u.grants,hlc:n.hlc,emitter:n.emitter,logger:n.logger}),await o({stores:n.stores,selfDID:p.id,groupID:u.groupID,revocations:u.revocations,hlc:n.hlc,emitter:n.emitter,logger:n.logger});let l=await a({stores:n.stores,emitter:n.emitter,invite:u});return n.pendingJoinRequests.delete(g.viewerDID),l}}}
1
+ import { getP2PStore } from '@kubun/store-p2p';
2
+ import { controlCapabilities, createKeyPackageBundle, readGroupAnchor } from '@kumiai/mls';
3
+ import { decodeFullJoinRequest, decodeInvitePayload, encodeFullJoinRequest, encodeJoinRequest } from '../groups/invite-payload.js';
4
+ import { applyInviteControlState, applyInviteGrants, applyInviteRevocations, finalizeJoinedGroup } from '../groups/join-utils.js';
5
+ import { checkPeerAccess } from './types.js';
6
+ export function createJoinContext(ctx, deps) {
7
+ return {
8
+ prepareRequest: async ()=>{
9
+ checkPeerAccess(ctx.viewerDID, deps.identity.id, deps.autoAcceptPeers);
10
+ const ownIdentity = deps.identity;
11
+ // The invitee leaf must advertise the anchor extension capability or
12
+ // commitInvite rejects the added leaf when joining an anchored group.
13
+ const bundle = await createKeyPackageBundle(ownIdentity, {
14
+ capabilities: controlCapabilities()
15
+ });
16
+ // Persisted, not stashed in memory: the offline exchange spans a human
17
+ // round-trip, so the answering call is routinely a different process —
18
+ // always so for a CLI device, where the two halves are two commands.
19
+ const p2pStore = await getP2PStore(deps.stores);
20
+ await p2pStore.putPendingJoinRequest({
21
+ did: ctx.viewerDID,
22
+ // The same encoding the connected join path hands between devices — a
23
+ // key package carries BigInts and binary that plain JSON drops.
24
+ payload: encodeFullJoinRequest({
25
+ did: ownIdentity.id,
26
+ publicPackage: bundle.publicPackage,
27
+ privatePackage: bundle.privatePackage
28
+ })
29
+ });
30
+ const encoded = encodeJoinRequest({
31
+ did: ownIdentity.id,
32
+ publicPackage: bundle.publicPackage
33
+ });
34
+ return {
35
+ joinRequest: encoded
36
+ };
37
+ },
38
+ complete: async (invitePayloadStr)=>{
39
+ checkPeerAccess(ctx.viewerDID, deps.identity.id, deps.autoAcceptPeers);
40
+ const p2pStore = await getP2PStore(deps.stores);
41
+ const stored = await p2pStore.getPendingJoinRequest(ctx.viewerDID);
42
+ if (stored == null) {
43
+ // Naming the way out matters: "no pending join request" reads to an
44
+ // operator as "the payload you were handed is bad", and they go asking
45
+ // for a new invite against key material that was never the problem.
46
+ throw new Error('The invite payload is fine, but this device has no join request awaiting it — create a new join request and ask to be invited again.');
47
+ }
48
+ const ownIdentity = deps.identity;
49
+ const invite = decodeInvitePayload(invitePayloadStr);
50
+ const pending = decodeFullJoinRequest(stored.payload);
51
+ const keyPackageBundle = {
52
+ publicPackage: pending.publicPackage,
53
+ privatePackage: pending.privatePackage,
54
+ ownerDID: ownIdentity.id
55
+ };
56
+ await deps.groupManager.joinGroup({
57
+ stores: deps.stores,
58
+ identity: ownIdentity,
59
+ groupID: invite.groupID,
60
+ groupName: invite.groupName,
61
+ hubs: invite.suggestedHubs,
62
+ invite: invite.invite,
63
+ keyPackageBundle,
64
+ welcomeMessage: invite.welcomeMessage,
65
+ ratchetTree: invite.ratchetTree
66
+ });
67
+ await applyInviteGrants({
68
+ stores: deps.stores,
69
+ selfDID: ownIdentity.id,
70
+ groupID: invite.groupID,
71
+ grants: invite.grants,
72
+ hlc: deps.hlc,
73
+ emitter: deps.emitter,
74
+ logger: deps.logger
75
+ });
76
+ await applyInviteRevocations({
77
+ stores: deps.stores,
78
+ selfDID: ownIdentity.id,
79
+ groupID: invite.groupID,
80
+ revocations: invite.revocations,
81
+ hlc: deps.hlc,
82
+ emitter: deps.emitter,
83
+ logger: deps.logger
84
+ });
85
+ // Fold the invite-carried control state (ledger, access-defaults,
86
+ // catalogs) so the joiner projects circles/members at join — the same
87
+ // seeding `joinGroup` performs. The MLS handle now exists, so the genesis
88
+ // anchor is readable; the anchor is read through `deps.stores` so the
89
+ // registry shares its connection (a separate connection deadlocks
90
+ // single-connection SQLite).
91
+ await applyInviteControlState({
92
+ stores: deps.stores,
93
+ groupID: invite.groupID,
94
+ accessDefaults: invite.accessDefaults,
95
+ catalogs: invite.catalogs,
96
+ hlc: deps.hlc,
97
+ getGroupAnchor: (gid)=>deps.registry.readHandle(gid, (handle)=>readGroupAnchor(handle), {
98
+ stores: deps.stores
99
+ }),
100
+ getGroupLedger: (gid)=>deps.registry.readHandle(gid, (handle)=>handle.ledger, {
101
+ stores: deps.stores
102
+ }),
103
+ logger: deps.logger
104
+ });
105
+ const result = await finalizeJoinedGroup({
106
+ stores: deps.stores,
107
+ emitter: deps.emitter,
108
+ invite,
109
+ logger: deps.logger
110
+ });
111
+ await p2pStore.deletePendingJoinRequest(ctx.viewerDID);
112
+ return result;
113
+ }
114
+ };
115
+ }
@@ -0,0 +1,92 @@
1
+ import type { ExecutionContext } from '@kubun/engine';
2
+ import type { InviteAccessDefault } from '../groups/invite-payload.js';
3
+ import type { GroupRequestContext, PeerRequestContext, SharePeerGroupData } from '../types.js';
4
+ import type { ContextDeps } from './types.js';
5
+ /**
6
+ * The inviter leg of the inbound dance, run on behalf of a verified caller by
7
+ * the `group/invite` peer handler. It designates NO circle itself: the joiner
8
+ * lands only in circles the inviter ALREADY designated — a `sharePeerGroup`
9
+ * pre-add, or an explicit `requestAddCircleMember` — which `collectInviteSeeds`
10
+ * snapshots from the ledger. Both of those land through an enacted commit, so
11
+ * by the time a designation is snapshottable here it is already durable. The
12
+ * invite runs as THIS device's own identity; the caller only supplies the group
13
+ * and its KeyPackage.
14
+ */
15
+ export declare function serveGroupInvite(deps: ContextDeps, params: {
16
+ callerDID: string;
17
+ groupID: string;
18
+ joinRequest: string;
19
+ }): Promise<{
20
+ invitePayload: string;
21
+ }>;
22
+ /**
23
+ * Apply the access-default rows a verified caller pushed for itself, run by the
24
+ * `group/push-control` peer handler. The source is UNTRUSTED, so the rows apply
25
+ * sender-bound — a row is kept only when its owner equals the authenticated
26
+ * caller, on top of the owner-equals-token-issuer check
27
+ * `applyAccessDefaultSetFrame` already enforces. A caller therefore cannot
28
+ * inject another owner's sharing policy.
29
+ *
30
+ * The caller must additionally be a member of the named group per THIS device's
31
+ * own membership view. The other remote ledger-ingest path rides the group's hub
32
+ * topic, whose MLS derivation is an implicit membership perimeter; this
33
+ * procedure is reachable by any accepted peer, so the perimeter is restated
34
+ * here. Fail-closed: an unknown group reads as no membership.
35
+ */
36
+ export declare function servePushControl(deps: ContextDeps, params: {
37
+ callerDID: string;
38
+ groupID: string;
39
+ accessDefaults: Array<InviteAccessDefault>;
40
+ }): Promise<{
41
+ accessDefaultsApplied: number;
42
+ }>;
43
+ export type DesignatePeerAccessParams = {
44
+ /**
45
+ * The request/settle facade, supplied rather than built here so a caller that
46
+ * mints an invite afterwards drives it through the same one.
47
+ */
48
+ groupCtx: GroupRequestContext;
49
+ /**
50
+ * The designating admin: admitted to the share circle beside the peer, and the
51
+ * owner whose read defaults are classified.
52
+ */
53
+ selfDID: string;
54
+ peerDID: string;
55
+ /** Share an existing group, or create a new one with this name. */
56
+ target: {
57
+ groupID: string;
58
+ } | {
59
+ name: string;
60
+ };
61
+ /**
62
+ * The calling mutation's name and the action its admin gate refuses by, so a
63
+ * failure inside a shared step names the operation the caller actually
64
+ * invoked. Without it every caller's errors would answer to whichever one
65
+ * happened to be written first.
66
+ */
67
+ operation: {
68
+ name: string;
69
+ adminAction: string;
70
+ };
71
+ /** The user's exact model selection, or `null` for no send leg. */
72
+ sendModels: Array<string> | null;
73
+ receiveActivate: boolean;
74
+ /** See {@link SharePeerGroupParams.setReadDefaultForCircle}. */
75
+ setReadDefaultForCircle: (modelID: string, circleID: string) => Promise<void>;
76
+ };
77
+ /**
78
+ * Designate one peer's access to one group: resolve or create the group, fold
79
+ * the commit-hub designation, find or create the share circle, admit both DIDs,
80
+ * and write the catalog + read defaults the share's scope rests on.
81
+ *
82
+ * Local state only — no peer connection is read or written. Whatever follows a
83
+ * designation (the MLS dance, registering the sync peer) belongs to the caller,
84
+ * so admitting a peer without a live connection reuses this rather than copying
85
+ * it: an invite payload snapshots exactly what these steps wrote, so a second
86
+ * copy of the seeding order drifts into a joiner landing in no circle, silently.
87
+ *
88
+ * The steps are ORDER-dependent — each reads what an earlier one wrote — so the
89
+ * sequence below is the contract, not an arrangement.
90
+ */
91
+ export declare function designatePeerAccess(deps: ContextDeps, params: DesignatePeerAccessParams): Promise<SharePeerGroupData>;
92
+ export declare function createPeerContext(ctx: ExecutionContext, deps: ContextDeps): PeerRequestContext;