@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
|
@@ -2,8 +2,8 @@ import { Database } from '@quereus/quereus';
|
|
|
2
2
|
import type { SqlParameters } from '@quereus/quereus';
|
|
3
3
|
import type { Libp2p } from '@libp2p/interface';
|
|
4
4
|
import type { IRepo } from '@optimystic/db-core';
|
|
5
|
-
import type { StrandRow, JoinRequestRow, JoinOutcome, PendingJoin, NodeProfile, PeerAddressRecord, CadrePeerRow, OwnerKeyRow, RevocationRow, RevocationLedgerOpenResult, DeviceTokenRecord, DeviceTokenRow, CadreInviteRow, CadreInviteUsageRow, CadreInviteRedemptionResult, CadreInviteStatus, SeedPeer, MachineRemovalResult } from './types.js';
|
|
6
|
-
import type { ControlTable, RevocableTable, BoundControlDomain, ControlAction, CadreInviteSignedFields } from './control-authorization.js';
|
|
5
|
+
import type { StrandRow, JoinRequestRow, JoinOutcome, PendingJoin, NodeProfile, PeerAddressRecord, CadrePeerRow, OwnerKeyRow, RevocationRow, RevocationLedgerOpenResult, DeviceTokenRecord, DeviceTokenRow, CadreInviteRow, CadreInviteUsageRow, CadreInviteRedemptionResult, CadreInviteStatus, OwnerGrantRow, OwnerGrantUsageRow, OwnerGrantRedemptionResult, OwnerGrantStatus, SeedPeer, MachineRemovalResult } from './types.js';
|
|
6
|
+
import type { ControlTable, RevocableTable, BoundControlDomain, ControlAction, CadreInviteSignedFields, OwnerGrantSignedFields } from './control-authorization.js';
|
|
7
7
|
import type { ControlRetryAbandonment } from './control-retry.js';
|
|
8
8
|
export type { ControlTable, RevocableTable, ControlDomain, ControlAction } from './control-authorization.js';
|
|
9
9
|
/**
|
|
@@ -52,13 +52,14 @@ export declare class FormationAbortedError extends Error {
|
|
|
52
52
|
/**
|
|
53
53
|
* The invitation's seat budget is already spent: the count of recorded usage rows for the
|
|
54
54
|
* invitation has reached its `TotalUses`, so this redemption cannot be given a seat. `token`
|
|
55
|
-
* is the `FormationInvite.Token` or the `
|
|
56
|
-
* redeemed against.
|
|
55
|
+
* is the `FormationInvite.Token`, the `CadreInvite.Key` or the `OwnerGrant.Key`, whichever
|
|
56
|
+
* table the caller redeemed against.
|
|
57
57
|
*
|
|
58
58
|
* Raised off the committed-count check ({@link ControlDatabase.assertSeatRemains}) ahead of
|
|
59
59
|
* the write, inside {@link ControlDatabase.redeemInvitation} /
|
|
60
60
|
* {@link ControlDatabase.recordFormationUsage}, off the same check for a cadre invitation
|
|
61
|
-
* ({@link ControlDatabase.redeemCadreInvite})
|
|
61
|
+
* ({@link ControlDatabase.redeemCadreInvite}) and an owner grant
|
|
62
|
+
* ({@link ControlDatabase.redeemOwnerGrant}), and off the unlocked pre-check
|
|
62
63
|
* `ControlFormationUsageRecorder.authorizeUsage` runs before the manager issues a membership
|
|
63
64
|
* pass — and off nothing else. It exists because
|
|
64
65
|
* without it the refusal surfaces as a generic `CHECK constraint failed: Authorized` from the
|
|
@@ -89,6 +90,35 @@ export declare class CadreInviteIssuerUnknownError extends Error {
|
|
|
89
90
|
cause?: unknown;
|
|
90
91
|
});
|
|
91
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* {@link ControlDatabase.seatOwnerGrant} was refused by `OwnerGrant.AuthorizedInsert`: the
|
|
95
|
+
* grant twin of {@link CadreInviteIssuerUnknownError}, for the same two causes (the issuer's
|
|
96
|
+
* `OwnerKey` row has not replicated here yet or has since been removed; or a tampered row).
|
|
97
|
+
* Its own class rather than a shared one so a caller that handles invitation and grant
|
|
98
|
+
* bundles can tell by `instanceof` which credential was refused.
|
|
99
|
+
*/
|
|
100
|
+
export declare class OwnerGrantIssuerUnknownError extends Error {
|
|
101
|
+
readonly grantKey: string;
|
|
102
|
+
readonly issuerKey: string;
|
|
103
|
+
constructor(grantKey: string, issuerKey: string, options?: {
|
|
104
|
+
cause?: unknown;
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* An issued row (a `CadreInvite` or an `OwnerGrant`) the cadre holds that this node cannot read
|
|
109
|
+
* yet: {@link ControlDatabase.seatCadreInvite} / {@link ControlDatabase.seatOwnerGrant} had its
|
|
110
|
+
* insert refused by a cohort member already holding the row, and a refreshed read here still
|
|
111
|
+
* finds nothing; or the redemption that follows the seat finds no row. It clears once this
|
|
112
|
+
* node's store catches up, which is why the redemption paths answer it with the retryable
|
|
113
|
+
* `conflict` code.
|
|
114
|
+
*/
|
|
115
|
+
export declare class IssuedRowNotHereYetError extends Error {
|
|
116
|
+
readonly table: IssuedTable;
|
|
117
|
+
readonly key: string;
|
|
118
|
+
constructor(table: IssuedTable, key: string, options?: {
|
|
119
|
+
cause?: unknown;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
92
122
|
/**
|
|
93
123
|
* What a recorded `FormationUsage` row came out as.
|
|
94
124
|
*
|
|
@@ -238,6 +268,73 @@ export declare function cadreInviteRedeemMessage(partyId: string, fields: CadreI
|
|
|
238
268
|
* distinct action tag, so neither signature can stand in for the other.
|
|
239
269
|
*/
|
|
240
270
|
export declare function cadreInviteConsentMessage(partyId: string, fields: CadreInviteRedemptionFields): Uint8Array;
|
|
271
|
+
/**
|
|
272
|
+
* The exact bytes an owner signs to seat an `OwnerGrant` row — the TS mirror of the `'add'`
|
|
273
|
+
* digest in `OwnerGrant.AuthorizedInsert`, over the whole row in the schema's order with
|
|
274
|
+
* nullable columns as `''` (`ownerGrantRowFields`, shared with the base64url verifier
|
|
275
|
+
* `peer-authorization.ts`'s `ownerGrantAddDigest`). `expiresAt` must already be the stored
|
|
276
|
+
* canonical `datetime` string, as {@link ControlDatabase.insertOwnerGrant} produces it, for
|
|
277
|
+
* the reason on {@link cadreInviteAddMessage}.
|
|
278
|
+
*/
|
|
279
|
+
export declare function ownerGrantAddMessage(partyId: string, row: OwnerGrantSignedFields): Uint8Array;
|
|
280
|
+
/** The fields both owner-grant redemption signatures cover; see {@link ownerGrantRedeemMessage}. */
|
|
281
|
+
export interface OwnerGrantRedemptionFields {
|
|
282
|
+
grantKey: string;
|
|
283
|
+
/** The single-use nonce the holder minted for this redemption (`generateStampId`). */
|
|
284
|
+
usageStampId: string;
|
|
285
|
+
/** The owner key being seated (base64url ed25519). */
|
|
286
|
+
seatedKey: string;
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* The exact bytes the HOLDER of an owner grant signs, with the grant's private key, to redeem
|
|
290
|
+
* it once — the TS mirror of the `'redeem'` digest `OwnerGrantUsage.GrantPossessed` verifies
|
|
291
|
+
* against the grant's public key. Binding the seated key and the nonce means a captured
|
|
292
|
+
* redemption can seat no other key and spend no other use.
|
|
293
|
+
*/
|
|
294
|
+
export declare function ownerGrantRedeemMessage(partyId: string, fields: OwnerGrantRedemptionFields): Uint8Array;
|
|
295
|
+
/**
|
|
296
|
+
* The exact bytes the SEATED key signs, proving the holder holds it — the TS mirror of the
|
|
297
|
+
* `'consent'` digest `OwnerGrantUsage.OwnerConsented` verifies against `SeatedKey`. Same
|
|
298
|
+
* fields as {@link ownerGrantRedeemMessage} under a distinct action tag, so neither
|
|
299
|
+
* signature can stand in for the other.
|
|
300
|
+
*/
|
|
301
|
+
export declare function ownerGrantConsentMessage(partyId: string, fields: OwnerGrantRedemptionFields): Uint8Array;
|
|
302
|
+
/**
|
|
303
|
+
* A guarded table of owner-issued credentials: signed rows that travel inside a bundle, are
|
|
304
|
+
* seated verbatim by any member ({@link ControlDatabase.seatCadreInvite},
|
|
305
|
+
* {@link ControlDatabase.seatOwnerGrant}), and are never deleted (`NoDelete`) because the
|
|
306
|
+
* rows they admitted prove themselves through them. Withdrawing one is a tombstone alone
|
|
307
|
+
* ({@link ControlDatabase.withdrawCadreInvite}, {@link ControlDatabase.withdrawOwnerGrant}).
|
|
308
|
+
*/
|
|
309
|
+
type IssuedTable = Extract<RevocableTable, 'CadreInvite' | 'OwnerGrant'>;
|
|
310
|
+
/**
|
|
311
|
+
* An owner key's label as its row stores it: trimmed, a blank one left out, and refused past
|
|
312
|
+
* {@link OWNER_KEY_MAX_LABEL} by name rather than by the schema's `LabelLength` check.
|
|
313
|
+
* Idempotent, so a caller that checks before opening an owner approval can pass the result on.
|
|
314
|
+
*/
|
|
315
|
+
export declare function ownerKeyLabel(label: string | null | undefined): string | null;
|
|
316
|
+
/**
|
|
317
|
+
* Signs one owner digest's raw bytes (no pre-hash) and returns a base64url Ed25519 signature:
|
|
318
|
+
* the callback every owner-signed writer takes. An `OwnerSign` from an open
|
|
319
|
+
* `OwnerSigner.approve` fits it, and so does a synchronous signer.
|
|
320
|
+
*
|
|
321
|
+
* NOTE: writers that read the stamp they sign do so inside the write lock, so the lock is held
|
|
322
|
+
* across these calls, and a `lockedWithRetry` re-run signs again inside the same approval.
|
|
323
|
+
* Free for a software signer. If a prompting signer ever holds the lock long enough to stall
|
|
324
|
+
* other control writes, sign outside the lock and re-check the stamp inside it.
|
|
325
|
+
*/
|
|
326
|
+
export type ControlSign = (message: Uint8Array) => string | Promise<string>;
|
|
327
|
+
/** What an owner asks {@link ControlDatabase.insertCadreInvite} (or {@link ControlDatabase.signCadreInvite}) to sign. */
|
|
328
|
+
export interface CadreInviteFields {
|
|
329
|
+
/** The invitation public key (base64url ed25519), the row's primary key. */
|
|
330
|
+
key: string;
|
|
331
|
+
/** The device the invitation is for, or null for whoever redeems first. */
|
|
332
|
+
peerId?: string | null;
|
|
333
|
+
/** Epoch ms after which it cannot be redeemed; omit for never. */
|
|
334
|
+
expiresAtMs?: number | null;
|
|
335
|
+
/** How many redemptions it allows; omit for unlimited. */
|
|
336
|
+
totalUses?: number | null;
|
|
337
|
+
}
|
|
241
338
|
/** The control tables keyed by a strand id whose repeat insert a caller may treat as idempotent. */
|
|
242
339
|
type StrandIdTable = Extract<RevocableTable, 'Strand' | 'JoinedStrand'>;
|
|
243
340
|
/**
|
|
@@ -256,6 +353,13 @@ type StrandIdTable = Extract<RevocableTable, 'Strand' | 'JoinedStrand'>;
|
|
|
256
353
|
* reword reddens there.
|
|
257
354
|
*/
|
|
258
355
|
export declare function isStrandIdConflict(error: unknown, table: StrandIdTable): boolean;
|
|
356
|
+
/**
|
|
357
|
+
* Did a seat of an issued row fail because a cohort member already holds a row under its key?
|
|
358
|
+
* {@link ControlDatabase.seatCadreInvite} / {@link ControlDatabase.seatOwnerGrant} reach this
|
|
359
|
+
* on a node whose store lags the cohort: its guard read missed the row, and the member that
|
|
360
|
+
* checked the commit refused the insert. Text-matched, failing closed, like {@link isStrandIdConflict}.
|
|
361
|
+
*/
|
|
362
|
+
export declare function isIssuedRowKeyConflict(error: unknown, table: IssuedTable): boolean;
|
|
259
363
|
/**
|
|
260
364
|
* Did {@link ControlDatabase.insertJoinRequest} fail because the party already holds a
|
|
261
365
|
* `JoinRequest` row for this invitation? The caller adopts the existing request instead.
|
|
@@ -299,22 +403,17 @@ export declare const REVOCATION_LEDGER_MARKER: {
|
|
|
299
403
|
};
|
|
300
404
|
/**
|
|
301
405
|
* Project `CadrePeer` rows into the {@link SeedPeer} list a seed, or a cadre invitation
|
|
302
|
-
* redemption reply, carries.
|
|
406
|
+
* redemption reply, carries. `minterPeerId` is the machine minting the seed or answering the
|
|
407
|
+
* redemption; it contributes an entry only through its own `CadrePeer` row.
|
|
303
408
|
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
*
|
|
310
|
-
* NOTE: this is the one owner lookup deliberately left on the REPLICATED table rather than
|
|
311
|
-
* the node-local anchor. `SeedPeer.isOwner` is a dial hint — the receiver dials owner-flagged
|
|
312
|
-
* peers first — not a trust decision, and the receiver re-derives real trust from its own
|
|
313
|
-
* anchor. So a polluted table costs at most a wasted dial, while anchoring here would
|
|
314
|
-
* silently drop legitimate co-owners this node never pinned. If `isOwner` ever gates
|
|
315
|
-
* anything the receiver TRUSTS, move it to the anchor.
|
|
409
|
+
* A peer is `preferred` when it is the minter, the one machine certain to be reachable by the
|
|
410
|
+
* receiver at that moment, since it is delivering the seed or the reply, or an always-on
|
|
411
|
+
* machine (`Profile = 'storage'`), the ones that stay dialable. Preference is a dial hint and
|
|
412
|
+
* reads no `OwnerKey` row: owner keys are not machines, and a receiver derives trust from its
|
|
413
|
+
* own anchor, never from this list. A `Profile` is self-declared and not checked here, so a
|
|
414
|
+
* machine misdeclaring one costs the receiver a wasted dial at most.
|
|
316
415
|
*/
|
|
317
|
-
export declare function projectSeedPeers(rows: readonly CadrePeerRow[],
|
|
416
|
+
export declare function projectSeedPeers(rows: readonly CadrePeerRow[], minterPeerId: string): SeedPeer[];
|
|
318
417
|
/**
|
|
319
418
|
* Guarded tables a node may reap locally once their tombstone has committed — the
|
|
320
419
|
* tables whose `AuthorizedDelete` carries the REAP branch (see the constraint comment on
|
|
@@ -326,8 +425,8 @@ export declare function projectSeedPeers(rows: readonly CadrePeerRow[], ownerKey
|
|
|
326
425
|
* hold, so a reaped row is recoverable by re-forming. `JoinRequest` is reapable for the same
|
|
327
426
|
* reason: the inviting party and the user also hold its invitation. `OwnerKey` has no
|
|
328
427
|
* production removal path and `MinOneOwner` makes an automated owner-key reap a
|
|
329
|
-
* party-bricking hazard. `CadreInvite`
|
|
330
|
-
*
|
|
428
|
+
* party-bricking hazard. `CadreInvite` and `OwnerGrant` are never deleted at all (their
|
|
429
|
+
* tombstones are withdrawals over rows that stay), so there is nothing to reap.
|
|
331
430
|
*/
|
|
332
431
|
export declare const REAPABLE_TABLES: readonly ["CadrePeer", "DeviceToken", "ValidationKey", "JoinedStrand", "JoinRequest"];
|
|
333
432
|
export type ReapableTable = (typeof REAPABLE_TABLES)[number];
|
|
@@ -361,8 +460,6 @@ export type MembershipChangeListener = (reason: string) => Promise<void>;
|
|
|
361
460
|
* the writer that files the row supplies it.
|
|
362
461
|
*/
|
|
363
462
|
export type RevokedRowRef = Pick<RevocationRow, 'tableName' | 'rowKey' | 'stampId'>;
|
|
364
|
-
/** What {@link ControlDatabase.removeMachine} wrote: `CadreNode.removeMachine`'s result, less the re-vouches it made first. */
|
|
365
|
-
export type MachineRemovalWrites = Pick<MachineRemovalResult, 'removedPeer' | 'removedOwnerKey' | 'removedDeviceToken' | 'withdrawnInvitations'>;
|
|
366
463
|
/**
|
|
367
464
|
* Notified after a guarded-table delete — and the `CadreControl.Revocation`
|
|
368
465
|
* tombstone it writes in the same transaction — has COMMITTED.
|
|
@@ -400,6 +497,8 @@ export interface ControlDatabaseConfig {
|
|
|
400
497
|
* Optional path to the control schema file.
|
|
401
498
|
* If not provided, uses the embedded schema for cross-platform compatibility.
|
|
402
499
|
* Only use this if you need to override the default schema (e.g., for testing).
|
|
500
|
+
* The file has the shape of `schemas/control.qsql`: one `declare schema CadreControl`
|
|
501
|
+
* followed by one plain `apply schema CadreControl` ({@link controlSchemaFileDeclaration}).
|
|
403
502
|
*/
|
|
404
503
|
schemaPath?: string;
|
|
405
504
|
/** Libp2p node for the control network (injected) */
|
|
@@ -448,6 +547,8 @@ export declare class ControlDatabase {
|
|
|
448
547
|
*/
|
|
449
548
|
initialize(): Promise<void>;
|
|
450
549
|
private loadSchema;
|
|
550
|
+
/** Apply the declared control schema, retrying transient cohort failures. */
|
|
551
|
+
private applyControlSchema;
|
|
451
552
|
/**
|
|
452
553
|
* One UNRETRIED drain of a control read to rows — every read in this class bottoms out
|
|
453
554
|
* here (almost all via {@link readRows}, which adds the transient-failure retry), and
|
|
@@ -592,7 +693,7 @@ export declare class ControlDatabase {
|
|
|
592
693
|
/**
|
|
593
694
|
* Every strand this party joined from another party (`CadreControl.JoinedStrand`), shaped
|
|
594
695
|
* as a {@link StrandRow} so a reader can treat it like one of the party's own. A joiner
|
|
595
|
-
* never founds, so `FounderOwnerKey`
|
|
696
|
+
* never founds, so `FounderOwnerKey` and `FounderPeerId` are always null. Read raw, like {@link queryStrands}:
|
|
596
697
|
* no retired-stamp filter.
|
|
597
698
|
*/
|
|
598
699
|
queryJoinedStrands(): Promise<StrandRow[]>;
|
|
@@ -640,8 +741,10 @@ export declare class ControlDatabase {
|
|
|
640
741
|
* Idempotent genesis: insert `key` as the founding owner key only when
|
|
641
742
|
* the party has none yet. Returns true if it inserted, false if an owner
|
|
642
743
|
* key already existed (so a repeat `--owner` start is a no-op).
|
|
744
|
+
*
|
|
745
|
+
* @param label - the user's name for the key ({@link insertOwnerKey}).
|
|
643
746
|
*/
|
|
644
|
-
ensureOwnerKey(key: string): Promise<boolean>;
|
|
747
|
+
ensureOwnerKey(key: string, label?: string): Promise<boolean>;
|
|
645
748
|
/**
|
|
646
749
|
* Collect every owner key (`CadreControl.OwnerKey.Key`) as a set.
|
|
647
750
|
*
|
|
@@ -666,8 +769,9 @@ export declare class ControlDatabase {
|
|
|
666
769
|
*/
|
|
667
770
|
getOwnerKeys(retry?: boolean): Promise<Set<string>>;
|
|
668
771
|
/**
|
|
669
|
-
* Every live `OwnerKey` row with its persisted proof ({@link OwnerKeyRow}) — what
|
|
670
|
-
* trusted-owner anchor sync derives from (`CadreNode.syncOwnerAnchor`)
|
|
772
|
+
* Every live `OwnerKey` row with its label and persisted proof ({@link OwnerKeyRow}) — what
|
|
773
|
+
* the trusted-owner anchor sync derives from (`CadreNode.syncOwnerAnchor`) and the owner-key
|
|
774
|
+
* listing shows (`CadreNode.listOwnerKeys`). Rows whose
|
|
671
775
|
* `StampId` is retired in `CadreControl.Revocation` are excluded here, as
|
|
672
776
|
* {@link queryCadrePeers} excludes them and for the reason on {@link getOwnerKeys}, which
|
|
673
777
|
* reads through this. A row with no stamp (the schema permits it; no writer produces one)
|
|
@@ -723,15 +827,15 @@ export declare class ControlDatabase {
|
|
|
723
827
|
queryCadrePeers(retry?: boolean): Promise<CadrePeerRow[]>;
|
|
724
828
|
/**
|
|
725
829
|
* The cadre's peers as a seed or a redemption reply carries them: every live `CadrePeer`
|
|
726
|
-
* row with its addresses,
|
|
727
|
-
*
|
|
830
|
+
* row with its addresses, the minter and every always-on machine marked preferred
|
|
831
|
+
* ({@link projectSeedPeers}).
|
|
728
832
|
*
|
|
729
833
|
* Read through {@link queryCadrePeers}, never a raw `CadrePeer` select, so a removed
|
|
730
834
|
* member's addresses are never packed into a seed or handed to a redeeming device as a
|
|
731
835
|
* dial hint: a revoked peer is off the addressable surface everywhere, and this is one of
|
|
732
836
|
* its exits.
|
|
733
837
|
*/
|
|
734
|
-
querySeedPeers(): Promise<SeedPeer[]>;
|
|
838
|
+
querySeedPeers(minterPeerId: string): Promise<SeedPeer[]>;
|
|
735
839
|
/**
|
|
736
840
|
* Every approver public key currently enrolled in `CadreControl.ValidationKey` — the
|
|
737
841
|
* set a `ValidationUrl` redemption's approval signature must be signed by (see
|
|
@@ -948,19 +1052,34 @@ export declare class ControlDatabase {
|
|
|
948
1052
|
updateSelfDeviceToken(record: DeviceTokenRecord): Promise<void>;
|
|
949
1053
|
/**
|
|
950
1054
|
* Insert the initial owner key (bootstrap - no existing owners required)
|
|
1055
|
+
*
|
|
1056
|
+
* @param label - the user's name for the key, at most {@link OWNER_KEY_MAX_LABEL}
|
|
1057
|
+
* characters once trimmed; blank is stored as none.
|
|
951
1058
|
*/
|
|
952
|
-
insertOwnerKey(key: string): Promise<void>;
|
|
1059
|
+
insertOwnerKey(key: string, label?: string): Promise<void>;
|
|
953
1060
|
/**
|
|
954
1061
|
* Owner-signed INSERT of a further `OwnerKey` row, notifying the membership listener once
|
|
955
1062
|
* it has committed. The signed-add branch of `OwnerKey.Authorized`: the signer must be an
|
|
956
1063
|
* owner in the pre-transaction snapshot, its signature covers the `'add'` digest over
|
|
957
|
-
* (Key, StampId), and the stored `VouchOwner`/`VouchSig` are that same pair, so a
|
|
958
|
-
* can later judge the add against its own anchor (`verifyOwnerKeyVoucher`).
|
|
1064
|
+
* (Key, Label, StampId), and the stored `VouchOwner`/`VouchSig` are that same pair, so a
|
|
1065
|
+
* reader can later judge the add against its own anchor (`verifyOwnerKeyVoucher`).
|
|
959
1066
|
*
|
|
960
1067
|
* Through {@link mutateCadrePeer}, the membership hub. The liveness read, the stamp and the
|
|
961
1068
|
* signature all sit inside the locked body: a {@link lockedWithRetry} re-run starts over a
|
|
962
1069
|
* rolled-back attempt with a fresh stamp, as {@link redeemCadreInvite} does.
|
|
963
1070
|
*
|
|
1071
|
+
* @param label - the user's name for the key, as {@link insertOwnerKey}'s.
|
|
1072
|
+
* @returns `true` when this call seated the row, `false` when `key` is already a live owner
|
|
1073
|
+
* here (nothing written, whatever its label).
|
|
1074
|
+
* @throws when this node holds `key`'s row under a retired stamp
|
|
1075
|
+
* ({@link liveOwnerKeyStamp}), or when the schema refuses the signer.
|
|
1076
|
+
*/
|
|
1077
|
+
insertOwnerKeyVouched(key: string, ownerKey: string, signMessage: ControlSign, label?: string | null): Promise<boolean>;
|
|
1078
|
+
/**
|
|
1079
|
+
* The stamp of `key`'s live `OwnerKey` row here, or null when this node holds none: the
|
|
1080
|
+
* insert-if-absent read of {@link insertOwnerKeyVouched} and {@link redeemOwnerGrant}. Inside
|
|
1081
|
+
* the locked body (both reads `retry: false`; see queryStampId's NOTE).
|
|
1082
|
+
*
|
|
964
1083
|
* NOTE: re-adding a removed owner from a machine that still holds its retired physical row
|
|
965
1084
|
* is refused here by name: `OwnerKey` is not reaped, so a machine that received the
|
|
966
1085
|
* tombstone without the delete (see {@link getOwnerKeys}) keeps the row, and an insert there
|
|
@@ -968,12 +1087,15 @@ export declare class ControlDatabase {
|
|
|
968
1087
|
* machine the delete reached; if re-adding removed owners becomes routine, the fix is an
|
|
969
1088
|
* `OwnerKey` reap branch, gated so it can never take the table below `MinOneOwner`.
|
|
970
1089
|
*
|
|
971
|
-
* @
|
|
972
|
-
* here (nothing written).
|
|
973
|
-
* @throws when this node holds `key`'s row under a retired stamp (above), or when the
|
|
974
|
-
* schema refuses the signer.
|
|
1090
|
+
* @throws when this node holds `key`'s row under a retired stamp (above).
|
|
975
1091
|
*/
|
|
976
|
-
|
|
1092
|
+
private liveOwnerKeyStamp;
|
|
1093
|
+
/**
|
|
1094
|
+
* The signed-add statement of {@link insertOwnerKeyVouched} and {@link revouchOwnerKey}, the
|
|
1095
|
+
* stored voucher being the signing pair. Bare `exec`: callers hold the write lock, which is
|
|
1096
|
+
* NOT re-entrant.
|
|
1097
|
+
*/
|
|
1098
|
+
private execOwnerKeyVouchedInsert;
|
|
977
1099
|
/**
|
|
978
1100
|
* Owner-signed removal of one `OwnerKey` row plus the `Revocation` tombstone retiring its
|
|
979
1101
|
* stamp, notifying the membership listener once it has committed. The remove branch of
|
|
@@ -991,56 +1113,51 @@ export declare class ControlDatabase {
|
|
|
991
1113
|
* (nothing written, no tombstone).
|
|
992
1114
|
* @throws when `key` is `ownerKey`, or is the only live owner.
|
|
993
1115
|
*/
|
|
994
|
-
deleteOwnerKey(key: string, ownerKey: string, signMessage:
|
|
1116
|
+
deleteOwnerKey(key: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
995
1117
|
/**
|
|
996
1118
|
* Re-vouch a live `OwnerKey` row under this owner's signature: in ONE transaction, the
|
|
997
1119
|
* current row's guarded delete plus tombstone ({@link deleteOwnerKey}'s body) and an insert
|
|
998
|
-
* of the same key under a fresh stamp, signed by `ownerKey`
|
|
999
|
-
* body), as {@link rewritePendingJoin} re-seats a request. `OwnerKey` has `NoUpdate`, so
|
|
1120
|
+
* of the same key and label under a fresh stamp, signed by `ownerKey`
|
|
1121
|
+
* ({@link insertOwnerKeyVouched}'s body), as {@link rewritePendingJoin} re-seats a request. `OwnerKey` has `NoUpdate`, so
|
|
1000
1122
|
* this is the only way to rebind a row's voucher; the anchor derivation lets the re-add
|
|
1001
1123
|
* under a fresh stamp beat the older tombstone (`deriveOwnerAnchor`). For
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1124
|
+
* `CadreNode.removeOwner`: the owners a key being removed had added keep their anchor place
|
|
1125
|
+
* everywhere once their row stands on a remaining owner's signature.
|
|
1004
1126
|
*
|
|
1005
|
-
* Through {@link mutateCadrePeer}, the membership hub. The
|
|
1127
|
+
* Through {@link mutateCadrePeer}, the membership hub. The row read runs inside the locked
|
|
1006
1128
|
* body; a row whose stamp is already retired (held without its delete) is reported as absent
|
|
1007
1129
|
* rather than tombstoned twice.
|
|
1008
1130
|
*
|
|
1009
1131
|
* @returns `true` when the row was re-vouched, `false` when `key` is not a live owner here.
|
|
1010
1132
|
* @throws when `key` is `ownerKey` (an owner cannot sign its own row's removal).
|
|
1011
1133
|
*/
|
|
1012
|
-
revouchOwnerKey(key: string, ownerKey: string, signMessage:
|
|
1134
|
+
revouchOwnerKey(key: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1013
1135
|
/**
|
|
1014
|
-
* Remove one machine completely, in ONE transaction: its `CadrePeer` row
|
|
1015
|
-
*
|
|
1016
|
-
*
|
|
1017
|
-
*
|
|
1018
|
-
*
|
|
1019
|
-
*
|
|
1020
|
-
*
|
|
1021
|
-
*
|
|
1136
|
+
* Remove one machine completely, in ONE transaction: its `CadrePeer` row and its
|
|
1137
|
+
* `DeviceToken` row when it has one, each with the `Revocation` tombstone retiring its stamp
|
|
1138
|
+
* ({@link deleteGuardedRow}'s statement pair, composed bare as {@link deleteStrandAndPartyKey}
|
|
1139
|
+
* composes it), plus a withdrawal tombstone for every `CadreInvite` in `target.inviteKeys`
|
|
1140
|
+
* that is still live here ({@link withdrawCadreInvite}'s body). One transaction so no reader
|
|
1141
|
+
* sees the machine half removed: a row gone but its push token resolvable, or an invitation
|
|
1142
|
+
* it could redeem again still open. Owner keys are never touched: they are the person's, and
|
|
1143
|
+
* removing one is {@link deleteOwnerKey}.
|
|
1022
1144
|
*
|
|
1023
1145
|
* Through {@link mutateCadrePeer}, the membership hub. Every stamp is read inside the locked
|
|
1024
1146
|
* body, and a row whose stamp is already retired (held without its delete) is skipped rather
|
|
1025
|
-
* than tombstoned twice.
|
|
1026
|
-
* owner-key arm. Nothing to remove writes nothing and returns all false.
|
|
1027
|
-
*
|
|
1028
|
-
* The caller decides WHAT to remove, which owner key is the machine's and which invitations
|
|
1029
|
-
* it could use, from its plan (`CadreNode.planMachineRemoval`); this method only refuses to
|
|
1030
|
-
* write an inconsistent set.
|
|
1147
|
+
* than tombstoned twice. Nothing to remove writes nothing and returns all false.
|
|
1031
1148
|
*
|
|
1032
|
-
*
|
|
1149
|
+
* The caller decides which invitations the machine could use, from its plan
|
|
1150
|
+
* (`CadreNode.planMachineRemoval`).
|
|
1033
1151
|
*/
|
|
1034
1152
|
removeMachine(target: {
|
|
1035
1153
|
peerId: string;
|
|
1036
|
-
ownerKey: string | null;
|
|
1037
1154
|
inviteKeys: readonly string[];
|
|
1038
|
-
}, ownerKey: string, signMessage:
|
|
1155
|
+
}, ownerKey: string, signMessage: ControlSign): Promise<MachineRemovalResult>;
|
|
1039
1156
|
/**
|
|
1040
1157
|
* The guarded removals {@link removeMachine} runs, signed over each row's current stamp:
|
|
1041
|
-
* the `CadrePeer` row
|
|
1042
|
-
*
|
|
1043
|
-
*
|
|
1158
|
+
* the `CadrePeer` row and the `DeviceToken` row, each when present. Inside the locked body
|
|
1159
|
+
* (every read `retry: false`); one `Revocation` scan serves the retired-stamp check for both
|
|
1160
|
+
* tables.
|
|
1044
1161
|
*/
|
|
1045
1162
|
private signMachineRemovals;
|
|
1046
1163
|
/**
|
|
@@ -1062,7 +1179,7 @@ export declare class ControlDatabase {
|
|
|
1062
1179
|
* with the owner's private key, returning a base64url signature
|
|
1063
1180
|
* @param memberPrivateKey - Optional private key for membership in closed strands
|
|
1064
1181
|
*/
|
|
1065
|
-
insertStrand(strandId: string, type: 'o' | 'c', ownerKey: string, signMessage:
|
|
1182
|
+
insertStrand(strandId: string, type: 'o' | 'c', ownerKey: string, signMessage: ControlSign, memberPrivateKey?: string): Promise<void>;
|
|
1066
1183
|
/**
|
|
1067
1184
|
* Delete a strand from the control database using an owner signature.
|
|
1068
1185
|
*
|
|
@@ -1090,7 +1207,7 @@ export declare class ControlDatabase {
|
|
|
1090
1207
|
* A no-op (no throw, no tombstone) when the row does not exist — `false` then, `true`
|
|
1091
1208
|
* when a row was actually removed.
|
|
1092
1209
|
*/
|
|
1093
|
-
deleteStrand(strandId: string, ownerKey: string, signMessage:
|
|
1210
|
+
deleteStrand(strandId: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1094
1211
|
/**
|
|
1095
1212
|
* The locked body of {@link deleteStrand}: the `Strand` delete + tombstone, plus — when
|
|
1096
1213
|
* one exists — the strand's `StrandPartyKey` delete + tombstone, all in ONE
|
|
@@ -1120,7 +1237,7 @@ export declare class ControlDatabase {
|
|
|
1120
1237
|
* @param signMessage - Function that ed25519-signs the raw message bytes (no pre-hash)
|
|
1121
1238
|
* with the owner's private key, returning a base64url signature.
|
|
1122
1239
|
*/
|
|
1123
|
-
insertStrandPartyKey(strandId: string, privateKey: string, ownerKey: string, signMessage:
|
|
1240
|
+
insertStrandPartyKey(strandId: string, privateKey: string, ownerKey: string, signMessage: ControlSign): Promise<void>;
|
|
1124
1241
|
/**
|
|
1125
1242
|
* Owner-signed removal of one `StrandPartyKey` row — the party's own membership
|
|
1126
1243
|
* identity for that strand, stored nowhere else, so this is as destructive as
|
|
@@ -1133,7 +1250,7 @@ export declare class ControlDatabase {
|
|
|
1133
1250
|
* `Revocation` tombstone in the same transaction. A no-op (no throw, no tombstone)
|
|
1134
1251
|
* when the row does not exist — `false` then, `true` when a row was actually removed.
|
|
1135
1252
|
*/
|
|
1136
|
-
deleteStrandPartyKey(strandId: string, ownerKey: string, signMessage:
|
|
1253
|
+
deleteStrandPartyKey(strandId: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1137
1254
|
/**
|
|
1138
1255
|
* Record, party-wide, a strand this party joined from another party (`JoinedStrand` row),
|
|
1139
1256
|
* using an owner signature.
|
|
@@ -1146,14 +1263,14 @@ export declare class ControlDatabase {
|
|
|
1146
1263
|
* Fails with a `JoinedStrand.Id` uniqueness violation when the id is already recorded;
|
|
1147
1264
|
* `isStrandIdConflict(error, 'JoinedStrand')` identifies that case.
|
|
1148
1265
|
*/
|
|
1149
|
-
insertJoinedStrand(row: Pick<StrandRow, 'Id' | 'Type' | 'MemberPrivateKey'>, ownerKey: string, signMessage:
|
|
1266
|
+
insertJoinedStrand(row: Pick<StrandRow, 'Id' | 'Type' | 'MemberPrivateKey'>, ownerKey: string, signMessage: ControlSign): Promise<void>;
|
|
1150
1267
|
/**
|
|
1151
1268
|
* Owner-signed removal of one `JoinedStrand` row. Mirrors {@link deleteStrandPartyKey}:
|
|
1152
1269
|
* `'remove'`-tagged digest over (Id, StampId), `Revocation` tombstone in the same
|
|
1153
1270
|
* transaction. A no-op (no throw, no tombstone) when the row does not exist — `false`
|
|
1154
1271
|
* then, `true` when a row was actually removed.
|
|
1155
1272
|
*/
|
|
1156
|
-
deleteJoinedStrand(strandId: string, ownerKey: string, signMessage:
|
|
1273
|
+
deleteJoinedStrand(strandId: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1157
1274
|
/**
|
|
1158
1275
|
* Record, party-wide, a join this party asked for: a `JoinRequest` row under a fresh stamp,
|
|
1159
1276
|
* owner-signed over every column. Returns the pending join as written.
|
|
@@ -1161,7 +1278,7 @@ export declare class ControlDatabase {
|
|
|
1161
1278
|
* Fails with a `JoinRequest.Id` uniqueness violation when the party already holds a request
|
|
1162
1279
|
* for this invitation; {@link isPendingJoinConflict} identifies that case.
|
|
1163
1280
|
*/
|
|
1164
|
-
insertJoinRequest(request: JoinRequestFields, ownerKey: string, signMessage:
|
|
1281
|
+
insertJoinRequest(request: JoinRequestFields, ownerKey: string, signMessage: ControlSign): Promise<PendingJoin>;
|
|
1165
1282
|
/**
|
|
1166
1283
|
* Record the outcome of the pending join `expected`: its owner-signed `JoinSuccess` /
|
|
1167
1284
|
* `JoinFailure` row, keyed by the request's stamp. A join recorded over a failure deletes the
|
|
@@ -1175,7 +1292,7 @@ export declare class ControlDatabase {
|
|
|
1175
1292
|
* transaction instead (its outcome row collides on the primary key, or its tombstone retires
|
|
1176
1293
|
* the stamp), and the caller re-reads either way.
|
|
1177
1294
|
*/
|
|
1178
|
-
recordJoinOutcome(expected: PendingJoin, outcome: JoinOutcome, ownerKey: string, signMessage:
|
|
1295
|
+
recordJoinOutcome(expected: PendingJoin, outcome: JoinOutcome, ownerKey: string, signMessage: ControlSign): Promise<PendingJoin>;
|
|
1179
1296
|
/**
|
|
1180
1297
|
* Re-write the pending join `next` (request and outcome) under a fresh stamp, retiring the
|
|
1181
1298
|
* live request incarnation `expectedStampId`: how a finished request is asked for again, and
|
|
@@ -1190,14 +1307,14 @@ export declare class ControlDatabase {
|
|
|
1190
1307
|
* that committed but reported failure throws {@link PendingJoinChangedError} whose live join
|
|
1191
1308
|
* carries the returned stamp, so a caller that re-reads finds its write.
|
|
1192
1309
|
*/
|
|
1193
|
-
rewritePendingJoin(expectedStampId: string, next: PendingJoinFields, ownerKey: string, signMessage:
|
|
1310
|
+
rewritePendingJoin(expectedStampId: string, next: PendingJoinFields, ownerKey: string, signMessage: ControlSign): Promise<PendingJoin>;
|
|
1194
1311
|
/**
|
|
1195
1312
|
* Owner-signed removal of one pending join, as {@link deleteJoinedStrand} removes its row:
|
|
1196
1313
|
* `'remove'`-tagged digest over (Id, StampId), `Revocation` tombstone in the same transaction,
|
|
1197
1314
|
* and the request's outcome row with it. A no-op (no throw, no tombstone) when there is no
|
|
1198
1315
|
* request — `false` then, `true` when one was actually removed.
|
|
1199
1316
|
*/
|
|
1200
|
-
deletePendingJoin(id: string, ownerKey: string, signMessage:
|
|
1317
|
+
deletePendingJoin(id: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1201
1318
|
/**
|
|
1202
1319
|
* Mint a fresh stamp for `request` and the owner signature `JoinRequest.AuthorizedInsert`
|
|
1203
1320
|
* verifies, returning the row as it will be written and its insert.
|
|
@@ -1224,7 +1341,7 @@ export declare class ControlDatabase {
|
|
|
1224
1341
|
* @param signMessage - Function that ed25519-signs the raw message bytes (no pre-hash)
|
|
1225
1342
|
* with the owner's private key, returning a base64url signature
|
|
1226
1343
|
*/
|
|
1227
|
-
insertValidationKey(key: string, ownerKey: string, signMessage:
|
|
1344
|
+
insertValidationKey(key: string, ownerKey: string, signMessage: ControlSign): Promise<void>;
|
|
1228
1345
|
/**
|
|
1229
1346
|
* Delete a validation key from the control database using an owner signature.
|
|
1230
1347
|
*
|
|
@@ -1241,7 +1358,7 @@ export declare class ControlDatabase {
|
|
|
1241
1358
|
* A no-op (no throw, no tombstone) when the row does not exist — `false` then, `true`
|
|
1242
1359
|
* when a row was actually removed.
|
|
1243
1360
|
*/
|
|
1244
|
-
deleteValidationKey(key: string, ownerKey: string, signMessage:
|
|
1361
|
+
deleteValidationKey(key: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1245
1362
|
/**
|
|
1246
1363
|
* Owner-vouched INSERT of one `CadrePeer` membership row, notifying the membership
|
|
1247
1364
|
* listener once it has committed.
|
|
@@ -1286,7 +1403,7 @@ export declare class ControlDatabase {
|
|
|
1286
1403
|
updatedAt: number;
|
|
1287
1404
|
sig: string | null;
|
|
1288
1405
|
profile: NodeProfile | null;
|
|
1289
|
-
}, ownerKey: string, signMessage:
|
|
1406
|
+
}, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1290
1407
|
/**
|
|
1291
1408
|
* Owner "re-touch" of an existing `CadrePeer` membership row: bump `UpdatedAt` and
|
|
1292
1409
|
* rewrite `VouchOwner`/`VouchSig` under the owner branch of `CadrePeer.AuthorizedUpdate`
|
|
@@ -1327,13 +1444,13 @@ export declare class ControlDatabase {
|
|
|
1327
1444
|
*
|
|
1328
1445
|
* NOTE: this rebinds VouchOwner to the CALLING owner's key, and the authorized-membership
|
|
1329
1446
|
* predicate (`CadreNode.listAuthorizedMembers`) now judges rows by that column against each
|
|
1330
|
-
* reader's node-local anchor.
|
|
1331
|
-
*
|
|
1332
|
-
*
|
|
1333
|
-
*
|
|
1334
|
-
* anchor the original owner but not this one would
|
|
1335
|
-
* must re-vouch deliberately (or preserve the existing
|
|
1336
|
-
* inherit this rebinding.
|
|
1447
|
+
* reader's node-local anchor. The two callers rebind on purpose: the write-while-alone drain
|
|
1448
|
+
* re-touches solely rows this node itself authored (`pendingPeerWrites`), so the voucher is
|
|
1449
|
+
* rewritten to the key that already signed it, and `CadreNode.removeOwner` moves rows off a
|
|
1450
|
+
* key it is about to remove. A new caller that re-touches a row a DIFFERENT owner vouched
|
|
1451
|
+
* flips the voucher silently: readers that anchor the original owner but not this one would
|
|
1452
|
+
* drop a legitimate member. It must re-vouch deliberately (or preserve the existing
|
|
1453
|
+
* VouchOwner/VouchSig) rather than inherit this rebinding.
|
|
1337
1454
|
*
|
|
1338
1455
|
* @param peerId - the membership row to re-touch.
|
|
1339
1456
|
* @param updatedAt - the strictly-increasing freshness stamp to write.
|
|
@@ -1341,7 +1458,7 @@ export declare class ControlDatabase {
|
|
|
1341
1458
|
* @param signMessage - ed25519-signs the raw message bytes with that owner's private key.
|
|
1342
1459
|
* @returns `true` when the row was re-touched, `false` when no row exists (no notify).
|
|
1343
1460
|
*/
|
|
1344
|
-
reauthorizeCadrePeer(peerId: string, updatedAt: number, ownerKey: string, signMessage:
|
|
1461
|
+
reauthorizeCadrePeer(peerId: string, updatedAt: number, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1345
1462
|
/**
|
|
1346
1463
|
* Owner-signed removal of one `CadrePeer` membership row, notifying the membership
|
|
1347
1464
|
* listener once it has committed.
|
|
@@ -1362,7 +1479,7 @@ export declare class ControlDatabase {
|
|
|
1362
1479
|
* A no-op (no throw, no tombstone) when the row does not exist — `false` then, `true`
|
|
1363
1480
|
* when a row was actually removed.
|
|
1364
1481
|
*/
|
|
1365
|
-
deleteCadrePeer(peerId: string, ownerKey: string, signMessage:
|
|
1482
|
+
deleteCadrePeer(peerId: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1366
1483
|
/**
|
|
1367
1484
|
* Owner-signed removal of one peer's `DeviceToken` row (logout / token invalidation).
|
|
1368
1485
|
*
|
|
@@ -1376,7 +1493,7 @@ export declare class ControlDatabase {
|
|
|
1376
1493
|
* A no-op (no throw, no tombstone) when the row does not exist — `false` then, `true`
|
|
1377
1494
|
* when a row was actually removed.
|
|
1378
1495
|
*/
|
|
1379
|
-
deleteDeviceToken(peerId: string, ownerKey: string, signMessage:
|
|
1496
|
+
deleteDeviceToken(peerId: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
1380
1497
|
/**
|
|
1381
1498
|
* Owner-signed delete of one guarded row plus the `Revocation` tombstone retiring its
|
|
1382
1499
|
* stamp, in ONE transaction. The single body behind EVERY guarded delete —
|
|
@@ -1421,8 +1538,8 @@ export declare class ControlDatabase {
|
|
|
1421
1538
|
private execGuardedRemoval;
|
|
1422
1539
|
/**
|
|
1423
1540
|
* File the owner-signed `Revocation` tombstone retiring one row incarnation — the second
|
|
1424
|
-
* statement of every guarded removal ({@link execGuardedRemoval}) and the whole of
|
|
1425
|
-
*
|
|
1541
|
+
* statement of every guarded removal ({@link execGuardedRemoval}) and the whole of an
|
|
1542
|
+
* issued row's withdrawal ({@link withdrawIssuedRow}), whose row stays — or the ledger
|
|
1426
1543
|
* marker ({@link openRevocationLedger}), which retires nothing. The caller supplies the
|
|
1427
1544
|
* lock and, where one is needed, the transaction.
|
|
1428
1545
|
*
|
|
@@ -1575,7 +1692,7 @@ export declare class ControlDatabase {
|
|
|
1575
1692
|
*
|
|
1576
1693
|
* Returns how many UPDATE statements ran (`rows.length` on success).
|
|
1577
1694
|
*/
|
|
1578
|
-
reissueRevocations(rows: readonly RevocationRow[], reissuedAt: number, ownerKey: string, signMessage:
|
|
1695
|
+
reissueRevocations(rows: readonly RevocationRow[], reissuedAt: number, ownerKey: string, signMessage: ControlSign): Promise<number>;
|
|
1579
1696
|
/**
|
|
1580
1697
|
* Owner-signed filing of the singleton `Revocation` ledger marker
|
|
1581
1698
|
* ({@link REVOCATION_LEDGER_MARKER}), the one row in that table that retires nothing.
|
|
@@ -1607,7 +1724,7 @@ export declare class ControlDatabase {
|
|
|
1607
1724
|
* @returns `'opened'` when this call filed the marker, `'already-open'` when it was
|
|
1608
1725
|
* already there.
|
|
1609
1726
|
*/
|
|
1610
|
-
openRevocationLedger(ownerKey: string, signMessage:
|
|
1727
|
+
openRevocationLedger(ownerKey: string, signMessage: ControlSign): Promise<RevocationLedgerOpenResult>;
|
|
1611
1728
|
/**
|
|
1612
1729
|
* Whether the ledger marker is present locally: a scan of the `'Revocation'` rows with the
|
|
1613
1730
|
* stamp compared in TypeScript (see {@link openRevocationLedger} for why not a seek).
|
|
@@ -1672,8 +1789,9 @@ export declare class ControlDatabase {
|
|
|
1672
1789
|
* Covers every write that can change who is a member or an owner: EVERY `CadrePeer`
|
|
1673
1790
|
* writer, with one documented exception ({@link updateSelfPeerRecord}, which cannot
|
|
1674
1791
|
* change the snapshot), the `OwnerKey` writers ({@link insertOwnerKeyVouched},
|
|
1675
|
-
* {@link deleteOwnerKey})
|
|
1676
|
-
*
|
|
1792
|
+
* {@link deleteOwnerKey}), the invitation redemption ({@link redeemCadreInvite}), which
|
|
1793
|
+
* seats both, and the owner-grant redemption ({@link redeemOwnerGrant}). That is what
|
|
1794
|
+
* makes the party-membership snapshot refresh automatic
|
|
1677
1795
|
* rather than a caller obligation. The name predates the `OwnerKey` writers. A
|
|
1678
1796
|
* writer necessarily holds the target node's ControlDatabase (the `SeedBootstrapService`'s
|
|
1679
1797
|
* event callbacks are NOT a viable seam: the temp service `CadreNode.applySeed`
|
|
@@ -1760,7 +1878,7 @@ export declare class ControlDatabase {
|
|
|
1760
1878
|
* backoff never stalls the other local writers queued behind it. Safe because every
|
|
1761
1879
|
* locked body is atomic and re-runnable (the contract on {@link withWriteLock}).
|
|
1762
1880
|
*
|
|
1763
|
-
* Also carries {@link
|
|
1881
|
+
* Also carries {@link applyControlSchema}'s distributed DDL, the one caller that runs BEFORE
|
|
1764
1882
|
* `initialized` is set — so it reaches this method directly rather than through
|
|
1765
1883
|
* {@link execWrite}, whose {@link ensureInitialized} would reject it. The write queue is
|
|
1766
1884
|
* necessarily empty at that point, so the lock is uncontended there; it is taken anyway
|
|
@@ -1863,7 +1981,7 @@ export declare class ControlDatabase {
|
|
|
1863
1981
|
* @param options - Optional `expiresAtMs` (epoch ms), `totalUses`, `validationUrl`,
|
|
1864
1982
|
* `strandId` (bind to a pre-existing host strand for provision-then-record)
|
|
1865
1983
|
*/
|
|
1866
|
-
insertFormationInvite(token: string, sAppId: string, ownerKey: string, signMessage:
|
|
1984
|
+
insertFormationInvite(token: string, sAppId: string, ownerKey: string, signMessage: ControlSign, options?: {
|
|
1867
1985
|
expiresAtMs?: number;
|
|
1868
1986
|
totalUses?: number;
|
|
1869
1987
|
validationUrl?: string;
|
|
@@ -2091,31 +2209,53 @@ export declare class ControlDatabase {
|
|
|
2091
2209
|
* private half to the invitee out of band. It is never stored here (`Strand.Invite` has the
|
|
2092
2210
|
* same shape).
|
|
2093
2211
|
*
|
|
2094
|
-
* @param invite.key - the invitation public key (base64url ed25519), the row's primary key
|
|
2095
|
-
* @param invite.peerId - the device the invitation is for, or null for whoever redeems first
|
|
2096
|
-
* @param invite.grantsOwner - whether redemption also seats an `OwnerKey` row for the device
|
|
2097
|
-
* @param invite.expiresAtMs - epoch ms after which it cannot be redeemed; omit for never
|
|
2098
|
-
* @param invite.totalUses - how many redemptions it allows; omit for unlimited
|
|
2099
2212
|
* @returns the row as stored, `expiresAt` in its canonical form
|
|
2100
2213
|
*/
|
|
2101
|
-
insertCadreInvite(invite:
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2214
|
+
insertCadreInvite(invite: CadreInviteFields, ownerKey: string, signMessage: ControlSign): Promise<CadreInviteRow>;
|
|
2215
|
+
/**
|
|
2216
|
+
* Build and owner-sign a `CadreInvite` row WITHOUT inserting it: the signing half of
|
|
2217
|
+
* {@link insertCadreInvite}, which `CadreNode.joinWithOwnerGrant` uses alone for the
|
|
2218
|
+
* invitation a node issues to itself, since a store that holds no `OwnerKey` row for the
|
|
2219
|
+
* issuer would refuse the local insert and the row is seated at a member from the bundle
|
|
2220
|
+
* instead. `ExpiresAt` is canonicalized by this database's engine, which is a format, not a
|
|
2221
|
+
* store, so the row reads the same wherever it is seated.
|
|
2222
|
+
*/
|
|
2223
|
+
signCadreInvite(invite: CadreInviteFields, ownerKey: string, signMessage: ControlSign): Promise<CadreInviteRow>;
|
|
2108
2224
|
/**
|
|
2109
2225
|
* Seat an owner-signed `CadreInvite` row received inside an invitation bundle, under its own
|
|
2110
2226
|
* stored `IssuerKey`/`IssuerSig` as the write context — insert-if-absent, so a member that
|
|
2111
|
-
* already holds the row (by replication, or from an earlier redemption attempt) is a no-op
|
|
2227
|
+
* already holds the row (by replication, or from an earlier redemption attempt) is a no-op,
|
|
2228
|
+
* and so is one whose insert the cohort refuses because a member there holds it.
|
|
2112
2229
|
*
|
|
2113
2230
|
* @returns `true` when this call inserted the row, `false` when it was already present.
|
|
2114
2231
|
* @throws {CadreInviteIssuerUnknownError} when `CadreInvite.AuthorizedInsert` refuses it: the
|
|
2115
2232
|
* issuer is not an owner on this node (not replicated yet, or removed), or the signature does
|
|
2116
|
-
* not verify.
|
|
2233
|
+
* not verify. {@link IssuedRowNotHereYetError} when the cohort holds the row and this node
|
|
2234
|
+
* cannot read it yet. Every other refusal (a withdrawn stamp, `NotRevoked`) propagates as is.
|
|
2117
2235
|
*/
|
|
2118
2236
|
seatCadreInvite(row: CadreInviteRow): Promise<boolean>;
|
|
2237
|
+
/**
|
|
2238
|
+
* Insert-if-absent of an issued row from a bundle's copy, under the write lock: the body of
|
|
2239
|
+
* {@link seatCadreInvite} and {@link seatOwnerGrant}. `exec` runs the bare insert under the
|
|
2240
|
+
* row's own stored issuer pair.
|
|
2241
|
+
*
|
|
2242
|
+
* @returns `true` when this call inserted the row, `false` when it was already present —
|
|
2243
|
+
* here, or at the cohort member that refused the insert for holding it
|
|
2244
|
+
* ({@link adoptCohortHeldRow}).
|
|
2245
|
+
* @throws `refuse(cause)` when the table's `AuthorizedInsert` refuses the row;
|
|
2246
|
+
* {@link IssuedRowNotHereYetError} when the cohort holds the row and this node still cannot
|
|
2247
|
+
* read it; every other refusal (a withdrawn stamp, `NotRevoked`) propagates as is.
|
|
2248
|
+
*/
|
|
2249
|
+
private seatIssuedRow;
|
|
2250
|
+
/**
|
|
2251
|
+
* The seat's insert was refused because a cohort member already holds a row under `key`, which
|
|
2252
|
+
* this node's guard read missed because its store lags the cohort. A bundle's copy is verbatim
|
|
2253
|
+
* and every column is under the issuer's signature, so the held row is this one: it counts as
|
|
2254
|
+
* seated once a refreshing read (unlocked, so retried as any read) finds it here.
|
|
2255
|
+
*
|
|
2256
|
+
* @throws {IssuedRowNotHereYetError} when the read still does not find it.
|
|
2257
|
+
*/
|
|
2258
|
+
private adoptCohortHeldRow;
|
|
2119
2259
|
/** Bare `CadreInvite` insert shared by {@link insertCadreInvite} and {@link seatCadreInvite}; the caller holds the lock. */
|
|
2120
2260
|
private execCadreInviteInsert;
|
|
2121
2261
|
/**
|
|
@@ -2149,6 +2289,8 @@ export declare class ControlDatabase {
|
|
|
2149
2289
|
* `retry: false` is passed by {@link assertCadreInviteSeatRemains}, inside the locked body.
|
|
2150
2290
|
*/
|
|
2151
2291
|
countCadreInviteUsage(key: string, retry?: boolean): Promise<number>;
|
|
2292
|
+
/** The one-row count read behind {@link countCadreInviteUsage} and {@link countOwnerGrantUsage}. */
|
|
2293
|
+
private countUsageRows;
|
|
2152
2294
|
/**
|
|
2153
2295
|
* Is any cadre invitation still redeemable — not withdrawn, unexpired, with uses below its
|
|
2154
2296
|
* `TotalUses`, and issued by a current owner? The conditions `CadreInviteUsage.Authorized`
|
|
@@ -2172,6 +2314,11 @@ export declare class ControlDatabase {
|
|
|
2172
2314
|
* what enforce every condition.
|
|
2173
2315
|
*/
|
|
2174
2316
|
isCadreInviteLive(invite: CadreInviteRow, nowMs?: number): Promise<boolean>;
|
|
2317
|
+
/**
|
|
2318
|
+
* The body of {@link isCadreInviteLive} and {@link isOwnerGrantLive}: is `row` (the one held,
|
|
2319
|
+
* or a verified bundle copy; null for neither) still open and under its use cap?
|
|
2320
|
+
*/
|
|
2321
|
+
private issuedRowLive;
|
|
2175
2322
|
/**
|
|
2176
2323
|
* Every `CadreInvite` row this node holds with its standing: how many redemptions are
|
|
2177
2324
|
* recorded, whether it is withdrawn, and whether it is still redeemable — the same
|
|
@@ -2180,6 +2327,11 @@ export declare class ControlDatabase {
|
|
|
2180
2327
|
* occasional read, not a gate's.
|
|
2181
2328
|
*/
|
|
2182
2329
|
listCadreInviteStatuses(nowMs?: number): Promise<CadreInviteStatus[]>;
|
|
2330
|
+
/**
|
|
2331
|
+
* The body of {@link listCadreInviteStatuses} and {@link listOwnerGrantStatuses}: each of
|
|
2332
|
+
* `rows` with its withdrawal, its recorded uses and whether it is still redeemable.
|
|
2333
|
+
*/
|
|
2334
|
+
private issuedRowStandings;
|
|
2183
2335
|
/**
|
|
2184
2336
|
* Owner-signed WITHDRAWAL of a cadre invitation: file the `Revocation` tombstone retiring its
|
|
2185
2337
|
* stamp, with the row kept. The row stays because every device the invitation admitted
|
|
@@ -2197,24 +2349,29 @@ export declare class ControlDatabase {
|
|
|
2197
2349
|
* Fires the guarded-delete listener like a removal does, so a withdrawal committed while the
|
|
2198
2350
|
* node was alone is queued for re-issue on cohort growth ({@link CadreNode.noteGuardedDelete}).
|
|
2199
2351
|
*/
|
|
2200
|
-
withdrawCadreInvite(key: string, ownerKey: string, signMessage:
|
|
2352
|
+
withdrawCadreInvite(key: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
2201
2353
|
/**
|
|
2202
|
-
* The
|
|
2203
|
-
*
|
|
2204
|
-
*
|
|
2354
|
+
* The body of {@link withdrawCadreInvite} and {@link withdrawOwnerGrant}: under the write
|
|
2355
|
+
* lock, file the owner-signed tombstone retiring the row's stamp with the row kept, and fire
|
|
2356
|
+
* the guarded-delete listener for it (so a withdrawal made while alone is re-issued).
|
|
2357
|
+
*/
|
|
2358
|
+
private withdrawIssuedRow;
|
|
2359
|
+
/**
|
|
2360
|
+
* The withdrawal tombstones for the `table` rows among `keys` that are held here and not
|
|
2361
|
+
* yet withdrawn, signed and ready for {@link execTombstone}: the body
|
|
2362
|
+
* {@link withdrawIssuedRow} files one of and {@link removeMachine} files several of in one
|
|
2205
2363
|
* transaction. Inside the locked body (every read `retry: false`); an absent or already
|
|
2206
2364
|
* withdrawn key is logged and left out.
|
|
2207
2365
|
*/
|
|
2208
|
-
private
|
|
2366
|
+
private signWithdrawals;
|
|
2209
2367
|
/**
|
|
2210
|
-
* Redeem a seated `CadreInvite` on the owner's behalf: admit `peerId` as a cadre member
|
|
2211
|
-
*
|
|
2212
|
-
* transaction writes the `CadrePeer` row (consent branch of `AuthorizedInsert`)
|
|
2213
|
-
* `
|
|
2214
|
-
*
|
|
2215
|
-
*
|
|
2216
|
-
*
|
|
2217
|
-
* ({@link seatCadreInvite}): every consent branch reads `committed.CadreInvite`.
|
|
2368
|
+
* Redeem a seated `CadreInvite` on the owner's behalf: admit `peerId` as a cadre member —
|
|
2369
|
+
* with NO owner signature, and as a machine only (ownership is {@link redeemOwnerGrant}'s).
|
|
2370
|
+
* One transaction writes the `CadrePeer` row (consent branch of `AuthorizedInsert`) and then
|
|
2371
|
+
* the `CadreInviteUsage` row that authorizes it; the usage row's own check (`PeerExists`)
|
|
2372
|
+
* reads the peer row back, so the two are mutually bound to this exact redemption and
|
|
2373
|
+
* neither can be held in reserve. The invitation must already be committed
|
|
2374
|
+
* ({@link seatCadreInvite}): the consent branch reads `committed.CadreInvite`.
|
|
2218
2375
|
*
|
|
2219
2376
|
* The two signatures are the holder's and the device's, minted on the device over
|
|
2220
2377
|
* {@link cadreInviteRedeemMessage} / {@link cadreInviteConsentMessage}; this machine only
|
|
@@ -2223,18 +2380,17 @@ export declare class ControlDatabase {
|
|
|
2223
2380
|
* (`verifyInvitationAdmission`) — a mismatched pair is written and then never trusted.
|
|
2224
2381
|
*
|
|
2225
2382
|
* Idempotent for a retry after a dropped reply: when the device's row is already present
|
|
2226
|
-
*
|
|
2227
|
-
*
|
|
2228
|
-
*
|
|
2229
|
-
* The seat check ({@link assertCadreInviteSeatRemains}) runs after that, so the retry of a
|
|
2230
|
-
* single-use redemption is answered `alreadyMember` rather than refused as exhausted.
|
|
2383
|
+
* nothing is written and no seat is spent — `alreadyMember: true`. The seat check
|
|
2384
|
+
* ({@link assertCadreInviteSeatRemains}) runs after that, so the retry of a single-use
|
|
2385
|
+
* redemption is answered `alreadyMember` rather than refused as exhausted.
|
|
2231
2386
|
*
|
|
2232
2387
|
* Through {@link mutateCadrePeer}, so the membership hub is notified; inside the locked body
|
|
2233
2388
|
* every read passes `retry: false` and every stamp is minted per attempt, so a
|
|
2234
2389
|
* {@link lockedWithRetry} re-run starts clean.
|
|
2235
2390
|
*
|
|
2236
2391
|
* @throws {InvitationExhaustedError} when the invitation's seats are all spent (by name,
|
|
2237
|
-
* ahead of the schema's generic `Authorized` refusal — see {@link assertSeatRemains})
|
|
2392
|
+
* ahead of the schema's generic `Authorized` refusal — see {@link assertSeatRemains});
|
|
2393
|
+
* {@link IssuedRowNotHereYetError} when no `CadreInvite` row is readable here.
|
|
2238
2394
|
*/
|
|
2239
2395
|
redeemCadreInvite(params: {
|
|
2240
2396
|
inviteKey: string;
|
|
@@ -2251,13 +2407,6 @@ export declare class ControlDatabase {
|
|
|
2251
2407
|
peerSig: string;
|
|
2252
2408
|
nowMs?: number;
|
|
2253
2409
|
}): Promise<CadreInviteRedemptionResult>;
|
|
2254
|
-
/**
|
|
2255
|
-
* The rows a redemption would seat that are already present: the device's `CadrePeer`
|
|
2256
|
-
* stamp, and — only when the invitation grants ownership — its `OwnerKey` stamp. Raw stamp
|
|
2257
|
-
* reads ({@link queryStampId}), so a physically present row counts; inside the locked
|
|
2258
|
-
* body, so never retried.
|
|
2259
|
-
*/
|
|
2260
|
-
private readSeatedAdmission;
|
|
2261
2410
|
/**
|
|
2262
2411
|
* Refuse a redemption that would consume a seat the cadre invitation does not have, by
|
|
2263
2412
|
* name ({@link InvitationExhaustedError}) — the cadre-invitation twin of
|
|
@@ -2266,6 +2415,137 @@ export declare class ControlDatabase {
|
|
|
2266
2415
|
* committed count inside the locked body, so on a same-node race the loser is refused here.
|
|
2267
2416
|
*/
|
|
2268
2417
|
private assertCadreInviteSeatRemains;
|
|
2418
|
+
/** The body of {@link assertCadreInviteSeatRemains} and {@link assertOwnerGrantSeatRemains}; `countUsage` reads inside the locked body. */
|
|
2419
|
+
private assertIssuedSeatRemains;
|
|
2420
|
+
/**
|
|
2421
|
+
* Mint and insert an owner-signed `OwnerGrant`: a grant of ownership to whoever holds the
|
|
2422
|
+
* grant's private half, redeemable through any member machine ({@link redeemOwnerGrant})
|
|
2423
|
+
* to seat an owner key the holder generates or already holds. No machine is admitted and no
|
|
2424
|
+
* peer id is named. The grant twin of {@link insertCadreInvite}: the owner signs the whole
|
|
2425
|
+
* row ({@link ownerGrantAddMessage}), `ExpiresAt` through {@link canonicalDatetime}, and
|
|
2426
|
+
* the returned row is what the grant bundle carries, re-inserted verbatim by
|
|
2427
|
+
* {@link seatOwnerGrant} on a member that has not received it by replication.
|
|
2428
|
+
*
|
|
2429
|
+
* @param grant.key - the grant's ed25519 PUBLIC key (base64url), the row's primary key; the
|
|
2430
|
+
* caller minted the pair and hands the private half to the holder out of band
|
|
2431
|
+
* @param grant.label - the label every key this grant seats will carry
|
|
2432
|
+
* ({@link ownerKeyLabel}'s rule: trimmed, at most 64 characters, blank meaning none); the
|
|
2433
|
+
* issuer's choice, which the holder cannot change
|
|
2434
|
+
* @param grant.expiresAtMs - epoch ms after which it cannot be redeemed; omit or null for
|
|
2435
|
+
* never. The short default lifetime is the node API's to apply, not this writer's.
|
|
2436
|
+
* @param grant.totalUses - how many redemptions it allows; omit for unlimited
|
|
2437
|
+
* @returns the row as stored, `expiresAt` in its canonical form
|
|
2438
|
+
*/
|
|
2439
|
+
insertOwnerGrant(grant: {
|
|
2440
|
+
key: string;
|
|
2441
|
+
label?: string | null;
|
|
2442
|
+
expiresAtMs?: number | null;
|
|
2443
|
+
totalUses?: number | null;
|
|
2444
|
+
}, ownerKey: string, signMessage: ControlSign): Promise<OwnerGrantRow>;
|
|
2445
|
+
/**
|
|
2446
|
+
* Seat an owner-signed `OwnerGrant` row received inside a grant bundle, under its own stored
|
|
2447
|
+
* `IssuerKey`/`IssuerSig` as the write context — insert-if-absent, as {@link seatCadreInvite}.
|
|
2448
|
+
*
|
|
2449
|
+
* @returns `true` when this call inserted the row, `false` when it was already present.
|
|
2450
|
+
* @throws {OwnerGrantIssuerUnknownError} when `OwnerGrant.AuthorizedInsert` refuses it: the
|
|
2451
|
+
* issuer is not an owner on this node (not replicated yet, or removed), or the signature does
|
|
2452
|
+
* not verify. {@link IssuedRowNotHereYetError} when the cohort holds the row and this node
|
|
2453
|
+
* cannot read it yet. Every other refusal (a withdrawn stamp, `NotRevoked`) propagates as is.
|
|
2454
|
+
*/
|
|
2455
|
+
seatOwnerGrant(row: OwnerGrantRow): Promise<boolean>;
|
|
2456
|
+
/** Bare `OwnerGrant` insert shared by {@link insertOwnerGrant} and {@link seatOwnerGrant}; the caller holds the lock. */
|
|
2457
|
+
private execOwnerGrantInsert;
|
|
2458
|
+
/**
|
|
2459
|
+
* Read one `OwnerGrant` row by key, or null when absent. Withdrawn rows are returned too,
|
|
2460
|
+
* as {@link queryCadreInvite} returns them: the row is the holder's proof and never goes
|
|
2461
|
+
* away. `retry: false` is passed by {@link redeemOwnerGrant}, inside its locked body.
|
|
2462
|
+
*/
|
|
2463
|
+
queryOwnerGrant(key: string, retry?: boolean): Promise<OwnerGrantRow | null>;
|
|
2464
|
+
/**
|
|
2465
|
+
* Every `OwnerGrant` row this node holds, withdrawn and expired ones included. Read by the
|
|
2466
|
+
* anchor sync when at least one `OwnerKey` row is consent-seated (`CadreNode.loadOwnerGrantChain`);
|
|
2467
|
+
* `retry: false` is forwarded from the membership-gate refresh for {@link queryCadrePeers}' reason.
|
|
2468
|
+
*/
|
|
2469
|
+
queryOwnerGrants(retry?: boolean): Promise<OwnerGrantRow[]>;
|
|
2470
|
+
/** Every `OwnerGrantUsage` row this node holds — the redemption records a grant-seated `OwnerKey` row is verified against. Same `retry` contract as {@link queryOwnerGrants}. */
|
|
2471
|
+
queryOwnerGrantUsages(retry?: boolean): Promise<OwnerGrantUsageRow[]>;
|
|
2472
|
+
/**
|
|
2473
|
+
* How many times an owner grant has been redeemed (its `OwnerGrantUsage` rows), served
|
|
2474
|
+
* through the `OwnerGrantUsageByGrant` index. A permissive PRE-check like
|
|
2475
|
+
* {@link countCadreInviteUsage}: the cap is `OwnerGrantUsage.Authorized`'s own committed count.
|
|
2476
|
+
*/
|
|
2477
|
+
countOwnerGrantUsage(key: string, retry?: boolean): Promise<number>;
|
|
2478
|
+
/**
|
|
2479
|
+
* Is this one owner grant still redeemable here — not withdrawn, unexpired, uses left, issuer
|
|
2480
|
+
* still an owner — judged as {@link isCadreInviteLive} judges an invitation: by the row this
|
|
2481
|
+
* node holds under `grant.key`, else by `grant` itself (a bundle's copy, not seated yet)
|
|
2482
|
+
* when its issuer signature verifies. A permissive pre-check; {@link seatOwnerGrant} and the
|
|
2483
|
+
* redemption's own write enforce every condition.
|
|
2484
|
+
*/
|
|
2485
|
+
isOwnerGrantLive(grant: OwnerGrantRow, nowMs?: number): Promise<boolean>;
|
|
2486
|
+
/**
|
|
2487
|
+
* Every `OwnerGrant` row this node holds with its standing — recorded redemptions, withdrawal,
|
|
2488
|
+
* and whether it is still redeemable — for an owner's listing, as {@link listCadreInviteStatuses}.
|
|
2489
|
+
*/
|
|
2490
|
+
listOwnerGrantStatuses(nowMs?: number): Promise<OwnerGrantStatus[]>;
|
|
2491
|
+
/**
|
|
2492
|
+
* Owner-signed WITHDRAWAL of an owner grant: file the `Revocation` tombstone retiring its
|
|
2493
|
+
* stamp, with the row kept, exactly as {@link withdrawCadreInvite} withdraws an invitation
|
|
2494
|
+
* and for the same reason (every key the grant seated proves itself through the row;
|
|
2495
|
+
* `OwnerGrant.NoDelete`, `Revocation.RowIsGone`). Afterwards `OwnerGrantUsage.Authorized`
|
|
2496
|
+
* refuses every further redemption and `OwnerGrant.NotRevoked` refuses re-seating the
|
|
2497
|
+
* holder's copy on any node holding the tombstone. Keys already seated stay owners;
|
|
2498
|
+
* removing one is {@link deleteOwnerKey}.
|
|
2499
|
+
*
|
|
2500
|
+
* Idempotent: `false` (nothing written) when the row is absent here or already withdrawn,
|
|
2501
|
+
* `true` when this call filed the tombstone. Fires the guarded-delete listener, so a
|
|
2502
|
+
* withdrawal committed while alone is re-issued on cohort growth.
|
|
2503
|
+
*/
|
|
2504
|
+
withdrawOwnerGrant(key: string, ownerKey: string, signMessage: ControlSign): Promise<boolean>;
|
|
2505
|
+
/**
|
|
2506
|
+
* Redeem a seated `OwnerGrant` on the issuer's behalf: seat `seatedKey` as an owner with NO
|
|
2507
|
+
* owner signature. One transaction writes the `OwnerKey` row (the grant branch of
|
|
2508
|
+
* `Authorized`: `VouchOwner` the issuer, `VouchSig` null, `VouchUsage` the usage stamp,
|
|
2509
|
+
* `Label` the grant's) and then the `OwnerGrantUsage` row that authorizes it; the usage
|
|
2510
|
+
* row's `SeatedExists` reads the owner row back, so the two are bound to this exact
|
|
2511
|
+
* redemption and neither can be held in reserve. The grant must already be committed
|
|
2512
|
+
* ({@link seatOwnerGrant}): both consent reads are of `committed.OwnerGrant`. No machine is
|
|
2513
|
+
* admitted and no peer id is named.
|
|
2514
|
+
*
|
|
2515
|
+
* The two signatures are minted where the private keys are (the grant's and the seated
|
|
2516
|
+
* key's), over {@link ownerGrantRedeemMessage} / {@link ownerGrantConsentMessage}; this
|
|
2517
|
+
* machine only verifies them, by writing the row.
|
|
2518
|
+
*
|
|
2519
|
+
* Idempotent, and a no-op for a key that is already an owner: when `seatedKey` has a live
|
|
2520
|
+
* `OwnerKey` row here nothing is written and no use is spent — `alreadyOwner: true` with
|
|
2521
|
+
* that row's stamp. The seat check ({@link assertOwnerGrantSeatRemains}) runs after that, so
|
|
2522
|
+
* the retry of a single-use redemption is answered `alreadyOwner` rather than refused as
|
|
2523
|
+
* exhausted. A key removed earlier is seated afresh under a new stamp (`NotRevoked` passes),
|
|
2524
|
+
* except on a machine that still holds its retired row, which refuses by name
|
|
2525
|
+
* ({@link liveOwnerKeyStamp}).
|
|
2526
|
+
*
|
|
2527
|
+
* Through {@link mutateCadrePeer}, so the membership hub is notified and the anchors follow;
|
|
2528
|
+
* inside the locked body every read passes `retry: false` and the stamp is minted per
|
|
2529
|
+
* attempt, so a {@link lockedWithRetry} re-run starts clean.
|
|
2530
|
+
*
|
|
2531
|
+
* @throws {InvitationExhaustedError} when the grant's uses are all spent (by name, ahead of
|
|
2532
|
+
* the schema's generic `Authorized` refusal); its `token` is the grant key.
|
|
2533
|
+
* {@link IssuedRowNotHereYetError} when no `OwnerGrant` row is readable here.
|
|
2534
|
+
*/
|
|
2535
|
+
redeemOwnerGrant(params: {
|
|
2536
|
+
grantKey: string;
|
|
2537
|
+
/** The owner key to seat (base64url ed25519): the holder generated it, or already holds it. */
|
|
2538
|
+
seatedKey: string;
|
|
2539
|
+
/** Single-use nonce minted by the holder; both signatures cover it. */
|
|
2540
|
+
usageStampId: string;
|
|
2541
|
+
/** The holder's signature with the grant private key over the `'redeem'` digest. */
|
|
2542
|
+
grantSig: string;
|
|
2543
|
+
/** The seated key's signature over the `'consent'` digest. */
|
|
2544
|
+
ownerSig: string;
|
|
2545
|
+
nowMs?: number;
|
|
2546
|
+
}): Promise<OwnerGrantRedemptionResult>;
|
|
2547
|
+
/** {@link assertCadreInviteSeatRemains} for an owner grant: refuse an exhausted grant by name, ahead of the write. */
|
|
2548
|
+
private assertOwnerGrantSeatRemains;
|
|
2269
2549
|
/**
|
|
2270
2550
|
* Close the database and cleanup resources.
|
|
2271
2551
|
*
|