@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
package/dist/machine-removal.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Removing a machine from a cadre: the listing an app shows (`CadreNode.listMachines`)
|
|
3
|
-
* read-only plan behind its confirmation step (`CadreNode.planMachineRemoval`)
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* `ControlDatabase.removeMachine`'s.
|
|
2
|
+
* Removing a machine from a cadre: the listing an app shows (`CadreNode.listMachines`) and the
|
|
3
|
+
* read-only plan behind its confirmation step (`CadreNode.planMachineRemoval`). Pure
|
|
4
|
+
* projections over rows the node has already read; the reads and writes themselves are
|
|
5
|
+
* `CadreNode.removeMachine`'s and `ControlDatabase.removeMachine`'s.
|
|
7
6
|
*
|
|
8
|
-
* A machine
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* and an owner key with no machine row is still listed, as the machine it names.
|
|
7
|
+
* A machine is a `CadrePeer` row and nothing more: an owner key is the person's, so removing a
|
|
8
|
+
* machine leaves every owner key standing, the one the machine's user signed with included
|
|
9
|
+
* (removing one is `CadreNode.removeOwner`, `owner-keys.ts`).
|
|
12
10
|
*/
|
|
13
|
-
import { ed25519PublicKeyB64FromPeerId
|
|
11
|
+
import { ed25519PublicKeyB64FromPeerId } from './ed25519-key.js';
|
|
14
12
|
import { verifyPeerRecordSignature } from './peer-record.js';
|
|
15
13
|
/**
|
|
16
14
|
* `CadreNode.removeMachine` refused by name, before any write. `code` says which rule
|
|
@@ -26,41 +24,18 @@ export class MachineRemovalRefusedError extends Error {
|
|
|
26
24
|
}
|
|
27
25
|
const MACHINE_REMOVAL_REFUSAL_REASON = {
|
|
28
26
|
'not-an-owner': 'this node holds no enrolled owner key',
|
|
29
|
-
'self': '
|
|
30
|
-
'last-owner': 'it holds the only live owner key',
|
|
27
|
+
'self': 'it is this node; leaving a cadre is stopping the node and deleting its data',
|
|
31
28
|
};
|
|
32
29
|
/**
|
|
33
|
-
* One entry per live `CadrePeer` row
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
* bump `UpdatedAt` without re-signing.
|
|
30
|
+
* One entry per live `CadrePeer` row. `profile` and `lastPublishedAt` are reported only when
|
|
31
|
+
* the row's self-signature verifies under the key behind its peer id, as
|
|
32
|
+
* `CadreNode.resolvePeerAddrs` judges the same record: no insert rule constrains `Profile`, and
|
|
33
|
+
* an owner re-vouch may bump `UpdatedAt` without re-signing.
|
|
38
34
|
*/
|
|
39
35
|
export function describeMachines(input) {
|
|
40
|
-
|
|
41
|
-
const machines = input.rows.map((row) => describeMemberRow(input, row, ownerKeys));
|
|
42
|
-
const listed = new Set(machines.map((machine) => machine.peerId));
|
|
43
|
-
for (const owner of input.owners) {
|
|
44
|
-
const peerId = peerIdFromEd25519PublicKeyB64(owner.key);
|
|
45
|
-
if (peerId === null || listed.has(peerId)) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
listed.add(peerId);
|
|
49
|
-
machines.push({
|
|
50
|
-
peerId,
|
|
51
|
-
publicKey: owner.key,
|
|
52
|
-
isSelf: peerId === input.selfPeerId,
|
|
53
|
-
isOwner: true,
|
|
54
|
-
isMember: false,
|
|
55
|
-
trusted: false,
|
|
56
|
-
profile: null,
|
|
57
|
-
lastPublishedAt: null,
|
|
58
|
-
vouchedBy: owner.vouchOwner,
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
return machines;
|
|
36
|
+
return input.rows.map((row) => describeMemberRow(input, row));
|
|
62
37
|
}
|
|
63
|
-
function describeMemberRow(input, row
|
|
38
|
+
function describeMemberRow(input, row) {
|
|
64
39
|
const publicKey = ed25519PublicKeyB64FromPeerId(row.peerId);
|
|
65
40
|
const selfPublished = publicKey !== null && row.publicKey === publicKey && row.sig !== null
|
|
66
41
|
&& verifyPeerRecordSignature(input.partyId, {
|
|
@@ -75,8 +50,6 @@ function describeMemberRow(input, row, ownerKeys) {
|
|
|
75
50
|
peerId: row.peerId,
|
|
76
51
|
publicKey,
|
|
77
52
|
isSelf: row.peerId === input.selfPeerId,
|
|
78
|
-
isOwner: publicKey !== null && ownerKeys.has(publicKey),
|
|
79
|
-
isMember: true,
|
|
80
53
|
trusted: input.trusted(row),
|
|
81
54
|
profile: selfPublished ? row.profile : null,
|
|
82
55
|
lastPublishedAt: selfPublished ? row.updatedAt : null,
|
|
@@ -84,83 +57,36 @@ function describeMemberRow(input, row, ownerKeys) {
|
|
|
84
57
|
};
|
|
85
58
|
}
|
|
86
59
|
/**
|
|
87
|
-
* The plan for removing `peerId` from these rows. The refusals come first and by name: a
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
* owner, but the local count can be wrong under concurrency, so the name stays.
|
|
92
|
-
*
|
|
93
|
-
* `dependents` are the rows the machine's owner key vouched, which every reader judges
|
|
94
|
-
* against its anchor through `VouchOwner`: once the key is removed they stop passing the
|
|
95
|
-
* membership predicate on every machine unless re-vouched first. This node's own rows are
|
|
96
|
-
* reported as `ownDependency` instead, since it cannot re-vouch them itself.
|
|
60
|
+
* The plan for removing `peerId` from these rows. The refusals come first and by name: a node
|
|
61
|
+
* that cannot sign, and the node itself (leaving a cadre is the embedder's flow, which stops
|
|
62
|
+
* the node and deletes its data; the removal notice a machine acts on is built around another
|
|
63
|
+
* machine removing it).
|
|
97
64
|
*
|
|
98
65
|
* `invitations` are the live ones the machine could redeem to come back: targeted at it,
|
|
99
66
|
* or already redeemed by it and still redeemable (a multi-use invitation, or an open
|
|
100
|
-
*
|
|
67
|
+
* invitation inside its lifetime). The schema's rule: keeping a device out is removing
|
|
101
68
|
* its row and withdrawing the invitation.
|
|
102
69
|
*/
|
|
103
70
|
export function planMachineRemoval(input) {
|
|
104
|
-
const machine = input.machines.find((candidate) => candidate.peerId === input.peerId) ?? null;
|
|
105
|
-
const key = machine?.isOwner ? machine.publicKey : null;
|
|
106
|
-
const refusal = refusalFor(input, machine);
|
|
107
|
-
const removesOwnerKey = key !== null;
|
|
108
71
|
return {
|
|
109
|
-
machine,
|
|
110
|
-
refusal,
|
|
111
|
-
removesOwnerKey,
|
|
112
|
-
dependents: removesOwnerKey ? dependentsOf(input, key) : { owners: [], peers: [] },
|
|
72
|
+
machine: input.machines.find((candidate) => candidate.peerId === input.peerId) ?? null,
|
|
73
|
+
refusal: refusalFor(input),
|
|
113
74
|
invitations: invitationsOpenTo(input),
|
|
114
|
-
ownDependency: removesOwnerKey && isOwnDependency(input, key),
|
|
115
75
|
};
|
|
116
76
|
}
|
|
117
|
-
function refusalFor(input
|
|
118
|
-
if (input.
|
|
77
|
+
function refusalFor(input) {
|
|
78
|
+
if (!input.canSign) {
|
|
119
79
|
return 'not-an-owner';
|
|
120
80
|
}
|
|
121
81
|
if (input.peerId === input.selfPeerId) {
|
|
122
82
|
return 'self';
|
|
123
83
|
}
|
|
124
|
-
if (machine?.isOwner && input.owners.length <= 1) {
|
|
125
|
-
return 'last-owner';
|
|
126
|
-
}
|
|
127
84
|
return null;
|
|
128
85
|
}
|
|
129
|
-
function dependentsOf(input, key) {
|
|
130
|
-
return {
|
|
131
|
-
owners: input.owners
|
|
132
|
-
.filter((owner) => owner.vouchOwner === key && owner.key !== key && owner.key !== input.selfOwnerKey)
|
|
133
|
-
.map((owner) => owner.key),
|
|
134
|
-
peers: input.rows
|
|
135
|
-
.filter((row) => row.vouchOwner === key && row.peerId !== input.peerId && row.peerId !== input.selfPeerId)
|
|
136
|
-
.map((row) => row.peerId),
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function isOwnDependency(input, key) {
|
|
140
|
-
const ownRow = input.rows.find((row) => row.peerId === input.selfPeerId);
|
|
141
|
-
const ownOwner = input.owners.find((owner) => owner.key === input.selfOwnerKey);
|
|
142
|
-
return ownRow?.vouchOwner === key || ownOwner?.vouchOwner === key;
|
|
143
|
-
}
|
|
144
86
|
function invitationsOpenTo(input) {
|
|
145
87
|
const redeemed = new Set(input.usages.filter((usage) => usage.peerId === input.peerId).map((usage) => usage.inviteKey));
|
|
146
88
|
return input.invites
|
|
147
89
|
.filter((status) => status.live && (status.invite.peerId === input.peerId || redeemed.has(status.invite.key)))
|
|
148
90
|
.map((status) => status.invite.key);
|
|
149
91
|
}
|
|
150
|
-
/**
|
|
151
|
-
* The re-vouch a caller asked for, checked against the plan: every key and peer must be in
|
|
152
|
-
* `plan.dependents`, so a caller cannot re-vouch a row the removed owner never vouched (nor
|
|
153
|
-
* the machine itself, which `dependents.peers` excludes). Throws naming the offenders, before
|
|
154
|
-
* `CadreNode.removeMachine` writes anything. Duplicates are dropped.
|
|
155
|
-
*/
|
|
156
|
-
export function revouchWithinPlan(plan, revouch) {
|
|
157
|
-
const owners = [...new Set(revouch?.owners ?? [])];
|
|
158
|
-
const peers = [...new Set(revouch?.peers ?? [])];
|
|
159
|
-
const strayOwners = owners.filter((key) => !plan.dependents.owners.includes(key));
|
|
160
|
-
const strayPeers = peers.filter((peerId) => !plan.dependents.peers.includes(peerId));
|
|
161
|
-
if (strayOwners.length > 0 || strayPeers.length > 0) {
|
|
162
|
-
throw new Error(`removeMachine: re-vouch names rows the removed machine did not vouch: ${[...strayOwners, ...strayPeers].join(', ')}`);
|
|
163
|
-
}
|
|
164
|
-
return { owners, peers };
|
|
165
|
-
}
|
|
166
92
|
//# sourceMappingURL=machine-removal.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"machine-removal.js","sourceRoot":"","sources":["../src/machine-removal.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"machine-removal.js","sourceRoot":"","sources":["../src/machine-removal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The member-dialed redemption exchange shared by cadre invitations
|
|
3
|
+
* (`cadre-invite-protocol.ts`) and owner grants (`owner-grant-protocol.ts`): a holder dials ANY
|
|
4
|
+
* member machine its bundle names, proves possession in one signed frame, and the member writes
|
|
5
|
+
* rows on the issuer's behalf and answers in one frame. The issuer need not be online.
|
|
6
|
+
*
|
|
7
|
+
* What is the same for both credentials lives here, generic over the request and reply types:
|
|
8
|
+
*
|
|
9
|
+
* - the member side's hardening and sequencing ({@link MemberRedemptionHandler}): a concurrency
|
|
10
|
+
* cap, a read timeout, the checks before any database work, the push of this member's control
|
|
11
|
+
* store to the holder for a credential still live here, and the writes under that admission;
|
|
12
|
+
* - the holder side's dialing ({@link dialRedemption}, {@link dialRedemptionInPasses}): the
|
|
13
|
+
* bundle's addresses one at a time under a per-address deadline, one signed request for every
|
|
14
|
+
* address and every pass, and the pacing between passes;
|
|
15
|
+
* - the shape checks both directions apply off the wire, and the frame cap.
|
|
16
|
+
*
|
|
17
|
+
* What differs — the bundle, the request's fields and signatures, the refusal codes, the rows
|
|
18
|
+
* written and the reply's contents — stays in each protocol module, which passes it in. Same
|
|
19
|
+
* shape as the seed and wake protocols underneath: one length-prefixed JSON frame each way over
|
|
20
|
+
* the primitives in `control-stream.ts`.
|
|
21
|
+
*/
|
|
22
|
+
import type { Libp2p } from '@libp2p/interface';
|
|
23
|
+
import { type ControlStream } from './control-stream.js';
|
|
24
|
+
import type { CadreJoinProgress } from './types.js';
|
|
25
|
+
/**
|
|
26
|
+
* Cap on either frame of either protocol. A request is one signed row, two signatures and a few
|
|
27
|
+
* addresses; a reply adds at most a peer list of a cadre's size. Both fit in a few KB; 64 KiB is
|
|
28
|
+
* the defensive bound a stranger can make the handler buffer.
|
|
29
|
+
*/
|
|
30
|
+
export declare const MAX_REDEMPTION_FRAME_BYTES: number;
|
|
31
|
+
/** Longest string field accepted off the wire; a stamp is 342 base64url chars, a signature 86. */
|
|
32
|
+
export declare const MAX_WIRE_FIELD_LENGTH = 1024;
|
|
33
|
+
export declare function isBoundedString(value: unknown, max?: number): value is string;
|
|
34
|
+
export declare function isEd25519KeyB64(value: unknown): value is string;
|
|
35
|
+
/** Is `value` a null-or-bounded-string column, as the nullable text columns of a travelling row are? */
|
|
36
|
+
export declare function isNullableBoundedString(value: unknown, max?: number): value is string | null;
|
|
37
|
+
/** Is `value` a null-or-positive-integer use cap? */
|
|
38
|
+
export declare function isNullableUseCap(value: unknown): value is number | null;
|
|
39
|
+
/** A refusal as the member sends it: a code, a reason, nothing else. */
|
|
40
|
+
export interface RedemptionRejection<Code extends string> {
|
|
41
|
+
accepted: false;
|
|
42
|
+
code: Code;
|
|
43
|
+
reason: string;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A refusal as the holder reads it: the code and reason verbatim off the wire, untyped, so each
|
|
47
|
+
* protocol's rejected-error is the one place that classifies a code this build does not know
|
|
48
|
+
* (as `'unrecognized'`, retryable) rather than the decoder folding it into a code it does.
|
|
49
|
+
*/
|
|
50
|
+
export interface RedemptionRefusalFrame {
|
|
51
|
+
accepted: false;
|
|
52
|
+
code: unknown;
|
|
53
|
+
reason: unknown;
|
|
54
|
+
}
|
|
55
|
+
export declare function rejection<Code extends string>(code: Code, reason: string): RedemptionRejection<Code>;
|
|
56
|
+
/** One row of a protocol's constraint table: the constraint names the schema refuses a write with, and the code and reason each maps to. */
|
|
57
|
+
export type ConstraintRefusalRow<Code extends string> = readonly [RegExp, Code, string];
|
|
58
|
+
/** Which named constraint, if any, refused the write — read off the error's cause chain. */
|
|
59
|
+
export declare function constraintRefusal<Code extends string>(error: unknown, table: ReadonlyArray<ConstraintRefusalRow<Code>>): RedemptionRejection<Code> | null;
|
|
60
|
+
/**
|
|
61
|
+
* A write failure that is not a refusal: this member cannot read the credential row the cadre
|
|
62
|
+
* holds yet, or the retry gave up (`conflict`), or something else (`internal`).
|
|
63
|
+
*/
|
|
64
|
+
export declare function writeFailureRefusal(error: unknown): RedemptionRejection<'conflict' | 'internal'>;
|
|
65
|
+
export interface MemberRedemptionHandlerOptions {
|
|
66
|
+
/** This member's party id; a request for another party is refused by name. */
|
|
67
|
+
partyId: string;
|
|
68
|
+
/**
|
|
69
|
+
* Push this member's control store to the holder, resolving once the push has finished, and
|
|
70
|
+
* keep the holder counted in this member's control write cohort until the returned release
|
|
71
|
+
* is called. Called after the request verified and before the row is seated and the
|
|
72
|
+
* redemption is written; the handler releases once both have settled. Absent, the writes go
|
|
73
|
+
* ahead with the holder neither caught up nor in the cohort, so it does not hold them; see
|
|
74
|
+
* {@link MemberRedemptionHandler.admitIfLive}.
|
|
75
|
+
*/
|
|
76
|
+
admitDevice?: (peerId: string) => Promise<() => void>;
|
|
77
|
+
/** Time to wait for the request frame; default {@link DEFAULT_REDEEM_READ_TIMEOUT_MS}. */
|
|
78
|
+
redeemReadTimeoutMs?: number;
|
|
79
|
+
/** Cap on concurrent inbound redemptions; default {@link DEFAULT_MAX_CONCURRENT_REDEMPTIONS}. */
|
|
80
|
+
maxConcurrentRedemptions?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Member side of one redemption protocol: registers the handler and drives each inbound stream
|
|
84
|
+
* through the checks in cost order — shape, then whatever the protocol checks without database
|
|
85
|
+
* work (party, signatures, and for an invitation the connecting identity and target device) —
|
|
86
|
+
* so no database work happens for a stranger that cannot prove anything; then admits the holder
|
|
87
|
+
* to this member's control replication (`admitDevice`) when the credential is live here, and
|
|
88
|
+
* runs the protocol's writes. Hardened as the seed handler is: a concurrency cap (over it,
|
|
89
|
+
* `busy`) and a read timeout.
|
|
90
|
+
*
|
|
91
|
+
* Registered only by a started `CadreNode`, after its control database is up, like the
|
|
92
|
+
* formation responder; nothing here is reachable on a node without one.
|
|
93
|
+
*
|
|
94
|
+
* A subclass supplies the six credential-specific pieces: the request's shape, the checks after
|
|
95
|
+
* it, the read of the credential row this member holds, the liveness judgement behind the push,
|
|
96
|
+
* the writes, and the two fixed refusals the base sends on its own (`busy`, and `internal` for a
|
|
97
|
+
* frame it could not read or a failure it did not expect).
|
|
98
|
+
*
|
|
99
|
+
* The held row is read once, after the checks, and carried to the liveness judgement and the
|
|
100
|
+
* writes: the schema refuses every update and delete of an issued credential row, so the row
|
|
101
|
+
* read before the write lock is the row a read inside it would return.
|
|
102
|
+
*/
|
|
103
|
+
export declare abstract class MemberRedemptionHandler<Request, Reply extends {
|
|
104
|
+
accepted: boolean;
|
|
105
|
+
}, Held> {
|
|
106
|
+
protected readonly label: string;
|
|
107
|
+
protected readonly protocolId: string;
|
|
108
|
+
protected readonly partyId: string;
|
|
109
|
+
private readonly admitDevice;
|
|
110
|
+
private readonly readTimeoutMs;
|
|
111
|
+
private readonly maxConcurrent;
|
|
112
|
+
private readonly registered;
|
|
113
|
+
private active;
|
|
114
|
+
/**
|
|
115
|
+
* @param label - Names the credential in log lines and stream labels ("Cadre invite", "Owner grant").
|
|
116
|
+
* @param protocolId - The protocol id {@link register} handles by default.
|
|
117
|
+
*/
|
|
118
|
+
protected constructor(label: string, protocolId: string, options: MemberRedemptionHandlerOptions);
|
|
119
|
+
/** The refusal for a caller over the concurrency cap. */
|
|
120
|
+
protected abstract readonly busyRefusal: Reply;
|
|
121
|
+
/** The refusal for a request frame the member could not read, or an unexpected failure: nothing was proved either way, so the holder stays free to try the next address. */
|
|
122
|
+
protected abstract readonly internalRefusal: Reply;
|
|
123
|
+
/** The refusal for a frame that is not a well-formed request. */
|
|
124
|
+
protected abstract readonly malformedRefusal: Reply;
|
|
125
|
+
/** The request's shape, every field in its wire form and bounded. */
|
|
126
|
+
protected abstract isWellFormedRequest(value: unknown): value is Request;
|
|
127
|
+
/** The checks after the shape that cost no database work; null to go on to the writes. */
|
|
128
|
+
protected abstract checkRequest(request: Request, remotePeerId: string): Reply | null;
|
|
129
|
+
/** The credential row this member holds under the (checked) request's key, or null; a failed read is answered `internal`. */
|
|
130
|
+
protected abstract readHeld(request: Request): Promise<Held | null>;
|
|
131
|
+
/** Is the credential the request carries still live on this member, judged on `held` (else the bundle's copy)? Decides whether the holder is pushed the store (see {@link admitIfLive}). */
|
|
132
|
+
protected abstract isLive(request: Request, held: Held | null): Promise<boolean>;
|
|
133
|
+
/** The protocol's writes under the admission, each answered by name when it fails; `held` is {@link readHeld}'s row. */
|
|
134
|
+
protected abstract redeemAdmitted(request: Request, remotePeerId: string, held: Held | null): Promise<Reply>;
|
|
135
|
+
/** In-flight inbound redemptions. */
|
|
136
|
+
get activeCount(): number;
|
|
137
|
+
/**
|
|
138
|
+
* Register on `node`; rejects when libp2p refuses the registration. `runOnLimitedConnection`
|
|
139
|
+
* because a phone redeems through a relay, whose connection libp2p marks limited; the
|
|
140
|
+
* exchange is two small frames, well inside any relay's cap.
|
|
141
|
+
*/
|
|
142
|
+
register(node: Libp2p, protocolId?: string): Promise<void>;
|
|
143
|
+
unregister(node: Libp2p, protocolId?: string): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* One inbound redemption: read the frame, decide, reply, close. Public as the seam a protocol
|
|
146
|
+
* spec drives directly; `remotePeerId` is the connection's authenticated peer.
|
|
147
|
+
*/
|
|
148
|
+
handleStream(stream: ControlStream, remotePeerId: string): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Read the request frame to EOF, bounded and size-capped; JSON only, shape is the subclass's.
|
|
151
|
+
* Throws on a frame that cannot be read, which {@link handleStream} answers `internal`: a read
|
|
152
|
+
* timeout or a cut frame proves nothing about the credential, so the holder must stay free to
|
|
153
|
+
* try the next address.
|
|
154
|
+
*/
|
|
155
|
+
private readRequest;
|
|
156
|
+
private decide;
|
|
157
|
+
/**
|
|
158
|
+
* Admit the holder to this member's control replication before the first write of the
|
|
159
|
+
* exchange (the seat, when this member does not hold the row; otherwise the redemption), and
|
|
160
|
+
* resolve to the release that ends the admission. The holder is not a member, so only this
|
|
161
|
+
* puts it in this member's control write cohort, and it must be there to hold the rows the
|
|
162
|
+
* exchange writes. It is pushed the control store first, because a cohort node with no base
|
|
163
|
+
* revision of a block refuses a commit to it; a member alone with the holder would then have
|
|
164
|
+
* half its cohort refusing, and the write would tear instead of committing.
|
|
165
|
+
*
|
|
166
|
+
* Only for a credential still live here ({@link isLive}, judged on the row this member holds
|
|
167
|
+
* or, before the seat, on the bundle's copy once its issuer signature verifies): the holder
|
|
168
|
+
* of a withdrawn, expired or spent one, or of a forged copy naming a real owner, is refused
|
|
169
|
+
* without being sent anything. The retry of a redemption already written can read as spent
|
|
170
|
+
* too and is sent nothing; it needs nothing, since the rows it would hold are written and
|
|
171
|
+
* the ordinary catch-up reaches a member. Best-effort: a failed check or push is logged and
|
|
172
|
+
* the writes go ahead without the holder admitted, then commit or are answered retryably.
|
|
173
|
+
*/
|
|
174
|
+
private admitIfLive;
|
|
175
|
+
}
|
|
176
|
+
/** What one address the bundle named came to. */
|
|
177
|
+
export interface RedemptionAddressOutcome {
|
|
178
|
+
addr: string;
|
|
179
|
+
/** A dial or exchange failure, or a retryable refusal. */
|
|
180
|
+
error: Error;
|
|
181
|
+
}
|
|
182
|
+
/** A member's refusal as the holder classifies it; `retryable` decides whether the next address is tried. */
|
|
183
|
+
export type RedemptionRefusal = Error & {
|
|
184
|
+
readonly retryable: boolean;
|
|
185
|
+
};
|
|
186
|
+
/** What a successful redemption yields the holder. */
|
|
187
|
+
export interface MemberRedemption<Accepted> {
|
|
188
|
+
reply: Accepted;
|
|
189
|
+
/** The answering member's peer id, from the address's trailing `/p2p/`; null when it named none. */
|
|
190
|
+
memberPeerId: string | null;
|
|
191
|
+
/** The address that answered. */
|
|
192
|
+
memberAddr: string;
|
|
193
|
+
}
|
|
194
|
+
export interface MemberRedemptionOptions<Accepted> {
|
|
195
|
+
/** Names the credential in log lines and deadline labels ("Cadre invitation", "Owner grant"). */
|
|
196
|
+
label: string;
|
|
197
|
+
protocolId: string;
|
|
198
|
+
/** The bundle's multiaddrs ending in `/p2p/<peerId>`, dialed in order. */
|
|
199
|
+
members: readonly string[];
|
|
200
|
+
/** The signed request; the same one goes to every address (and every pass). */
|
|
201
|
+
request: unknown;
|
|
202
|
+
/** Read an acceptance frame's fields into `Accepted`, sanitizing them; throws on a malformed one, which moves the holder to the next address. */
|
|
203
|
+
decodeAccepted(frame: Record<string, unknown>): Accepted;
|
|
204
|
+
/** A refusal frame's code and reason, as the protocol classifies them. */
|
|
205
|
+
refusal(code: unknown, reason: unknown): RedemptionRefusal;
|
|
206
|
+
/** The holder's check on an acceptance; throws when it does not check out, which ends the call. */
|
|
207
|
+
verifyAccepted(reply: Accepted): void;
|
|
208
|
+
/** The error for every address tried and none accepting. */
|
|
209
|
+
unreachable(outcomes: readonly RedemptionAddressOutcome[]): Error;
|
|
210
|
+
/**
|
|
211
|
+
* This machine's declared link round trip (`NetworkConfig.linkRoundTripMs`), from which the
|
|
212
|
+
* per-address deadline derives: a dial that may need a relay plus one request
|
|
213
|
+
* (`relayedRequestBudgetMs`, 28.5 s at the default declaration), as seed delivery's is.
|
|
214
|
+
*/
|
|
215
|
+
linkRoundTripMs?: number;
|
|
216
|
+
/** Per-address deadline override; default derived from `linkRoundTripMs`. */
|
|
217
|
+
addressBudgetMs?: number;
|
|
218
|
+
/** Cancels the dial or stream in flight and ends the pass; the call throws the signal's reason. */
|
|
219
|
+
signal?: AbortSignal;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Holder side: try the bundle's addresses one at a time ({@link dialOnce}) and throw the
|
|
223
|
+
* protocol's unreachable error when none accepts.
|
|
224
|
+
*
|
|
225
|
+
* Every address gets the SAME request, with one usage nonce: the usage table is keyed by it,
|
|
226
|
+
* so a member the holder gave up on that was still committing cannot redeem it twice, and a
|
|
227
|
+
* retry at the next member after a dropped reply is answered as already done.
|
|
228
|
+
*
|
|
229
|
+
* @throws the protocol's rejected error on a final refusal, its reply-invalid error on an
|
|
230
|
+
* acceptance that fails `verifyAccepted`, its unreachable error when every address was tried
|
|
231
|
+
* and none accepted, and the reason of `options.signal` once it aborts.
|
|
232
|
+
*/
|
|
233
|
+
export declare function dialRedemption<Accepted>(node: Libp2p, options: MemberRedemptionOptions<Accepted>): Promise<MemberRedemption<Accepted>>;
|
|
234
|
+
export interface MemberRedemptionPassOptions<Accepted> extends MemberRedemptionOptions<Accepted> {
|
|
235
|
+
/** Keep making passes while every answer is retryable; false makes exactly one. */
|
|
236
|
+
waitForMember: boolean;
|
|
237
|
+
/** Called at each pass and each pause; an exception it throws is logged and ignored. */
|
|
238
|
+
onProgress?: (progress: CadreJoinProgress) => void;
|
|
239
|
+
/** The credential's stored expiry (its row's `expiresAt`, a `datetime` string); null for none. */
|
|
240
|
+
expiresAt: string | null;
|
|
241
|
+
/** The refusal a member would give the credential once expired, thrown when this holder's own clock says it has. */
|
|
242
|
+
expired(): Error;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Holder side, in passes: one {@link dialOnce} pass over the bundle's addresses, or, with
|
|
246
|
+
* `waitForMember`, as many as it takes, pausing between them (5 s after the first, doubling to
|
|
247
|
+
* 60 s, never past the credential's expiry). Every pass sends the same signed request, so a
|
|
248
|
+
* member that wrote the redemption on a pass whose reply was lost answers a later pass as
|
|
249
|
+
* already done, with no second usage row.
|
|
250
|
+
*
|
|
251
|
+
* A wait ends at the first acceptance, at a final refusal or an invalid reply (thrown as
|
|
252
|
+
* {@link dialRedemption} throws them), when the credential expires by this holder's clock
|
|
253
|
+
* (thrown as `expired()`), when `signal` aborts (its reason thrown), and when `node` stops. A
|
|
254
|
+
* bundle that names no address has nothing to wait for and throws at once.
|
|
255
|
+
*/
|
|
256
|
+
export declare function dialRedemptionInPasses<Accepted>(node: Libp2p, options: MemberRedemptionPassOptions<Accepted>): Promise<MemberRedemption<Accepted>>;
|