@serfab/cadre-core 1.16.0 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/bootstrap-peer-store-file.js +1 -1
- package/dist/bootstrap-peer-store.d.ts +1 -1
- package/dist/bootstrap-peer-store.js +4 -4
- package/dist/cadre-invite-protocol.d.ts +81 -142
- package/dist/cadre-invite-protocol.js +168 -458
- package/dist/cadre-invite-protocol.js.map +1 -1
- package/dist/cadre-join-request.d.ts +15 -2
- package/dist/cadre-join-request.js +22 -3
- package/dist/cadre-join-request.js.map +1 -1
- package/dist/cadre-node.d.ts +1089 -475
- package/dist/cadre-node.js +2170 -1037
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +36 -14
- package/dist/control-authorization.js +20 -3
- package/dist/control-authorization.js.map +1 -1
- package/dist/control-cohort.d.ts +88 -32
- package/dist/control-cohort.js +91 -33
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +856 -318
- package/dist/control-database.js +1422 -701
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +46 -43
- package/dist/control-formation-recorder.js +77 -78
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-protocol-guard.d.ts +11 -4
- package/dist/control-protocol-guard.js +20 -5
- package/dist/control-protocol-guard.js.map +1 -1
- package/dist/control-schema-version.d.ts +97 -0
- package/dist/control-schema-version.js +225 -0
- package/dist/control-schema-version.js.map +1 -0
- package/dist/control-schema.d.ts +45 -5
- package/dist/control-schema.js +262 -78
- package/dist/control-schema.js.map +1 -1
- package/dist/control-write-retry.d.ts +45 -10
- package/dist/control-write-retry.js +92 -22
- package/dist/control-write-retry.js.map +1 -1
- package/dist/delegate-admission.d.ts +13 -0
- package/dist/delegate-admission.js +27 -0
- package/dist/delegate-admission.js.map +1 -1
- package/dist/ed25519-key.d.ts +10 -7
- package/dist/ed25519-key.js +23 -20
- package/dist/ed25519-key.js.map +1 -1
- package/dist/edge-node/config.d.ts +122 -0
- package/dist/edge-node/config.js +199 -0
- package/dist/edge-node/config.js.map +1 -0
- package/dist/edge-node/dissolve.d.ts +95 -0
- package/dist/edge-node/dissolve.js +184 -0
- package/dist/edge-node/dissolve.js.map +1 -0
- package/dist/edge-node/edge-join.d.ts +44 -0
- package/dist/edge-node/edge-join.js +227 -0
- package/dist/edge-node/edge-join.js.map +1 -0
- package/dist/edge-node/edge-node.d.ts +295 -0
- package/dist/edge-node/edge-node.js +704 -0
- package/dist/edge-node/edge-node.js.map +1 -0
- package/dist/edge-node/index.d.ts +18 -0
- package/dist/edge-node/index.js +18 -0
- package/dist/edge-node/index.js.map +1 -0
- package/dist/edge-node/join-credential.d.ts +11 -0
- package/dist/edge-node/join-credential.js +11 -0
- package/dist/edge-node/join-credential.js.map +1 -0
- package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
- package/dist/edge-node/lazy-raw-storage.js +134 -0
- package/dist/edge-node/lazy-raw-storage.js.map +1 -0
- package/dist/edge-node/node-local.d.ts +60 -0
- package/dist/edge-node/node-local.js +58 -0
- package/dist/edge-node/node-local.js.map +1 -0
- package/dist/edge-node/options.d.ts +83 -0
- package/dist/edge-node/options.js +110 -0
- package/dist/edge-node/options.js.map +1 -0
- package/dist/edge-node/owner-grant.d.ts +41 -0
- package/dist/edge-node/owner-grant.js +38 -0
- package/dist/edge-node/owner-grant.js.map +1 -0
- package/dist/edge-node/platform.d.ts +140 -0
- package/dist/edge-node/platform.js +12 -0
- package/dist/edge-node/platform.js.map +1 -0
- package/dist/edge-node/strand-move.d.ts +115 -0
- package/dist/edge-node/strand-move.js +223 -0
- package/dist/edge-node/strand-move.js.map +1 -0
- package/dist/edge-node/strands.d.ts +28 -0
- package/dist/edge-node/strands.js +60 -0
- package/dist/edge-node/strands.js.map +1 -0
- package/dist/enrolled-machine-store.d.ts +3 -3
- package/dist/enrolled-machine-store.js +3 -3
- package/dist/formation-token.d.ts +48 -0
- package/dist/formation-token.js +26 -0
- package/dist/formation-token.js.map +1 -0
- package/dist/index.d.ts +27 -17
- package/dist/index.js +39 -17
- package/dist/index.js.map +1 -1
- package/dist/joined-strand-store.d.ts +29 -12
- package/dist/joined-strand-store.js +66 -27
- package/dist/joined-strand-store.js.map +1 -1
- package/dist/key-store.d.ts +9 -4
- package/dist/key-store.js +9 -4
- package/dist/key-store.js.map +1 -1
- package/dist/link-budget.d.ts +9 -7
- package/dist/link-budget.js +9 -7
- package/dist/link-budget.js.map +1 -1
- package/dist/machine-removal.d.ts +19 -39
- package/dist/machine-removal.js +24 -98
- package/dist/machine-removal.js.map +1 -1
- package/dist/member-redemption.d.ts +256 -0
- package/dist/member-redemption.js +490 -0
- package/dist/member-redemption.js.map +1 -0
- package/dist/membership-view.d.ts +91 -0
- package/dist/membership-view.js +110 -0
- package/dist/membership-view.js.map +1 -0
- package/dist/owner-anchor-sync.d.ts +4 -4
- package/dist/owner-anchor-sync.js +16 -15
- package/dist/owner-anchor-sync.js.map +1 -1
- package/dist/owner-grant-protocol.d.ts +251 -0
- package/dist/owner-grant-protocol.js +344 -0
- package/dist/owner-grant-protocol.js.map +1 -0
- package/dist/owner-key-export.d.ts +70 -0
- package/dist/owner-key-export.js +279 -0
- package/dist/owner-key-export.js.map +1 -0
- package/dist/owner-key-flows.d.ts +55 -0
- package/dist/owner-key-flows.js +60 -0
- package/dist/owner-key-flows.js.map +1 -0
- package/dist/owner-key-store.d.ts +31 -0
- package/dist/owner-key-store.js +81 -0
- package/dist/owner-key-store.js.map +1 -0
- package/dist/owner-keys.d.ts +78 -0
- package/dist/owner-keys.js +133 -0
- package/dist/owner-keys.js.map +1 -0
- package/dist/owner-signer.d.ts +54 -0
- package/dist/owner-signer.js +51 -0
- package/dist/owner-signer.js.map +1 -0
- package/dist/peer-addr-book.d.ts +10 -1
- package/dist/peer-addr-book.js +4 -3
- package/dist/peer-addr-book.js.map +1 -1
- package/dist/peer-authorization.d.ts +83 -18
- package/dist/peer-authorization.js +123 -53
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-join-backfill.d.ts +89 -20
- package/dist/peer-join-backfill.js +265 -108
- package/dist/peer-join-backfill.js.map +1 -1
- package/dist/pending-join-runner.d.ts +48 -18
- package/dist/pending-join-runner.js +74 -56
- package/dist/pending-join-runner.js.map +1 -1
- package/dist/provider/rent-node.js +1 -1
- package/dist/provider/rent-node.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +162 -133
- package/dist/seed-bootstrap.js +300 -283
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.js +4 -4
- package/dist/strand-addr-protocol.d.ts +4 -1
- package/dist/strand-addr-protocol.js +8 -2
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-cohort.d.ts +3 -0
- package/dist/strand-cohort.js.map +1 -1
- package/dist/strand-database.d.ts +24 -2
- package/dist/strand-database.js +29 -6
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-first-sync-gate.d.ts +176 -88
- package/dist/strand-first-sync-gate.js +288 -121
- package/dist/strand-first-sync-gate.js.map +1 -1
- package/dist/strand-formation-deadlines.d.ts +9 -9
- package/dist/strand-formation-deadlines.js +9 -9
- package/dist/strand-formation-manager.d.ts +23 -17
- package/dist/strand-formation-manager.js +46 -44
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +5 -8
- package/dist/strand-formation-protocol.js +101 -16
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +27 -18
- package/dist/strand-instance-manager.js +119 -50
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-membership-reconciler.d.ts +16 -13
- package/dist/strand-membership-reconciler.js +30 -22
- package/dist/strand-membership-reconciler.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +45 -22
- package/dist/strand-membership-writer.js +226 -131
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-move.d.ts +6 -5
- package/dist/strand-move.js +36 -32
- package/dist/strand-move.js.map +1 -1
- package/dist/strand-peer-redial.js +2 -3
- package/dist/strand-peer-redial.js.map +1 -1
- package/dist/strand-revocation-enforcer.d.ts +134 -60
- package/dist/strand-revocation-enforcer.js +144 -71
- package/dist/strand-revocation-enforcer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +50 -39
- package/dist/strand-solicitation.js +10 -3
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-watcher.d.ts +80 -13
- package/dist/strand-watcher.js +152 -48
- package/dist/strand-watcher.js.map +1 -1
- package/dist/table-change-watch.d.ts +63 -0
- package/dist/table-change-watch.js +209 -0
- package/dist/table-change-watch.js.map +1 -0
- package/dist/timeout-scheduler.d.ts +4 -3
- package/dist/timeout-scheduler.js +4 -3
- package/dist/timeout-scheduler.js.map +1 -1
- package/dist/types.d.ts +374 -150
- package/dist/types.js +4 -4
- package/dist/types.js.map +1 -1
- package/package.json +17 -8
- package/src/bootstrap-peer-store-file.ts +44 -44
- package/src/bootstrap-peer-store.ts +214 -214
- package/src/cadre-invite-protocol.ts +740 -1052
- package/src/cadre-join-request.ts +24 -3
- package/src/cadre-node.ts +2402 -1079
- package/src/control-authorization.ts +47 -14
- package/src/control-cohort.ts +204 -128
- package/src/control-database.ts +1749 -820
- package/src/control-formation-recorder.ts +359 -353
- package/src/control-protocol-guard.ts +23 -6
- package/src/control-schema-version.ts +301 -0
- package/src/control-schema.ts +273 -78
- package/src/control-write-retry.ts +641 -567
- package/src/delegate-admission.ts +351 -314
- package/src/ed25519-key.ts +174 -171
- package/src/edge-node/config.ts +277 -0
- package/src/edge-node/dissolve.ts +251 -0
- package/src/edge-node/edge-join.ts +319 -0
- package/src/edge-node/edge-node.ts +1045 -0
- package/src/edge-node/index.ts +59 -0
- package/src/edge-node/join-credential.ts +16 -0
- package/src/edge-node/lazy-raw-storage.ts +178 -0
- package/src/edge-node/node-local.ts +84 -0
- package/src/edge-node/options.ts +179 -0
- package/src/edge-node/owner-grant.ts +63 -0
- package/src/edge-node/platform.ts +151 -0
- package/src/edge-node/strand-move.ts +270 -0
- package/src/edge-node/strands.ts +72 -0
- package/src/enrolled-machine-store.ts +3 -3
- package/src/formation-token.ts +63 -0
- package/src/index.ts +899 -789
- package/src/joined-strand-store.ts +73 -28
- package/src/key-store.ts +10 -4
- package/src/link-budget.ts +9 -7
- package/src/machine-removal.ts +26 -111
- package/src/member-redemption.ts +649 -0
- package/src/membership-view.ts +144 -0
- package/src/owner-anchor-sync.ts +19 -18
- package/src/owner-grant-protocol.ts +547 -0
- package/src/owner-key-export.ts +348 -0
- package/src/owner-key-flows.ts +109 -0
- package/src/owner-key-store.ts +85 -0
- package/src/owner-keys.ts +178 -0
- package/src/owner-signer.ts +94 -0
- package/src/peer-addr-book.ts +4 -3
- package/src/peer-authorization.ts +558 -488
- package/src/peer-join-backfill.ts +971 -761
- package/src/pending-join-runner.ts +76 -53
- package/src/provider/rent-node.ts +1 -1
- package/src/seed-bootstrap.ts +343 -349
- package/src/seed-trust-policy.ts +4 -4
- package/src/strand-addr-protocol.ts +547 -538
- package/src/strand-cohort.ts +4 -0
- package/src/strand-database.ts +352 -319
- package/src/strand-first-sync-gate.ts +334 -133
- package/src/strand-formation-deadlines.ts +227 -227
- package/src/strand-formation-manager.ts +731 -725
- package/src/strand-formation-protocol.ts +122 -24
- package/src/strand-instance-manager.ts +2092 -2026
- package/src/strand-membership-reconciler.ts +33 -25
- package/src/strand-membership-writer.ts +2093 -1974
- package/src/strand-move.ts +50 -38
- package/src/strand-peer-redial.ts +2 -3
- package/src/strand-revocation-enforcer.ts +740 -654
- package/src/strand-solicitation.ts +61 -34
- package/src/strand-watcher.ts +175 -56
- package/src/table-change-watch.ts +285 -0
- package/src/timeout-scheduler.ts +29 -28
- package/src/types.ts +391 -152
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Owner grant redemption transport: `/sereus/owner-grant/1.0.0`.
|
|
3
|
+
*
|
|
4
|
+
* A holder of an owner grant (an owner-signed `OwnerGrant` row plus the private half of the
|
|
5
|
+
* grant keypair, see {@link OwnerGrantBundle}) dials ANY member machine the grant names and
|
|
6
|
+
* proves possession of the grant and of the key it wants seated; the member seats the row if
|
|
7
|
+
* it does not hold it yet and writes the holder's `OwnerKey` row through
|
|
8
|
+
* `ControlDatabase.redeemOwnerGrant`. No machine is admitted and no peer id is named: the
|
|
9
|
+
* holder's network identity has nothing to do with the key it seats, so unlike the invitation
|
|
10
|
+
* protocol there is no connecting-identity check — both signatures already bind the grant, the
|
|
11
|
+
* nonce and the key. The owner that minted the grant need not be online.
|
|
12
|
+
*
|
|
13
|
+
* The exchange shape — one frame each way, the member's hardening and admission of the
|
|
14
|
+
* holder, the holder's address-by-address dialing in passes — is `member-redemption.ts`'s,
|
|
15
|
+
* shared with the invitation protocol; this module supplies the grant's bundle, request,
|
|
16
|
+
* checks, writes and refusal codes. Stranger-facing by design, classed `stranger-open` in
|
|
17
|
+
* `control-protocol-guard.ts`; the handler's own trust decision is the two signatures.
|
|
18
|
+
*/
|
|
19
|
+
import debug from 'debug';
|
|
20
|
+
import { toString as uint8ArrayToString, fromString as uint8ArrayFromString } from 'uint8arrays';
|
|
21
|
+
import { sign } from '@optimystic/quereus-plugin-crypto';
|
|
22
|
+
import { ownerGrantRedeemMessage, ownerGrantConsentMessage, OwnerGrantIssuerUnknownError, InvitationExhaustedError } from './control-database.js';
|
|
23
|
+
import { verifyOwnerGrantRow, verifyOwnerGrantRedemption } from './peer-authorization.js';
|
|
24
|
+
import { OWNER_KEY_MAX_LABEL } from './owner-key-export.js';
|
|
25
|
+
import { CADRE_INVITATION_PREFIX, CADRE_JOIN_REQUEST_PREFIX, OWNER_GRANT_PREFIX, OWNER_KEY_EXPORT_STEM } from './cadre-join-request.js';
|
|
26
|
+
import { MemberRedemptionHandler, constraintRefusal, dialRedemption, dialRedemptionInPasses, isBoundedString, isEd25519KeyB64, isNullableBoundedString, isNullableUseCap, rejection, writeFailureRefusal } from './member-redemption.js';
|
|
27
|
+
const log = debug('sereus:cadre:owner-grant-proto');
|
|
28
|
+
/** Protocol id for owner grant redemption (parallel to `/sereus/cadre-invite/1.0.0`). */
|
|
29
|
+
export const OWNER_GRANT_PROTOCOL = '/sereus/owner-grant/1.0.0';
|
|
30
|
+
/** What the dialer calls the credential in log lines, deadline labels and error text. */
|
|
31
|
+
const LABEL = 'Owner grant';
|
|
32
|
+
/** Most machines a grant bundle may name; the issuer caps its own list far below this. */
|
|
33
|
+
const MAX_BUNDLE_MEMBERS = 64;
|
|
34
|
+
/**
|
|
35
|
+
* Encode a bundle for out-of-band delivery: {@link OWNER_GRANT_PREFIX}, then base64url over
|
|
36
|
+
* JSON. The prefix lets an app tell a grant from an invitation, a join request or an owner key
|
|
37
|
+
* and name the mistake when one is pasted for the other.
|
|
38
|
+
*/
|
|
39
|
+
export function encodeOwnerGrant(bundle) {
|
|
40
|
+
return `${OWNER_GRANT_PREFIX}${uint8ArrayToString(new TextEncoder().encode(JSON.stringify(bundle)), 'base64url')}`;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Decode a bundle, refusing anything that is not a well-formed version-1
|
|
44
|
+
* {@link OwnerGrantBundle} behind {@link OWNER_GRANT_PREFIX}. Standalone, like
|
|
45
|
+
* `decodeCadreInvitation`, so a client without a node can decode one.
|
|
46
|
+
*
|
|
47
|
+
* @throws on an owner key, a cadre invitation or a join request, a string without the prefix
|
|
48
|
+
* or that is not base64url JSON after it, or one that decodes to another shape or version.
|
|
49
|
+
*/
|
|
50
|
+
export function decodeOwnerGrant(encoded) {
|
|
51
|
+
const trimmed = encoded.trim();
|
|
52
|
+
if (trimmed.startsWith(OWNER_KEY_EXPORT_STEM)) {
|
|
53
|
+
throw new Error('This is an owner key, not an owner grant; it is private, so import it only where the app asks for an owner key');
|
|
54
|
+
}
|
|
55
|
+
if (trimmed.startsWith(CADRE_INVITATION_PREFIX)) {
|
|
56
|
+
throw new Error('This is a cadre invitation, not an owner grant; paste it where the app asks for an invitation');
|
|
57
|
+
}
|
|
58
|
+
if (trimmed.startsWith(CADRE_JOIN_REQUEST_PREFIX)) {
|
|
59
|
+
throw new Error('This is a join request, not an owner grant; paste it into the app that manages your cadre');
|
|
60
|
+
}
|
|
61
|
+
if (!trimmed.startsWith(OWNER_GRANT_PREFIX)) {
|
|
62
|
+
throw new Error(`Owner grant must start with "${OWNER_GRANT_PREFIX}" (this one starts with "${trimmed.slice(0, OWNER_GRANT_PREFIX.length)}")`);
|
|
63
|
+
}
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(new TextDecoder().decode(uint8ArrayFromString(trimmed.slice(OWNER_GRANT_PREFIX.length), 'base64url')));
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw new Error('Owner grant does not decode as base64url JSON', { cause: error });
|
|
70
|
+
}
|
|
71
|
+
if (!isWellFormedOwnerGrantBundle(parsed)) {
|
|
72
|
+
throw new Error('Owner grant is malformed or of an unsupported version');
|
|
73
|
+
}
|
|
74
|
+
return parsed;
|
|
75
|
+
}
|
|
76
|
+
function isWellFormedOwnerGrantBundle(value) {
|
|
77
|
+
if (typeof value !== 'object' || value === null)
|
|
78
|
+
return false;
|
|
79
|
+
const { v, partyId, grantPrivateKey, grant, ownerKeys, members } = value;
|
|
80
|
+
return v === 1
|
|
81
|
+
&& isBoundedString(partyId)
|
|
82
|
+
&& isEd25519KeyB64(grantPrivateKey)
|
|
83
|
+
&& isWellFormedOwnerGrantRow(grant)
|
|
84
|
+
&& Array.isArray(ownerKeys) && ownerKeys.length > 0 && ownerKeys.every((key) => isEd25519KeyB64(key))
|
|
85
|
+
&& Array.isArray(members) && members.length <= MAX_BUNDLE_MEMBERS && members.every((addr) => isBoundedString(addr));
|
|
86
|
+
}
|
|
87
|
+
/** Is `value` an {@link OwnerGrantRow} with every column in its stored form and bounded? */
|
|
88
|
+
export function isWellFormedOwnerGrantRow(value) {
|
|
89
|
+
if (typeof value !== 'object' || value === null)
|
|
90
|
+
return false;
|
|
91
|
+
const row = value;
|
|
92
|
+
return isEd25519KeyB64(row.key)
|
|
93
|
+
&& isNullableBoundedString(row.label, OWNER_KEY_MAX_LABEL)
|
|
94
|
+
&& isNullableBoundedString(row.expiresAt)
|
|
95
|
+
&& isNullableUseCap(row.totalUses)
|
|
96
|
+
&& isBoundedString(row.stampId)
|
|
97
|
+
&& isEd25519KeyB64(row.issuerKey)
|
|
98
|
+
&& isBoundedString(row.issuerSig);
|
|
99
|
+
}
|
|
100
|
+
/** Which refusals are worth repeating at the next address — the one place that says so. */
|
|
101
|
+
export const OWNER_GRANT_REJECTION_RETRYABLE = {
|
|
102
|
+
'issuer-unknown': true,
|
|
103
|
+
'grant-invalid': false,
|
|
104
|
+
'grant-spent': false,
|
|
105
|
+
'party-mismatch': false,
|
|
106
|
+
'busy': true,
|
|
107
|
+
'conflict': true,
|
|
108
|
+
'internal': true
|
|
109
|
+
};
|
|
110
|
+
/** Is `value` a code this build knows? Own keys only, so a peer's `'constructor'` does not pass. */
|
|
111
|
+
export function isOwnerGrantRejectionCode(value) {
|
|
112
|
+
return typeof value === 'string' && Object.prototype.hasOwnProperty.call(OWNER_GRANT_REJECTION_RETRYABLE, value);
|
|
113
|
+
}
|
|
114
|
+
// ── Errors the holder sees ───────────────────────────────────────────────────
|
|
115
|
+
/**
|
|
116
|
+
* A member answered and refused (on `CadreNode.redeemOwnerGrant`'s local path, the member is
|
|
117
|
+
* this node). `code` is `'unrecognized'` when the frame carried none this build knows, and such
|
|
118
|
+
* a refusal counts as retryable, as the invitation's does.
|
|
119
|
+
*/
|
|
120
|
+
export class OwnerGrantRejectedError extends Error {
|
|
121
|
+
/** Both arguments come straight off the wire, so neither is trusted to have its declared type. */
|
|
122
|
+
constructor(code, reason) {
|
|
123
|
+
const text = typeof reason === 'string' ? reason : 'no reason provided';
|
|
124
|
+
super(`Owner grant refused: ${text}`);
|
|
125
|
+
this.name = 'OwnerGrantRejectedError';
|
|
126
|
+
this.code = isOwnerGrantRejectionCode(code) ? code : 'unrecognized';
|
|
127
|
+
this.reason = text;
|
|
128
|
+
this.retryable = this.code === 'unrecognized' || OWNER_GRANT_REJECTION_RETRYABLE[this.code];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Every address the bundle names was tried and none accepted. Always retryable; `outcomes` says what each address did. */
|
|
132
|
+
export class OwnerGrantUnreachableError extends Error {
|
|
133
|
+
constructor(outcomes) {
|
|
134
|
+
super(outcomes.length === 0
|
|
135
|
+
? 'Owner grant names no dialable member'
|
|
136
|
+
: `Owner grant could not be redeemed at any of its ${outcomes.length} address(es): `
|
|
137
|
+
+ outcomes.map((outcome) => `${outcome.addr}: ${outcome.error.message}`).join('; '));
|
|
138
|
+
this.outcomes = outcomes;
|
|
139
|
+
this.retryable = true;
|
|
140
|
+
this.name = 'OwnerGrantUnreachableError';
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* A member accepted but its reply does not check out: another party id, another grant key, or
|
|
145
|
+
* a row not signed by one of the pinned owner keys. Final, for the invitation's reason: such a
|
|
146
|
+
* machine is not a member of the cadre the grant names.
|
|
147
|
+
*/
|
|
148
|
+
export class OwnerGrantReplyInvalidError extends Error {
|
|
149
|
+
constructor(message) {
|
|
150
|
+
super(`Owner grant reply refused: ${message}`);
|
|
151
|
+
this.retryable = false;
|
|
152
|
+
this.name = 'OwnerGrantReplyInvalidError';
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// ── Shape checks ─────────────────────────────────────────────────────────────
|
|
156
|
+
function isWellFormedRedeemRequest(value) {
|
|
157
|
+
if (typeof value !== 'object' || value === null)
|
|
158
|
+
return false;
|
|
159
|
+
const { v, partyId, grant, ownerKey, usageStampId, grantSig, ownerSig } = value;
|
|
160
|
+
return v === 1
|
|
161
|
+
&& isBoundedString(partyId)
|
|
162
|
+
&& isWellFormedOwnerGrantRow(grant)
|
|
163
|
+
&& isEd25519KeyB64(ownerKey)
|
|
164
|
+
&& isBoundedString(usageStampId)
|
|
165
|
+
&& isBoundedString(grantSig)
|
|
166
|
+
&& isBoundedString(ownerSig);
|
|
167
|
+
}
|
|
168
|
+
/** An acceptance frame's fields. Throws on a malformed one. */
|
|
169
|
+
function decodeAccepted(reply) {
|
|
170
|
+
if (!isBoundedString(reply.partyId) || !isWellFormedOwnerGrantRow(reply.grant)) {
|
|
171
|
+
throw new Error('Owner grant acceptance is missing its party id or a well-formed row');
|
|
172
|
+
}
|
|
173
|
+
return { accepted: true, partyId: reply.partyId, grant: reply.grant, alreadyOwner: reply.alreadyOwner === true };
|
|
174
|
+
}
|
|
175
|
+
// ── Request construction (holder side) ───────────────────────────────────────
|
|
176
|
+
/**
|
|
177
|
+
* Build and sign the request for `bundle`: the holder's `'redeem'` signature with the grant's
|
|
178
|
+
* private key (held in memory for the length of this call) and the seated key's `'consent'`
|
|
179
|
+
* signature through `signConsent`, the `sign` of an open owner approval on the signer that
|
|
180
|
+
* holds `ownerKey`; both over (grant key, `usageStampId`, `ownerKey`) for the grant's party. The
|
|
181
|
+
* same bytes the schema verifies at the member (`ownerGrantRedeemMessage` /
|
|
182
|
+
* `ownerGrantConsentMessage`), and the same request goes to every member and every pass.
|
|
183
|
+
*/
|
|
184
|
+
export async function signOwnerGrantRequest(bundle, ownerKey, signConsent, usageStampId) {
|
|
185
|
+
const fields = { grantKey: bundle.grant.key, usageStampId, seatedKey: ownerKey };
|
|
186
|
+
const grantSig = sign(ownerGrantRedeemMessage(bundle.partyId, fields), bundle.grantPrivateKey, 'ed25519', 'bytes', 'base64url', 'base64url');
|
|
187
|
+
const ownerSig = await signConsent(ownerGrantConsentMessage(bundle.partyId, fields));
|
|
188
|
+
return { v: 1, partyId: bundle.partyId, grant: bundle.grant, ownerKey, usageStampId, grantSig, ownerSig };
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* The holder's check on an acceptance: the same party, the same grant key, and a row signed by
|
|
192
|
+
* an issuer the holder pinned from the bundle. Throws {@link OwnerGrantReplyInvalidError} naming
|
|
193
|
+
* the first failure. Bounded as `verifyRedeemReply` is: it catches a wrong-party or wrong-row
|
|
194
|
+
* answer, not a forged member, which the holder's anchor settles once its store syncs.
|
|
195
|
+
*/
|
|
196
|
+
export function verifyOwnerGrantReply(reply, bundle, isTrustedIssuer) {
|
|
197
|
+
if (reply.partyId !== bundle.partyId) {
|
|
198
|
+
throw new OwnerGrantReplyInvalidError(`the member serves party ${reply.partyId}, the grant names ${bundle.partyId}`);
|
|
199
|
+
}
|
|
200
|
+
if (reply.grant.key !== bundle.grant.key) {
|
|
201
|
+
throw new OwnerGrantReplyInvalidError('the member answered for a different grant');
|
|
202
|
+
}
|
|
203
|
+
if (!isTrustedIssuer(reply.grant.issuerKey)) {
|
|
204
|
+
throw new OwnerGrantReplyInvalidError('the row the member holds is not issued by one of the grant\'s owner keys');
|
|
205
|
+
}
|
|
206
|
+
if (!verifyOwnerGrantRow(bundle.partyId, reply.grant)) {
|
|
207
|
+
throw new OwnerGrantReplyInvalidError('the row the member holds is not signed by its issuer');
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/** Constraint names the schema refuses a redemption with, and the code each maps to. */
|
|
211
|
+
const REDEEM_CONSTRAINT_CODES = [
|
|
212
|
+
[/CHECK constraint failed: Authorized\b/, 'grant-spent', 'The grant is expired, withdrawn, exhausted, or its issuer is no longer an owner'],
|
|
213
|
+
[/CHECK constraint failed: (GrantPossessed|OwnerConsented|SeatedExists)\b/, 'grant-invalid', 'The redemption does not satisfy the grant'],
|
|
214
|
+
[/CHECK constraint failed: NotRevoked\b/, 'grant-spent', 'The grant was withdrawn'],
|
|
215
|
+
];
|
|
216
|
+
/**
|
|
217
|
+
* Member side of the grant protocol (`MemberRedemptionHandler` has the shape): after the
|
|
218
|
+
* request's shape, it checks the party and both signatures — nothing about the connecting
|
|
219
|
+
* peer, whose identity is unrelated to the key it seats — then seats the row from the bundle
|
|
220
|
+
* when this member holds none and redeems. An issuer this member does not know as an owner is
|
|
221
|
+
* the one retryable refusal left: the holder tries the next address, which may hold the
|
|
222
|
+
* issuer's row.
|
|
223
|
+
*/
|
|
224
|
+
export class OwnerGrantHandler extends MemberRedemptionHandler {
|
|
225
|
+
constructor(options) {
|
|
226
|
+
super('Owner grant', OWNER_GRANT_PROTOCOL, options);
|
|
227
|
+
this.busyRefusal = rejection('busy', 'Too many concurrent redemptions');
|
|
228
|
+
this.internalRefusal = rejection('internal', 'Internal redemption error');
|
|
229
|
+
this.malformedRefusal = rejection('grant-invalid', 'Malformed redemption request');
|
|
230
|
+
this.store = options.store;
|
|
231
|
+
}
|
|
232
|
+
isWellFormedRequest(value) {
|
|
233
|
+
return isWellFormedRedeemRequest(value);
|
|
234
|
+
}
|
|
235
|
+
checkRequest(request) {
|
|
236
|
+
if (request.partyId !== this.partyId) {
|
|
237
|
+
return rejection('party-mismatch', 'This machine serves another cadre');
|
|
238
|
+
}
|
|
239
|
+
const fields = { grantKey: request.grant.key, usageStampId: request.usageStampId, seatedKey: request.ownerKey };
|
|
240
|
+
if (!verifyOwnerGrantRedemption(this.partyId, fields, request.grantSig, request.ownerSig)) {
|
|
241
|
+
return rejection('grant-invalid', 'The redemption signatures do not verify');
|
|
242
|
+
}
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
readHeld(request) {
|
|
246
|
+
return this.store.queryOwnerGrant(request.grant.key);
|
|
247
|
+
}
|
|
248
|
+
isLive(request, held) {
|
|
249
|
+
return this.store.isOwnerGrantLive(request.grant, held);
|
|
250
|
+
}
|
|
251
|
+
/** The two writes — the seat only when this member holds no row, and the redemption, carrying the held row — as `CadreInviteHandler.redeemAdmitted`. */
|
|
252
|
+
async redeemAdmitted(request, remotePeerId, held) {
|
|
253
|
+
if (held === null) {
|
|
254
|
+
const refused = await this.seat(request, remotePeerId);
|
|
255
|
+
if (refused)
|
|
256
|
+
return refused;
|
|
257
|
+
}
|
|
258
|
+
let alreadyOwner;
|
|
259
|
+
try {
|
|
260
|
+
const result = await this.store.redeemOwnerGrant({
|
|
261
|
+
grantKey: request.grant.key,
|
|
262
|
+
...(held ? { held } : {}),
|
|
263
|
+
seatedKey: request.ownerKey,
|
|
264
|
+
usageStampId: request.usageStampId,
|
|
265
|
+
grantSig: request.grantSig,
|
|
266
|
+
ownerSig: request.ownerSig
|
|
267
|
+
});
|
|
268
|
+
alreadyOwner = result.alreadyOwner;
|
|
269
|
+
log('grant %s redeemed for %s by %s (alreadyOwner=%s)', request.grant.key, request.ownerKey, remotePeerId, alreadyOwner);
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
if (error instanceof InvitationExhaustedError) {
|
|
273
|
+
return rejection('grant-spent', 'The grant has no uses left');
|
|
274
|
+
}
|
|
275
|
+
log('redeeming grant %s for %s failed: %o', request.grant.key, request.ownerKey, error);
|
|
276
|
+
return constraintRefusal(error, REDEEM_CONSTRAINT_CODES) ?? writeFailureRefusal(error);
|
|
277
|
+
}
|
|
278
|
+
// The row as held: read before the write, or the bundle's copy the seat inserted verbatim or found.
|
|
279
|
+
return { accepted: true, partyId: this.partyId, grant: held ?? request.grant, alreadyOwner };
|
|
280
|
+
}
|
|
281
|
+
/** Seat the bundle's copy of the row; a refusal answered by name, or null once it is held. */
|
|
282
|
+
async seat(request, remotePeerId) {
|
|
283
|
+
try {
|
|
284
|
+
await this.store.seatOwnerGrant(request.grant);
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
if (error instanceof OwnerGrantIssuerUnknownError) {
|
|
289
|
+
return rejection('issuer-unknown', 'The grant\'s issuer is not known as an owner here');
|
|
290
|
+
}
|
|
291
|
+
log('seating grant %s from %s failed: %o', request.grant.key, remotePeerId, error);
|
|
292
|
+
return constraintRefusal(error, REDEEM_CONSTRAINT_CODES) ?? writeFailureRefusal(error);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
/** The grant's half of the shared dial loop: its label, protocol, bundle, codec, checks and errors. */
|
|
297
|
+
function grantRedemption(options) {
|
|
298
|
+
return {
|
|
299
|
+
label: LABEL,
|
|
300
|
+
protocolId: options.protocolId ?? OWNER_GRANT_PROTOCOL,
|
|
301
|
+
members: options.bundle.members,
|
|
302
|
+
request: options.request,
|
|
303
|
+
decodeAccepted,
|
|
304
|
+
refusal: (code, reason) => new OwnerGrantRejectedError(code, reason),
|
|
305
|
+
verifyAccepted: (reply) => verifyOwnerGrantReply(reply, options.bundle, options.isTrustedIssuer),
|
|
306
|
+
unreachable: (outcomes) => new OwnerGrantUnreachableError(outcomes),
|
|
307
|
+
...(options.linkRoundTripMs !== undefined ? { linkRoundTripMs: options.linkRoundTripMs } : {}),
|
|
308
|
+
...(options.addressBudgetMs !== undefined ? { addressBudgetMs: options.addressBudgetMs } : {}),
|
|
309
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Holder side: try the bundle's addresses one at a time and stop at the first acceptance or
|
|
314
|
+
* the first final refusal (`dialRedemption` in `member-redemption.ts`). Every address gets the
|
|
315
|
+
* SAME request, with one `usageStampId`: `OwnerGrantUsage` is keyed by it, so a member the
|
|
316
|
+
* holder gave up on that was still committing cannot spend a second use, and a retry after a
|
|
317
|
+
* dropped reply is answered `alreadyOwner`.
|
|
318
|
+
*
|
|
319
|
+
* @throws {OwnerGrantRejectedError} on a final refusal (its `retryable` is false).
|
|
320
|
+
* @throws {OwnerGrantReplyInvalidError} on an acceptance that fails {@link verifyOwnerGrantReply}.
|
|
321
|
+
* @throws {OwnerGrantUnreachableError} when every address was tried and none accepted.
|
|
322
|
+
* @throws the reason of `options.signal` once it aborts.
|
|
323
|
+
*/
|
|
324
|
+
export function redeemOwnerGrantAtMembers(node, options) {
|
|
325
|
+
return dialRedemption(node, grantRedemption(options));
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Holder side, in passes (`dialRedemptionInPasses` in `member-redemption.ts`): one
|
|
329
|
+
* {@link redeemOwnerGrantAtMembers} pass, or, with `waitForMember`, as many as it takes,
|
|
330
|
+
* pausing between them and never past the grant's expiry, which by this holder's clock is
|
|
331
|
+
* thrown as the `grant-spent` refusal a member would give. A backup grant has no expiry, so a
|
|
332
|
+
* wait on one ends only by acceptance, a final refusal, the signal or the node stopping.
|
|
333
|
+
*/
|
|
334
|
+
export function redeemOwnerGrantInPasses(node, options) {
|
|
335
|
+
const passes = {
|
|
336
|
+
...grantRedemption(options),
|
|
337
|
+
waitForMember: options.waitForMember,
|
|
338
|
+
expiresAt: options.bundle.grant.expiresAt,
|
|
339
|
+
expired: () => new OwnerGrantRejectedError('grant-spent', 'the grant expired before a member accepted it'),
|
|
340
|
+
...(options.onProgress ? { onProgress: options.onProgress } : {}),
|
|
341
|
+
};
|
|
342
|
+
return dialRedemptionInPasses(node, passes);
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=owner-grant-protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"owner-grant-protocol.js","sourceRoot":"","sources":["../src/owner-grant-protocol.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** Version 1 of the plain export. */
|
|
2
|
+
export declare const OWNER_KEY_EXPORT_PREFIX = "sereus-owner-key:1.";
|
|
3
|
+
/** Version 1 of the passphrase-locked export. */
|
|
4
|
+
export declare const LOCKED_OWNER_KEY_EXPORT_PREFIX = "sereus-owner-key-locked:1.";
|
|
5
|
+
/** Longest label an owner key carries, the user's name for it ("phone", "paper backup"): in an export and on its `OwnerKey` row (the control schema's `LabelLength` check). */
|
|
6
|
+
export declare const OWNER_KEY_MAX_LABEL = 64;
|
|
7
|
+
export type OwnerKeyExportErrorCode =
|
|
8
|
+
/** Owner-key text whose body does not decode, or a version this build does not read. */
|
|
9
|
+
'malformed'
|
|
10
|
+
/** The passphrase does not open a locked export, or the locked text was altered. */
|
|
11
|
+
| 'wrong-passphrase'
|
|
12
|
+
/** A locked export given with no passphrase (`importOwnerKeyInto` in `owner-key-flows.ts`). */
|
|
13
|
+
| 'passphrase-required'
|
|
14
|
+
/** Not owner-key text; the message names an invitation, an owner grant, a join request or a join code. */
|
|
15
|
+
| 'not-an-owner-key'
|
|
16
|
+
/** A locked export asks for more scrypt work than {@link MAX_SCRYPT_N} and {@link MAX_SCRYPT_R_TIMES_P} allow. */
|
|
17
|
+
| 'cost-too-high';
|
|
18
|
+
/** Every refusal from this module. The message is for the user and never includes key material. */
|
|
19
|
+
export declare class OwnerKeyExportError extends Error {
|
|
20
|
+
readonly code: OwnerKeyExportErrorCode;
|
|
21
|
+
constructor(code: OwnerKeyExportErrorCode, message: string, options?: {
|
|
22
|
+
cause?: unknown;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/** What an export holds once read. */
|
|
26
|
+
export interface OwnerKeyExportContents {
|
|
27
|
+
/** The 32-byte Ed25519 seed, base64url: the input to `importOwnerKey`. */
|
|
28
|
+
privateKeyB64: string;
|
|
29
|
+
/** The matching public key, base64url: the `OwnerKey.Key` it signs for. */
|
|
30
|
+
publicKey: string;
|
|
31
|
+
label?: string;
|
|
32
|
+
}
|
|
33
|
+
/** {@link decodeOwnerKeyExport}'s answer: the key, or word that a passphrase is needed ({@link unlockOwnerKeyExport}). */
|
|
34
|
+
export type DecodedOwnerKeyExport = ({
|
|
35
|
+
locked: false;
|
|
36
|
+
} & OwnerKeyExportContents) | {
|
|
37
|
+
locked: true;
|
|
38
|
+
};
|
|
39
|
+
export interface OwnerKeyExportInput {
|
|
40
|
+
/** The 32-byte Ed25519 seed, base64url (`readOwnerPrivateKey`). */
|
|
41
|
+
privateKeyB64: string;
|
|
42
|
+
/** Trimmed; at most {@link OWNER_KEY_MAX_LABEL} characters; a blank one is left out. */
|
|
43
|
+
label?: string;
|
|
44
|
+
/** Locks the export when present; must not be empty. */
|
|
45
|
+
passphrase?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Encode an owner key as text, locked under `passphrase` when one is given.
|
|
49
|
+
*
|
|
50
|
+
* @throws {OwnerKeyExportError} `malformed` for a key that is not a 32-byte seed, an over-long
|
|
51
|
+
* label or an empty passphrase.
|
|
52
|
+
*/
|
|
53
|
+
export declare function encodeOwnerKeyExport(input: OwnerKeyExportInput): Promise<string>;
|
|
54
|
+
/**
|
|
55
|
+
* Read owner-key text without a passphrase: a plain export's key, or `{ locked: true }` for a
|
|
56
|
+
* locked one whose envelope is well formed and whose scrypt cost is within the cap, so an app
|
|
57
|
+
* refuses a hostile file before asking for the passphrase.
|
|
58
|
+
*
|
|
59
|
+
* @throws {OwnerKeyExportError} `not-an-owner-key` (naming an invitation, an owner grant, a join
|
|
60
|
+
* request or a join code), `malformed` or `cost-too-high`.
|
|
61
|
+
*/
|
|
62
|
+
export declare function decodeOwnerKeyExport(text: string): DecodedOwnerKeyExport;
|
|
63
|
+
/**
|
|
64
|
+
* Read owner-key text with its passphrase. A plain export is read as {@link decodeOwnerKeyExport}
|
|
65
|
+
* reads it, the passphrase unused.
|
|
66
|
+
*
|
|
67
|
+
* @throws {OwnerKeyExportError} as {@link decodeOwnerKeyExport}, and `wrong-passphrase` when the
|
|
68
|
+
* passphrase does not open it or the sealed part, nonce or scrypt parameters were altered.
|
|
69
|
+
*/
|
|
70
|
+
export declare function unlockOwnerKeyExport(text: string, passphrase: string): Promise<OwnerKeyExportContents>;
|