@serfab/cadre-core 1.13.0 → 1.14.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/dist/cadre-invite-protocol.d.ts +348 -0
- package/dist/cadre-invite-protocol.js +605 -0
- package/dist/cadre-invite-protocol.js.map +1 -0
- package/dist/cadre-node.d.ts +523 -180
- package/dist/cadre-node.js +921 -293
- package/dist/cadre-node.js.map +1 -1
- package/dist/claim-proof.d.ts +45 -0
- package/dist/claim-proof.js +132 -0
- package/dist/claim-proof.js.map +1 -0
- package/dist/control-authorization.d.ts +80 -19
- package/dist/control-authorization.js +55 -13
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.js +1 -1
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +514 -97
- package/dist/control-database.js +1033 -195
- package/dist/control-database.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +142 -0
- package/dist/control-protocol-guard.js +293 -0
- package/dist/control-protocol-guard.js.map +1 -0
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +801 -1067
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +4 -0
- package/dist/control-stream.js +22 -7
- package/dist/control-stream.js.map +1 -1
- package/dist/delegate-admission.d.ts +1 -1
- package/dist/delegate-admission.js +1 -1
- package/dist/device-token.d.ts +11 -9
- package/dist/device-token.js +14 -12
- package/dist/device-token.js.map +1 -1
- package/dist/ed25519-key.d.ts +15 -0
- package/dist/ed25519-key.js +28 -1
- package/dist/ed25519-key.js.map +1 -1
- package/dist/index.d.ts +14 -9
- package/dist/index.js +27 -9
- package/dist/index.js.map +1 -1
- package/dist/link-budget.d.ts +16 -29
- package/dist/link-budget.js +16 -31
- package/dist/link-budget.js.map +1 -1
- package/dist/membership-connection-gater.d.ts +190 -151
- package/dist/membership-connection-gater.js +235 -173
- package/dist/membership-connection-gater.js.map +1 -1
- package/dist/node-claim-payload.d.ts +19 -0
- package/dist/node-claim-payload.js +120 -0
- package/dist/node-claim-payload.js.map +1 -0
- package/dist/owner-anchor-sync.d.ts +22 -0
- package/dist/owner-anchor-sync.js +143 -0
- package/dist/owner-anchor-sync.js.map +1 -0
- package/dist/peer-authorization.d.ts +113 -18
- package/dist/peer-authorization.js +225 -29
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-dial.d.ts +19 -0
- package/dist/peer-dial.js +22 -1
- package/dist/peer-dial.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +26 -2
- package/dist/peer-join-backfill.js +54 -12
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/peer-record.d.ts +13 -9
- package/dist/peer-record.js +16 -12
- package/dist/peer-record.js.map +1 -1
- package/dist/pending-join-runner.d.ts +66 -58
- package/dist/pending-join-runner.js +136 -145
- package/dist/pending-join-runner.js.map +1 -1
- package/dist/push-fanout.d.ts +8 -2
- package/dist/push-fanout.js +8 -2
- package/dist/push-fanout.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +147 -122
- package/dist/seed-bootstrap.js +284 -269
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.d.ts +98 -37
- package/dist/seed-trust-policy.js +188 -2
- package/dist/seed-trust-policy.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +24 -26
- package/dist/strand-addr-protocol.js +19 -26
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-formation-protocol.js +1 -1
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-revocation-enforcer.d.ts +11 -11
- package/dist/strand-revocation-enforcer.js +11 -11
- package/dist/strand-wake-protocol.d.ts +13 -16
- package/dist/strand-wake-protocol.js +13 -20
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/trusted-owner-store.d.ts +35 -5
- package/dist/trusted-owner-store.js +50 -8
- package/dist/trusted-owner-store.js.map +1 -1
- package/dist/types.d.ts +302 -87
- package/dist/types.js.map +1 -1
- package/package.json +8 -7
- package/src/cadre-invite-protocol.ts +864 -0
- package/src/cadre-node.ts +9381 -8686
- package/src/claim-proof.ts +147 -0
- package/src/control-authorization.ts +203 -117
- package/src/control-cohort.ts +1 -1
- package/src/control-database.ts +1216 -218
- package/src/control-protocol-guard.ts +348 -0
- package/src/control-schema.ts +812 -1078
- package/src/control-stream.ts +26 -7
- package/src/delegate-admission.ts +1 -1
- package/src/device-token.ts +14 -10
- package/src/ed25519-key.ts +28 -1
- package/src/index.ts +84 -10
- package/src/link-budget.ts +16 -32
- package/src/membership-connection-gater.ts +637 -570
- package/src/node-claim-payload.ts +125 -0
- package/src/owner-anchor-sync.ts +171 -0
- package/src/peer-authorization.ts +258 -29
- package/src/peer-dial.ts +22 -1
- package/src/peer-join-backfill.ts +56 -11
- package/src/peer-record.ts +16 -11
- package/src/pending-join-runner.ts +729 -735
- package/src/push-fanout.ts +8 -2
- package/src/seed-bootstrap.ts +344 -321
- package/src/seed-trust-policy.ts +353 -140
- package/src/strand-addr-protocol.ts +26 -35
- package/src/strand-formation-protocol.ts +1 -1
- package/src/strand-revocation-enforcer.ts +11 -11
- package/src/strand-wake-protocol.ts +13 -23
- package/src/trusted-owner-store.ts +242 -171
- package/src/types.ts +2471 -2242
|
@@ -0,0 +1,605 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cadre invitation redemption transport: `/sereus/cadre-invite/1.0.0`.
|
|
3
|
+
*
|
|
4
|
+
* A device holding a cadre invitation (an owner-signed `CadreInvite` row plus the private
|
|
5
|
+
* half of the invitation keypair, see {@link CadreInvitation}) dials ANY member machine the
|
|
6
|
+
* invitation names and proves possession; the member seats the row if it does not hold it
|
|
7
|
+
* yet, writes the device's `CadrePeer` row (and `OwnerKey` row, when the invitation grants
|
|
8
|
+
* ownership) through `ControlDatabase.redeemCadreInvite`, and answers with the party id,
|
|
9
|
+
* the row and dial hints. The owner that minted the invitation need not be online.
|
|
10
|
+
*
|
|
11
|
+
* Same shape as the seed and wake protocols: one length-prefixed JSON frame each way over
|
|
12
|
+
* the shared primitives in `control-stream.ts`. Stranger-facing by design — the device is
|
|
13
|
+
* not a member when it dials — so the connection gate admits a stranger while a live
|
|
14
|
+
* invitation exists (`membership-connection-gater.ts`), and the handler's own trust
|
|
15
|
+
* decision is the proof of possession: nothing touches the database for a caller that
|
|
16
|
+
* cannot prove it holds the invitation and is the identity it names.
|
|
17
|
+
*
|
|
18
|
+
* The seed protocol is left untouched so `SeedMessage` can grow independently.
|
|
19
|
+
*/
|
|
20
|
+
import debug from 'debug';
|
|
21
|
+
import { multiaddr } from '@multiformats/multiaddr';
|
|
22
|
+
import { toString as uint8ArrayToString, fromString as uint8ArrayFromString } from 'uint8arrays';
|
|
23
|
+
import { sign } from '@optimystic/quereus-plugin-crypto';
|
|
24
|
+
import { withDeadline, exchangeFrame, readStreamToEnd, replyAndClose } from './control-stream.js';
|
|
25
|
+
import { decodeLengthPrefixedFrame } from './seed-bootstrap.js';
|
|
26
|
+
import { relayedRequestBudgetMs } from './link-budget.js';
|
|
27
|
+
import { cadreInviteRedeemMessage, cadreInviteConsentMessage, CadreInviteIssuerUnknownError, InvitationExhaustedError } from './control-database.js';
|
|
28
|
+
import { verifyCadreInviteRow, verifyCadreInviteRedemption } from './peer-authorization.js';
|
|
29
|
+
import { ed25519PublicKeyB64FromPeerId, requireEd25519PublicKeyB64 } from './ed25519-key.js';
|
|
30
|
+
import { trailingPeerId } from './peer-record.js';
|
|
31
|
+
import { nextMacrotask } from './peer-dial.js';
|
|
32
|
+
import { chainMessages } from './control-retry.js';
|
|
33
|
+
import { isRetriableControlWriteFailure } from './control-write-retry.js';
|
|
34
|
+
const log = debug('sereus:cadre:invite-proto');
|
|
35
|
+
/** Protocol id for cadre invitation redemption (parallel to `/sereus/seed/1.0.0`). */
|
|
36
|
+
export const CADRE_INVITE_PROTOCOL = '/sereus/cadre-invite/1.0.0';
|
|
37
|
+
/**
|
|
38
|
+
* Cap on either frame. A request is one signed row, two signatures and a few addresses;
|
|
39
|
+
* a reply adds a peer list of a cadre's size. Both fit in a few KB; 64 KiB is the
|
|
40
|
+
* defensive bound a stranger can make the handler buffer.
|
|
41
|
+
*/
|
|
42
|
+
const MAX_CADRE_INVITE_MSG_SIZE = 64 * 1024;
|
|
43
|
+
/**
|
|
44
|
+
* Default time the member waits for the request frame before aborting the read (ms). The
|
|
45
|
+
* read only: a stranger opens the stream, so the bound is on a caller that opens one and
|
|
46
|
+
* never sends. Same figure as the seed handler's.
|
|
47
|
+
*/
|
|
48
|
+
// eslint-disable-next-line no-restricted-syntax -- link-independent: a receiver cap on one frame on a stream the peer already opened; it bounds a peer that opens a stream and never sends, not the dial
|
|
49
|
+
const DEFAULT_REDEEM_READ_TIMEOUT_MS = 10000;
|
|
50
|
+
/** Default cap on concurrent inbound redemptions; over it the handler answers `busy`. */
|
|
51
|
+
const DEFAULT_MAX_CONCURRENT_REDEMPTIONS = 100;
|
|
52
|
+
/** Longest string field accepted off the wire; a stamp is 342 base64url chars, a signature 86. */
|
|
53
|
+
const MAX_WIRE_FIELD_LENGTH = 1024;
|
|
54
|
+
/** Most addresses a request may carry for the device, and a reply per peer (`sanitizeAddrs`). */
|
|
55
|
+
const MAX_ADDRS = 16;
|
|
56
|
+
/** Most machines an invitation bundle may name; the issuer caps its own list far below this. */
|
|
57
|
+
const MAX_BUNDLE_MEMBERS = 64;
|
|
58
|
+
/** Most peers a reply may carry as dial hints. */
|
|
59
|
+
const MAX_REPLY_PEERS = 256;
|
|
60
|
+
const BASE64URL = /^[A-Za-z0-9_-]+$/;
|
|
61
|
+
/** Encode a bundle for out-of-band delivery: base64url over JSON, as seeds and open invitations are. */
|
|
62
|
+
export function encodeCadreInvitation(invitation) {
|
|
63
|
+
return uint8ArrayToString(new TextEncoder().encode(JSON.stringify(invitation)), 'base64url');
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Decode a bundle, refusing anything that is not a well-formed version-1
|
|
67
|
+
* {@link CadreInvitation}. Standalone so a client without a node (a settings screen that
|
|
68
|
+
* wants to show which cadre an invitation joins) can decode one.
|
|
69
|
+
*
|
|
70
|
+
* @throws on a string that is not base64url JSON, or decodes to another shape or version.
|
|
71
|
+
*/
|
|
72
|
+
export function decodeCadreInvitation(encoded) {
|
|
73
|
+
let parsed;
|
|
74
|
+
try {
|
|
75
|
+
parsed = JSON.parse(new TextDecoder().decode(uint8ArrayFromString(encoded.trim(), 'base64url')));
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
throw new Error('Cadre invitation does not decode as base64url JSON', { cause: error });
|
|
79
|
+
}
|
|
80
|
+
if (!isWellFormedCadreInvitation(parsed)) {
|
|
81
|
+
throw new Error('Cadre invitation is malformed or of an unsupported version');
|
|
82
|
+
}
|
|
83
|
+
return parsed;
|
|
84
|
+
}
|
|
85
|
+
function isWellFormedCadreInvitation(value) {
|
|
86
|
+
if (typeof value !== 'object' || value === null)
|
|
87
|
+
return false;
|
|
88
|
+
const { v, partyId, invitePrivateKey, invite, ownerKeys, members } = value;
|
|
89
|
+
return v === 1
|
|
90
|
+
&& isBoundedString(partyId)
|
|
91
|
+
&& isEd25519KeyB64(invitePrivateKey)
|
|
92
|
+
&& isWellFormedCadreInviteRow(invite)
|
|
93
|
+
&& Array.isArray(ownerKeys) && ownerKeys.length > 0 && ownerKeys.every((key) => isEd25519KeyB64(key))
|
|
94
|
+
&& Array.isArray(members) && members.length <= MAX_BUNDLE_MEMBERS && members.every((addr) => isBoundedString(addr));
|
|
95
|
+
}
|
|
96
|
+
/** Which refusals are worth repeating at the next address — the one place that says so. */
|
|
97
|
+
export const CADRE_INVITE_REJECTION_RETRYABLE = {
|
|
98
|
+
'issuer-unknown': true,
|
|
99
|
+
'invite-invalid': false,
|
|
100
|
+
'invite-spent': false,
|
|
101
|
+
'party-mismatch': false,
|
|
102
|
+
'busy': true,
|
|
103
|
+
'conflict': true,
|
|
104
|
+
'internal': true
|
|
105
|
+
};
|
|
106
|
+
/** Is `value` a code this build knows? Own keys only, so a peer's `'constructor'` does not pass. */
|
|
107
|
+
export function isCadreInviteRejectionCode(value) {
|
|
108
|
+
return typeof value === 'string' && Object.prototype.hasOwnProperty.call(CADRE_INVITE_REJECTION_RETRYABLE, value);
|
|
109
|
+
}
|
|
110
|
+
function rejection(code, reason) {
|
|
111
|
+
return { accepted: false, code, reason };
|
|
112
|
+
}
|
|
113
|
+
// ── Errors the device sees ───────────────────────────────────────────────────
|
|
114
|
+
/**
|
|
115
|
+
* A member answered and refused. `code` is `'unrecognized'` when the frame carried none this
|
|
116
|
+
* build knows (the two machines may run different versions), and such a refusal counts as
|
|
117
|
+
* retryable: trying the next address on an unknown answer is safe, giving up is not.
|
|
118
|
+
*/
|
|
119
|
+
export class CadreInviteRejectedError extends Error {
|
|
120
|
+
/** Both arguments come straight off the wire, so neither is trusted to have its declared type. */
|
|
121
|
+
constructor(code, reason) {
|
|
122
|
+
const text = typeof reason === 'string' ? reason : 'no reason provided';
|
|
123
|
+
super(`Cadre invitation refused: ${text}`);
|
|
124
|
+
this.name = 'CadreInviteRejectedError';
|
|
125
|
+
this.code = isCadreInviteRejectionCode(code) ? code : 'unrecognized';
|
|
126
|
+
this.reason = text;
|
|
127
|
+
this.retryable = this.code === 'unrecognized' || CADRE_INVITE_REJECTION_RETRYABLE[this.code];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Every address the bundle names was tried and none accepted: each failed to dial, answered
|
|
132
|
+
* malformed, or refused retryably. Always retryable; `outcomes` says what each address did.
|
|
133
|
+
*/
|
|
134
|
+
export class CadreInviteUnreachableError extends Error {
|
|
135
|
+
constructor(outcomes) {
|
|
136
|
+
super(outcomes.length === 0
|
|
137
|
+
? 'Cadre invitation names no dialable member'
|
|
138
|
+
: `Cadre invitation could not be redeemed at any of its ${outcomes.length} address(es): `
|
|
139
|
+
+ outcomes.map((outcome) => `${outcome.addr}: ${outcome.error.message}`).join('; '));
|
|
140
|
+
this.outcomes = outcomes;
|
|
141
|
+
this.retryable = true;
|
|
142
|
+
this.name = 'CadreInviteUnreachableError';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* A member accepted but its reply does not check out: another party id, another invitation
|
|
147
|
+
* key, or a row not signed by one of the pinned owner keys. Final: a machine that answers
|
|
148
|
+
* like this is not a member of the cadre the invitation names, and the row it wrote (if any)
|
|
149
|
+
* is in a database the device will never trust.
|
|
150
|
+
*/
|
|
151
|
+
export class CadreInviteReplyInvalidError extends Error {
|
|
152
|
+
constructor(message) {
|
|
153
|
+
super(`Cadre invitation reply refused: ${message}`);
|
|
154
|
+
this.retryable = false;
|
|
155
|
+
this.name = 'CadreInviteReplyInvalidError';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
// ── Shape checks (both directions) ───────────────────────────────────────────
|
|
159
|
+
function isBoundedString(value, max = MAX_WIRE_FIELD_LENGTH) {
|
|
160
|
+
return typeof value === 'string' && value.length > 0 && value.length <= max;
|
|
161
|
+
}
|
|
162
|
+
function isEd25519KeyB64(value) {
|
|
163
|
+
if (!isBoundedString(value) || !BASE64URL.test(value))
|
|
164
|
+
return false;
|
|
165
|
+
try {
|
|
166
|
+
requireEd25519PublicKeyB64(value, 'key');
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** Is `value` a {@link CadreInviteRow} with every column in its stored form and bounded? */
|
|
174
|
+
export function isWellFormedCadreInviteRow(value) {
|
|
175
|
+
if (typeof value !== 'object' || value === null)
|
|
176
|
+
return false;
|
|
177
|
+
const row = value;
|
|
178
|
+
return isEd25519KeyB64(row.key)
|
|
179
|
+
&& (row.peerId === null || isBoundedString(row.peerId))
|
|
180
|
+
&& typeof row.grantsOwner === 'boolean'
|
|
181
|
+
&& (row.expiresAt === null || isBoundedString(row.expiresAt))
|
|
182
|
+
&& (row.totalUses === null || (Number.isInteger(row.totalUses) && row.totalUses >= 1))
|
|
183
|
+
&& isBoundedString(row.stampId)
|
|
184
|
+
&& isEd25519KeyB64(row.issuerKey)
|
|
185
|
+
&& isBoundedString(row.issuerSig);
|
|
186
|
+
}
|
|
187
|
+
function isWellFormedRedeemRequest(value) {
|
|
188
|
+
if (typeof value !== 'object' || value === null)
|
|
189
|
+
return false;
|
|
190
|
+
const { v, partyId, invite, peerKey, multiaddrs, usageStampId, inviteSig, peerSig } = value;
|
|
191
|
+
return v === 1
|
|
192
|
+
&& isBoundedString(partyId)
|
|
193
|
+
&& isWellFormedCadreInviteRow(invite)
|
|
194
|
+
&& isEd25519KeyB64(peerKey)
|
|
195
|
+
&& Array.isArray(multiaddrs)
|
|
196
|
+
&& isBoundedString(usageStampId)
|
|
197
|
+
&& isBoundedString(inviteSig)
|
|
198
|
+
&& isBoundedString(peerSig);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Keep only parsable, distinct multiaddr strings, capped at {@link MAX_ADDRS}, order kept.
|
|
202
|
+
* Applied on both sides: the member bounds what it stores on the device's row, the device
|
|
203
|
+
* bounds what it merges into its peer store. A malformed entry is skipped, not fatal.
|
|
204
|
+
*/
|
|
205
|
+
export function sanitizeAddrs(addrs) {
|
|
206
|
+
if (!Array.isArray(addrs))
|
|
207
|
+
return [];
|
|
208
|
+
const out = [];
|
|
209
|
+
const seen = new Set();
|
|
210
|
+
for (const entry of addrs) {
|
|
211
|
+
if (out.length >= MAX_ADDRS)
|
|
212
|
+
break;
|
|
213
|
+
if (!isBoundedString(entry) || seen.has(entry))
|
|
214
|
+
continue;
|
|
215
|
+
try {
|
|
216
|
+
multiaddr(entry);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
log('addrs: skipping unparsable entry: %o', error);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
seen.add(entry);
|
|
223
|
+
out.push(entry);
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
/** The reply's peer list, bounded and each entry's addresses sanitized; anything else dropped. */
|
|
228
|
+
function sanitizePeers(peers) {
|
|
229
|
+
if (!Array.isArray(peers))
|
|
230
|
+
return [];
|
|
231
|
+
const out = [];
|
|
232
|
+
for (const entry of peers) {
|
|
233
|
+
if (out.length >= MAX_REPLY_PEERS)
|
|
234
|
+
break;
|
|
235
|
+
if (typeof entry !== 'object' || entry === null)
|
|
236
|
+
continue;
|
|
237
|
+
const { peerId, multiaddrs, isOwner, publicKey } = entry;
|
|
238
|
+
if (!isBoundedString(peerId))
|
|
239
|
+
continue;
|
|
240
|
+
out.push({
|
|
241
|
+
peerId,
|
|
242
|
+
multiaddrs: sanitizeAddrs(multiaddrs),
|
|
243
|
+
isOwner: isOwner === true,
|
|
244
|
+
...(isEd25519KeyB64(publicKey) ? { publicKey } : {})
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return out;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Read a reply frame into a typed reply, sanitizing the attacker-influenced fields of an
|
|
251
|
+
* acceptance. A refusal keeps only its code and reason, as sent. Throws on a frame that is
|
|
252
|
+
* neither.
|
|
253
|
+
*/
|
|
254
|
+
function decodeReply(value) {
|
|
255
|
+
if (typeof value !== 'object' || value === null) {
|
|
256
|
+
throw new Error('Cadre invitation reply is not an object');
|
|
257
|
+
}
|
|
258
|
+
const reply = value;
|
|
259
|
+
if (reply.accepted !== true) {
|
|
260
|
+
return { accepted: false, code: reply.code, reason: reply.reason };
|
|
261
|
+
}
|
|
262
|
+
if (!isBoundedString(reply.partyId) || !isWellFormedCadreInviteRow(reply.invite)) {
|
|
263
|
+
throw new Error('Cadre invitation acceptance is missing its party id or a well-formed row');
|
|
264
|
+
}
|
|
265
|
+
return { accepted: true, partyId: reply.partyId, invite: reply.invite, peers: sanitizePeers(reply.peers) };
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Build and sign the request for `invitation`: the holder's `'redeem'` signature with the
|
|
269
|
+
* invitation's private key and the device's `'consent'` signature with its own, both over
|
|
270
|
+
* (invitation key, `usageStampId`, device key) for the invitation's party. The same bytes
|
|
271
|
+
* the schema verifies at the member (`cadreInviteRedeemMessage` / `cadreInviteConsentMessage`),
|
|
272
|
+
* whose own `party_id()` must therefore be the party the bundle names.
|
|
273
|
+
*/
|
|
274
|
+
export function signRedeemRequest(invitation, signer, usageStampId, multiaddrs) {
|
|
275
|
+
const fields = { inviteKey: invitation.invite.key, usageStampId, peerKey: signer.peerKey };
|
|
276
|
+
const signB64 = (message, privateKey) => sign(message, privateKey, 'ed25519', 'bytes', 'base64url', 'base64url');
|
|
277
|
+
return {
|
|
278
|
+
v: 1,
|
|
279
|
+
partyId: invitation.partyId,
|
|
280
|
+
invite: invitation.invite,
|
|
281
|
+
peerKey: signer.peerKey,
|
|
282
|
+
multiaddrs: sanitizeAddrs(multiaddrs),
|
|
283
|
+
usageStampId,
|
|
284
|
+
inviteSig: signB64(cadreInviteRedeemMessage(invitation.partyId, fields), invitation.invitePrivateKey),
|
|
285
|
+
peerSig: signB64(cadreInviteConsentMessage(invitation.partyId, fields), signer.peerPrivateKey)
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* The device's check on an acceptance: the same party, the same invitation key, and a row
|
|
290
|
+
* signed by an issuer the device pinned from the bundle. Throws
|
|
291
|
+
* {@link CadreInviteReplyInvalidError} naming the first failure.
|
|
292
|
+
*
|
|
293
|
+
* What it proves is bounded: the request already carried the owner-signed row, so a machine
|
|
294
|
+
* that is not a member can echo it. The check catches a reply for another party or
|
|
295
|
+
* invitation and a row the pinned owners never signed; whether the answering machine is a
|
|
296
|
+
* member is settled afterwards, when the device syncs the control database and judges every
|
|
297
|
+
* row against its anchor (`CadreNode.listAuthorizedMembers`).
|
|
298
|
+
*/
|
|
299
|
+
export function verifyRedeemReply(reply, invitation, isTrustedIssuer) {
|
|
300
|
+
if (reply.partyId !== invitation.partyId) {
|
|
301
|
+
throw new CadreInviteReplyInvalidError(`the member serves party ${reply.partyId}, the invitation names ${invitation.partyId}`);
|
|
302
|
+
}
|
|
303
|
+
if (reply.invite.key !== invitation.invite.key) {
|
|
304
|
+
throw new CadreInviteReplyInvalidError('the member answered for a different invitation');
|
|
305
|
+
}
|
|
306
|
+
if (!isTrustedIssuer(reply.invite.issuerKey)) {
|
|
307
|
+
throw new CadreInviteReplyInvalidError('the row the member holds is not issued by one of the invitation\'s owner keys');
|
|
308
|
+
}
|
|
309
|
+
if (!verifyCadreInviteRow(invitation.partyId, reply.invite)) {
|
|
310
|
+
throw new CadreInviteReplyInvalidError('the row the member holds is not signed by its issuer');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
/** Constraint names the schema refuses a redemption with, and the code each maps to. */
|
|
314
|
+
const REDEEM_CONSTRAINT_CODES = [
|
|
315
|
+
[/CHECK constraint failed: Authorized\b/, 'invite-spent', 'The invitation is expired, withdrawn, exhausted, or its issuer is no longer an owner'],
|
|
316
|
+
[/CHECK constraint failed: (InvitePossessed|PeerConsented|PeerExists|OwnerExists)\b/, 'invite-invalid', 'The redemption does not satisfy the invitation'],
|
|
317
|
+
[/CHECK constraint failed: NotRevoked\b/, 'invite-spent', 'The invitation was withdrawn'],
|
|
318
|
+
];
|
|
319
|
+
/** Which named constraint, if any, refused the write — read off the error's cause chain. */
|
|
320
|
+
function constraintRejection(error) {
|
|
321
|
+
if (!(error instanceof Error))
|
|
322
|
+
return null;
|
|
323
|
+
const messages = chainMessages(error);
|
|
324
|
+
for (const [pattern, code, reason] of REDEEM_CONSTRAINT_CODES) {
|
|
325
|
+
if (messages.some((message) => pattern.test(message)))
|
|
326
|
+
return rejection(code, reason);
|
|
327
|
+
}
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
/** A write failure that is not a refusal: the retry gave up (`conflict`), or something else (`internal`). */
|
|
331
|
+
function writeFailureRejection(error) {
|
|
332
|
+
// NOTE: a seat torn on a member alone with an uncaught-up device was measured answering
|
|
333
|
+
// `internal`, not `conflict` (cadre-invite-redeemed-at-a-member-without-the-row). Both are
|
|
334
|
+
// retryable, so the device is unaffected; if the two codes ever diverge in retryability,
|
|
335
|
+
// find which tear `isRetriableControlWriteFailure` does not recognise.
|
|
336
|
+
return isRetriableControlWriteFailure(error)
|
|
337
|
+
? rejection('conflict', 'The redemption write failed transiently; nothing was recorded')
|
|
338
|
+
: rejection('internal', 'Internal redemption error');
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Member side: registers the handler and drives each inbound stream through the checks in
|
|
342
|
+
* cost order — shape, connecting identity, both signatures, target device — so no database
|
|
343
|
+
* work happens for a stranger that cannot prove anything, then hands the device the control
|
|
344
|
+
* store (`catchUpDevice`), seats the row and redeems.
|
|
345
|
+
* Hardened as the seed handler is: a concurrency cap (over it, `busy`) and a read timeout.
|
|
346
|
+
*
|
|
347
|
+
* Registered only by a started `CadreNode`, after its control database is up, like the
|
|
348
|
+
* formation responder; nothing here is reachable on a node without one.
|
|
349
|
+
*/
|
|
350
|
+
export class CadreInviteHandler {
|
|
351
|
+
constructor(options) {
|
|
352
|
+
this.registered = new Set();
|
|
353
|
+
this.active = 0;
|
|
354
|
+
this.options = options;
|
|
355
|
+
this.readTimeoutMs = options.redeemReadTimeoutMs ?? DEFAULT_REDEEM_READ_TIMEOUT_MS;
|
|
356
|
+
this.maxConcurrent = options.maxConcurrentRedemptions ?? DEFAULT_MAX_CONCURRENT_REDEMPTIONS;
|
|
357
|
+
}
|
|
358
|
+
/** In-flight inbound redemptions. */
|
|
359
|
+
get activeCount() {
|
|
360
|
+
return this.active;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Register on `node`; rejects when libp2p refuses the registration. `runOnLimitedConnection`
|
|
364
|
+
* because a phone redeems through a relay, whose connection libp2p marks limited; the
|
|
365
|
+
* exchange is two small frames, well inside any relay's cap.
|
|
366
|
+
*/
|
|
367
|
+
async register(node, protocolId = CADRE_INVITE_PROTOCOL) {
|
|
368
|
+
if (this.registered.has(node))
|
|
369
|
+
return;
|
|
370
|
+
await node.handle(protocolId, async (rawStream, rawConnection) => {
|
|
371
|
+
await this.handleStream(rawStream, rawConnection.remotePeer.toString());
|
|
372
|
+
}, { runOnLimitedConnection: true });
|
|
373
|
+
this.registered.add(node);
|
|
374
|
+
log('cadre invitation handler registered (%s)', protocolId);
|
|
375
|
+
}
|
|
376
|
+
async unregister(node, protocolId = CADRE_INVITE_PROTOCOL) {
|
|
377
|
+
if (!this.registered.has(node))
|
|
378
|
+
return;
|
|
379
|
+
await node.unhandle(protocolId);
|
|
380
|
+
this.registered.delete(node);
|
|
381
|
+
log('cadre invitation handler unregistered (%s)', protocolId);
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* One inbound redemption: read the frame, decide, reply, close. Public as the seam the
|
|
385
|
+
* protocol spec drives directly; `remotePeerId` is the connection's authenticated peer.
|
|
386
|
+
*/
|
|
387
|
+
async handleStream(stream, remotePeerId) {
|
|
388
|
+
if (this.active >= this.maxConcurrent) {
|
|
389
|
+
log('refusing redemption from %s: %d in flight at cap %d', remotePeerId, this.active, this.maxConcurrent);
|
|
390
|
+
await replyAndClose(stream, rejection('busy', 'Too many concurrent redemptions'), 'Cadre invite');
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
this.active++;
|
|
394
|
+
try {
|
|
395
|
+
const reply = await this.decide(stream, remotePeerId);
|
|
396
|
+
log('redemption from %s: %s', remotePeerId, reply.accepted ? 'accepted' : `refused (${reply.code})`);
|
|
397
|
+
await replyAndClose(stream, reply, 'Cadre invite');
|
|
398
|
+
}
|
|
399
|
+
catch (error) {
|
|
400
|
+
log('redemption from %s failed: %o', remotePeerId, error);
|
|
401
|
+
await replyAndClose(stream, rejection('internal', 'Internal redemption error'), 'Cadre invite');
|
|
402
|
+
}
|
|
403
|
+
finally {
|
|
404
|
+
this.active--;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Read the request frame to EOF, bounded and size-capped; JSON only, shape is `decide`'s.
|
|
409
|
+
* Throws on a frame that cannot be read, which {@link handleStream} answers `internal`:
|
|
410
|
+
* a read timeout or a cut frame proves nothing about the invitation, so the device must
|
|
411
|
+
* stay free to try the next address.
|
|
412
|
+
*/
|
|
413
|
+
async readRequest(stream) {
|
|
414
|
+
const data = await readStreamToEnd(stream, { maxBytes: MAX_CADRE_INVITE_MSG_SIZE, timeoutMs: this.readTimeoutMs, label: 'Cadre invite' });
|
|
415
|
+
return JSON.parse(new TextDecoder().decode(decodeLengthPrefixedFrame(data, MAX_CADRE_INVITE_MSG_SIZE)));
|
|
416
|
+
}
|
|
417
|
+
async decide(stream, remotePeerId) {
|
|
418
|
+
const request = await this.readRequest(stream);
|
|
419
|
+
const refused = this.checkRequest(request, remotePeerId);
|
|
420
|
+
if (refused)
|
|
421
|
+
return refused;
|
|
422
|
+
return await this.seatAndRedeem(request, remotePeerId);
|
|
423
|
+
}
|
|
424
|
+
/**
|
|
425
|
+
* The checks that cost no database work, in order: shape, party, connecting identity (an
|
|
426
|
+
* ed25519 peer id is an identity multihash of the very key, and this is the only layer that
|
|
427
|
+
* can check the pair), both signatures, and a targeted invitation's device.
|
|
428
|
+
*/
|
|
429
|
+
checkRequest(request, remotePeerId) {
|
|
430
|
+
if (!isWellFormedRedeemRequest(request)) {
|
|
431
|
+
return rejection('invite-invalid', 'Malformed redemption request');
|
|
432
|
+
}
|
|
433
|
+
if (request.partyId !== this.options.partyId) {
|
|
434
|
+
return rejection('party-mismatch', 'This machine serves another cadre');
|
|
435
|
+
}
|
|
436
|
+
if (ed25519PublicKeyB64FromPeerId(remotePeerId) !== request.peerKey) {
|
|
437
|
+
return rejection('invite-invalid', 'The peer key is not the connecting identity');
|
|
438
|
+
}
|
|
439
|
+
const fields = { inviteKey: request.invite.key, usageStampId: request.usageStampId, peerKey: request.peerKey };
|
|
440
|
+
if (!verifyCadreInviteRedemption(this.options.partyId, fields, request.inviteSig, request.peerSig)) {
|
|
441
|
+
return rejection('invite-invalid', 'The redemption signatures do not verify');
|
|
442
|
+
}
|
|
443
|
+
if (request.invite.peerId !== null && request.invite.peerId !== remotePeerId) {
|
|
444
|
+
return rejection('invite-invalid', 'The invitation names another device');
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Hand the device this member's control store, seat the row from the bundle (its own
|
|
450
|
+
* transaction; a no-op when held), then redeem. The push comes first because the seat is
|
|
451
|
+
* already a write the device takes part in (see {@link catchUpDeviceIfLive}). An issuer this
|
|
452
|
+
* member does not know as an owner is the one retryable refusal left: the device tries the
|
|
453
|
+
* next address, which may hold the issuer's row.
|
|
454
|
+
*/
|
|
455
|
+
async seatAndRedeem(request, remotePeerId) {
|
|
456
|
+
const { store } = this.options;
|
|
457
|
+
await this.catchUpDeviceIfLive(request.invite, remotePeerId);
|
|
458
|
+
try {
|
|
459
|
+
await store.seatCadreInvite(request.invite);
|
|
460
|
+
}
|
|
461
|
+
catch (error) {
|
|
462
|
+
if (error instanceof CadreInviteIssuerUnknownError) {
|
|
463
|
+
return rejection('issuer-unknown', 'The invitation\'s issuer is not known as an owner here');
|
|
464
|
+
}
|
|
465
|
+
log('seating invitation %s from %s failed: %o', request.invite.key, remotePeerId, error);
|
|
466
|
+
return constraintRejection(error) ?? writeFailureRejection(error);
|
|
467
|
+
}
|
|
468
|
+
try {
|
|
469
|
+
const result = await store.redeemCadreInvite({
|
|
470
|
+
inviteKey: request.invite.key,
|
|
471
|
+
peerId: remotePeerId,
|
|
472
|
+
peerKey: request.peerKey,
|
|
473
|
+
multiaddrs: sanitizeAddrs(request.multiaddrs),
|
|
474
|
+
usageStampId: request.usageStampId,
|
|
475
|
+
inviteSig: request.inviteSig,
|
|
476
|
+
peerSig: request.peerSig
|
|
477
|
+
});
|
|
478
|
+
log('invitation %s redeemed by %s (alreadyMember=%s)', request.invite.key, remotePeerId, result.alreadyMember);
|
|
479
|
+
}
|
|
480
|
+
catch (error) {
|
|
481
|
+
if (error instanceof InvitationExhaustedError) {
|
|
482
|
+
return rejection('invite-spent', 'The invitation has no uses left');
|
|
483
|
+
}
|
|
484
|
+
log('redeeming invitation %s for %s failed: %o', request.invite.key, remotePeerId, error);
|
|
485
|
+
return constraintRejection(error) ?? writeFailureRejection(error);
|
|
486
|
+
}
|
|
487
|
+
// The row as held: the seat inserted this copy verbatim, or found the same row (its key
|
|
488
|
+
// is the primary key and every column is under the issuer's signature).
|
|
489
|
+
return { accepted: true, partyId: this.options.partyId, invite: request.invite, peers: await this.peersBestEffort() };
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Push this member's control store to the device before the first write of the exchange
|
|
493
|
+
* (the seat, when this member does not hold the row; otherwise the admission). The device
|
|
494
|
+
* joined this member's control write cohort when it connected, and a cohort node with no
|
|
495
|
+
* base revision of a block refuses a commit to it; a member alone with the device then has
|
|
496
|
+
* half its cohort refusing, and the write tears instead of committing. With the store in
|
|
497
|
+
* hand the device holds the write too.
|
|
498
|
+
*
|
|
499
|
+
* Only for an invitation still live here, judged on the row this member holds or, before
|
|
500
|
+
* the seat, on the bundle's copy once its issuer signature verifies
|
|
501
|
+
* (`ControlDatabase.isCadreInviteLive`): the holder of a withdrawn, expired or spent one,
|
|
502
|
+
* or of a forged copy naming a real owner, is refused without being sent anything. The
|
|
503
|
+
* retry of an admission already written can read as spent too and is sent nothing; it
|
|
504
|
+
* needs nothing, since the device is a member and the ordinary catch-up reaches it.
|
|
505
|
+
* Best-effort: a failed check or push is logged and the writes go ahead, then commit or are
|
|
506
|
+
* answered retryably.
|
|
507
|
+
*/
|
|
508
|
+
async catchUpDeviceIfLive(invite, remotePeerId) {
|
|
509
|
+
const { catchUpDevice, store } = this.options;
|
|
510
|
+
if (!catchUpDevice)
|
|
511
|
+
return;
|
|
512
|
+
try {
|
|
513
|
+
if (!await store.isCadreInviteLive(invite)) {
|
|
514
|
+
log('invitation %s is not live here; %s is not caught up before the write', invite.key, remotePeerId);
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// NOTE: accepted tradeoff — when a write then fails (the seat refused, the last use
|
|
518
|
+
// taken by a race, the row expiring between this check and the write), the device keeps
|
|
519
|
+
// a copy of a control store it was not admitted to. It holds a live owner-signed
|
|
520
|
+
// invitation and proved possession and consent, and a seed hands an un-enrolled machine
|
|
521
|
+
// the same information. Revisit if a control table ever carries data a non-member must not see.
|
|
522
|
+
await catchUpDevice(remotePeerId);
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
log('catching up %s before its admission failed (writing anyway): %o', remotePeerId, error);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
/** Dial hints are an optimization; a failed read costs the device its hints, not its membership. */
|
|
529
|
+
async peersBestEffort() {
|
|
530
|
+
try {
|
|
531
|
+
return await this.options.store.querySeedPeers();
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
log('peer projection for the reply failed (continuing without hints): %o', error);
|
|
535
|
+
return [];
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Open one stream to `addr`, send the request, half-close, read the one reply frame.
|
|
541
|
+
* `signal` is the per-address deadline: it cancels the dial and resets a live stream.
|
|
542
|
+
*/
|
|
543
|
+
async function exchangeRedemption(node, addr, request, signal, budgetMs, protocolId) {
|
|
544
|
+
// `runOnLimitedConnection`: a member may be reachable only through a relay (see `register`).
|
|
545
|
+
const rawStream = await node.dialProtocol(addr, protocolId, { runOnLimitedConnection: true, signal });
|
|
546
|
+
return await exchangeFrame(rawStream, signal, request, async (stream) => {
|
|
547
|
+
const data = await readStreamToEnd(stream, { maxBytes: MAX_CADRE_INVITE_MSG_SIZE, timeoutMs: budgetMs, label: 'Cadre invite reply' });
|
|
548
|
+
return decodeReply(JSON.parse(new TextDecoder().decode(decodeLengthPrefixedFrame(data, MAX_CADRE_INVITE_MSG_SIZE))));
|
|
549
|
+
}, 'Cadre invitation redemption aborted by its deadline');
|
|
550
|
+
}
|
|
551
|
+
function asError(error) {
|
|
552
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Device side: try the bundle's addresses one at a time, each under its own deadline, and
|
|
556
|
+
* stop at the first acceptance or the first final refusal. A dial failure, a malformed
|
|
557
|
+
* reply or a retryable refusal moves to the next address.
|
|
558
|
+
*
|
|
559
|
+
* Every address gets the SAME request, with one `usageStampId`: `CadreInviteUsage` is keyed
|
|
560
|
+
* by it, so a member the device gave up on that was still committing cannot admit it twice,
|
|
561
|
+
* and a retry at the next member after a dropped reply is answered as already a member.
|
|
562
|
+
*
|
|
563
|
+
* @throws {CadreInviteRejectedError} on a final refusal (its `retryable` is false).
|
|
564
|
+
* @throws {CadreInviteReplyInvalidError} on an acceptance that fails {@link verifyRedeemReply}.
|
|
565
|
+
* @throws {CadreInviteUnreachableError} when every address was tried and none accepted.
|
|
566
|
+
*/
|
|
567
|
+
export async function redeemAtMembers(node, options) {
|
|
568
|
+
const protocolId = options.protocolId ?? CADRE_INVITE_PROTOCOL;
|
|
569
|
+
const budgetMs = options.addressBudgetMs ?? relayedRequestBudgetMs(options.linkRoundTripMs);
|
|
570
|
+
const outcomes = [];
|
|
571
|
+
for (const addr of options.invitation.members) {
|
|
572
|
+
// Between attempts: libp2p's dial queue needs a macrotask to drop an aborted job.
|
|
573
|
+
if (outcomes.length > 0)
|
|
574
|
+
await nextMacrotask();
|
|
575
|
+
let parsed;
|
|
576
|
+
try {
|
|
577
|
+
parsed = multiaddr(addr);
|
|
578
|
+
}
|
|
579
|
+
catch (error) {
|
|
580
|
+
outcomes.push({ addr, error: new Error('not a multiaddr', { cause: error }) });
|
|
581
|
+
continue;
|
|
582
|
+
}
|
|
583
|
+
try {
|
|
584
|
+
const reply = await withDeadline(budgetMs, `Cadre invitation redemption via ${addr}`, (signal) => exchangeRedemption(node, parsed, options.request, signal, budgetMs, protocolId));
|
|
585
|
+
if (!reply.accepted) {
|
|
586
|
+
const refusal = new CadreInviteRejectedError(reply.code, reply.reason);
|
|
587
|
+
if (!refusal.retryable)
|
|
588
|
+
throw refusal;
|
|
589
|
+
log('redemption via %s refused retryably (%s); trying the next address', addr, refusal.code);
|
|
590
|
+
outcomes.push({ addr, error: refusal });
|
|
591
|
+
continue;
|
|
592
|
+
}
|
|
593
|
+
verifyRedeemReply(reply, options.invitation, options.isTrustedIssuer);
|
|
594
|
+
return { reply, memberPeerId: trailingPeerId(parsed), memberAddr: addr };
|
|
595
|
+
}
|
|
596
|
+
catch (error) {
|
|
597
|
+
if (error instanceof CadreInviteRejectedError || error instanceof CadreInviteReplyInvalidError)
|
|
598
|
+
throw error;
|
|
599
|
+
log('redemption via %s failed before an answer (%s); trying the next address', addr, asError(error).message);
|
|
600
|
+
outcomes.push({ addr, error: asError(error) });
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
throw new CadreInviteUnreachableError(outcomes);
|
|
604
|
+
}
|
|
605
|
+
//# sourceMappingURL=cadre-invite-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cadre-invite-protocol.js","sourceRoot":"","sources":["../src/cadre-invite-protocol.ts"],"names":[],"mappings":"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"}
|