@kubun/plugin-p2p 0.11.0 → 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.
- package/lib/context/group.js +1073 -746
- package/lib/context/hub.js +2 -1
- package/lib/context/join.js +50 -15
- package/lib/context/peer.d.ts +92 -0
- package/lib/context/peer.js +1216 -0
- package/lib/context/require-admin.d.ts +57 -0
- package/lib/context/require-admin.js +57 -0
- package/lib/context/sync.d.ts +12 -0
- package/lib/context/sync.js +234 -1
- package/lib/context/types.d.ts +56 -20
- package/lib/context/types.js +23 -3
- package/lib/groups/access-default-token.d.ts +44 -0
- package/lib/groups/access-default-token.js +103 -0
- package/lib/groups/admin-roster.d.ts +42 -117
- package/lib/groups/admin-roster.js +53 -168
- package/lib/groups/anchor-store.d.ts +15 -0
- package/lib/groups/anchor-store.js +37 -0
- package/lib/groups/app-cursor-store.d.ts +17 -0
- package/lib/groups/app-cursor-store.js +34 -0
- package/lib/groups/broadcast.d.ts +199 -81
- package/lib/groups/broadcast.js +443 -368
- package/lib/groups/catalog-token.d.ts +50 -0
- package/lib/groups/catalog-token.js +96 -0
- package/lib/groups/circle-projection.d.ts +32 -64
- package/lib/groups/circle-projection.js +95 -181
- package/lib/groups/circle-reducers.d.ts +71 -110
- package/lib/groups/circle-reducers.js +57 -127
- package/lib/groups/commit-adoption.d.ts +77 -0
- package/lib/groups/commit-adoption.js +108 -0
- package/lib/groups/commit-journal.d.ts +14 -0
- package/lib/groups/commit-journal.js +65 -0
- package/lib/groups/control-request.d.ts +66 -0
- package/lib/groups/control-request.js +101 -0
- package/lib/groups/events.d.ts +14 -1
- package/lib/groups/group-anchor.d.ts +18 -57
- package/lib/groups/group-anchor.js +15 -100
- package/lib/groups/group-crypto.d.ts +36 -13
- package/lib/groups/group-crypto.js +124 -50
- package/lib/groups/group-handle-registry.d.ts +106 -36
- package/lib/groups/group-handle-registry.js +351 -61
- package/lib/groups/group-handlers.d.ts +5 -4
- package/lib/groups/group-handlers.js +155 -29
- package/lib/groups/group-health-monitor.d.ts +1 -1
- package/lib/groups/group-health-monitor.js +1 -1
- package/lib/groups/group-mls.d.ts +35 -4
- package/lib/groups/group-mls.js +594 -62
- package/lib/groups/group-peer-manager.d.ts +212 -19
- package/lib/groups/group-peer-manager.js +1061 -69
- package/lib/groups/group-protocols.d.ts +485 -28
- package/lib/groups/group-protocols.js +289 -15
- package/lib/groups/invite-payload.d.ts +48 -0
- package/lib/groups/join-request-identity.d.ts +21 -0
- package/lib/groups/join-request-identity.js +43 -0
- package/lib/groups/join-utils.d.ts +60 -0
- package/lib/groups/join-utils.js +262 -0
- package/lib/groups/ledger-affected-events.d.ts +11 -6
- package/lib/groups/ledger-affected-events.js +15 -26
- package/lib/groups/ledger-commit-fold.d.ts +107 -0
- package/lib/groups/ledger-commit-fold.js +167 -0
- package/lib/groups/ledger-ingest.d.ts +124 -0
- package/lib/groups/ledger-ingest.js +212 -0
- package/lib/groups/ledger.d.ts +44 -0
- package/lib/groups/ledger.js +30 -0
- package/lib/groups/manager.d.ts +202 -19
- package/lib/groups/manager.js +623 -106
- package/lib/groups/mls-codec.d.ts +27 -6
- package/lib/groups/mls-codec.js +30 -15
- package/lib/groups/mls-encryptor.js +9 -6
- package/lib/groups/mls-group-handle.d.ts +37 -2
- package/lib/groups/mls-group-handle.js +12 -4
- package/lib/groups/mls-receive-errors.d.ts +0 -2
- package/lib/groups/mls-receive-errors.js +3 -6
- package/lib/groups/mls-state.d.ts +5 -3
- package/lib/groups/mls-state.js +27 -4
- package/lib/groups/peer-presence.d.ts +90 -0
- package/lib/groups/peer-presence.js +295 -0
- package/lib/groups/peer-selection.d.ts +37 -0
- package/lib/groups/peer-selection.js +72 -0
- package/lib/groups/roster-projection.d.ts +53 -0
- package/lib/groups/roster-projection.js +65 -0
- package/lib/groups/store-received-grant.d.ts +4 -3
- package/lib/groups/store-received-grant.js +80 -35
- package/lib/groups/store-received-revocation.d.ts +16 -13
- package/lib/groups/store-received-revocation.js +23 -22
- package/lib/hub/http-client.d.ts +10 -27
- package/lib/hub/http-client.js +2 -19
- package/lib/hub/hub-like.d.ts +57 -6
- package/lib/hub/hub-like.js +539 -52
- package/lib/hub/loopback-log-hub.d.ts +20 -0
- package/lib/hub/loopback-log-hub.js +178 -0
- package/lib/hub/manager.d.ts +25 -40
- package/lib/hub/manager.js +51 -94
- package/lib/hub/peer-scoped-hub-view.d.ts +35 -0
- package/lib/hub/peer-scoped-hub-view.js +92 -0
- package/lib/hub/sender-scoped-hub-view.d.ts +41 -0
- package/lib/hub/sender-scoped-hub-view.js +59 -0
- package/lib/hub/server-did.d.ts +67 -0
- package/lib/hub/server-did.js +131 -0
- package/lib/hub/wiring.d.ts +71 -14
- package/lib/hub/wiring.js +80 -23
- package/lib/index.d.ts +48 -13
- package/lib/index.js +257 -86
- package/lib/peer/connection-registry.d.ts +45 -0
- package/lib/peer/connection-registry.js +52 -0
- package/lib/peer/handlers.d.ts +69 -0
- package/lib/peer/handlers.js +110 -0
- package/lib/protocol.d.ts +213 -0
- package/lib/protocol.js +268 -0
- package/lib/schema.d.ts +2 -1
- package/lib/schema.js +805 -131
- package/lib/sync/access-default-sender.d.ts +23 -0
- package/lib/sync/access-default-sender.js +80 -0
- package/lib/sync/authorize.d.ts +57 -3
- package/lib/sync/authorize.js +45 -4
- package/lib/sync/broadcast-queue.d.ts +3 -4
- package/lib/sync/broadcast-queue.js +2 -3
- package/lib/sync/catalog-scope.d.ts +8 -7
- package/lib/sync/catalog-scope.js +55 -39
- package/lib/sync/errors.d.ts +9 -0
- package/lib/sync/errors.js +10 -0
- package/lib/sync/forwarder.d.ts +13 -19
- package/lib/sync/forwarder.js +13 -19
- package/lib/sync/handlers.js +54 -14
- package/lib/sync/hub-tunnel-sync-listener.d.ts +16 -6
- package/lib/sync/hub-tunnel-sync-listener.js +126 -37
- package/lib/sync/hub-tunnel-sync-provider.d.ts +31 -11
- package/lib/sync/hub-tunnel-sync-provider.js +68 -19
- package/lib/sync/merkle-channel.d.ts +9 -1
- package/lib/sync/merkle-channel.js +0 -0
- package/lib/sync/peer-registry.d.ts +14 -10
- package/lib/sync/peer-registry.js +0 -25
- package/lib/sync/sync-client.d.ts +11 -1
- package/lib/sync/sync-client.js +72 -10
- package/lib/sync/sync-manager.d.ts +22 -4
- package/lib/sync/sync-manager.js +39 -10
- package/lib/sync/tunnel-listeners.d.ts +52 -0
- package/lib/sync/tunnel-listeners.js +175 -0
- package/lib/sync/tunnel-observability.d.ts +25 -0
- package/lib/sync/tunnel-observability.js +63 -0
- package/lib/sync/tunnel-topics.d.ts +20 -0
- package/lib/sync/tunnel-topics.js +28 -0
- package/lib/types.d.ts +699 -28
- package/lib/types.js +0 -1
- package/lib/util/handler-error.d.ts +16 -0
- package/lib/util/handler-error.js +64 -0
- package/lib/util/mutex.d.ts +14 -1
- package/lib/util/mutex.js +37 -3
- package/package.json +56 -52
- package/lib/groups/ledger-entry.d.ts +0 -37
- package/lib/groups/ledger-entry.js +0 -56
- package/lib/groups/ledger-fold.d.ts +0 -56
- package/lib/groups/ledger-fold.js +0 -53
- package/lib/hub/did-observing-transport.d.ts +0 -75
- package/lib/hub/did-observing-transport.js +0 -126
- package/lib/hub/errors.d.ts +0 -24
- package/lib/hub/errors.js +0 -33
|
@@ -1,14 +1,18 @@
|
|
|
1
|
+
import type { OwnIdentity } from '@kokuin/token';
|
|
1
2
|
import type { DefaultAccessLevel, GraphInternals } from '@kubun/engine';
|
|
2
3
|
import { HLC } from '@kubun/hlc';
|
|
3
4
|
import type { Logger } from '@kubun/logger';
|
|
4
5
|
import type { CatalogRecord } from '@kubun/protocol';
|
|
5
6
|
import type { DelegationStoreAPI } from '@kubun/store-delegation';
|
|
6
7
|
import type { GraphStoreAPI } from '@kubun/store-graph';
|
|
7
|
-
import type {
|
|
8
|
+
import type { P2PStoreAPI } from '@kubun/store-p2p';
|
|
9
|
+
import type { GroupAnchor, LedgerLogEntry, RosterState } from '@kumiai/mls';
|
|
8
10
|
import { type ForwardingConfig } from '../sync/forwarder.js';
|
|
9
11
|
import type { StoreUnreadableMode } from '../types.js';
|
|
12
|
+
import { type AccessDefaultRule } from './access-default-token.js';
|
|
10
13
|
import type { P2PEventEmitter } from './events.js';
|
|
11
|
-
import type {
|
|
14
|
+
import type { PeerAvailability, PeerCapability } from './group-protocols.js';
|
|
15
|
+
import { type AffectedRow, type LedgerIngestParams } from './ledger-ingest.js';
|
|
12
16
|
export type MutationApplyEntry = {
|
|
13
17
|
mutationJWT: string;
|
|
14
18
|
docID: string;
|
|
@@ -43,6 +47,39 @@ export type GroupBroadcastMessage = {
|
|
|
43
47
|
catalogID: string;
|
|
44
48
|
hlc: string;
|
|
45
49
|
auth?: ControlAuth;
|
|
50
|
+
} | {
|
|
51
|
+
/**
|
|
52
|
+
* A member states its own model access-default so co-members can evaluate
|
|
53
|
+
* the owner's documents. `auth` is a signed token whose issuer is the
|
|
54
|
+
* owner; the receiver derives every authoritative field from that verified
|
|
55
|
+
* token and applies the rule ONLY when `ownerDID` equals the issuer (a
|
|
56
|
+
* member states its own policy, never another's). LWW by `hlc`.
|
|
57
|
+
*/
|
|
58
|
+
type: 'access-default:set';
|
|
59
|
+
modelID: string;
|
|
60
|
+
permissionType: 'read' | 'write';
|
|
61
|
+
rule: {
|
|
62
|
+
level: string;
|
|
63
|
+
allowedDIDs: Array<string> | null;
|
|
64
|
+
allowedCircles: Array<string> | null;
|
|
65
|
+
allowedGroups: Array<string> | null;
|
|
66
|
+
};
|
|
67
|
+
ownerDID: string;
|
|
68
|
+
hlc: string;
|
|
69
|
+
auth: ControlAuth;
|
|
70
|
+
} | {
|
|
71
|
+
/**
|
|
72
|
+
* A member removes its own model access-default(s). Same sender-bound and
|
|
73
|
+
* LWW rules as `access-default:set`: applied only when `ownerDID` equals
|
|
74
|
+
* the verified `auth` issuer and the removal `hlc` is newer than the
|
|
75
|
+
* stored rule's.
|
|
76
|
+
*/
|
|
77
|
+
type: 'access-default:remove';
|
|
78
|
+
modelID: string;
|
|
79
|
+
permissionTypes: Array<'read' | 'write'>;
|
|
80
|
+
ownerDID: string;
|
|
81
|
+
hlc: string;
|
|
82
|
+
auth: ControlAuth;
|
|
46
83
|
} | {
|
|
47
84
|
/**
|
|
48
85
|
* Distributes a minted `document/write` capability token to group
|
|
@@ -74,10 +111,6 @@ export type GroupBroadcastMessage = {
|
|
|
74
111
|
groupID: string;
|
|
75
112
|
/** HLC stamped by the revoker; mirrors the revoker-side stored row. */
|
|
76
113
|
hlc: string;
|
|
77
|
-
} | {
|
|
78
|
-
type: 'ledger:entry';
|
|
79
|
-
groupID: string;
|
|
80
|
-
token: string;
|
|
81
114
|
} | {
|
|
82
115
|
/**
|
|
83
116
|
* A member announces a voluntary leave. MLS forbids self-removal, so this
|
|
@@ -93,64 +126,35 @@ export type GroupBroadcastMessage = {
|
|
|
93
126
|
} | {
|
|
94
127
|
type: 'mutation:apply';
|
|
95
128
|
entries: Array<MutationApplyEntry>;
|
|
96
|
-
/** DID of the peer that authored this broadcast — used for loopback drop. */
|
|
97
|
-
senderPeerDID: string;
|
|
98
|
-
} | {
|
|
99
129
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
130
|
+
* DID of the peer this broadcast arrived FROM — used for loopback drop.
|
|
131
|
+
* For a forwarded mutation that is the forwarding peer, not the author, so
|
|
132
|
+
* it is never an authorization input: each entry carries its own signed
|
|
133
|
+
* `mutationJWT`, verified independently.
|
|
134
|
+
*
|
|
135
|
+
* `undefined` when the frame opened at a leaf that could not be named. The
|
|
136
|
+
* entries still apply — see the loopback guard.
|
|
103
137
|
*/
|
|
104
|
-
|
|
105
|
-
requestID: string;
|
|
138
|
+
senderPeerDID: string | undefined;
|
|
106
139
|
} | {
|
|
107
140
|
/**
|
|
108
|
-
* A
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
141
|
+
* A device advertises itself to its co-members. Everything here except
|
|
142
|
+
* `announcerDID` came off the wire; `announcerDID` did NOT — it is the
|
|
143
|
+
* MLS-authenticated sender the handler read from `message.payload.iss`,
|
|
144
|
+
* because the announce body carries no DID field at all.
|
|
145
|
+
*
|
|
146
|
+
* `undefined` when the frame opened at a leaf that could not be named, and
|
|
147
|
+
* the apply then writes nothing: a capability claim attributed to nobody is
|
|
148
|
+
* worse than no row.
|
|
113
149
|
*/
|
|
114
|
-
type: '
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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';
|
|
139
|
-
row: Circle;
|
|
140
|
-
} | {
|
|
141
|
-
entry: 'circle.member';
|
|
142
|
-
row: CircleMember;
|
|
143
|
-
} | {
|
|
144
|
-
entry: 'circle.member.removed';
|
|
145
|
-
row: CircleMember;
|
|
146
|
-
} | {
|
|
147
|
-
entry: 'group.settings';
|
|
148
|
-
row: Group;
|
|
149
|
-
};
|
|
150
|
-
export type AffectedRow = {
|
|
151
|
-
kind: 'ledger';
|
|
152
|
-
ledger: LedgerAffected;
|
|
150
|
+
type: 'peer:announce';
|
|
151
|
+
label: string;
|
|
152
|
+
availability: PeerAvailability;
|
|
153
|
+
capabilities: Array<PeerCapability>;
|
|
154
|
+
hlc: string;
|
|
155
|
+
announcerDID: string | undefined;
|
|
153
156
|
};
|
|
157
|
+
export type { AffectedRow, LedgerAffected } from './ledger-ingest.js';
|
|
154
158
|
export type ProcessBroadcastResult = {
|
|
155
159
|
applied: boolean;
|
|
156
160
|
/**
|
|
@@ -187,6 +191,8 @@ export type ProcessBroadcastResult = {
|
|
|
187
191
|
};
|
|
188
192
|
export type ProcessBroadcastParams = {
|
|
189
193
|
p2pStore: P2PStoreAPI;
|
|
194
|
+
/** See {@link ApplyLedgerEntriesParams.withProjectionTransaction}. */
|
|
195
|
+
withProjectionTransaction?: ApplyLedgerEntriesParams['withProjectionTransaction'];
|
|
190
196
|
/**
|
|
191
197
|
* Delegation store for received-grant / received-revocation handling. The
|
|
192
198
|
* delegation token and revocation rows live here (group-agnostic); the p2p
|
|
@@ -203,6 +209,12 @@ export type ProcessBroadcastParams = {
|
|
|
203
209
|
graph: GraphInternals;
|
|
204
210
|
/** Self DID for `mutation:apply` loopback detection. */
|
|
205
211
|
selfDID: string;
|
|
212
|
+
/**
|
|
213
|
+
* This device's signing identity, used to restate its own access-defaults on
|
|
214
|
+
* a `control/policyCatchup`. Optional: a device without one serves nothing
|
|
215
|
+
* rather than an unsigned rule, which a requester would drop anyway.
|
|
216
|
+
*/
|
|
217
|
+
identity?: OwnIdentity;
|
|
206
218
|
/**
|
|
207
219
|
* Receiver storage mode for incoming `mutation:apply` entries.
|
|
208
220
|
* - `'persist'` (default) — every entry the engine accepts is persisted;
|
|
@@ -239,7 +251,7 @@ export type ProcessBroadcastParams = {
|
|
|
239
251
|
*/
|
|
240
252
|
emitter?: P2PEventEmitter;
|
|
241
253
|
/**
|
|
242
|
-
* Device-wide monotonic clock. On a newly-appended
|
|
254
|
+
* Device-wide monotonic clock. On a newly-appended control-ledger entry the
|
|
243
255
|
* receiver merges the entry's HLC into this clock so its own later entries
|
|
244
256
|
* sort strictly after observed ones. Optional: callers that never receive
|
|
245
257
|
* ledger entries (and existing tests) omit it and the merge is skipped.
|
|
@@ -247,12 +259,28 @@ export type ProcessBroadcastParams = {
|
|
|
247
259
|
hlc?: HLC;
|
|
248
260
|
/**
|
|
249
261
|
* Resolve a group's genesis anchor, or `null` when none is available yet. A
|
|
250
|
-
*
|
|
251
|
-
* only be projected once the authenticated epoch-0 root is known; a `null`
|
|
262
|
+
* control-ledger entry is always stored durably, but the admin-role overlay
|
|
263
|
+
* can only be projected once the authenticated epoch-0 root is known; a `null`
|
|
252
264
|
* anchor defers projection without dropping the entry. Optional so callers
|
|
253
265
|
* that never project (and existing tests) can omit it.
|
|
254
266
|
*/
|
|
255
267
|
getGroupAnchor?: (groupID: string) => Promise<GroupAnchor | null>;
|
|
268
|
+
/** Read the group's control ledger — see `LedgerIngestParams.getGroupLedger`. */
|
|
269
|
+
getGroupLedger?: (groupID: string) => Promise<ReadonlyArray<LedgerLogEntry>>;
|
|
270
|
+
/**
|
|
271
|
+
* The epoch this device's handle is at, stamped onto a `peer:announce` row.
|
|
272
|
+
* Synchronous because it reads the registry's published scalar rather than a
|
|
273
|
+
* handle.
|
|
274
|
+
*
|
|
275
|
+
* It is also the epoch the announcement was SEALED at, not merely the one it
|
|
276
|
+
* was applied at: the app lane only ever hands a handler a frame whose seal
|
|
277
|
+
* epoch matches the current one — everything else is held as `ahead` or
|
|
278
|
+
* discarded as dead — so the two cannot disagree for a delivered frame.
|
|
279
|
+
*
|
|
280
|
+
* Optional; a caller that omits it (or a group the registry has not observed)
|
|
281
|
+
* stamps 0, which reads as "epoch unknown" rather than "epoch zero".
|
|
282
|
+
*/
|
|
283
|
+
getGroupEpoch?: (groupID: string) => number | undefined;
|
|
256
284
|
/** Optional logger — warns on per-entry apply failures. */
|
|
257
285
|
logger?: Logger;
|
|
258
286
|
};
|
|
@@ -275,31 +303,121 @@ export type ProcessBroadcastParams = {
|
|
|
275
303
|
*/
|
|
276
304
|
export declare function processBroadcast(params: ProcessBroadcastParams, message: GroupBroadcastMessage, groupID: string): Promise<ProcessBroadcastResult>;
|
|
277
305
|
/**
|
|
278
|
-
*
|
|
306
|
+
* Sender-bound, LWW apply of a signed `access-default:set` frame. Shared by the
|
|
307
|
+
* live broadcast path and the invite-seeding path so both authenticate through
|
|
308
|
+
* the same token verification. The authoritative fields come from the verified
|
|
309
|
+
* token; the plaintext `ownerDID` is used only for the sender-bound equality
|
|
310
|
+
* check. Returns whether a row was written.
|
|
279
311
|
*/
|
|
280
|
-
export type
|
|
281
|
-
|
|
312
|
+
export type AccessDefaultSetFrame = {
|
|
313
|
+
ownerDID: string;
|
|
314
|
+
modelID: string;
|
|
315
|
+
permissionType: 'read' | 'write';
|
|
316
|
+
rule: AccessDefaultRule;
|
|
317
|
+
hlc: string;
|
|
318
|
+
auth: string;
|
|
319
|
+
};
|
|
320
|
+
export type ApplyAccessDefaultSetFrameParams = {
|
|
321
|
+
graphStore: GraphStoreAPI;
|
|
322
|
+
frame: AccessDefaultSetFrame;
|
|
323
|
+
logger?: Logger;
|
|
324
|
+
/** Scopes the log lines to the group the frame arrived on, when it arrived on one. */
|
|
325
|
+
groupID?: string;
|
|
326
|
+
};
|
|
327
|
+
export declare function applyAccessDefaultSetFrame(params: ApplyAccessDefaultSetFrameParams): Promise<boolean>;
|
|
328
|
+
/**
|
|
329
|
+
* Apply one signed access-default set token, with no surrounding frame.
|
|
330
|
+
*
|
|
331
|
+
* What a catch-up reply carries: a token and nothing else, so there is no
|
|
332
|
+
* plaintext mirror to cross-check and none is needed — the frame's mirror
|
|
333
|
+
* fields exist for a receiver rebuilding what was signed, and the authoritative
|
|
334
|
+
* values were always the claim's. Every other rule is the live path's: the owner
|
|
335
|
+
* is the authenticated issuer, and the write happens only if strictly newer than
|
|
336
|
+
* what is stored.
|
|
337
|
+
*/
|
|
338
|
+
export declare function applyAccessDefaultSetToken(params: {
|
|
339
|
+
graphStore: GraphStoreAPI;
|
|
340
|
+
token: string;
|
|
341
|
+
logger?: Logger;
|
|
342
|
+
groupID: string;
|
|
343
|
+
}): Promise<boolean>;
|
|
344
|
+
/** Summary of a whole-ledger adoption. */
|
|
345
|
+
type ApplyLedgerCatchupReplyResult = {
|
|
346
|
+
/** Size of the ledger that was adopted. */
|
|
282
347
|
applied: number;
|
|
283
|
-
/**
|
|
348
|
+
/**
|
|
349
|
+
* Always `0` on this path, and structurally so: the install verifies the whole
|
|
350
|
+
* ledger against the authenticated head before folding a single entry, so a
|
|
351
|
+
* bad token rejects the batch rather than dropping an entry out of it. Carried
|
|
352
|
+
* because the catch-up summary reports it beside counts from paths that CAN
|
|
353
|
+
* drop per entry.
|
|
354
|
+
*/
|
|
284
355
|
rejected: number;
|
|
285
356
|
};
|
|
357
|
+
/** Inputs the ledger-fold helpers need — a strict subset of the receive params. */
|
|
358
|
+
export type ApplyLedgerEntriesParams = LedgerIngestParams & {
|
|
359
|
+
/**
|
|
360
|
+
* Run the projection rebuild atomically, given the store to write it through.
|
|
361
|
+
*
|
|
362
|
+
* A rebuild materializes several tables from one fold — circle rows, every
|
|
363
|
+
* circle's `open` flag, the group row, the commit-hub designation — and a crash
|
|
364
|
+
* between them leaves them disagreeing. A device with no flagged commit hub
|
|
365
|
+
* cannot commit at all, which reads as a hang rather than an error.
|
|
366
|
+
*
|
|
367
|
+
* A seam rather than a `StoreProvider` because the surrounding operations must
|
|
368
|
+
* NOT be in the same transaction: an install or a gather waits on the network,
|
|
369
|
+
* and a transaction spanning one holds kubun's single SQLite connection across
|
|
370
|
+
* it. So the caller decides what the transaction covers. Omitted, the rebuild
|
|
371
|
+
* runs on {@link LedgerIngestParams.p2pStore} unwrapped — correct when the
|
|
372
|
+
* caller already holds a transaction, and self-healing on the bootstrap paths,
|
|
373
|
+
* where the next reproject rebuilds from the same ledger anyway.
|
|
374
|
+
*
|
|
375
|
+
* Declared here rather than on {@link LedgerIngestParams} because only the
|
|
376
|
+
* batch folds below read it: `ingestVerifiedLedgerEntry` reprojects through
|
|
377
|
+
* `p2pStore` directly, so a caller passing this to the single-entry path would
|
|
378
|
+
* be promised an atomicity it does not get.
|
|
379
|
+
*/
|
|
380
|
+
withProjectionTransaction?: (run: (store: P2PStoreAPI) => Promise<void>) => Promise<void>;
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
* Adopt the control state a handle already holds: merge the HLCs its entries
|
|
384
|
+
* carry into this device's clock, then rebuild every projection from them.
|
|
385
|
+
*
|
|
386
|
+
* For a peer whose handle was filled by something other than a landed commit —
|
|
387
|
+
* a joiner, whose Welcome folded the group's whole ledger and checked it against
|
|
388
|
+
* the authenticated head before this device had any say. Those entries never
|
|
389
|
+
* passed through the receive path, so nothing else has observed their clocks or
|
|
390
|
+
* projected them.
|
|
391
|
+
*
|
|
392
|
+
* The merge keeps this device's later writes sorting after what the group did
|
|
393
|
+
* before it arrived. It decides no authority — that reads the enacted position —
|
|
394
|
+
* but the LWW comparisons that are not ledger folds (removal timestamps,
|
|
395
|
+
* access-default rules) still depend on a clock that has seen the group's.
|
|
396
|
+
*/
|
|
397
|
+
export declare function adoptHandleLedgerState(params: ApplyLedgerEntriesParams, groupID: string): Promise<void>;
|
|
398
|
+
/** Inputs {@link bootstrapGroupLedger} needs on top of the ingest's. */
|
|
399
|
+
export type BootstrapGroupLedgerParams = ApplyLedgerEntriesParams & {
|
|
400
|
+
/**
|
|
401
|
+
* Install a gathered WHOLE ledger into the group's handle, verified against
|
|
402
|
+
* the authenticated head before a single entry is folded, and answer with the
|
|
403
|
+
* roster the install left the handle holding. Throws when the list does not
|
|
404
|
+
* reproduce that head.
|
|
405
|
+
*
|
|
406
|
+
* The roster is returned rather than read back afterwards so it is the one the
|
|
407
|
+
* install itself produced, taken while the handle is still held: a second read
|
|
408
|
+
* could observe a handle another operation has since advanced.
|
|
409
|
+
*/
|
|
410
|
+
installGroupLedger: (groupID: string, tokens: Array<string>) => Promise<RosterState>;
|
|
411
|
+
};
|
|
286
412
|
/**
|
|
287
|
-
*
|
|
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.
|
|
413
|
+
* Adopt a whole ledger gathered from one untrusted responder.
|
|
292
414
|
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
295
|
-
*
|
|
296
|
-
*
|
|
297
|
-
*
|
|
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.
|
|
415
|
+
* Signatures do NOT cover omission or reordering — a responder can hand back
|
|
416
|
+
* genuine tokens with a demotion missing — so the list is installed against the
|
|
417
|
+
* authenticated head FIRST and rejected whole on mismatch, before anything is
|
|
418
|
+
* projected. Roles are reconciled against the roster the install folded, not the
|
|
419
|
+
* ledger, because nothing later revisits a stale `admin` row.
|
|
301
420
|
*
|
|
302
|
-
*
|
|
421
|
+
* Throws whatever the install throws — a rejected ledger changes nothing.
|
|
303
422
|
*/
|
|
304
|
-
export declare function
|
|
305
|
-
export {};
|
|
423
|
+
export declare function bootstrapGroupLedger(params: BootstrapGroupLedgerParams, tokens: Array<string>, groupID: string): Promise<ApplyLedgerCatchupReplyResult>;
|