@serfab/cadre-core 0.8.1 → 0.10.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 +6 -4
- package/dist/bootstrap-peer-store-file.d.ts +13 -0
- package/dist/bootstrap-peer-store-file.js +42 -0
- package/dist/bootstrap-peer-store-file.js.map +1 -0
- package/dist/bootstrap-peer-store.d.ts +73 -0
- package/dist/bootstrap-peer-store.js +128 -0
- package/dist/bootstrap-peer-store.js.map +1 -0
- package/dist/cadre-node.d.ts +1006 -101
- package/dist/cadre-node.js +1977 -283
- package/dist/cadre-node.js.map +1 -1
- package/dist/control-authorization.d.ts +93 -0
- package/dist/control-authorization.js +57 -0
- package/dist/control-authorization.js.map +1 -0
- package/dist/control-cohort.d.ts +15 -15
- package/dist/control-cohort.js +14 -14
- package/dist/control-cohort.js.map +1 -1
- package/dist/control-database.d.ts +1033 -76
- package/dist/control-database.js +1520 -153
- package/dist/control-database.js.map +1 -1
- package/dist/control-formation-recorder.d.ts +91 -12
- package/dist/control-formation-recorder.js +131 -16
- package/dist/control-formation-recorder.js.map +1 -1
- package/dist/control-schema.d.ts +1 -1
- package/dist/control-schema.js +774 -186
- package/dist/control-schema.js.map +1 -1
- package/dist/control-stream.d.ts +38 -4
- package/dist/control-stream.js +70 -4
- package/dist/control-stream.js.map +1 -1
- package/dist/control-write-retry.d.ts +151 -0
- package/dist/control-write-retry.js +411 -0
- package/dist/control-write-retry.js.map +1 -0
- package/dist/delegate-admission.d.ts +134 -0
- package/dist/delegate-admission.js +244 -0
- package/dist/delegate-admission.js.map +1 -0
- package/dist/device-token.d.ts +12 -12
- package/dist/device-token.js +15 -14
- package/dist/device-token.js.map +1 -1
- package/dist/ed25519-key.d.ts +68 -0
- package/dist/ed25519-key.js +106 -0
- package/dist/ed25519-key.js.map +1 -0
- package/dist/file-durable-slot.d.ts +20 -0
- package/dist/file-durable-slot.js +53 -0
- package/dist/file-durable-slot.js.map +1 -0
- package/dist/formation-approval.d.ts +157 -0
- package/dist/formation-approval.js +375 -0
- package/dist/formation-approval.js.map +1 -0
- package/dist/fs-atomic.d.ts +38 -0
- package/dist/fs-atomic.js +97 -0
- package/dist/fs-atomic.js.map +1 -0
- package/dist/identity-key.d.ts +49 -0
- package/dist/identity-key.js +81 -0
- package/dist/identity-key.js.map +1 -0
- package/dist/index.d.ts +16 -6
- package/dist/index.js +44 -10
- package/dist/index.js.map +1 -1
- package/dist/key-store-file.d.ts +2 -24
- package/dist/key-store-file.js +10 -77
- package/dist/key-store-file.js.map +1 -1
- package/dist/key-store.d.ts +1 -1
- package/dist/key-store.js +1 -1
- package/dist/membership-connection-gater.d.ts +146 -0
- package/dist/membership-connection-gater.js +180 -0
- package/dist/membership-connection-gater.js.map +1 -0
- package/dist/node-local-snapshot.d.ts +109 -0
- package/dist/node-local-snapshot.js +182 -0
- package/dist/node-local-snapshot.js.map +1 -0
- package/dist/peer-authorization.d.ts +150 -11
- package/dist/peer-authorization.js +170 -13
- package/dist/peer-authorization.js.map +1 -1
- package/dist/peer-record.d.ts +24 -11
- package/dist/peer-record.js +29 -13
- package/dist/peer-record.js.map +1 -1
- package/dist/push-node.d.ts +38 -0
- package/dist/push-node.js +48 -0
- package/dist/push-node.js.map +1 -0
- package/dist/push-notifier-apns.d.ts +10 -2
- package/dist/push-notifier-apns.js +14 -10
- package/dist/push-notifier-apns.js.map +1 -1
- package/dist/push-notifier-fcm.d.ts +7 -1
- package/dist/push-notifier-fcm.js +9 -10
- package/dist/push-notifier-fcm.js.map +1 -1
- package/dist/push-notifier.d.ts +16 -35
- package/dist/push-notifier.js +16 -44
- package/dist/push-notifier.js.map +1 -1
- package/dist/relay-addrs.d.ts +45 -0
- package/dist/relay-addrs.js +103 -0
- package/dist/relay-addrs.js.map +1 -0
- package/dist/relay-reservation.d.ts +266 -0
- package/dist/relay-reservation.js +613 -0
- package/dist/relay-reservation.js.map +1 -0
- package/dist/seed-bootstrap.d.ts +263 -79
- package/dist/seed-bootstrap.js +413 -211
- package/dist/seed-bootstrap.js.map +1 -1
- package/dist/seed-trust-policy.d.ts +50 -24
- package/dist/seed-trust-policy.js +28 -19
- package/dist/seed-trust-policy.js.map +1 -1
- package/dist/strand-addr-protocol.d.ts +42 -13
- package/dist/strand-addr-protocol.js +58 -52
- package/dist/strand-addr-protocol.js.map +1 -1
- package/dist/strand-backfill.d.ts +109 -0
- package/dist/strand-backfill.js +279 -0
- package/dist/strand-backfill.js.map +1 -0
- package/dist/strand-database.d.ts +5 -5
- package/dist/strand-database.js +4 -4
- package/dist/strand-database.js.map +1 -1
- package/dist/strand-formation-manager.d.ts +38 -9
- package/dist/strand-formation-manager.js +120 -17
- package/dist/strand-formation-manager.js.map +1 -1
- package/dist/strand-formation-protocol.d.ts +104 -5
- package/dist/strand-formation-protocol.js +233 -5
- package/dist/strand-formation-protocol.js.map +1 -1
- package/dist/strand-instance-manager.d.ts +40 -2
- package/dist/strand-instance-manager.js +93 -15
- package/dist/strand-instance-manager.js.map +1 -1
- package/dist/strand-member-key.d.ts +6 -6
- package/dist/strand-member-key.js +6 -6
- package/dist/strand-member-key.js.map +1 -1
- package/dist/strand-member-registry.d.ts +12 -12
- package/dist/strand-member-registry.js +22 -14
- package/dist/strand-member-registry.js.map +1 -1
- package/dist/strand-membership-writer.d.ts +532 -138
- package/dist/strand-membership-writer.js +800 -179
- package/dist/strand-membership-writer.js.map +1 -1
- package/dist/strand-solicitation.d.ts +99 -33
- package/dist/strand-solicitation.js +100 -52
- package/dist/strand-solicitation.js.map +1 -1
- package/dist/strand-transport-key.d.ts +40 -0
- package/dist/strand-transport-key.js +56 -0
- package/dist/strand-transport-key.js.map +1 -0
- package/dist/strand-wake-protocol.d.ts +9 -7
- package/dist/strand-wake-protocol.js +22 -46
- package/dist/strand-wake-protocol.js.map +1 -1
- package/dist/strand-watcher.d.ts +17 -2
- package/dist/strand-watcher.js +47 -3
- package/dist/strand-watcher.js.map +1 -1
- package/dist/trusted-owner-store-file.d.ts +12 -0
- package/dist/trusted-owner-store-file.js +39 -0
- package/dist/trusted-owner-store-file.js.map +1 -0
- package/dist/trusted-owner-store.d.ts +64 -0
- package/dist/trusted-owner-store.js +126 -0
- package/dist/trusted-owner-store.js.map +1 -0
- package/dist/types.d.ts +325 -50
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -1
- package/package.json +23 -8
- package/src/bootstrap-peer-store-file.ts +44 -0
- package/src/bootstrap-peer-store.ts +172 -0
- package/src/cadre-node.ts +2129 -295
- package/src/control-authorization.ts +114 -0
- package/src/control-cohort.ts +17 -17
- package/src/control-database.ts +1766 -191
- package/src/control-formation-recorder.ts +174 -21
- package/src/control-schema.ts +786 -198
- package/src/control-stream.ts +81 -4
- package/src/control-write-retry.ts +473 -0
- package/src/delegate-admission.ts +302 -0
- package/src/device-token.ts +19 -14
- package/src/ed25519-key.ts +134 -0
- package/src/file-durable-slot.ts +61 -0
- package/src/formation-approval.ts +604 -0
- package/src/fs-atomic.ts +107 -0
- package/src/identity-key.ts +109 -0
- package/src/index.ts +157 -24
- package/src/key-store-file.ts +10 -81
- package/src/key-store.ts +1 -1
- package/src/membership-connection-gater.ts +204 -0
- package/src/node-local-snapshot.ts +260 -0
- package/src/peer-authorization.ts +219 -13
- package/src/peer-record.ts +31 -13
- package/src/push-node.ts +64 -0
- package/src/push-notifier-apns.ts +16 -12
- package/src/push-notifier-fcm.ts +9 -10
- package/src/push-notifier.ts +16 -57
- package/src/relay-addrs.ts +111 -0
- package/src/relay-reservation.ts +823 -0
- package/src/seed-bootstrap.ts +490 -226
- package/src/seed-trust-policy.ts +65 -32
- package/src/strand-addr-protocol.ts +84 -53
- package/src/strand-backfill.ts +366 -0
- package/src/strand-database.ts +6 -6
- package/src/strand-formation-manager.ts +145 -19
- package/src/strand-formation-protocol.ts +310 -9
- package/src/strand-instance-manager.ts +113 -16
- package/src/strand-member-key.ts +7 -7
- package/src/strand-member-registry.ts +28 -23
- package/src/strand-membership-writer.ts +1430 -640
- package/src/strand-solicitation.ts +155 -90
- package/src/strand-transport-key.ts +58 -0
- package/src/strand-wake-protocol.ts +29 -46
- package/src/strand-watcher.ts +66 -3
- package/src/trusted-owner-store-file.ts +41 -0
- package/src/trusted-owner-store.ts +171 -0
- package/src/types.ts +342 -51
- package/dist/authority-key.d.ts +0 -41
- package/dist/authority-key.js +0 -52
- package/dist/authority-key.js.map +0 -1
- package/src/authority-key.ts +0 -68
package/src/types.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import type { ConnectionGater, Libp2p, PeerId, PrivateKey } from '@libp2p/interface';
|
|
2
2
|
import type { IRawStorage, Libp2pTransports } from '@optimystic/db-p2p';
|
|
3
|
-
import type { IRepo } from '@optimystic/db-core';
|
|
3
|
+
import type { IPeerNetwork, IRepo } from '@optimystic/db-core';
|
|
4
|
+
import type { StrandBackfillConfig } from './strand-backfill.js';
|
|
4
5
|
import type { StrandDatabase } from './strand-database.js';
|
|
5
6
|
import type { SeedTrustPolicy } from './seed-trust-policy.js';
|
|
6
7
|
import type { KeyStore, KeyId } from './key-store.js';
|
|
8
|
+
import type { TrustedOwnerStore, TrustSource } from './trusted-owner-store.js';
|
|
9
|
+
import type { BootstrapPeerStore } from './bootstrap-peer-store.js';
|
|
10
|
+
import type { PushNotifier } from './push-notifier.js';
|
|
11
|
+
import type { RevocableTable } from './control-authorization.js';
|
|
7
12
|
|
|
8
13
|
/**
|
|
9
14
|
* Extended Libp2p node with the coordinatedRepo attached by db-p2p's
|
|
@@ -11,6 +16,14 @@ import type { KeyStore, KeyId } from './key-store.js';
|
|
|
11
16
|
*/
|
|
12
17
|
export interface Libp2pNodeWithRepo extends Libp2p {
|
|
13
18
|
coordinatedRepo: IRepo;
|
|
19
|
+
/**
|
|
20
|
+
* The node's key-addressed peer network, attached by db-p2p's factory alongside
|
|
21
|
+
* `coordinatedRepo` (`libp2p-node-base.ts`). Optional deliberately: the base
|
|
22
|
+
* factory always assigns it, but it is not part of the upstream node type, so
|
|
23
|
+
* declaring it required would be a claim this repo cannot enforce. The strand
|
|
24
|
+
* backfill (`strand-backfill.ts`) logs once and stays inert when it is absent.
|
|
25
|
+
*/
|
|
26
|
+
keyNetwork?: IPeerNetwork;
|
|
14
27
|
}
|
|
15
28
|
|
|
16
29
|
/**
|
|
@@ -119,8 +132,9 @@ export interface StorageConfig {
|
|
|
119
132
|
*
|
|
120
133
|
* For React Native, use the appropriate storage from @optimystic/db-p2p-storage-rn:
|
|
121
134
|
* ```typescript
|
|
122
|
-
* import {
|
|
123
|
-
*
|
|
135
|
+
* import { LevelDBRawStorage } from '@optimystic/db-p2p-storage-rn';
|
|
136
|
+
* // `db` is an open LevelDB handle (see `openOptimysticRNDb` over `rn-leveldb`)
|
|
137
|
+
* storage: { provider: (strandId) => new LevelDBRawStorage(openDb(`sereus-${strandId}`)) }
|
|
124
138
|
* ```
|
|
125
139
|
*
|
|
126
140
|
* For in-memory storage (testing):
|
|
@@ -138,7 +152,47 @@ export interface StorageConfig {
|
|
|
138
152
|
*/
|
|
139
153
|
export interface NetworkConfig {
|
|
140
154
|
listenAddrs?: string[];
|
|
155
|
+
/**
|
|
156
|
+
* Addresses to advertise to peers instead of `listenAddrs` — for a node behind
|
|
157
|
+
* NAT or a reverse proxy that is reachable at a different address than it binds
|
|
158
|
+
* (e.g. `mynode.example.com:4001` in front of a `0.0.0.0:4001` listener).
|
|
159
|
+
*
|
|
160
|
+
* Accepted (settable in `cadre.yaml`, via `CADRE_ANNOUNCE_ADDRS`, and through the
|
|
161
|
+
* Docker entrypoint) but **not yet applied**: libp2p only takes announce addresses
|
|
162
|
+
* at construction (`addresses.announce` / `addresses.appendAnnounce`), and
|
|
163
|
+
* `@optimystic/db-p2p`'s `NodeOptions` has no field for either — there is nothing
|
|
164
|
+
* in this repo to wire this into. `CadreNode.start()` warns when this is set,
|
|
165
|
+
* rather than pretending it works. Unblocked by the `announce-addrs-option`
|
|
166
|
+
* request filed against `@optimystic/db-p2p`; wiring lands here once this repo's
|
|
167
|
+
* dependency range moves to a version carrying it.
|
|
168
|
+
*
|
|
169
|
+
* Use {@link relayAddrs} for reachability behind NAT today (a reserved relay slot
|
|
170
|
+
* gives the node a real dialable circuit address), or `inviteAddressResolver` to
|
|
171
|
+
* substitute a reachable address into invite payloads specifically — neither
|
|
172
|
+
* changes what this node advertises on the wire, but both cover the operator need
|
|
173
|
+
* this field cannot yet.
|
|
174
|
+
*/
|
|
141
175
|
announceAddrs?: string[];
|
|
176
|
+
/**
|
|
177
|
+
* Circuit-relay servers this node reserves a slot on, so peers can reach it
|
|
178
|
+
* from behind NAT. Each entry is the relay's DIRECT dial multiaddr ending in
|
|
179
|
+
* its peerId — `/dns4/relay.example.com/tcp/4001/p2p/12D3KooW…` — with no
|
|
180
|
+
* `/p2p-circuit` suffix (one is appended for you; an entry that already has
|
|
181
|
+
* one is passed through unchanged).
|
|
182
|
+
*
|
|
183
|
+
* This is sugar for a circuit entry in {@link listenAddrs}: `relay-addrs.ts`
|
|
184
|
+
* resolves the two into one listen list, and listening on a relay's circuit
|
|
185
|
+
* addr is what makes libp2p dial it and hold the reservation. A relay named
|
|
186
|
+
* here therefore also becomes a delegate-announce target, so this node's
|
|
187
|
+
* strand nodes may reserve on it too (see `delegate-admission.ts`).
|
|
188
|
+
*
|
|
189
|
+
* Setting this gives the node a listener even when `listenAddrs` is `[]` —
|
|
190
|
+
* that is the point of naming a relay, but note that React Native hosts
|
|
191
|
+
* (`reference-app-rn`'s `cadre-phone.ts`) deliberately do NOT listen, and
|
|
192
|
+
* must not acquire a listener by accident.
|
|
193
|
+
*
|
|
194
|
+
* A malformed entry throws at node start rather than being silently dropped.
|
|
195
|
+
*/
|
|
142
196
|
relayAddrs?: string[];
|
|
143
197
|
/**
|
|
144
198
|
* Enable circuit relay server - allows this node to relay connections for other peers.
|
|
@@ -165,7 +219,7 @@ export interface NetworkConfig {
|
|
|
165
219
|
transports?: Libp2pTransports;
|
|
166
220
|
/**
|
|
167
221
|
* Optional async resolver returning the multiaddrs to embed in invites
|
|
168
|
-
* (and other
|
|
222
|
+
* (and other owner-address contexts). When unset, `libp2pNode.getMultiaddrs()`
|
|
169
223
|
* is used. Hosts behind NAT supply this to substitute their DDNS hostname
|
|
170
224
|
* and externally-mapped port — see `@serfab/cadre-host`'s NatService.
|
|
171
225
|
*/
|
|
@@ -178,6 +232,12 @@ export interface NetworkConfig {
|
|
|
178
232
|
* Playwright e2e, RN simulators) supply a permissive gater here — e.g.
|
|
179
233
|
* `{ denyDialMultiaddr: () => false }`. See `@optimystic/db-p2p`'s
|
|
180
234
|
* `libp2p-node-base` for the underlying option.
|
|
235
|
+
*
|
|
236
|
+
* On the CONTROL node this gater is composed with the built-in membership
|
|
237
|
+
* admission gate (`membership-connection-gater.ts`): every hook supplied here
|
|
238
|
+
* is honored unchanged, and on inbound encrypted connections a deny from
|
|
239
|
+
* either this gater or the membership policy denies. Strand cohort nodes
|
|
240
|
+
* receive this gater as-is (their peers are legitimately cross-party).
|
|
181
241
|
*/
|
|
182
242
|
connectionGater?: ConnectionGater;
|
|
183
243
|
/**
|
|
@@ -190,8 +250,8 @@ export interface NetworkConfig {
|
|
|
190
250
|
*/
|
|
191
251
|
controlCohort?: {
|
|
192
252
|
/**
|
|
193
|
-
* Cap on the number of NON-
|
|
194
|
-
* (backbone/
|
|
253
|
+
* Cap on the number of NON-owner siblings dialed per reconcile pass
|
|
254
|
+
* (backbone/owner members are always dialed and do not count). Defaults
|
|
195
255
|
* to {@link DEFAULT_CONTROL_COHORT_TARGET_DEGREE}.
|
|
196
256
|
*/
|
|
197
257
|
targetDegree?: number;
|
|
@@ -236,6 +296,27 @@ export interface ControlNetworkConfig {
|
|
|
236
296
|
schemaPath?: string;
|
|
237
297
|
}
|
|
238
298
|
|
|
299
|
+
/**
|
|
300
|
+
* Replication-breadth constants, both networks' cluster policies, and the strand
|
|
301
|
+
* resolver, re-exported so cadre embedders, the SQL plugin and the integration
|
|
302
|
+
* harness share one definition. Defined in `@serfab/quereus-plugin-sereus`
|
|
303
|
+
* because that package also creates libp2p nodes and cannot depend on this one.
|
|
304
|
+
* The control network's breadth is fixed ({@link CONTROL_REPLICATION_BREADTH},
|
|
305
|
+
* not configurable) and so is its consensus policy ({@link CONTROL_CLUSTER_POLICY}
|
|
306
|
+
* — notably the super-majority threshold, which it leaves at Optimystic's default
|
|
307
|
+
* by omission); the strand breadth defaults to
|
|
308
|
+
* {@link CadreNodeConfig.strandClusterSize} while its policy is the fixed
|
|
309
|
+
* {@link STRAND_CLUSTER_POLICY}.
|
|
310
|
+
*/
|
|
311
|
+
export {
|
|
312
|
+
MIN_CLUSTER_SIZE,
|
|
313
|
+
CONTROL_REPLICATION_BREADTH,
|
|
314
|
+
CONTROL_CLUSTER_POLICY,
|
|
315
|
+
DEFAULT_STRAND_CLUSTER_SIZE,
|
|
316
|
+
STRAND_CLUSTER_POLICY,
|
|
317
|
+
resolveStrandClusterSize
|
|
318
|
+
} from '@serfab/quereus-plugin-sereus';
|
|
319
|
+
|
|
239
320
|
/**
|
|
240
321
|
* Main configuration for a CadreNode
|
|
241
322
|
*/
|
|
@@ -254,10 +335,10 @@ export interface CadreNodeConfig {
|
|
|
254
335
|
* form). Mutually exclusive with {@link privateKey}. Absent ⇒ legacy behavior
|
|
255
336
|
* (use `privateKey`, else libp2p generates an ephemeral key).
|
|
256
337
|
*
|
|
257
|
-
* In the single-key reference model the
|
|
258
|
-
* the node identity (see {@link CadreNode.
|
|
259
|
-
* protecting the identity in a secure enclave protects the
|
|
260
|
-
* A future separate-
|
|
338
|
+
* In the single-key reference model the owner signing key is *derived from*
|
|
339
|
+
* the node identity (see {@link CadreNode.getIdentityOwnerKey}), so
|
|
340
|
+
* protecting the identity in a secure enclave protects the owner key too.
|
|
341
|
+
* A future separate-owner slot (`ownerKeyId`) is anticipated but not
|
|
261
342
|
* built here.
|
|
262
343
|
*/
|
|
263
344
|
keyStore?: KeyStore;
|
|
@@ -280,6 +361,43 @@ export interface CadreNodeConfig {
|
|
|
280
361
|
/** Network configuration */
|
|
281
362
|
network?: NetworkConfig;
|
|
282
363
|
|
|
364
|
+
/**
|
|
365
|
+
* Number of nodes Optimystic is told a **strand** network's replication cluster
|
|
366
|
+
* should have, for every strand this node starts. The control network is not
|
|
367
|
+
* configurable — it always replicates to {@link CONTROL_REPLICATION_BREADTH}
|
|
368
|
+
* nodes, which is above any party's node count, because every member reads the
|
|
369
|
+
* whole control database and a member that misses a write may never learn the
|
|
370
|
+
* fact (see that constant for the full reasoning).
|
|
371
|
+
*
|
|
372
|
+
* Every node on the same strand should use the same value: the number bounds the
|
|
373
|
+
* cohort a member independently derives, and Optimystic's cluster-membership gate
|
|
374
|
+
* can reject a coordinator's smaller declared set as a downsize against a larger
|
|
375
|
+
* derived view.
|
|
376
|
+
*
|
|
377
|
+
* Frozen when the strand's libp2p node is created; a change takes effect on
|
|
378
|
+
* restart, not when cadre membership grows. Defaults to
|
|
379
|
+
* {@link DEFAULT_STRAND_CLUSTER_SIZE} (4 — the smallest breadth whose 0.75
|
|
380
|
+
* super-majority still commits with one holder offline); anything below
|
|
381
|
+
* {@link MIN_CLUSTER_SIZE} is rejected before a node is created. The cost of a
|
|
382
|
+
* small value is replication breadth and reliance on read repair, not commit
|
|
383
|
+
* correctness — but at 2 that read repair cannot converge, because a lone
|
|
384
|
+
* corroborator's stale answer is accepted as the cluster's truth.
|
|
385
|
+
*/
|
|
386
|
+
strandClusterSize?: number;
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Tuning for the strand peer-join block catch-up (see `strand-backfill.ts`):
|
|
390
|
+
* when a strand's libp2p node connects to a peer this runtime has not yet
|
|
391
|
+
* caught up, every block in the strand's own raw store is pushed to that peer,
|
|
392
|
+
* so a machine that joined the strand after blocks were committed still ends
|
|
393
|
+
* up physically holding them. Applies to every strand this node starts;
|
|
394
|
+
* strand-only — the control network has its own row-level re-issue queue
|
|
395
|
+
* (`drainPendingControlReplication`). Omit for the defaults
|
|
396
|
+
* (`DEFAULT_STRAND_BACKFILL`); `{ enabled: false }` restores the
|
|
397
|
+
* no-backfill behaviour.
|
|
398
|
+
*/
|
|
399
|
+
strandBackfill?: StrandBackfillConfig;
|
|
400
|
+
|
|
283
401
|
/** Hibernation configuration */
|
|
284
402
|
hibernation?: HibernationConfig;
|
|
285
403
|
|
|
@@ -296,25 +414,106 @@ export interface CadreNodeConfig {
|
|
|
296
414
|
|
|
297
415
|
/**
|
|
298
416
|
* Node-wide default trust anchor for INBOUND control-network seeds. Forwarded
|
|
299
|
-
* into every SeedBootstrapService this node constructs (
|
|
417
|
+
* into every SeedBootstrapService this node constructs (owner, receive-only
|
|
300
418
|
* listener, and the temp service used by applySeed when no service exists), and
|
|
301
419
|
* used as the service-level default the libp2p seed-protocol handler relies on
|
|
302
|
-
* (that path has no per-call override seam). Defaults to
|
|
303
|
-
* inside SeedBootstrapService when unset — a
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* from a CadreInvite).
|
|
420
|
+
* (that path has no per-call override seam). Defaults to anchoredTrustPolicy()
|
|
421
|
+
* inside SeedBootstrapService when unset — a node whose node-local trusted-owner
|
|
422
|
+
* anchor was never seeded then rejects every seed. A per-call
|
|
423
|
+
* `applySeed(seed, { trustPolicy })` override still wins over this default for
|
|
424
|
+
* callers that hold out-of-band material (e.g. a pinned key from a CadreInvite).
|
|
307
425
|
*/
|
|
308
426
|
seedTrustPolicy?: SeedTrustPolicy;
|
|
309
427
|
|
|
310
428
|
/**
|
|
311
|
-
*
|
|
312
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
429
|
+
* Node-local trusted-owner anchor (see `trusted-owner-store.ts`): the
|
|
430
|
+
* NON-replicated, per-party record of owner keys established out of band —
|
|
431
|
+
* the anchor membership/seed trust can rest on, since the replicated
|
|
432
|
+
* `OwnerKey` table can be polluted by a stranger's self-genesis. Absent ⇒ an
|
|
433
|
+
* in-memory store is created at start() (ephemeral: anchored trust does not
|
|
434
|
+
* survive the process).
|
|
316
435
|
*/
|
|
317
|
-
|
|
436
|
+
trustedOwners?: {
|
|
437
|
+
/**
|
|
438
|
+
* Injected store instance — e.g. a `FileTrustedOwnerStore` from the
|
|
439
|
+
* Node-only subpath `@serfab/cadre-core/trusted-owner-store-file`,
|
|
440
|
+
* persisted in the node's state directory (same injection/isolation pattern
|
|
441
|
+
* as {@link keyStore}). Its `partyId` must match `controlNetwork.partyId`;
|
|
442
|
+
* start() fails closed on a mismatch.
|
|
443
|
+
*/
|
|
444
|
+
store?: TrustedOwnerStore;
|
|
445
|
+
/**
|
|
446
|
+
* Owner keys (base64url ed25519) established out of band and seeded into
|
|
447
|
+
* the store during start(): operator pins (e.g. cadre-cli
|
|
448
|
+
* `--pin-owner-key` / `CADRE_OWNER_KEYS`) or a `CadreInvite.ownerKeys`
|
|
449
|
+
* already known at config time. Seeding is idempotent across restarts.
|
|
450
|
+
*/
|
|
451
|
+
pinnedKeys?: string[];
|
|
452
|
+
/**
|
|
453
|
+
* Provenance recorded for {@link pinnedKeys}. Default: 'operator'.
|
|
454
|
+
* ('genesis' is reserved for the node's own founding key, seeded
|
|
455
|
+
* internally by `initializeSeedBootstrap`.)
|
|
456
|
+
*/
|
|
457
|
+
pinnedSource?: Exclude<TrustSource, 'genesis'>;
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Node-local cold-start bootstrap-peer store (see `bootstrap-peer-store.ts`):
|
|
462
|
+
* the owner addresses an applied seed nominated, kept so
|
|
463
|
+
* `reconcileControlCohort`'s cold-start branch can keep retrying a node that
|
|
464
|
+
* was seeded but never managed to connect. Sibling of {@link trustedOwners} —
|
|
465
|
+
* same NON-replicated, per-party, injected-backend shape — but nothing here is
|
|
466
|
+
* trust-bearing: these are dial hints only, and every address is re-bound to
|
|
467
|
+
* its peer id before the dial. Absent ⇒ an in-memory store is created at
|
|
468
|
+
* start() (ephemeral: the retry set does not survive the process, and such a
|
|
469
|
+
* node is stranded permanently if it restarts before connecting).
|
|
470
|
+
*
|
|
471
|
+
* The Node CLI, the browser, and React Native all inject a durable backend
|
|
472
|
+
* today; only NativeScript stays ephemeral, tracked by the
|
|
473
|
+
* `ns-durable-node-local-stores` ticket (which covers the same gap for
|
|
474
|
+
* {@link trustedOwners}). A new platform needs no new store class: supply a
|
|
475
|
+
* `DurableSlot` for the platform's storage and inject
|
|
476
|
+
* `PersistentBootstrapPeerStore.open(slot, partyId)` /
|
|
477
|
+
* `PersistentTrustedOwnerStore.open(slot, partyId)`.
|
|
478
|
+
*/
|
|
479
|
+
bootstrapPeers?: {
|
|
480
|
+
/**
|
|
481
|
+
* Injected store instance — e.g. a `FileBootstrapPeerStore` from the
|
|
482
|
+
* Node-only subpath `@serfab/cadre-core/bootstrap-peer-store-file`, persisted
|
|
483
|
+
* in the node's state directory (same injection/isolation pattern as
|
|
484
|
+
* {@link keyStore} / {@link trustedOwners}). Its `partyId` must match
|
|
485
|
+
* `controlNetwork.partyId`; start() fails closed on a mismatch.
|
|
486
|
+
*/
|
|
487
|
+
store?: BootstrapPeerStore;
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
/**
|
|
491
|
+
* Platform push-delivery for suspended mobile peers. When present, the node's
|
|
492
|
+
* server fan-out can deliver strand-wake data messages over the platform push
|
|
493
|
+
* channel (FCM/APNs). Absent ⇒ no platform push (control-network push-wake only).
|
|
494
|
+
*
|
|
495
|
+
* The `notifier` is **injected**, not constructed here: a Node host builds it
|
|
496
|
+
* from `@serfab/cadre-core/push-node` (`createPushNotifier(credentials)`) and
|
|
497
|
+
* passes the instance, so the FCM/APNs modules (`node:crypto`/`node:http2`)
|
|
498
|
+
* stay out of the cross-platform core graph. Ownership transfers to the node on
|
|
499
|
+
* injection — `CadreNode.stop` closes the notifier (via `PushFanoutService`),
|
|
500
|
+
* releasing its APNs HTTP/2 session; a host must NOT also close an injected
|
|
501
|
+
* notifier itself. `cooldownMs`/`debounceMs` carry the fan-out anti-spam policy.
|
|
502
|
+
*/
|
|
503
|
+
push?: {
|
|
504
|
+
/** The Node-constructed push router (see `@serfab/cadre-core/push-node`). */
|
|
505
|
+
notifier: PushNotifier;
|
|
506
|
+
/**
|
|
507
|
+
* Per-`(peer, strand)` minimum gap between push-wakes the fan-out emits.
|
|
508
|
+
* Default {@link DEFAULT_PUSH_COOLDOWN_MS} (5 min).
|
|
509
|
+
*/
|
|
510
|
+
cooldownMs?: number;
|
|
511
|
+
/**
|
|
512
|
+
* Per-strand burst-coalescing window: a second trigger within it is dropped.
|
|
513
|
+
* Default {@link DEFAULT_PUSH_DEBOUNCE_MS} (10 s).
|
|
514
|
+
*/
|
|
515
|
+
debounceMs?: number;
|
|
516
|
+
};
|
|
318
517
|
}
|
|
319
518
|
|
|
320
519
|
/**
|
|
@@ -347,6 +546,14 @@ export interface StrandInstance {
|
|
|
347
546
|
status: StrandStatus;
|
|
348
547
|
sAppInfo?: SAppInfo;
|
|
349
548
|
|
|
549
|
+
/**
|
|
550
|
+
* The lifecycle mode this instance's runtime was built with — `bootstrap`
|
|
551
|
+
* (purely local transactor) or `networked` (cluster transactor). Refreshed on
|
|
552
|
+
* every runtime (re)build, so a resume that shifts `bootstrap → networked`
|
|
553
|
+
* updates it. Read-only reporting; drives no behavior.
|
|
554
|
+
*/
|
|
555
|
+
mode: StrandMode;
|
|
556
|
+
|
|
350
557
|
/** The libp2p node for this strand (only when active/idle) */
|
|
351
558
|
libp2pNode?: Libp2p;
|
|
352
559
|
|
|
@@ -458,7 +665,7 @@ export interface StrandConfig {
|
|
|
458
665
|
* published the strand (the responder in formation, the creator in host/solo
|
|
459
666
|
* paths; the same party that calls {@link CadreNode.publishStrand}). The founder
|
|
460
667
|
* runs the one-time membership bootstrap at bring-up (writes `Strand.Header`, and
|
|
461
|
-
* for a closed strand the founding `Member`+`
|
|
668
|
+
* for a closed strand the founding `Member`+`Manager`). A joiner leaves this
|
|
462
669
|
* `false`/unset and writes nothing — it receives those rows via Optimystic sync.
|
|
463
670
|
* Defaults to `false`.
|
|
464
671
|
*/
|
|
@@ -536,16 +743,6 @@ export interface FormStrandResult {
|
|
|
536
743
|
memberPrivateKey?: string;
|
|
537
744
|
}
|
|
538
745
|
|
|
539
|
-
/**
|
|
540
|
-
* Result of validating a strand formation request
|
|
541
|
-
*/
|
|
542
|
-
export interface ValidateFormationResult {
|
|
543
|
-
/** Public key for validating this formation */
|
|
544
|
-
validationKey: string;
|
|
545
|
-
/** Signature over the formation approval */
|
|
546
|
-
validationSignature: string;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
746
|
/**
|
|
550
747
|
* Disclosure object provided during strand formation.
|
|
551
748
|
* Contains identity and context information from the initiator.
|
|
@@ -636,13 +833,60 @@ export interface PeerAddressRecord {
|
|
|
636
833
|
sig: string;
|
|
637
834
|
}
|
|
638
835
|
|
|
836
|
+
/**
|
|
837
|
+
* One `CadreControl.CadrePeer` row as read by `ControlDatabase.queryCadrePeers`:
|
|
838
|
+
* the addressing columns plus the persisted membership voucher.
|
|
839
|
+
*
|
|
840
|
+
* The voucher triple (`stampId`, `vouchOwner`, `vouchSig`) is what
|
|
841
|
+
* {@link CadreNode.listAuthorizedMembers} re-checks against the node-local
|
|
842
|
+
* trusted-owner anchor — it is null on a row written before the voucher columns
|
|
843
|
+
* existed, and all three must be present for the row to be authorizable. Named
|
|
844
|
+
* (rather than restated inline at each read site) so a future column addition
|
|
845
|
+
* cannot reach the query without the predicate seeing it.
|
|
846
|
+
*/
|
|
847
|
+
export interface CadrePeerRow {
|
|
848
|
+
/** libp2p peer ID (base58btc) — the row key. */
|
|
849
|
+
peerId: string;
|
|
850
|
+
/** Comma-joined dialable multiaddrs as stored, or null when unpublished. */
|
|
851
|
+
multiaddr: string | null;
|
|
852
|
+
/** Single-use anti-replay nonce the voucher signature is bound to. */
|
|
853
|
+
stampId: string | null;
|
|
854
|
+
/** ed25519 public key (base64url) of the owner that vouched this row. */
|
|
855
|
+
vouchOwner: string | null;
|
|
856
|
+
/** That owner's signature over `digest('CadreControl.CadrePeer', 'vouch', peerId, stampId)`, base64url. */
|
|
857
|
+
vouchSig: string | null;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
/** The voucher-bearing subset of a {@link CadrePeerRow} — what the authorized-membership predicate reads. */
|
|
861
|
+
export type CadrePeerVoucherFields = Pick<CadrePeerRow, 'peerId' | 'stampId' | 'vouchOwner' | 'vouchSig'>;
|
|
862
|
+
|
|
863
|
+
/**
|
|
864
|
+
* One `CadreControl.Revocation` tombstone as read by
|
|
865
|
+
* `ControlDatabase.queryRevocations`: the identity triple plus the `ReissuedAt`
|
|
866
|
+
* counter an owner bumps (`ControlDatabase.reissueRevocations`) to re-write — and
|
|
867
|
+
* therefore re-broadcast — a tombstone that committed while the node was alone.
|
|
868
|
+
*/
|
|
869
|
+
export interface RevocationRow {
|
|
870
|
+
/** Which guarded table's stamp was retired. */
|
|
871
|
+
tableName: RevocableTable;
|
|
872
|
+
/** Primary key of the removed row (peer id / key / strand id). */
|
|
873
|
+
rowKey: string;
|
|
874
|
+
/** The retired single-use nonce — the tombstone's identity. */
|
|
875
|
+
stampId: string;
|
|
876
|
+
/** Monotonic re-issue counter; carries no semantics (see the schema comment). */
|
|
877
|
+
reissuedAt: number;
|
|
878
|
+
}
|
|
879
|
+
|
|
639
880
|
/**
|
|
640
881
|
* Outcome of {@link CadreNode.registerSelf}, surfaced so callers (e.g. the CLI
|
|
641
|
-
* `--
|
|
642
|
-
* - `inserted` — the first
|
|
882
|
+
* `--owner` branch) can log what happened:
|
|
883
|
+
* - `inserted` — the first owner-signed INSERT of this node's `CadrePeer` row.
|
|
643
884
|
* - `refreshed` — a self-signed UPDATE of an existing row (heartbeat / addr change).
|
|
644
|
-
*
|
|
645
|
-
*
|
|
885
|
+
* Also reported when a first publish found its row seated by a concurrent
|
|
886
|
+
* `authorizePeer` of this node's own id and fell through to the UPDATE — the write
|
|
887
|
+
* really was an UPDATE, even though the caller's intent was a first publish.
|
|
888
|
+
* - `skipped` — nothing written (no self-signing key, not yet a member with no
|
|
889
|
+
* owner service to self-insert, or the row was removed mid-publish).
|
|
646
890
|
*/
|
|
647
891
|
export type SelfRegistrationOutcome = 'inserted' | 'refreshed' | 'skipped';
|
|
648
892
|
|
|
@@ -661,7 +905,7 @@ export interface ResolveOpts {
|
|
|
661
905
|
/**
|
|
662
906
|
* Pluggable trust gate, evaluated after signature + freshness pass. Defaults
|
|
663
907
|
* to {@link currentMemberTrustPolicy} (any existing — therefore
|
|
664
|
-
*
|
|
908
|
+
* owner-vouched — CadrePeer member is trusted). Inject a stricter policy
|
|
665
909
|
* (e.g. from `seed-signerkey-trust-policy`) to reject otherwise-valid records
|
|
666
910
|
* before dialing.
|
|
667
911
|
*/
|
|
@@ -725,6 +969,21 @@ export interface DeviceTokenRecord {
|
|
|
725
969
|
sig: string;
|
|
726
970
|
}
|
|
727
971
|
|
|
972
|
+
/**
|
|
973
|
+
* A stored `CadreControl.DeviceToken` row: the self-signed
|
|
974
|
+
* {@link DeviceTokenRecord} plus the row's single-use `StampId` nonce.
|
|
975
|
+
*
|
|
976
|
+
* The stamp is deliberately OUTSIDE `DeviceTokenRecord`: the peer's own `Sig`
|
|
977
|
+
* covers only (peerId, platform, token, updatedAt), while the stamp is bound by
|
|
978
|
+
* the OWNER's insert/delete approvals. Readers need it to drop a row whose stamp
|
|
979
|
+
* has been retired into `CadreControl.Revocation` (a token resurrected by an
|
|
980
|
+
* approval replay on a node that had not yet converged on the tombstone).
|
|
981
|
+
*/
|
|
982
|
+
export interface DeviceTokenRow extends DeviceTokenRecord {
|
|
983
|
+
/** Single-use authorization nonce; rotates on every (re)insert. */
|
|
984
|
+
stampId: string;
|
|
985
|
+
}
|
|
986
|
+
|
|
728
987
|
/**
|
|
729
988
|
* Options for {@link CadreNode.resolveDeviceToken}.
|
|
730
989
|
*/
|
|
@@ -809,9 +1068,9 @@ export interface SeedPeer {
|
|
|
809
1068
|
peerId: string;
|
|
810
1069
|
/** Multiaddrs for connecting to this peer */
|
|
811
1070
|
multiaddrs: string[];
|
|
812
|
-
/** Whether this peer holds an
|
|
813
|
-
|
|
814
|
-
/** ed25519 public key (base64url) — present on
|
|
1071
|
+
/** Whether this peer holds an owner key */
|
|
1072
|
+
isOwner: boolean;
|
|
1073
|
+
/** ed25519 public key (base64url) — present on owner peers for signerKey verification */
|
|
815
1074
|
publicKey?: string;
|
|
816
1075
|
}
|
|
817
1076
|
|
|
@@ -826,9 +1085,9 @@ export interface ControlNetworkSeed {
|
|
|
826
1085
|
partyId: string;
|
|
827
1086
|
/** Known peers in the cadre */
|
|
828
1087
|
peers: SeedPeer[];
|
|
829
|
-
/** Signature over the seed by an
|
|
1088
|
+
/** Signature over the seed by an owner key */
|
|
830
1089
|
signature: string;
|
|
831
|
-
/** The
|
|
1090
|
+
/** The owner key that signed this seed */
|
|
832
1091
|
signerKey: string;
|
|
833
1092
|
}
|
|
834
1093
|
|
|
@@ -841,9 +1100,9 @@ export interface SeedMessage {
|
|
|
841
1100
|
partyId: string;
|
|
842
1101
|
/** Known peers in the cadre */
|
|
843
1102
|
peers: SeedPeer[];
|
|
844
|
-
/** Signature by an
|
|
1103
|
+
/** Signature by an owner key */
|
|
845
1104
|
signature: string;
|
|
846
|
-
/** The
|
|
1105
|
+
/** The owner key that signed this message */
|
|
847
1106
|
signerKey: string;
|
|
848
1107
|
}
|
|
849
1108
|
|
|
@@ -898,6 +1157,14 @@ export interface WakeAck {
|
|
|
898
1157
|
export interface StrandAddrRequest {
|
|
899
1158
|
/** The strand whose strand-network address the requester wants to seed from. */
|
|
900
1159
|
strandId: string;
|
|
1160
|
+
/**
|
|
1161
|
+
* The derived transport peerId the requester's own strand-`strandId` node
|
|
1162
|
+
* runs as (see `strand-transport-key.ts`). Present → the responder records a
|
|
1163
|
+
* delegate admission grant for it, so its own connection gate (and thus its
|
|
1164
|
+
* circuit-relay server, when it runs one) admits that peerId; absent →
|
|
1165
|
+
* today's behavior exactly. See `delegate-admission.ts` for the trust model.
|
|
1166
|
+
*/
|
|
1167
|
+
delegatePeerId?: string;
|
|
901
1168
|
}
|
|
902
1169
|
|
|
903
1170
|
/**
|
|
@@ -935,6 +1202,27 @@ export interface ApplySeedResult {
|
|
|
935
1202
|
peersAdded: number;
|
|
936
1203
|
/** Error message if not successful */
|
|
937
1204
|
error?: string;
|
|
1205
|
+
/**
|
|
1206
|
+
* Owner-flagged seed peers this apply attempted to dial. Zero when the seed
|
|
1207
|
+
* carried no owner peer with an address, or when the seed was rejected before
|
|
1208
|
+
* the dial loop ran.
|
|
1209
|
+
*/
|
|
1210
|
+
ownerDialsAttempted: number;
|
|
1211
|
+
/**
|
|
1212
|
+
* How many of those dials threw. `ownerDialsAttempted > 0 && ownerDialsFailed
|
|
1213
|
+
* === ownerDialsAttempted` means "seeded but stranded": the seed itself was
|
|
1214
|
+
* accepted (`success: true`) but no owner could be reached, so this node has
|
|
1215
|
+
* no connection to bootstrap its control database from. The node retries those
|
|
1216
|
+
* addresses on every control-cohort reconcile pass (see
|
|
1217
|
+
* `CadreNode.dialColdStartBootstrap`), so this is a signal, not a fatal error.
|
|
1218
|
+
*
|
|
1219
|
+
* Zero failures is NOT proof of a live connection: the receiving node's
|
|
1220
|
+
* membership gate denies AFTER the dialer's upgrade completes (see
|
|
1221
|
+
* `createMembershipConnectionGater`), so a dial can resolve and then be torn
|
|
1222
|
+
* down. Treat a non-zero value as certain failure and zero as "no failure
|
|
1223
|
+
* observed".
|
|
1224
|
+
*/
|
|
1225
|
+
ownerDialsFailed: number;
|
|
938
1226
|
}
|
|
939
1227
|
|
|
940
1228
|
// ============================================================================
|
|
@@ -953,15 +1241,18 @@ export type NodeTopology = 'public' | 'nat';
|
|
|
953
1241
|
export interface CadreInvite {
|
|
954
1242
|
/** Party ID of the cadre */
|
|
955
1243
|
partyId: string;
|
|
956
|
-
/** Multiaddrs to dial the
|
|
957
|
-
|
|
1244
|
+
/** Multiaddrs to dial the owner */
|
|
1245
|
+
ownerAddrs: string[];
|
|
958
1246
|
/**
|
|
959
|
-
*
|
|
960
|
-
* so a cold-start invitee can pin the trusted
|
|
1247
|
+
* Owner ed25519 public keys (base64url) of the cadre, carried out-of-band
|
|
1248
|
+
* so a cold-start invitee can pin the trusted owner set before applying
|
|
961
1249
|
* any seed (the seed itself cannot vouch for its own signer). Populated by
|
|
962
|
-
* `createInvite` from the issuer's
|
|
1250
|
+
* `createInvite` from the issuer's OWN node-local trusted-owner anchor —
|
|
1251
|
+
* not its replicated `OwnerKey` table, because the invitee anchors whatever
|
|
1252
|
+
* arrives here and a stranger's genesis-inserted key must not ride an
|
|
1253
|
+
* otherwise-legitimate invite into a fresh node's anchor.
|
|
963
1254
|
*/
|
|
964
|
-
|
|
1255
|
+
ownerKeys?: string[];
|
|
965
1256
|
/** Optional invite token for validation */
|
|
966
1257
|
token?: string;
|
|
967
1258
|
/** Timestamp when invite was created */
|
package/dist/authority-key.d.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import type { PrivateKey } from '@libp2p/interface';
|
|
2
|
-
/**
|
|
3
|
-
* A household authority keypair expressed in the base64url Ed25519 form that
|
|
4
|
-
* `@optimystic/quereus-plugin-crypto` (`sign`/`verify`/`getPublicKey`) consumes.
|
|
5
|
-
*/
|
|
6
|
-
export interface AuthorityKeyPair {
|
|
7
|
-
/** 32-byte Ed25519 seed, base64url-encoded — the crypto-plugin private key. */
|
|
8
|
-
privateKeyB64: string;
|
|
9
|
-
/** 32-byte Ed25519 public key, base64url-encoded. */
|
|
10
|
-
publicKeyB64: string;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Bridge a libp2p Ed25519 private key into the base64url keypair used by the
|
|
14
|
-
* control-database authority constraints.
|
|
15
|
-
*
|
|
16
|
-
* libp2p stores an Ed25519 private key as 64 raw bytes: the first 32 are the
|
|
17
|
-
* seed (the actual scalar source), the last 32 are the public key — see
|
|
18
|
-
* `@libp2p/crypto`'s `Ed25519PrivateKey`. `@optimystic/quereus-plugin-crypto`
|
|
19
|
-
* (via `@noble/curves`) treats the 32-byte seed *as* the private key and
|
|
20
|
-
* derives the public key from it with standard Ed25519. The two derivations
|
|
21
|
-
* agree, so the node's peer identity and its authority key are one keypair:
|
|
22
|
-
* `getPublicKey(privateKeyB64)` === `publicKeyB64`.
|
|
23
|
-
*
|
|
24
|
-
* @param privateKey - The node's libp2p Ed25519 private key.
|
|
25
|
-
* @returns The base64url seed/public-key pair for authority operations.
|
|
26
|
-
* @throws If the key is not Ed25519 or the raw bytes aren't the expected length.
|
|
27
|
-
*/
|
|
28
|
-
export declare function authorityKeyFromLibp2p(privateKey: PrivateKey): AuthorityKeyPair;
|
|
29
|
-
/**
|
|
30
|
-
* Derive the base64url Ed25519 public key from a base64url 32-byte private seed
|
|
31
|
-
* — the same derivation the seed-bootstrap signer uses internally
|
|
32
|
-
* (`SeedBootstrapService` constructor). Use this to enroll a standalone
|
|
33
|
-
* (non-libp2p) authority key into the control DB via
|
|
34
|
-
* `ControlDatabase.ensureAuthorityKey` before minting an invite, when the
|
|
35
|
-
* authority key is *not* the node's peer identity (so `authorityKeyFromLibp2p`,
|
|
36
|
-
* which needs a libp2p key object, does not apply).
|
|
37
|
-
*
|
|
38
|
-
* @param privateKeyB64 - The base64url-encoded 32-byte Ed25519 seed.
|
|
39
|
-
* @returns The base64url-encoded Ed25519 public key.
|
|
40
|
-
*/
|
|
41
|
-
export declare function authorityPublicKeyFromPrivate(privateKeyB64: string): string;
|
package/dist/authority-key.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { toString as uint8ArrayToString } from 'uint8arrays';
|
|
2
|
-
import { getPublicKey } from '@optimystic/quereus-plugin-crypto';
|
|
3
|
-
/**
|
|
4
|
-
* Bridge a libp2p Ed25519 private key into the base64url keypair used by the
|
|
5
|
-
* control-database authority constraints.
|
|
6
|
-
*
|
|
7
|
-
* libp2p stores an Ed25519 private key as 64 raw bytes: the first 32 are the
|
|
8
|
-
* seed (the actual scalar source), the last 32 are the public key — see
|
|
9
|
-
* `@libp2p/crypto`'s `Ed25519PrivateKey`. `@optimystic/quereus-plugin-crypto`
|
|
10
|
-
* (via `@noble/curves`) treats the 32-byte seed *as* the private key and
|
|
11
|
-
* derives the public key from it with standard Ed25519. The two derivations
|
|
12
|
-
* agree, so the node's peer identity and its authority key are one keypair:
|
|
13
|
-
* `getPublicKey(privateKeyB64)` === `publicKeyB64`.
|
|
14
|
-
*
|
|
15
|
-
* @param privateKey - The node's libp2p Ed25519 private key.
|
|
16
|
-
* @returns The base64url seed/public-key pair for authority operations.
|
|
17
|
-
* @throws If the key is not Ed25519 or the raw bytes aren't the expected length.
|
|
18
|
-
*/
|
|
19
|
-
export function authorityKeyFromLibp2p(privateKey) {
|
|
20
|
-
if (privateKey.type !== 'Ed25519') {
|
|
21
|
-
throw new Error(`authorityKeyFromLibp2p requires an Ed25519 key, got ${privateKey.type}`);
|
|
22
|
-
}
|
|
23
|
-
const raw = privateKey.raw;
|
|
24
|
-
if (raw.length !== 64) {
|
|
25
|
-
throw new Error(`Expected a 64-byte Ed25519 raw private key (seed||public), got ${raw.length} bytes`);
|
|
26
|
-
}
|
|
27
|
-
const seed = raw.subarray(0, 32);
|
|
28
|
-
const publicKeyRaw = privateKey.publicKey.raw;
|
|
29
|
-
if (publicKeyRaw.length !== 32) {
|
|
30
|
-
throw new Error(`Expected a 32-byte Ed25519 public key, got ${publicKeyRaw.length} bytes`);
|
|
31
|
-
}
|
|
32
|
-
return {
|
|
33
|
-
privateKeyB64: uint8ArrayToString(seed, 'base64url'),
|
|
34
|
-
publicKeyB64: uint8ArrayToString(publicKeyRaw, 'base64url'),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Derive the base64url Ed25519 public key from a base64url 32-byte private seed
|
|
39
|
-
* — the same derivation the seed-bootstrap signer uses internally
|
|
40
|
-
* (`SeedBootstrapService` constructor). Use this to enroll a standalone
|
|
41
|
-
* (non-libp2p) authority key into the control DB via
|
|
42
|
-
* `ControlDatabase.ensureAuthorityKey` before minting an invite, when the
|
|
43
|
-
* authority key is *not* the node's peer identity (so `authorityKeyFromLibp2p`,
|
|
44
|
-
* which needs a libp2p key object, does not apply).
|
|
45
|
-
*
|
|
46
|
-
* @param privateKeyB64 - The base64url-encoded 32-byte Ed25519 seed.
|
|
47
|
-
* @returns The base64url-encoded Ed25519 public key.
|
|
48
|
-
*/
|
|
49
|
-
export function authorityPublicKeyFromPrivate(privateKeyB64) {
|
|
50
|
-
return getPublicKey(privateKeyB64, 'ed25519', 'base64url', 'base64url');
|
|
51
|
-
}
|
|
52
|
-
//# sourceMappingURL=authority-key.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"authority-key.js","sourceRoot":"","sources":["../src/authority-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAcjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,sBAAsB,CAAC,UAAsB;IAC3D,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,uDAAuD,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC;IAC3B,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,kEAAkE,GAAG,CAAC,MAAM,QAAQ,CAAC,CAAC;IACxG,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjC,MAAM,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC;IAC9C,IAAI,YAAY,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,8CAA8C,YAAY,CAAC,MAAM,QAAQ,CAAC,CAAC;IAC7F,CAAC;IAED,OAAO;QACL,aAAa,EAAE,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC;QACpD,YAAY,EAAE,kBAAkB,CAAC,YAAY,EAAE,WAAW,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,6BAA6B,CAAC,aAAqB;IACjE,OAAO,YAAY,CAAC,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,CAAW,CAAC;AACpF,CAAC"}
|