@serfab/cadre-core 1.14.0 → 1.16.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/README.md +293 -274
- package/dist/cadre-invite-protocol.d.ts +64 -29
- package/dist/cadre-invite-protocol.js +207 -38
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-join-request.d.ts +42 -0
- package/dist/cadre-join-request.js +144 -0
- package/dist/cadre-join-request.js.map +1 -0
- package/dist/cadre-node.d.ts +375 -79
- package/dist/cadre-node.js +758 -186
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-database.d.ts +92 -12
- package/dist/control-database.js +323 -142
- package/dist/control-database.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +19 -3
- package/dist/control-schema.js.map +1 -1
- package/dist/ed25519-key.d.ts +7 -9
- package/dist/ed25519-key.js +20 -10
- package/dist/ed25519-key.js.map +1 -1
- package/dist/fetch-impl.d.ts +12 -0
- package/dist/fetch-impl.js +23 -0
- package/dist/fetch-impl.js.map +1 -0
- package/dist/file-durable-slot.js +2 -1
- package/dist/file-durable-slot.js.map +1 -1
- package/dist/file-safe-name.d.ts +25 -0
- package/dist/file-safe-name.js +29 -0
- package/dist/file-safe-name.js.map +1 -0
- package/dist/formation-approval.js +5 -11
- package/dist/formation-approval.js.map +1 -1
- package/dist/fs-atomic.d.ts +0 -19
- package/dist/fs-atomic.js +3 -24
- package/dist/fs-atomic.js.map +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +31 -6
- package/dist/index.js.map +1 -1
- package/dist/invitation-bootstrap.d.ts +50 -12
- package/dist/invitation-bootstrap.js +76 -13
- package/dist/invitation-bootstrap.js.map +1 -1
- package/dist/joined-strand-store.d.ts +7 -0
- package/dist/joined-strand-store.js +9 -0
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store-file.js +2 -1
- package/dist/key-store-file.js.map +1 -1
- package/dist/link-budget.d.ts +9 -4
- package/dist/link-budget.js +9 -4
- package/dist/link-budget.js.map +1 -1
- package/dist/machine-removal.d.ts +78 -0
- package/dist/machine-removal.js +166 -0
- package/dist/machine-removal.js.map +1 -0
- package/dist/membership-connection-gater.d.ts +6 -5
- package/dist/membership-connection-gater.js +6 -5
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-claim-payload.d.ts +55 -0
- package/dist/node-claim-payload.js +108 -4
- package/dist/node-claim-payload.js.map +1 -1
- package/dist/node-default-transports.d.ts +27 -0
- package/dist/node-default-transports.js +35 -0
- package/dist/node-default-transports.js.map +1 -0
- package/dist/node-transports.d.ts +30 -0
- package/dist/node-transports.js +40 -0
- package/dist/node-transports.js.map +1 -0
- package/dist/peer-dial.d.ts +124 -27
- package/dist/peer-dial.js +254 -30
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.js +6 -0
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/peer-record.d.ts +6 -45
- package/dist/peer-record.js +8 -50
- package/dist/peer-record.js.map +1 -1
- package/dist/primary-lan-address.d.ts +8 -0
- package/dist/primary-lan-address.js +46 -0
- package/dist/primary-lan-address.js.map +1 -0
- package/dist/provider/client.d.ts +131 -0
- package/dist/provider/client.js +226 -0
- package/dist/provider/client.js.map +1 -0
- package/dist/provider/descriptor.d.ts +41 -0
- package/dist/provider/descriptor.js +103 -0
- package/dist/provider/descriptor.js.map +1 -0
- package/dist/provider/index.d.ts +9 -0
- package/dist/provider/index.js +10 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/provider/oauth.d.ts +125 -0
- package/dist/provider/oauth.js +199 -0
- package/dist/provider/oauth.js.map +1 -0
- package/dist/provider/rent-node.d.ts +101 -0
- package/dist/provider/rent-node.js +220 -0
- package/dist/provider/rent-node.js.map +1 -0
- package/dist/relay-addrs.d.ts +9 -4
- package/dist/relay-addrs.js +9 -4
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +51 -16
- package/dist/relay-reservation.js +107 -24
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +22 -5
- package/dist/seed-bootstrap.js +48 -25
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/strand-commit-durability.d.ts +54 -0
- package/dist/strand-commit-durability.js +74 -0
- package/dist/strand-commit-durability.js.map +1 -0
- package/dist/strand-database.d.ts +23 -8
- package/dist/strand-database.js +41 -13
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +7 -1
- package/dist/strand-first-sync-gate.js +7 -1
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-protocol.js +4 -1
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-fret-addrs.d.ts +19 -1
- package/dist/strand-fret-addrs.js +27 -9
- package/dist/strand-fret-addrs.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +81 -19
- package/dist/strand-instance-manager.js +216 -105
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-move.d.ts +124 -0
- package/dist/strand-move.js +438 -0
- package/dist/strand-move.js.map +1 -0
- package/dist/strand-network-config.js +7 -6
- package/dist/strand-network-config.js.map +1 -1
- package/dist/strand-peer-redial.d.ts +113 -0
- package/dist/strand-peer-redial.js +236 -0
- package/dist/strand-peer-redial.js.map +1 -0
- package/dist/strand-wake-protocol.js +10 -7
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +212 -4
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/package.json +18 -10
- package/src/cadre-invite-protocol.ts +1052 -864
- package/src/cadre-join-request.ts +167 -0
- package/src/cadre-node.ts +10003 -9381
- package/src/control-database.ts +4663 -4451
- package/src/control-schema.ts +19 -3
- package/src/ed25519-key.ts +20 -10
- package/src/fetch-impl.ts +23 -0
- package/src/file-durable-slot.ts +64 -63
- package/src/file-safe-name.ts +32 -0
- package/src/formation-approval.ts +604 -610
- package/src/fs-atomic.ts +92 -117
- package/src/index.ts +71 -4
- package/src/invitation-bootstrap.ts +91 -13
- package/src/joined-strand-store.ts +508 -498
- package/src/key-store-file.ts +142 -141
- package/src/link-budget.ts +9 -4
- package/src/machine-removal.ts +216 -0
- package/src/membership-connection-gater.ts +638 -637
- package/src/node-claim-payload.ts +138 -4
- package/src/node-default-transports.ts +38 -0
- package/src/node-transports.ts +49 -0
- package/src/peer-dial.ts +604 -336
- package/src/peer-join-backfill.ts +6 -0
- package/src/peer-record.ts +159 -202
- package/src/primary-lan-address.ts +47 -0
- package/src/provider/client.ts +326 -0
- package/src/provider/descriptor.ts +129 -0
- package/src/provider/index.ts +59 -0
- package/src/provider/oauth.ts +285 -0
- package/src/provider/rent-node.ts +275 -0
- package/src/relay-addrs.ts +9 -4
- package/src/relay-reservation.ts +1211 -1121
- package/src/seed-bootstrap.ts +1502 -1471
- package/src/strand-commit-durability.ts +116 -0
- package/src/strand-database.ts +46 -14
- package/src/strand-first-sync-gate.ts +7 -1
- package/src/strand-formation-protocol.ts +1039 -1036
- package/src/strand-fret-addrs.ts +179 -145
- package/src/strand-instance-manager.ts +2026 -1883
- package/src/strand-move.ts +585 -0
- package/src/strand-network-config.ts +7 -6
- package/src/strand-peer-redial.ts +307 -0
- package/src/strand-wake-protocol.ts +10 -7
- package/src/types.ts +220 -4
package/dist/cadre-node.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import type { Libp2p, PeerId } from '@libp2p/interface';
|
|
2
2
|
import type { Multiaddr } from '@multiformats/multiaddr';
|
|
3
|
-
import type { CadreNodeConfig, StrandInstance, StrandRow, StrandConfig, FoundStrandConfig, FoundStrandResult, SAppConfig, CadreNodeEvents, ControlNetworkSeed, ApplySeedResult, SeedAckMessage, SeedDeliveryTarget, AddDroneOptions, DroneInitResult, OpenInvitation, FormStrandResult, StrandFormationDisclosure, StrandMembershipInvite, ResolveOpts, SelfRegistrationOutcome, ServiceWakeResult, PushPlatform, DeviceTokenRecord, CadreInviteStatus, CreateCadreInvitationOptions, CreateCadreInvitationResult, RedeemCadreInvitationResult, ResolveDeviceTokenOpts, PendingJoinStatus } from './types.js';
|
|
3
|
+
import type { CadreNodeConfig, StrandInstance, StrandRow, StrandConfig, FoundStrandConfig, FoundStrandResult, SAppConfig, CadreNodeEvents, ControlNetworkSeed, ApplySeedResult, SeedAckMessage, SeedDeliveryTarget, AddDroneOptions, DroneInitResult, OpenInvitation, FormStrandResult, StrandFormationDisclosure, StrandMembershipInvite, ResolveOpts, SelfRegistrationOutcome, ServiceWakeResult, PushPlatform, DeviceTokenRecord, CadreInviteStatus, CadreMachine, MachineRemovalPlan, MachineRemovalResult, RemoveMachineOptions, CadreRemoval, CadreInvitationReach, CreateCadreInvitationOptions, CreateCadreInvitationResult, RedeemCadreInvitationOptions, RedeemCadreInvitationResult, ResolveDeviceTokenOpts, PendingJoinStatus } from './types.js';
|
|
4
4
|
import { type Ed25519KeyPair } from './ed25519-key.js';
|
|
5
5
|
import { type TrustedOwnerStore, type TrustSource } from './trusted-owner-store.js';
|
|
6
6
|
import { type BootstrapPeerStore } from './bootstrap-peer-store.js';
|
|
7
7
|
import { type StrandNetworkStateStore } from './strand-network-state.js';
|
|
8
8
|
import { type EnrolledMachineStore } from './enrolled-machine-store.js';
|
|
9
|
+
import { type JoinedStrandRecord } from './joined-strand-store.js';
|
|
9
10
|
import { type SAppIdLookup } from './strand-watcher.js';
|
|
10
11
|
import { type ControlCohortReconcileResult } from './control-cohort.js';
|
|
11
12
|
import { EnrollmentService } from './enrollment.js';
|
|
@@ -90,8 +91,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
90
91
|
private controlBackfill;
|
|
91
92
|
/**
|
|
92
93
|
* Devices whose invitation redemption this member has verified and is admitting right now,
|
|
93
|
-
* each with its count of overlapping redemptions. The control backfill's gate
|
|
94
|
-
*
|
|
94
|
+
* each with its count of overlapping redemptions. The control backfill's gate and the
|
|
95
|
+
* control cohort choice (Optimystic's `isNetworkMember`) count them as members, for that
|
|
96
|
+
* window only ({@link admitRedeemingDevice}); the per-stream gate never does.
|
|
95
97
|
*/
|
|
96
98
|
private readonly admittingDevices;
|
|
97
99
|
private strandWatcher;
|
|
@@ -141,6 +143,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
141
143
|
private pushFanoutService;
|
|
142
144
|
/** Backing field for the {@link running} / {@link isRunning} getters. */
|
|
143
145
|
private _running;
|
|
146
|
+
/** The {@link stop} in progress, or null; a concurrent `stop()` awaits it. */
|
|
147
|
+
private stopInFlight;
|
|
144
148
|
/**
|
|
145
149
|
* The control database's own raw storage (cache-wrapped), resolved once per
|
|
146
150
|
* `start()` and released in {@link cleanup}. Owning it is what keeps the
|
|
@@ -247,13 +251,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
247
251
|
private bootstrapPeerIds;
|
|
248
252
|
/**
|
|
249
253
|
* Materialized in-memory snapshot of the AUTHORIZED member peer ids (the
|
|
250
|
-
* {@link listAuthorizedMembers} result), consulted
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
*
|
|
254
|
-
*
|
|
255
|
-
*
|
|
256
|
-
*
|
|
254
|
+
* {@link listAuthorizedMembers} result), consulted through
|
|
255
|
+
* {@link isControlNetworkMember} by the per-stream control-DB gate
|
|
256
|
+
* ({@link authorizeInboundControlStream}) and by Optimystic's cohort choice
|
|
257
|
+
* (`isNetworkMember`). The stream gate runs on EVERY inbound Optimystic
|
|
258
|
+
* control-DB stream and must never await a control-DB read: those reads pull
|
|
259
|
+
* blocks over the very protocols the predicate gates, which closes a circular
|
|
260
|
+
* wait that ends in mutual denial (the upstream gate is fail-closed on
|
|
261
|
+
* timeout, unlike the fail-open connection gater). Optimystic's predicate is
|
|
262
|
+
* synchronous by contract. So the set is refreshed OUT OF BAND instead — after
|
|
257
263
|
* start, on every {@link reconcileControlCohort} pass (15s cadence), and
|
|
258
264
|
* immediately after every LOCAL membership mutation so a just-vouched peer
|
|
259
265
|
* is admitted without waiting for the timer. That last refresh is AUTOMATIC:
|
|
@@ -262,7 +268,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
262
268
|
* writer has to remember. A change that arrives by REPLICATION is picked up
|
|
263
269
|
* on the next timed refresh — bounded staleness, acceptable because this gate
|
|
264
270
|
* is defense in depth: rows an unadmitted peer manages to write are still
|
|
265
|
-
* disbelieved at read time by the voucher-anchored predicate.
|
|
271
|
+
* disbelieved at read time by the voucher-anchored predicate. For cohort
|
|
272
|
+
* choice the same window means a member whose row arrived by replication is
|
|
273
|
+
* left out of this node's control-block cohorts until that refresh.
|
|
274
|
+
* Optimystic reads the predicate live on every lookup, so replacing this set
|
|
275
|
+
* is the whole re-evaluation; there is no per-peer reclassify call to make.
|
|
266
276
|
*/
|
|
267
277
|
private authorizedControlPeers;
|
|
268
278
|
/**
|
|
@@ -274,6 +284,17 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
274
284
|
private membershipGateDirty;
|
|
275
285
|
private membershipGateDrain;
|
|
276
286
|
private membershipGateDeferDepth;
|
|
287
|
+
/**
|
|
288
|
+
* This node's own `CadrePeer` row as a membership refresh last read it live, or null until
|
|
289
|
+
* one has. Reporting a removal needs it ({@link noteOwnRemoval}). Kept across
|
|
290
|
+
* stop()→start(), like {@link removal}.
|
|
291
|
+
*/
|
|
292
|
+
private ownLiveRow;
|
|
293
|
+
/**
|
|
294
|
+
* The removal this node learned of ({@link removedFromCadre}), or null. Set once and never
|
|
295
|
+
* cleared, so `cadre:removed` fires once per node object.
|
|
296
|
+
*/
|
|
297
|
+
private removal;
|
|
277
298
|
/**
|
|
278
299
|
* In-memory delegate admission grants (see `delegate-admission.ts`): the
|
|
279
300
|
* strand-node transport peerIds this party's members have announced over the
|
|
@@ -554,9 +575,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
554
575
|
* When this node last PUBLISHED its own `CadrePeer` address record (inserted or
|
|
555
576
|
* refreshed), or null if it never has in this session. Stamped by
|
|
556
577
|
* {@link noteSelfRecordPublished} on every successful publish, wherever it was driven
|
|
557
|
-
* from — the boot pass, the heartbeat, an address change, a drain or an
|
|
578
|
+
* from — the boot pass, the heartbeat, an address change, a drain, a reconcile pass or an
|
|
579
|
+
* explicit call.
|
|
558
580
|
*
|
|
559
|
-
* Read by {@link
|
|
581
|
+
* Read by {@link publishFirstSelfRecord}, which stops trying once it is set, and by
|
|
582
|
+
* {@link escalateIfSelfRecordStale} for the one consequence worth an operator's
|
|
560
583
|
* attention: once this gap exceeds {@link DEFAULT_PEER_RECORD_MAX_AGE_MS}, every other
|
|
561
584
|
* machine in the party is already discarding this node's address as stale.
|
|
562
585
|
*/
|
|
@@ -724,6 +747,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
724
747
|
* Stop the cadre node
|
|
725
748
|
*/
|
|
726
749
|
stop(): Promise<void>;
|
|
750
|
+
private stopRunning;
|
|
727
751
|
/**
|
|
728
752
|
* Subscribe to events
|
|
729
753
|
*/
|
|
@@ -803,12 +827,19 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
803
827
|
* {@link start} has built the anchor.
|
|
804
828
|
*
|
|
805
829
|
* While true the node admits strangers' CONNECTIONS (the claim seed has to ride one)
|
|
806
|
-
* and refuses
|
|
830
|
+
* and refuses them control-database streams and cohort places ({@link isControlNetworkMember})
|
|
807
831
|
* and relay reservations ({@link admitControlRelayReservation}): an unclaimed node has
|
|
808
832
|
* no siblings to replicate from and must not become a public relay while it waits.
|
|
809
833
|
* cadre-cli reports this on `/status`; `claim:accepted` is the event form.
|
|
810
834
|
*/
|
|
811
835
|
isAwaitingClaim(): boolean;
|
|
836
|
+
/**
|
|
837
|
+
* The removal from the cadre this node has learned of, or null: the query form of
|
|
838
|
+
* `cadre:removed`, for an embedder that subscribed after it fired. Kept once set, across
|
|
839
|
+
* stop()→start() and a later re-admission, because the embedder acts on it once (stops this
|
|
840
|
+
* node, deletes the cadre's data); a re-added machine joins again from a fresh node.
|
|
841
|
+
*/
|
|
842
|
+
removedFromCadre(): CadreRemoval | null;
|
|
812
843
|
/**
|
|
813
844
|
* Construct (or adopt) the node-local cold-start bootstrap-peer store (see
|
|
814
845
|
* {@link bootstrapPeerStore}). Synchronous: an injected store has already
|
|
@@ -881,6 +912,29 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
881
912
|
* strand restart with?".
|
|
882
913
|
*/
|
|
883
914
|
getStrandNetworkStateStore(): StrandNetworkStateStore | null;
|
|
915
|
+
/**
|
|
916
|
+
* The strands this machine joined from another party and has not published party-wide
|
|
917
|
+
* (see {@link CadreNodeConfig.joinedStrands}). Publication needs a control connection,
|
|
918
|
+
* so a one-node party's joins live here until it holds one; `exportStrandBundle` reads
|
|
919
|
+
* it beside the `JoinedStrand` table.
|
|
920
|
+
*
|
|
921
|
+
* @throws when the node is not started.
|
|
922
|
+
*/
|
|
923
|
+
listUnpublishedJoinedStrands(): Promise<JoinedStrandRecord[]>;
|
|
924
|
+
/**
|
|
925
|
+
* The strand-network addresses a formation carried for `strandId` (see
|
|
926
|
+
* {@link formationStrandAddrs}), as kept: attributed per peer. Empty when none were
|
|
927
|
+
* carried or the strand's peers were forgotten. A copy; the node keeps its own list.
|
|
928
|
+
*/
|
|
929
|
+
getCarriedStrandAddrs(strandId: string): string[];
|
|
930
|
+
/**
|
|
931
|
+
* Seat `addrs` as the addresses a formation carried for `strandId`, exactly as a
|
|
932
|
+
* formation of this node's own would ({@link recordFormationStrandAddrs}): a strand
|
|
933
|
+
* move seats the exporting node's list here so the first launch has a cross-party
|
|
934
|
+
* seed. The usual rules apply: a list naming a peer replaces the strand's earlier
|
|
935
|
+
* one, and a list naming none records nothing.
|
|
936
|
+
*/
|
|
937
|
+
recordCarriedStrandAddrs(strandId: string, addrs: readonly string[]): Promise<void>;
|
|
884
938
|
/**
|
|
885
939
|
* The node-local trusted-owner anchor (null before {@link start}). This is
|
|
886
940
|
* the set the authorized-membership predicate and seed-trust anchor consult —
|
|
@@ -943,7 +997,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
943
997
|
* connect-then-authorize — on every committed membership change via
|
|
944
998
|
* {@link refreshAuthorizedControlPeers}'s `scheduleConnectedPeers()` call.
|
|
945
999
|
* The one non-member the gate admits is a device whose invitation
|
|
946
|
-
* redemption this member is admitting ({@link
|
|
1000
|
+
* redemption this member is admitting ({@link admitRedeemingDevice}).
|
|
947
1001
|
*
|
|
948
1002
|
* No-ops (logged) when the embedder configured no control storage or the
|
|
949
1003
|
* node exposes no key network — the backfill would have nothing to read or
|
|
@@ -951,18 +1005,23 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
951
1005
|
*/
|
|
952
1006
|
private startControlBackfill;
|
|
953
1007
|
/**
|
|
954
|
-
*
|
|
955
|
-
*
|
|
956
|
-
*
|
|
957
|
-
*
|
|
958
|
-
*
|
|
1008
|
+
* Admit a device whose invitation redemption has verified to this member's control
|
|
1009
|
+
* replication until the returned release is called (the handler's `admitDevice`;
|
|
1010
|
+
* `CadreInviteHandler.admitDeviceIfLive` says why the redemption's writes need it): push it
|
|
1011
|
+
* this member's control store and wait for the push, with the device counted as a member by
|
|
1012
|
+
* the backfill's gate and by the control cohort choice ({@link admittingDevices}), so the
|
|
1013
|
+
* seat and the admission that follow are written to the device too. A failed push releases
|
|
1014
|
+
* at once and rethrows: in the cohort without the store, the device would refuse the
|
|
1015
|
+
* commits and tear the writes. With no backfill on this node nothing is admitted.
|
|
959
1016
|
*
|
|
960
1017
|
* Through {@link PeerJoinBackfill.forceCatchUpPeer}, not `catchUpPeer`: the device's own
|
|
961
1018
|
* `peer:identify` scheduled a run a moment earlier, which was denied if it passed the gate
|
|
962
1019
|
* before the device was added here, and `catchUpPeer` would answer that run's in-flight
|
|
963
1020
|
* status with an empty result.
|
|
964
1021
|
*/
|
|
965
|
-
private
|
|
1022
|
+
private admitRedeemingDevice;
|
|
1023
|
+
/** Count one more redemption admitting `peerId`; the returned release undoes it, once. */
|
|
1024
|
+
private countAdmittingDevice;
|
|
966
1025
|
/**
|
|
967
1026
|
* The control database's cache-wrapped raw storage, resolved from
|
|
968
1027
|
* `config.storage.provider` on first use and then held in {@link controlStorage}
|
|
@@ -1167,13 +1226,17 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1167
1226
|
*/
|
|
1168
1227
|
private admitControlPeerUnconditionally;
|
|
1169
1228
|
/**
|
|
1170
|
-
*
|
|
1171
|
-
*
|
|
1172
|
-
*
|
|
1173
|
-
*
|
|
1174
|
-
*
|
|
1175
|
-
*
|
|
1176
|
-
*
|
|
1229
|
+
* The membership rule this control node applies to a remote peer, without logging. Two
|
|
1230
|
+
* consumers: the per-stream gate ({@link authorizeInboundControlStream}, who may SEND to
|
|
1231
|
+
* this node on a members-only protocol) and Optimystic's `isNetworkMember` (who this node
|
|
1232
|
+
* sends control writes TO — which peers can be a control block's cohort member or
|
|
1233
|
+
* coordinator), which adds only a device whose verified invitation redemption is being
|
|
1234
|
+
* written ({@link admittingDevices}). One rule for both keeps the outbound choice as
|
|
1235
|
+
* permissive as the inbound acceptance: a joining node's cohort must include the inviter
|
|
1236
|
+
* its empty anchor or empty snapshot lets replicate to it, or it would see a self-only
|
|
1237
|
+
* cohort, rule an existing collection absent and found a second copy. Sereus builds the
|
|
1238
|
+
* control node with `bootstrapNodes: []`, so Optimystic's refused-bootstrap-peer guard
|
|
1239
|
+
* does not cover that.
|
|
1177
1240
|
*
|
|
1178
1241
|
* The STRICT SUBSET of {@link admitInboundControlConnection}: the same
|
|
1179
1242
|
* "no basis to judge" admissions (shared via
|
|
@@ -1182,40 +1245,54 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1182
1245
|
* grant). The first two exist so a stranger can reach `/sereus/formation/1.0.0`
|
|
1183
1246
|
* and `/sereus/cadre-invite/1.0.0` — neither is gated here, and admitting a
|
|
1184
1247
|
* stranger to `repo` while an invitation is live is exactly the hole this
|
|
1185
|
-
*
|
|
1248
|
+
* rule closes. A DELEGATE-admitted connection (a member's strand node
|
|
1186
1249
|
* holding a circuit-relay reservation, see `delegate-admission.ts`) is
|
|
1187
|
-
* likewise
|
|
1188
|
-
* the connection and never the control DB.
|
|
1250
|
+
* likewise refused: the delegate gets the connection and never the control DB.
|
|
1189
1251
|
*
|
|
1190
1252
|
* Refuses everyone, ahead of every admission below, while this node is waiting to be
|
|
1191
1253
|
* claimed ({@link isAwaitingClaim}). The empty-anchor and empty-snapshot admissions
|
|
1192
1254
|
* exist so a brand-new node can take replication from the siblings that will
|
|
1193
1255
|
* authorize it; an unclaimed node has no siblings yet, and the rows that will
|
|
1194
1256
|
* authorize anyone arrive only after the claim, over the owner's streams that the
|
|
1195
|
-
* same two admissions then let in.
|
|
1196
|
-
*
|
|
1257
|
+
* same two admissions then let in. Its cohort is itself alone meanwhile, which costs
|
|
1258
|
+
* nothing: it built its control database with zero connections. Configured bootstrap
|
|
1259
|
+
* infra is refused too: a relay opens no control-database stream.
|
|
1197
1260
|
*
|
|
1198
1261
|
* Otherwise admits when ANY of:
|
|
1199
1262
|
* 1. a shared-baseline check admits (not running / no DB, empty anchor,
|
|
1200
|
-
* configured bootstrap infra
|
|
1263
|
+
* configured bootstrap infra — which Optimystic still classifies `foreign`,
|
|
1264
|
+
* since a relay serves none of the party's database protocols);
|
|
1201
1265
|
* 2. the materialized authorized set is empty — cold start, before the
|
|
1202
1266
|
* rows that would authorize anyone have replicated in;
|
|
1203
1267
|
* 3. the peer IS in the materialized authorized set.
|
|
1204
1268
|
*
|
|
1205
1269
|
* Deliberately SYNCHRONOUS and pure in-memory — see
|
|
1206
1270
|
* {@link authorizedControlPeers} for why a control-DB read here would
|
|
1207
|
-
* deadlock into mutual denial.
|
|
1208
|
-
* (`authorizeInboundStreamTimeoutMs`) never trips, so it stays at its
|
|
1209
|
-
* default.
|
|
1271
|
+
* deadlock into mutual denial.
|
|
1210
1272
|
*
|
|
1211
1273
|
* NOTE: the snapshot keys on PEER ID, so a node with no persistent identity
|
|
1212
1274
|
* key never lands a `CadrePeer` row ({@link registerSelf} skips) and its
|
|
1213
|
-
* siblings deny its control-DB streams
|
|
1214
|
-
* owner status is a KEY, not a peer id, so
|
|
1215
|
-
* Harmless today (a real deployment persists
|
|
1216
|
-
* case appears only in tests, which configure
|
|
1217
|
-
* `push-wake-e2e.integration.ts` design note 3).
|
|
1218
|
-
* ever becomes a supported deployment, this
|
|
1275
|
+
* siblings deny its control-DB streams, and leave it out of their cohorts, once
|
|
1276
|
+
* their own snapshot is non-empty — owner status is a KEY, not a peer id, so
|
|
1277
|
+
* being the owner does not exempt it. Harmless today (a real deployment persists
|
|
1278
|
+
* its identity; the ephemeral-owner case appears only in tests, which configure
|
|
1279
|
+
* the owner as bootstrap infra — `push-wake-e2e.integration.ts` design note 3).
|
|
1280
|
+
* If an ephemeral-identity node ever becomes a supported deployment, this rule
|
|
1281
|
+
* needs a key-based admission.
|
|
1282
|
+
*/
|
|
1283
|
+
private isControlNetworkMember;
|
|
1284
|
+
/**
|
|
1285
|
+
* Per-stream authorization for the protocol guard's `members-snapshot` class
|
|
1286
|
+
* (`control-protocol-guard.ts`): the four Optimystic control-DB protocols
|
|
1287
|
+
* (`/optimystic/control-<party>/{repo,cluster,sync,block-transfer}/…`), FRET's
|
|
1288
|
+
* five, and any protocol nobody classed. Also wired as Optimystic's
|
|
1289
|
+
* `authorizeInboundStream` in {@link createControlNode} for the four DB
|
|
1290
|
+
* protocols. The fail-closed layer behind the fail-open connection gater.
|
|
1291
|
+
* Control node ONLY: strand cohort nodes legitimately serve cross-party peers.
|
|
1292
|
+
*
|
|
1293
|
+
* Admits exactly the peers {@link isControlNetworkMember} does, and logs why it
|
|
1294
|
+
* refused one. With a sync predicate the upstream deadline
|
|
1295
|
+
* (`authorizeInboundStreamTimeoutMs`) never trips, so it stays at its default.
|
|
1219
1296
|
*/
|
|
1220
1297
|
private authorizeInboundControlStream;
|
|
1221
1298
|
/**
|
|
@@ -1235,9 +1312,50 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1235
1312
|
*
|
|
1236
1313
|
* The trusted-owner anchor sync ({@link syncOwnerAnchor}) runs FIRST, so the snapshot
|
|
1237
1314
|
* built here judges `CadrePeer` rows against the anchor as the `OwnerKey` table now
|
|
1238
|
-
* has it. The invitation chain both reads may need
|
|
1315
|
+
* has it. The invitation chain both reads may need, and the tombstone list the sync and
|
|
1316
|
+
* the two end-of-refresh checks ({@link hangUpRevokedControlPeers}, then
|
|
1317
|
+
* {@link noteOwnRemoval}) read, are each loaded at most once per refresh.
|
|
1239
1318
|
*/
|
|
1240
1319
|
private refreshAuthorizedControlPeers;
|
|
1320
|
+
/**
|
|
1321
|
+
* Hang up this node's control connections to every peer that a `CadrePeer` tombstone names
|
|
1322
|
+
* and that has no live `CadrePeer` row: a machine removed from the cadre, whose streams the
|
|
1323
|
+
* protocol guard already refuses but whose open connection would otherwise stay up until it
|
|
1324
|
+
* dropped on its own. Runs at the end of every membership refresh: on the remover from the
|
|
1325
|
+
* removal's own membership notification, so the connection is closed before
|
|
1326
|
+
* {@link removeMachine} returns; on every other machine from the refresh that follows the
|
|
1327
|
+
* replicated tombstone.
|
|
1328
|
+
*
|
|
1329
|
+
* Denies only on evidence held, the strand enforcer's rule (`strand-revocation-enforcer.ts`):
|
|
1330
|
+
* a peer that merely dropped out of the authorized snapshot (an anchor read that failed, a
|
|
1331
|
+
* voucher whose owner is gone) keeps its connection, self is never hung up, and a peer with a
|
|
1332
|
+
* tombstone AND a live row under a fresh stamp was re-admitted and keeps its connection too.
|
|
1333
|
+
* The tombstones are the refresh's own read (`loadTombstones`, shared with
|
|
1334
|
+
* {@link syncOwnerAnchor}), taken only when some connected peer has no live row, so a cadre
|
|
1335
|
+
* with every connection accounted for costs nothing here. A failed `hangUp` is logged and
|
|
1336
|
+
* retried by the next refresh. Never rejects.
|
|
1337
|
+
*/
|
|
1338
|
+
private hangUpRevokedControlPeers;
|
|
1339
|
+
/**
|
|
1340
|
+
* Learn that an owner removed this node from the cadre, at the end of every membership
|
|
1341
|
+
* refresh: a `CadrePeer` tombstone names this node's peer id, `rows` (the refresh's own
|
|
1342
|
+
* retired-stamp-filtered read) has no live row for it, and an earlier refresh of this process
|
|
1343
|
+
* saw one ({@link ownLiveRow}). Then it records {@link removal} and emits `cadre:removed`,
|
|
1344
|
+
* once. Nothing is stopped here: stopping and deleting are the embedder's, whose storage,
|
|
1345
|
+
* saved starts and process lifetime they are.
|
|
1346
|
+
*
|
|
1347
|
+
* The "seen live" condition keeps a machine joining again, whose fresh store can sync the old
|
|
1348
|
+
* tombstone before its new row, from reporting itself removed. A re-admission under a fresh
|
|
1349
|
+
* stamp is a live row, so a tombstone plus a live row is never a removal. The tombstones are
|
|
1350
|
+
* the refresh's shared read, taken only once the own row is missing. Never rejects.
|
|
1351
|
+
*/
|
|
1352
|
+
private noteOwnRemoval;
|
|
1353
|
+
/**
|
|
1354
|
+
* The tombstone's signer when its signature verifies and the key is in this node's
|
|
1355
|
+
* trusted-owner anchor, else null. A tombstone that fails this is still a removal (every
|
|
1356
|
+
* other machine honours it, and this node's own row is gone), so it is logged, not refused.
|
|
1357
|
+
*/
|
|
1358
|
+
private anchoredRevocationSigner;
|
|
1241
1359
|
/**
|
|
1242
1360
|
* Add the configured `controlNetwork.bootstrapNodes` to the control node's address book
|
|
1243
1361
|
* and dial each peer once — what `@libp2p/bootstrap` would have done had the node been
|
|
@@ -1270,6 +1388,18 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1270
1388
|
* reference apps), and a write it signed would only be refused.
|
|
1271
1389
|
*/
|
|
1272
1390
|
private enrolledOwnerSigningKey;
|
|
1391
|
+
/**
|
|
1392
|
+
* Whether this node can act as an owner of its cadre: its identity key is wired as its
|
|
1393
|
+
* owner signing key ({@link initializeSeedBootstrap}) and enrolled in the live `OwnerKey`
|
|
1394
|
+
* set ({@link enrolledOwnerSigningKey}). What the owner-only flows (`createCadreInvitation`,
|
|
1395
|
+
* `claimNode`, `rentNode`) need; false before `start()`, and on a machine that joined as a
|
|
1396
|
+
* member. The reference apps' "is this device an owner" check.
|
|
1397
|
+
*/
|
|
1398
|
+
isOwner(): Promise<boolean>;
|
|
1399
|
+
/** {@link enrolledOwnerSigningKey} judged against an owner set the caller has already read. */
|
|
1400
|
+
private ownerSigningKeyEnrolledIn;
|
|
1401
|
+
/** This machine's identity key as an owner signing key, when its seed bootstrap is wired to sign; enrollment not yet checked. */
|
|
1402
|
+
private ownerSigningKeyCandidate;
|
|
1273
1403
|
/**
|
|
1274
1404
|
* Schedule the node's initial self-record publish + ongoing refresh shortly
|
|
1275
1405
|
* after start (non-blocking). {@link registerSelf} is idempotent and safely
|
|
@@ -1407,17 +1537,30 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1407
1537
|
*/
|
|
1408
1538
|
getIdentityOwnerKey(): Ed25519KeyPair;
|
|
1409
1539
|
/**
|
|
1410
|
-
* Publish this node's own address record from the reconcile pass
|
|
1411
|
-
*
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1417
|
-
*
|
|
1418
|
-
*
|
|
1419
|
-
|
|
1420
|
-
|
|
1540
|
+
* Publish this node's own address record from the reconcile pass until its first publish of
|
|
1541
|
+
* this session lands. A node with no owner key — seeded (`addDrone`, `cadre enroll add`),
|
|
1542
|
+
* claimed (`claimNode`) or otherwise authorized after it booted — cannot insert its own
|
|
1543
|
+
* `CadrePeer` row; the row an owner wrote for it reaches it by replication once the two
|
|
1544
|
+
* connect, usually after the boot-time publish and the first-growth drain have both found
|
|
1545
|
+
* nothing to publish. Without this the record would wait for the heartbeat
|
|
1546
|
+
* ({@link startRecordRefresh}), and the cadre could not resolve the node's addresses until
|
|
1547
|
+
* then. The reconcile pass is where replicated membership is picked up, so each pass tries.
|
|
1548
|
+
* It also retries an owner's boot-time publish that failed.
|
|
1549
|
+
*
|
|
1550
|
+
* A try before the row has arrived is a `skipped` publish: one `CadrePeer` read. Not tried
|
|
1551
|
+
* while the node waits to be claimed (it has no row and no party yet) or once it has learned
|
|
1552
|
+
* it was removed (nothing to publish). A removed node that never saw its own row live in this
|
|
1553
|
+
* process does not learn it was removed ({@link noteOwnRemoval}), and pays that read on every
|
|
1554
|
+
* pass.
|
|
1555
|
+
*
|
|
1556
|
+
* Connected-only: a row written elsewhere arrives over a connection, and an owner's publish
|
|
1557
|
+
* committed alone is local-only and forks its own history — a restarted owner whose embedder
|
|
1558
|
+
* runs a pass before it reconnects would write its row on the stale side of the fork, and
|
|
1559
|
+
* then never see rows written while it was down (`cadre-invite-any-member` runs that pass and
|
|
1560
|
+
* fails every time without this condition). The boot-time publish and the first-growth drain already try at the node's lone and
|
|
1561
|
+
* first-connection moments; this retries on the passes after.
|
|
1562
|
+
*/
|
|
1563
|
+
private publishFirstSelfRecord;
|
|
1421
1564
|
/**
|
|
1422
1565
|
* Collect this node's current dialable addresses for publication, signaling
|
|
1423
1566
|
* (`/p2p-circuit`) first. Prefers the best invite/NAT-resolved set and folds
|
|
@@ -1480,8 +1623,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1480
1623
|
* ({@link bootstrapDialAddrs}).
|
|
1481
1624
|
*
|
|
1482
1625
|
* An address naming a DIFFERENT trailing peer id is dropped: it does not reach
|
|
1483
|
-
* `peerId`, so it does not belong in this peer's candidate list (
|
|
1484
|
-
*
|
|
1626
|
+
* `peerId`, so it does not belong in this peer's candidate list (`dialPeerAddrs`
|
|
1627
|
+
* binds every address to the peer it is given and drops the same shape one
|
|
1628
|
+
* layer lower; here it is dropped before it can reach the address book).
|
|
1485
1629
|
*/
|
|
1486
1630
|
private normalizeDialAddrs;
|
|
1487
1631
|
/**
|
|
@@ -1654,9 +1798,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1654
1798
|
*
|
|
1655
1799
|
* The list may name transports this node cannot dial (a lent node reports TCP
|
|
1656
1800
|
* and `/ws` addresses to a phone that dials WebSockets only). That needs no
|
|
1657
|
-
* filtering here: libp2p's
|
|
1658
|
-
*
|
|
1659
|
-
*
|
|
1801
|
+
* filtering here: libp2p's transport manager rejects an address no transport
|
|
1802
|
+
* can dial before touching the network (`TransportUnavailableError`), so such
|
|
1803
|
+
* an address costs a log line.
|
|
1660
1804
|
*
|
|
1661
1805
|
* The list is NOT handed to one `dial()`. libp2p tries a multi-address dial's
|
|
1662
1806
|
* addresses one after another under a single deadline, and sorts loopback
|
|
@@ -3147,6 +3291,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3147
3291
|
*
|
|
3148
3292
|
* Passing an empty list stops the supervisor and resets the posture to `none`
|
|
3149
3293
|
* without dialing or waiting.
|
|
3294
|
+
*
|
|
3295
|
+
* NOTE: on a node with no `network.relayAddrs` (a browser tab with a hand-written
|
|
3296
|
+
* bare `/p2p-circuit`), libp2p's relay discovery stays on, because it was the only
|
|
3297
|
+
* thing filling that slot before this call. Such a tab connected to some other
|
|
3298
|
+
* relay-capable peer it identifies (a party member running the relay server) can
|
|
3299
|
+
* lose its one slot to that peer before this supervisor's request runs, the theft
|
|
3300
|
+
* `nodeTransportsOption` prevents on configured nodes (`relay-reservation.ts`). If a
|
|
3301
|
+
* tab is ever seen reserved through a relay it was not asked to use, build that
|
|
3302
|
+
* node through `withoutRelayDiscovery` too, or have the tab configure `relayAddrs`.
|
|
3150
3303
|
*/
|
|
3151
3304
|
reserveRelays(addrs: string[], opts?: RelayReservationSupervisorOptions): Promise<RelayReservationState>;
|
|
3152
3305
|
/**
|
|
@@ -3238,6 +3391,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3238
3391
|
* {@link isMember}). Rows whose stamp is retired in `CadreControl.Revocation`
|
|
3239
3392
|
* never reach here: {@link ControlDatabase.queryCadrePeers} excludes them, so a
|
|
3240
3393
|
* revoked peer is not addressable either.
|
|
3394
|
+
*
|
|
3395
|
+
* NOTE: answers the whole `CadrePeerRow`, past the two fields declared; the integration
|
|
3396
|
+
* harness (`owner-cli-node.ts`, `OwnerCliMemberRow.sig`) reads `sig` off it through the admin
|
|
3397
|
+
* channel to tell a row another member seated from one the subject published itself. Narrow
|
|
3398
|
+
* this to the declared fields only together with that reader.
|
|
3241
3399
|
*/
|
|
3242
3400
|
listMembers(): Promise<Array<{
|
|
3243
3401
|
peerId: string;
|
|
@@ -3319,6 +3477,14 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3319
3477
|
* source so the membership refresh can share one load with {@link syncOwnerAnchor}.
|
|
3320
3478
|
*/
|
|
3321
3479
|
private collectAuthorizedMembers;
|
|
3480
|
+
/** The invitation chain, loaded only when some row in `rows` is invitation-admitted. */
|
|
3481
|
+
private chainFor;
|
|
3482
|
+
/**
|
|
3483
|
+
* The authorized members among `rows`, already read: checks 1 to 4 of
|
|
3484
|
+
* {@link listAuthorizedMembers}' predicate (check 5 is `queryCadrePeers`'s), so the membership
|
|
3485
|
+
* refresh can judge the rows it has to read anyway for the hang-up of removed peers.
|
|
3486
|
+
*/
|
|
3487
|
+
private authorizedAmong;
|
|
3322
3488
|
/**
|
|
3323
3489
|
* Checks 2–4 of the authorized-membership predicate (see
|
|
3324
3490
|
* {@link listAuthorizedMembers}) for one `CadrePeer` row, by the kind of proof it
|
|
@@ -3357,7 +3523,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3357
3523
|
*
|
|
3358
3524
|
* NOTE: cost per membership refresh is one `OwnerKey` read and one `Revocation` read (the
|
|
3359
3525
|
* tombstone list, whose `OwnerKey` stamps are handed to `queryOwnerKeyRows` so it does not
|
|
3360
|
-
* read the retired set again
|
|
3526
|
+
* read the retired set again, and which the hang-up of removed peers shares through
|
|
3527
|
+
* `loadTombstones`), plus the invitation chain when some owner row is
|
|
3361
3528
|
* invitation-admitted — shared with the membership read through `loadChain`; the memo
|
|
3362
3529
|
* suggestion on {@link loadInvitationChain} covers that part. Before the Revocation ledger
|
|
3363
3530
|
* marker is filed that one read is a cohort consult per refresh, which
|
|
@@ -3365,6 +3532,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3365
3532
|
*
|
|
3366
3533
|
* @param retry - forwarded to every read; `false` on the refresh path, for the reason on
|
|
3367
3534
|
* {@link refreshAuthorizedControlPeers}.
|
|
3535
|
+
* @param loadTombstones - the refresh's shared tombstone read ({@link revocationSource}).
|
|
3368
3536
|
*/
|
|
3369
3537
|
private syncOwnerAnchor;
|
|
3370
3538
|
/**
|
|
@@ -3387,6 +3555,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3387
3555
|
* {@link loadInvitationChain}, every later call shares that load.
|
|
3388
3556
|
*/
|
|
3389
3557
|
private invitationChainSource;
|
|
3558
|
+
/** A per-refresh {@link RevocationSource}: one `queryRevocations` read, shared by every caller in the pass. */
|
|
3559
|
+
private revocationSource;
|
|
3390
3560
|
/**
|
|
3391
3561
|
* The usage and invitation rows {@link hasAnchoredProof} and {@link syncOwnerAnchor}
|
|
3392
3562
|
* resolve an invitation-admitted row through, keyed for lookup. Both reads honour `retry`
|
|
@@ -3563,6 +3733,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3563
3733
|
* Remove a previously-authorized peer from the cadre.
|
|
3564
3734
|
* Signs the peer ID with the owner key and deletes the CadrePeer row.
|
|
3565
3735
|
*
|
|
3736
|
+
* Removes the `CadrePeer` row only. The call that removes a machine completely (its owner
|
|
3737
|
+
* key, its push token and the invitations it could come back through, in one transaction)
|
|
3738
|
+
* is {@link removeMachine}.
|
|
3739
|
+
*
|
|
3566
3740
|
* @param peerId - The peer ID to remove
|
|
3567
3741
|
*/
|
|
3568
3742
|
removePeer(peerId: string): Promise<void>;
|
|
@@ -3582,10 +3756,82 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3582
3756
|
* tombstone rides the committed-delete seam ({@link noteGuardedDelete}), so one committed
|
|
3583
3757
|
* while alone is re-issued on cohort growth like every other removal.
|
|
3584
3758
|
*
|
|
3759
|
+
* Removes the `OwnerKey` row only. The call that removes a machine completely, its owner key
|
|
3760
|
+
* included, is {@link removeMachine}.
|
|
3761
|
+
*
|
|
3585
3762
|
* @param ownerPublicKeyB64 - the owner's ed25519 public key (base64url).
|
|
3586
3763
|
* @returns `true` when the row was removed, `false` when the key is not a live owner here.
|
|
3587
3764
|
*/
|
|
3588
3765
|
removeOwner(ownerPublicKeyB64: string): Promise<boolean>;
|
|
3766
|
+
/**
|
|
3767
|
+
* The cadre's machines as an app lists them ({@link CadreMachine}): one entry per live
|
|
3768
|
+
* `CadrePeer` row, judged by this node's membership predicate, plus one per live `OwnerKey`
|
|
3769
|
+
* whose key names a machine with no row (`describeMachines` in `machine-removal.ts`).
|
|
3770
|
+
* Unlocked reads with the default retry.
|
|
3771
|
+
*/
|
|
3772
|
+
listMachines(): Promise<CadreMachine[]>;
|
|
3773
|
+
/**
|
|
3774
|
+
* What {@link removeMachine} would do for `peerId`, read without writing, for an app's
|
|
3775
|
+
* confirmation step ({@link MachineRemovalPlan}): whether this node may remove it, whether its
|
|
3776
|
+
* owner key goes with it, which rows that key vouched (so the app can offer to keep them by
|
|
3777
|
+
* re-vouching), which invitations it could come back through, and whether this node's own
|
|
3778
|
+
* rows depend on it (`planMachineRemoval` in `machine-removal.ts`).
|
|
3779
|
+
*/
|
|
3780
|
+
planMachineRemoval(peerId: string): Promise<MachineRemovalPlan>;
|
|
3781
|
+
/**
|
|
3782
|
+
* Remove a machine from the cadre completely, signed by this node's owner key: its
|
|
3783
|
+
* `CadrePeer` row, its `OwnerKey` row when its key is an owner, its `DeviceToken` row (a
|
|
3784
|
+
* removed phone must not be push-woken) and a withdrawal of every live invitation it could
|
|
3785
|
+
* redeem to come back, in ONE transaction ({@link ControlDatabase.removeMachine}), with a
|
|
3786
|
+
* `Revocation` tombstone per row so the removal holds on every machine
|
|
3787
|
+
* ("Deletes made while alone" in docs/architecture.md).
|
|
3788
|
+
*
|
|
3789
|
+
* Steps: re-plan ({@link planMachineRemoval}) and throw {@link MachineRemovalRefusedError} on a
|
|
3790
|
+
* refusal; check `options.revouch` against the plan's dependents and throw before any write
|
|
3791
|
+
* when it names anything else; **re-vouch first**, under this node's key, the dependents the
|
|
3792
|
+
* caller chose to keep (`ControlDatabase.revouchOwnerKey`, `reauthorizePeer`), so they are
|
|
3793
|
+
* never untrusted in between, and stop before the removal if one fails (re-vouches already
|
|
3794
|
+
* made stay, harmlessly); then the removal transaction. Afterwards this node forgets the
|
|
3795
|
+
* machine as a dial target, and the refresh the write notifies hangs up its control
|
|
3796
|
+
* connection ({@link hangUpRevokedControlPeers}) before this resolves.
|
|
3797
|
+
*
|
|
3798
|
+
* Not undone by this call: the machine keeps every row and strand block it already synced
|
|
3799
|
+
* and the party's strand member keys (`tickets/blocked/decide-removed-machine-strand-access`).
|
|
3800
|
+
* A fresh invitation re-admits it later under fresh stamps.
|
|
3801
|
+
*
|
|
3802
|
+
* @returns what was written ({@link MachineRemovalResult}); all false when no live row names
|
|
3803
|
+
* the machine here (already removed elsewhere).
|
|
3804
|
+
* @throws {MachineRemovalRefusedError} `'not-an-owner'`, `'self'` or `'last-owner'`.
|
|
3805
|
+
*/
|
|
3806
|
+
removeMachine(peerId: string, options?: RemoveMachineOptions): Promise<MachineRemovalResult>;
|
|
3807
|
+
/**
|
|
3808
|
+
* The reads behind {@link listMachines}: the live `CadrePeer` and `OwnerKey` rows and the
|
|
3809
|
+
* machines projected from them, with this node's membership predicate as the `trusted` test.
|
|
3810
|
+
*/
|
|
3811
|
+
private readMachines;
|
|
3812
|
+
/** {@link planMachineRemoval} with the rows it was judged from, which {@link removeMachine} re-vouches dependents by. */
|
|
3813
|
+
private planMachineRemovalWith;
|
|
3814
|
+
/**
|
|
3815
|
+
* The re-vouch step of {@link removeMachine}: every chosen dependent row is rebound to this
|
|
3816
|
+
* node's key BEFORE the removal, so no reader ever judges it against an owner that is gone.
|
|
3817
|
+
* Owners through `ControlDatabase.revouchOwnerKey` (a delete-and-re-add under a fresh stamp,
|
|
3818
|
+
* the only rebinding `OwnerKey.NoUpdate` allows); peers through the owner re-touch
|
|
3819
|
+
* (`reauthorizePeer`), which rewrites `VouchOwner`/`VouchSig` and clears `VouchUsage`.
|
|
3820
|
+
*
|
|
3821
|
+
* The peer re-touch keeps the row's `UpdatedAt` where it is. The owner branch of
|
|
3822
|
+
* `CadrePeer.AuthorizedUpdate` does not require a bump, and bumping a self-published row
|
|
3823
|
+
* would leave its `Sig` unverifiable, so unresolvable and unlisted as self-published, until
|
|
3824
|
+
* the machine's next heartbeat (the NOTE on that constraint); the write-while-alone drain
|
|
3825
|
+
* bumps because it re-issues an otherwise unchanged row, whereas here the voucher itself
|
|
3826
|
+
* changes. A row never self-published carries no stamp to keep and gets the clock.
|
|
3827
|
+
*
|
|
3828
|
+
* A failure rethrows and stops the call before the removal; re-vouches already made stay
|
|
3829
|
+
* (the row is only re-signed by a current owner) and the caller retries. An owner re-vouch
|
|
3830
|
+
* committed alone is not re-issued on growth (the add half has no re-issue path,
|
|
3831
|
+
* `tickets/backlog/debt-owner-key-add-while-alone-has-no-reissue`), so it is logged as
|
|
3832
|
+
* {@link addOwner} logs it; the peer re-touches are re-issued by the existing drain.
|
|
3833
|
+
*/
|
|
3834
|
+
private revouchDependents;
|
|
3589
3835
|
/**
|
|
3590
3836
|
* Create a seed from the current control network state.
|
|
3591
3837
|
* The seed contains peer information and is signed by an owner.
|
|
@@ -3706,6 +3952,20 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3706
3952
|
* Use when both nodes are NAT'd (phone-to-phone).
|
|
3707
3953
|
*/
|
|
3708
3954
|
addPhoneWithRelay(phonePeerId: string): Promise<DroneInitResult>;
|
|
3955
|
+
/**
|
|
3956
|
+
* Where a device could redeem a cadre invitation minted now from, once this machine is gone
|
|
3957
|
+
* ({@link CadreInvitationReach}): the same member selection {@link createCadreInvitation}
|
|
3958
|
+
* makes, judged without minting anything. `rentNode` reads it to choose between inviting a
|
|
3959
|
+
* provider's container and claiming it; `local-network` counts as not reachable there, since
|
|
3960
|
+
* the container is never on a member's LAN. Requires a started node.
|
|
3961
|
+
*/
|
|
3962
|
+
invitationReach(): Promise<CadreInvitationReach>;
|
|
3963
|
+
/**
|
|
3964
|
+
* The `members` a cadre invitation minted now would name, and how far they reach: one
|
|
3965
|
+
* selection behind both {@link createCadreInvitation} and {@link invitationReach}, so the
|
|
3966
|
+
* two cannot disagree.
|
|
3967
|
+
*/
|
|
3968
|
+
private selectInvitationMembers;
|
|
3709
3969
|
/**
|
|
3710
3970
|
* Mint an owner-signed cadre invitation that a device can redeem at ANY member machine of
|
|
3711
3971
|
* this party, the owner offline included ({@link redeemCadreInvitation}). Requires the
|
|
@@ -3723,9 +3983,13 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3723
3983
|
* only, never the raw replicated `OwnerKey` table, because the device anchors whatever
|
|
3724
3984
|
* arrives and a stranger's genesis-inserted key must not ride an invitation into a fresh
|
|
3725
3985
|
* node's anchor; an empty anchor is refused because a reply could not be checked), and
|
|
3726
|
-
*
|
|
3727
|
-
*
|
|
3728
|
-
*
|
|
3986
|
+
* at most six addresses: this machine's first ({@link resolveInviteAddresses}, which
|
|
3987
|
+
* honours pushed NAT addresses), then two each of up to three other members
|
|
3988
|
+
* (`selectCadreInvitationMembers`), so the encoded text fits a QR code.
|
|
3989
|
+
*
|
|
3990
|
+
* The result's `reach` says where a device could redeem it from once this machine is gone
|
|
3991
|
+
* (`cadreInvitationReach`, judged on the other members' addresses only), so an app can warn
|
|
3992
|
+
* before handing over an invitation only it could answer.
|
|
3729
3993
|
*
|
|
3730
3994
|
* The row commits locally and replicates like any other control write; minted while
|
|
3731
3995
|
* alone (a phone with no connection), it reaches the other members through the
|
|
@@ -3762,7 +4026,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3762
4026
|
* Steps: refuse an invitation for another party; pin its `ownerKeys` into the node-local
|
|
3763
4027
|
* anchor (`'invite'` provenance — the pin sticks even if the redemption then fails, as it
|
|
3764
4028
|
* does for a seed invite, so a later attempt or seed from the same owner is anchored);
|
|
3765
|
-
* sign the request; dial the listed members in order (
|
|
4029
|
+
* sign the request; dial the listed members in order (`redeemAtMembers`); on
|
|
3766
4030
|
* acceptance verify the reply against the pinned keys, merge its dial hints into the peer
|
|
3767
4031
|
* store, retain the answering member as a cold-start dial target, refresh the membership
|
|
3768
4032
|
* gate and start a cohort reconcile pass. This node then syncs the control database over
|
|
@@ -3771,11 +4035,20 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3771
4035
|
* A device that is already a member (a restart with the invitation still configured) is
|
|
3772
4036
|
* answered as accepted by the member's idempotent redemption, with no second usage row.
|
|
3773
4037
|
*
|
|
4038
|
+
* One pass over the listed members by default. With `options.waitForMember` the call keeps
|
|
4039
|
+
* making passes while every answer is retryable — for a device redeeming while no
|
|
4040
|
+
* always-on member is up — until a member accepts, one refuses finally, the invitation
|
|
4041
|
+
* expires (thrown as the `invite-spent` refusal) or `options.signal` aborts
|
|
4042
|
+
* (`redeemInPasses`). The request is signed once, before the first pass, and every pass
|
|
4043
|
+
* sends it: its `usageStampId` keys the usage row, so a member that admitted this node on a
|
|
4044
|
+
* pass whose reply was lost answers the next as already a member. `options.onProgress`
|
|
4045
|
+
* reports each pass and pause.
|
|
4046
|
+
*
|
|
3774
4047
|
* When the invitation grants ownership, the member seats this node's key as an `OwnerKey`
|
|
3775
|
-
* row by consent. The caller still has to wire that key for signing afterwards
|
|
3776
|
-
*
|
|
3777
|
-
*
|
|
3778
|
-
*
|
|
4048
|
+
* row by consent. The caller still has to wire that key for signing afterwards, with
|
|
4049
|
+
* {@link wireOwnKeyIfOwner}; never `ensureOwnerKey`, the row is already there. Other
|
|
4050
|
+
* machines derive this node's key into their anchors from the row's stored proof on their
|
|
4051
|
+
* next membership refresh ({@link syncOwnerAnchor}).
|
|
3779
4052
|
*
|
|
3780
4053
|
* Limit: the reply check ({@link verifyRedeemReply}) does not authenticate the member. A
|
|
3781
4054
|
* forged bundle, or a legitimate one whose addresses were swapped, dials a machine that
|
|
@@ -3786,10 +4059,29 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3786
4059
|
*
|
|
3787
4060
|
* @throws `CadreInviteRejectedError` on a final refusal, `CadreInviteReplyInvalidError` on
|
|
3788
4061
|
* an acceptance that fails the reply check, `CadreInviteUnreachableError` when every
|
|
3789
|
-
* listed address was tried without an acceptance
|
|
3790
|
-
* preconditions above.
|
|
4062
|
+
* listed address was tried without an acceptance (one pass only), the signal's reason on
|
|
4063
|
+
* an abort, and a plain `Error` for the preconditions above.
|
|
4064
|
+
*/
|
|
4065
|
+
redeemCadreInvitation(invitation: CadreInvitation, options?: RedeemCadreInvitationOptions): Promise<RedeemCadreInvitationResult>;
|
|
4066
|
+
/**
|
|
4067
|
+
* If this node's identity key is a live `OwnerKey` here, wire it for signing
|
|
4068
|
+
* ({@link initializeSeedBootstrap}) and return true; otherwise change nothing and return
|
|
4069
|
+
* false. For a machine that joined someone else's cadre by invitation: after an
|
|
4070
|
+
* owner-granting redemption, and on every later start. A redemption seats the request's
|
|
4071
|
+
* `peerKey`, the key behind this node's peer id, so the identity key is the one to look for.
|
|
4072
|
+
*
|
|
4073
|
+
* Unlike `ensureOwnerKey` it never inserts an `OwnerKey` row, so a machine that joined a
|
|
4074
|
+
* cadre can never genesis-insert itself into it, and a non-owner never anchors its own key.
|
|
4075
|
+
* An owner whose key is already wired (an earlier call) is answered true with nothing redone.
|
|
4076
|
+
*
|
|
4077
|
+
* NOTE: a node running the listener-only seed service ({@link enableSeedListener}) cannot be
|
|
4078
|
+
* wired this way: libp2p takes one seed handler, and {@link initializeSeedBootstrap} would
|
|
4079
|
+
* register a second. Nothing that joins by invitation enables the listener today; if one
|
|
4080
|
+
* does, replace the listener here rather than beside it.
|
|
4081
|
+
*
|
|
4082
|
+
* @throws when the node is not started.
|
|
3791
4083
|
*/
|
|
3792
|
-
|
|
4084
|
+
wireOwnKeyIfOwner(): Promise<boolean>;
|
|
3793
4085
|
/**
|
|
3794
4086
|
* Replace this node's strand solicitation service — the formation responder every node
|
|
3795
4087
|
* installs at {@link start}, and the initiator side of {@link formStrand}. Optional: call it
|
|
@@ -3842,17 +4134,21 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3842
4134
|
*/
|
|
3843
4135
|
createOpenInvitation(sAppId: string, expirationMs?: number): Promise<OpenInvitation>;
|
|
3844
4136
|
/**
|
|
3845
|
-
* Addresses of the party's other machines for an invitation's bootstrap list
|
|
3846
|
-
*
|
|
3847
|
-
|
|
3848
|
-
|
|
3849
|
-
|
|
3850
|
-
*
|
|
4137
|
+
* Addresses of the party's other machines for an open strand invitation's bootstrap list
|
|
4138
|
+
* ({@link createOpenInvitation}); {@link selectInvitationSiblingAddrs} picks which.
|
|
4139
|
+
*/
|
|
4140
|
+
private siblingInvitationAddrs;
|
|
4141
|
+
/**
|
|
4142
|
+
* The party's other machines an invitation may name — an open strand invitation
|
|
4143
|
+
* ({@link siblingInvitationAddrs}) or a cadre invitation ({@link createCadreInvitation}) —
|
|
4144
|
+
* and which of them this node is connected to. The source is each authorized member's
|
|
4145
|
+
* signed `CadrePeer` record ({@link resolvePeerRecord}), so a machine whose record is
|
|
4146
|
+
* missing, stale or untrusted is left out; live connections only order them.
|
|
3851
4147
|
*
|
|
3852
4148
|
* Best-effort: a failed read leaves the invitation naming this machine alone, which is what
|
|
3853
4149
|
* it named before siblings were added.
|
|
3854
4150
|
*/
|
|
3855
|
-
private
|
|
4151
|
+
private resolveInvitationSiblings;
|
|
3856
4152
|
/**
|
|
3857
4153
|
* Form a strand with a responder via an open invitation: one attempt, from this machine, with
|
|
3858
4154
|
* nothing recorded party-wide. {@link requestJoin} is the path that keeps trying.
|