@serfab/cadre-core 0.7.1 → 0.8.1
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 +9 -8
- package/dist/authority-key.d.ts +41 -0
- package/dist/authority-key.js +52 -0
- package/dist/authority-key.js.map +1 -0
- package/dist/cadre-node.d.ts +770 -11
- package/dist/cadre-node.js +1900 -88
- package/dist/cadre-node.js.map +1 -1
- package/dist/canonical-datetime.d.ts +33 -0
- package/dist/canonical-datetime.js +38 -0
- package/dist/canonical-datetime.js.map +1 -0
- package/dist/canonical-json.d.ts +12 -0
- package/dist/canonical-json.js +33 -0
- package/dist/canonical-json.js.map +1 -0
- package/dist/control-cohort.d.ts +63 -0
- package/dist/control-cohort.js +80 -0
- package/dist/control-cohort.js.map +1 -0
- package/dist/control-database.d.ts +259 -5
- package/dist/control-database.js +541 -128
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +89 -0
- package/dist/control-formation-recorder.js +139 -0
- package/dist/control-formation-recorder.js.map +1 -0
- package/dist/control-schema.d.ts +12 -0
- package/dist/control-schema.js +199 -0
- package/dist/control-schema.js.map +1 -0
- package/dist/control-stream.d.ts +54 -0
- package/dist/control-stream.js +104 -0
- package/dist/control-stream.js.map +1 -0
- package/dist/device-token.d.ts +50 -0
- package/dist/device-token.js +66 -0
- package/dist/device-token.js.map +1 -0
- package/dist/diagnostics/connection-path.d.ts +118 -0
- package/dist/diagnostics/connection-path.js +204 -0
- package/dist/diagnostics/connection-path.js.map +1 -0
- package/dist/diagnostics/webrtc-turn-tracker.d.ts +50 -0
- package/dist/diagnostics/webrtc-turn-tracker.js +196 -0
- package/dist/diagnostics/webrtc-turn-tracker.js.map +1 -0
- package/dist/hibernation-manager.d.ts +79 -0
- package/dist/hibernation-manager.js +173 -23
- package/dist/hibernation-manager.js.map +1 -1
- package/dist/index.d.ts +25 -4
- package/dist/index.js +59 -4
- package/dist/index.js.map +1 -1
- package/dist/key-store-file.d.ts +52 -0
- package/dist/key-store-file.js +194 -0
- package/dist/key-store-file.js.map +1 -0
- package/dist/key-store.d.ts +75 -0
- package/dist/key-store.js +66 -0
- package/dist/key-store.js.map +1 -0
- package/dist/peer-authorization.d.ts +26 -0
- package/dist/peer-authorization.js +40 -0
- package/dist/peer-authorization.js.map +1 -0
- package/dist/peer-record.d.ts +89 -0
- package/dist/peer-record.js +117 -0
- package/dist/peer-record.js.map +1 -0
- package/dist/push-credentials.d.ts +34 -0
- package/dist/push-credentials.js +81 -0
- package/dist/push-credentials.js.map +1 -0
- package/dist/push-fanout.d.ts +137 -0
- package/dist/push-fanout.js +197 -0
- package/dist/push-fanout.js.map +1 -0
- package/dist/push-notifier-apns.d.ts +53 -0
- package/dist/push-notifier-apns.js +195 -0
- package/dist/push-notifier-apns.js.map +1 -0
- package/dist/push-notifier-fcm.d.ts +38 -0
- package/dist/push-notifier-fcm.js +183 -0
- package/dist/push-notifier-fcm.js.map +1 -0
- package/dist/push-notifier-shared.d.ts +18 -0
- package/dist/push-notifier-shared.js +25 -0
- package/dist/push-notifier-shared.js.map +1 -0
- package/dist/push-notifier.d.ts +71 -0
- package/dist/push-notifier.js +50 -0
- package/dist/push-notifier.js.map +1 -0
- package/dist/schema-verification.d.ts +11 -2
- package/dist/schema-verification.js +15 -4
- package/dist/schema-verification.js.map +1 -1
- package/dist/seed-bootstrap.d.ts +197 -9
- package/dist/seed-bootstrap.js +396 -137
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.d.ts +63 -0
- package/dist/seed-trust-policy.js +56 -0
- package/dist/seed-trust-policy.js.map +1 -0
- package/dist/strand-addr-protocol.d.ts +132 -0
- package/dist/strand-addr-protocol.js +313 -0
- package/dist/strand-addr-protocol.js.map +1 -0
- package/dist/strand-cohort.d.ts +54 -0
- package/dist/strand-cohort.js +38 -0
- package/dist/strand-cohort.js.map +1 -0
- package/dist/strand-database.d.ts +54 -6
- package/dist/strand-database.js +88 -100
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-formation-manager.d.ts +79 -19
- package/dist/strand-formation-manager.js +196 -104
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +162 -0
- package/dist/strand-formation-protocol.js +279 -0
- package/dist/strand-formation-protocol.js.map +1 -0
- package/dist/strand-instance-manager.d.ts +73 -4
- package/dist/strand-instance-manager.js +167 -41
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-member-key.d.ts +29 -0
- package/dist/strand-member-key.js +38 -0
- package/dist/strand-member-key.js.map +1 -0
- package/dist/strand-member-registry.d.ts +98 -0
- package/dist/strand-member-registry.js +129 -0
- package/dist/strand-member-registry.js.map +1 -0
- package/dist/strand-membership-writer.d.ts +361 -0
- package/dist/strand-membership-writer.js +449 -0
- package/dist/strand-membership-writer.js.map +1 -0
- package/dist/strand-solicitation.d.ts +77 -2
- package/dist/strand-solicitation.js +25 -4
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-wake-payload.d.ts +30 -0
- package/dist/strand-wake-payload.js +20 -0
- package/dist/strand-wake-payload.js.map +1 -0
- package/dist/strand-wake-protocol.d.ts +114 -0
- package/dist/strand-wake-protocol.js +257 -0
- package/dist/strand-wake-protocol.js.map +1 -0
- package/dist/strand-watcher.d.ts +6 -2
- package/dist/strand-watcher.js +57 -17
- package/dist/strand-watcher.js.map +1 -1
- package/dist/types.d.ts +406 -23
- package/dist/types.js +22 -5
- package/dist/types.js.map +1 -1
- package/package.json +14 -8
- package/src/authority-key.ts +68 -0
- package/src/cadre-node.ts +2937 -948
- package/src/canonical-datetime.ts +39 -0
- package/src/canonical-json.ts +30 -0
- package/src/control-cohort.ts +103 -0
- package/src/control-database.ts +931 -383
- package/src/control-formation-recorder.ts +151 -0
- package/src/control-schema.ts +198 -0
- package/src/control-stream.ts +128 -0
- package/src/device-token.ts +89 -0
- package/src/diagnostics/connection-path.ts +277 -0
- package/src/diagnostics/webrtc-turn-tracker.ts +222 -0
- package/src/hibernation-manager.ts +197 -28
- package/src/index.ts +273 -79
- package/src/key-store-file.ts +203 -0
- package/src/key-store.ts +99 -0
- package/src/peer-authorization.ts +53 -0
- package/src/peer-record.ts +146 -0
- package/src/push-credentials.ts +84 -0
- package/src/push-fanout.ts +272 -0
- package/src/push-notifier-apns.ts +243 -0
- package/src/push-notifier-fcm.ts +221 -0
- package/src/push-notifier-shared.ts +28 -0
- package/src/push-notifier.ts +93 -0
- package/src/schema-verification.ts +18 -4
- package/src/seed-bootstrap.ts +464 -188
- package/src/seed-trust-policy.ts +107 -0
- package/src/strand-addr-protocol.ts +410 -0
- package/src/strand-cohort.ts +77 -0
- package/src/strand-database.ts +113 -141
- package/src/strand-formation-manager.ts +233 -142
- package/src/strand-formation-protocol.ts +391 -0
- package/src/strand-instance-manager.ts +207 -56
- package/src/strand-member-key.ts +39 -0
- package/src/strand-member-registry.ts +177 -0
- package/src/strand-membership-writer.ts +640 -0
- package/src/strand-solicitation.ts +93 -4
- package/src/strand-wake-payload.ts +32 -0
- package/src/strand-wake-protocol.ts +331 -0
- package/src/strand-watcher.ts +61 -17
- package/src/types.ts +1012 -582
|
@@ -1,7 +1,36 @@
|
|
|
1
1
|
import { Database } from '@quereus/quereus';
|
|
2
2
|
import type { Libp2p } from '@libp2p/interface';
|
|
3
3
|
import type { IRepo } from '@optimystic/db-core';
|
|
4
|
-
import type { StrandRow } from './types.js';
|
|
4
|
+
import type { StrandRow, PeerAddressRecord, DeviceTokenRecord } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* Build the canonical authorization message that authority signatures are bound to.
|
|
7
|
+
*
|
|
8
|
+
* The message is a SINGLE framed SHA-256 digest over the ordered tuple of fields (the
|
|
9
|
+
* crypto plugin's injective multi-field encoding), in a fixed field order, with the
|
|
10
|
+
* single-use StampId as the final field:
|
|
11
|
+
*
|
|
12
|
+
* message = sha256(encodeFields([field_1, field_2, ..., StampId])) // raw digest bytes
|
|
13
|
+
*
|
|
14
|
+
* ed25519 signs these raw digest bytes DIRECTLY (no second hash). The SQL constraints
|
|
15
|
+
* verify the identical bytes with one variadic call
|
|
16
|
+
* (`verify(digest(field_1, ..., StampId), context.Signature, A.Key, 'ed25519')`): SQL
|
|
17
|
+
* `digest(...)` returns the base64url string of the same digest, which `verify`'s default
|
|
18
|
+
* base64url input encoding decodes back to those raw bytes — so signer and verifier
|
|
19
|
+
* operate on the same bytes. Every field is TEXT on both sides (the SQL columns are
|
|
20
|
+
* `cast(... as text)` / `coalesce(...,'')`; the TS args are strings), so the per-field
|
|
21
|
+
* type tags agree. This binds the signature to the row contents (not a bare stamp),
|
|
22
|
+
* closing the captured-stamp replay / privilege-escalation hole. Single source of truth:
|
|
23
|
+
* every signed writer (and every test/harness signer) MUST build the message through this
|
|
24
|
+
* function in the schema's field order, or `verify` will reject the row.
|
|
25
|
+
*/
|
|
26
|
+
export declare function buildAuthorizationMessage(fields: string[]): Uint8Array;
|
|
27
|
+
/**
|
|
28
|
+
* Names of the CadreControl tables that {@link ControlDatabase.countRows} can
|
|
29
|
+
* read. Constraining to this union keeps the dynamic `from` clause off the
|
|
30
|
+
* SQL-injection surface and hands callers (e.g. the integration harness's
|
|
31
|
+
* `waitForControlSync`) a typo-proof table argument.
|
|
32
|
+
*/
|
|
33
|
+
export type ControlTable = 'AuthorityKey' | 'ValidationKey' | 'Strand' | 'CadrePeer' | 'DeviceToken' | 'FormationInvite' | 'FormationUsage';
|
|
5
34
|
export interface ControlDatabaseConfig {
|
|
6
35
|
/** Party ID for the control network */
|
|
7
36
|
partyId: string;
|
|
@@ -35,25 +64,250 @@ export declare class ControlDatabase {
|
|
|
35
64
|
* Query all strands from the control database
|
|
36
65
|
*/
|
|
37
66
|
queryStrands(): Promise<StrandRow[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Read a single strand row by id, or null when absent. Single-row sibling of
|
|
69
|
+
* {@link queryStrands}; the responder uses it to read a host strand's
|
|
70
|
+
* `MemberPrivateKey` (the closed-strand read-gating secret) for delivery to a
|
|
71
|
+
* validated invitee during provision-then-record formation.
|
|
72
|
+
*/
|
|
73
|
+
queryStrand(strandId: string): Promise<StrandRow | null>;
|
|
74
|
+
/**
|
|
75
|
+
* Count rows in a CadreControl table as seen by THIS database instance.
|
|
76
|
+
*
|
|
77
|
+
* `table` is validated against {@link CONTROL_TABLES} before it is interpolated
|
|
78
|
+
* into the `from` clause: the names are not user input, but the check keeps the
|
|
79
|
+
* dynamic query off the injection surface and fails loudly on a typo instead of
|
|
80
|
+
* emitting a malformed statement. The count reflects only the rows this node's
|
|
81
|
+
* control DB has converged on — in the integration harness that is the authority
|
|
82
|
+
* node (one ControlDatabase per party), i.e. the authoritative control-network
|
|
83
|
+
* view, not a per-drone convergence guarantee.
|
|
84
|
+
*/
|
|
85
|
+
countRows(table: ControlTable): Promise<number>;
|
|
38
86
|
/**
|
|
39
87
|
* Get the underlying database for advanced queries
|
|
40
88
|
*/
|
|
41
89
|
getDatabase(): Database;
|
|
90
|
+
/**
|
|
91
|
+
* Check whether any authority key exists in the control database.
|
|
92
|
+
* Used to decide whether a fresh-party genesis insert is required.
|
|
93
|
+
*/
|
|
94
|
+
hasAuthorityKey(): Promise<boolean>;
|
|
95
|
+
/**
|
|
96
|
+
* Idempotent genesis: insert `key` as the founding authority key only when
|
|
97
|
+
* the party has none yet. Returns true if it inserted, false if an authority
|
|
98
|
+
* key already existed (so a repeat `--authority` start is a no-op).
|
|
99
|
+
*/
|
|
100
|
+
ensureAuthorityKey(key: string): Promise<boolean>;
|
|
101
|
+
/**
|
|
102
|
+
* Collect every authority key (`CadreControl.AuthorityKey.Key`) as a set.
|
|
103
|
+
*
|
|
104
|
+
* This is the steady-state trust anchor for seeds: a seed's signer key is
|
|
105
|
+
* trusted only if it is already enrolled here (see `SeedTrustPolicy`). It is
|
|
106
|
+
* also the authority-identity source for `queryPeers`, decoupling authority
|
|
107
|
+
* status from the libp2p transport peer ID.
|
|
108
|
+
*/
|
|
109
|
+
getAuthorityKeys(): Promise<Set<string>>;
|
|
110
|
+
/**
|
|
111
|
+
* Enumerate the CadrePeer rows (cadre membership) for admin/membership reads.
|
|
112
|
+
*/
|
|
113
|
+
queryCadrePeers(): Promise<Array<{
|
|
114
|
+
peerId: string;
|
|
115
|
+
multiaddr: string | null;
|
|
116
|
+
}>>;
|
|
117
|
+
/**
|
|
118
|
+
* Read a single peer's address record (the full `CadrePeer` row) by PeerId.
|
|
119
|
+
*
|
|
120
|
+
* Returns null when no row exists. Missing/legacy column values are coalesced
|
|
121
|
+
* to their empty form (`''` key/sig, `[]` addrs, `0` stamp) so the caller's
|
|
122
|
+
* verify/freshness gates uniformly reject an unpublished or malformed row.
|
|
123
|
+
* The split `addrs` re-join to the exact stored `Multiaddr` (split-on-`,` is
|
|
124
|
+
* the inverse of join-on-`,`), so the resolver re-verifies over the same bytes
|
|
125
|
+
* the publisher signed.
|
|
126
|
+
*/
|
|
127
|
+
queryPeerRecord(peerId: string): Promise<PeerAddressRecord | null>;
|
|
128
|
+
/**
|
|
129
|
+
* Apply a peer's own self-signed address-record update to an existing row.
|
|
130
|
+
*
|
|
131
|
+
* Authorization is carried entirely by the record: the `Sig` column (verified
|
|
132
|
+
* by the `AuthorizedUpdate` self-branch against the stored `PublicKey`) plus
|
|
133
|
+
* the strictly-increasing `UpdatedAt`. No authority key is involved, so this
|
|
134
|
+
* is the refresh path for any member — authority or drone — once its row
|
|
135
|
+
* exists. `PublicKey` is intentionally not in the SET list (it is immutable on
|
|
136
|
+
* self-update and the constraint enforces `new.PublicKey = old.PublicKey`).
|
|
137
|
+
*/
|
|
138
|
+
updateSelfPeerRecord(record: PeerAddressRecord): Promise<void>;
|
|
139
|
+
/**
|
|
140
|
+
* Read a single peer's device push token (the full `DeviceToken` row) by PeerId.
|
|
141
|
+
*
|
|
142
|
+
* Returns null when no row exists. Missing/legacy column values are coalesced to
|
|
143
|
+
* their empty form (`''` token/sig, `0` stamp) so the caller's verify/freshness
|
|
144
|
+
* gates uniformly reject an unpublished or malformed row. `platform` is returned
|
|
145
|
+
* verbatim (the resolver validates it against {@link PushPlatform} and re-verifies
|
|
146
|
+
* the self-signature, which covers the platform field).
|
|
147
|
+
*/
|
|
148
|
+
queryDeviceToken(peerId: string): Promise<DeviceTokenRecord | null>;
|
|
149
|
+
/**
|
|
150
|
+
* Apply a peer's own self-signed device-token update to an existing row.
|
|
151
|
+
*
|
|
152
|
+
* Authorization is carried entirely by the record: the `Sig` column (verified by
|
|
153
|
+
* the `DeviceToken.AuthorizedUpdate` self-branch against the stored
|
|
154
|
+
* `CadrePeer.PublicKey`) plus the strictly-increasing `UpdatedAt`. No authority key
|
|
155
|
+
* is involved, so this is the refresh / rotation path for any member once both its
|
|
156
|
+
* `CadrePeer` row (for the PublicKey) and its `DeviceToken` row exist. `PeerId` is
|
|
157
|
+
* intentionally not in the SET list (immutable; the constraint enforces
|
|
158
|
+
* `new.PeerId = old.PeerId`). Mirrors {@link updateSelfPeerRecord}.
|
|
159
|
+
*/
|
|
160
|
+
updateSelfDeviceToken(record: DeviceTokenRecord): Promise<void>;
|
|
42
161
|
/**
|
|
43
162
|
* Insert the initial authority key (bootstrap - no existing authorities required)
|
|
44
163
|
*/
|
|
45
164
|
insertAuthorityKey(key: string): Promise<void>;
|
|
46
165
|
/**
|
|
47
|
-
* Insert a strand into the control database using authority signature.
|
|
48
|
-
*
|
|
166
|
+
* Insert a strand into the control database using an authority signature.
|
|
167
|
+
*
|
|
168
|
+
* The authority signs the canonical row-bound authorization message (see
|
|
169
|
+
* {@link buildAuthorizationMessage}) — NOT a bare stamp — so the signature is bound to
|
|
170
|
+
* this strand's contents and cannot be transplanted onto an attacker-chosen row. The
|
|
171
|
+
* StampId is persisted as a unique column for single-use anti-replay.
|
|
49
172
|
*
|
|
50
173
|
* @param strandId - Unique identifier for the strand
|
|
51
174
|
* @param type - Strand type: 'o' for open, 'c' for closed
|
|
52
175
|
* @param authorityKey - Public key of the authorizing authority
|
|
53
|
-
* @param
|
|
176
|
+
* @param signMessage - Function that ed25519-signs the raw message bytes (no pre-hash)
|
|
177
|
+
* with the authority's private key, returning a base64url signature
|
|
54
178
|
* @param memberPrivateKey - Optional private key for membership in closed strands
|
|
55
179
|
*/
|
|
56
|
-
insertStrand(strandId: string, type: 'o' | 'c', authorityKey: string,
|
|
180
|
+
insertStrand(strandId: string, type: 'o' | 'c', authorityKey: string, signMessage: (message: Uint8Array) => string, memberPrivateKey?: string): Promise<void>;
|
|
181
|
+
/**
|
|
182
|
+
* Insert a validation key into the control database using an authority signature.
|
|
183
|
+
*
|
|
184
|
+
* Mirrors {@link insertStrand}: the authority signs the canonical row-bound
|
|
185
|
+
* authorization message over (Key, StampId), and the StampId is persisted as a unique
|
|
186
|
+
* column for single-use anti-replay. A `ValidationKey` authorizes verifying strand
|
|
187
|
+
* formation disclosures.
|
|
188
|
+
*
|
|
189
|
+
* @param key - The validation public key to enroll
|
|
190
|
+
* @param authorityKey - Public key of the authorizing authority
|
|
191
|
+
* @param signMessage - Function that ed25519-signs the raw message bytes (no pre-hash)
|
|
192
|
+
* with the authority's private key, returning a base64url signature
|
|
193
|
+
*/
|
|
194
|
+
insertValidationKey(key: string, authorityKey: string, signMessage: (message: Uint8Array) => string): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* Insert an authority-signed `FormationInvite` (open invitation token).
|
|
197
|
+
*
|
|
198
|
+
* The invite is the on-network record that later authorizes an
|
|
199
|
+
* authority-signature-FREE `Strand` creation: an invited cadre peer redeems it
|
|
200
|
+
* by inserting a matching `FormationUsage` row (see {@link redeemInvitation}),
|
|
201
|
+
* which satisfies the consent branch of `Strand.Authorized`.
|
|
202
|
+
*
|
|
203
|
+
* Like {@link insertStrand}/{@link insertValidationKey}, the authority signs the
|
|
204
|
+
* canonical row-bound authorization message (see {@link buildAuthorizationMessage})
|
|
205
|
+
* over (Token, sAppId, ExpiresAt, TotalUses, ValidationUrl, StrandId, StampId) — NOT a
|
|
206
|
+
* bare stamp — so the signature is bound to this invite's contents and cannot be
|
|
207
|
+
* transplanted onto an attacker-chosen row. The StampId is persisted as a unique
|
|
208
|
+
* column for single-use anti-replay. `FormationInvite.AuthorizedAddOrRemove` gates
|
|
209
|
+
* both insert and delete; its `coalesce(new, old)` verify binds the NEW row on
|
|
210
|
+
* insert and the OLD row on delete.
|
|
211
|
+
*
|
|
212
|
+
* `StrandId` binds the invite to a pre-existing host strand (provision-then-record):
|
|
213
|
+
* when set, a responder redeeming this token records a `FormationUsage` against that
|
|
214
|
+
* strand and returns it (see {@link ControlFormationUsageRecorder.resolveStrand}); a
|
|
215
|
+
* null `StrandId` (the default) leaves the legacy responder-provisions path in place.
|
|
216
|
+
* Like ValidationUrl it is a nullable bound field, signed as `''` when absent.
|
|
217
|
+
*
|
|
218
|
+
* The ExpiresAt and TotalUses message fields must byte-match what the (auto-deferred,
|
|
219
|
+
* because it has a subquery) CHECK sees AFTER column coercion: TotalUses becomes a
|
|
220
|
+
* decimal string (`String(totalUses)` ⇔ `cast(new.TotalUses as text)`) and ExpiresAt
|
|
221
|
+
* becomes the engine's canonical `PlainDateTime` string — sourced here from
|
|
222
|
+
* {@link canonicalDatetime} (a `select datetime(?)` round-trip) rather than a hand-rolled
|
|
223
|
+
* ISO formatter, so signer and verifier agree exactly. A null ExpiresAt / TotalUses /
|
|
224
|
+
* ValidationUrl signs as `''`, matching the schema's `coalesce(..., '')`.
|
|
225
|
+
*
|
|
226
|
+
* @param token - Invitation token (the `FormationInvite` primary key)
|
|
227
|
+
* @param sAppId - The sApp a redeemed strand will use
|
|
228
|
+
* @param authorityKey - Public key of the authorizing authority
|
|
229
|
+
* @param signMessage - ed25519-signs the raw message bytes (no pre-hash),
|
|
230
|
+
* returning a base64url signature — the same callback shape {@link insertStrand} uses
|
|
231
|
+
* @param options - Optional `expiresAtMs` (epoch ms), `totalUses`, `validationUrl`,
|
|
232
|
+
* `strandId` (bind to a pre-existing host strand for provision-then-record)
|
|
233
|
+
*/
|
|
234
|
+
insertFormationInvite(token: string, sAppId: string, authorityKey: string, signMessage: (message: Uint8Array) => string, options?: {
|
|
235
|
+
expiresAtMs?: number;
|
|
236
|
+
totalUses?: number;
|
|
237
|
+
validationUrl?: string;
|
|
238
|
+
strandId?: string;
|
|
239
|
+
}): Promise<void>;
|
|
240
|
+
/**
|
|
241
|
+
* Redeem a `FormationInvite` by inserting the `Strand` row and a matching
|
|
242
|
+
* `FormationUsage` row **atomically, in one transaction**.
|
|
243
|
+
*
|
|
244
|
+
* The two CHECK constraints are mutually circular under immediate evaluation:
|
|
245
|
+
* `Strand.Authorized`'s consent branch requires the `FormationUsage` row, while
|
|
246
|
+
* `FormationUsage.StrandExists` requires the `Strand` row. Both CHECKs contain
|
|
247
|
+
* subqueries, so Quereus auto-defers them to transaction commit — wrapping both
|
|
248
|
+
* inserts in a single explicit `begin … commit` lets both deferred CHECKs see
|
|
249
|
+
* both rows at commit. The strand is authorised WITHOUT an authority signature
|
|
250
|
+
* (the `FormationUsage` branch of `Strand.Authorized`) but still gets a fresh,
|
|
251
|
+
* unique `StampId` column to satisfy the not-null/unique anti-replay column.
|
|
252
|
+
*
|
|
253
|
+
* `UseNumber` is computed as `max(UseNumber)+1` for the token (the `Monotonic`
|
|
254
|
+
* constraint); callers redeeming concurrently against the same token must
|
|
255
|
+
* serialise, since the next use number is read before the insert.
|
|
256
|
+
*/
|
|
257
|
+
redeemInvitation(params: {
|
|
258
|
+
token: string;
|
|
259
|
+
strandId: string;
|
|
260
|
+
type?: 'o' | 'c';
|
|
261
|
+
memberPrivateKey?: string;
|
|
262
|
+
disclosure?: string;
|
|
263
|
+
peerId?: string;
|
|
264
|
+
peerSignature?: string;
|
|
265
|
+
nowMs?: number;
|
|
266
|
+
validationKey?: string;
|
|
267
|
+
validationSignature?: string;
|
|
268
|
+
}): Promise<void>;
|
|
269
|
+
/**
|
|
270
|
+
* Record a `FormationUsage` against an **already-existing** `Strand` (no strand
|
|
271
|
+
* insert). This is the redemption path when the strand was provisioned
|
|
272
|
+
* separately (e.g. authority-signed) and the consent record is added after the
|
|
273
|
+
* fact: the single insert auto-commits, and the deferred `StrandExists` CHECK
|
|
274
|
+
* is satisfied by the pre-existing committed strand row. Returns the assigned
|
|
275
|
+
* `UseNumber`.
|
|
276
|
+
*
|
|
277
|
+
* Use {@link redeemInvitation} instead when the strand must be created by
|
|
278
|
+
* consent atomically with the usage.
|
|
279
|
+
*/
|
|
280
|
+
recordFormationUsage(params: {
|
|
281
|
+
token: string;
|
|
282
|
+
strandId: string;
|
|
283
|
+
disclosure?: string;
|
|
284
|
+
peerId?: string;
|
|
285
|
+
peerSignature?: string;
|
|
286
|
+
nowMs?: number;
|
|
287
|
+
validationKey?: string;
|
|
288
|
+
validationSignature?: string;
|
|
289
|
+
}): Promise<number>;
|
|
290
|
+
/** Parameterised `FormationUsage` insert shared by redeem + record paths. */
|
|
291
|
+
private execFormationUsageInsert;
|
|
292
|
+
/**
|
|
293
|
+
* Read a `FormationInvite` row by token, or null when absent. `expiresAtMs` is
|
|
294
|
+
* the parsed epoch-ms of the stored `datetime` (null when the invite never
|
|
295
|
+
* expires); the caller compares it against the wall clock for freshness.
|
|
296
|
+
*/
|
|
297
|
+
queryFormationInvite(token: string): Promise<{
|
|
298
|
+
token: string;
|
|
299
|
+
sAppId: string;
|
|
300
|
+
expiresAtMs: number | null;
|
|
301
|
+
totalUses: number | null;
|
|
302
|
+
validationUrl: string | null;
|
|
303
|
+
strandId: string | null;
|
|
304
|
+
} | null>;
|
|
305
|
+
/**
|
|
306
|
+
* Count `FormationUsage` rows recorded against a token (uses consumed so far).
|
|
307
|
+
*/
|
|
308
|
+
countFormationUsage(token: string): Promise<number>;
|
|
309
|
+
/** Next `UseNumber` for a token = max(existing)+1, per the `Monotonic` constraint. */
|
|
310
|
+
private nextUseNumber;
|
|
57
311
|
/**
|
|
58
312
|
* Close the database and cleanup resources
|
|
59
313
|
*/
|