@serfab/cadre-core 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/bootstrap-peer-store-file.js +1 -1
- package/dist/bootstrap-peer-store.d.ts +1 -1
- package/dist/bootstrap-peer-store.js +4 -4
- package/dist/cadre-invite-protocol.d.ts +81 -142
- package/dist/cadre-invite-protocol.js +168 -458
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-join-request.d.ts +15 -2
- package/dist/cadre-join-request.js +22 -3
- package/dist/cadre-join-request.js.map +1 -1
- package/dist/cadre-node.d.ts +1089 -475
- package/dist/cadre-node.js +2170 -1037
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +36 -14
- package/dist/control-authorization.js +20 -3
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +88 -32
- package/dist/control-cohort.js +91 -33
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +856 -318
- package/dist/control-database.js +1422 -701
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +46 -43
- package/dist/control-formation-recorder.js +77 -78
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +11 -4
- package/dist/control-protocol-guard.js +20 -5
- package/dist/control-protocol-guard.js.map +1 -1
- package/dist/control-schema-version.d.ts +97 -0
- package/dist/control-schema-version.js +225 -0
- package/dist/control-schema-version.js.map +1 -0
- package/dist/control-schema.d.ts +45 -5
- package/dist/control-schema.js +262 -78
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +45 -10
- package/dist/control-write-retry.js +92 -22
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +13 -0
- package/dist/delegate-admission.js +27 -0
- package/dist/delegate-admission.js.map +1 -1
- package/dist/ed25519-key.d.ts +10 -7
- package/dist/ed25519-key.js +23 -20
- package/dist/ed25519-key.js.map +1 -1
- package/dist/edge-node/config.d.ts +122 -0
- package/dist/edge-node/config.js +199 -0
- package/dist/edge-node/config.js.map +1 -0
- package/dist/edge-node/dissolve.d.ts +95 -0
- package/dist/edge-node/dissolve.js +184 -0
- package/dist/edge-node/dissolve.js.map +1 -0
- package/dist/edge-node/edge-join.d.ts +44 -0
- package/dist/edge-node/edge-join.js +227 -0
- package/dist/edge-node/edge-join.js.map +1 -0
- package/dist/edge-node/edge-node.d.ts +295 -0
- package/dist/edge-node/edge-node.js +704 -0
- package/dist/edge-node/edge-node.js.map +1 -0
- package/dist/edge-node/index.d.ts +18 -0
- package/dist/edge-node/index.js +18 -0
- package/dist/edge-node/index.js.map +1 -0
- package/dist/edge-node/join-credential.d.ts +11 -0
- package/dist/edge-node/join-credential.js +11 -0
- package/dist/edge-node/join-credential.js.map +1 -0
- package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
- package/dist/edge-node/lazy-raw-storage.js +134 -0
- package/dist/edge-node/lazy-raw-storage.js.map +1 -0
- package/dist/edge-node/node-local.d.ts +60 -0
- package/dist/edge-node/node-local.js +58 -0
- package/dist/edge-node/node-local.js.map +1 -0
- package/dist/edge-node/options.d.ts +83 -0
- package/dist/edge-node/options.js +110 -0
- package/dist/edge-node/options.js.map +1 -0
- package/dist/edge-node/owner-grant.d.ts +41 -0
- package/dist/edge-node/owner-grant.js +38 -0
- package/dist/edge-node/owner-grant.js.map +1 -0
- package/dist/edge-node/platform.d.ts +140 -0
- package/dist/edge-node/platform.js +12 -0
- package/dist/edge-node/platform.js.map +1 -0
- package/dist/edge-node/strand-move.d.ts +115 -0
- package/dist/edge-node/strand-move.js +223 -0
- package/dist/edge-node/strand-move.js.map +1 -0
- package/dist/edge-node/strands.d.ts +28 -0
- package/dist/edge-node/strands.js +60 -0
- package/dist/edge-node/strands.js.map +1 -0
- package/dist/enrolled-machine-store.d.ts +3 -3
- package/dist/enrolled-machine-store.js +3 -3
- package/dist/formation-token.d.ts +48 -0
- package/dist/formation-token.js +26 -0
- package/dist/formation-token.js.map +1 -0
- package/dist/index.d.ts +27 -17
- package/dist/index.js +39 -17
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +29 -12
- package/dist/joined-strand-store.js +66 -27
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store.d.ts +9 -4
- package/dist/key-store.js +9 -4
- package/dist/key-store.js.map +1 -1
- package/dist/link-budget.d.ts +9 -7
- package/dist/link-budget.js +9 -7
- package/dist/link-budget.js.map +1 -1
- package/dist/machine-removal.d.ts +19 -39
- package/dist/machine-removal.js +24 -98
- package/dist/machine-removal.js.map +1 -1
- package/dist/member-redemption.d.ts +256 -0
- package/dist/member-redemption.js +490 -0
- package/dist/member-redemption.js.map +1 -0
- package/dist/membership-view.d.ts +91 -0
- package/dist/membership-view.js +110 -0
- package/dist/membership-view.js.map +1 -0
- package/dist/owner-anchor-sync.d.ts +4 -4
- package/dist/owner-anchor-sync.js +16 -15
- package/dist/owner-anchor-sync.js.map +1 -1
- package/dist/owner-grant-protocol.d.ts +251 -0
- package/dist/owner-grant-protocol.js +344 -0
- package/dist/owner-grant-protocol.js.map +1 -0
- package/dist/owner-key-export.d.ts +70 -0
- package/dist/owner-key-export.js +279 -0
- package/dist/owner-key-export.js.map +1 -0
- package/dist/owner-key-flows.d.ts +55 -0
- package/dist/owner-key-flows.js +60 -0
- package/dist/owner-key-flows.js.map +1 -0
- package/dist/owner-key-store.d.ts +31 -0
- package/dist/owner-key-store.js +81 -0
- package/dist/owner-key-store.js.map +1 -0
- package/dist/owner-keys.d.ts +78 -0
- package/dist/owner-keys.js +133 -0
- package/dist/owner-keys.js.map +1 -0
- package/dist/owner-signer.d.ts +54 -0
- package/dist/owner-signer.js +51 -0
- package/dist/owner-signer.js.map +1 -0
- package/dist/peer-addr-book.d.ts +10 -1
- package/dist/peer-addr-book.js +4 -3
- package/dist/peer-addr-book.js.map +1 -1
- package/dist/peer-authorization.d.ts +83 -18
- package/dist/peer-authorization.js +123 -53
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +89 -20
- package/dist/peer-join-backfill.js +265 -108
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/pending-join-runner.d.ts +48 -18
- package/dist/pending-join-runner.js +74 -56
- package/dist/pending-join-runner.js.map +1 -1
- package/dist/provider/rent-node.js +1 -1
- package/dist/provider/rent-node.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +162 -133
- package/dist/seed-bootstrap.js +300 -283
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.js +4 -4
- package/dist/strand-addr-protocol.d.ts +4 -1
- package/dist/strand-addr-protocol.js +8 -2
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-cohort.d.ts +3 -0
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +24 -2
- package/dist/strand-database.js +29 -6
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +176 -88
- package/dist/strand-first-sync-gate.js +288 -121
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +9 -9
- package/dist/strand-formation-deadlines.js +9 -9
- package/dist/strand-formation-manager.d.ts +23 -17
- package/dist/strand-formation-manager.js +46 -44
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +5 -8
- package/dist/strand-formation-protocol.js +101 -16
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +27 -18
- package/dist/strand-instance-manager.js +119 -50
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +16 -13
- package/dist/strand-membership-reconciler.js +30 -22
- package/dist/strand-membership-reconciler.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +45 -22
- package/dist/strand-membership-writer.js +226 -131
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-move.d.ts +6 -5
- package/dist/strand-move.js +36 -32
- package/dist/strand-move.js.map +1 -1
- package/dist/strand-peer-redial.js +2 -3
- package/dist/strand-peer-redial.js.map +1 -1
- package/dist/strand-revocation-enforcer.d.ts +134 -60
- package/dist/strand-revocation-enforcer.js +144 -71
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +50 -39
- package/dist/strand-solicitation.js +10 -3
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-watcher.d.ts +80 -13
- package/dist/strand-watcher.js +152 -48
- package/dist/strand-watcher.js.map +1 -1
- package/dist/table-change-watch.d.ts +63 -0
- package/dist/table-change-watch.js +209 -0
- package/dist/table-change-watch.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +4 -3
- package/dist/timeout-scheduler.js +4 -3
- package/dist/timeout-scheduler.js.map +1 -1
- package/dist/types.d.ts +374 -150
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -1
- package/package.json +17 -8
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +214 -214
- package/src/cadre-invite-protocol.ts +740 -1052
- package/src/cadre-join-request.ts +24 -3
- package/src/cadre-node.ts +2402 -1079
- package/src/control-authorization.ts +47 -14
- package/src/control-cohort.ts +204 -128
- package/src/control-database.ts +1749 -820
- package/src/control-formation-recorder.ts +359 -353
- package/src/control-protocol-guard.ts +23 -6
- package/src/control-schema-version.ts +301 -0
- package/src/control-schema.ts +273 -78
- package/src/control-write-retry.ts +641 -567
- package/src/delegate-admission.ts +351 -314
- package/src/ed25519-key.ts +174 -171
- package/src/edge-node/config.ts +277 -0
- package/src/edge-node/dissolve.ts +251 -0
- package/src/edge-node/edge-join.ts +319 -0
- package/src/edge-node/edge-node.ts +1045 -0
- package/src/edge-node/index.ts +59 -0
- package/src/edge-node/join-credential.ts +16 -0
- package/src/edge-node/lazy-raw-storage.ts +178 -0
- package/src/edge-node/node-local.ts +84 -0
- package/src/edge-node/options.ts +179 -0
- package/src/edge-node/owner-grant.ts +63 -0
- package/src/edge-node/platform.ts +151 -0
- package/src/edge-node/strand-move.ts +270 -0
- package/src/edge-node/strands.ts +72 -0
- package/src/enrolled-machine-store.ts +3 -3
- package/src/formation-token.ts +63 -0
- package/src/index.ts +899 -789
- package/src/joined-strand-store.ts +73 -28
- package/src/key-store.ts +10 -4
- package/src/link-budget.ts +9 -7
- package/src/machine-removal.ts +26 -111
- package/src/member-redemption.ts +649 -0
- package/src/membership-view.ts +144 -0
- package/src/owner-anchor-sync.ts +19 -18
- package/src/owner-grant-protocol.ts +547 -0
- package/src/owner-key-export.ts +348 -0
- package/src/owner-key-flows.ts +109 -0
- package/src/owner-key-store.ts +85 -0
- package/src/owner-keys.ts +178 -0
- package/src/owner-signer.ts +94 -0
- package/src/peer-addr-book.ts +4 -3
- package/src/peer-authorization.ts +558 -488
- package/src/peer-join-backfill.ts +971 -761
- package/src/pending-join-runner.ts +76 -53
- package/src/provider/rent-node.ts +1 -1
- package/src/seed-bootstrap.ts +343 -349
- package/src/seed-trust-policy.ts +4 -4
- package/src/strand-addr-protocol.ts +547 -538
- package/src/strand-cohort.ts +4 -0
- package/src/strand-database.ts +352 -319
- package/src/strand-first-sync-gate.ts +334 -133
- package/src/strand-formation-deadlines.ts +227 -227
- package/src/strand-formation-manager.ts +731 -725
- package/src/strand-formation-protocol.ts +122 -24
- package/src/strand-instance-manager.ts +2092 -2026
- package/src/strand-membership-reconciler.ts +33 -25
- package/src/strand-membership-writer.ts +2093 -1974
- package/src/strand-move.ts +50 -38
- package/src/strand-peer-redial.ts +2 -3
- package/src/strand-revocation-enforcer.ts +740 -654
- package/src/strand-solicitation.ts +61 -34
- package/src/strand-watcher.ts +175 -56
- package/src/table-change-watch.ts +285 -0
- package/src/timeout-scheduler.ts +29 -28
- package/src/types.ts +391 -152
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An in-memory copy of one small control table's rows, kept until the table changes, so the
|
|
3
|
+
* membership, owner, peer-record and admission-chain reads that every flow repeats cost nothing
|
|
4
|
+
* after the first.
|
|
5
|
+
* `ControlDatabase` keeps one per cached table and drops it from its table-change subscription
|
|
6
|
+
* (`docs/architecture.md` → "Membership rows view"); this class holds only the caching rules, so
|
|
7
|
+
* they are testable without a database.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* How long installed rows are served before the next read reloads them, change or no change.
|
|
11
|
+
* The reload is the only path for a change that reaches this node with no storage event: a
|
|
12
|
+
* machine outside the block's cohort (a cadre larger than its replication breadth), or one in
|
|
13
|
+
* the cohort that the writer could not reach when it committed (one side of a NAT pair; the
|
|
14
|
+
* cohort auto-connect scenarios). Longer than Optimystic's read-repair window
|
|
15
|
+
* ({@link OPTIMYSTIC_READ_REPAIR_WINDOW_MS}), so the reload's storage read starts the cohort
|
|
16
|
+
* consult that brings such a change in instead of being served from the local copy, and
|
|
17
|
+
* shorter than any reconcile backstop (`DEFAULT_CONTROL_COHORT_RECONCILE_MS`, 2 minutes), so
|
|
18
|
+
* the backstop pass, the only reader on an idle party, reloads rather than serving kept rows.
|
|
19
|
+
* The reload rate on an idle party is therefore the pass rate, and the staleness bound for
|
|
20
|
+
* such a change is the backstop, not this value.
|
|
21
|
+
*
|
|
22
|
+
* A machine that was offline when a sibling committed is covered sooner than either:
|
|
23
|
+
* `CadreNode` drops every view when a sibling becomes reachable (its identify on a new
|
|
24
|
+
* connection; `ControlDatabase.invalidateMembershipViews`), so the pass that starts then reads
|
|
25
|
+
* storage.
|
|
26
|
+
*/
|
|
27
|
+
export const MEMBERSHIP_VIEW_MAX_AGE_MS = 15_000;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Optimystic's lazy read-repair window (`clusterPolicy.readRepairWindowMs`, which cadre does
|
|
31
|
+
* not set, so this is Optimystic's default): a read of a held block checked less than this
|
|
32
|
+
* long ago is served from the local copy without asking the cohort. A read after it is
|
|
33
|
+
* served locally too, but starts a cohort consult whose landing raises a table change.
|
|
34
|
+
*
|
|
35
|
+
* NOTE: a copy, since db-p2p exports no default for it. If cadre ever sets
|
|
36
|
+
* `readRepairWindowMs` in its cluster policy, derive this from that value.
|
|
37
|
+
*/
|
|
38
|
+
const OPTIMYSTIC_READ_REPAIR_WINDOW_MS = 10_000;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* How long after a sibling becomes reachable (its identify on a new connection) `CadreNode`
|
|
42
|
+
* drops the views a second time: one read-repair window, plus a second for timer slop. The
|
|
43
|
+
* drop at the identify is not enough by itself. A node's reads before the sibling arrived
|
|
44
|
+
* (its start pass, with no cohort to ask) stamp the window, so the reload the identify
|
|
45
|
+
* starts is served from the local copy without a consult and the view would keep the
|
|
46
|
+
* pre-connection rows until {@link MEMBERSHIP_VIEW_MAX_AGE_MS}. The reload past the window
|
|
47
|
+
* starts the consults that bring in what the sibling committed while this node was away.
|
|
48
|
+
*/
|
|
49
|
+
export const MEMBERSHIP_VIEW_RECONNECT_RELOAD_MS = OPTIMYSTIC_READ_REPAIR_WINDOW_MS + 1_000;
|
|
50
|
+
|
|
51
|
+
/** One load's rows, and whether the view may keep them. */
|
|
52
|
+
export interface MembershipViewLoad<Row> {
|
|
53
|
+
rows: readonly Row[];
|
|
54
|
+
/**
|
|
55
|
+
* False when no later change event is guaranteed to reach the view for these rows (the
|
|
56
|
+
* read that produced them did not arm the table's change bridge), so they serve only the
|
|
57
|
+
* callers of this load.
|
|
58
|
+
*/
|
|
59
|
+
installable: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Reads the whole table. `retry` is the caller's: false from a caller holding the control
|
|
64
|
+
* write lock, whose read must not sleep a retry backoff.
|
|
65
|
+
*/
|
|
66
|
+
export type MembershipViewLoader<Row> = (retry: boolean) => Promise<MembershipViewLoad<Row>>;
|
|
67
|
+
|
|
68
|
+
export interface MembershipViewOptions {
|
|
69
|
+
/** Default {@link MEMBERSHIP_VIEW_MAX_AGE_MS}. */
|
|
70
|
+
maxAgeMs?: number;
|
|
71
|
+
/** Monotonic clock in milliseconds; default `performance.now`. */
|
|
72
|
+
now?: () => number;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export class MembershipView<Row> {
|
|
76
|
+
private rows: readonly Row[] | undefined;
|
|
77
|
+
/** When the load that produced {@link rows} started: the rows are no older than this. */
|
|
78
|
+
private loadedAt = 0;
|
|
79
|
+
/** Bumped by every {@link invalidate}; a load installs only if it is unchanged since the load began. */
|
|
80
|
+
private generation = 0;
|
|
81
|
+
/**
|
|
82
|
+
* The load each `retry` flag's callers share, current generation only. Separate per flag
|
|
83
|
+
* so a caller under the write lock never awaits a retrying load's backoff sleep.
|
|
84
|
+
*/
|
|
85
|
+
private readonly inFlight = new Map<boolean, Promise<readonly Row[]>>();
|
|
86
|
+
private readonly maxAgeMs: number;
|
|
87
|
+
private readonly now: () => number;
|
|
88
|
+
|
|
89
|
+
constructor(private readonly loader: MembershipViewLoader<Row>, options: MembershipViewOptions = {}) {
|
|
90
|
+
this.maxAgeMs = options.maxAgeMs ?? MEMBERSHIP_VIEW_MAX_AGE_MS;
|
|
91
|
+
this.now = options.now ?? (() => performance.now());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The table's rows: the installed copy while it is younger than the maximum age, otherwise a
|
|
96
|
+
* load, shared with every caller of the same `retry` flag that arrives while it runs. A load
|
|
97
|
+
* that raced an {@link invalidate} answers its own callers with what it read, as an uncached
|
|
98
|
+
* read would have, and keeps nothing. A failed load keeps nothing and rejects.
|
|
99
|
+
*/
|
|
100
|
+
read(retry: boolean): Promise<readonly Row[]> {
|
|
101
|
+
if (this.rows !== undefined && this.now() - this.loadedAt < this.maxAgeMs) {
|
|
102
|
+
return Promise.resolve(this.rows);
|
|
103
|
+
}
|
|
104
|
+
return this.inFlight.get(retry) ?? this.startLoad(retry);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/** Read the table without serving, joining or keeping anything. */
|
|
108
|
+
async readThrough(retry: boolean): Promise<readonly Row[]> {
|
|
109
|
+
return (await this.loader(retry)).rows;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Drop the installed rows and every load in flight, so the next read loads afresh and no
|
|
114
|
+
* earlier load installs. Synchronous and flag-setting only: the table-change handler calls
|
|
115
|
+
* it inside the committing call.
|
|
116
|
+
*/
|
|
117
|
+
invalidate(): void {
|
|
118
|
+
this.generation++;
|
|
119
|
+
this.rows = undefined;
|
|
120
|
+
this.inFlight.clear();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
private startLoad(retry: boolean): Promise<readonly Row[]> {
|
|
124
|
+
const load = this.load(retry, this.generation, this.now());
|
|
125
|
+
this.inFlight.set(retry, load);
|
|
126
|
+
const forget = (): void => {
|
|
127
|
+
if (this.inFlight.get(retry) === load) {
|
|
128
|
+
this.inFlight.delete(retry);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
// Both arms handled, so a failed load rejects only for the callers awaiting it.
|
|
132
|
+
void load.then(forget, forget);
|
|
133
|
+
return load;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private async load(retry: boolean, generation: number, startedAt: number): Promise<readonly Row[]> {
|
|
137
|
+
const { rows, installable } = await this.loader(retry);
|
|
138
|
+
if (installable && generation === this.generation) {
|
|
139
|
+
this.rows = rows;
|
|
140
|
+
this.loadedAt = startedAt;
|
|
141
|
+
}
|
|
142
|
+
return rows;
|
|
143
|
+
}
|
|
144
|
+
}
|
package/src/owner-anchor-sync.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* How the node-local trusted-owner anchor follows the replicated `OwnerKey` table along
|
|
3
3
|
* VERIFIABLE chains — the pure rule, with no I/O, so it can be tested without a database.
|
|
4
|
-
* `CadreNode.syncOwnerAnchor` reads the table, the tombstones and the
|
|
4
|
+
* `CadreNode.syncOwnerAnchor` reads the table, the tombstones and the owner-grant chain,
|
|
5
5
|
* calls {@link deriveOwnerAnchor}, and applies the difference to the store.
|
|
6
6
|
*
|
|
7
7
|
* Why not simply trust the table: an empty local copy can seat any key through the
|
|
@@ -19,9 +19,10 @@
|
|
|
19
19
|
* retired, although nothing could re-derive it from the remaining base.
|
|
20
20
|
* - **derivable from S**: a live row whose stored proof verifies against S — owner-signed
|
|
21
21
|
* (`vouchSig` set): `vouchOwner` is in S and `verifyOwnerKeyVoucher` holds;
|
|
22
|
-
*
|
|
23
|
-
* holds with S as the anchor. The founding row (all three null)
|
|
24
|
-
* founders enter anchors only out of band.
|
|
22
|
+
* grant-seated (`vouchSig` null, `vouchUsage` naming an `OwnerGrantUsage` row):
|
|
23
|
+
* `verifyOwnerGrantAdmission` holds with S as the anchor. The founding row (all three null)
|
|
24
|
+
* is never derivable: founders enter anchors only out of band. A machine invitation admits a
|
|
25
|
+
* machine and never seats an owner row, so no `CadreInviteUsage` row is read here.
|
|
25
26
|
* - **derived**: the least fixpoint from base ∪ kept — add every key whose live row is
|
|
26
27
|
* derivable from what is already in the set, until nothing changes. **chainDerived** is the
|
|
27
28
|
* keys whose live row was derivable in this pass, base keys included.
|
|
@@ -45,9 +46,9 @@
|
|
|
45
46
|
* two cannot be told apart from replicated state; keeping is the only rule under which a
|
|
46
47
|
* sole owner can rotate at all.
|
|
47
48
|
*/
|
|
48
|
-
import {
|
|
49
|
+
import { verifyOwnerGrantAdmission, verifyOwnerKeyVoucher, verifyRevocationSigner } from './peer-authorization.js';
|
|
49
50
|
import type { TrustSource } from './trusted-owner-store.js';
|
|
50
|
-
import type {
|
|
51
|
+
import type { OwnerGrantChain, OwnerKeyRow, RevocationRow } from './types.js';
|
|
51
52
|
|
|
52
53
|
export interface OwnerAnchorInputs {
|
|
53
54
|
/** The party whose table is being read; every stored proof is judged for it, so a voucher or tombstone signed for another party neither seats nor removes a key. */
|
|
@@ -58,8 +59,8 @@ export interface OwnerAnchorInputs {
|
|
|
58
59
|
rows: readonly OwnerKeyRow[];
|
|
59
60
|
/** `Revocation` rows; only those with `tableName === 'OwnerKey'` are consulted. */
|
|
60
61
|
tombstones: readonly RevocationRow[];
|
|
61
|
-
/** The usage and
|
|
62
|
-
|
|
62
|
+
/** The usage and grant rows, when some owner row is grant-seated; null when none is, which skips every consent-seated row. */
|
|
63
|
+
grantChain: OwnerGrantChain | null;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
export interface OwnerAnchorDerivation {
|
|
@@ -70,7 +71,7 @@ export interface OwnerAnchorDerivation {
|
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
/** The rule above, as one pure function. */
|
|
73
|
-
export function deriveOwnerAnchor({ partyId, anchor, rows, tombstones,
|
|
74
|
+
export function deriveOwnerAnchor({ partyId, anchor, rows, tombstones, grantChain }: OwnerAnchorInputs): OwnerAnchorDerivation {
|
|
74
75
|
const ownerTombstones = tombstones.filter(tombstone => tombstone.tableName === 'OwnerKey');
|
|
75
76
|
// NOTE: a stamp is retired by any tombstone that names it, verified or not — the same
|
|
76
77
|
// reading `queryCadrePeers` and `getOwnerKeys` give the table. A forged tombstone can
|
|
@@ -80,7 +81,7 @@ export function deriveOwnerAnchor({ partyId, anchor, rows, tombstones, chain }:
|
|
|
80
81
|
const liveRows = rows.filter(row => !retired.has(row.stampId));
|
|
81
82
|
|
|
82
83
|
const derived = passStartSet(anchor, liveRows);
|
|
83
|
-
const chainDerived = deriveFixpoint(partyId, derived, liveRows,
|
|
84
|
+
const chainDerived = deriveFixpoint(partyId, derived, liveRows, grantChain);
|
|
84
85
|
|
|
85
86
|
const removed = removedKeys(partyId, derived, chainDerived, ownerTombstones);
|
|
86
87
|
if (removed.size > 0 && removed.size === derived.size) {
|
|
@@ -108,7 +109,7 @@ function passStartSet(anchor: ReadonlyMap<string, TrustSource>, liveRows: readon
|
|
|
108
109
|
* trust only grows within a pass, and a proof that failed against a trusted voucher cannot
|
|
109
110
|
* pass later, so no row is verified twice.
|
|
110
111
|
*/
|
|
111
|
-
function deriveFixpoint(partyId: string, derived: Set<string>, liveRows: readonly OwnerKeyRow[],
|
|
112
|
+
function deriveFixpoint(partyId: string, derived: Set<string>, liveRows: readonly OwnerKeyRow[], grantChain: OwnerGrantChain | null): Set<string> {
|
|
112
113
|
const chainDerived = new Set<string>();
|
|
113
114
|
const pending = new Set(liveRows);
|
|
114
115
|
let progress = true;
|
|
@@ -119,7 +120,7 @@ function deriveFixpoint(partyId: string, derived: Set<string>, liveRows: readonl
|
|
|
119
120
|
continue;
|
|
120
121
|
}
|
|
121
122
|
pending.delete(row);
|
|
122
|
-
if (proofVerifies(partyId, row, derived,
|
|
123
|
+
if (proofVerifies(partyId, row, derived, grantChain)) {
|
|
123
124
|
chainDerived.add(row.key);
|
|
124
125
|
derived.add(row.key);
|
|
125
126
|
progress = true;
|
|
@@ -130,22 +131,22 @@ function deriveFixpoint(partyId: string, derived: Set<string>, liveRows: readonl
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
/** Does `row`'s stored proof verify against `trusted`, for `partyId`, by the kind of proof it carries (see the module doc)? */
|
|
133
|
-
function proofVerifies(partyId: string, row: OwnerKeyRow, trusted: ReadonlySet<string>,
|
|
134
|
+
function proofVerifies(partyId: string, row: OwnerKeyRow, trusted: ReadonlySet<string>, grantChain: OwnerGrantChain | null): boolean {
|
|
134
135
|
if (row.vouchOwner === null) {
|
|
135
136
|
return false;
|
|
136
137
|
}
|
|
137
138
|
if (row.vouchSig !== null) {
|
|
138
|
-
return verifyOwnerKeyVoucher(partyId, row.key, row.stampId, row.vouchOwner, row.vouchSig);
|
|
139
|
+
return verifyOwnerKeyVoucher(partyId, row.key, row.label, row.stampId, row.vouchOwner, row.vouchSig);
|
|
139
140
|
}
|
|
140
141
|
if (row.vouchUsage === null) {
|
|
141
142
|
return false;
|
|
142
143
|
}
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
144
|
+
const grantUsage = grantChain?.usages.get(row.vouchUsage);
|
|
145
|
+
const grant = grantUsage === undefined ? undefined : grantChain?.grants.get(grantUsage.grantKey);
|
|
146
|
+
if (grantUsage === undefined || grant === undefined) {
|
|
146
147
|
return false;
|
|
147
148
|
}
|
|
148
|
-
return
|
|
149
|
+
return verifyOwnerGrantAdmission(partyId, row, grantUsage, grant, (key) => trusted.has(key));
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
/**
|