@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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import debug from 'debug';
|
|
2
2
|
import { digest, verify } from '@optimystic/quereus-plugin-crypto';
|
|
3
|
-
import { controlAuthorizationFields, unboundAuthorizationFields, cadreInviteRowFields } from './control-authorization.js';
|
|
3
|
+
import { controlAuthorizationFields, unboundAuthorizationFields, cadreInviteRowFields, ownerGrantRowFields } from './control-authorization.js';
|
|
4
4
|
import { ed25519PublicKeyB64FromPeerId } from './ed25519-key.js';
|
|
5
5
|
const log = debug('sereus:cadre:peer-authorization');
|
|
6
6
|
/**
|
|
@@ -142,20 +142,22 @@ export function verifyRevocationSigner(partyId, row) {
|
|
|
142
142
|
/**
|
|
143
143
|
* Canonical digest an owner signs to seat a further `OwnerKey` row — the signed-add branch
|
|
144
144
|
* of `OwnerKey.Authorized`, which stores the pair as `VouchOwner` / `VouchSig`. SQL mirror:
|
|
145
|
-
* `digest('CadreControl.OwnerKey', 'add', party_id(), new.Key, new.StampId)`.
|
|
146
|
-
* digest needs no builder here: it is the generic guarded `'remove'` digest over
|
|
145
|
+
* `digest('CadreControl.OwnerKey', 'add', party_id(), new.Key, coalesce(new.Label, ''), new.StampId)`.
|
|
146
|
+
* The removal digest needs no builder here: it is the generic guarded `'remove'` digest over
|
|
147
|
+
* (Key, StampId).
|
|
147
148
|
*/
|
|
148
|
-
export function ownerKeyAddDigest(partyId, key, stampId) {
|
|
149
|
-
return taggedDigest('CadreControl.OwnerKey', 'add', partyId, [key, stampId]);
|
|
149
|
+
export function ownerKeyAddDigest(partyId, key, label, stampId) {
|
|
150
|
+
return taggedDigest('CadreControl.OwnerKey', 'add', partyId, [key, label ?? '', stampId]);
|
|
150
151
|
}
|
|
151
152
|
/**
|
|
152
153
|
* Does an owner-signed `OwnerKey` row's stored voucher verify — `vouchSig` by `vouchOwner`
|
|
153
|
-
* over {@link ownerKeyAddDigest} for (`partyId`, `key`, `stampId`)? Whether
|
|
154
|
-
* anchored is the caller's question, as for {@link verifyCadrePeerVoucher}.
|
|
154
|
+
* over {@link ownerKeyAddDigest} for (`partyId`, `key`, `label`, `stampId`)? Whether
|
|
155
|
+
* `vouchOwner` is anchored is the caller's question, as for {@link verifyCadrePeerVoucher}.
|
|
156
|
+
* Never throws.
|
|
155
157
|
*/
|
|
156
|
-
export function verifyOwnerKeyVoucher(partyId, key, stampId, vouchOwner, vouchSig) {
|
|
158
|
+
export function verifyOwnerKeyVoucher(partyId, key, label, stampId, vouchOwner, vouchSig) {
|
|
157
159
|
try {
|
|
158
|
-
return verifyB64(ownerKeyAddDigest(partyId, key, stampId), vouchSig, vouchOwner);
|
|
160
|
+
return verifyB64(ownerKeyAddDigest(partyId, key, label, stampId), vouchSig, vouchOwner);
|
|
159
161
|
}
|
|
160
162
|
catch (error) {
|
|
161
163
|
log('verifyOwnerKeyVoucher failed: %o', error);
|
|
@@ -310,40 +312,24 @@ export function verifyCadreInviteRedemption(partyId, fields, inviteSig, peerSig)
|
|
|
310
312
|
return false;
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
|
-
/**
|
|
314
|
-
* The links of an invitation admission that do not depend on WHICH table's row was
|
|
315
|
-
* admitted — shared by {@link verifyInvitationAdmission} (`CadrePeer`) and
|
|
316
|
-
* {@link verifyInvitationOwnerAdmission} (`OwnerKey`), which differ only in the row
|
|
317
|
-
* columns the usage must name and, for an owner row, in `grantsOwner`. The chain is
|
|
318
|
-
*
|
|
319
|
-
* row --vouchUsage--> usage --inviteKey--> invitation --issuerKey--> anchored owner
|
|
320
|
-
*
|
|
321
|
-
* and every link here is verified: the issuer is anchored (`isAnchored`) and is the row's
|
|
322
|
-
* `vouchOwner`; the usage names this invitation and is the row's `vouchUsage`; a targeted
|
|
323
|
-
* invitation names the redeeming device; the stored `peerKey` really is the key behind the
|
|
324
|
-
* usage's `peerId` (the schema cannot unwrap a multihash, so a writer could assert any
|
|
325
|
-
* pair); the invitation's stored `'add'` signature verifies over the row rebuilt from its
|
|
326
|
-
* columns; the holder's `'redeem'` signature verifies with the invitation key and the
|
|
327
|
-
* device's `'consent'` signature with its own key. All three signatures are judged for
|
|
328
|
-
* `partyId`. Throws on malformed input; the two callers turn that into `false`.
|
|
329
|
-
*/
|
|
330
|
-
function verifyAdmissionChain(partyId, vouchOwner, vouchUsage, usage, invite, isAnchored) {
|
|
331
|
-
return isAnchored(vouchOwner)
|
|
332
|
-
&& invite.issuerKey === vouchOwner
|
|
333
|
-
&& usage.usageStampId === vouchUsage
|
|
334
|
-
&& usage.inviteKey === invite.key
|
|
335
|
-
&& (invite.peerId === null || invite.peerId === usage.peerId)
|
|
336
|
-
&& ed25519PublicKeyB64FromPeerId(usage.peerId) === usage.peerKey
|
|
337
|
-
&& verifyB64(cadreInviteAddDigest(partyId, invite), invite.issuerSig, invite.issuerKey)
|
|
338
|
-
&& verifyB64(cadreInviteRedeemDigest(partyId, invite.key, usage.usageStampId, usage.peerKey), usage.inviteSig, invite.key)
|
|
339
|
-
&& verifyB64(cadreInviteConsentDigest(partyId, invite.key, usage.usageStampId, usage.peerKey), usage.peerSig, usage.peerKey);
|
|
340
|
-
}
|
|
341
315
|
/**
|
|
342
316
|
* Is an invitation-admitted `CadrePeer` row (`vouchSig` null, `vouchUsage` set) a
|
|
343
317
|
* member this node should trust? The read-side mirror of the consent branch of
|
|
344
318
|
* `CadrePeer.AuthorizedInsert`, re-checked against THIS node's anchor because the
|
|
345
|
-
* replicated `OwnerKey` table can be polluted. The
|
|
346
|
-
*
|
|
319
|
+
* replicated `OwnerKey` table can be polluted. The chain is
|
|
320
|
+
*
|
|
321
|
+
* row --vouchUsage--> usage --inviteKey--> invitation --issuerKey--> anchored owner
|
|
322
|
+
*
|
|
323
|
+
* and every link is verified: the issuer is anchored (`isAnchored`) and is the row's
|
|
324
|
+
* `vouchOwner`; the usage is the row's `vouchUsage`, names this peer id and this exact row
|
|
325
|
+
* incarnation (`peerStampId`), and names this invitation; a targeted invitation names the
|
|
326
|
+
* redeeming device; the stored `peerKey` really is the key behind the usage's `peerId` (the
|
|
327
|
+
* schema cannot unwrap a multihash, so a writer could assert any pair); the invitation's
|
|
328
|
+
* stored `'add'` signature verifies over the row rebuilt from its columns; the holder's
|
|
329
|
+
* `'redeem'` signature verifies with the invitation key and the device's `'consent'`
|
|
330
|
+
* signature with its own key. All three signatures are judged for `partyId`. A machine
|
|
331
|
+
* invitation admits a machine and nothing else, so no `OwnerKey` row is ever verified
|
|
332
|
+
* through this chain; a grant-seated owner row is {@link verifyOwnerGrantAdmission}'s.
|
|
347
333
|
*
|
|
348
334
|
* Expiry, use count and withdrawal are deliberately NOT re-checked: they were conditions
|
|
349
335
|
* at redemption (`CadreInviteUsage.Authorized`), and membership persists until an owner
|
|
@@ -358,9 +344,17 @@ export function verifyInvitationAdmission(partyId, row, usage, invite, isAnchore
|
|
|
358
344
|
return row.stampId !== null
|
|
359
345
|
&& row.vouchOwner !== null
|
|
360
346
|
&& row.vouchUsage !== null
|
|
347
|
+
&& isAnchored(row.vouchOwner)
|
|
348
|
+
&& invite.issuerKey === row.vouchOwner
|
|
349
|
+
&& usage.usageStampId === row.vouchUsage
|
|
361
350
|
&& usage.peerStampId === row.stampId
|
|
362
351
|
&& usage.peerId === row.peerId
|
|
363
|
-
&&
|
|
352
|
+
&& usage.inviteKey === invite.key
|
|
353
|
+
&& (invite.peerId === null || invite.peerId === usage.peerId)
|
|
354
|
+
&& ed25519PublicKeyB64FromPeerId(usage.peerId) === usage.peerKey
|
|
355
|
+
&& verifyB64(cadreInviteAddDigest(partyId, invite), invite.issuerSig, invite.issuerKey)
|
|
356
|
+
&& verifyB64(cadreInviteRedeemDigest(partyId, invite.key, usage.usageStampId, usage.peerKey), usage.inviteSig, invite.key)
|
|
357
|
+
&& verifyB64(cadreInviteConsentDigest(partyId, invite.key, usage.usageStampId, usage.peerKey), usage.peerSig, usage.peerKey);
|
|
364
358
|
}
|
|
365
359
|
catch (error) {
|
|
366
360
|
log('verifyInvitationAdmission failed: %o', error);
|
|
@@ -368,25 +362,101 @@ export function verifyInvitationAdmission(partyId, row, usage, invite, isAnchore
|
|
|
368
362
|
}
|
|
369
363
|
}
|
|
370
364
|
/**
|
|
371
|
-
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
374
|
-
*
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
365
|
+
* Canonical digest an owner signs to seat an `OwnerGrant` row — the read-side mirror of
|
|
366
|
+
* `ownerGrantAddMessage` in control-database.ts, base64url instead of raw bytes. The whole
|
|
367
|
+
* row is bound, the label included, through the one field builder both sides share
|
|
368
|
+
* (`ownerGrantRowFields`). SQL mirror: `OwnerGrant.AuthorizedInsert`.
|
|
369
|
+
*/
|
|
370
|
+
export function ownerGrantAddDigest(partyId, row) {
|
|
371
|
+
return taggedDigest('CadreControl.OwnerGrant', 'add', partyId, ownerGrantRowFields(row));
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Canonical digest the HOLDER of an `OwnerGrant`'s private key signs to redeem it once:
|
|
375
|
+
* proof of possession, verified against the grant key itself
|
|
376
|
+
* (`OwnerGrantUsage.GrantPossessed`). Binds the redemption's nonce and the key being seated,
|
|
377
|
+
* so one redemption cannot be re-presented for another key or another use. Mirror of
|
|
378
|
+
* `ownerGrantRedeemMessage` in control-database.ts.
|
|
379
|
+
*/
|
|
380
|
+
export function ownerGrantRedeemDigest(partyId, grantKey, usageStampId, seatedKey) {
|
|
381
|
+
return taggedDigest('CadreControl.OwnerGrantUsage', 'redeem', partyId, [grantKey, usageStampId, seatedKey]);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Canonical digest the key being SEATED signs to prove it is held by the redeemer
|
|
385
|
+
* (`OwnerGrantUsage.OwnerConsented`), over the same fields as {@link ownerGrantRedeemDigest}
|
|
386
|
+
* under a distinct action tag, so the holder's and the key's signatures are never
|
|
387
|
+
* interchangeable. Mirror of `ownerGrantConsentMessage` in control-database.ts.
|
|
388
|
+
*/
|
|
389
|
+
export function ownerGrantConsentDigest(partyId, grantKey, usageStampId, seatedKey) {
|
|
390
|
+
return taggedDigest('CadreControl.OwnerGrantUsage', 'consent', partyId, [grantKey, usageStampId, seatedKey]);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Does an `OwnerGrant` row carry a valid `'add'` signature by its own `issuerKey`, for
|
|
394
|
+
* `partyId`? The grant twin of {@link verifyCadreInviteRow}, with the same contract: whether
|
|
395
|
+
* that issuer IS an owner is the caller's question, and a malformed row verifies as `false`.
|
|
396
|
+
*/
|
|
397
|
+
export function verifyOwnerGrantRow(partyId, grant) {
|
|
398
|
+
try {
|
|
399
|
+
return verifyB64(ownerGrantAddDigest(partyId, grant), grant.issuerSig, grant.issuerKey);
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
log('verifyOwnerGrantRow failed: %o', error);
|
|
403
|
+
return false;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* The two signatures an owner-grant redemption carries, checked before any database work:
|
|
408
|
+
* the holder's `'redeem'` signature with the grant key and the seated key's `'consent'`
|
|
409
|
+
* signature with itself, both over (`grantKey`, `usageStampId`, `seatedKey`). The grant twin
|
|
410
|
+
* of {@link verifyCadreInviteRedemption}, with the same contract: the schema
|
|
411
|
+
* (`OwnerGrantUsage.GrantPossessed` / `OwnerConsented`) stays the authority, this turns a bad
|
|
412
|
+
* signature into a clean refusal instead of a constraint failure at commit. Never throws.
|
|
413
|
+
*/
|
|
414
|
+
export function verifyOwnerGrantRedemption(partyId, fields, grantSig, ownerSig) {
|
|
415
|
+
try {
|
|
416
|
+
return verifyB64(ownerGrantRedeemDigest(partyId, fields.grantKey, fields.usageStampId, fields.seatedKey), grantSig, fields.grantKey)
|
|
417
|
+
&& verifyB64(ownerGrantConsentDigest(partyId, fields.grantKey, fields.usageStampId, fields.seatedKey), ownerSig, fields.seatedKey);
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
log('verifyOwnerGrantRedemption failed: %o', error);
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* Is a grant-seated `OwnerKey` row (`vouchSig` null, `vouchUsage` naming an `OwnerGrantUsage`
|
|
426
|
+
* row) an owner this node should derive into its anchor? The read-side mirror of the grant
|
|
427
|
+
* branch of `OwnerKey.Authorized` (`owner-anchor-sync.ts` → `deriveOwnerAnchor`). The chain is
|
|
428
|
+
*
|
|
429
|
+
* row --vouchUsage--> usage --grantKey--> grant --issuerKey--> anchored owner
|
|
430
|
+
*
|
|
431
|
+
* and every link is verified: the issuer is anchored and is the row's `vouchOwner`; the usage
|
|
432
|
+
* is the row's `vouchUsage` and names this key and this exact row incarnation
|
|
433
|
+
* (`seatedStampId`); the usage names this grant; the row carries the grant's label; the
|
|
434
|
+
* grant's stored `'add'` signature verifies over the row rebuilt from its columns; the
|
|
435
|
+
* holder's `'redeem'` signature verifies with the grant key and the seated key's `'consent'`
|
|
436
|
+
* signature with itself. All three signatures are judged for `partyId`.
|
|
437
|
+
*
|
|
438
|
+
* Expiry, use count and withdrawal are deliberately NOT re-checked, as
|
|
439
|
+
* {@link verifyInvitationAdmission} omits them: they were conditions at redemption
|
|
440
|
+
* (`OwnerGrantUsage.Authorized`), and ownership persists until an owner removes the row.
|
|
441
|
+
* Same never-throws contract as the siblings.
|
|
378
442
|
*/
|
|
379
|
-
export function
|
|
443
|
+
export function verifyOwnerGrantAdmission(partyId, row, usage, grant, isAnchored) {
|
|
380
444
|
try {
|
|
381
445
|
return row.vouchOwner !== null
|
|
382
446
|
&& row.vouchUsage !== null
|
|
383
|
-
&&
|
|
384
|
-
&&
|
|
385
|
-
&& usage.
|
|
386
|
-
&&
|
|
447
|
+
&& isAnchored(row.vouchOwner)
|
|
448
|
+
&& grant.issuerKey === row.vouchOwner
|
|
449
|
+
&& usage.usageStampId === row.vouchUsage
|
|
450
|
+
&& usage.seatedKey === row.key
|
|
451
|
+
&& usage.seatedStampId === row.stampId
|
|
452
|
+
&& usage.grantKey === grant.key
|
|
453
|
+
&& (grant.label ?? '') === (row.label ?? '')
|
|
454
|
+
&& verifyB64(ownerGrantAddDigest(partyId, grant), grant.issuerSig, grant.issuerKey)
|
|
455
|
+
&& verifyB64(ownerGrantRedeemDigest(partyId, grant.key, usage.usageStampId, usage.seatedKey), usage.grantSig, grant.key)
|
|
456
|
+
&& verifyB64(ownerGrantConsentDigest(partyId, grant.key, usage.usageStampId, usage.seatedKey), usage.ownerSig, usage.seatedKey);
|
|
387
457
|
}
|
|
388
458
|
catch (error) {
|
|
389
|
-
log('
|
|
459
|
+
log('verifyOwnerGrantAdmission failed: %o', error);
|
|
390
460
|
return false;
|
|
391
461
|
}
|
|
392
462
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"peer-authorization.js","sourceRoot":"","sources":["../src/peer-authorization.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,EAAE,0BAA0B,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"peer-authorization.js","sourceRoot":"","sources":["../src/peer-authorization.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Libp2p, PeerId } from '@libp2p/interface';
|
|
2
2
|
import type { IPeerNetwork } from '@optimystic/db-core';
|
|
3
3
|
import { BlockTransferClient, type IRawStorage } from '@optimystic/db-p2p';
|
|
4
|
+
import type { Libp2pNodeWithRepo } from './types.js';
|
|
4
5
|
/**
|
|
5
6
|
* The block-transfer protocol's hard cap on one length-prefixed message. Mirrors
|
|
6
7
|
* `MAX_BLOCK_MESSAGE_BYTES` in `@optimystic/db-p2p`'s `protocol-limits.ts` (8 MiB), which
|
|
@@ -19,7 +20,10 @@ export declare const PEER_JOIN_BACKFILL_WARN_AFTER_FAILURES = 3;
|
|
|
19
20
|
export interface PeerJoinBackfillConfig {
|
|
20
21
|
/** Default true. False disables the catch-up entirely (the pre-existing behaviour). */
|
|
21
22
|
enabled?: boolean;
|
|
22
|
-
/**
|
|
23
|
+
/**
|
|
24
|
+
* Settle time before re-running a peer this instance has already run for (a denied or failed
|
|
25
|
+
* peer's next schedule), ms. Default 1000. A peer never run for is run at once.
|
|
26
|
+
*/
|
|
23
27
|
debounceMs?: number;
|
|
24
28
|
/** Ceiling on blocks copied in one catch-up. Default 10_000. Reaching it is LOGGED, never silent. */
|
|
25
29
|
maxBlocks?: number;
|
|
@@ -76,6 +80,12 @@ export interface PeerJoinBackfillDeps {
|
|
|
76
80
|
* see the module comment for why the strand no-gate argument does not carry over.
|
|
77
81
|
*/
|
|
78
82
|
authorizePeer?: (peerId: string) => Promise<boolean>;
|
|
83
|
+
/**
|
|
84
|
+
* The node's `reportBlockHeld` ({@link Libp2pNodeWithRepo.reportBlockHeld}), told of each block a
|
|
85
|
+
* peer accepted so Optimystic's under-replication drain does not push the peer the same block
|
|
86
|
+
* again, one request per block. Absent = nothing reported, and the drain re-sends.
|
|
87
|
+
*/
|
|
88
|
+
reportBlockHeld?: Libp2pNodeWithRepo['reportBlockHeld'];
|
|
79
89
|
/**
|
|
80
90
|
* Test seam: build the per-peer push client. Defaults to a real
|
|
81
91
|
* `BlockTransferClient` over {@link peerNetwork} + {@link protocolPrefix}.
|
|
@@ -84,11 +94,21 @@ export interface PeerJoinBackfillDeps {
|
|
|
84
94
|
}
|
|
85
95
|
/** What one peer's catch-up actually did. Returned for tests and logged at the end of each run. */
|
|
86
96
|
export interface PeerJoinBackfillResult {
|
|
87
|
-
/** Blocks offered: had a committed `latest` AND materialized content locally. */
|
|
97
|
+
/** Blocks offered: had a committed `latest` AND materialized content locally, and the peer was not known to hold it. */
|
|
88
98
|
offered: number;
|
|
99
|
+
/**
|
|
100
|
+
* Skipped: the peer already accepted or refused this revision from this node, or signed its
|
|
101
|
+
* commit (see "What is pushed" in the module comment). Not offered, so not counted toward
|
|
102
|
+
* `maxBlocks`.
|
|
103
|
+
*/
|
|
104
|
+
alreadyHeld: number;
|
|
89
105
|
/** Blocks the remote reported it persisted. */
|
|
90
106
|
accepted: number;
|
|
91
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* Block ids the remote reported in `missing` (unparseable, not persisted, or pushed without a
|
|
109
|
+
* commit proof it accepts). Remembered per revision; not offered to that peer again until the
|
|
110
|
+
* local revision moves.
|
|
111
|
+
*/
|
|
92
112
|
rejected: string[];
|
|
93
113
|
/** Skipped: metadata has no `latest` (pending-only — not yet a durability claim here). */
|
|
94
114
|
uncommitted: number;
|
|
@@ -109,8 +129,8 @@ export interface PeerJoinBackfillResult {
|
|
|
109
129
|
*
|
|
110
130
|
* Best-effort throughout: nothing here throws into a libp2p event handler or into a
|
|
111
131
|
* runtime bring-up; a failed chunk is logged and the run continues. A peer is marked
|
|
112
|
-
*
|
|
113
|
-
*
|
|
132
|
+
* caught up only after a run with no thrown chunk, so a peer whose catch-up failed is
|
|
133
|
+
* retried; each block is pushed to each peer once (see "What is pushed" in the module comment).
|
|
114
134
|
*/
|
|
115
135
|
export declare class PeerJoinBackfill {
|
|
116
136
|
private readonly deps;
|
|
@@ -119,10 +139,22 @@ export declare class PeerJoinBackfill {
|
|
|
119
139
|
private started;
|
|
120
140
|
private stopped;
|
|
121
141
|
/**
|
|
122
|
-
* Peers
|
|
123
|
-
* {@link forceCatchUpPeer}
|
|
142
|
+
* Peers caught up since their current connection opened. Cleared for a peer when its last
|
|
143
|
+
* connection closes, so a reconnect re-runs; {@link forceCatchUpPeer} runs past it.
|
|
124
144
|
*/
|
|
125
145
|
private readonly done;
|
|
146
|
+
/**
|
|
147
|
+
* Peers that disconnected while their run was in flight, so that run does not mark them
|
|
148
|
+
* {@link done} after the disconnect cleared it. Cleared when the run ends.
|
|
149
|
+
*/
|
|
150
|
+
private readonly disconnectedInFlight;
|
|
151
|
+
/** Per-peer, per-block verdicts from push replies; see {@link PeerMemo}. */
|
|
152
|
+
private readonly memos;
|
|
153
|
+
/**
|
|
154
|
+
* Peers a run has been started for. A peer not in it is run at once on its first schedule;
|
|
155
|
+
* later schedules wait out `debounceMs`.
|
|
156
|
+
*/
|
|
157
|
+
private readonly contacted;
|
|
126
158
|
/**
|
|
127
159
|
* Peers with a catch-up currently running, each with that run (suppresses concurrent
|
|
128
160
|
* duplicates; {@link forceCatchUpPeer} waits on the run).
|
|
@@ -130,9 +162,9 @@ export declare class PeerJoinBackfill {
|
|
|
130
162
|
private readonly inFlight;
|
|
131
163
|
/**
|
|
132
164
|
* Peers whose (re-)schedule arrived while their OWN run was in flight, replayed once
|
|
133
|
-
* that run finishes. Load-bearing for the gated path: the gate
|
|
134
|
-
* a
|
|
135
|
-
*
|
|
165
|
+
* that run finishes. Load-bearing for the gated path: the gate is judged once, at the start
|
|
166
|
+
* of a run, and the run's pushes take long enough for the very membership commit that would
|
|
167
|
+
* have authorized the peer to land after that check —
|
|
136
168
|
* dropping that schedule (rather than deferring it) leaves the denied peer waiting for a
|
|
137
169
|
* reconnect, which is exactly what the re-arm exists to avoid.
|
|
138
170
|
*/
|
|
@@ -150,17 +182,32 @@ export declare class PeerJoinBackfill {
|
|
|
150
182
|
/** Peers already reported on `console.warn`; reset when a run finally lands cleanly. */
|
|
151
183
|
private readonly warned;
|
|
152
184
|
private readonly onPeerIdentify;
|
|
185
|
+
private readonly onPeerDisconnect;
|
|
153
186
|
private loggedNoListBlockIds;
|
|
154
187
|
constructor(deps: PeerJoinBackfillDeps, config?: PeerJoinBackfillConfig);
|
|
155
188
|
/**
|
|
156
189
|
* Subscribe to `peer:identify` AND schedule a catch-up for already-connected peers whose
|
|
157
190
|
* stored protocols already name this network's block-transfer protocol — a peer that does
|
|
158
|
-
* not speak it (a bare circuit relay, a bootstrap node) is never scheduled at all.
|
|
159
|
-
*
|
|
191
|
+
* not speak it (a bare circuit relay, a bootstrap node) is never scheduled at all. Also
|
|
192
|
+
* subscribes to `peer:disconnect`, so a peer caught up before it went away is run again on
|
|
193
|
+
* its return. See {@link speaksBlockTransfer} and the module comment.
|
|
160
194
|
*/
|
|
161
195
|
start(): void;
|
|
162
196
|
/** Unsubscribe, clear timers; in-flight runs observe the stopped flag and bail. */
|
|
163
197
|
stop(): void;
|
|
198
|
+
/**
|
|
199
|
+
* A peer's last connection closed: it is no longer caught up, so its next `peer:identify`
|
|
200
|
+
* re-runs. Only `done` is cleared. The memo stays (it keeps the re-run to what moved),
|
|
201
|
+
* `contacted` stays (a link flapping inside one settle window costs one run), and the
|
|
202
|
+
* backoff state stays (clearing it re-dials an unreachable peer on every reconnect; see the
|
|
203
|
+
* module comment's retry paragraph).
|
|
204
|
+
*
|
|
205
|
+
* When the returning peer wrote while away with a cohort that named this node, its own
|
|
206
|
+
* re-run pushes those blocks here and reports them through `reportBlockHeld`, so its
|
|
207
|
+
* under-replication drain does not push them again. Revisit if a measurement shows reconnect
|
|
208
|
+
* re-runs dominating push traffic.
|
|
209
|
+
*/
|
|
210
|
+
private forgetCaughtUp;
|
|
164
211
|
/**
|
|
165
212
|
* (Re-)schedule a debounced catch-up for every currently-connected peer whose protocols, as
|
|
166
213
|
* already stored in the libp2p peer store, name this network's block-transfer protocol. A
|
|
@@ -177,7 +224,11 @@ export declare class PeerJoinBackfill {
|
|
|
177
224
|
scheduleConnectedPeers(): Promise<number>;
|
|
178
225
|
/** Schedules `peerId` only if the peer store already knows it speaks this network's protocol. */
|
|
179
226
|
private scheduleIfKnownToSpeak;
|
|
180
|
-
/**
|
|
227
|
+
/**
|
|
228
|
+
* Entry point for connection churn: one run per peer per settle window, except that a peer
|
|
229
|
+
* never run for is run at once (on a zero-delay timer, so a burst of identify events in one
|
|
230
|
+
* turn still collapses, and no work happens inside the libp2p event dispatch).
|
|
231
|
+
*/
|
|
181
232
|
private schedulePeer;
|
|
182
233
|
/**
|
|
183
234
|
* Run one peer's catch-up now, bypassing the debounce. Never rejects. Returns an
|
|
@@ -186,14 +237,16 @@ export declare class PeerJoinBackfill {
|
|
|
186
237
|
*/
|
|
187
238
|
catchUpPeer(peerId: PeerId): Promise<PeerJoinBackfillResult>;
|
|
188
239
|
/**
|
|
189
|
-
* Run one peer's catch-up now and wait for it,
|
|
190
|
-
*
|
|
191
|
-
*
|
|
240
|
+
* Run one peer's catch-up now and wait for it, even when the peer is memoized as caught up:
|
|
241
|
+
* wait out a run already in flight for it, then run again. The run still skips every block the
|
|
242
|
+
* peer is known to hold (it accepted or refused that revision, or signed it), so for a peer that
|
|
243
|
+
* holds the whole store it pushes nothing and returns `alreadyHeld` equal to the store size.
|
|
244
|
+
* Never rejects; an all-zero result only when stopped.
|
|
192
245
|
*
|
|
193
246
|
* For a caller that must know the peer holds this store before it proceeds, where
|
|
194
247
|
* {@link catchUpPeer}'s empty answer would hide a run that was denied (a `peer:identify`
|
|
195
|
-
* run can pass the gate a moment before the caller authorized the peer) or
|
|
196
|
-
*
|
|
248
|
+
* run can pass the gate a moment before the caller authorized the peer) or commits made
|
|
249
|
+
* since the peer was caught up.
|
|
197
250
|
*/
|
|
198
251
|
forceCatchUpPeer(peerId: PeerId): Promise<PeerJoinBackfillResult>;
|
|
199
252
|
/**
|
|
@@ -218,10 +271,26 @@ export declare class PeerJoinBackfill {
|
|
|
218
271
|
* timeout. Reset when a run finally lands cleanly, so a peer that recovers can report again.
|
|
219
272
|
*/
|
|
220
273
|
private warnPersistentFailure;
|
|
274
|
+
/** This peer's memo, created empty on first use. */
|
|
275
|
+
private memoFor;
|
|
276
|
+
/**
|
|
277
|
+
* Tell the node which blocks of one push reply the peer accepted, at the revision pushed.
|
|
278
|
+
* Best-effort: a failed report is logged and costs only the drain's duplicate push.
|
|
279
|
+
*/
|
|
280
|
+
private reportAccepted;
|
|
221
281
|
/**
|
|
222
|
-
* The actual copy. `clean` =
|
|
282
|
+
* The actual copy. `clean` = the gate admitted the peer, the store is listable and no push
|
|
283
|
+
* threw (a block the receiver refused is remembered per block instead, see {@link PeerMemo}).
|
|
223
284
|
* `pushFailed` = at least one push THREW, which is the only non-clean outcome a retry can
|
|
224
|
-
* change; the others are verdicts (denied, inert store
|
|
285
|
+
* change; the others are verdicts (denied, inert store).
|
|
225
286
|
*/
|
|
226
287
|
private runCatchUp;
|
|
288
|
+
/**
|
|
289
|
+
* Read one block for the push to `key`, or count why it is skipped in `result` and return
|
|
290
|
+
* nothing: not committed, already held by the peer (memo or its own commit signature), not
|
|
291
|
+
* materialized, or too large for one message. The proof is read for EXACTLY the revision the
|
|
292
|
+
* meta will name, from the same `latest`; absent is normal (nothing retained a proof for that
|
|
293
|
+
* revision), and the block still ships with its meta for the receiver to decide.
|
|
294
|
+
*/
|
|
295
|
+
private readOffer;
|
|
227
296
|
}
|