@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
|
@@ -9,13 +9,21 @@
|
|
|
9
9
|
* every `Strand.MemberPeer.PeerId` whose `MemberKey` has no live `Strand.Member`
|
|
10
10
|
* row. `MemberPeer.MemberExists` runs on insert, so an orphaned binding can only
|
|
11
11
|
* mean "this member existed and was removed" — it is exactly the durable,
|
|
12
|
-
* replicated record of a removed party's machines.
|
|
13
|
-
*
|
|
12
|
+
* replicated record of a removed party's machines. That holds on the network
|
|
13
|
+
* only because every writer commits a party's `Member` row before its
|
|
14
|
+
* `MemberPeer` row, as separate commits (the membership reconciler does, and
|
|
15
|
+
* `registerMemberPeer` requires an existing member): Optimystic can save a
|
|
16
|
+
* multi-collection transaction's tables separately, and remaining members can
|
|
17
|
+
* apply them in either order. The one exception is the founder's bootstrap,
|
|
18
|
+
* which writes its own `Member` and `MemberPeer` in one transaction; that is
|
|
19
|
+
* acceptable because it commits before any other party has joined, so no other
|
|
20
|
+
* party's enforcer is reading yet. Everything NOT in the set is admitted.
|
|
21
|
+
* Deliberate consequences:
|
|
14
22
|
*
|
|
15
23
|
* - **Open strands are untouched.** `Member`/`MemberPeer` carry `OnlyClosed`
|
|
16
24
|
* checks, so an open strand has no rows and the set is empty — and
|
|
17
25
|
* `StrandInstanceManager` additionally skips arming this enforcer entirely for
|
|
18
|
-
* `Type !== 'c'`, so an open strand
|
|
26
|
+
* `Type !== 'c'`, so an open strand neither subscribes nor reads.
|
|
19
27
|
* - **A joining party mid-admission is admitted** (it has no orphaned binding).
|
|
20
28
|
* An allowlist ("only peers bound to live members") was considered and
|
|
21
29
|
* rejected: a joiner's first membership writes must travel through the very
|
|
@@ -44,22 +52,19 @@
|
|
|
44
52
|
* Refusing NEW connections only half-honors "no longer be answered to or
|
|
45
53
|
* communicated with": a long-lived strand connection may never re-dial, so
|
|
46
54
|
* without teardown a removed party keeps whatever session it already holds.
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* `revokeMember`/`leaveStrand` writes through a bare `Database` handle this
|
|
51
|
-
* runtime never sees — so teardown rides the snapshot refresh itself, with
|
|
52
|
-
* `refresh()` exposed for immediacy
|
|
55
|
+
* Teardown rides the snapshot refresh itself, which runs when the membership
|
|
56
|
+
* tables change (see "When the deny set is refreshed"). `refresh()` is public
|
|
57
|
+
* for a caller that wants to await the cut
|
|
53
58
|
* (`StrandInstanceManager.refreshRevocationEnforcement` →
|
|
54
|
-
* `CadreNode.refreshRevocationEnforcement`).
|
|
59
|
+
* `CadreNode.refreshRevocationEnforcement`): it resolves only after the sweep.
|
|
55
60
|
*
|
|
56
61
|
* The sweep is driven by MEMBERSHIP in the deny set, never by an
|
|
57
62
|
* entered-the-set diff: every refresh hangs up every currently-connected
|
|
58
63
|
* revoked peer, so a resume, a crashed refresh, or a connection established
|
|
59
64
|
* between two refreshes cannot leave a session standing (a diff would be a
|
|
60
65
|
* strict subset of this, so none is kept). A peer with no open connection is
|
|
61
|
-
* simply not swept, and a failed `hangUp` is logged, never thrown
|
|
62
|
-
* refresh
|
|
66
|
+
* simply not swept, and a failed `hangUp` is logged, never thrown, and retried
|
|
67
|
+
* by a refresh {@link REVOCATION_REFRESH_RETRY_MS} later. `hangUp` closes DIRECT and RELAYED connections alike, and
|
|
63
68
|
* on a strand node running the relay server it also drops any reservation the
|
|
64
69
|
* revoked peer holds there (a reservation dies with its connection — see the
|
|
65
70
|
* relay-seam doc in `membership-connection-gater.ts`).
|
|
@@ -81,6 +86,25 @@
|
|
|
81
86
|
* Best-effort by nature: a removed party learns only if the tombstone
|
|
82
87
|
* replicated to it before the cut.
|
|
83
88
|
*
|
|
89
|
+
* ## When the deny set is refreshed
|
|
90
|
+
*
|
|
91
|
+
* Every refresh runs on one table change subscription (`table-change-watch.ts`),
|
|
92
|
+
* single-flight, so no two scheduled refreshes overlap:
|
|
93
|
+
*
|
|
94
|
+
* - **On a change** to `Strand.Member` or `Strand.MemberPeer`. App writes
|
|
95
|
+
* (`revokeMember`, `leaveStrand`) commit on the same Quereus `Database` the
|
|
96
|
+
* subscription is on, so a local removal is acted on within the coalescing
|
|
97
|
+
* window; a replicated one when this node's storage takes it in.
|
|
98
|
+
* - **Once at start**, when the strand's database is published.
|
|
99
|
+
* - **On a backstop** ({@link DEFAULT_REVOCATION_BACKSTOP_MS} after the last
|
|
100
|
+
* refresh settled), for a change that raised no event.
|
|
101
|
+
* - **After a failed refresh**, {@link REVOCATION_REFRESH_RETRY_MS} later. The
|
|
102
|
+
* first successful read also arms the plugin's bridge for replicated changes
|
|
103
|
+
* (the NOTE on `watchTables`), so until one succeeds only local commits wake it.
|
|
104
|
+
* - **After each successful refresh on a wide strand** — more distinct bound peer ids than
|
|
105
|
+
* the cluster size — {@link REVOCATION_WIDE_STRAND_REFRESH_MS} later, because
|
|
106
|
+
* this node may be outside a membership block's cohort and so hear no change.
|
|
107
|
+
*
|
|
84
108
|
* ## What transfers from the control gater (`membership-connection-gater.ts`)
|
|
85
109
|
*
|
|
86
110
|
* Transfers: the two-layer shape (fail-closed per-stream gate as PRIMARY, a
|
|
@@ -116,14 +140,33 @@
|
|
|
116
140
|
*/
|
|
117
141
|
import type { ConnectionGater, PeerId } from '@libp2p/interface';
|
|
118
142
|
import type { Database } from '@quereus/quereus';
|
|
143
|
+
import { type TableChangeWatch, type TableChangeWatchOptions } from './table-change-watch.js';
|
|
144
|
+
import type { TimeoutScheduler } from './timeout-scheduler.js';
|
|
145
|
+
/**
|
|
146
|
+
* Default longest wait between two deny-set refreshes when no membership change
|
|
147
|
+
* is heard. Overridable via {@link StrandRevocationEnforcementConfig.backstopMs}.
|
|
148
|
+
*
|
|
149
|
+
* NOTE: the backstop covers two paths the change subscription does not: a
|
|
150
|
+
* machine that was offline when the removal was committed (it normally takes
|
|
151
|
+
* the blocks in by join backfill or read repair on reconnect, which raise the
|
|
152
|
+
* change too), and any change that reaches storage without an event. The case
|
|
153
|
+
* of a node outside a membership block's cohort is covered by
|
|
154
|
+
* {@link REVOCATION_WIDE_STRAND_REFRESH_MS}, not by this.
|
|
155
|
+
*/
|
|
156
|
+
export declare const DEFAULT_REVOCATION_BACKSTOP_MS: number;
|
|
119
157
|
/**
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
* replication delay itself. Overridable via
|
|
124
|
-
* {@link StrandRevocationEnforcementConfig.pollIntervalMs}.
|
|
158
|
+
* How soon a failed refresh is retried. Also how soon a replicated removal is
|
|
159
|
+
* heard after a first read that failed, since that read is what arms the
|
|
160
|
+
* plugin's replicated-change bridge for the two tables.
|
|
125
161
|
*/
|
|
126
|
-
export declare const
|
|
162
|
+
export declare const REVOCATION_REFRESH_RETRY_MS = 30000;
|
|
163
|
+
/**
|
|
164
|
+
* How soon a strand with more distinct bound peer ids than its cluster size is
|
|
165
|
+
* refreshed again after a successful refresh. On such a strand this node may be
|
|
166
|
+
* outside a membership block's cohort, so it takes in no storage event for a
|
|
167
|
+
* removal (`docs/architecture.md` → "Table change subscriptions").
|
|
168
|
+
*/
|
|
169
|
+
export declare const REVOCATION_WIDE_STRAND_REFRESH_MS = 30000;
|
|
127
170
|
/**
|
|
128
171
|
* Embedder tuning for the strand revoked-peer gate, threaded from
|
|
129
172
|
* `CadreNodeConfig.strandRevocationEnforcement` to
|
|
@@ -135,8 +178,17 @@ export interface StrandRevocationEnforcementConfig {
|
|
|
135
178
|
* the pre-existing behaviour (a removed party's peers keep being served).
|
|
136
179
|
*/
|
|
137
180
|
enabled?: boolean;
|
|
138
|
-
/**
|
|
139
|
-
|
|
181
|
+
/** Longest wait between two refreshes with no change heard, ms. Default {@link DEFAULT_REVOCATION_BACKSTOP_MS}. */
|
|
182
|
+
backstopMs?: number;
|
|
183
|
+
/**
|
|
184
|
+
* Default true: the enforcer schedules its own refreshes — on a change to
|
|
185
|
+
* `Strand.Member` or `Strand.MemberPeer`, after a failed read or hang-up, and
|
|
186
|
+
* on a wide strand's follow-up. False schedules none of those, so refreshes
|
|
187
|
+
* run only at start, on the backstop, and on `refreshRevocationEnforcement`.
|
|
188
|
+
* For test scenarios that need a machine's cuts to be the ones the test drove;
|
|
189
|
+
* a production node leaves it on.
|
|
190
|
+
*/
|
|
191
|
+
autoRefresh?: boolean;
|
|
140
192
|
}
|
|
141
193
|
/** One `Strand.MemberPeer` row, as the deny-set derivation consumes it. */
|
|
142
194
|
export interface StrandMemberPeerBinding {
|
|
@@ -150,16 +202,6 @@ export interface StrandRevocationRows {
|
|
|
150
202
|
/** Every `Strand.MemberPeer` row (live-membered and orphaned alike). */
|
|
151
203
|
bindings: readonly StrandMemberPeerBinding[];
|
|
152
204
|
}
|
|
153
|
-
/**
|
|
154
|
-
* Timer seam so the refresh cadence is testable without fake timers — the
|
|
155
|
-
* injectable-clock idiom of `UnauthorizedReservationBudget`'s `now` argument,
|
|
156
|
-
* shaped for an interval-driven component. The default schedules a real
|
|
157
|
-
* interval and unrefs it so an armed enforcer never holds a process open.
|
|
158
|
-
*/
|
|
159
|
-
export interface RevocationRefreshScheduler {
|
|
160
|
-
setInterval(fn: () => void, ms: number): unknown;
|
|
161
|
-
clearInterval(handle: unknown): void;
|
|
162
|
-
}
|
|
163
205
|
/**
|
|
164
206
|
* The minimum of a libp2p node the teardown sweep needs: who WE are, what is
|
|
165
207
|
* open, and how to close it. Deliberately not the node itself, so the sweep
|
|
@@ -206,8 +248,19 @@ export interface StrandRevocationEnforcerDeps {
|
|
|
206
248
|
* logged and swallowed — an embedder's handler must not derail the sweep.
|
|
207
249
|
*/
|
|
208
250
|
onSelfRevoked?: () => void;
|
|
209
|
-
/**
|
|
210
|
-
|
|
251
|
+
/**
|
|
252
|
+
* Subscribe `onChange` to changes in `Strand.Member` and `Strand.MemberPeer`
|
|
253
|
+
* (`StrandDatabase.watchTables`). Absent, or throwing: refreshes come from
|
|
254
|
+
* start, the backstop, retries and wide-strand follow-ups only (unit tests).
|
|
255
|
+
*/
|
|
256
|
+
watchChanges?(onChange: () => Promise<void>, options: TableChangeWatchOptions): TableChangeWatch;
|
|
257
|
+
/**
|
|
258
|
+
* The strand's cluster size. A snapshot with more distinct bound peer ids than
|
|
259
|
+
* this schedules {@link REVOCATION_WIDE_STRAND_REFRESH_MS}; absent, never.
|
|
260
|
+
*/
|
|
261
|
+
cohortBreadth?: number;
|
|
262
|
+
/** Timer seam for the watch; omit for real (unref'd) timeouts. */
|
|
263
|
+
scheduler?: TimeoutScheduler;
|
|
211
264
|
}
|
|
212
265
|
/**
|
|
213
266
|
* Read the deny-set inputs from a strand database: full-scan
|
|
@@ -221,17 +274,17 @@ export interface StrandRevocationEnforcerDeps {
|
|
|
221
274
|
* separate reads with nothing holding them to one snapshot, so a write that
|
|
222
275
|
* lands between them is seen by the second scan and not the first. A member
|
|
223
276
|
* joins as `Member` row then `MemberPeer` row (`MemberExists` forces that
|
|
224
|
-
* order
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
* missing from the NEWER key scan).
|
|
231
|
-
* matching the module doc.
|
|
277
|
+
* order; the module doc says why it holds on the network), so reading `Member`
|
|
278
|
+
* first would let a join that commits mid-read produce a binding whose member
|
|
279
|
+
* key is absent from the key set — a brand-new member classified as REVOKED,
|
|
280
|
+
* the one fail-CLOSED outcome this module exists to avoid. Reading bindings
|
|
281
|
+
* first inverts the skew: the new binding is simply not in the older bindings
|
|
282
|
+
* snapshot, so the joiner is unclassified and admitted, and a revocation that
|
|
283
|
+
* lands mid-read is still seen (the key is missing from the NEWER key scan).
|
|
284
|
+
* Every skew window then resolves fail-open, matching the module doc.
|
|
232
285
|
*
|
|
233
|
-
* NOTE: two whole-table scans per refresh (
|
|
234
|
-
* strand). Fine at strand scale (a handful of members and bindings); if
|
|
286
|
+
* NOTE: two whole-table scans per refresh (on each membership change and each
|
|
287
|
+
* backstop, per closed strand). Fine at strand scale (a handful of members and bindings); if
|
|
235
288
|
* membership churn ever makes these tables large, the fix is a reliable
|
|
236
289
|
* filtered read — which needs the networked point-lookup gap closed
|
|
237
290
|
* (`debt-composite-pk-point-lookup-unreliable-untracked`) — not a bigger scan.
|
|
@@ -240,25 +293,24 @@ export declare function readStrandRevocationRows(db: Database): Promise<StrandRe
|
|
|
240
293
|
/**
|
|
241
294
|
* The per-strand revoked-peer deny set and the synchronous predicates over it.
|
|
242
295
|
*
|
|
243
|
-
* Lifecycle
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
250
|
-
* never blocked on a refresh.
|
|
296
|
+
* Lifecycle: created in `StrandInstanceManager.buildStrandRuntime` (closed
|
|
297
|
+
* strands only), started when `publishDatabase` hands the strand's database to
|
|
298
|
+
* the app, stopped and dropped in `releaseRuntime` — so quiesce → resume
|
|
299
|
+
* rebuilds it with a fresh snapshot. The snapshot starts EMPTY (admit
|
|
300
|
+
* everything) and repopulates on the first successful read; a revoked peer
|
|
301
|
+
* therefore gets a short admit window per resume at the connection level.
|
|
302
|
+
* Accepted: fail-open posture, and resume is never blocked on a refresh.
|
|
251
303
|
*/
|
|
252
304
|
export declare class StrandRevocationEnforcer {
|
|
253
305
|
private readonly deps;
|
|
254
306
|
/** The materialized deny set — peer ids with an orphaned binding and no live one. */
|
|
255
307
|
private revokedPeerIds;
|
|
256
|
-
private readonly
|
|
257
|
-
private readonly
|
|
308
|
+
private readonly backstopMs;
|
|
309
|
+
private readonly autoRefresh;
|
|
258
310
|
/** Tail of the refresh chain — what serializes refreshes (no two reads in flight). */
|
|
259
311
|
private refreshTail;
|
|
260
|
-
|
|
261
|
-
private
|
|
312
|
+
/** Every scheduled refresh runs on it; present between {@link start} and {@link stop}. */
|
|
313
|
+
private watch;
|
|
262
314
|
private started;
|
|
263
315
|
private stopped;
|
|
264
316
|
/** Latch for the one-shot self-revocation signal — see {@link StrandRevocationEnforcerDeps.onSelfRevoked}. */
|
|
@@ -266,9 +318,14 @@ export declare class StrandRevocationEnforcer {
|
|
|
266
318
|
constructor(deps: StrandRevocationEnforcerDeps, config?: StrandRevocationEnforcementConfig);
|
|
267
319
|
/** Number of peers currently denied — test/diagnostic surface. */
|
|
268
320
|
get revokedCount(): number;
|
|
269
|
-
/**
|
|
321
|
+
/**
|
|
322
|
+
* Subscribe to membership changes and run one refresh at once (not awaited —
|
|
323
|
+
* bring-up never blocks on it); the module doc's "When the deny set is
|
|
324
|
+
* refreshed" has the full schedule. Call once the strand's database is
|
|
325
|
+
* published. A second call, or one after {@link stop}, is a no-op.
|
|
326
|
+
*/
|
|
270
327
|
start(): void;
|
|
271
|
-
/**
|
|
328
|
+
/** Stop scheduling refreshes; a read already in flight completes but its result is discarded. */
|
|
272
329
|
stop(): void;
|
|
273
330
|
/**
|
|
274
331
|
* Refresh the deny set now. Never rejects. Serialized: concurrent calls chain,
|
|
@@ -291,22 +348,39 @@ export declare class StrandRevocationEnforcer {
|
|
|
291
348
|
* over a connection admitted before the revocation replicated here.
|
|
292
349
|
*/
|
|
293
350
|
authorizeStream(remotePeerId: string, _protocol: string): boolean;
|
|
351
|
+
/**
|
|
352
|
+
* The watch every scheduled refresh runs on: the deps' change subscription, or
|
|
353
|
+
* a timer-only one when changes are not watched or the subscription throws.
|
|
354
|
+
*
|
|
355
|
+
* NOTE: a refresh can wake itself once. Its read may start a read-repair sync
|
|
356
|
+
* (a held block read more than Optimystic's `readRepairWindowMs`, 10 s, after
|
|
357
|
+
* its last check), and that sync's landing raises a change. The follow-up
|
|
358
|
+
* refresh runs within the coalescing window plus one read — well inside the
|
|
359
|
+
* read-repair window — so it starts no second sync and the chain stops there.
|
|
360
|
+
* If idle sync counts on a strand do not fall with this subscription, suspect
|
|
361
|
+
* this loop first.
|
|
362
|
+
*/
|
|
363
|
+
private subscribe;
|
|
294
364
|
/**
|
|
295
365
|
* One serialized snapshot rebuild; contains every failure (contract: never rejects).
|
|
296
366
|
*
|
|
297
367
|
* NOTE: nothing here carries a deadline — a `readRows` or a `hangUp` that never
|
|
298
|
-
* settles wedges the refresh chain, which freezes the deny set (the
|
|
299
|
-
*
|
|
368
|
+
* settles wedges the refresh chain, which freezes the deny set (the watch runs
|
|
369
|
+
* one refresh at a time, and every later `refresh()` chains behind the stuck one).
|
|
300
370
|
* Both are bounded in practice (Quereus reads settle; libp2p's connection close
|
|
301
371
|
* has its own timeout). If a strand is ever seen with a stale deny set and no
|
|
302
372
|
* refresh log line, wrap both awaits in a timeout rather than hunting further.
|
|
303
373
|
*/
|
|
304
374
|
private doRefresh;
|
|
375
|
+
/** Arm one more refresh `delayMs` from now, unless {@link StrandRevocationEnforcementConfig.autoRefresh} is off or the enforcer is not running. */
|
|
376
|
+
private scheduleFollowUp;
|
|
377
|
+
/** Schedule {@link REVOCATION_WIDE_STRAND_REFRESH_MS} when `rows` bind more distinct peers than the cluster holds. */
|
|
378
|
+
private followUpWideStrand;
|
|
305
379
|
/**
|
|
306
380
|
* Close what is already OPEN to the peers of `revoked` — the teardown half of
|
|
307
381
|
* enforcement (module doc: "Removal cuts LIVE sessions too"). Never rejects:
|
|
308
|
-
* every failure is contained, so the refresh
|
|
309
|
-
*
|
|
382
|
+
* every failure is contained, so the refresh chain survives, and a failed
|
|
383
|
+
* `hangUp` schedules a retry.
|
|
310
384
|
*
|
|
311
385
|
* `revoked` is passed IN rather than read off the field, so the sweep judges
|
|
312
386
|
* the exact snapshot its own refresh produced; likewise the connection list is
|