@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
|
@@ -69,11 +69,11 @@ export interface JoinedStrandStore {
|
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* The {@link StrandRow} a record stands for — the shape `addStrand` and `strand:discovered`
|
|
72
|
-
* carry.
|
|
73
|
-
* reads null as "not mine" and joins.
|
|
72
|
+
* carry. The founder columns are null because a joiner never founds; `CadreNode.launchStrand`
|
|
73
|
+
* reads a null `FounderPeerId` as "not mine" and joins.
|
|
74
74
|
*/
|
|
75
75
|
export function joinedStrandRow(record: JoinedStrandRecord): StrandRow {
|
|
76
|
-
return { Id: record.Id, Type: record.Type, MemberPrivateKey: record.MemberPrivateKey, FounderOwnerKey: null };
|
|
76
|
+
return { Id: record.Id, Type: record.Type, MemberPrivateKey: record.MemberPrivateKey, FounderOwnerKey: null, FounderPeerId: null };
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
/**
|
|
@@ -234,7 +234,7 @@ function parseRecord(strandId: string, bytes: Uint8Array): JoinedStrandRecord |
|
|
|
234
234
|
* key; this module sees only the seam, so it stays free of the runtime.
|
|
235
235
|
*/
|
|
236
236
|
export interface PartyJoinedStrandLedger {
|
|
237
|
-
/** Every party-wide join, as offerable rows (
|
|
237
|
+
/** Every party-wide join, as offerable rows (null founder columns). */
|
|
238
238
|
list(): Promise<StrandRow[]>;
|
|
239
239
|
/** Whether this party's control database names `strandId`, as its own strand or as a party-wide join. */
|
|
240
240
|
names(strandId: string): Promise<boolean>;
|
package/src/key-store.ts
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
* Backend-agnostic store for raw private key material.
|
|
3
3
|
*
|
|
4
4
|
* Mobile cadre nodes hold sensitive key material at rest: the libp2p
|
|
5
|
-
* peer/node identity key and
|
|
6
|
-
*
|
|
7
|
-
* through so a platform-secure backend (iOS Keychain / Android Keystore)
|
|
8
|
-
* plugged in without `@serfab/cadre-core` taking any platform dependency.
|
|
5
|
+
* peer/node identity key and, on a device the user made an owner, an owner key
|
|
6
|
+
* in a slot of its own (`owner-key-store.ts`). This module defines the seam those
|
|
7
|
+
* keys flow through so a platform-secure backend (iOS Keychain / Android Keystore)
|
|
8
|
+
* can be plugged in without `@serfab/cadre-core` taking any platform dependency.
|
|
9
9
|
*
|
|
10
10
|
* This file is dependency-free (no node:/RN imports) so it is safe in every
|
|
11
11
|
* entry graph. The Node reference {@link FileKeyStore} lives in a separate
|
|
@@ -23,6 +23,12 @@ export type KeyId = string;
|
|
|
23
23
|
*/
|
|
24
24
|
export const DEFAULT_IDENTITY_KEY_ID: KeyId = 'cadre/identity';
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Default slot id for an owner key (`owner-key-store.ts`). Not party-scoped: one
|
|
28
|
+
* owner key may own several cadres, each owner signature binding its party id.
|
|
29
|
+
*/
|
|
30
|
+
export const DEFAULT_OWNER_KEY_ID: KeyId = 'cadre/owner';
|
|
31
|
+
|
|
26
32
|
/**
|
|
27
33
|
* Backend-agnostic store for raw private key material. Implementations may be
|
|
28
34
|
* file-based, OS-keyring, or platform secure-enclave backed (iOS Keychain /
|
package/src/machine-removal.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
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
11
|
|
|
14
|
-
import { ed25519PublicKeyB64FromPeerId
|
|
12
|
+
import { ed25519PublicKeyB64FromPeerId } from './ed25519-key.js';
|
|
15
13
|
import { verifyPeerRecordSignature } from './peer-record.js';
|
|
16
14
|
import type {
|
|
17
15
|
CadreInviteStatus,
|
|
@@ -20,8 +18,6 @@ import type {
|
|
|
20
18
|
CadrePeerRow,
|
|
21
19
|
MachineRemovalPlan,
|
|
22
20
|
MachineRemovalRefusal,
|
|
23
|
-
OwnerKeyRow,
|
|
24
|
-
RemoveMachineOptions,
|
|
25
21
|
} from './types.js';
|
|
26
22
|
|
|
27
23
|
/**
|
|
@@ -42,8 +38,7 @@ export class MachineRemovalRefusedError extends Error {
|
|
|
42
38
|
|
|
43
39
|
const MACHINE_REMOVAL_REFUSAL_REASON: Record<MachineRemovalRefusal, string> = {
|
|
44
40
|
'not-an-owner': 'this node holds no enrolled owner key',
|
|
45
|
-
'self': '
|
|
46
|
-
'last-owner': 'it holds the only live owner key',
|
|
41
|
+
'self': 'it is this node; leaving a cadre is stopping the node and deleting its data',
|
|
47
42
|
};
|
|
48
43
|
|
|
49
44
|
/** What {@link describeMachines} projects from: one node's live rows and its own predicate. */
|
|
@@ -52,45 +47,21 @@ export interface MachineListingInput {
|
|
|
52
47
|
selfPeerId: string | null;
|
|
53
48
|
/** Live `CadrePeer` rows (`ControlDatabase.queryCadrePeers`). */
|
|
54
49
|
rows: readonly CadrePeerRow[];
|
|
55
|
-
/** Live `OwnerKey` rows (`ControlDatabase.queryOwnerKeyRows`). */
|
|
56
|
-
owners: readonly OwnerKeyRow[];
|
|
57
50
|
/** This node's authorized-membership predicate for one row (`CadreNode.listAuthorizedMembers`'s). */
|
|
58
51
|
trusted: (row: CadrePeerRow) => boolean;
|
|
59
52
|
}
|
|
60
53
|
|
|
61
54
|
/**
|
|
62
|
-
* One entry per live `CadrePeer` row
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* bump `UpdatedAt` without re-signing.
|
|
55
|
+
* One entry per live `CadrePeer` row. `profile` and `lastPublishedAt` are reported only when
|
|
56
|
+
* the row's self-signature verifies under the key behind its peer id, as
|
|
57
|
+
* `CadreNode.resolvePeerAddrs` judges the same record: no insert rule constrains `Profile`, and
|
|
58
|
+
* an owner re-vouch may bump `UpdatedAt` without re-signing.
|
|
67
59
|
*/
|
|
68
60
|
export function describeMachines(input: MachineListingInput): CadreMachine[] {
|
|
69
|
-
|
|
70
|
-
const machines = input.rows.map((row) => describeMemberRow(input, row, ownerKeys));
|
|
71
|
-
const listed = new Set(machines.map((machine) => machine.peerId));
|
|
72
|
-
for (const owner of input.owners) {
|
|
73
|
-
const peerId = peerIdFromEd25519PublicKeyB64(owner.key);
|
|
74
|
-
if (peerId === null || listed.has(peerId)) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
listed.add(peerId);
|
|
78
|
-
machines.push({
|
|
79
|
-
peerId,
|
|
80
|
-
publicKey: owner.key,
|
|
81
|
-
isSelf: peerId === input.selfPeerId,
|
|
82
|
-
isOwner: true,
|
|
83
|
-
isMember: false,
|
|
84
|
-
trusted: false,
|
|
85
|
-
profile: null,
|
|
86
|
-
lastPublishedAt: null,
|
|
87
|
-
vouchedBy: owner.vouchOwner,
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
return machines;
|
|
61
|
+
return input.rows.map((row) => describeMemberRow(input, row));
|
|
91
62
|
}
|
|
92
63
|
|
|
93
|
-
function describeMemberRow(input: MachineListingInput, row: CadrePeerRow
|
|
64
|
+
function describeMemberRow(input: MachineListingInput, row: CadrePeerRow): CadreMachine {
|
|
94
65
|
const publicKey = ed25519PublicKeyB64FromPeerId(row.peerId);
|
|
95
66
|
const selfPublished = publicKey !== null && row.publicKey === publicKey && row.sig !== null
|
|
96
67
|
&& verifyPeerRecordSignature(input.partyId, {
|
|
@@ -105,8 +76,6 @@ function describeMemberRow(input: MachineListingInput, row: CadrePeerRow, ownerK
|
|
|
105
76
|
peerId: row.peerId,
|
|
106
77
|
publicKey,
|
|
107
78
|
isSelf: row.peerId === input.selfPeerId,
|
|
108
|
-
isOwner: publicKey !== null && ownerKeys.has(publicKey),
|
|
109
|
-
isMember: true,
|
|
110
79
|
trusted: input.trusted(row),
|
|
111
80
|
profile: selfPublished ? row.profile : null,
|
|
112
81
|
lastPublishedAt: selfPublished ? row.updatedAt : null,
|
|
@@ -118,99 +87,45 @@ function describeMemberRow(input: MachineListingInput, row: CadrePeerRow, ownerK
|
|
|
118
87
|
export interface MachineRemovalPlanInput {
|
|
119
88
|
peerId: string;
|
|
120
89
|
machines: readonly CadreMachine[];
|
|
121
|
-
rows: readonly CadrePeerRow[];
|
|
122
|
-
owners: readonly OwnerKeyRow[];
|
|
123
90
|
selfPeerId: string | null;
|
|
124
|
-
/**
|
|
125
|
-
|
|
91
|
+
/** Whether this node's owner signer is an enrolled owner (`CadreNode.ownerSignerEnrolledIn`), so it can sign the removal. */
|
|
92
|
+
canSign: boolean;
|
|
126
93
|
invites: readonly CadreInviteStatus[];
|
|
127
94
|
usages: readonly CadreInviteUsageRow[];
|
|
128
95
|
}
|
|
129
96
|
|
|
130
97
|
/**
|
|
131
|
-
* The plan for removing `peerId` from these rows. The refusals come first and by name: a
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
* owner, but the local count can be wrong under concurrency, so the name stays.
|
|
136
|
-
*
|
|
137
|
-
* `dependents` are the rows the machine's owner key vouched, which every reader judges
|
|
138
|
-
* against its anchor through `VouchOwner`: once the key is removed they stop passing the
|
|
139
|
-
* membership predicate on every machine unless re-vouched first. This node's own rows are
|
|
140
|
-
* reported as `ownDependency` instead, since it cannot re-vouch them itself.
|
|
98
|
+
* The plan for removing `peerId` from these rows. The refusals come first and by name: a node
|
|
99
|
+
* that cannot sign, and the node itself (leaving a cadre is the embedder's flow, which stops
|
|
100
|
+
* the node and deletes its data; the removal notice a machine acts on is built around another
|
|
101
|
+
* machine removing it).
|
|
141
102
|
*
|
|
142
103
|
* `invitations` are the live ones the machine could redeem to come back: targeted at it,
|
|
143
104
|
* or already redeemed by it and still redeemable (a multi-use invitation, or an open
|
|
144
|
-
*
|
|
105
|
+
* invitation inside its lifetime). The schema's rule: keeping a device out is removing
|
|
145
106
|
* its row and withdrawing the invitation.
|
|
146
107
|
*/
|
|
147
108
|
export function planMachineRemoval(input: MachineRemovalPlanInput): MachineRemovalPlan {
|
|
148
|
-
const machine = input.machines.find((candidate) => candidate.peerId === input.peerId) ?? null;
|
|
149
|
-
const key = machine?.isOwner ? machine.publicKey : null;
|
|
150
|
-
const refusal = refusalFor(input, machine);
|
|
151
|
-
const removesOwnerKey = key !== null;
|
|
152
109
|
return {
|
|
153
|
-
machine,
|
|
154
|
-
refusal,
|
|
155
|
-
removesOwnerKey,
|
|
156
|
-
dependents: removesOwnerKey ? dependentsOf(input, key) : { owners: [], peers: [] },
|
|
110
|
+
machine: input.machines.find((candidate) => candidate.peerId === input.peerId) ?? null,
|
|
111
|
+
refusal: refusalFor(input),
|
|
157
112
|
invitations: invitationsOpenTo(input),
|
|
158
|
-
ownDependency: removesOwnerKey && isOwnDependency(input, key),
|
|
159
113
|
};
|
|
160
114
|
}
|
|
161
115
|
|
|
162
|
-
function refusalFor(input: MachineRemovalPlanInput
|
|
163
|
-
if (input.
|
|
116
|
+
function refusalFor(input: MachineRemovalPlanInput): MachineRemovalRefusal | null {
|
|
117
|
+
if (!input.canSign) {
|
|
164
118
|
return 'not-an-owner';
|
|
165
119
|
}
|
|
166
120
|
if (input.peerId === input.selfPeerId) {
|
|
167
121
|
return 'self';
|
|
168
122
|
}
|
|
169
|
-
if (machine?.isOwner && input.owners.length <= 1) {
|
|
170
|
-
return 'last-owner';
|
|
171
|
-
}
|
|
172
123
|
return null;
|
|
173
124
|
}
|
|
174
125
|
|
|
175
|
-
function dependentsOf(input: MachineRemovalPlanInput, key: string): MachineRemovalPlan['dependents'] {
|
|
176
|
-
return {
|
|
177
|
-
owners: input.owners
|
|
178
|
-
.filter((owner) => owner.vouchOwner === key && owner.key !== key && owner.key !== input.selfOwnerKey)
|
|
179
|
-
.map((owner) => owner.key),
|
|
180
|
-
peers: input.rows
|
|
181
|
-
.filter((row) => row.vouchOwner === key && row.peerId !== input.peerId && row.peerId !== input.selfPeerId)
|
|
182
|
-
.map((row) => row.peerId),
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function isOwnDependency(input: MachineRemovalPlanInput, key: string): boolean {
|
|
187
|
-
const ownRow = input.rows.find((row) => row.peerId === input.selfPeerId);
|
|
188
|
-
const ownOwner = input.owners.find((owner) => owner.key === input.selfOwnerKey);
|
|
189
|
-
return ownRow?.vouchOwner === key || ownOwner?.vouchOwner === key;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
126
|
function invitationsOpenTo(input: MachineRemovalPlanInput): string[] {
|
|
193
127
|
const redeemed = new Set(input.usages.filter((usage) => usage.peerId === input.peerId).map((usage) => usage.inviteKey));
|
|
194
128
|
return input.invites
|
|
195
129
|
.filter((status) => status.live && (status.invite.peerId === input.peerId || redeemed.has(status.invite.key)))
|
|
196
130
|
.map((status) => status.invite.key);
|
|
197
131
|
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* The re-vouch a caller asked for, checked against the plan: every key and peer must be in
|
|
201
|
-
* `plan.dependents`, so a caller cannot re-vouch a row the removed owner never vouched (nor
|
|
202
|
-
* the machine itself, which `dependents.peers` excludes). Throws naming the offenders, before
|
|
203
|
-
* `CadreNode.removeMachine` writes anything. Duplicates are dropped.
|
|
204
|
-
*/
|
|
205
|
-
export function revouchWithinPlan(plan: MachineRemovalPlan, revouch: RemoveMachineOptions['revouch']): MachineRemovalPlan['dependents'] {
|
|
206
|
-
const owners = [...new Set(revouch?.owners ?? [])];
|
|
207
|
-
const peers = [...new Set(revouch?.peers ?? [])];
|
|
208
|
-
const strayOwners = owners.filter((key) => !plan.dependents.owners.includes(key));
|
|
209
|
-
const strayPeers = peers.filter((peerId) => !plan.dependents.peers.includes(peerId));
|
|
210
|
-
if (strayOwners.length > 0 || strayPeers.length > 0) {
|
|
211
|
-
throw new Error(
|
|
212
|
-
`removeMachine: re-vouch names rows the removed machine did not vouch: ${[...strayOwners, ...strayPeers].join(', ')}`,
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
return { owners, peers };
|
|
216
|
-
}
|