@serfab/cadre-core 1.16.0 → 1.18.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 +4 -3
- 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 +81 -142
- package/dist/cadre-invite-protocol.js +168 -458
- 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 +1089 -475
- package/dist/cadre-node.js +2170 -1037
- 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 +88 -32
- package/dist/control-cohort.js +91 -33
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +856 -318
- package/dist/control-database.js +1422 -701
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +46 -43
- package/dist/control-formation-recorder.js +77 -78
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +11 -4
- package/dist/control-protocol-guard.js +20 -5
- package/dist/control-protocol-guard.js.map +1 -1
- package/dist/control-schema-version.d.ts +97 -0
- package/dist/control-schema-version.js +225 -0
- package/dist/control-schema-version.js.map +1 -0
- package/dist/control-schema.d.ts +45 -5
- package/dist/control-schema.js +262 -78
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +45 -10
- package/dist/control-write-retry.js +92 -22
- 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/enrolled-machine-store.d.ts +3 -3
- package/dist/enrolled-machine-store.js +3 -3
- package/dist/formation-token.d.ts +48 -0
- package/dist/formation-token.js +26 -0
- package/dist/formation-token.js.map +1 -0
- package/dist/index.d.ts +27 -17
- package/dist/index.js +39 -17
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +29 -12
- package/dist/joined-strand-store.js +66 -27
- 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/link-budget.d.ts +9 -7
- package/dist/link-budget.js +9 -7
- package/dist/link-budget.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 +256 -0
- package/dist/member-redemption.js +490 -0
- package/dist/member-redemption.js.map +1 -0
- package/dist/membership-view.d.ts +91 -0
- package/dist/membership-view.js +110 -0
- package/dist/membership-view.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 +251 -0
- package/dist/owner-grant-protocol.js +344 -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-addr-book.d.ts +10 -1
- package/dist/peer-addr-book.js +4 -3
- package/dist/peer-addr-book.js.map +1 -1
- 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/peer-join-backfill.d.ts +89 -20
- package/dist/peer-join-backfill.js +265 -108
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/pending-join-runner.d.ts +48 -18
- package/dist/pending-join-runner.js +74 -56
- package/dist/pending-join-runner.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 +162 -133
- package/dist/seed-bootstrap.js +300 -283
- 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 +24 -2
- package/dist/strand-database.js +29 -6
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +176 -88
- package/dist/strand-first-sync-gate.js +288 -121
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +9 -9
- package/dist/strand-formation-deadlines.js +9 -9
- package/dist/strand-formation-manager.d.ts +23 -17
- package/dist/strand-formation-manager.js +46 -44
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +5 -8
- package/dist/strand-formation-protocol.js +101 -16
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +27 -18
- package/dist/strand-instance-manager.js +119 -50
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +16 -13
- package/dist/strand-membership-reconciler.js +30 -22
- package/dist/strand-membership-reconciler.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +45 -22
- package/dist/strand-membership-writer.js +226 -131
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-move.d.ts +6 -5
- package/dist/strand-move.js +36 -32
- 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/strand-revocation-enforcer.d.ts +134 -60
- package/dist/strand-revocation-enforcer.js +144 -71
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +50 -39
- package/dist/strand-solicitation.js +10 -3
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-watcher.d.ts +80 -13
- package/dist/strand-watcher.js +152 -48
- package/dist/strand-watcher.js.map +1 -1
- package/dist/table-change-watch.d.ts +63 -0
- package/dist/table-change-watch.js +209 -0
- package/dist/table-change-watch.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +4 -3
- package/dist/timeout-scheduler.js +4 -3
- package/dist/timeout-scheduler.js.map +1 -1
- package/dist/types.d.ts +374 -150
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -1
- package/package.json +17 -8
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +214 -214
- package/src/cadre-invite-protocol.ts +740 -1052
- package/src/cadre-join-request.ts +24 -3
- package/src/cadre-node.ts +2402 -1079
- package/src/control-authorization.ts +47 -14
- package/src/control-cohort.ts +204 -128
- package/src/control-database.ts +1749 -820
- package/src/control-formation-recorder.ts +359 -353
- package/src/control-protocol-guard.ts +23 -6
- package/src/control-schema-version.ts +301 -0
- package/src/control-schema.ts +273 -78
- package/src/control-write-retry.ts +641 -567
- package/src/delegate-admission.ts +351 -314
- 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/enrolled-machine-store.ts +3 -3
- package/src/formation-token.ts +63 -0
- package/src/index.ts +899 -789
- package/src/joined-strand-store.ts +73 -28
- package/src/key-store.ts +10 -4
- package/src/link-budget.ts +9 -7
- package/src/machine-removal.ts +26 -111
- package/src/member-redemption.ts +649 -0
- package/src/membership-view.ts +144 -0
- package/src/owner-anchor-sync.ts +19 -18
- package/src/owner-grant-protocol.ts +547 -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-addr-book.ts +4 -3
- package/src/peer-authorization.ts +558 -488
- package/src/peer-join-backfill.ts +971 -761
- package/src/pending-join-runner.ts +76 -53
- package/src/provider/rent-node.ts +1 -1
- package/src/seed-bootstrap.ts +343 -349
- 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 +352 -319
- package/src/strand-first-sync-gate.ts +334 -133
- package/src/strand-formation-deadlines.ts +227 -227
- package/src/strand-formation-manager.ts +731 -725
- package/src/strand-formation-protocol.ts +122 -24
- package/src/strand-instance-manager.ts +2092 -2026
- package/src/strand-membership-reconciler.ts +33 -25
- package/src/strand-membership-writer.ts +2093 -1974
- package/src/strand-move.ts +50 -38
- package/src/strand-peer-redial.ts +2 -3
- package/src/strand-revocation-enforcer.ts +740 -654
- package/src/strand-solicitation.ts +61 -34
- package/src/strand-watcher.ts +175 -56
- package/src/table-change-watch.ts +285 -0
- package/src/timeout-scheduler.ts +29 -28
- package/src/types.ts +391 -152
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `EdgeNode.joinCadre`'s sequence: redeem in place on a node already running in the credential's
|
|
3
|
+
* party, or stop the running node, start one in that party, redeem there and go back on any
|
|
4
|
+
* failure (docs/architecture.md → "An app joins an existing cadre"). The credential is a machine
|
|
5
|
+
* invitation, which admits the app as a machine, or an owner grant, which makes it a machine and
|
|
6
|
+
* an owner at once ({@link JoinCredential}). Runs over a narrow view of the runtime
|
|
7
|
+
* ({@link JoinRuntime}), inside one of its transitions.
|
|
8
|
+
*/
|
|
9
|
+
import { exportStrandBundle } from '../strand-move.js';
|
|
10
|
+
import { isOwnerGrant } from './join-credential.js';
|
|
11
|
+
import { adoptMovesInto, deferredOutcome, joinMoveRecord, toError, } from './strand-move.js';
|
|
12
|
+
export async function joinCadre(runtime, credential, options, signal) {
|
|
13
|
+
signal.throwIfAborted();
|
|
14
|
+
const running = runtime.node;
|
|
15
|
+
if (running?.isRunning && runtime.runningOptions?.partyId === credential.partyId) {
|
|
16
|
+
return redeemInPlace(runtime, running, runtime.runningOptions, credential, options, signal);
|
|
17
|
+
}
|
|
18
|
+
return switchParty(runtime, credential, options, signal);
|
|
19
|
+
}
|
|
20
|
+
/** Redeem on the node already running in the credential's party; nothing to switch or roll back. */
|
|
21
|
+
async function redeemInPlace(runtime, node, running, credential, options, signal) {
|
|
22
|
+
const admission = await redeemOnRunningNode(runtime, node, credential, options, signal);
|
|
23
|
+
reportProgress(options, { state: 'admitted', asOwner: admission.asOwner });
|
|
24
|
+
const owner = await runtime.wireJoinedOwner(node, admission.asOwner);
|
|
25
|
+
// An owner grant redeemed on a node that joined as a member finishes the move that join deferred.
|
|
26
|
+
const strandMove = await settleJoinMove(runtime, node, running.formerPartyId, owner);
|
|
27
|
+
if (owner === 'enrolled')
|
|
28
|
+
runtime.setStatus({ state: 'running', node, owner });
|
|
29
|
+
return joinResult(admission, running.formerPartyId, strandMove);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* A machine invitation is redeemed as on a fresh node (a device already admitted is answered as
|
|
33
|
+
* such). An owner grant is redeemed for the owner slot's key: the node is a member of the party,
|
|
34
|
+
* so `redeemOwnerGrant` writes the row in its own store and the key can be wired at once.
|
|
35
|
+
*/
|
|
36
|
+
async function redeemOnRunningNode(runtime, node, credential, options, signal) {
|
|
37
|
+
if (!isOwnerGrant(credential))
|
|
38
|
+
return redeemInvitation(node, credential, options, signal);
|
|
39
|
+
// NOTE: a member running with no control connection to the party is refused `alone`
|
|
40
|
+
// (`OwnerKeyAddRefusedError`) by the grant's local path, where the invitation path waits for a
|
|
41
|
+
// member; if apps meet that, wait for a control connection before redeeming in place.
|
|
42
|
+
const redeemed = await node.redeemOwnerGrant(credential, await runtime.ownerSlotSigner(), redeemOptions(options, signal));
|
|
43
|
+
return { peerId: redeemed.memberPeerId, redeemedAt: new Date().toISOString(), asOwner: true };
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Stop the running node, start one in the credential's party and redeem there; on any
|
|
47
|
+
* failure, go back to what ran before. Nothing is saved until a member has admitted this app.
|
|
48
|
+
* Leaving a party this app founded, its strands are recorded for the move first, so a refusal
|
|
49
|
+
* leaves that party running and unchanged.
|
|
50
|
+
*/
|
|
51
|
+
async function switchParty(runtime, credential, options, signal) {
|
|
52
|
+
const restore = runtime.runningOptions;
|
|
53
|
+
const leaving = restore ?? (await runtime.readSavedStart())?.options ?? null;
|
|
54
|
+
const founded = foundedPartyLeft(credential.partyId, leaving);
|
|
55
|
+
// The party this app founded, carried through any later join.
|
|
56
|
+
// NOTE: leaving a joined party while the move from `formerPartyId` into it is still pending
|
|
57
|
+
// (the key never wired as an owner there) leaves that record naming the party left, adopted
|
|
58
|
+
// only if the app goes back; `pendingStrandMove` keeps reporting it. If apps need the move
|
|
59
|
+
// to follow the app, retarget the record to the credential's party here.
|
|
60
|
+
const formerPartyId = founded?.partyId ?? leaving?.formerPartyId;
|
|
61
|
+
const joined = {
|
|
62
|
+
partyId: credential.partyId,
|
|
63
|
+
// None needed: the redemption retains the member that admits this app as a dial
|
|
64
|
+
// target in the party's bootstrap-peer store, which every later start reads.
|
|
65
|
+
bootstrapAddrs: [],
|
|
66
|
+
relayAddrs: options.relayAddrs ?? leaving?.relayAddrs ?? [],
|
|
67
|
+
origin: 'joined',
|
|
68
|
+
};
|
|
69
|
+
const noiseCryptoMode = options.noiseCryptoMode ?? leaving?.noiseCryptoMode;
|
|
70
|
+
if (noiseCryptoMode !== undefined)
|
|
71
|
+
joined.noiseCryptoMode = noiseCryptoMode;
|
|
72
|
+
if (formerPartyId !== undefined)
|
|
73
|
+
joined.formerPartyId = formerPartyId;
|
|
74
|
+
const movingFrom = founded ? await recordStrandMove(runtime, founded, credential) : undefined;
|
|
75
|
+
try {
|
|
76
|
+
await runtime.shutDown();
|
|
77
|
+
return await startAndRedeem(runtime, joined, credential, options, signal, movingFrom);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
await returnToLeftParty(runtime, restore, movingFrom);
|
|
81
|
+
throw err;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Export the founded party's strands and keep them in the key store for the joined party to
|
|
86
|
+
* adopt; the party's id when there was anything to move. Throws `StrandsNeedOwnerJoinError`, or
|
|
87
|
+
* the export's or the write's error: switching without the record would lose the strands.
|
|
88
|
+
* An empty export removes the party's record instead: one left by an earlier join that never
|
|
89
|
+
* finished describes strands the party no longer holds, and adopting it would bring them back.
|
|
90
|
+
*/
|
|
91
|
+
async function recordStrandMove(runtime, founded, credential) {
|
|
92
|
+
const running = runtime.node;
|
|
93
|
+
const bundle = running?.isRunning
|
|
94
|
+
? await exportStrandBundle(running)
|
|
95
|
+
: await runtime.readAlone(founded, exportStrandBundle, 'export strands');
|
|
96
|
+
const record = joinMoveRecord(bundle, credential);
|
|
97
|
+
if (!record) {
|
|
98
|
+
await runtime.moves.remove(founded.partyId);
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
await runtime.moves.save(record);
|
|
102
|
+
return founded.partyId;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Admitted, the start is saved before the strands are adopted, so an app killed mid-adoption
|
|
106
|
+
* relaunches into the joined party and finishes the move there; `running` is reported after.
|
|
107
|
+
*/
|
|
108
|
+
async function startAndRedeem(runtime, joined, credential, options, signal, movingFrom) {
|
|
109
|
+
runtime.setStatus({ state: 'starting' });
|
|
110
|
+
const { node, resolved } = await runtime.bringUp(joined);
|
|
111
|
+
runtime.setStatus({ state: 'joining', node });
|
|
112
|
+
const admission = await redeemOnJoinedNode(runtime, node, credential, options, signal);
|
|
113
|
+
reportProgress(options, { state: 'admitted', asOwner: admission.asOwner });
|
|
114
|
+
const owner = await runtime.wireJoinedOwner(node, admission.asOwner);
|
|
115
|
+
await runtime.saveRunningStart(resolved);
|
|
116
|
+
if (movingFrom !== undefined && owner === 'enrolled')
|
|
117
|
+
reportProgress(options, { state: 'moving-strands' });
|
|
118
|
+
const strandMove = await settleJoinMove(runtime, node, resolved.formerPartyId, owner);
|
|
119
|
+
runtime.reportRunning(node, resolved, owner);
|
|
120
|
+
return joinResult(admission, resolved.formerPartyId, strandMove);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Redeem on the node started in the credential's party: a machine invitation admits the app, an
|
|
124
|
+
* owner grant makes it a machine and an owner (`CadreNode.joinWithOwnerGrant`).
|
|
125
|
+
*
|
|
126
|
+
* The grant seats the owner slot's key, created now when the slot is empty: the app's one owner
|
|
127
|
+
* key, which may already own the party the app founded and is leaving (one key, several cadres).
|
|
128
|
+
* A fresh key would need a second owner slot and a second signer per node, which the kits do not
|
|
129
|
+
* have, and replacing the slot's key would lose the left party, which a rollback restarts.
|
|
130
|
+
* Created before the redemption, the key stays in the slot when the join fails, an owner of
|
|
131
|
+
* nothing until the next founding or grant uses it.
|
|
132
|
+
*/
|
|
133
|
+
async function redeemOnJoinedNode(runtime, node, credential, options, signal) {
|
|
134
|
+
if (!isOwnerGrant(credential))
|
|
135
|
+
return redeemInvitation(node, credential, options, signal);
|
|
136
|
+
// NOTE: a rollback after the grant was redeemed but the self-invitation refused leaves the
|
|
137
|
+
// slot's key an owner of the credential's party while the app is back in the party it left;
|
|
138
|
+
// a later join there, with a machine invitation too, picks the ownership up through that key.
|
|
139
|
+
// NOTE: `joinWithOwnerGrant` already waits up to its own bound for the key's `OwnerKey` row
|
|
140
|
+
// and wires it; `wireJoinedOwner(node, true)` then asks again for up to the owner-step bound
|
|
141
|
+
// while it is still unwired, so a row that never arrives costs both waits before `timed-out`.
|
|
142
|
+
// If that shows up, carry `joined.ownerWired` in the admission and skip the second wait.
|
|
143
|
+
const joined = await node.joinWithOwnerGrant(credential, await runtime.ownerSlotSigner(), redeemOptions(options, signal));
|
|
144
|
+
return { peerId: joined.peerId, redeemedAt: joined.redeemedAt, asOwner: true };
|
|
145
|
+
}
|
|
146
|
+
async function redeemInvitation(node, invitation, options, signal) {
|
|
147
|
+
const redemption = await node.redeemCadreInvitation(invitation, redeemOptions(options, signal));
|
|
148
|
+
return { ...redemption, asOwner: false };
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* The move from `formerPartyId` as an admitted join reports it: adopted with every other move
|
|
152
|
+
* naming this party once the app's key is wired as its owner, deferred otherwise. Never throws:
|
|
153
|
+
* the app is admitted, and what is not adopted now is adopted by the next start.
|
|
154
|
+
*/
|
|
155
|
+
async function settleJoinMove(runtime, node, formerPartyId, owner) {
|
|
156
|
+
try {
|
|
157
|
+
if (owner === 'enrolled') {
|
|
158
|
+
const outcomes = await adoptMovesInto(node, runtime.moves);
|
|
159
|
+
return formerPartyId === undefined ? undefined : outcomes.get(formerPartyId);
|
|
160
|
+
}
|
|
161
|
+
const record = formerPartyId === undefined ? undefined : await runtime.moves.load(formerPartyId);
|
|
162
|
+
return record?.targetPartyId === node.partyId ? { state: 'deferred', reason: 'owner-key-not-wired' } : undefined;
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
console.warn('[cadre-core/edge-node] reading the strand move records failed; the next start retries:', err);
|
|
166
|
+
return formerPartyId === undefined ? undefined : deferredOutcome(err);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* After a failed join: discard the move record it wrote, stop the joined party's node and start
|
|
171
|
+
* again what ran before, if anything did.
|
|
172
|
+
*/
|
|
173
|
+
async function returnToLeftParty(runtime, restore, movingFrom) {
|
|
174
|
+
await discardStrandMove(runtime.moves, movingFrom);
|
|
175
|
+
try {
|
|
176
|
+
await runtime.shutDown();
|
|
177
|
+
if (restore)
|
|
178
|
+
await runtime.buildAndStart(restore);
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
// The join's own error is the one its caller gets; this one is only logged.
|
|
182
|
+
console.warn('[cadre-core/edge-node] going back to the party a failed join left failed:', err);
|
|
183
|
+
if (runtime.status.state !== 'failed')
|
|
184
|
+
runtime.setStatus({ state: 'failed', error: toError(err) });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
async function discardStrandMove(moves, fromPartyId) {
|
|
188
|
+
if (fromPartyId === undefined)
|
|
189
|
+
return;
|
|
190
|
+
try {
|
|
191
|
+
await moves.remove(fromPartyId);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
// Harmless when left: it names the party the join failed to reach, and the next join from
|
|
195
|
+
// this party replaces it.
|
|
196
|
+
console.warn(`[cadre-core/edge-node] could not discard the strand move record of party ${fromPartyId}:`, err);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** Wait for a member: an app joining by invitation is often the only machine of the party up. The grant's options are the invitation's. */
|
|
200
|
+
function redeemOptions(options, signal) {
|
|
201
|
+
return { waitForMember: true, signal, ...(options.onProgress ? { onProgress: options.onProgress } : {}) };
|
|
202
|
+
}
|
|
203
|
+
/** Tell the join's listener; one that throws must not fail a join a member has already admitted. */
|
|
204
|
+
function reportProgress(options, progress) {
|
|
205
|
+
try {
|
|
206
|
+
options.onProgress?.(progress);
|
|
207
|
+
}
|
|
208
|
+
catch (err) {
|
|
209
|
+
console.warn('[cadre-core/edge-node] join progress listener threw:', err);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* The start a join to `partyId` leaves, when it is of a party this app founded: the party whose
|
|
214
|
+
* strands move and which is kept as `formerPartyId` for clean-up. Null when the join leaves no
|
|
215
|
+
* start, a party this app joined, or the party it joins.
|
|
216
|
+
*/
|
|
217
|
+
function foundedPartyLeft(partyId, leaving) {
|
|
218
|
+
return leaving && leaving.origin !== 'joined' && leaving.partyId !== partyId ? leaving : null;
|
|
219
|
+
}
|
|
220
|
+
function joinResult(admission, formerPartyId, strandMove) {
|
|
221
|
+
return {
|
|
222
|
+
...admission,
|
|
223
|
+
...(formerPartyId !== undefined && { formerPartyId }),
|
|
224
|
+
...(strandMove !== undefined && { strandMove }),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=edge-join.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-join.js","sourceRoot":"","sources":["../../src/edge-node/edge-join.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `createEdgeNode` — build, start, stop and rebuild an edge device's `CadreNode` the prescribed
|
|
3
|
+
* way: identity and owner key in the app's key store, the four node-local stores durable and
|
|
4
|
+
* party-scoped, raw storage per scope from the app's storage, owner genesis, the last start
|
|
5
|
+
* remembered for unattended starts, and the switch to another party's cadre by machine
|
|
6
|
+
* invitation or owner grant.
|
|
7
|
+
*
|
|
8
|
+
* Nothing platform-specific is imported here. The app passes in its key store, its storage and
|
|
9
|
+
* its transports (`EdgeNodePlatform`), so a Node test runs this code over fakes, and the React
|
|
10
|
+
* Native kit's `createPhoneNode` is this over the secure store and rn-leveldb.
|
|
11
|
+
*/
|
|
12
|
+
import { CadreNode } from '../cadre-node.js';
|
|
13
|
+
import type { ExportedOwnerKey, ImportedOwnerKey, OwnerKeyExportOptions } from '../owner-key-flows.js';
|
|
14
|
+
import { type StrandMoveReport } from '../strand-move.js';
|
|
15
|
+
import type { DurableSlot } from '../node-local-snapshot.js';
|
|
16
|
+
import type { CadreJoinProgress, CadreNodeEvents, CadreOwnerKey, CadreRemoval, CreateOwnerGrantOptions, CreateOwnerGrantResult, OwnerGrantStatus, OwnerKeyRemovalPlan, OwnerKeyRemovalResult, RedeemCadreInvitationResult, RemoveOwnerOptions } from '../types.js';
|
|
17
|
+
import { type OwnerGenesisResult } from './config.js';
|
|
18
|
+
import { type DissolveFormerPartyOptions } from './dissolve.js';
|
|
19
|
+
import type { JoinCredential } from './join-credential.js';
|
|
20
|
+
import { type KvStoreApi } from './node-local.js';
|
|
21
|
+
import { type EdgeNodeOptions, type NoiseCryptoMode, type SavedStart } from './options.js';
|
|
22
|
+
import { type RedeemedOwnerGrant } from './owner-grant.js';
|
|
23
|
+
import { type EdgeNodePlatform } from './platform.js';
|
|
24
|
+
import { type PendingStrandMove, type StrandMoveOutcome } from './strand-move.js';
|
|
25
|
+
export type EdgeNodeStatus = {
|
|
26
|
+
state: 'stopped';
|
|
27
|
+
} | {
|
|
28
|
+
state: 'starting';
|
|
29
|
+
}
|
|
30
|
+
/** Started in the party an invitation names and redeeming it there; not admitted yet, so `EdgeNode.node` is null. */
|
|
31
|
+
| {
|
|
32
|
+
state: 'joining';
|
|
33
|
+
node: CadreNode;
|
|
34
|
+
} | {
|
|
35
|
+
state: 'running';
|
|
36
|
+
node: CadreNode;
|
|
37
|
+
owner: OwnerGenesisResult;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* An owner of `partyId` removed this app from it: the node is stopped and the party's local
|
|
41
|
+
* storage deleted. `formerPartyId` is the party this app founded and left, which the saved start
|
|
42
|
+
* now names with `autoStart: false`; absent when there is none. `start` and `joinCadre` work as
|
|
43
|
+
* from `stopped`.
|
|
44
|
+
*/
|
|
45
|
+
| {
|
|
46
|
+
state: 'removed';
|
|
47
|
+
partyId: string;
|
|
48
|
+
removal: CadreRemoval;
|
|
49
|
+
formerPartyId?: string;
|
|
50
|
+
} | {
|
|
51
|
+
state: 'failed';
|
|
52
|
+
error: Error;
|
|
53
|
+
};
|
|
54
|
+
/** A join's progress: the redemption's passes and pauses, then the steps after a member admitted the app. */
|
|
55
|
+
export type EdgeJoinProgress = CadreJoinProgress
|
|
56
|
+
/**
|
|
57
|
+
* A member admitted the app; the edge node is wiring its owner key and saving the start.
|
|
58
|
+
* `asOwner`: the app joined with an owner grant, so its owner slot's key is an owner of the party.
|
|
59
|
+
*/
|
|
60
|
+
| {
|
|
61
|
+
state: 'admitted';
|
|
62
|
+
asOwner: boolean;
|
|
63
|
+
}
|
|
64
|
+
/** The app's key is wired as an owner and the strands this join recorded are being adopted. */
|
|
65
|
+
| {
|
|
66
|
+
state: 'moving-strands';
|
|
67
|
+
};
|
|
68
|
+
/** How {@link EdgeNode.joinCadre} runs. */
|
|
69
|
+
export interface JoinCadreOptions {
|
|
70
|
+
/** Relays the joined node reserves on. Default: the running start's, else the saved one's. */
|
|
71
|
+
relayAddrs?: string[];
|
|
72
|
+
/** Default: the running start's, else the saved one's, else the platform default. */
|
|
73
|
+
noiseCryptoMode?: NoiseCryptoMode;
|
|
74
|
+
/** Cancels the join: the app goes back to the party it left, and the call throws the signal's reason. */
|
|
75
|
+
signal?: AbortSignal;
|
|
76
|
+
/**
|
|
77
|
+
* Each redemption pass and each pause while no member is ready, for a "waiting for an always-on
|
|
78
|
+
* machine" display; then `admitted`, and `moving-strands` when this join's strands are adopted.
|
|
79
|
+
* A listener that throws is logged and ignored.
|
|
80
|
+
*/
|
|
81
|
+
onProgress?: (progress: EdgeJoinProgress) => void;
|
|
82
|
+
}
|
|
83
|
+
/** What an admitted {@link EdgeNode.joinCadre} returns. */
|
|
84
|
+
export interface JoinCadreResult extends RedeemCadreInvitationResult {
|
|
85
|
+
/** The app joined with an owner grant: its owner slot's key is an owner of the party (`status.owner` says whether it is wired). */
|
|
86
|
+
asOwner: boolean;
|
|
87
|
+
/** The party this app founded and has left, when it founded one; saved as `EdgeNodeOptions.formerPartyId`. */
|
|
88
|
+
formerPartyId?: string;
|
|
89
|
+
/** How moving that party's strands into this one settled; absent when there was nothing to move. */
|
|
90
|
+
strandMove?: StrandMoveOutcome;
|
|
91
|
+
}
|
|
92
|
+
/** A removal a dissolve marker names that the last start tried to finish and could not: {@link EdgeNode.unfinishedRemoval}. */
|
|
93
|
+
export interface UnfinishedRemoval {
|
|
94
|
+
partyId: string;
|
|
95
|
+
error: Error;
|
|
96
|
+
}
|
|
97
|
+
export interface EdgeNode {
|
|
98
|
+
readonly status: EdgeNodeStatus;
|
|
99
|
+
/** The running node, or null; null as soon as a stop or restart begins tearing it down, and while joining. */
|
|
100
|
+
readonly node: CadreNode | null;
|
|
101
|
+
/** The options the running node was started with, the Noise mode resolved; null unless running. */
|
|
102
|
+
readonly options: EdgeNodeOptions | null;
|
|
103
|
+
/**
|
|
104
|
+
* The removal the dissolve marker names that the last start tried to finish and could not
|
|
105
|
+
* (logged there too), for an app to show; null when the last start found no marker or finished
|
|
106
|
+
* it, or a dissolve or removal since finished it. The next start retries.
|
|
107
|
+
*/
|
|
108
|
+
readonly unfinishedRemoval: UnfinishedRemoval | null;
|
|
109
|
+
/**
|
|
110
|
+
* This app's peer id, the same in every party: the node's while one is running or joining,
|
|
111
|
+
* answered at once; otherwise read from the key store, created there when absent, in turn
|
|
112
|
+
* with start, stop and join so a first start cannot generate a different key. A read the
|
|
113
|
+
* key store refuses throws.
|
|
114
|
+
*/
|
|
115
|
+
identityPeerId(): Promise<string>;
|
|
116
|
+
/**
|
|
117
|
+
* Start, or join the start in flight; a running node is returned as is, whatever its options.
|
|
118
|
+
* A start called during a stop or restart runs after it. Opens the four node-local stores
|
|
119
|
+
* party-scoped, builds and starts the node, runs owner genesis (fail-soft, bounded), then saves
|
|
120
|
+
* the options with `autoStart: true`. A failed start closes everything it opened and leaves
|
|
121
|
+
* `status` `failed`; the next call starts afresh.
|
|
122
|
+
*/
|
|
123
|
+
start(options: EdgeNodeOptions): Promise<CadreNode>;
|
|
124
|
+
/**
|
|
125
|
+
* The user's "disconnect": waits for a start in flight, saves `autoStart: false` so nothing
|
|
126
|
+
* starts the node unattended, stops it, and closes every store it opened — control, each
|
|
127
|
+
* strand, node-local — through `EdgeNodeStorage.close`. A platform that locks a store per
|
|
128
|
+
* name (rn-leveldb) would otherwise fail the next start on one left open.
|
|
129
|
+
*/
|
|
130
|
+
stop(): Promise<void>;
|
|
131
|
+
/**
|
|
132
|
+
* Rebuild the node with new options (relays, Noise mode): libp2p reads them only when the node
|
|
133
|
+
* is built. `autoStart` is not cleared on the way, so a crash mid-restart still starts again.
|
|
134
|
+
*/
|
|
135
|
+
restart(options: EdgeNodeOptions): Promise<CadreNode>;
|
|
136
|
+
/**
|
|
137
|
+
* Join the cadre `credential` names: this app's one node moves to that party, keeping its
|
|
138
|
+
* identity, so the peer id an owner targeted is the one that redeems. A machine invitation
|
|
139
|
+
* admits the app as a machine; an owner grant makes it a machine and an owner at once
|
|
140
|
+
* (`CadreNode.joinWithOwnerGrant`), seating the owner slot's key, created when the slot is
|
|
141
|
+
* empty, which is how an app brings the strands of the party it founded along. Runs in turn
|
|
142
|
+
* with start, stop and restart; a start called meanwhile runs after it, and a `stop` cancels it.
|
|
143
|
+
*
|
|
144
|
+
* Already running in that party: redeems on the running node (a device already admitted is
|
|
145
|
+
* answered as such; an owner grant is redeemed for the slot's key, `CadreNode.redeemOwnerGrant`)
|
|
146
|
+
* and wires its owner key. Otherwise: stops the running node, starts one for the credential's
|
|
147
|
+
* party with `origin: 'joined'` (status `joining`), and redeems there, waiting for a member to
|
|
148
|
+
* answer until one accepts, one refuses, the credential expires (`invite-spent`,
|
|
149
|
+
* `grant-spent`) or `options.signal` aborts. Admitted: wires the owner key when the party
|
|
150
|
+
* lists the slot's key (after an owner grant, asking again until its row arrives or the
|
|
151
|
+
* owner-step bound passes), saves the start (`autoStart: true`) and reports `running`.
|
|
152
|
+
* Anything else: stops that node, starts again what was running before (a node that was
|
|
153
|
+
* stopped stays stopped), and throws the join's error; a failure restarting is logged and
|
|
154
|
+
* leaves status `failed`.
|
|
155
|
+
*
|
|
156
|
+
* The start is saved only once admitted, so an app killed while waiting relaunches into the
|
|
157
|
+
* party it left. The left party's data stays on disk under its own names.
|
|
158
|
+
*
|
|
159
|
+
* Leaving a party this app founded moves its strands: they are exported before anything stops
|
|
160
|
+
* (from a node started for the export alone when none runs) and kept in the key store, and
|
|
161
|
+
* adopted once the app's key is wired as an owner of the joined party, before the join reports
|
|
162
|
+
* `running`; otherwise a later start adopts them (`result.strandMove` says which). Refused
|
|
163
|
+
* before anything stops, with `StrandsNeedOwnerJoinError`, when there is something to move and
|
|
164
|
+
* the credential is a machine invitation; and with the export's error when it fails.
|
|
165
|
+
*/
|
|
166
|
+
joinCadre(credential: JoinCredential, options?: JoinCadreOptions): Promise<JoinCadreResult>;
|
|
167
|
+
/**
|
|
168
|
+
* The move from `formerPartyId` (the running start's, else the saved one's) still waiting to be
|
|
169
|
+
* adopted, or `undefined` when there is none. A read the key store refuses throws.
|
|
170
|
+
*/
|
|
171
|
+
pendingStrandMove(): Promise<PendingStrandMove | undefined>;
|
|
172
|
+
/**
|
|
173
|
+
* Adopt the waiting move on the running node now, in turn with start, stop and join; `undefined`
|
|
174
|
+
* when no move from `formerPartyId` names the running party. On a joined party with no owner
|
|
175
|
+
* signer wired, first asks the party once more whether it lists the owner slot's key, so an
|
|
176
|
+
* owner grant whose row arrived after the join stopped waiting is picked up here,
|
|
177
|
+
* `status.owner` becoming `enrolled`. Throws what the adoption throws,
|
|
178
|
+
* `StrandMoveNotOwnerError` while the app's key is no owner there.
|
|
179
|
+
*/
|
|
180
|
+
finishStrandMove(): Promise<StrandMoveReport | undefined>;
|
|
181
|
+
/**
|
|
182
|
+
* Delete the local storage of `formerPartyId`, the party this app founded and left, once the
|
|
183
|
+
* running party holds everything it holds and no other machine is in it. Runs in turn with
|
|
184
|
+
* start, stop, restart and join. Stops the running node, reads the former party from a node
|
|
185
|
+
* started for that alone, and starts the running party again, without `formerPartyId` once the
|
|
186
|
+
* former party is removed; a failure of that last start is the call's error.
|
|
187
|
+
*
|
|
188
|
+
* Refused before anything stops: with an `Error` unless running with a `formerPartyId` other
|
|
189
|
+
* than the running party, and with `StrandMovePendingError` while a move from it waits. Refused
|
|
190
|
+
* after the read, the running party started again and nothing deleted: with
|
|
191
|
+
* `FormerPartyNotEmptyError`, whose strand and pending-join ids `options.discard` may give up.
|
|
192
|
+
*/
|
|
193
|
+
dissolveFormerParty(options?: DissolveFormerPartyOptions): Promise<void>;
|
|
194
|
+
/**
|
|
195
|
+
* The last start, or `undefined` when none is saved or the record is unusable. A read fault
|
|
196
|
+
* throws. Called during a stop, it answers after the stop has saved `autoStart: false`; during
|
|
197
|
+
* the edge node's response to a removal, after that has replaced the saved start.
|
|
198
|
+
*/
|
|
199
|
+
loadSavedStart(): Promise<SavedStart | undefined>;
|
|
200
|
+
/**
|
|
201
|
+
* The slot `LinkRequestStore` (`@serfab/cadre-rn/app-link`) keeps the open link join request in,
|
|
202
|
+
* beside the saved start in the node-local store. Works with no node running, as
|
|
203
|
+
* `loadSavedStart` does; a read fault throws.
|
|
204
|
+
*/
|
|
205
|
+
linkRequestSlot(): DurableSlot;
|
|
206
|
+
/**
|
|
207
|
+
* The slot `AppRecommendationStore` (`@serfab/cadre-rn/app-link`) keeps the apps the user allowed
|
|
208
|
+
* to join before they asked in, beside the saved start. Works with no node running, as
|
|
209
|
+
* `linkRequestSlot` does; a read fault throws.
|
|
210
|
+
*/
|
|
211
|
+
recommendationSlot(): DurableSlot;
|
|
212
|
+
/**
|
|
213
|
+
* The node-local store as a key-value store, for records the app keeps beside the saved start
|
|
214
|
+
* under keys of its own: `rentalStore` (`@serfab/cadre-rn/provider`) files the provider rentals
|
|
215
|
+
* here, under `rentalsKvKey(partyId)`. Works with no node running, as `linkRequestSlot` does; a
|
|
216
|
+
* read fault throws. Keys of the form `<record>.<partyId>` are the edge node's; an app's own
|
|
217
|
+
* records take a dot-free key.
|
|
218
|
+
*/
|
|
219
|
+
nodeLocalRecords(): KvStoreApi;
|
|
220
|
+
/**
|
|
221
|
+
* Subscribe to a node event for the life of this edge node, not of one `CadreNode`: the
|
|
222
|
+
* handler is re-applied to every node a start or restart builds, before it starts. Register the
|
|
223
|
+
* `strand:discovered` handler that attaches strands here, before `start` — strands joined from
|
|
224
|
+
* another party come back through it after every start. Returns an unsubscribe.
|
|
225
|
+
*/
|
|
226
|
+
on<K extends keyof CadreNodeEvents>(event: K, handler: (data: CadreNodeEvents[K]) => void): () => void;
|
|
227
|
+
onStatus(listener: (status: EdgeNodeStatus) => void): () => void;
|
|
228
|
+
/**
|
|
229
|
+
* The public key (base64url) of the owner key the running node signs with, for pairing and
|
|
230
|
+
* approval prompts: the owner slot's key. Null unless running with it wired. Never private
|
|
231
|
+
* material.
|
|
232
|
+
*/
|
|
233
|
+
ownerPublicKey(): string | null;
|
|
234
|
+
/**
|
|
235
|
+
* The cadre's live owner keys (cadre-core's `CadreNode.listOwnerKeys`): label, voucher, whether
|
|
236
|
+
* this app holds it and whether this device trusts it. In turn with start, stop and join, as are
|
|
237
|
+
* the calls below; this one and every other that names the node throws while none is running.
|
|
238
|
+
*/
|
|
239
|
+
listOwnerKeys(): Promise<CadreOwnerKey[]>;
|
|
240
|
+
/**
|
|
241
|
+
* This app's own owner key, from the key store's owner slot, which is the key the node signs
|
|
242
|
+
* with, as export text, with the key it holds (`publicKey`). Works with no node running. Throws
|
|
243
|
+
* when the slot is empty: a member holds no owner key of its own.
|
|
244
|
+
*/
|
|
245
|
+
exportOwnerKey(options?: OwnerKeyExportOptions): Promise<ExportedOwnerKey>;
|
|
246
|
+
/**
|
|
247
|
+
* Import owner-key text into the owner slot, unlocking it with `passphrase` when it is locked.
|
|
248
|
+
* With a node running, the key is wired when the party lists it as a live owner (`wired`, and
|
|
249
|
+
* `status.owner` becomes `enrolled`); stored otherwise, and wired by a later start once an owner
|
|
250
|
+
* adds it. Works with no node running. Refused with `OwnerKeySlotOccupiedError` when the slot
|
|
251
|
+
* holds a different key (one owner key per app), and with `OwnerKeyExportError` for text that
|
|
252
|
+
* is not an owner key, a locked one with no passphrase (`passphrase-required`) or the wrong
|
|
253
|
+
* passphrase. A strand move waiting on an owner key is adopted by the next start or
|
|
254
|
+
* `finishStrandMove()`.
|
|
255
|
+
*/
|
|
256
|
+
importOwnerKey(text: string, passphrase?: string): Promise<ImportedOwnerKey>;
|
|
257
|
+
/** What `removeOwner(key)` would do, for the confirmation step (cadre-core's `CadreNode.planOwnerKeyRemoval`). */
|
|
258
|
+
planOwnerKeyRemoval(key: string): Promise<OwnerKeyRemovalPlan>;
|
|
259
|
+
/** Remove an owner key, re-vouching first the dependents `options.revouch` keeps (cadre-core's `CadreNode.removeOwner`). */
|
|
260
|
+
removeOwner(key: string, options?: RemoveOwnerOptions): Promise<OwnerKeyRemovalResult>;
|
|
261
|
+
/**
|
|
262
|
+
* Mint an owner grant (cadre-core's `CadreNode.createOwnerGrant`): whoever redeems the
|
|
263
|
+
* returned text first becomes an owner of the cadre. 15 minutes and one use by default;
|
|
264
|
+
* `expiresInMs: null` is a backup grant, with no expiry.
|
|
265
|
+
*/
|
|
266
|
+
createOwnerGrant(options?: CreateOwnerGrantOptions): Promise<CreateOwnerGrantResult>;
|
|
267
|
+
/** Every owner grant the running node holds, with its standing (cadre-core's `CadreNode.listOwnerGrants`). */
|
|
268
|
+
listOwnerGrants(): Promise<OwnerGrantStatus[]>;
|
|
269
|
+
/** Withdraw an owner grant by its key (cadre-core's `CadreNode.withdrawOwnerGrant`); false when it is not held here or was already withdrawn. */
|
|
270
|
+
withdrawOwnerGrant(key: string): Promise<boolean>;
|
|
271
|
+
/**
|
|
272
|
+
* Become an owner of the running party with owner-grant text: the owner slot's key, created
|
|
273
|
+
* when the slot is empty, is seated (cadre-core's `CadreNode.redeemOwnerGrant`) and wired, and
|
|
274
|
+
* `status.owner` becomes `enrolled`. A slot key that is already a live owner of the party is
|
|
275
|
+
* answered `alreadyOwner` before anything is signed or written, connected or not. A member
|
|
276
|
+
* machine writes the row in its own store and wires the key at once; any other node redeems
|
|
277
|
+
* at the grant's members and asks for the row until the owner-step bound passes (`wired:
|
|
278
|
+
* false` then, and a later start wires it). A strand move waiting on an owner key is adopted
|
|
279
|
+
* by the next start or `finishStrandMove()`.
|
|
280
|
+
*
|
|
281
|
+
* Refused with the decoder's error for text that is not an owner grant (an owner key, an
|
|
282
|
+
* invitation and a join request are named), with `OwnerGrantRejectedError` `party-mismatch`
|
|
283
|
+
* for a grant of another party, and with what `CadreNode.redeemOwnerGrant` throws:
|
|
284
|
+
* `OwnerKeyAddRefusedError` `alone` on a member with no connection to the party's other
|
|
285
|
+
* machines, `OwnerGrantRejectedError` (`grant-spent` for an expired, withdrawn or used grant),
|
|
286
|
+
* `OwnerGrantIssuerUnknownError`, `OwnerGrantUnreachableError`.
|
|
287
|
+
*/
|
|
288
|
+
redeemOwnerGrant(text: string): Promise<RedeemedOwnerGrant>;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Create the app's edge node. Call once per app. Throws when an edge node over the same storage
|
|
292
|
+
* (`platform.storage.id`) is starting or running in this process; a stopped or failed one is
|
|
293
|
+
* replaced (a development reload re-runs the module that calls this).
|
|
294
|
+
*/
|
|
295
|
+
export declare function createEdgeNode(platform: EdgeNodePlatform): EdgeNode;
|