@serfab/cadre-core 1.16.0 → 1.17.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 +1 -1
- package/dist/bootstrap-peer-store-file.js +1 -1
- package/dist/bootstrap-peer-store.d.ts +1 -1
- package/dist/bootstrap-peer-store.js +4 -4
- package/dist/cadre-invite-protocol.d.ts +55 -138
- package/dist/cadre-invite-protocol.js +94 -449
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-join-request.d.ts +15 -2
- package/dist/cadre-join-request.js +22 -3
- package/dist/cadre-join-request.js.map +1 -1
- package/dist/cadre-node.d.ts +530 -247
- package/dist/cadre-node.js +1046 -525
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +36 -14
- package/dist/control-authorization.js +20 -3
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +22 -23
- package/dist/control-cohort.js +24 -24
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +416 -136
- package/dist/control-database.js +757 -378
- package/dist/control-database.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +5 -4
- package/dist/control-protocol-guard.js +6 -3
- package/dist/control-protocol-guard.js.map +1 -1
- package/dist/control-schema-version.d.ts +80 -0
- package/dist/control-schema-version.js +203 -0
- package/dist/control-schema-version.js.map +1 -0
- package/dist/control-schema.d.ts +45 -5
- package/dist/control-schema.js +254 -70
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +5 -5
- package/dist/control-write-retry.js +10 -8
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +13 -0
- package/dist/delegate-admission.js +27 -0
- package/dist/delegate-admission.js.map +1 -1
- package/dist/ed25519-key.d.ts +10 -7
- package/dist/ed25519-key.js +23 -20
- package/dist/ed25519-key.js.map +1 -1
- package/dist/edge-node/config.d.ts +122 -0
- package/dist/edge-node/config.js +199 -0
- package/dist/edge-node/config.js.map +1 -0
- package/dist/edge-node/dissolve.d.ts +95 -0
- package/dist/edge-node/dissolve.js +184 -0
- package/dist/edge-node/dissolve.js.map +1 -0
- package/dist/edge-node/edge-join.d.ts +44 -0
- package/dist/edge-node/edge-join.js +227 -0
- package/dist/edge-node/edge-join.js.map +1 -0
- package/dist/edge-node/edge-node.d.ts +295 -0
- package/dist/edge-node/edge-node.js +704 -0
- package/dist/edge-node/edge-node.js.map +1 -0
- package/dist/edge-node/index.d.ts +18 -0
- package/dist/edge-node/index.js +18 -0
- package/dist/edge-node/index.js.map +1 -0
- package/dist/edge-node/join-credential.d.ts +11 -0
- package/dist/edge-node/join-credential.js +11 -0
- package/dist/edge-node/join-credential.js.map +1 -0
- package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
- package/dist/edge-node/lazy-raw-storage.js +134 -0
- package/dist/edge-node/lazy-raw-storage.js.map +1 -0
- package/dist/edge-node/node-local.d.ts +60 -0
- package/dist/edge-node/node-local.js +58 -0
- package/dist/edge-node/node-local.js.map +1 -0
- package/dist/edge-node/options.d.ts +83 -0
- package/dist/edge-node/options.js +110 -0
- package/dist/edge-node/options.js.map +1 -0
- package/dist/edge-node/owner-grant.d.ts +41 -0
- package/dist/edge-node/owner-grant.js +38 -0
- package/dist/edge-node/owner-grant.js.map +1 -0
- package/dist/edge-node/platform.d.ts +140 -0
- package/dist/edge-node/platform.js +12 -0
- package/dist/edge-node/platform.js.map +1 -0
- package/dist/edge-node/strand-move.d.ts +115 -0
- package/dist/edge-node/strand-move.js +223 -0
- package/dist/edge-node/strand-move.js.map +1 -0
- package/dist/edge-node/strands.d.ts +28 -0
- package/dist/edge-node/strands.js +60 -0
- package/dist/edge-node/strands.js.map +1 -0
- package/dist/index.d.ts +16 -7
- package/dist/index.js +26 -7
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +3 -3
- package/dist/joined-strand-store.js +3 -3
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store.d.ts +9 -4
- package/dist/key-store.js +9 -4
- package/dist/key-store.js.map +1 -1
- package/dist/machine-removal.d.ts +19 -39
- package/dist/machine-removal.js +24 -98
- package/dist/machine-removal.js.map +1 -1
- package/dist/member-redemption.d.ts +250 -0
- package/dist/member-redemption.js +485 -0
- package/dist/member-redemption.js.map +1 -0
- package/dist/owner-anchor-sync.d.ts +4 -4
- package/dist/owner-anchor-sync.js +16 -15
- package/dist/owner-anchor-sync.js.map +1 -1
- package/dist/owner-grant-protocol.d.ts +247 -0
- package/dist/owner-grant-protocol.js +330 -0
- package/dist/owner-grant-protocol.js.map +1 -0
- package/dist/owner-key-export.d.ts +70 -0
- package/dist/owner-key-export.js +279 -0
- package/dist/owner-key-export.js.map +1 -0
- package/dist/owner-key-flows.d.ts +55 -0
- package/dist/owner-key-flows.js +60 -0
- package/dist/owner-key-flows.js.map +1 -0
- package/dist/owner-key-store.d.ts +31 -0
- package/dist/owner-key-store.js +81 -0
- package/dist/owner-key-store.js.map +1 -0
- package/dist/owner-keys.d.ts +78 -0
- package/dist/owner-keys.js +133 -0
- package/dist/owner-keys.js.map +1 -0
- package/dist/owner-signer.d.ts +54 -0
- package/dist/owner-signer.js +51 -0
- package/dist/owner-signer.js.map +1 -0
- package/dist/peer-authorization.d.ts +83 -18
- package/dist/peer-authorization.js +123 -53
- package/dist/peer-authorization.js.map +1 -1
- package/dist/provider/rent-node.js +1 -1
- package/dist/provider/rent-node.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +131 -109
- package/dist/seed-bootstrap.js +260 -248
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.js +4 -4
- package/dist/strand-addr-protocol.d.ts +4 -1
- package/dist/strand-addr-protocol.js +8 -2
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-cohort.d.ts +3 -0
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +1 -0
- package/dist/strand-database.js +3 -0
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +4 -3
- package/dist/strand-first-sync-gate.js +4 -3
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +1 -1
- package/dist/strand-membership-writer.d.ts +13 -2
- package/dist/strand-membership-writer.js +52 -16
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-move.d.ts +6 -5
- package/dist/strand-move.js +31 -30
- package/dist/strand-move.js.map +1 -1
- package/dist/strand-peer-redial.js +2 -3
- package/dist/strand-peer-redial.js.map +1 -1
- package/dist/types.d.ts +286 -103
- package/dist/types.js.map +1 -1
- package/package.json +11 -2
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +214 -214
- package/src/cadre-invite-protocol.ts +652 -1052
- package/src/cadre-join-request.ts +24 -3
- package/src/cadre-node.ts +1166 -541
- package/src/control-authorization.ts +47 -14
- package/src/control-cohort.ts +127 -128
- package/src/control-database.ts +940 -456
- package/src/control-protocol-guard.ts +7 -4
- package/src/control-schema-version.ts +251 -0
- package/src/control-schema.ts +265 -70
- package/src/control-write-retry.ts +569 -567
- package/src/delegate-admission.ts +37 -0
- package/src/ed25519-key.ts +174 -171
- package/src/edge-node/config.ts +277 -0
- package/src/edge-node/dissolve.ts +251 -0
- package/src/edge-node/edge-join.ts +319 -0
- package/src/edge-node/edge-node.ts +1045 -0
- package/src/edge-node/index.ts +59 -0
- package/src/edge-node/join-credential.ts +16 -0
- package/src/edge-node/lazy-raw-storage.ts +178 -0
- package/src/edge-node/node-local.ts +84 -0
- package/src/edge-node/options.ts +179 -0
- package/src/edge-node/owner-grant.ts +63 -0
- package/src/edge-node/platform.ts +151 -0
- package/src/edge-node/strand-move.ts +270 -0
- package/src/edge-node/strands.ts +72 -0
- package/src/index.ts +886 -789
- package/src/joined-strand-store.ts +4 -4
- package/src/key-store.ts +10 -4
- package/src/machine-removal.ts +26 -111
- package/src/member-redemption.ts +642 -0
- package/src/owner-anchor-sync.ts +19 -18
- package/src/owner-grant-protocol.ts +532 -0
- package/src/owner-key-export.ts +348 -0
- package/src/owner-key-flows.ts +109 -0
- package/src/owner-key-store.ts +85 -0
- package/src/owner-keys.ts +178 -0
- package/src/owner-signer.ts +94 -0
- package/src/peer-authorization.ts +558 -488
- package/src/provider/rent-node.ts +1 -1
- package/src/seed-bootstrap.ts +301 -312
- package/src/seed-trust-policy.ts +4 -4
- package/src/strand-addr-protocol.ts +547 -538
- package/src/strand-cohort.ts +4 -0
- package/src/strand-database.ts +3 -0
- package/src/strand-first-sync-gate.ts +4 -3
- package/src/strand-instance-manager.ts +1 -1
- package/src/strand-membership-writer.ts +61 -16
- package/src/strand-move.ts +45 -36
- package/src/strand-peer-redial.ts +2 -3
- package/src/types.ts +304 -103
package/dist/cadre-node.d.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
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, CadreMachine, MachineRemovalPlan, MachineRemovalResult,
|
|
4
|
-
import { type Ed25519KeyPair } from './ed25519-key.js';
|
|
3
|
+
import type { CadreNodeConfig, StrandInstance, StrandRow, StrandConfig, FoundStrandConfig, FoundStrandResult, FoundStrandOptions, FoundStrandBackgroundResult, SAppConfig, CadreNodeEvents, ControlNetworkSeed, ApplySeedResult, SeedAckMessage, SeedDeliveryTarget, AddDroneOptions, DroneInitResult, OpenInvitation, FormStrandResult, StrandFormationDisclosure, StrandMembershipInvite, ResolveOpts, SelfRegistrationOutcome, ServiceWakeResult, PushPlatform, DeviceTokenRecord, CadreInviteStatus, CadreMachine, CadreOwnerKey, MachineRemovalPlan, MachineRemovalResult, OwnerKeyRemovalPlan, OwnerKeyRemovalResult, RemoveOwnerOptions, CadreRemoval, CadreInvitationReach, CreateCadreInvitationOptions, CreateCadreInvitationResult, RedeemCadreInvitationOptions, RedeemCadreInvitationResult, CreateOwnerGrantOptions, CreateOwnerGrantResult, OwnerGrantStatus, RedeemOwnerGrantOptions, RedeemOwnerGrantResult, JoinWithOwnerGrantResult, ResolveDeviceTokenOpts, PendingJoinStatus } from './types.js';
|
|
5
4
|
import { type TrustedOwnerStore, type TrustSource } from './trusted-owner-store.js';
|
|
5
|
+
import { type OwnerSign, type OwnerSigner } from './owner-signer.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
9
|
import { type JoinedStrandRecord } from './joined-strand-store.js';
|
|
10
10
|
import { type SAppIdLookup } from './strand-watcher.js';
|
|
11
|
+
import type { CohortPeerRow } from './strand-cohort.js';
|
|
11
12
|
import { type ControlCohortReconcileResult } from './control-cohort.js';
|
|
12
13
|
import { EnrollmentService } from './enrollment.js';
|
|
13
14
|
import { ControlDatabase } from './control-database.js';
|
|
14
15
|
import { type CadreInvitation } from './cadre-invite-protocol.js';
|
|
16
|
+
import { type OwnerGrantBundle } from './owner-grant-protocol.js';
|
|
15
17
|
import { SeedBootstrapService } from './seed-bootstrap.js';
|
|
16
18
|
import { type SeedTrustPolicy } from './seed-trust-policy.js';
|
|
17
19
|
import { StrandSolicitationService, type StrandSolicitationServiceOptions } from './strand-solicitation.js';
|
|
@@ -41,13 +43,13 @@ export declare const STRAND_PEER_ADDR_REFRESH_MS: number;
|
|
|
41
43
|
*/
|
|
42
44
|
export declare const STRAND_PEER_ADDR_RETRY_MS: number;
|
|
43
45
|
/**
|
|
44
|
-
* Default lifetime of an
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
46
|
+
* Default lifetime of an owner grant ({@link CadreNode.createOwnerGrant}): whoever holds the
|
|
47
|
+
* bundle can become an owner, so it is a bearer credential for admin rights and lives only
|
|
48
|
+
* long enough to be pasted or scanned by the device it was minted for. A backup grant is minted
|
|
49
|
+
* with no expiry, by explicit choice.
|
|
48
50
|
*/
|
|
49
|
-
export declare const
|
|
50
|
-
/** Default lifetime of
|
|
51
|
+
export declare const OWNER_GRANT_DEFAULT_TTL_MS: number;
|
|
52
|
+
/** Default lifetime of a cadre invitation ({@link CadreNode.createCadreInvitation}): it admits a machine and grants nothing else, so it need not be short. */
|
|
51
53
|
export declare const CADRE_INVITATION_DEFAULT_TTL_MS: number;
|
|
52
54
|
type EventHandler<T> = (data: T) => void;
|
|
53
55
|
/**
|
|
@@ -134,6 +136,13 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
134
136
|
* once that database is up, on every node.
|
|
135
137
|
*/
|
|
136
138
|
private cadreInviteHandler;
|
|
139
|
+
/**
|
|
140
|
+
* Owner grant redemption handler (`/sereus/owner-grant/1.0.0`): a holder of a grant dials
|
|
141
|
+
* this machine and proves possession of the grant and of the key it seats; the handler seats
|
|
142
|
+
* the row and writes the `OwnerKey` row against this node's control database. Registered by
|
|
143
|
+
* {@link start} beside the invitation handler, on every node.
|
|
144
|
+
*/
|
|
145
|
+
private ownerGrantHandler;
|
|
137
146
|
/**
|
|
138
147
|
* Server-side push-wake fan-out. Constructed by {@link start} only when
|
|
139
148
|
* `config.push` (an injected `PushNotifier` + policy) is present — without it
|
|
@@ -335,6 +344,25 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
335
344
|
* strand is no longer running are pruned on each reconcile pass.
|
|
336
345
|
*/
|
|
337
346
|
private readonly delegateAnnounceAt;
|
|
347
|
+
/**
|
|
348
|
+
* Relay peerIds whose protocol negotiation refused the delegate announcement
|
|
349
|
+
* (strand-addr outcome `unsupported`), recorded and cleared by
|
|
350
|
+
* {@link recordDelegateAnnounces} from every announce pass's outcomes. Only a
|
|
351
|
+
* party-run relay has a membership gate that needs a grant, and it serves
|
|
352
|
+
* strand-addr, so a refusal marks a dedicated relay: {@link circuitRelayTargets}
|
|
353
|
+
* leaves it out of the founding/resume and periodic announcements, which would
|
|
354
|
+
* otherwise spend a protocol negotiation on it every time (gotchoices/sereus#49). Not
|
|
355
|
+
* pruned: one entry per relay this node ever named.
|
|
356
|
+
*
|
|
357
|
+
* {@link announceDelegateToRelay} still asks every relay, remembered or not, and
|
|
358
|
+
* its reply is what clears an entry. A party-run relay registers its strand-addr
|
|
359
|
+
* handler late in its own `start()`, after its libp2p node and relay server are
|
|
360
|
+
* up, so an announcement landing in that window is refused by a relay that serves
|
|
361
|
+
* the protocol a moment later. If the re-drive hook skipped remembered relays too,
|
|
362
|
+
* that relay would stay skipped until this node restarts and its gate would deny
|
|
363
|
+
* every reservation through it.
|
|
364
|
+
*/
|
|
365
|
+
private readonly relaysRefusingStrandAddr;
|
|
338
366
|
/**
|
|
339
367
|
* When each connected sibling is next due a strand-addr RPC for each running
|
|
340
368
|
* strand, keyed `peerStrandKey(siblingControlPeerId, strandId)` → epoch ms; a
|
|
@@ -460,7 +488,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
460
488
|
/**
|
|
461
489
|
* Dial targets learned out of band, keyed by peer id, valued by the multiaddr
|
|
462
490
|
* strings this node was handed (parsed lazily, at dial time). Two writers:
|
|
463
|
-
* {@link recordSeedBootstrapPeers} retains the
|
|
491
|
+
* {@link recordSeedBootstrapPeers} retains the preferred peers of every seed
|
|
464
492
|
* this node applies, and {@link addDrone} retains the addresses of every node
|
|
465
493
|
* this node adds. Two readers: {@link dialColdStartBootstrap} dials every entry
|
|
466
494
|
* while the control database still has no siblings, and
|
|
@@ -777,7 +805,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
777
805
|
/**
|
|
778
806
|
* Construct (or adopt) the node-local trusted-owner anchor and seed the
|
|
779
807
|
* out-of-band pinned keys from `config.trustedOwners`. The anchor's BASE comes
|
|
780
|
-
* only from out of band — genesis self-trust ({@link
|
|
808
|
+
* only from out of band — genesis self-trust ({@link useOwnerSigner}),
|
|
781
809
|
* config pins (here), runtime enrollment pins ({@link trustOwnerKeys}) and a
|
|
782
810
|
* claim; everything else in it is derived from that base along verifiable
|
|
783
811
|
* chains through the replicated `OwnerKey` table ({@link syncOwnerAnchor}),
|
|
@@ -943,12 +971,13 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
943
971
|
getTrustedOwnerStore(): TrustedOwnerStore | null;
|
|
944
972
|
/**
|
|
945
973
|
* Persist out-of-band-established owner keys into the node-local anchor —
|
|
946
|
-
* the runtime enrollment seam: {@link redeemCadreInvitation}
|
|
947
|
-
*
|
|
948
|
-
* holds the pins when the reply and the rows
|
|
974
|
+
* the runtime enrollment seam: {@link redeemCadreInvitation} and
|
|
975
|
+
* {@link redeemOwnerGrant} call it with their bundle's `ownerKeys` BEFORE dialing
|
|
976
|
+
* a member (so the anchor already holds the pins when the reply and the rows
|
|
977
|
+
* that follow are judged), and an
|
|
949
978
|
* embedder calls it with an operator-supplied pin. Idempotent. ('genesis'
|
|
950
979
|
* provenance is reserved for the node's own founding key, seeded internally by
|
|
951
|
-
* {@link
|
|
980
|
+
* {@link useOwnerSigner}; 'chain' is written only by {@link syncOwnerAnchor}.)
|
|
952
981
|
*
|
|
953
982
|
* Validates every key's shape before trusting any of them (all-or-nothing):
|
|
954
983
|
* a malformed entry anywhere in `keys` rejects the whole call before a
|
|
@@ -1095,10 +1124,18 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1095
1124
|
* own strand node, so a NAT'd member's strand node can hold a
|
|
1096
1125
|
* circuit-relay reservation here (see `delegate-admission.ts`).
|
|
1097
1126
|
* Connection only: the per-stream gate below never honors a grant;
|
|
1098
|
-
* 3. the
|
|
1127
|
+
* 3. the peer is a device whose invitation or owner-grant redemption this
|
|
1128
|
+
* node has verified and is writing ({@link admittingDevices}): its
|
|
1129
|
+
* connection carries the exchange, which must not be cut at the
|
|
1130
|
+
* provisional deadline. A LIVE OWNER GRANT does not count as expecting a
|
|
1131
|
+
* stranger the way check 7's invitation does: a backup grant has no
|
|
1132
|
+
* expiry, and counting it would admit every stranger outright for the
|
|
1133
|
+
* party's life, so the holder rides the provisional admission until its
|
|
1134
|
+
* redemption verifies, then this;
|
|
1135
|
+
* 4. the authorized-member set is empty — cold start: the rows that would
|
|
1099
1136
|
* authorize anyone arrive by replication over these very connections;
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1137
|
+
* 5. the peer IS an authorized member; or
|
|
1138
|
+
* 6. an open invitation is OUTSTANDING — at least one unexpired,
|
|
1102
1139
|
* not-fully-consumed invitation this node minted or persisted (see
|
|
1103
1140
|
* `StrandSolicitationService.hasOutstandingInvitation`). A formation
|
|
1104
1141
|
* initiator is another party's peer by design and its token is only
|
|
@@ -1106,19 +1143,19 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1106
1143
|
* "does this node expect a stranger at all?". REGISTERING the responder
|
|
1107
1144
|
* does not count: every node registers one at {@link start}, and
|
|
1108
1145
|
* registering mints no invitation; or
|
|
1109
|
-
*
|
|
1146
|
+
* 7. a LIVE cadre invitation exists — a `CadreInvite` row this node holds
|
|
1110
1147
|
* that is not withdrawn, unexpired, has uses left and whose issuer is
|
|
1111
1148
|
* still an owner (`ControlDatabase.hasLiveCadreInvite`). The device that
|
|
1112
1149
|
* redeems it is a stranger until the redemption writes its row, and its
|
|
1113
1150
|
* proof of possession is only checkable inside `/sereus/cadre-invite/1.0.0`;
|
|
1114
|
-
* same reasoning as check
|
|
1151
|
+
* same reasoning as check 6, and the same expectation-of-a-stranger key.
|
|
1115
1152
|
*
|
|
1116
1153
|
* Ordering is semantically free (the checks are OR'd) but decides who pays:
|
|
1117
|
-
* checks 1-
|
|
1118
|
-
* that is otherwise headed for a provisional admission reaches check
|
|
1119
|
-
*
|
|
1154
|
+
* checks 1-3 are in-memory, 4/5 share one control-DB read, and only a peer
|
|
1155
|
+
* that is otherwise headed for a provisional admission reaches check 6's and
|
|
1156
|
+
* 7's invitation lookups.
|
|
1120
1157
|
*
|
|
1121
|
-
* Caveats of check
|
|
1158
|
+
* Caveats of check 6:
|
|
1122
1159
|
* - the in-memory mint registry dies with the process, so after a restart
|
|
1123
1160
|
* only invitations persisted as `FormationInvite` rows still hold the
|
|
1124
1161
|
* window open (re-mint otherwise);
|
|
@@ -1129,8 +1166,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1129
1166
|
* When every check falls through: `'admit-provisionally'`, on every node,
|
|
1130
1167
|
* relay server or not. The gate sees only a peer id, so it cannot tell an
|
|
1131
1168
|
* outsider from a device whose invitation row this node has not received yet
|
|
1132
|
-
* (it redeems here by seating the row from its bundle, after which check
|
|
1133
|
-
* admits) or from a sibling whose membership row is still replicating (check
|
|
1169
|
+
* (it redeems here by seating the row from its bundle, after which check 7
|
|
1170
|
+
* admits) or from a sibling whose membership row is still replicating (check 5
|
|
1134
1171
|
* admits once it lands; if it has not landed by the deadline, the connection
|
|
1135
1172
|
* is closed and either side's next reconcile dial re-forms it, since outbound
|
|
1136
1173
|
* dials are never gated). The protocol guard refuses all of them every
|
|
@@ -1138,12 +1175,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1138
1175
|
* {@link admitControlRelayReservation} disarms the deadline (see
|
|
1139
1176
|
* `membership-connection-gater.ts` → "The relay-reservation seam").
|
|
1140
1177
|
*
|
|
1141
|
-
* NOTE: check
|
|
1178
|
+
* NOTE: check 4/5 runs a control-DB read per inbound connection
|
|
1142
1179
|
* (`listAuthorizedMembers`); connections are rare and cadres small, and this
|
|
1143
1180
|
* layer is fail-open behind `ADMISSION_DECISION_TIMEOUT_MS`, so the live
|
|
1144
1181
|
* read is safe here — unlike the per-stream gate, which must consult the
|
|
1145
1182
|
* materialized {@link authorizedControlPeers} snapshot instead.
|
|
1146
|
-
* NOTE: checks
|
|
1183
|
+
* NOTE: checks 6 and 7 add two more control reads (`hasOutstandingFormationInvite`,
|
|
1147
1184
|
* `hasLiveCadreInvite`) for a stranger with no locally minted invitation in play, on
|
|
1148
1185
|
* every node, and a provisional connection's deadline runs the whole decision once
|
|
1149
1186
|
* more. If stranger connections ever arrive fast enough for those reads — on arrival
|
|
@@ -1312,9 +1349,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1312
1349
|
*
|
|
1313
1350
|
* The trusted-owner anchor sync ({@link syncOwnerAnchor}) runs FIRST, so the snapshot
|
|
1314
1351
|
* built here judges `CadrePeer` rows against the anchor as the `OwnerKey` table now
|
|
1315
|
-
* has it. The
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1352
|
+
* has it. The tombstone list the sync and the two end-of-refresh checks
|
|
1353
|
+
* ({@link hangUpRevokedControlPeers}, then {@link noteOwnRemoval}) read is loaded at most
|
|
1354
|
+
* once per refresh, as is the invitation chain the membership read may need.
|
|
1318
1355
|
*/
|
|
1319
1356
|
private refreshAuthorizedControlPeers;
|
|
1320
1357
|
/**
|
|
@@ -1381,25 +1418,31 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1381
1418
|
*/
|
|
1382
1419
|
private createPartyJoinedStrandLedger;
|
|
1383
1420
|
/**
|
|
1384
|
-
* This machine's owner
|
|
1385
|
-
*
|
|
1386
|
-
*
|
|
1387
|
-
*
|
|
1388
|
-
*
|
|
1421
|
+
* This machine's owner signer when it can make owner-gated control writes: one is wired
|
|
1422
|
+
* ({@link useOwnerSigner}) and the party's `OwnerKey` table enrolls its key. A phone that
|
|
1423
|
+
* ran self-genesis after another machine founded the party passes the first test and not
|
|
1424
|
+
* the second (`runOwnerGenesis` in the reference apps), and a write it signed would only
|
|
1425
|
+
* be refused.
|
|
1389
1426
|
*/
|
|
1390
|
-
private
|
|
1427
|
+
private enrolledOwnerSigner;
|
|
1391
1428
|
/**
|
|
1392
|
-
* Whether this node can act as an owner of its cadre:
|
|
1393
|
-
*
|
|
1394
|
-
*
|
|
1395
|
-
*
|
|
1396
|
-
*
|
|
1429
|
+
* Whether this node can act as an owner of its cadre: an owner signer is wired
|
|
1430
|
+
* ({@link useOwnerSigner}) and its key is in the live `OwnerKey` set. What the owner-only
|
|
1431
|
+
* flows (`createCadreInvitation`, `claimNode`, `rentNode`) need; false before `start()`,
|
|
1432
|
+
* and on a machine that joined as a member. The reference apps' "is this device an owner"
|
|
1433
|
+
* check.
|
|
1397
1434
|
*/
|
|
1398
1435
|
isOwner(): Promise<boolean>;
|
|
1399
|
-
/** {@link
|
|
1400
|
-
private
|
|
1401
|
-
/**
|
|
1402
|
-
|
|
1436
|
+
/** {@link enrolledOwnerSigner} judged against an owner set the caller has already read. */
|
|
1437
|
+
private ownerSignerEnrolledIn;
|
|
1438
|
+
/**
|
|
1439
|
+
* The owner signer {@link useOwnerSigner} wired, or null: every owner signature this node
|
|
1440
|
+
* makes goes through it. Enrollment is not checked ({@link isOwner} does). A caller that
|
|
1441
|
+
* makes several owner writes as one action opens one `approve` on it and hands the
|
|
1442
|
+
* resulting `sign` to the methods that take one (`publishStrand`, `ensureStrandPartyKey`,
|
|
1443
|
+
* `publishFormationInvite`), as `adoptStrandBundle` does.
|
|
1444
|
+
*/
|
|
1445
|
+
getOwnerSigner(): OwnerSigner | null;
|
|
1403
1446
|
/**
|
|
1404
1447
|
* Schedule the node's initial self-record publish + ongoing refresh shortly
|
|
1405
1448
|
* after start (non-blocking). {@link registerSelf} is idempotent and safely
|
|
@@ -1501,41 +1544,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1501
1544
|
*/
|
|
1502
1545
|
private getSelfSigningKey;
|
|
1503
1546
|
/**
|
|
1504
|
-
* Does the `Strand` row name THIS machine as its founder —
|
|
1505
|
-
*
|
|
1506
|
-
*
|
|
1507
|
-
*
|
|
1508
|
-
*
|
|
1509
|
-
* enough for {@link launchStrand}'s tracked-instance
|
|
1510
|
-
*
|
|
1511
|
-
* NOTE: reads "one owner key per machine" — the reference model, where the owner
|
|
1512
|
-
* key IS the key behind the PeerId. Two machines running the SAME identity key
|
|
1513
|
-
* would both derive `true` and each bootstrap on its own replica, which is the
|
|
1514
|
-
* double-`Header` hazard this derivation exists to avoid. Unreachable today: that
|
|
1515
|
-
* configuration also gives both machines one PeerId, which already breaks control
|
|
1516
|
-
* networking well before any strand launches. Revisit if machines ever share an
|
|
1517
|
-
* owner key while holding distinct transport identities — the derivation would
|
|
1518
|
-
* then need a per-machine discriminator (the `CadrePeer` PeerId) on the row.
|
|
1547
|
+
* Does the `Strand` row name THIS machine as its founder — is its `FounderPeerId` this
|
|
1548
|
+
* node's peer id? A null column (a consent-seated strand, or a hand-built row) derives
|
|
1549
|
+
* `false`: without a positive match this machine must attach, never bootstrap. A peer id
|
|
1550
|
+
* rather than an owner key, so two machines holding one owner key never both bootstrap
|
|
1551
|
+
* (the double-`Header` hazard), and a node derives it before any owner signer is wired.
|
|
1552
|
+
* Pure comparison, no I/O — cheap enough for {@link launchStrand}'s tracked-instance
|
|
1553
|
+
* early return.
|
|
1519
1554
|
*/
|
|
1520
1555
|
private isSelfFoundedRow;
|
|
1521
|
-
/**
|
|
1522
|
-
* The owner keypair (base64url Ed25519) derived from this node's resolved
|
|
1523
|
-
* identity key. In the single-key reference model the owner signing key is
|
|
1524
|
-
* *derived from* the node identity (see {@link ed25519KeyPairFromLibp2p}), so the
|
|
1525
|
-
* same key material protected in a secure enclave backs both.
|
|
1526
|
-
*
|
|
1527
|
-
* Exposed so the hosting app retains control of owner genesis: cadre-core
|
|
1528
|
-
* resolves + protects the identity, then the app sources this pair to drive
|
|
1529
|
-
* `ensureOwnerKey(pub)` + `initializeSeedBootstrap(priv)` itself — cadre-core
|
|
1530
|
-
* never silently runs genesis. A future separate-owner slot would return a
|
|
1531
|
-
* distinct key here instead of the identity-derived one.
|
|
1532
|
-
*
|
|
1533
|
-
* @returns The base64url seed/public-key owner pair.
|
|
1534
|
-
* @throws If called before {@link start} has resolved the identity, or when the
|
|
1535
|
-
* node runs on an ephemeral libp2p key (no `keyStore`/`privateKey` configured),
|
|
1536
|
-
* since that key is internal to libp2p and not exposed.
|
|
1537
|
-
*/
|
|
1538
|
-
getIdentityOwnerKey(): Ed25519KeyPair;
|
|
1539
1556
|
/**
|
|
1540
1557
|
* Publish this node's own address record from the reconcile pass until its first publish of
|
|
1541
1558
|
* this session lands. A node with no owner key — seeded (`addDrone`, `cadre enroll add`),
|
|
@@ -1847,8 +1864,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1847
1864
|
*/
|
|
1848
1865
|
private peerStoreAddrs;
|
|
1849
1866
|
/**
|
|
1850
|
-
* Retain a just-applied seed's
|
|
1851
|
-
* dial targets (see {@link bootstrapPeerStore}).
|
|
1867
|
+
* Retain a just-applied seed's preferred peers (`SeedPeer.preferred`) as cold-start
|
|
1868
|
+
* bootstrap dial targets (see {@link bootstrapPeerStore}).
|
|
1852
1869
|
*
|
|
1853
1870
|
* Called for every seed this node accepts, on BOTH intake paths — the
|
|
1854
1871
|
* {@link applySeed} wrapper (which may run on a throwaway service, so the
|
|
@@ -1856,7 +1873,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1856
1873
|
* `/sereus/seed/1.0.0` handler via `onSeedApplied`. Peers with no address are
|
|
1857
1874
|
* skipped: there is nothing to dial.
|
|
1858
1875
|
*
|
|
1859
|
-
* `
|
|
1876
|
+
* `preferred` is the seed's own claim about its peers, exactly as
|
|
1860
1877
|
* `SeedBootstrapService.applySeed` uses it to choose its dial targets. That is
|
|
1861
1878
|
* sound here for the same reason it is sound there — the whole seed is
|
|
1862
1879
|
* signature-checked against a trust-anchored signer before this runs, and the
|
|
@@ -1886,12 +1903,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1886
1903
|
private retainDialTarget;
|
|
1887
1904
|
/**
|
|
1888
1905
|
* Cold-start branch of the reconcile pass: re-dial every retained out-of-band
|
|
1889
|
-
* dial target (see {@link bootstrapPeerStore}) — in practice the
|
|
1890
|
-
* the seeds this node applied — while the control database still has no
|
|
1906
|
+
* dial target (see {@link bootstrapPeerStore}) — in practice the preferred peers
|
|
1907
|
+
* of the seeds this node applied — while the control database still has no
|
|
1891
1908
|
* siblings to dial.
|
|
1892
1909
|
*
|
|
1893
|
-
* `SeedBootstrapService.applySeed` dials those
|
|
1894
|
-
* When that single dial fails —
|
|
1910
|
+
* `SeedBootstrapService.applySeed` dials those peers exactly ONCE, best-effort.
|
|
1911
|
+
* When that single dial fails — peer momentarily down, relay reservation not
|
|
1895
1912
|
* up yet, NAT traversal lost the race — the joining node has an empty
|
|
1896
1913
|
* `CadrePeer` table and no connection, and the steady-state pass above cannot
|
|
1897
1914
|
* help: it dials only siblings enumerated from that very table. Retrying here
|
|
@@ -1901,9 +1918,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
1901
1918
|
* gated by "the control database has no siblings", so it stops the moment the
|
|
1902
1919
|
* node is actually in the party, and a node that is stranded MUST keep trying —
|
|
1903
1920
|
* a give-up rule would turn a transient outage into a permanent one. The steady
|
|
1904
|
-
* cost is one dial per bootstrap peer per pass (
|
|
1905
|
-
*
|
|
1906
|
-
*
|
|
1921
|
+
* cost is one dial per bootstrap peer per pass (a seed prefers its minter and the
|
|
1922
|
+
* party's always-on machines, a few at most), each failing fast against an
|
|
1923
|
+
* unreachable address.
|
|
1924
|
+
* NOTE: if seeds ever carry many preferred peers, or the reconcile cadence tightens
|
|
1907
1925
|
* well below its 15 s default, add per-peer backoff here.
|
|
1908
1926
|
*
|
|
1909
1927
|
* Best-effort per peer, exactly like {@link dialControlSibling}: one dead
|
|
@@ -2322,7 +2340,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2322
2340
|
* `Strand` row. A joiner (the row arrived over the control network) wants exactly this; a
|
|
2323
2341
|
* FOUNDER wants {@link foundStrand}, which publishes and attaches in one resumable call.
|
|
2324
2342
|
* With no explicit `founder` flag, founder-ness is derived from the row's
|
|
2325
|
-
* `
|
|
2343
|
+
* `FounderPeerId` (see {@link StrandConfig.founder}) — so attaching a row THIS machine
|
|
2326
2344
|
* published (e.g. re-attaching its own orphan after a restart) founds it, and attaching
|
|
2327
2345
|
* anyone else's row joins, without the caller needing to know which it is.
|
|
2328
2346
|
*
|
|
@@ -2393,11 +2411,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2393
2411
|
* fresh publish also clears it for an OPEN strand, but is destructive for a closed one
|
|
2394
2412
|
* (the key exists nowhere else). No app-data wipe is needed for either.
|
|
2395
2413
|
*
|
|
2396
|
-
* The insert is signed
|
|
2397
|
-
*
|
|
2398
|
-
*
|
|
2399
|
-
*
|
|
2400
|
-
*
|
|
2414
|
+
* The insert is signed through this node's owner signer ({@link getOwnerSigner}), in one
|
|
2415
|
+
* approval with a closed strand's party-key mint. The signer's key is persisted on the row
|
|
2416
|
+
* as `FounderOwnerKey` (the schema pins the column to the verified signer), and this node's
|
|
2417
|
+
* peer id as `FounderPeerId` (bound into the signature), which is what later lets any
|
|
2418
|
+
* launch derive "this machine is the founder" from the row alone.
|
|
2401
2419
|
* The key must be enrolled in `OwnerKey` (e.g. via {@link ControlDatabase.ensureOwnerKey}
|
|
2402
2420
|
* at genesis) or the schema's `Strand.AuthorizedInsert` constraint rejects the write.
|
|
2403
2421
|
* Failing loudly here is intentional: a silently-unpublished strand would run
|
|
@@ -2413,15 +2431,23 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2413
2431
|
* {@link addStrand}).
|
|
2414
2432
|
* @param type - `'o'` for open (default) or `'c'` for closed.
|
|
2415
2433
|
* @param memberPrivateKey - Optional shared membership (read) key for a closed strand.
|
|
2434
|
+
* @param sign - The `sign` of an owner approval the caller already holds
|
|
2435
|
+
* ({@link getOwnerSigner}), so a larger action asks once; omitted, this asks for its own.
|
|
2416
2436
|
* @returns The live `Strand` row — the one just inserted, or the matching one already
|
|
2417
2437
|
* there. A closed strand's caller should carry THIS row's `MemberPrivateKey` forward:
|
|
2418
2438
|
* on a repeat it is the stored key, not the argument.
|
|
2419
|
-
* @throws if the node is not started,
|
|
2439
|
+
* @throws if the node is not started, has no owner signer wired, the id is blank or
|
|
2420
2440
|
* unusable as a storage scope key (`InvalidStrandIdError` — see `storage-scope.ts`), a
|
|
2421
2441
|
* row with the same id holds different content, or the control DB rejects the
|
|
2422
2442
|
* (unauthorized) insert.
|
|
2423
2443
|
*/
|
|
2424
|
-
publishStrand(strandId: string, type?: 'o' | 'c', memberPrivateKey?: string): Promise<StrandRow>;
|
|
2444
|
+
publishStrand(strandId: string, type?: 'o' | 'c', memberPrivateKey?: string, sign?: OwnerSign): Promise<StrandRow>;
|
|
2445
|
+
/**
|
|
2446
|
+
* The write half of {@link publishStrand}: insert `desired`, adopting a matching row a
|
|
2447
|
+
* concurrent founder landed first, then mint a closed strand's party key, all under one
|
|
2448
|
+
* owner approval's `sign`.
|
|
2449
|
+
*/
|
|
2450
|
+
private insertStrandRow;
|
|
2425
2451
|
/**
|
|
2426
2452
|
* Found a strand — publish its row cadre-wide AND start the local instance as its
|
|
2427
2453
|
* founder — in one resumable call. The single entry point for creating a strand; a
|
|
@@ -2438,7 +2464,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2438
2464
|
* attempt (as the reference apps do) would otherwise present a key that does not match
|
|
2439
2465
|
* the membership already seated in the strand.
|
|
2440
2466
|
* - **instance already running** → the tracked instance is returned, and — because the
|
|
2441
|
-
* `Strand` row records its
|
|
2467
|
+
* `Strand` row records its founding machine (`FounderPeerId`) — a founder request
|
|
2442
2468
|
* against an instance that was first launched as a joiner now runs the (idempotent,
|
|
2443
2469
|
* insert-if-absent) founder bootstrap on it rather than silently skipping it
|
|
2444
2470
|
* ({@link StrandInstanceManager.foundExistingStrand}). So a strand that something else
|
|
@@ -2447,7 +2473,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2447
2473
|
* `resolveCohortSeed` window — still ends up with its `Strand.Header` written.
|
|
2448
2474
|
*
|
|
2449
2475
|
* Founder-ness is resolved FROM THE ROW, not assumed: this machine founds iff the
|
|
2450
|
-
* resolved row's `
|
|
2476
|
+
* resolved row's `FounderPeerId` is its own peer id. A fresh publish records this
|
|
2451
2477
|
* machine, so the common path founds; a machine that lost a concurrent founding race to
|
|
2452
2478
|
* a sibling adopts the sibling's row and ATTACHES instead — deliberately, since two
|
|
2453
2479
|
* machines bootstrapping the same strand on separate replicas is the double-`Header`
|
|
@@ -2456,12 +2482,41 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2456
2482
|
* `Type` is compared, not adopted: a stored row of the other type means the caller and
|
|
2457
2483
|
* the control plane disagree about what this strand IS, so it throws.
|
|
2458
2484
|
*
|
|
2485
|
+
* **Returning after the publish.** `foundStrand(config, { background: true })` resolves
|
|
2486
|
+
* once the row is published and continues the local start in
|
|
2487
|
+
* {@link FoundStrandBackgroundResult.ready}, for a caller that needs only the row (an
|
|
2488
|
+
* invitation reads just its id and membership key). Publish errors still reject the call
|
|
2489
|
+
* itself. A joiner that redeems an invitation to a closed strand before its start finishes
|
|
2490
|
+
* is refused with the retryable `host-strand-unavailable` and the token stays unspent:
|
|
2491
|
+
* {@link requestJoin} retries that on its backoff, while {@link formStrand} throws it to its
|
|
2492
|
+
* caller once every machine the invitation names has refused.
|
|
2493
|
+
*
|
|
2459
2494
|
* @returns The instance AND the row the strand actually runs under — read the membership
|
|
2460
2495
|
* key from the returned row, not from a freshly minted one ({@link FoundStrandResult}).
|
|
2461
2496
|
* @throws if the node is not started, the id is blank, a published row of the same id has
|
|
2462
2497
|
* a different `Type`, or either half rejects.
|
|
2463
2498
|
*/
|
|
2464
2499
|
foundStrand(config: FoundStrandConfig): Promise<FoundStrandResult>;
|
|
2500
|
+
/**
|
|
2501
|
+
* Publish the strand's row as {@link foundStrand} does, and resolve once it is published;
|
|
2502
|
+
* the local start continues in {@link FoundStrandBackgroundResult.ready}.
|
|
2503
|
+
*
|
|
2504
|
+
* @throws as the default form does for the publish half; the start's own failure rejects
|
|
2505
|
+
* `ready` instead.
|
|
2506
|
+
*/
|
|
2507
|
+
foundStrand(config: FoundStrandConfig, options: FoundStrandOptions): Promise<FoundStrandBackgroundResult>;
|
|
2508
|
+
/**
|
|
2509
|
+
* The publish half of {@link foundStrand}: adopt the stored row or publish a new one, and
|
|
2510
|
+
* derive whether this machine founds it. Every check that can refuse the founding is here,
|
|
2511
|
+
* so both forms of `foundStrand` reject on it before returning.
|
|
2512
|
+
*/
|
|
2513
|
+
private publishFoundingRow;
|
|
2514
|
+
/**
|
|
2515
|
+
* The start half of {@link foundStrand}. Its timing lines (`addStrand: start`, then the
|
|
2516
|
+
* duration or `failed after`) are logged when the start settles, also in the background
|
|
2517
|
+
* form, so a start that hangs still shows as a start line with no end line.
|
|
2518
|
+
*/
|
|
2519
|
+
private attachFoundingRow;
|
|
2465
2520
|
/**
|
|
2466
2521
|
* Resume onto an already-published `Strand` row: keep its stored content, reject a type
|
|
2467
2522
|
* disagreement. The stored `MemberPrivateKey` is authoritative — see {@link foundStrand}
|
|
@@ -2521,7 +2576,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2521
2576
|
* step, not a widening of this method.
|
|
2522
2577
|
*
|
|
2523
2578
|
* @param strandId - The `Strand` row id to remove (as passed to {@link publishStrand}).
|
|
2524
|
-
* @throws if the node is not started,
|
|
2579
|
+
* @throws if the node is not started, has no owner signer wired, the id is blank, or
|
|
2525
2580
|
* the signer is not an enrolled owner (the schema's `Strand.AuthorizedDelete` rejects
|
|
2526
2581
|
* the write and the row survives). A rejection does NOT imply the row survived: the
|
|
2527
2582
|
* local stop runs after the control-plane delete has already committed, so a failure
|
|
@@ -2552,15 +2607,17 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2552
2607
|
* `generateStrandMemberKey` mints). When a row already holds a DIFFERENT key this
|
|
2553
2608
|
* throws — a party has one identity per strand; rotation is a deliberate
|
|
2554
2609
|
* remove-then-insert, not a silent swap.
|
|
2610
|
+
* @param sign - The `sign` of an owner approval the caller already holds; omitted, a
|
|
2611
|
+
* mint asks for its own (a stored key asks for none).
|
|
2555
2612
|
* @returns The live party key: the one just seated, or the stored one.
|
|
2556
2613
|
*/
|
|
2557
|
-
ensureStrandPartyKey(strandId: string, partyMemberPrivateKey?: string): Promise<string>;
|
|
2614
|
+
ensureStrandPartyKey(strandId: string, partyMemberPrivateKey?: string, sign?: OwnerSign): Promise<string>;
|
|
2558
2615
|
/**
|
|
2559
2616
|
* Resolve the party membership key a closed strand's launch threads into the founder
|
|
2560
2617
|
* bootstrap: the explicit attach-time key when given, else the party's persisted
|
|
2561
|
-
* `StrandPartyKey` row, else — on the one machine
|
|
2562
|
-
*
|
|
2563
|
-
*
|
|
2618
|
+
* `StrandPartyKey` row, else — on the one machine the row names as founder (`FounderPeerId`)
|
|
2619
|
+
* — a freshly minted-and-persisted key, which seats the identity for a publish that was
|
|
2620
|
+
* interrupted before its mint. Everyone else resolves undefined: non-founding
|
|
2564
2621
|
* machines never mint (no mint race between a party's machines), and only a founder
|
|
2565
2622
|
* bootstrap needs the key at all.
|
|
2566
2623
|
*
|
|
@@ -2589,22 +2646,23 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2589
2646
|
* from this row. A host minting a closed-strand invite does both (mint +
|
|
2590
2647
|
* publish), exactly as the integration harness's `createInvitation` does.
|
|
2591
2648
|
*
|
|
2592
|
-
* Signs
|
|
2593
|
-
*
|
|
2594
|
-
* Throws loudly if the node isn't started or exposes no signing key.
|
|
2649
|
+
* Signs through the same owner signer as {@link publishStrand}, whose key must be an
|
|
2650
|
+
* enrolled `OwnerKey`. Throws loudly if the node isn't started or has no owner signer wired.
|
|
2595
2651
|
*
|
|
2596
2652
|
* @param token - Invitation token (the `FormationInvite` primary key); use the
|
|
2597
2653
|
* `token` of the {@link OpenInvitation} from {@link createOpenInvitation}.
|
|
2598
2654
|
* @param sAppId - The sApp a redeemed strand will use.
|
|
2599
2655
|
* @param options - Optional `expiresAtMs` (epoch ms), `totalUses`, `validationUrl`,
|
|
2600
2656
|
* `strandId` (bind a closed/pre-existing host strand for provision-then-record).
|
|
2657
|
+
* @param sign - The `sign` of an owner approval the caller already holds; omitted, this
|
|
2658
|
+
* asks for its own.
|
|
2601
2659
|
*/
|
|
2602
2660
|
publishFormationInvite(token: string, sAppId: string, options?: {
|
|
2603
2661
|
expiresAtMs?: number;
|
|
2604
2662
|
totalUses?: number;
|
|
2605
2663
|
validationUrl?: string;
|
|
2606
2664
|
strandId?: string;
|
|
2607
|
-
}): Promise<void>;
|
|
2665
|
+
}, sign?: OwnerSign): Promise<void>;
|
|
2608
2666
|
/**
|
|
2609
2667
|
* Enroll an approver public key allowed to sign off on `ValidationUrl` redemptions.
|
|
2610
2668
|
*
|
|
@@ -2613,10 +2671,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2613
2671
|
* — this is how a party says which outside approver it trusts. Without an enrollment,
|
|
2614
2672
|
* every such invitation is unredeemable.
|
|
2615
2673
|
*
|
|
2616
|
-
* Signs
|
|
2617
|
-
*
|
|
2618
|
-
*
|
|
2619
|
-
*
|
|
2674
|
+
* Signs through the same owner signer as {@link publishStrand}, whose key must be an
|
|
2675
|
+
* enrolled `OwnerKey`. Throws loudly if the node isn't started, has no owner signer wired,
|
|
2676
|
+
* or the key is blank — a blank key would reach the database and fail the signature CHECK
|
|
2677
|
+
* as an opaque constraint error.
|
|
2620
2678
|
*
|
|
2621
2679
|
* Enrolling this node's OWN owner key is permitted and harmless: the domain/action tags
|
|
2622
2680
|
* baked into each authorization digest mean an owner-key signature can never satisfy the
|
|
@@ -2650,22 +2708,22 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2650
2708
|
removeValidationKey(key: string): Promise<void>;
|
|
2651
2709
|
/**
|
|
2652
2710
|
* The approver keys currently enrolled, sorted. Read-only, so unlike
|
|
2653
|
-
* {@link enrollValidationKey} / {@link removeValidationKey} it needs no owner
|
|
2654
|
-
*
|
|
2711
|
+
* {@link enrollValidationKey} / {@link removeValidationKey} it needs no owner signer —
|
|
2712
|
+
* only a started node.
|
|
2655
2713
|
*/
|
|
2656
2714
|
listValidationKeys(): Promise<string[]>;
|
|
2657
2715
|
/**
|
|
2658
|
-
*
|
|
2659
|
-
* {@link unpublishStrand}, {@link
|
|
2660
|
-
* {@link removeValidationKey}
|
|
2661
|
-
*
|
|
2662
|
-
*
|
|
2716
|
+
* The owner signer every owner-signed strand write needs — {@link publishStrand},
|
|
2717
|
+
* {@link unpublishStrand}, {@link ensureStrandPartyKey}, {@link publishFormationInvite},
|
|
2718
|
+
* {@link enrollValidationKey}, {@link removeValidationKey} — failing loudly in one two-part
|
|
2719
|
+
* shape (not started / no signer wired, naming the wiring call as the fix). Enrollment is the
|
|
2720
|
+
* schema's to judge. Narrows `controlDatabase` for the caller: a return means
|
|
2663
2721
|
* `this.controlDatabase` is non-null too.
|
|
2664
2722
|
*
|
|
2665
2723
|
* @param action - Infinitive phrase naming the attempted write, e.g. `'enroll a
|
|
2666
2724
|
* validation key'`; interpolated into both messages.
|
|
2667
2725
|
*/
|
|
2668
|
-
private
|
|
2726
|
+
private requireOwnerSigner;
|
|
2669
2727
|
/**
|
|
2670
2728
|
* Shared strand launch path for both the explicit (`addStrand`) and the
|
|
2671
2729
|
* control-discovered (`handleStrandAdded`) entry points. Resolves the cohort
|
|
@@ -2673,10 +2731,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2673
2731
|
* before emitting `strand:started`.
|
|
2674
2732
|
*
|
|
2675
2733
|
* Founder-ness: an explicit `founder` argument wins (the formation/responder flows pass
|
|
2676
|
-
* one deliberately — their consent-seated rows carry a null `
|
|
2734
|
+
* one deliberately — their consent-seated rows carry a null `FounderPeerId`); when
|
|
2677
2735
|
* unset it is DERIVED from the row, so the control-discovered path
|
|
2678
2736
|
* (`handleStrandAdded`) and a restart's re-attach found this machine's own strands
|
|
2679
|
-
* without the caller having to know. The derivation is pure
|
|
2737
|
+
* without the caller having to know. The derivation is a pure peer-id comparison — no I/O.
|
|
2680
2738
|
*
|
|
2681
2739
|
* Idempotent when the strand manager already tracks `strand.Id` — the watcher
|
|
2682
2740
|
* rediscovers a row this node already started and calls this again via
|
|
@@ -2745,10 +2803,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2745
2803
|
* receiver records an admission grant BEFORE `libp2p.start()` (re-)dials the
|
|
2746
2804
|
* relay reservation — the gate denial there is fatal, not degraded. A
|
|
2747
2805
|
* party-member relay answers the RPC (its control node admits us as a
|
|
2748
|
-
* member); a dedicated ops/ relay does not speak the protocol
|
|
2749
|
-
*
|
|
2750
|
-
*
|
|
2751
|
-
*
|
|
2806
|
+
* member); a dedicated ops/ relay does not speak the protocol, comes back
|
|
2807
|
+
* `unsupported`, and is left out of later passes
|
|
2808
|
+
* ({@link relaysRefusingStrandAddr}) — it has no membership gate and needs no
|
|
2809
|
+
* grant. The relay's direct addr rides along as the dial fallback for a relay
|
|
2810
|
+
* we are not yet connected to.
|
|
2752
2811
|
*/
|
|
2753
2812
|
private resolveCohortSeed;
|
|
2754
2813
|
/**
|
|
@@ -2811,20 +2870,25 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2811
2870
|
* `libp2p.start()`. Kept because it costs one spread and this is the one place
|
|
2812
2871
|
* that would silently stop announcing if that rejection is ever relaxed.
|
|
2813
2872
|
*
|
|
2873
|
+
* Less the relays remembered as refusing the strand-addr protocol
|
|
2874
|
+
* ({@link relaysRefusingStrandAddr}): both callers are announcement passes, and
|
|
2875
|
+
* those relays hold no grant.
|
|
2876
|
+
*
|
|
2814
2877
|
* NOTE: a relay the STRAND node discovers on its own (autorelay — in neither
|
|
2815
2878
|
* source) gets no announcement, and a membership-gated one will deny it; fine
|
|
2816
2879
|
* now, every realistic topology feeds one of the two sources.
|
|
2817
2880
|
*/
|
|
2818
2881
|
private circuitRelayTargets;
|
|
2819
2882
|
/**
|
|
2820
|
-
* Record
|
|
2821
|
-
*
|
|
2883
|
+
* Record what a delegate-carrying announce pass learned about the relays it
|
|
2884
|
+
* dialed: the announce timestamps {@link refreshDelegateGrants} throttles on,
|
|
2885
|
+
* and from `outcomes` which relays refuse the strand-addr protocol
|
|
2886
|
+
* ({@link relaysRefusingStrandAddr}). Every announce site records through here.
|
|
2822
2887
|
*
|
|
2823
|
-
*
|
|
2824
|
-
*
|
|
2825
|
-
*
|
|
2826
|
-
*
|
|
2827
|
-
* costs the strand supervisor its first attempt only (the relay denies the
|
|
2888
|
+
* Timestamps are recorded OPTIMISTICALLY, whatever `collectStrandAddrs` reports
|
|
2889
|
+
* per peer: recording only on success would re-announce to an unreachable relay
|
|
2890
|
+
* on every reconcile tick, one dial timeout each. A failed INITIAL announce costs
|
|
2891
|
+
* the strand supervisor its first attempt only (the relay denies the
|
|
2828
2892
|
* reservation; every re-drive re-announces first through
|
|
2829
2893
|
* {@link announceDelegateToRelay}); a failed REFRESH retries within
|
|
2830
2894
|
* `DELEGATE_GRANT_TTL_MS / 2` (15 min), still inside the 30 min TTL.
|
|
@@ -2860,11 +2924,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
2860
2924
|
* The throttle map is updated afterwards, so the periodic pass does not announce
|
|
2861
2925
|
* again right away.
|
|
2862
2926
|
*
|
|
2863
|
-
*
|
|
2864
|
-
*
|
|
2865
|
-
*
|
|
2866
|
-
*
|
|
2867
|
-
*
|
|
2927
|
+
* Unlike the other announce passes, this asks a relay remembered in
|
|
2928
|
+
* {@link relaysRefusingStrandAddr} too, and its reply clears the entry — see
|
|
2929
|
+
* there for why. Against a dedicated ops relay (no strand-addr RPC) that costs
|
|
2930
|
+
* one refused protocol negotiation (`unsupported`) per re-drive attempt, bounded
|
|
2931
|
+
* by the supervisor's backoff. Never throws on that path; a relay addr that names
|
|
2932
|
+
* no peer id is logged and skipped (the hook's caller runs the drive regardless).
|
|
2868
2933
|
*
|
|
2869
2934
|
* NOTE: against a relay that is DOWN this hook costs up to two strand-addr
|
|
2870
2935
|
* timeouts (dial by peer id, then by addr; 28.5 s each at the default declared link round trip)
|
|
@@ -3342,10 +3407,26 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3342
3407
|
*/
|
|
3343
3408
|
getSAppConfig(strandId: string): SAppConfig | undefined;
|
|
3344
3409
|
/**
|
|
3345
|
-
*
|
|
3346
|
-
*
|
|
3410
|
+
* Make `signer` this node's owner signer (`owner-signer.ts`): every owner signature the node
|
|
3411
|
+
* produces from now on goes through it — seeds, vouches, removals, invitations, strand and
|
|
3412
|
+
* join rows, and the background re-issues. Builds the owner-capable seed bootstrap service
|
|
3413
|
+
* around it, so this must come before any of those. One signer per node; a node started
|
|
3414
|
+
* with `claim` is refused (its claimant owns it).
|
|
3347
3415
|
*
|
|
3348
|
-
*
|
|
3416
|
+
* Anchors the signer's key in the node-local trusted-owner store as `genesis`: wiring a key
|
|
3417
|
+
* declares it an authority of this party.
|
|
3418
|
+
*
|
|
3419
|
+
* @throws when the node is not started, was started with `claim`, or already holds the
|
|
3420
|
+
* seed protocol handler (a second wiring, or {@link enableSeedListener} first).
|
|
3421
|
+
*/
|
|
3422
|
+
useOwnerSigner(signer: OwnerSigner): Promise<void>;
|
|
3423
|
+
/**
|
|
3424
|
+
* {@link useOwnerSigner} with a software signer over `ownerPrivateKey` (`softwareOwnerSigner`):
|
|
3425
|
+
* the owner key held in this process's memory. Kept as a convenience because most tests and
|
|
3426
|
+
* the embedders that hold the key themselves wire it this way; one that keeps the key
|
|
3427
|
+
* elsewhere wires its own signer.
|
|
3428
|
+
*
|
|
3429
|
+
* @param ownerPrivateKey - The owner key's 32-byte Ed25519 seed, base64url.
|
|
3349
3430
|
*/
|
|
3350
3431
|
initializeSeedBootstrap(ownerPrivateKey: string): Promise<void>;
|
|
3351
3432
|
/**
|
|
@@ -3392,15 +3473,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3392
3473
|
* never reach here: {@link ControlDatabase.queryCadrePeers} excludes them, so a
|
|
3393
3474
|
* revoked peer is not addressable either.
|
|
3394
3475
|
*
|
|
3395
|
-
* NOTE: answers the whole `CadrePeerRow`, past the
|
|
3476
|
+
* NOTE: answers the whole `CadrePeerRow`, past the fields declared; the integration
|
|
3396
3477
|
* harness (`owner-cli-node.ts`, `OwnerCliMemberRow.sig`) reads `sig` off it through the admin
|
|
3397
3478
|
* channel to tell a row another member seated from one the subject published itself. Narrow
|
|
3398
3479
|
* this to the declared fields only together with that reader.
|
|
3399
3480
|
*/
|
|
3400
|
-
listMembers(): Promise<
|
|
3401
|
-
peerId: string;
|
|
3402
|
-
multiaddr: string | null;
|
|
3403
|
-
}>>;
|
|
3481
|
+
listMembers(): Promise<CohortPeerRow[]>;
|
|
3404
3482
|
/**
|
|
3405
3483
|
* Probe whether a given peer is a `CadrePeer` member.
|
|
3406
3484
|
*
|
|
@@ -3524,11 +3602,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3524
3602
|
* NOTE: cost per membership refresh is one `OwnerKey` read and one `Revocation` read (the
|
|
3525
3603
|
* tombstone list, whose `OwnerKey` stamps are handed to `queryOwnerKeyRows` so it does not
|
|
3526
3604
|
* read the retired set again, and which the hang-up of removed peers shares through
|
|
3527
|
-
* `loadTombstones`), plus the
|
|
3528
|
-
*
|
|
3529
|
-
*
|
|
3530
|
-
* marker is filed that one read is a cohort consult per refresh, which
|
|
3531
|
-
* `control-founding-consult-budget.spec.ts` pins.
|
|
3605
|
+
* `loadTombstones`), plus the owner-grant chain when some owner row is grant-seated (the
|
|
3606
|
+
* NOTE in the body). Before the Revocation ledger marker is filed that one read is a cohort
|
|
3607
|
+
* consult per refresh, which `control-founding-consult-budget.spec.ts` pins.
|
|
3532
3608
|
*
|
|
3533
3609
|
* @param retry - forwarded to every read; `false` on the refresh path, for the reason on
|
|
3534
3610
|
* {@link refreshAuthorizedControlPeers}.
|
|
@@ -3558,9 +3634,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3558
3634
|
/** A per-refresh {@link RevocationSource}: one `queryRevocations` read, shared by every caller in the pass. */
|
|
3559
3635
|
private revocationSource;
|
|
3560
3636
|
/**
|
|
3561
|
-
* The usage and invitation rows {@link hasAnchoredProof}
|
|
3562
|
-
*
|
|
3563
|
-
*
|
|
3637
|
+
* The usage and invitation rows {@link hasAnchoredProof} resolves an invitation-admitted
|
|
3638
|
+
* `CadrePeer` row through, keyed for lookup. Both reads honour `retry` for
|
|
3639
|
+
* {@link listAuthorizedMembers}' reason.
|
|
3564
3640
|
*
|
|
3565
3641
|
* NOTE: once invitations are the only admission route (ticket
|
|
3566
3642
|
* `cadre-invitations-redeemable-by-any-member`), every cadre with a member not yet
|
|
@@ -3571,6 +3647,12 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3571
3647
|
* when every admitted row is already memoised.
|
|
3572
3648
|
*/
|
|
3573
3649
|
private loadInvitationChain;
|
|
3650
|
+
/**
|
|
3651
|
+
* The usage and grant rows {@link syncOwnerAnchor} resolves a grant-seated `OwnerKey` row
|
|
3652
|
+
* through, keyed for lookup: the owner-grant twin of {@link loadInvitationChain}, read only by
|
|
3653
|
+
* the anchor sync, since a grant admits no machine.
|
|
3654
|
+
*/
|
|
3655
|
+
private loadOwnerGrantChain;
|
|
3574
3656
|
/**
|
|
3575
3657
|
* The owner-voucher half of {@link hasAnchoredProof}: complete voucher, `VouchOwner` in
|
|
3576
3658
|
* the node-local anchor, signature valid over the row's (PeerId, StampId) voucher digest.
|
|
@@ -3635,11 +3717,11 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3635
3717
|
private installSeedBootstrapService;
|
|
3636
3718
|
/**
|
|
3637
3719
|
* The event callbacks every {@link SeedBootstrapService} this node owns is
|
|
3638
|
-
* wired with — shared by the owner-capable service ({@link
|
|
3720
|
+
* wired with — shared by the owner-capable service ({@link useOwnerSigner})
|
|
3639
3721
|
* and the listener-only one ({@link enableSeedListener}) so the two cannot drift.
|
|
3640
3722
|
*
|
|
3641
3723
|
* A seed applied by the INBOUND protocol handler writes no `CadrePeer` row of
|
|
3642
|
-
* its own (it merges the libp2p peer store and dials
|
|
3724
|
+
* its own (it merges the libp2p peer store and dials the seed's preferred peers), so the automatic
|
|
3643
3725
|
* write-driven refresh never fires for it — yet applying it can ANCHOR a new
|
|
3644
3726
|
* owner key, which flips rows already present from unauthorized to authorized.
|
|
3645
3727
|
* Hence the explicit refresh here; without it a peer the freshly anchored owner
|
|
@@ -3729,13 +3811,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3729
3811
|
* @param multiaddrs - Optional multiaddrs for the peer
|
|
3730
3812
|
*/
|
|
3731
3813
|
authorizePeer(peerId: string, multiaddrs?: string[]): Promise<void>;
|
|
3814
|
+
/** {@link authorizePeer}'s body, signing inside the caller's owner approval when it passes `sign` ({@link claimNode}). */
|
|
3815
|
+
private vouchPeer;
|
|
3732
3816
|
/**
|
|
3733
3817
|
* Remove a previously-authorized peer from the cadre.
|
|
3734
3818
|
* Signs the peer ID with the owner key and deletes the CadrePeer row.
|
|
3735
3819
|
*
|
|
3736
|
-
* Removes the `CadrePeer` row only. The call that removes a machine completely (its
|
|
3737
|
-
*
|
|
3738
|
-
*
|
|
3820
|
+
* Removes the `CadrePeer` row only. The call that removes a machine completely (its push
|
|
3821
|
+
* token and the invitations it could come back through too, in one transaction) is
|
|
3822
|
+
* {@link removeMachine}.
|
|
3739
3823
|
*
|
|
3740
3824
|
* @param peerId - The peer ID to remove
|
|
3741
3825
|
*/
|
|
@@ -3745,55 +3829,121 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3745
3829
|
* `OwnerKey.Authorized`; the row stores the vouching pair). The write notifies the
|
|
3746
3830
|
* membership hub, so the refresh it triggers has run by the time this resolves.
|
|
3747
3831
|
*
|
|
3832
|
+
* Refused while alone ({@link ownerKeyWriteAlone}): an `OwnerKey` add has no re-issue path,
|
|
3833
|
+
* so one committed with no control connection would stay on this node.
|
|
3834
|
+
*
|
|
3748
3835
|
* @param ownerPublicKeyB64 - the new owner's ed25519 public key (base64url).
|
|
3836
|
+
* @param options.label - the user's name for the key ("phone", "paper backup"), bound into
|
|
3837
|
+
* the signature; trimmed, at most 64 characters, blank meaning none.
|
|
3749
3838
|
* @returns `true` when the row was seated, `false` when the key is already a live owner.
|
|
3839
|
+
* @throws OwnerKeyAddRefusedError `alone`, before any write.
|
|
3840
|
+
*/
|
|
3841
|
+
addOwner(ownerPublicKeyB64: string, options?: {
|
|
3842
|
+
label?: string;
|
|
3843
|
+
}): Promise<boolean>;
|
|
3844
|
+
/**
|
|
3845
|
+
* Whether an `OwnerKey` add made now ({@link addOwner}, or the re-add of
|
|
3846
|
+
* {@link removeOwner}'s re-vouch) is refused `alone`: this node holds no control connection
|
|
3847
|
+
* while the party has other members. An `OwnerKey` row has no update to re-touch, so nothing
|
|
3848
|
+
* re-issues it on cohort growth, and a row written now would fork this node's history. A
|
|
3849
|
+
* party with no other members cannot fork, so a solo founder adding an owner key is not
|
|
3850
|
+
* refused.
|
|
3851
|
+
*
|
|
3852
|
+
* Waits out an in-flight membership refresh first: until `start`'s first one lands the
|
|
3853
|
+
* member snapshot is empty, which would read as a party with no other members.
|
|
3854
|
+
*/
|
|
3855
|
+
private ownerKeyWriteAlone;
|
|
3856
|
+
/**
|
|
3857
|
+
* Every live owner key with its label, who vouched it, whether this node's owner signer
|
|
3858
|
+
* holds it and whether this node's trusted-owner anchor trusts it ({@link CadreOwnerKey}),
|
|
3859
|
+
* for an app's owner-key screen.
|
|
3860
|
+
*/
|
|
3861
|
+
listOwnerKeys(): Promise<CadreOwnerKey[]>;
|
|
3862
|
+
/**
|
|
3863
|
+
* What {@link removeOwner} would do for `ownerPublicKeyB64`, read without writing, for an
|
|
3864
|
+
* app's confirmation step ({@link OwnerKeyRemovalPlan}): whether this node may remove it,
|
|
3865
|
+
* which rows the key vouched (so the app can offer to keep them by re-vouching), and how this
|
|
3866
|
+
* node's own rows depend on it (`planOwnerKeyRemoval` in `owner-keys.ts`).
|
|
3867
|
+
*/
|
|
3868
|
+
planOwnerKeyRemoval(ownerPublicKeyB64: string): Promise<OwnerKeyRemovalPlan>;
|
|
3869
|
+
/**
|
|
3870
|
+
* Remove an owner key, signed by this node's owner key (which must be a different owner): the
|
|
3871
|
+
* `OwnerKey` row and its `Revocation` tombstone commit together, and the tombstone rides the
|
|
3872
|
+
* committed-delete seam ({@link noteGuardedDelete}), so one committed while alone is re-issued
|
|
3873
|
+
* on cohort growth like every other removal. The machines the key was used on stay members;
|
|
3874
|
+
* removing one is {@link removeMachine}. The owner grants the key issued stop being redeemable
|
|
3875
|
+
* (the plan's `grants` names the live ones) and are not withdrawn: a tombstone over a grant
|
|
3876
|
+
* nobody can redeem would add nothing.
|
|
3877
|
+
*
|
|
3878
|
+
* Steps: re-plan ({@link planOwnerKeyRemoval}) and throw {@link OwnerKeyRemovalRefusedError}
|
|
3879
|
+
* on a refusal; check `options.revouch` against the plan's dependents and throw before any
|
|
3880
|
+
* write when it names anything else; refuse `alone` ({@link ownerKeyWriteAlone}) when an
|
|
3881
|
+
* `OwnerKey` row is to be re-vouched, since that re-add has no re-issue path; **re-vouch
|
|
3882
|
+
* first**, under this node's key, the dependents the caller chose to keep and this node's own
|
|
3883
|
+
* row when the key vouched it (`ControlDatabase.revouchOwnerKey`, `reauthorizePeer`), so they
|
|
3884
|
+
* are never untrusted in between, and stop before the removal if one fails (re-vouches
|
|
3885
|
+
* already made stay, harmlessly); then the removal. The re-vouches and the removal are one owner approval. The
|
|
3886
|
+
* write notifies the membership hub, so the refresh it triggers has run by the time this
|
|
3887
|
+
* resolves.
|
|
3888
|
+
*
|
|
3889
|
+
* @param ownerPublicKeyB64 - the owner's ed25519 public key (base64url).
|
|
3890
|
+
* @returns what was written ({@link OwnerKeyRemovalResult}).
|
|
3891
|
+
* @throws {OwnerKeyRemovalRefusedError} `'not-an-owner'`, `'unknown'`, `'last-owner'`,
|
|
3892
|
+
* `'self'` or `'alone'`, before any write.
|
|
3750
3893
|
*/
|
|
3751
|
-
|
|
3894
|
+
removeOwner(ownerPublicKeyB64: string, options?: RemoveOwnerOptions): Promise<OwnerKeyRemovalResult>;
|
|
3895
|
+
/** {@link planOwnerKeyRemoval} with the rows it was judged from, which {@link removeOwner} re-vouches dependents by. */
|
|
3896
|
+
private planOwnerKeyRemovalWith;
|
|
3752
3897
|
/**
|
|
3753
|
-
*
|
|
3754
|
-
* the
|
|
3755
|
-
*
|
|
3756
|
-
*
|
|
3757
|
-
*
|
|
3898
|
+
* The re-vouch step of {@link removeOwner}: every chosen dependent row is rebound to this
|
|
3899
|
+
* node's key BEFORE the removal, so no reader ever judges it against an owner that is gone.
|
|
3900
|
+
* Owners through `ControlDatabase.revouchOwnerKey` (a delete-and-re-add under a fresh stamp,
|
|
3901
|
+
* the only rebinding `OwnerKey.NoUpdate` allows); peers through the owner re-touch
|
|
3902
|
+
* (`reauthorizePeer`), which rewrites `VouchOwner`/`VouchSig` and clears `VouchUsage`.
|
|
3758
3903
|
*
|
|
3759
|
-
*
|
|
3760
|
-
*
|
|
3904
|
+
* The peer re-touch keeps the row's `UpdatedAt` where it is. The owner branch of
|
|
3905
|
+
* `CadrePeer.AuthorizedUpdate` does not require a bump, and bumping a self-published row
|
|
3906
|
+
* would leave its `Sig` unverifiable, so unresolvable and unlisted as self-published, until
|
|
3907
|
+
* the machine's next heartbeat (the NOTE on that constraint); the write-while-alone drain
|
|
3908
|
+
* bumps because it re-issues an otherwise unchanged row, whereas here the voucher itself
|
|
3909
|
+
* changes. A row never self-published carries no stamp to keep and gets the clock.
|
|
3761
3910
|
*
|
|
3762
|
-
*
|
|
3763
|
-
*
|
|
3911
|
+
* A failure rethrows and stops the call before the removal; re-vouches already made stay
|
|
3912
|
+
* (the row is only re-signed by a current owner) and the caller retries. An owner re-vouch
|
|
3913
|
+
* is refused alone before this runs; one whose last connection drops while it runs is logged,
|
|
3914
|
+
* as {@link addOwner}'s add is.
|
|
3915
|
+
*
|
|
3916
|
+
* NOTE: a peer re-touch committed alone is not queued, and the drain skips self-published
|
|
3917
|
+
* rows anyway, so the other machines may get the removal's tombstone without the new voucher
|
|
3918
|
+
* (`tickets/backlog/bug-owner-key-removal-revouch-made-alone-is-not-reissued`).
|
|
3764
3919
|
*/
|
|
3765
|
-
|
|
3920
|
+
private revouchDependents;
|
|
3766
3921
|
/**
|
|
3767
3922
|
* 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
|
|
3769
|
-
*
|
|
3923
|
+
* `CadrePeer` row, judged by this node's membership predicate (`describeMachines` in
|
|
3924
|
+
* `machine-removal.ts`). Owner keys are not machines; {@link listOwnerKeys} lists them.
|
|
3770
3925
|
* Unlocked reads with the default retry.
|
|
3771
3926
|
*/
|
|
3772
3927
|
listMachines(): Promise<CadreMachine[]>;
|
|
3773
3928
|
/**
|
|
3774
3929
|
* 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
|
|
3776
|
-
*
|
|
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`).
|
|
3930
|
+
* confirmation step ({@link MachineRemovalPlan}): whether this node may remove it and which
|
|
3931
|
+
* invitations it could come back through (`planMachineRemoval` in `machine-removal.ts`).
|
|
3779
3932
|
*/
|
|
3780
3933
|
planMachineRemoval(peerId: string): Promise<MachineRemovalPlan>;
|
|
3781
3934
|
/**
|
|
3782
3935
|
* Remove a machine from the cadre completely, signed by this node's owner key: its
|
|
3783
|
-
* `CadrePeer` row, its `
|
|
3784
|
-
*
|
|
3785
|
-
*
|
|
3786
|
-
*
|
|
3787
|
-
*
|
|
3788
|
-
*
|
|
3789
|
-
*
|
|
3790
|
-
*
|
|
3791
|
-
*
|
|
3792
|
-
*
|
|
3793
|
-
*
|
|
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.
|
|
3936
|
+
* `CadrePeer` row, its `DeviceToken` row (a removed phone must not be push-woken) and a
|
|
3937
|
+
* withdrawal of every live invitation it could redeem to come back, in ONE transaction
|
|
3938
|
+
* ({@link ControlDatabase.removeMachine}), with a `Revocation` tombstone per row so the
|
|
3939
|
+
* removal holds on every machine ("Deletes made while alone" in docs/architecture.md), under
|
|
3940
|
+
* one owner approval. Afterwards this node forgets the machine as a dial target, and the
|
|
3941
|
+
* refresh the write notifies hangs up its control connection ({@link hangUpRevokedControlPeers})
|
|
3942
|
+
* before this resolves.
|
|
3943
|
+
*
|
|
3944
|
+
* Owner keys stay: they are the person's, not the machine's. A lost phone that held one can
|
|
3945
|
+
* still sign, a fresh machine invitation it could redeem included, so removing that key
|
|
3946
|
+
* ({@link removeOwner}) is the step that locks it out.
|
|
3797
3947
|
*
|
|
3798
3948
|
* Not undone by this call: the machine keeps every row and strand block it already synced
|
|
3799
3949
|
* and the party's strand member keys (`tickets/blocked/decide-removed-machine-strand-access`).
|
|
@@ -3801,37 +3951,14 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3801
3951
|
*
|
|
3802
3952
|
* @returns what was written ({@link MachineRemovalResult}); all false when no live row names
|
|
3803
3953
|
* the machine here (already removed elsewhere).
|
|
3804
|
-
* @throws {MachineRemovalRefusedError} `'not-an-owner'
|
|
3954
|
+
* @throws {MachineRemovalRefusedError} `'not-an-owner'` or `'self'`, before any write.
|
|
3805
3955
|
*/
|
|
3806
|
-
removeMachine(peerId: string
|
|
3956
|
+
removeMachine(peerId: string): Promise<MachineRemovalResult>;
|
|
3807
3957
|
/**
|
|
3808
|
-
* The
|
|
3809
|
-
*
|
|
3958
|
+
* The read behind {@link listMachines}: the live `CadrePeer` rows projected as machines, with
|
|
3959
|
+
* this node's membership predicate as the `trusted` test.
|
|
3810
3960
|
*/
|
|
3811
3961
|
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;
|
|
3835
3962
|
/**
|
|
3836
3963
|
* Create a seed from the current control network state.
|
|
3837
3964
|
* The seed contains peer information and is signed by an owner.
|
|
@@ -3852,9 +3979,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3852
3979
|
trustPolicy?: SeedTrustPolicy;
|
|
3853
3980
|
}): Promise<ApplySeedResult>;
|
|
3854
3981
|
/**
|
|
3855
|
-
* Post-process an {@link applySeed} outcome: retain the seed's
|
|
3982
|
+
* Post-process an {@link applySeed} outcome: retain the seed's preferred peers as
|
|
3856
3983
|
* cold-start bootstrap targets, and surface a seed that was accepted but whose
|
|
3857
|
-
* every
|
|
3984
|
+
* every dial failed — the node is now seeded yet unconnected, and the
|
|
3858
3985
|
* cold-start reconcile branch is what gets it out of that state.
|
|
3859
3986
|
*/
|
|
3860
3987
|
private noteAppliedSeed;
|
|
@@ -3910,9 +4037,10 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3910
4037
|
* vouch the node into this cadre and keep its addresses as a dial target. The owner
|
|
3911
4038
|
* side of the claim-secret entry in the Seed Delivery Protocol (`docs/architecture.md`).
|
|
3912
4039
|
*
|
|
3913
|
-
* Requires the seed service with an owner key ({@link
|
|
3914
|
-
* {@link addDrone} does
|
|
3915
|
-
*
|
|
4040
|
+
* Requires the seed service with an owner key ({@link useOwnerSigner}), as
|
|
4041
|
+
* {@link addDrone} does, and like it is one owner approval (the seed and the vouch). The
|
|
4042
|
+
* order is the REVERSE of `addDrone`'s: seed first, row second. A refused claim or a
|
|
4043
|
+
* failed delivery therefore leaves no `CadrePeer` row and
|
|
3916
4044
|
* no retained dial target behind on this node — a phone that typed the wrong secret,
|
|
3917
4045
|
* or scanned a node someone else already owns, has nothing to clean up.
|
|
3918
4046
|
*
|
|
@@ -3936,7 +4064,7 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3936
4064
|
* timed reconcile pass; a caller that wants to wait for the connection awaits
|
|
3937
4065
|
* {@link reconcileControlCohort} itself, with `addDrone`'s caveat that a pass already
|
|
3938
4066
|
* in flight is joined rather than restarted. The claimed node dials this one back too,
|
|
3939
|
-
*
|
|
4067
|
+
* as the seed's minter, when this node has a listen or relay address; a phone
|
|
3940
4068
|
* with neither is reached by its own reconcile pass alone.
|
|
3941
4069
|
*
|
|
3942
4070
|
* @throws {ClaimRefusedError} when the node was reached and refused the claim.
|
|
@@ -3969,13 +4097,13 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
3969
4097
|
/**
|
|
3970
4098
|
* Mint an owner-signed cadre invitation that a device can redeem at ANY member machine of
|
|
3971
4099
|
* this party, the owner offline included ({@link redeemCadreInvitation}). Requires the
|
|
3972
|
-
* owner key ({@link
|
|
4100
|
+
* owner key ({@link useOwnerSigner}).
|
|
3973
4101
|
*
|
|
3974
4102
|
* The invitation is an ed25519 keypair: the public half is the `CadreInvite` row's key,
|
|
3975
|
-
* the private half rides in the returned bundle as the proof of possession.
|
|
3976
|
-
*
|
|
3977
|
-
*
|
|
3978
|
-
*
|
|
4103
|
+
* the private half rides in the returned bundle as the proof of possession. It admits a
|
|
4104
|
+
* machine and grants nothing else (ownership is {@link createOwnerGrant}'s), so it defaults
|
|
4105
|
+
* to a 24-hour lifetime ({@link CADRE_INVITATION_DEFAULT_TTL_MS}) and one use unless `uses`
|
|
4106
|
+
* says otherwise. The bundle carries the signed row (so a
|
|
3979
4107
|
* member that has not received it by replication seats it from the bundle), this node's
|
|
3980
4108
|
* anchored owner keys — every current owner this node has verified, the out-of-band ones
|
|
3981
4109
|
* and those derived along verifiable chains ({@link syncOwnerAnchor}) — (the device pins
|
|
@@ -4000,7 +4128,15 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
4000
4128
|
* @throws when no owner key is wired, the anchor is empty, `uses` is not a positive
|
|
4001
4129
|
* integer, or neither this machine nor any other member has an address.
|
|
4002
4130
|
*/
|
|
4003
|
-
createCadreInvitation(options
|
|
4131
|
+
createCadreInvitation(options?: CreateCadreInvitationOptions): Promise<CreateCadreInvitationResult>;
|
|
4132
|
+
/**
|
|
4133
|
+
* What the two credentials an owner mints share ({@link createCadreInvitation},
|
|
4134
|
+
* {@link createOwnerGrant}), checked in this order: the owner-capable service; the anchor's
|
|
4135
|
+
* keys the holder pins (an empty anchor is refused rather than falling back to the table, since
|
|
4136
|
+
* a reply could not be checked against it); the use count; and the member addresses with their
|
|
4137
|
+
* reach, refused when neither this machine nor any other member has one.
|
|
4138
|
+
*/
|
|
4139
|
+
private issuedBundleParts;
|
|
4004
4140
|
/**
|
|
4005
4141
|
* Every cadre invitation this node holds — minted here or replicated in — with its
|
|
4006
4142
|
* standing ({@link CadreInviteStatus}: live, withdrawn, redemptions recorded).
|
|
@@ -4044,11 +4180,9 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
4044
4180
|
* pass whose reply was lost answers the next as already a member. `options.onProgress`
|
|
4045
4181
|
* reports each pass and pause.
|
|
4046
4182
|
*
|
|
4047
|
-
*
|
|
4048
|
-
*
|
|
4049
|
-
*
|
|
4050
|
-
* machines derive this node's key into their anchors from the row's stored proof on their
|
|
4051
|
-
* next membership refresh ({@link syncOwnerAnchor}).
|
|
4183
|
+
* The invitation admits this node as a machine and nothing else: ownership is an owner
|
|
4184
|
+
* grant ({@link redeemOwnerGrant}, {@link joinWithOwnerGrant}). A key of this node's that is
|
|
4185
|
+
* already an owner here is wired afterwards with {@link wireOwnKeyIfOwner}(signer).
|
|
4052
4186
|
*
|
|
4053
4187
|
* Limit: the reply check ({@link verifyRedeemReply}) does not authenticate the member. A
|
|
4054
4188
|
* forged bundle, or a legitimate one whose addresses were swapped, dials a machine that
|
|
@@ -4064,24 +4198,173 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
4064
4198
|
*/
|
|
4065
4199
|
redeemCadreInvitation(invitation: CadreInvitation, options?: RedeemCadreInvitationOptions): Promise<RedeemCadreInvitationResult>;
|
|
4066
4200
|
/**
|
|
4067
|
-
* If
|
|
4068
|
-
*
|
|
4069
|
-
*
|
|
4070
|
-
*
|
|
4071
|
-
* `
|
|
4201
|
+
* If `signer`'s key is a live `OwnerKey` here, wire it ({@link useOwnerSigner}) and return
|
|
4202
|
+
* true; otherwise change nothing and return false. For a machine that joined someone else's
|
|
4203
|
+
* cadre: after an owner grant seated its key, and on every later start.
|
|
4204
|
+
*
|
|
4205
|
+
* Without `signer` the candidate is the signer already wired, so the call answers whether
|
|
4206
|
+
* that key is still a live owner; with nothing wired it is false. The node's identity key is
|
|
4207
|
+
* never a candidate: a machine's network identity is never an owner key.
|
|
4072
4208
|
*
|
|
4073
4209
|
* Unlike `ensureOwnerKey` it never inserts an `OwnerKey` row, so a machine that joined a
|
|
4074
4210
|
* cadre can never genesis-insert itself into it, and a non-owner never anchors its own key.
|
|
4075
4211
|
* An owner whose key is already wired (an earlier call) is answered true with nothing redone.
|
|
4076
4212
|
*
|
|
4077
4213
|
* NOTE: a node running the listener-only seed service ({@link enableSeedListener}) cannot be
|
|
4078
|
-
* wired this way: libp2p takes one seed handler, and {@link
|
|
4079
|
-
*
|
|
4080
|
-
*
|
|
4081
|
-
*
|
|
4082
|
-
* @throws when the node is not started
|
|
4083
|
-
|
|
4084
|
-
|
|
4214
|
+
* wired this way: libp2p takes one seed handler, and {@link useOwnerSigner} would register a
|
|
4215
|
+
* second. Nothing that joins by invitation enables the listener today; if one does, replace
|
|
4216
|
+
* the listener here rather than beside it.
|
|
4217
|
+
*
|
|
4218
|
+
* @throws when the node is not started, or when `signer` names a different key from the
|
|
4219
|
+
* signer already wired (one signer per node).
|
|
4220
|
+
*/
|
|
4221
|
+
wireOwnKeyIfOwner(signer?: OwnerSigner): Promise<boolean>;
|
|
4222
|
+
/**
|
|
4223
|
+
* Mint an owner-signed owner grant: whoever holds the returned bundle can seat an owner key of
|
|
4224
|
+
* their own through ANY member machine of this party ({@link redeemOwnerGrant}), or join the
|
|
4225
|
+
* party as a machine and an owner at once ({@link joinWithOwnerGrant}), the owner that minted
|
|
4226
|
+
* it offline or not. Requires the owner key ({@link useOwnerSigner}); one owner approval.
|
|
4227
|
+
*
|
|
4228
|
+
* The grant is an ed25519 keypair: the public half is the `OwnerGrant` row's key, the private
|
|
4229
|
+
* half rides in the bundle as the proof of possession. It admits no machine and names no peer
|
|
4230
|
+
* id. Whoever holds the bundle can become an owner, so it defaults to a 15-minute lifetime
|
|
4231
|
+
* ({@link OWNER_GRANT_DEFAULT_TTL_MS}) and one use; `expiresInMs: null` mints a BACKUP grant
|
|
4232
|
+
* with no expiry, which must be asked for explicitly and kept as carefully as an owner key.
|
|
4233
|
+
* The label is the issuer's choice and goes on every key the grant seats ({@link addOwner}'s
|
|
4234
|
+
* rule: trimmed, at most 64 characters, blank meaning none). The bundle carries the signed
|
|
4235
|
+
* row, this node's anchored owner keys and at most six member addresses, chosen as
|
|
4236
|
+
* {@link createCadreInvitation} chooses them and for the same reasons; `reach` says where a
|
|
4237
|
+
* holder could redeem it once this machine is gone. A backup's addresses are those at mint
|
|
4238
|
+
* time, so it can go stale if every member's address changes.
|
|
4239
|
+
*
|
|
4240
|
+
* @throws when no owner key is wired, the anchor is empty, `uses` is not a positive integer,
|
|
4241
|
+
* the label is over 64 characters, or neither this machine nor any other member has an address.
|
|
4242
|
+
*/
|
|
4243
|
+
createOwnerGrant(options?: CreateOwnerGrantOptions): Promise<CreateOwnerGrantResult>;
|
|
4244
|
+
/**
|
|
4245
|
+
* Every owner grant this node holds — minted here or replicated in — with its standing
|
|
4246
|
+
* ({@link OwnerGrantStatus}: live, withdrawn, redemptions recorded).
|
|
4247
|
+
*/
|
|
4248
|
+
listOwnerGrants(): Promise<OwnerGrantStatus[]>;
|
|
4249
|
+
/**
|
|
4250
|
+
* Withdraw an owner grant: an owner-signed `Revocation` tombstone over its row, which stays as
|
|
4251
|
+
* the proof behind every key it seated; those keys stay owners (removing one is
|
|
4252
|
+
* {@link removeOwner}). No further redemption succeeds anywhere the tombstone reaches, and a
|
|
4253
|
+
* member holding it refuses the bundle's copy of the row. A withdrawal committed while alone is
|
|
4254
|
+
* re-issued on cohort growth like any other tombstone.
|
|
4255
|
+
*
|
|
4256
|
+
* @returns `true` when this call filed the tombstone, `false` when the row is not held here or
|
|
4257
|
+
* was already withdrawn.
|
|
4258
|
+
*/
|
|
4259
|
+
withdrawOwnerGrant(key: string): Promise<boolean>;
|
|
4260
|
+
/**
|
|
4261
|
+
* Become an owner of this node's party with an owner grant: seat `signer`'s key as an
|
|
4262
|
+
* `OwnerKey` row by consent, with no owner online. `signer` holds the key to seat; its one
|
|
4263
|
+
* `approve` names the party and says the key becomes an owner of it, and signs the `'consent'`
|
|
4264
|
+
* digest. The grant's `'redeem'` signature is made with the bundle's private key in memory.
|
|
4265
|
+
* A bundle for another party is refused before anything is pinned, dialed or written.
|
|
4266
|
+
*
|
|
4267
|
+
* Two paths, chosen by whether this node is a live member of the party ({@link isLiveMemberSelf})
|
|
4268
|
+
* or an enrolled owner of it ({@link isOwner}):
|
|
4269
|
+
*
|
|
4270
|
+
* - **Local**, on a member machine: the grant row is seated from the bundle when absent (its
|
|
4271
|
+
* own transaction) and the redemption written in this node's own control database
|
|
4272
|
+
* (`ControlDatabase.redeemOwnerGrant`), replicating like any other write. Refused `alone`
|
|
4273
|
+
* under {@link addOwner}'s rule, before the approval: an `OwnerKey` row has no re-issue
|
|
4274
|
+
* path, so one committed with no control connection while the party has other members would
|
|
4275
|
+
* stay here; connect, then try again. A grant that is not live here (withdrawn, expired,
|
|
4276
|
+
* spent, its issuer no longer an owner) is refused before the approval too, unless the key
|
|
4277
|
+
* is already an owner. The bundle's `ownerKeys` are NOT pinned: this node judges the issuer
|
|
4278
|
+
* by its own `OwnerKey` table, and its anchor must not be widened by pasted text.
|
|
4279
|
+
* - **Protocol**, otherwise (a device with no machine in the cadre; a node started in the
|
|
4280
|
+
* party on an empty store): the bundle's `ownerKeys` are pinned into the node-local anchor
|
|
4281
|
+
* (`'invite'` provenance, the pin a credential the owner handed out carries, as
|
|
4282
|
+
* {@link redeemCadreInvitation} pins), then the members the bundle names are dialed over
|
|
4283
|
+
* `/sereus/owner-grant/1.0.0` (`redeemOwnerGrantInPasses`), one signed request for every
|
|
4284
|
+
* address and every pass as {@link redeemCadreInvitation} dials, so a retry after a lost
|
|
4285
|
+
* reply is answered `alreadyOwner`. The member that accepts pushes its control store here
|
|
4286
|
+
* and writes the row with this node counted in its write cohort, so a member alone with
|
|
4287
|
+
* this node still commits. `options` are the invitation's (`waitForMember`, `signal`,
|
|
4288
|
+
* `onProgress`).
|
|
4289
|
+
*
|
|
4290
|
+
* Neither path wires the signer: call {@link wireOwnKeyIfOwner}(signer) afterwards — at once
|
|
4291
|
+
* on the local path, and on the protocol path once the row has synced here. A key that is
|
|
4292
|
+
* already a live owner is answered `alreadyOwner` with no use spent, on both paths. To join
|
|
4293
|
+
* the party as a machine too, use {@link joinWithOwnerGrant}.
|
|
4294
|
+
*
|
|
4295
|
+
* @throws a plain `Error` for a node not started or a bundle naming another party;
|
|
4296
|
+
* {@link OwnerKeyAddRefusedError} `alone` on the local path, `OwnerGrantIssuerUnknownError`
|
|
4297
|
+
* when the issuer is not an owner here, `OwnerGrantRejectedError` `grant-spent` for a
|
|
4298
|
+
* grant no longer live here, and `OwnerGrantRejectedError` `conflict` (retryable) when the
|
|
4299
|
+
* cadre holds the grant row and this node has not synced it yet; on the protocol path
|
|
4300
|
+
* `OwnerGrantRejectedError` on a final refusal, `OwnerGrantReplyInvalidError` on an
|
|
4301
|
+
* acceptance that fails the reply check,
|
|
4302
|
+
* `OwnerGrantUnreachableError` when every address was tried (one pass only), and the
|
|
4303
|
+
* signal's reason on an abort.
|
|
4304
|
+
*/
|
|
4305
|
+
redeemOwnerGrant(bundle: OwnerGrantBundle, signer: OwnerSigner, options?: RedeemOwnerGrantOptions): Promise<RedeemOwnerGrantResult>;
|
|
4306
|
+
/**
|
|
4307
|
+
* {@link redeemOwnerGrant}'s local path: the alone refusal, the seat, the liveness refusal,
|
|
4308
|
+
* then one approval around the consent signature and the write. Every refusal comes before
|
|
4309
|
+
* the approval, so a prompting signer never asks for a redemption this node would refuse.
|
|
4310
|
+
*/
|
|
4311
|
+
private redeemOwnerGrantLocally;
|
|
4312
|
+
/**
|
|
4313
|
+
* {@link redeemOwnerGrant}'s protocol path, given the signed request: dial the bundle's members
|
|
4314
|
+
* in passes, then refresh the membership gate, since the rows the member pushed and wrote are
|
|
4315
|
+
* judged against the pins and the anchor derives this node's key from them once its row is here.
|
|
4316
|
+
*/
|
|
4317
|
+
private redeemOwnerGrantAtMembers;
|
|
4318
|
+
/** The signed request for `bundle` seating `signer`'s key, under an open approval's `sign`; the nonce is minted here, by the holder. */
|
|
4319
|
+
private signOwnerGrantRequest;
|
|
4320
|
+
/**
|
|
4321
|
+
* Join this node's party as a machine AND an owner with one owner grant, for a node started in
|
|
4322
|
+
* the grant's party that is not a member: a fresh install recovering with a backup grant, an
|
|
4323
|
+
* app joining as an owner. Needs a started node with a stable identity, as
|
|
4324
|
+
* {@link redeemCadreInvitation} does, and a bundle for this party.
|
|
4325
|
+
*
|
|
4326
|
+
* Steps, the two signatures made under ONE approval of `signer` before any dial, so a
|
|
4327
|
+
* prompting signer asks once and its prompt does not span the network:
|
|
4328
|
+
*
|
|
4329
|
+
* 1. {@link redeemOwnerGrant}'s protocol path: the member that accepts seats `signer`'s key.
|
|
4330
|
+
* 2. A **self-invitation**: a `CadreInvite` row targeted at this node's peer id, one use, a
|
|
4331
|
+
* short expiry ({@link SELF_INVITATION_TTL_MS}), signed by `signer`
|
|
4332
|
+
* (`ControlDatabase.signCadreInvite`; never inserted here, since this store holds no
|
|
4333
|
+
* `OwnerKey` row for the issuer yet). Its bundle carries the grant's owner keys plus the
|
|
4334
|
+
* new key (the device's reply check wants the row's issuer among the pins) and the grant's
|
|
4335
|
+
* members with the member that accepted the grant first, since that member holds the new
|
|
4336
|
+
* key as a live owner and seats the row from the bundle; one the row has not reached yet
|
|
4337
|
+
* answers `issuer-unknown`, which is retryable.
|
|
4338
|
+
* 3. {@link redeemCadreInvitation} with it: the member admits this node.
|
|
4339
|
+
* 4. {@link wireOwnKeyIfOwner}(signer) once the `OwnerKey` row has synced here, asked every
|
|
4340
|
+
* {@link OWNER_ROW_SYNC_RECHECK_MS} up to {@link OWNER_ROW_SYNC_TIMEOUT_MS}; the result's
|
|
4341
|
+
* `ownerWired` says whether it did in time, and a later call wires it otherwise.
|
|
4342
|
+
*
|
|
4343
|
+
* @throws as {@link redeemOwnerGrant} (protocol path) and {@link redeemCadreInvitation} throw,
|
|
4344
|
+
* and a plain `Error` for an ephemeral identity.
|
|
4345
|
+
*/
|
|
4346
|
+
joinWithOwnerGrant(bundle: OwnerGrantBundle, signer: OwnerSigner, options?: RedeemOwnerGrantOptions): Promise<JoinWithOwnerGrantResult>;
|
|
4347
|
+
/**
|
|
4348
|
+
* {@link wireOwnKeyIfOwner}(signer) once `signer`'s `OwnerKey` row has reached this node, asked
|
|
4349
|
+
* again every {@link OWNER_ROW_SYNC_RECHECK_MS} until it has, the node stops, or
|
|
4350
|
+
* {@link OWNER_ROW_SYNC_TIMEOUT_MS} passes: false then, and a later call wires it. Usually
|
|
4351
|
+
* immediate: the member pushed its whole store, the row included, before the admission write.
|
|
4352
|
+
*/
|
|
4353
|
+
private wireOwnKeyOnceSynced;
|
|
4354
|
+
/**
|
|
4355
|
+
* The started node's handles for a grant call, the bundle's party checked first: a grant for
|
|
4356
|
+
* another party is refused before any pin, dial or write.
|
|
4357
|
+
*/
|
|
4358
|
+
private requireStartedForGrant;
|
|
4359
|
+
/**
|
|
4360
|
+
* Is this node a live member of its party: its own `CadrePeer` row is held here and passes the
|
|
4361
|
+
* membership predicate ({@link hasAnchoredProof}, judged as {@link listAuthorizedMembers}
|
|
4362
|
+
* judges every other row; that listing leaves this node out)? What decides
|
|
4363
|
+
* {@link redeemOwnerGrant}'s path. A machine's own row is written by its enrollment (or its
|
|
4364
|
+
* first self-record, for an owner's machine), so a node on an empty store, or one holding only
|
|
4365
|
+
* a store a member pushed it, is not one.
|
|
4366
|
+
*/
|
|
4367
|
+
private isLiveMemberSelf;
|
|
4085
4368
|
/**
|
|
4086
4369
|
* Replace this node's strand solicitation service — the formation responder every node
|
|
4087
4370
|
* installs at {@link start}, and the initiator side of {@link formStrand}. Optional: call it
|
|
@@ -4191,8 +4474,8 @@ export declare class CadreNode implements SAppIdLookup {
|
|
|
4191
4474
|
*/
|
|
4192
4475
|
dismissPendingJoin(id: string): Promise<boolean>;
|
|
4193
4476
|
private requirePendingJoins;
|
|
4194
|
-
/** This machine's owner
|
|
4195
|
-
private
|
|
4477
|
+
/** This machine's enrolled owner signer ({@link enrolledOwnerSigner}), or a throw carrying `refusal`. */
|
|
4478
|
+
private requireEnrolledOwnerSigner;
|
|
4196
4479
|
/**
|
|
4197
4480
|
* Write a join request, or adopt the party's existing request for the same invitation: a
|
|
4198
4481
|
* pending one as it is, a finished one rewritten as `requested` (the user asked again).
|