@serfab/cadre-core 1.14.0 → 1.15.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 +293 -274
- package/dist/cadre-invite-protocol.d.ts +64 -29
- package/dist/cadre-invite-protocol.js +207 -38
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-join-request.d.ts +42 -0
- package/dist/cadre-join-request.js +144 -0
- package/dist/cadre-join-request.js.map +1 -0
- package/dist/cadre-node.d.ts +375 -79
- package/dist/cadre-node.js +758 -186
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-database.d.ts +92 -12
- package/dist/control-database.js +323 -142
- package/dist/control-database.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +19 -3
- package/dist/control-schema.js.map +1 -1
- package/dist/ed25519-key.d.ts +7 -9
- package/dist/ed25519-key.js +20 -10
- package/dist/ed25519-key.js.map +1 -1
- package/dist/fetch-impl.d.ts +12 -0
- package/dist/fetch-impl.js +23 -0
- package/dist/fetch-impl.js.map +1 -0
- package/dist/file-durable-slot.js +2 -1
- package/dist/file-durable-slot.js.map +1 -1
- package/dist/file-safe-name.d.ts +25 -0
- package/dist/file-safe-name.js +29 -0
- package/dist/file-safe-name.js.map +1 -0
- package/dist/formation-approval.js +5 -11
- package/dist/formation-approval.js.map +1 -1
- package/dist/fs-atomic.d.ts +0 -19
- package/dist/fs-atomic.js +3 -24
- package/dist/fs-atomic.js.map +1 -1
- package/dist/index.d.ts +12 -5
- package/dist/index.js +31 -6
- package/dist/index.js.map +1 -1
- package/dist/invitation-bootstrap.d.ts +50 -12
- package/dist/invitation-bootstrap.js +76 -13
- package/dist/invitation-bootstrap.js.map +1 -1
- package/dist/joined-strand-store.d.ts +7 -0
- package/dist/joined-strand-store.js +9 -0
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store-file.js +2 -1
- package/dist/key-store-file.js.map +1 -1
- package/dist/link-budget.d.ts +9 -4
- package/dist/link-budget.js +9 -4
- package/dist/link-budget.js.map +1 -1
- package/dist/machine-removal.d.ts +78 -0
- package/dist/machine-removal.js +166 -0
- package/dist/machine-removal.js.map +1 -0
- package/dist/membership-connection-gater.d.ts +6 -5
- package/dist/membership-connection-gater.js +6 -5
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-claim-payload.d.ts +55 -0
- package/dist/node-claim-payload.js +108 -4
- package/dist/node-claim-payload.js.map +1 -1
- package/dist/node-default-transports.d.ts +27 -0
- package/dist/node-default-transports.js +35 -0
- package/dist/node-default-transports.js.map +1 -0
- package/dist/node-transports.d.ts +30 -0
- package/dist/node-transports.js +40 -0
- package/dist/node-transports.js.map +1 -0
- package/dist/peer-dial.d.ts +124 -27
- package/dist/peer-dial.js +254 -30
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.js +6 -0
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/peer-record.d.ts +6 -45
- package/dist/peer-record.js +8 -50
- package/dist/peer-record.js.map +1 -1
- package/dist/primary-lan-address.d.ts +8 -0
- package/dist/primary-lan-address.js +46 -0
- package/dist/primary-lan-address.js.map +1 -0
- package/dist/provider/client.d.ts +131 -0
- package/dist/provider/client.js +226 -0
- package/dist/provider/client.js.map +1 -0
- package/dist/provider/descriptor.d.ts +41 -0
- package/dist/provider/descriptor.js +103 -0
- package/dist/provider/descriptor.js.map +1 -0
- package/dist/provider/index.d.ts +9 -0
- package/dist/provider/index.js +10 -0
- package/dist/provider/index.js.map +1 -0
- package/dist/provider/oauth.d.ts +125 -0
- package/dist/provider/oauth.js +199 -0
- package/dist/provider/oauth.js.map +1 -0
- package/dist/provider/rent-node.d.ts +101 -0
- package/dist/provider/rent-node.js +220 -0
- package/dist/provider/rent-node.js.map +1 -0
- package/dist/relay-addrs.d.ts +9 -4
- package/dist/relay-addrs.js +9 -4
- package/dist/relay-addrs.js.map +1 -1
- package/dist/relay-reservation.d.ts +51 -16
- package/dist/relay-reservation.js +107 -24
- package/dist/relay-reservation.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +22 -5
- package/dist/seed-bootstrap.js +48 -25
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/strand-commit-durability.d.ts +54 -0
- package/dist/strand-commit-durability.js +74 -0
- package/dist/strand-commit-durability.js.map +1 -0
- package/dist/strand-database.d.ts +23 -8
- package/dist/strand-database.js +41 -13
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +7 -1
- package/dist/strand-first-sync-gate.js +7 -1
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-protocol.js +4 -1
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-fret-addrs.d.ts +19 -1
- package/dist/strand-fret-addrs.js +27 -9
- package/dist/strand-fret-addrs.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +81 -19
- package/dist/strand-instance-manager.js +216 -105
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-move.d.ts +124 -0
- package/dist/strand-move.js +438 -0
- package/dist/strand-move.js.map +1 -0
- package/dist/strand-network-config.js +7 -6
- package/dist/strand-network-config.js.map +1 -1
- package/dist/strand-peer-redial.d.ts +113 -0
- package/dist/strand-peer-redial.js +236 -0
- package/dist/strand-peer-redial.js.map +1 -0
- package/dist/strand-wake-protocol.js +10 -7
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/types.d.ts +212 -4
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -1
- package/package.json +18 -10
- package/src/cadre-invite-protocol.ts +233 -45
- package/src/cadre-join-request.ts +167 -0
- package/src/cadre-node.ts +10003 -9381
- package/src/control-database.ts +4663 -4451
- package/src/control-schema.ts +19 -3
- package/src/ed25519-key.ts +20 -10
- package/src/fetch-impl.ts +23 -0
- package/src/file-durable-slot.ts +64 -63
- package/src/file-safe-name.ts +32 -0
- package/src/formation-approval.ts +604 -610
- package/src/fs-atomic.ts +92 -117
- package/src/index.ts +789 -722
- package/src/invitation-bootstrap.ts +91 -13
- package/src/joined-strand-store.ts +508 -498
- package/src/key-store-file.ts +142 -141
- package/src/link-budget.ts +9 -4
- package/src/machine-removal.ts +216 -0
- package/src/membership-connection-gater.ts +638 -637
- package/src/node-claim-payload.ts +138 -4
- package/src/node-default-transports.ts +38 -0
- package/src/node-transports.ts +49 -0
- package/src/peer-dial.ts +604 -336
- package/src/peer-join-backfill.ts +6 -0
- package/src/peer-record.ts +159 -202
- package/src/primary-lan-address.ts +47 -0
- package/src/provider/client.ts +326 -0
- package/src/provider/descriptor.ts +129 -0
- package/src/provider/index.ts +59 -0
- package/src/provider/oauth.ts +285 -0
- package/src/provider/rent-node.ts +275 -0
- package/src/relay-addrs.ts +9 -4
- package/src/relay-reservation.ts +1211 -1121
- package/src/seed-bootstrap.ts +1502 -1471
- package/src/strand-commit-durability.ts +116 -0
- package/src/strand-database.ts +46 -14
- package/src/strand-first-sync-gate.ts +7 -1
- package/src/strand-formation-protocol.ts +1039 -1036
- package/src/strand-fret-addrs.ts +179 -145
- package/src/strand-instance-manager.ts +2026 -1883
- package/src/strand-move.ts +585 -0
- package/src/strand-network-config.ts +7 -6
- package/src/strand-peer-redial.ts +307 -0
- package/src/strand-wake-protocol.ts +10 -7
- package/src/types.ts +220 -4
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rent a node from a hosting provider and add it to this node's cadre, by whichever of the two
|
|
3
|
+
* join paths fits (`docs/architecture.md` → "Renting a node from an app"):
|
|
4
|
+
*
|
|
5
|
+
* - **invitation**, when the cadre has a member the container can reach from the provider's
|
|
6
|
+
* network (`CadreNode.invitationReach()` reads `anywhere`): mint a single-use, non-owner
|
|
7
|
+
* invitation and create the container with it; the container joins by itself and the app
|
|
8
|
+
* watches `getContainer(id).join`.
|
|
9
|
+
* - **claim**, otherwise (a phone that is the cadre's only member, or members reachable only on
|
|
10
|
+
* their LAN, which the provider's container is never on): create the container, poll for its
|
|
11
|
+
* node code, and claim it from this node.
|
|
12
|
+
*
|
|
13
|
+
* Nothing here persists anything: which container belongs to which cadre is the app's record,
|
|
14
|
+
* and {@link finishRental} is how an app resumes a rental it was killed in the middle of. The
|
|
15
|
+
* node code carries the claim secret, so neither the code nor the decoded payload is ever
|
|
16
|
+
* logged, only the container id and the peer id.
|
|
17
|
+
*/
|
|
18
|
+
import debug from 'debug';
|
|
19
|
+
import { decodeNodeClaimPayload } from '../node-claim-payload.js';
|
|
20
|
+
import { ProviderApiError } from './client.js';
|
|
21
|
+
const log = debug('sereus:cadre:provider-rent');
|
|
22
|
+
export class RentNodeError extends Error {
|
|
23
|
+
constructor(code, containerId) {
|
|
24
|
+
super(`rentNode: ${code}${containerId ? ` (container ${containerId})` : ''}`);
|
|
25
|
+
this.code = code;
|
|
26
|
+
this.containerId = containerId;
|
|
27
|
+
this.name = 'RentNodeError';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The provider's enrolment window is 60 s and an image pull can precede it. Bounds the
|
|
32
|
+
* container's start, not an exchange over the link, so it is not derived from the link budget.
|
|
33
|
+
*/
|
|
34
|
+
// eslint-disable-next-line no-restricted-syntax -- link-independent: bounds the container's start (an image pull plus the provider's 60 s enrolment window), not an exchange over the link
|
|
35
|
+
export const DEFAULT_NODE_CODE_DEADLINE_MS = 120000;
|
|
36
|
+
export const NODE_CODE_POLL_MS = 2000;
|
|
37
|
+
/** Lifetime of the invitation a rental mints: long enough for the container to start and redeem, short enough to limit a leaked bundle. */
|
|
38
|
+
export const RENTAL_INVITATION_TTL_MS = 15 * 60000;
|
|
39
|
+
/**
|
|
40
|
+
* Rent a node and add it to `node`'s cadre; see the module doc for the path choice.
|
|
41
|
+
*
|
|
42
|
+
* The claim path's `claimNode` can take up to the per-peer dial limit plus the seed request
|
|
43
|
+
* when no address answers (its doc: 114.5 s at the default link); no timeout is added on top
|
|
44
|
+
* of it, and `onProgress('claiming')` is the app's cue to show a slow hint. Two calls at once
|
|
45
|
+
* on one node both mint or both create; serializing them is the app's.
|
|
46
|
+
*
|
|
47
|
+
* @throws {RentNodeError} `not-an-owner`, or `node-not-ready` once the container exists.
|
|
48
|
+
* @throws {ProviderApiError} when the provider refused a request (a lapsed subscription is `403 QUOTA_EXCEEDED`).
|
|
49
|
+
* @throws {ClaimRefusedError} or {PeerUnreachableError} from `claimNode`, as it throws them.
|
|
50
|
+
*/
|
|
51
|
+
export async function rentNode(node, client, options = {}) {
|
|
52
|
+
await requireOwner(node);
|
|
53
|
+
const reach = await node.invitationReach();
|
|
54
|
+
log('renting a node for party %s; invitation reach %s → %s path', node.partyId, reach, reach === 'anywhere' ? 'invitation' : 'claim');
|
|
55
|
+
return reach === 'anywhere'
|
|
56
|
+
? await rentByInvitation(node, client, options)
|
|
57
|
+
: await rentByClaim(node, client, options);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Resume a rental: for an app that was killed mid-flow or hit the node-code deadline.
|
|
61
|
+
*
|
|
62
|
+
* - a `stopped` or `error` container → `RentNodeError('container-gone')`;
|
|
63
|
+
* - a claim container not yet claimed → the code, then `claimNode`, as {@link rentNode} would;
|
|
64
|
+
* - a claim container claimed into this cadre → done. The provider withholds the code once a
|
|
65
|
+
* claim is folded, so the claim is not re-run; one claimed by another cadre's owner →
|
|
66
|
+
* `RentNodeError('claimed-by-another')`;
|
|
67
|
+
* - an invitation container → done while `pending` or `accepted` (the app watches `join`), or
|
|
68
|
+
* the failed outcome, after which the app deletes the container and rents again (the claim
|
|
69
|
+
* path is chosen then if the member that answered is gone).
|
|
70
|
+
*/
|
|
71
|
+
export async function finishRental(node, client, containerId, options = {}) {
|
|
72
|
+
await requireOwner(node);
|
|
73
|
+
const report = await client.getContainer(containerId);
|
|
74
|
+
const kind = joinKindOf(report, containerId);
|
|
75
|
+
if (kind === 'claim') {
|
|
76
|
+
return await finishClaim(node, client, report, options);
|
|
77
|
+
}
|
|
78
|
+
const join = report.join;
|
|
79
|
+
if (join?.kind === 'invitation' && join.state === 'failed') {
|
|
80
|
+
return { containerId, outcome: 'failed', error: join.error, retryable: join.retryable };
|
|
81
|
+
}
|
|
82
|
+
return invitationResult(report.container);
|
|
83
|
+
}
|
|
84
|
+
async function requireOwner(node) {
|
|
85
|
+
if (!await node.isOwner())
|
|
86
|
+
throw new RentNodeError('not-an-owner');
|
|
87
|
+
}
|
|
88
|
+
async function rentByInvitation(node, client, options) {
|
|
89
|
+
report(options, 'inviting');
|
|
90
|
+
const { invitation, encoded } = await node.createCadreInvitation({ grantsOwner: false, uses: 1, expiresInMs: RENTAL_INVITATION_TTL_MS });
|
|
91
|
+
report(options, 'creating');
|
|
92
|
+
let container;
|
|
93
|
+
try {
|
|
94
|
+
container = await client.createContainer({ join: { kind: 'invitation', invitation: encoded }, profile: options.profile ?? 'storage' });
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
await withdrawQuietly(node, invitation.invite.key);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
log('container %s created with an invitation; it joins by itself', container.id);
|
|
101
|
+
report(options, 'done');
|
|
102
|
+
return invitationResult(container);
|
|
103
|
+
}
|
|
104
|
+
/** A create that failed leaves no live invitation behind; a withdrawal that fails too is logged, and the create's error is what the caller sees. */
|
|
105
|
+
async function withdrawQuietly(node, key) {
|
|
106
|
+
try {
|
|
107
|
+
await node.withdrawCadreInvitation(key);
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
log('withdrawing invitation %s after a failed create failed too: %o', key, error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function rentByClaim(node, client, options) {
|
|
114
|
+
report(options, 'creating');
|
|
115
|
+
const container = await client.createContainer({ join: { kind: 'claim' }, profile: options.profile ?? 'storage' });
|
|
116
|
+
log('container %s created for a claim', container.id);
|
|
117
|
+
return await claimContainer(node, client, container.id, options);
|
|
118
|
+
}
|
|
119
|
+
/** Poll for the node code, then claim; the second half of the claim path, shared with {@link finishRental}. */
|
|
120
|
+
async function claimContainer(node, client, containerId, options) {
|
|
121
|
+
report(options, 'waiting-for-code');
|
|
122
|
+
const { code, peerId } = await awaitNodeCode(client, containerId, options);
|
|
123
|
+
report(options, 'claiming');
|
|
124
|
+
// The claim is the owner's signature; the app asked the user before calling.
|
|
125
|
+
await node.claimNode(decodeNodeClaimPayload(code));
|
|
126
|
+
log('container %s claimed; its node is %s', containerId, peerId);
|
|
127
|
+
report(options, 'done');
|
|
128
|
+
return { containerId, join: 'claim', peerId };
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* `getNodeCode` every poll interval until it answers or the deadline passes. A container that
|
|
132
|
+
* dies meanwhile (`409 CONTAINER_GONE`) is `container-gone`, the same code `finishRental` gives
|
|
133
|
+
* one found dead, so an app handles both at one site.
|
|
134
|
+
*/
|
|
135
|
+
async function awaitNodeCode(client, containerId, options) {
|
|
136
|
+
const deadline = Date.now() + (options.nodeCodeDeadlineMs ?? DEFAULT_NODE_CODE_DEADLINE_MS);
|
|
137
|
+
const poll = options.nodeCodePollMs ?? NODE_CODE_POLL_MS;
|
|
138
|
+
for (;;) {
|
|
139
|
+
const answer = await requestNodeCode(client, containerId);
|
|
140
|
+
if (!('notReady' in answer))
|
|
141
|
+
return answer;
|
|
142
|
+
if (Date.now() >= deadline) {
|
|
143
|
+
log('container %s has no node code by the deadline', containerId);
|
|
144
|
+
throw new RentNodeError('node-not-ready', containerId);
|
|
145
|
+
}
|
|
146
|
+
await sleep(poll);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
async function requestNodeCode(client, containerId) {
|
|
150
|
+
try {
|
|
151
|
+
return await client.getNodeCode(containerId);
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
if (error instanceof ProviderApiError && error.code === 'CONTAINER_GONE')
|
|
155
|
+
throw new RentNodeError('container-gone', containerId);
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
async function finishClaim(node, client, report, options) {
|
|
160
|
+
const { container, join } = report;
|
|
161
|
+
if (join?.kind === 'claim' && join.state === 'claimed')
|
|
162
|
+
return judgeClaimed(node, report);
|
|
163
|
+
try {
|
|
164
|
+
return await claimContainer(node, client, container.id, options);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
// The node was claimed between the read and the code request; judge the fresh record.
|
|
168
|
+
if (error instanceof ProviderApiError && error.code === 'ALREADY_CLAIMED') {
|
|
169
|
+
return judgeClaimed(node, await client.getContainer(container.id));
|
|
170
|
+
}
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* A claimed container is this cadre's when the claim named this party.
|
|
176
|
+
*
|
|
177
|
+
* NOTE: the row `claimNode` writes after the node accepts is not re-run here, because the
|
|
178
|
+
* provider withholds the code once the claim is folded. An app killed between the node
|
|
179
|
+
* accepting and that one local insert has a node that belongs to this owner with no row for it
|
|
180
|
+
* on this machine, the window the React Native app's accepted tradeoff for cadre-host claims
|
|
181
|
+
* names; the remedy is to delete the container and rent again. Revisit if a device run sees it.
|
|
182
|
+
*/
|
|
183
|
+
function judgeClaimed(node, report) {
|
|
184
|
+
const { container, join } = report;
|
|
185
|
+
if (join?.kind !== 'claim' || join.state !== 'claimed')
|
|
186
|
+
throw new RentNodeError('container-gone', container.id);
|
|
187
|
+
if (join.partyId !== node.partyId) {
|
|
188
|
+
log('container %s was claimed into another party', container.id);
|
|
189
|
+
throw new RentNodeError('claimed-by-another', container.id);
|
|
190
|
+
}
|
|
191
|
+
return { containerId: container.id, join: 'claim', ...(container.peerId ? { peerId: container.peerId } : {}) };
|
|
192
|
+
}
|
|
193
|
+
/** Which path a container is on; refuses one that is gone or was never a rental. */
|
|
194
|
+
function joinKindOf(report, containerId) {
|
|
195
|
+
const { container, join } = report;
|
|
196
|
+
if (container.status === 'stopped' || container.status === 'error')
|
|
197
|
+
throw new RentNodeError('container-gone', containerId);
|
|
198
|
+
// The join state is folded from the node's first `/status` read, so a container still
|
|
199
|
+
// starting carries only the kind it was created with.
|
|
200
|
+
const kind = join?.kind ?? container.join?.kind;
|
|
201
|
+
if (kind === undefined)
|
|
202
|
+
throw new RentNodeError('not-a-rental', containerId);
|
|
203
|
+
return kind;
|
|
204
|
+
}
|
|
205
|
+
function invitationResult(container) {
|
|
206
|
+
return { containerId: container.id, join: 'invitation', ...(container.peerId ? { peerId: container.peerId } : {}) };
|
|
207
|
+
}
|
|
208
|
+
function report(options, step) {
|
|
209
|
+
try {
|
|
210
|
+
options.onProgress?.(step);
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
log('onProgress(%s) threw; ignored: %o', step, error);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function sleep(ms) {
|
|
217
|
+
// A plain timer, not an unref'd one: a Node process renting from a script must stay up for it.
|
|
218
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=rent-node.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rent-node.js","sourceRoot":"","sources":["../../src/provider/rent-node.ts"],"names":[],"mappings":"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"}
|
package/dist/relay-addrs.d.ts
CHANGED
|
@@ -50,7 +50,10 @@
|
|
|
50
50
|
* only once the relay-hop protocol id is in that peer's peer-store protocol list,
|
|
51
51
|
* and that list is written exclusively by IDENTIFY — which `@optimystic/db-p2p`
|
|
52
52
|
* namespaces per network, so a cadre node and a stock relay never identify each
|
|
53
|
-
* other.
|
|
53
|
+
* other. A node with configured relays is built with that discovery switched off
|
|
54
|
+
* altogether when it has a transport list to wrap (`node-transports.ts`), because it
|
|
55
|
+
* CAN reach a relay-capable peer in its own namespace and take the configured relay's
|
|
56
|
+
* slot. See `relay-reservation.ts`'s module doc and `docs/architecture.md`.
|
|
54
57
|
*/
|
|
55
58
|
import type { NetworkConfig } from './types.js';
|
|
56
59
|
import type { RelayReservationState } from './relay-reservation.js';
|
|
@@ -136,9 +139,11 @@ export interface ListenTransportOptions {
|
|
|
136
139
|
*
|
|
137
140
|
* - **WebSocket is DERIVED.** It is the one non-TCP transport the shipped configs need
|
|
138
141
|
* (the React Native reference app's companion drone; `cadre start --ws-port`), and
|
|
139
|
-
* `@optimystic/db-p2p` already knows how to add it
|
|
140
|
-
*
|
|
141
|
-
* (
|
|
142
|
+
* `@optimystic/db-p2p` already knows how to add it, so `cadre-core` flips db-p2p's own
|
|
143
|
+
* switch ({@link WS_TRANSPORT_SWITCH_PORT}). A node with configured relays is the one
|
|
144
|
+
* case where cadre passes the transport list itself (`node-transports.ts`, to switch
|
|
145
|
+
* relay discovery off); when the embedder gave none, that list comes from
|
|
146
|
+
* `network.defaultTransports`, which reads the same switch.
|
|
142
147
|
* - **Everything else is REFUSED.** Deriving `/quic-v1`, `/webrtc` or `/webtransport`
|
|
143
148
|
* would mean importing transport packages into every `cadre-core` consumer including
|
|
144
149
|
* the React Native and browser bundles (against the cross-platform rule in
|
package/dist/relay-addrs.js
CHANGED
|
@@ -50,7 +50,10 @@
|
|
|
50
50
|
* only once the relay-hop protocol id is in that peer's peer-store protocol list,
|
|
51
51
|
* and that list is written exclusively by IDENTIFY — which `@optimystic/db-p2p`
|
|
52
52
|
* namespaces per network, so a cadre node and a stock relay never identify each
|
|
53
|
-
* other.
|
|
53
|
+
* other. A node with configured relays is built with that discovery switched off
|
|
54
|
+
* altogether when it has a transport list to wrap (`node-transports.ts`), because it
|
|
55
|
+
* CAN reach a relay-capable peer in its own namespace and take the configured relay's
|
|
56
|
+
* slot. See `relay-reservation.ts`'s module doc and `docs/architecture.md`.
|
|
54
57
|
*/
|
|
55
58
|
import { multiaddr } from '@multiformats/multiaddr';
|
|
56
59
|
import { circuitRelayTargetOrThrow } from './delegate-admission.js';
|
|
@@ -169,9 +172,11 @@ const WS_TRANSPORT_SWITCH_PORT = 0;
|
|
|
169
172
|
*
|
|
170
173
|
* - **WebSocket is DERIVED.** It is the one non-TCP transport the shipped configs need
|
|
171
174
|
* (the React Native reference app's companion drone; `cadre start --ws-port`), and
|
|
172
|
-
* `@optimystic/db-p2p` already knows how to add it
|
|
173
|
-
*
|
|
174
|
-
* (
|
|
175
|
+
* `@optimystic/db-p2p` already knows how to add it, so `cadre-core` flips db-p2p's own
|
|
176
|
+
* switch ({@link WS_TRANSPORT_SWITCH_PORT}). A node with configured relays is the one
|
|
177
|
+
* case where cadre passes the transport list itself (`node-transports.ts`, to switch
|
|
178
|
+
* relay discovery off); when the embedder gave none, that list comes from
|
|
179
|
+
* `network.defaultTransports`, which reads the same switch.
|
|
175
180
|
* - **Everything else is REFUSED.** Deriving `/quic-v1`, `/webrtc` or `/webtransport`
|
|
176
181
|
* would mean importing transport packages into every `cadre-core` consumer including
|
|
177
182
|
* the React Native and browser bundles (against the cross-platform rule in
|
package/dist/relay-addrs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relay-addrs.js","sourceRoot":"","sources":["../src/relay-addrs.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"relay-addrs.js","sourceRoot":"","sources":["../src/relay-addrs.ts"],"names":[],"mappings":"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"}
|
|
@@ -73,12 +73,33 @@
|
|
|
73
73
|
* relay's address. So it asks the relay for a slot directly, via the
|
|
74
74
|
* circuit-relay transport's own reservation store.
|
|
75
75
|
*
|
|
76
|
+
* ⚠️ WHY LIBP2P'S RELAY DISCOVERY IS SWITCHED OFF. Discovery does reach SOME
|
|
77
|
+
* peers: every connected peer whose identify lists the relay-hop protocol, and a
|
|
78
|
+
* storage-profile member's strand node runs the relay server in the same
|
|
79
|
+
* `/optimystic/strand-<id>/…` namespace as this node's strand node (control nodes
|
|
80
|
+
* share `/optimystic/control-<partyId>/…` the same way). Discovery reserves on such
|
|
81
|
+
* a peer as soon as it connects, and that takes one of the slots the configured
|
|
82
|
+
* relays' supervisors were going to fill. Worse, the reservation store runs one
|
|
83
|
+
* request at a time and, when a request takes the last free slot, clears its queue
|
|
84
|
+
* with `@libp2p/utils`' `Queue.clear()`, which drops a queued job WITHOUT ever
|
|
85
|
+
* settling its promise — so a supervisor whose `addRelay` was queued behind it
|
|
86
|
+
* waits out its whole deadline (gotchoices/sereus#34). A reservation discovery picks is
|
|
87
|
+
* also unmanaged: no supervisor re-drives it, the status does not count it, and a
|
|
88
|
+
* member's strand node goes away whenever that member hibernates. So every node
|
|
89
|
+
* whose bare search entries cadre generated for configured relays is built through
|
|
90
|
+
* {@link withoutRelayDiscovery} (`node-transports.ts`), and its slots are filled by
|
|
91
|
+
* the supervisors alone — given a transport list to wrap, which React Native and
|
|
92
|
+
* browser embedders always pass and a Node host passes as
|
|
93
|
+
* `network.defaultTransports` (cadre-cli does). A hand-written bare `/p2p-circuit`
|
|
94
|
+
* with no configured relay keeps discovery, since nothing else would fill that slot.
|
|
95
|
+
*
|
|
76
96
|
* Status is derived from the node's LIVE multiaddrs on every read rather than
|
|
77
97
|
* cached at drive time: when a relay restarts or the connection drops, libp2p's
|
|
78
98
|
* listener clears its listening addrs and the circuit multiaddr disappears, so a
|
|
79
99
|
* cached snapshot would keep claiming `reserved` for a node nothing can dial.
|
|
80
100
|
*
|
|
81
|
-
* And because discovery is
|
|
101
|
+
* And because discovery is either off or blind to the relay, nothing would ever
|
|
102
|
+
* ASK again either —
|
|
82
103
|
* so {@link driveRelayReservation} stays a single-shot primitive and
|
|
83
104
|
* {@link superviseRelayReservation} owns the retry cadence on top of it. That
|
|
84
105
|
* loop is what makes a lost reservation recover without a page reload. Single-shot
|
|
@@ -87,20 +108,20 @@
|
|
|
87
108
|
* logs failures that read as real and — through the timers it still holds — keeps
|
|
88
109
|
* the process alive for the rest of its `timeoutMs`.
|
|
89
110
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* mechanisms in production, one guarantee; the specs disable discovery's half
|
|
111
|
+
* What switching discovery off costs: libp2p 3.1.3's `connection.newStream` records
|
|
112
|
+
* every protocol OUR OWN outbound stream negotiated into the peer store, so the
|
|
113
|
+
* explicit reservation request writes the relay-hop protocol id against the relay
|
|
114
|
+
* itself, and with discovery on it refilled a slot freed by a hangup (while the
|
|
115
|
+
* relay stays up) within ~25 ms, measured. With it off that refill waits for the
|
|
116
|
+
* supervisor's next liveness check ({@link DEFAULT_RELAY_CHECK_MS}, 5 s while held).
|
|
117
|
+
* A relay RESTART always needed the supervisor, which un-poisons the store's
|
|
118
|
+
* `relayFilter` before every attempt ({@link clearRelayFilterEntry}). The specs
|
|
119
|
+
* that build their own clients keep discovery on and disable its refill
|
|
100
120
|
* (`forgetRelayProtocol` in `relay-reservation.spec.ts`) so they prove the
|
|
101
121
|
* supervisor's.
|
|
102
122
|
*/
|
|
103
123
|
import type { Libp2p, PeerId } from '@libp2p/interface';
|
|
124
|
+
import type { Libp2pTransports } from '@optimystic/db-p2p';
|
|
104
125
|
/**
|
|
105
126
|
* Relay-reservation posture for a node that reserves through the SEARCH listen
|
|
106
127
|
* addr (bare `/p2p-circuit`) — every cadre control node, whether its relays came
|
|
@@ -232,6 +253,19 @@ export declare function circuitMultiaddrsVia(node: Libp2p, addrs: readonly strin
|
|
|
232
253
|
* module to the discovery wait that never fires.
|
|
233
254
|
*/
|
|
234
255
|
export declare function findCircuitRelayTransport(node: Libp2p): CircuitRelayTransportLike | null;
|
|
256
|
+
/**
|
|
257
|
+
* `transports` with libp2p's relay discovery switched off in the circuit-relay transport any of
|
|
258
|
+
* them builds, for a node whose bare `/p2p-circuit` slots cadre's own supervisors fill (see the
|
|
259
|
+
* module header, "WHY LIBP2P'S RELAY DISCOVERY IS SWITCHED OFF"). Every other transport is built
|
|
260
|
+
* untouched.
|
|
261
|
+
*
|
|
262
|
+
* Each factory is wrapped rather than the built node patched afterwards: libp2p calls the
|
|
263
|
+
* factories in its constructor, and `@optimystic/db-p2p` builds with `start: false`, so the
|
|
264
|
+
* switch lands before anything starts. Patching the running node instead races — db-p2p starts
|
|
265
|
+
* the node before `createLibp2pNode` returns, and a cohort-seed connection in that window has
|
|
266
|
+
* already queued a discovered reservation.
|
|
267
|
+
*/
|
|
268
|
+
export declare function withoutRelayDiscovery(transports: Libp2pTransports): Libp2pTransports;
|
|
235
269
|
/**
|
|
236
270
|
* Dial every relay in `addrs`, ask the first one that answers for a reservation
|
|
237
271
|
* slot, then wait until a `/p2p-circuit` address appears.
|
|
@@ -317,8 +351,9 @@ export interface RelayReservationSupervisor {
|
|
|
317
351
|
* it still is — the thing that makes a lost reservation recover on its own.
|
|
318
352
|
*
|
|
319
353
|
* Needed because nothing else re-drives: libp2p re-fills a search listener's
|
|
320
|
-
* freed slot from relay DISCOVERY,
|
|
321
|
-
*
|
|
354
|
+
* freed slot from relay DISCOVERY, which cannot see a stock-identify relay from a
|
|
355
|
+
* cadre node's namespaced identify, and which is switched off outright on every
|
|
356
|
+
* node whose slots cadre supervises (see the module header). Without this loop a
|
|
322
357
|
* relay restart left a browser tab undialable until the user reloaded the page.
|
|
323
358
|
*
|
|
324
359
|
* Per tick:
|
|
@@ -378,9 +413,9 @@ export declare function clearRelayFilterEntry(store: RelayReservationStoreLike,
|
|
|
378
413
|
* NOBODY IS GOING TO TRY AGAIN. A lost reservation does come back on its own now,
|
|
379
414
|
* as long as a {@link RelayReservationSupervisor} is running — that loop is the
|
|
380
415
|
* only thing that re-drives, since libp2p re-fills a search listener's freed slot
|
|
381
|
-
* from relay DISCOVERY, which a
|
|
382
|
-
*
|
|
383
|
-
* stopped, was never started, or there is no node at all.
|
|
416
|
+
* from relay DISCOVERY, which cannot see a stock-identify relay and is switched off
|
|
417
|
+
* on every node cadre supervises (see the module header). So `error` means the
|
|
418
|
+
* supervisor was stopped, was never started, or there is no node at all.
|
|
384
419
|
*
|
|
385
420
|
* `retryAtMs` is the supervisor's next scheduled tick (`null` while a drive runs
|
|
386
421
|
* or when there is no supervisor); it both selects `retrying` and is passed
|
|
@@ -73,12 +73,33 @@
|
|
|
73
73
|
* relay's address. So it asks the relay for a slot directly, via the
|
|
74
74
|
* circuit-relay transport's own reservation store.
|
|
75
75
|
*
|
|
76
|
+
* ⚠️ WHY LIBP2P'S RELAY DISCOVERY IS SWITCHED OFF. Discovery does reach SOME
|
|
77
|
+
* peers: every connected peer whose identify lists the relay-hop protocol, and a
|
|
78
|
+
* storage-profile member's strand node runs the relay server in the same
|
|
79
|
+
* `/optimystic/strand-<id>/…` namespace as this node's strand node (control nodes
|
|
80
|
+
* share `/optimystic/control-<partyId>/…` the same way). Discovery reserves on such
|
|
81
|
+
* a peer as soon as it connects, and that takes one of the slots the configured
|
|
82
|
+
* relays' supervisors were going to fill. Worse, the reservation store runs one
|
|
83
|
+
* request at a time and, when a request takes the last free slot, clears its queue
|
|
84
|
+
* with `@libp2p/utils`' `Queue.clear()`, which drops a queued job WITHOUT ever
|
|
85
|
+
* settling its promise — so a supervisor whose `addRelay` was queued behind it
|
|
86
|
+
* waits out its whole deadline (gotchoices/sereus#34). A reservation discovery picks is
|
|
87
|
+
* also unmanaged: no supervisor re-drives it, the status does not count it, and a
|
|
88
|
+
* member's strand node goes away whenever that member hibernates. So every node
|
|
89
|
+
* whose bare search entries cadre generated for configured relays is built through
|
|
90
|
+
* {@link withoutRelayDiscovery} (`node-transports.ts`), and its slots are filled by
|
|
91
|
+
* the supervisors alone — given a transport list to wrap, which React Native and
|
|
92
|
+
* browser embedders always pass and a Node host passes as
|
|
93
|
+
* `network.defaultTransports` (cadre-cli does). A hand-written bare `/p2p-circuit`
|
|
94
|
+
* with no configured relay keeps discovery, since nothing else would fill that slot.
|
|
95
|
+
*
|
|
76
96
|
* Status is derived from the node's LIVE multiaddrs on every read rather than
|
|
77
97
|
* cached at drive time: when a relay restarts or the connection drops, libp2p's
|
|
78
98
|
* listener clears its listening addrs and the circuit multiaddr disappears, so a
|
|
79
99
|
* cached snapshot would keep claiming `reserved` for a node nothing can dial.
|
|
80
100
|
*
|
|
81
|
-
* And because discovery is
|
|
101
|
+
* And because discovery is either off or blind to the relay, nothing would ever
|
|
102
|
+
* ASK again either —
|
|
82
103
|
* so {@link driveRelayReservation} stays a single-shot primitive and
|
|
83
104
|
* {@link superviseRelayReservation} owns the retry cadence on top of it. That
|
|
84
105
|
* loop is what makes a lost reservation recover without a page reload. Single-shot
|
|
@@ -87,23 +108,22 @@
|
|
|
87
108
|
* logs failures that read as real and — through the timers it still holds — keeps
|
|
88
109
|
* the process alive for the rest of its `timeoutMs`.
|
|
89
110
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* mechanisms in production, one guarantee; the specs disable discovery's half
|
|
111
|
+
* What switching discovery off costs: libp2p 3.1.3's `connection.newStream` records
|
|
112
|
+
* every protocol OUR OWN outbound stream negotiated into the peer store, so the
|
|
113
|
+
* explicit reservation request writes the relay-hop protocol id against the relay
|
|
114
|
+
* itself, and with discovery on it refilled a slot freed by a hangup (while the
|
|
115
|
+
* relay stays up) within ~25 ms, measured. With it off that refill waits for the
|
|
116
|
+
* supervisor's next liveness check ({@link DEFAULT_RELAY_CHECK_MS}, 5 s while held).
|
|
117
|
+
* A relay RESTART always needed the supervisor, which un-poisons the store's
|
|
118
|
+
* `relayFilter` before every attempt ({@link clearRelayFilterEntry}). The specs
|
|
119
|
+
* that build their own clients keep discovery on and disable its refill
|
|
100
120
|
* (`forgetRelayProtocol` in `relay-reservation.spec.ts`) so they prove the
|
|
101
121
|
* supervisor's.
|
|
102
122
|
*/
|
|
103
123
|
import debug from 'debug';
|
|
104
124
|
import { multiaddr } from '@multiformats/multiaddr';
|
|
105
125
|
import { peerIdFromString } from '@libp2p/peer-id';
|
|
106
|
-
import { trailingPeerId } from './peer-
|
|
126
|
+
import { trailingPeerId } from './peer-dial.js';
|
|
107
127
|
import { relayReservationBudgetMs } from './link-budget.js';
|
|
108
128
|
const log = debug('sereus:cadre:relay-reservation');
|
|
109
129
|
/**
|
|
@@ -218,6 +238,54 @@ function isReservationStore(value) {
|
|
|
218
238
|
const store = value;
|
|
219
239
|
return typeof store.addRelay === 'function' && typeof store.hasReservation === 'function';
|
|
220
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* `transports` with libp2p's relay discovery switched off in the circuit-relay transport any of
|
|
243
|
+
* them builds, for a node whose bare `/p2p-circuit` slots cadre's own supervisors fill (see the
|
|
244
|
+
* module header, "WHY LIBP2P'S RELAY DISCOVERY IS SWITCHED OFF"). Every other transport is built
|
|
245
|
+
* untouched.
|
|
246
|
+
*
|
|
247
|
+
* Each factory is wrapped rather than the built node patched afterwards: libp2p calls the
|
|
248
|
+
* factories in its constructor, and `@optimystic/db-p2p` builds with `start: false`, so the
|
|
249
|
+
* switch lands before anything starts. Patching the running node instead races — db-p2p starts
|
|
250
|
+
* the node before `createLibp2pNode` returns, and a cohort-seed connection in that window has
|
|
251
|
+
* already queued a discovered reservation.
|
|
252
|
+
*/
|
|
253
|
+
export function withoutRelayDiscovery(transports) {
|
|
254
|
+
return transports.map((factory) => (components) => switchOffRelayDiscovery(factory(components)));
|
|
255
|
+
}
|
|
256
|
+
/** Logged once per process, not per node: every node in it runs the same `@libp2p/circuit-relay-v2`. */
|
|
257
|
+
let reportedDiscoveryFieldMissing = false;
|
|
258
|
+
/**
|
|
259
|
+
* Clear `transport`'s `discovery` when it is the circuit-relay transport — duck-typed by its
|
|
260
|
+
* reservation store, as {@link findCircuitRelayTransport} finds it.
|
|
261
|
+
*
|
|
262
|
+
* `@libp2p/circuit-relay-v2` 4.2.13 has no init option for this, but declares the field optional
|
|
263
|
+
* and guards every use (`this.discovery?.startDiscovery()`, `start`/`stop` skip a non-startable
|
|
264
|
+
* value), so `undefined` before `start()` registers no relay-hop topology and never runs a
|
|
265
|
+
* peer-store scan.
|
|
266
|
+
*/
|
|
267
|
+
function switchOffRelayDiscovery(transport) {
|
|
268
|
+
if (typeof transport !== 'object' || transport === null) {
|
|
269
|
+
return transport;
|
|
270
|
+
}
|
|
271
|
+
const candidate = transport;
|
|
272
|
+
if (!isReservationStore(candidate.reservationStore)) {
|
|
273
|
+
return transport;
|
|
274
|
+
}
|
|
275
|
+
if (!Object.prototype.hasOwnProperty.call(candidate, 'discovery')) {
|
|
276
|
+
// Not thrown: a node with discovery left on still works, it is only exposed to the slot theft
|
|
277
|
+
// again — which the `withoutRelayDiscovery` spec in `relay-reservation.spec.ts` reproduces.
|
|
278
|
+
if (!reportedDiscoveryFieldMissing) {
|
|
279
|
+
reportedDiscoveryFieldMissing = true;
|
|
280
|
+
console.warn('cadre: the circuit-relay transport has no `discovery` field to switch off — the ' +
|
|
281
|
+
'@libp2p/circuit-relay-v2 shape changed. libp2p relay discovery stays on, and it can take a ' +
|
|
282
|
+
'configured relay\'s reservation slot.');
|
|
283
|
+
}
|
|
284
|
+
return transport;
|
|
285
|
+
}
|
|
286
|
+
candidate.discovery = undefined;
|
|
287
|
+
return transport;
|
|
288
|
+
}
|
|
221
289
|
/** Sentinel resolved by the wait arm of {@link requestOneReservation}'s race. */
|
|
222
290
|
const WAIT_OVER = Symbol('relay-reservation-wait-over');
|
|
223
291
|
/**
|
|
@@ -314,10 +382,12 @@ export async function driveRelayReservation(node, addrs, opts) {
|
|
|
314
382
|
const { connected, error: dialError } = await dialRelays(node, addrs, deadline, signal);
|
|
315
383
|
const attempt = await requestReservation(node, connected, deadline, signal);
|
|
316
384
|
// NOTE: a REJECTED reservation still spends the rest of the deadline polling,
|
|
317
|
-
// because
|
|
318
|
-
// `
|
|
319
|
-
//
|
|
320
|
-
//
|
|
385
|
+
// because on a node that kept libp2p's relay discovery (a hand-written bare
|
|
386
|
+
// `/p2p-circuit` driven through `reserveRelays`) discovery may independently land
|
|
387
|
+
// one (only the no-transport case is `fatal`). So a misconfigured node reports its
|
|
388
|
+
// now-legible reason a full `timeoutMs` late. If startup latency on that path ever
|
|
389
|
+
// matters, shorten the wait once every connected relay has rejected — do not skip
|
|
390
|
+
// it outright.
|
|
321
391
|
if (!attempt.fatal && (await waitForCircuitReservation(node, addrs, deadline, pollMs, signal))) {
|
|
322
392
|
return { error: null, cancelled: false };
|
|
323
393
|
}
|
|
@@ -410,9 +480,10 @@ function firstDialError(addrs, results) {
|
|
|
410
480
|
*
|
|
411
481
|
* Sequential, not concurrent: a bare `/p2p-circuit` listener registers exactly
|
|
412
482
|
* ONE pending reservation, so a second concurrent `addRelay` would be rejected
|
|
413
|
-
* with `HadEnoughRelaysError` — noise, not a failure.
|
|
414
|
-
* conflict
|
|
415
|
-
*
|
|
483
|
+
* with `HadEnoughRelaysError` — noise, not a failure. Discovery naming the SAME
|
|
484
|
+
* relay is not a conflict (`addRelay` dedupes through its own peer queue); discovery
|
|
485
|
+
* naming a DIFFERENT peer is, which is why it is off on every node cadre supervises
|
|
486
|
+
* (module header).
|
|
416
487
|
*/
|
|
417
488
|
async function requestReservation(node, connected, deadline, signal) {
|
|
418
489
|
if (connected.length === 0) {
|
|
@@ -459,6 +530,17 @@ async function requestReservation(node, connected, deadline, signal) {
|
|
|
459
530
|
* race settling some other way, aborted in the `finally`. Out-racing the deadline
|
|
460
531
|
* timer is not enough: the loser stays pending, and a pending timer is what holds
|
|
461
532
|
* a Node process open past the stop that asked for it.
|
|
533
|
+
*
|
|
534
|
+
* NOTE: the reservation store's `Queue.clear()` drops a queued job unsettled whenever
|
|
535
|
+
* a reservation takes the last pending slot. With relay discovery off, the slots never
|
|
536
|
+
* outnumber the supervised relays, so a first request cannot be dropped that way. A
|
|
537
|
+
* REFRESH of a held reservation that is queued at the moment another relay's first
|
|
538
|
+
* reservation takes the last slot still is, and that reservation then lapses at the
|
|
539
|
+
* relay unrefreshed. If that is ever observed, or anything besides the supervisors
|
|
540
|
+
* gains a way to fill these slots, also end this wait on the store's
|
|
541
|
+
* `relay:found-enough-relays` event while this relay holds no reservation. The real
|
|
542
|
+
* fix — `#checkReservationCount` aborting queued jobs instead of clearing them —
|
|
543
|
+
* belongs to js-libp2p.
|
|
462
544
|
*/
|
|
463
545
|
async function requestOneReservation(store, relay, deadline, signal) {
|
|
464
546
|
const waitOver = new AbortController();
|
|
@@ -554,8 +636,9 @@ export const DEFAULT_RELAY_MAX_BACKOFF_MS = 60000;
|
|
|
554
636
|
* it still is — the thing that makes a lost reservation recover on its own.
|
|
555
637
|
*
|
|
556
638
|
* Needed because nothing else re-drives: libp2p re-fills a search listener's
|
|
557
|
-
* freed slot from relay DISCOVERY,
|
|
558
|
-
*
|
|
639
|
+
* freed slot from relay DISCOVERY, which cannot see a stock-identify relay from a
|
|
640
|
+
* cadre node's namespaced identify, and which is switched off outright on every
|
|
641
|
+
* node whose slots cadre supervises (see the module header). Without this loop a
|
|
559
642
|
* relay restart left a browser tab undialable until the user reloaded the page.
|
|
560
643
|
*
|
|
561
644
|
* Per tick:
|
|
@@ -815,9 +898,9 @@ class RelayReservationLoop {
|
|
|
815
898
|
* NOBODY IS GOING TO TRY AGAIN. A lost reservation does come back on its own now,
|
|
816
899
|
* as long as a {@link RelayReservationSupervisor} is running — that loop is the
|
|
817
900
|
* only thing that re-drives, since libp2p re-fills a search listener's freed slot
|
|
818
|
-
* from relay DISCOVERY, which a
|
|
819
|
-
*
|
|
820
|
-
* stopped, was never started, or there is no node at all.
|
|
901
|
+
* from relay DISCOVERY, which cannot see a stock-identify relay and is switched off
|
|
902
|
+
* on every node cadre supervises (see the module header). So `error` means the
|
|
903
|
+
* supervisor was stopped, was never started, or there is no node at all.
|
|
821
904
|
*
|
|
822
905
|
* `retryAtMs` is the supervisor's next scheduled tick (`null` while a drive runs
|
|
823
906
|
* or when there is no supervisor); it both selects `retrying` and is passed
|