@serfab/cadre-core 1.15.0 → 1.17.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.
Files changed (199) hide show
  1. package/README.md +1 -1
  2. package/dist/bootstrap-peer-store-file.js +1 -1
  3. package/dist/bootstrap-peer-store.d.ts +1 -1
  4. package/dist/bootstrap-peer-store.js +4 -4
  5. package/dist/cadre-invite-protocol.d.ts +55 -138
  6. package/dist/cadre-invite-protocol.js +94 -449
  7. package/dist/cadre-invite-protocol.js.map +1 -1
  8. package/dist/cadre-join-request.d.ts +15 -2
  9. package/dist/cadre-join-request.js +22 -3
  10. package/dist/cadre-join-request.js.map +1 -1
  11. package/dist/cadre-node.d.ts +530 -247
  12. package/dist/cadre-node.js +1046 -525
  13. package/dist/cadre-node.js.map +1 -1
  14. package/dist/control-authorization.d.ts +36 -14
  15. package/dist/control-authorization.js +20 -3
  16. package/dist/control-authorization.js.map +1 -1
  17. package/dist/control-cohort.d.ts +22 -23
  18. package/dist/control-cohort.js +24 -24
  19. package/dist/control-cohort.js.map +1 -1
  20. package/dist/control-database.d.ts +416 -136
  21. package/dist/control-database.js +757 -378
  22. package/dist/control-database.js.map +1 -1
  23. package/dist/control-protocol-guard.d.ts +5 -4
  24. package/dist/control-protocol-guard.js +6 -3
  25. package/dist/control-protocol-guard.js.map +1 -1
  26. package/dist/control-schema-version.d.ts +80 -0
  27. package/dist/control-schema-version.js +203 -0
  28. package/dist/control-schema-version.js.map +1 -0
  29. package/dist/control-schema.d.ts +45 -5
  30. package/dist/control-schema.js +254 -70
  31. package/dist/control-schema.js.map +1 -1
  32. package/dist/control-write-retry.d.ts +5 -5
  33. package/dist/control-write-retry.js +10 -8
  34. package/dist/control-write-retry.js.map +1 -1
  35. package/dist/delegate-admission.d.ts +13 -0
  36. package/dist/delegate-admission.js +27 -0
  37. package/dist/delegate-admission.js.map +1 -1
  38. package/dist/ed25519-key.d.ts +10 -7
  39. package/dist/ed25519-key.js +23 -20
  40. package/dist/ed25519-key.js.map +1 -1
  41. package/dist/edge-node/config.d.ts +122 -0
  42. package/dist/edge-node/config.js +199 -0
  43. package/dist/edge-node/config.js.map +1 -0
  44. package/dist/edge-node/dissolve.d.ts +95 -0
  45. package/dist/edge-node/dissolve.js +184 -0
  46. package/dist/edge-node/dissolve.js.map +1 -0
  47. package/dist/edge-node/edge-join.d.ts +44 -0
  48. package/dist/edge-node/edge-join.js +227 -0
  49. package/dist/edge-node/edge-join.js.map +1 -0
  50. package/dist/edge-node/edge-node.d.ts +295 -0
  51. package/dist/edge-node/edge-node.js +704 -0
  52. package/dist/edge-node/edge-node.js.map +1 -0
  53. package/dist/edge-node/index.d.ts +18 -0
  54. package/dist/edge-node/index.js +18 -0
  55. package/dist/edge-node/index.js.map +1 -0
  56. package/dist/edge-node/join-credential.d.ts +11 -0
  57. package/dist/edge-node/join-credential.js +11 -0
  58. package/dist/edge-node/join-credential.js.map +1 -0
  59. package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
  60. package/dist/edge-node/lazy-raw-storage.js +134 -0
  61. package/dist/edge-node/lazy-raw-storage.js.map +1 -0
  62. package/dist/edge-node/node-local.d.ts +60 -0
  63. package/dist/edge-node/node-local.js +58 -0
  64. package/dist/edge-node/node-local.js.map +1 -0
  65. package/dist/edge-node/options.d.ts +83 -0
  66. package/dist/edge-node/options.js +110 -0
  67. package/dist/edge-node/options.js.map +1 -0
  68. package/dist/edge-node/owner-grant.d.ts +41 -0
  69. package/dist/edge-node/owner-grant.js +38 -0
  70. package/dist/edge-node/owner-grant.js.map +1 -0
  71. package/dist/edge-node/platform.d.ts +140 -0
  72. package/dist/edge-node/platform.js +12 -0
  73. package/dist/edge-node/platform.js.map +1 -0
  74. package/dist/edge-node/strand-move.d.ts +115 -0
  75. package/dist/edge-node/strand-move.js +223 -0
  76. package/dist/edge-node/strand-move.js.map +1 -0
  77. package/dist/edge-node/strands.d.ts +28 -0
  78. package/dist/edge-node/strands.js +60 -0
  79. package/dist/edge-node/strands.js.map +1 -0
  80. package/dist/index.d.ts +16 -7
  81. package/dist/index.js +26 -7
  82. package/dist/index.js.map +1 -1
  83. package/dist/joined-strand-store.d.ts +3 -3
  84. package/dist/joined-strand-store.js +3 -3
  85. package/dist/joined-strand-store.js.map +1 -1
  86. package/dist/key-store.d.ts +9 -4
  87. package/dist/key-store.js +9 -4
  88. package/dist/key-store.js.map +1 -1
  89. package/dist/machine-removal.d.ts +19 -39
  90. package/dist/machine-removal.js +24 -98
  91. package/dist/machine-removal.js.map +1 -1
  92. package/dist/member-redemption.d.ts +250 -0
  93. package/dist/member-redemption.js +485 -0
  94. package/dist/member-redemption.js.map +1 -0
  95. package/dist/owner-anchor-sync.d.ts +4 -4
  96. package/dist/owner-anchor-sync.js +16 -15
  97. package/dist/owner-anchor-sync.js.map +1 -1
  98. package/dist/owner-grant-protocol.d.ts +247 -0
  99. package/dist/owner-grant-protocol.js +330 -0
  100. package/dist/owner-grant-protocol.js.map +1 -0
  101. package/dist/owner-key-export.d.ts +70 -0
  102. package/dist/owner-key-export.js +279 -0
  103. package/dist/owner-key-export.js.map +1 -0
  104. package/dist/owner-key-flows.d.ts +55 -0
  105. package/dist/owner-key-flows.js +60 -0
  106. package/dist/owner-key-flows.js.map +1 -0
  107. package/dist/owner-key-store.d.ts +31 -0
  108. package/dist/owner-key-store.js +81 -0
  109. package/dist/owner-key-store.js.map +1 -0
  110. package/dist/owner-keys.d.ts +78 -0
  111. package/dist/owner-keys.js +133 -0
  112. package/dist/owner-keys.js.map +1 -0
  113. package/dist/owner-signer.d.ts +54 -0
  114. package/dist/owner-signer.js +51 -0
  115. package/dist/owner-signer.js.map +1 -0
  116. package/dist/peer-authorization.d.ts +83 -18
  117. package/dist/peer-authorization.js +123 -53
  118. package/dist/peer-authorization.js.map +1 -1
  119. package/dist/provider/rent-node.js +1 -1
  120. package/dist/provider/rent-node.js.map +1 -1
  121. package/dist/seed-bootstrap.d.ts +131 -109
  122. package/dist/seed-bootstrap.js +260 -248
  123. package/dist/seed-bootstrap.js.map +1 -1
  124. package/dist/seed-trust-policy.js +4 -4
  125. package/dist/strand-addr-protocol.d.ts +4 -1
  126. package/dist/strand-addr-protocol.js +8 -2
  127. package/dist/strand-addr-protocol.js.map +1 -1
  128. package/dist/strand-cohort.d.ts +3 -0
  129. package/dist/strand-cohort.js.map +1 -1
  130. package/dist/strand-database.d.ts +1 -0
  131. package/dist/strand-database.js +3 -0
  132. package/dist/strand-database.js.map +1 -1
  133. package/dist/strand-first-sync-gate.d.ts +4 -3
  134. package/dist/strand-first-sync-gate.js +4 -3
  135. package/dist/strand-first-sync-gate.js.map +1 -1
  136. package/dist/strand-instance-manager.d.ts +1 -1
  137. package/dist/strand-membership-writer.d.ts +13 -2
  138. package/dist/strand-membership-writer.js +52 -16
  139. package/dist/strand-membership-writer.js.map +1 -1
  140. package/dist/strand-move.d.ts +6 -5
  141. package/dist/strand-move.js +31 -30
  142. package/dist/strand-move.js.map +1 -1
  143. package/dist/strand-peer-redial.js +2 -3
  144. package/dist/strand-peer-redial.js.map +1 -1
  145. package/dist/types.d.ts +286 -103
  146. package/dist/types.js.map +1 -1
  147. package/package.json +16 -7
  148. package/src/bootstrap-peer-store-file.ts +44 -44
  149. package/src/bootstrap-peer-store.ts +214 -214
  150. package/src/cadre-invite-protocol.ts +131 -531
  151. package/src/cadre-join-request.ts +188 -167
  152. package/src/cadre-node.ts +1166 -541
  153. package/src/control-authorization.ts +47 -14
  154. package/src/control-cohort.ts +127 -128
  155. package/src/control-database.ts +940 -456
  156. package/src/control-protocol-guard.ts +7 -4
  157. package/src/control-schema-version.ts +251 -0
  158. package/src/control-schema.ts +265 -70
  159. package/src/control-write-retry.ts +569 -567
  160. package/src/delegate-admission.ts +37 -0
  161. package/src/ed25519-key.ts +174 -171
  162. package/src/edge-node/config.ts +277 -0
  163. package/src/edge-node/dissolve.ts +251 -0
  164. package/src/edge-node/edge-join.ts +319 -0
  165. package/src/edge-node/edge-node.ts +1045 -0
  166. package/src/edge-node/index.ts +59 -0
  167. package/src/edge-node/join-credential.ts +16 -0
  168. package/src/edge-node/lazy-raw-storage.ts +178 -0
  169. package/src/edge-node/node-local.ts +84 -0
  170. package/src/edge-node/options.ts +179 -0
  171. package/src/edge-node/owner-grant.ts +63 -0
  172. package/src/edge-node/platform.ts +151 -0
  173. package/src/edge-node/strand-move.ts +270 -0
  174. package/src/edge-node/strands.ts +72 -0
  175. package/src/index.ts +102 -5
  176. package/src/joined-strand-store.ts +4 -4
  177. package/src/key-store.ts +10 -4
  178. package/src/machine-removal.ts +26 -111
  179. package/src/member-redemption.ts +642 -0
  180. package/src/owner-anchor-sync.ts +19 -18
  181. package/src/owner-grant-protocol.ts +532 -0
  182. package/src/owner-key-export.ts +348 -0
  183. package/src/owner-key-flows.ts +109 -0
  184. package/src/owner-key-store.ts +85 -0
  185. package/src/owner-keys.ts +178 -0
  186. package/src/owner-signer.ts +94 -0
  187. package/src/peer-authorization.ts +558 -488
  188. package/src/provider/rent-node.ts +1 -1
  189. package/src/seed-bootstrap.ts +301 -312
  190. package/src/seed-trust-policy.ts +4 -4
  191. package/src/strand-addr-protocol.ts +547 -538
  192. package/src/strand-cohort.ts +4 -0
  193. package/src/strand-database.ts +3 -0
  194. package/src/strand-first-sync-gate.ts +4 -3
  195. package/src/strand-instance-manager.ts +1 -1
  196. package/src/strand-membership-writer.ts +61 -16
  197. package/src/strand-move.ts +45 -36
  198. package/src/strand-peer-redial.ts +2 -3
  199. package/src/types.ts +304 -103
@@ -1,9 +1,10 @@
1
1
  import type { Libp2p } from '@libp2p/interface';
2
2
  import { type PeerDialBudget } from './peer-dial.js';
3
- import type { ControlNetworkSeed, SeedPeer, SeedAckMessage, SeedDeliveryTarget, AuthorizePeerOptions, ApplySeedResult, AddDroneOptions, DroneInitResult, CadreInviteRow, PeerAddressRecord, DeviceTokenRecord, RevocationRow, RevocationLedgerOpenResult } from './types.js';
4
- import type { ControlDatabase, MachineRemovalWrites } from './control-database.js';
3
+ import type { ControlNetworkSeed, SeedPeer, SeedAckMessage, SeedDeliveryTarget, AuthorizePeerOptions, ApplySeedResult, AddDroneOptions, DroneInitResult, CadreInviteRow, OwnerGrantRow, PeerAddressRecord, DeviceTokenRecord, RevocationRow, RevocationLedgerOpenResult, MachineRemovalResult } from './types.js';
4
+ import type { ControlDatabase } from './control-database.js';
5
5
  import { type SeedTrustPolicy } from './seed-trust-policy.js';
6
6
  import type { TrustedOwnerStore } from './trusted-owner-store.js';
7
+ import { type OwnerSign, type OwnerSigner } from './owner-signer.js';
7
8
  /** Protocol ID for seed delivery */
8
9
  export declare const SEED_PROTOCOL = "/sereus/seed/1.0.0";
9
10
  /**
@@ -47,10 +48,12 @@ export declare function mergeSeedPeers(node: Libp2p, peers: readonly SeedPeer[])
47
48
  export interface SeedBootstrapConfig {
48
49
  /** Party ID for this cadre */
49
50
  partyId: string;
50
- /** Owner private key for signing seeds and peer authorizations (base64url) */
51
- ownerPrivateKey?: string;
52
- /** Owner public key (base64url) - derived from private key if not provided */
53
- ownerPublicKey?: string;
51
+ /**
52
+ * The owner signer every seed and owner-signed write of this service goes through
53
+ * (`owner-signer.ts`). Unset for a listener-only service, which receives and applies seeds
54
+ * but refuses every owner write.
55
+ */
56
+ ownerSigner?: OwnerSigner;
54
57
  /**
55
58
  * Trust anchor for incoming seeds. Decides whether a signature-verified
56
59
  * `signerKey` should be trusted, against the receiver's anchored owner
@@ -107,7 +110,7 @@ export interface SeedBootstrapConfig {
107
110
  * sized for the forms that dial inside it. Delivery does not set `runOnLimitedConnection`,
108
111
  * so it does not use a limited relayed connection today; the relayed-dial count is the upper
109
112
  * bound on the dial it can use, the same choice `CadreNode.controlDialBudget` makes for every
110
- * address. It holds only link work because the receiver acks before its owner dials.
113
+ * address. It holds only link work because the receiver acks before it dials the seed's peers.
111
114
  *
112
115
  * NOTE: no transfer allowance — a seed is a peer list of a few KB, and `MAX_SEED_SIZE` (1 MiB)
113
116
  * is a defensive cap. If seeds ever grow toward that cap, add an allowance the way
@@ -116,7 +119,7 @@ export interface SeedBootstrapConfig {
116
119
  seedDeliverTimeoutMs?: number;
117
120
  /**
118
121
  * Time limits for each peer this service dials from a list of addresses —
119
- * {@link SeedBootstrapService.applySeed}'s owner dials and
122
+ * {@link SeedBootstrapService.applySeed}'s dials of the seed's preferred peers and
120
123
  * {@link SeedBootstrapService.deliverSeed}'s dial of a peer id with addresses — per
121
124
  * address and per peer (see `peer-dial.ts`). Defaults to {@link DEFAULT_PEER_DIAL_BUDGET}; a `CadreNode`
122
125
  * passes its `network.controlCohort` limits.
@@ -135,7 +138,7 @@ export interface SeedEventCallbacks {
135
138
  * `seed` is the applied seed itself: the inbound protocol handler applies it
136
139
  * INSIDE the service, so this callback is the only seam through which a
137
140
  * `CadreNode` sees a wire-delivered seed's contents — which it needs to
138
- * retain the owner-flagged peers as cold-start bootstrap dial targets.
141
+ * retain the preferred peers as cold-start bootstrap dial targets.
139
142
  */
140
143
  onSeedApplied?: (partyId: string, peersAdded: number, seed: ControlNetworkSeed) => void;
141
144
  /** Called when seed application fails */
@@ -152,7 +155,6 @@ export declare class SeedBootstrapService {
152
155
  private readonly config;
153
156
  private libp2pNode;
154
157
  private controlDatabase;
155
- private readonly ownerPublicKey;
156
158
  private readonly trustPolicy;
157
159
  private readonly seedReadTimeoutMs;
158
160
  private readonly maxConcurrentSeeds;
@@ -168,20 +170,21 @@ export declare class SeedBootstrapService {
168
170
  */
169
171
  setEventCallbacks(callbacks: SeedEventCallbacks): void;
170
172
  /**
171
- * Whether this service holds an owner private key, i.e. can produce the
172
- * owner signatures that gate `CadrePeer` / `DeviceToken` inserts, deletes,
173
- * and re-authorizations. A seed-listener-only service (`enableSeedListener`,
174
- * no owner key) returns false: it can receive/apply seeds but cannot author
175
- * or re-issue owner writes. Used by the write-while-alone re-replication
176
- * drain to skip owner work on a non-owner node.
173
+ * Whether this service holds an owner signer, i.e. can produce the owner signatures that
174
+ * gate `CadrePeer` / `DeviceToken` inserts, deletes, and re-authorizations. A
175
+ * seed-listener-only service (`enableSeedListener`, no signer) returns false: it can
176
+ * receive/apply seeds but cannot author or re-issue owner writes. Used by the
177
+ * write-while-alone re-replication drain to skip owner work on a non-owner node.
177
178
  */
178
179
  canAuthorize(): boolean;
180
+ /** The owner signer this service was built with, or null for a listener-only service. */
181
+ get ownerSigner(): OwnerSigner | null;
179
182
  /**
180
183
  * Initialize the service with libp2p node and control database.
181
184
  *
182
185
  * `registerHandler` (default true) gates registration of the shared inbound
183
186
  * `/sereus/seed/1.0.0` handler on `libp2pNode`. Persistent services
184
- * (`initializeSeedBootstrap`, `enableSeedListener`) own that handler and leave
187
+ * (`CadreNode.useOwnerSigner`, `enableSeedListener`) own that handler and leave
185
188
  * it on. The throwaway temp service CadreNode builds in `applySeed` passes
186
189
  * `false`: it only needs the stored `libp2pNode` / `controlDatabase` for
187
190
  * dialing and known-key lookup, and must NOT bind a discarded closure to the
@@ -207,8 +210,11 @@ export declare class SeedBootstrapService {
207
210
  * `UpdatedAt` but no self-signature (`Sig` null) — the owner cannot produce
208
211
  * the peer's self-signature, so the peer must self-publish (see
209
212
  * {@link CadreNode.registerSelf}) before the row resolves.
213
+ *
214
+ * @param sign - The signature function of an owner approval the caller already holds, so a
215
+ * multi-step action asks once; omitted, this write asks for its own approval.
210
216
  */
211
- authorizePeer(options: AuthorizePeerOptions): Promise<void>;
217
+ authorizePeer(options: AuthorizePeerOptions, sign?: OwnerSign): Promise<void>;
212
218
  /**
213
219
  * Owner-signed INSERT of this node's OWN self-signed address record.
214
220
  *
@@ -222,17 +228,16 @@ export declare class SeedBootstrapService {
222
228
  * in which case the row in the database is NOT this record and carries whatever
223
229
  * `Sig` that writer had, so the caller must self-update to publish its signature.
224
230
  */
225
- insertSelfPeerRecord(record: PeerAddressRecord): Promise<boolean>;
231
+ insertSelfPeerRecord(record: PeerAddressRecord, sign?: OwnerSign): Promise<boolean>;
226
232
  /**
227
- * Shared owner-signed `CadrePeer` INSERT — the row fields and the owner-key
233
+ * Shared owner-signed `CadrePeer` INSERT — the row fields and the owner-signer
228
234
  * precondition; the stamp mint, voucher signature, write lock, existence check and
229
235
  * membership notify are all {@link ControlDatabase.insertCadrePeer}'s (see there for
230
236
  * the anti-replay and insert-race rationale). No `CadrePeer` write may bypass that
231
237
  * method: the write is what admits the peer's traffic.
232
238
  *
233
- * The DB check precedes the owner-key check here, matching the order this method has
234
- * always surfaced them (the owner-key error used to come out of the signer, after the
235
- * DB check).
239
+ * The DB check precedes the owner-signer check here, matching the order this method has
240
+ * always surfaced them.
236
241
  *
237
242
  * @returns `true` when this call performed the INSERT, `false` when the row was
238
243
  * already seated by a concurrent writer. The loser needs to know: an authorize seats
@@ -257,7 +262,7 @@ export declare class SeedBootstrapService {
257
262
  * fresh signature — unaffected by the cleared row's retired stamp
258
263
  * (`DeviceToken.NotRevoked`).
259
264
  */
260
- insertSelfDeviceToken(record: DeviceTokenRecord): Promise<void>;
265
+ insertSelfDeviceToken(record: DeviceTokenRecord, sign?: OwnerSign): Promise<void>;
261
266
  /**
262
267
  * Owner-signed DELETE of a peer's `DeviceToken` row (logout / token
263
268
  * invalidation). The `DeviceToken.AuthorizedDelete` constraint validates an owner
@@ -276,44 +281,27 @@ export declare class SeedBootstrapService {
276
281
  * Both digests, the stamp read, and the transaction come from
277
282
  * {@link ControlDatabase.deleteDeviceToken} — one shared implementation across
278
283
  * `CadrePeer` / `DeviceToken` / `Strand` / `ValidationKey`. What stays here is the
279
- * owner-key precondition; a no-op on an already-absent row is the shared body's
284
+ * owner-signer precondition; a no-op on an already-absent row is the shared body's
280
285
  * behavior, and unlike {@link removePeer} nothing here rides on it.
281
286
  */
282
- deleteDeviceToken(peerId: string): Promise<void>;
287
+ deleteDeviceToken(peerId: string, sign?: OwnerSign): Promise<void>;
283
288
  /**
284
- * Return the configured owner private key, or throw if none is set. The single
285
- * precondition gate for every owner-signed write. {@link removePeer} /
286
- * {@link reauthorizePeer} read the row's `StampId` from the DB BEFORE they sign, so
287
- * they call this up front — otherwise a keyless service would either surface the
288
- * wrong "Control database not initialized" error or, worse, silently no-op when the
289
- * target row is absent (the early `stampId === null` return) instead of rejecting.
290
- */
291
- private requireOwnerPrivateKey;
292
- /**
293
- * The owner PUBLIC key that rides in every owner-signed write's context, non-null.
294
- *
295
- * The field is nullable because a read-only service carries no owner key at all, but the
296
- * constructor derives the public key whenever `ownerPrivateKey` is set — so gating on
297
- * {@link requireOwnerPrivateKey} first makes the pair inseparable and the second throw
298
- * unreachable. Callers that must reject a keyless service BEFORE any DB read use this as
299
- * their first line and get the owner-key precondition for free.
289
+ * Return the configured owner signer, or throw if none is set. The single precondition
290
+ * gate for every owner-signed write. {@link removePeer} / {@link reauthorizePeer} read the
291
+ * row's `StampId` from the DB BEFORE they sign, so they call this up front — otherwise a
292
+ * signerless service would either surface the wrong "Control database not initialized"
293
+ * error or, worse, silently no-op when the target row is absent (the early
294
+ * `stampId === null` return) instead of rejecting.
300
295
  */
301
- private requireOwnerPublicKey;
296
+ private requireOwnerSigner;
297
+ /** The control database {@link initialize} attached, or a throw. */
298
+ private requireControlDatabase;
302
299
  /**
303
- * Sign a base64url digest with the owner key (ed25519). The single place the
304
- * owner private key is applied; callers pass the canonical domain-tagged digest for
305
- * the specific action ({@link deviceTokenAddDigest}), or the raw-bytes form via
306
- * {@link signMessageBytes}. Throws if no owner key is set.
300
+ * Run `body` with an owner signature function: the caller's `sign` when it passes one (a
301
+ * `CadreNode` action already inside its approval), otherwise one approval of this service's
302
+ * signer for `action`, the plain-words description a prompting signer shows.
307
303
  */
308
- private signDigest;
309
- /**
310
- * Adapt the control database's raw-bytes `signMessage` callback (every guarded delete
311
- * takes one) to {@link signDigest}'s base64url-string form. Both encodings hash to the
312
- * same signed bytes (`sign` decodes its base64url input), so a signature minted here
313
- * satisfies the same schema CHECK as one from the callers that sign the bytes directly — see
314
- * `control-revocation-replay.spec.ts`'s "raw-bytes and digest-string signers agree".
315
- */
316
- private signMessageBytes;
304
+ private withOwnerSign;
317
305
  /**
318
306
  * Remove a peer from the cadre by owner signature.
319
307
  *
@@ -336,45 +324,46 @@ export declare class SeedBootstrapService {
336
324
  * Both digests, the stamp read, the transaction, and the post-commit membership notify
337
325
  * come from {@link ControlDatabase.deleteCadrePeer} — one shared implementation across
338
326
  * `CadrePeer` / `DeviceToken` / `Strand` / `ValidationKey`. What stays here is the
339
- * owner-key precondition and the absent-row gate the notify depends on (below).
327
+ * owner-signer precondition and the absent-row gate the notify depends on (below).
340
328
  */
341
- removePeer(peerId: string): Promise<void>;
329
+ removePeer(peerId: string, sign?: OwnerSign): Promise<void>;
342
330
  /**
343
- * Seat `ownerPublicKey` as a further owner, signed by this service's owner key. The
331
+ * Seat `ownerPublicKey` as a further owner, signed by this service's owner signer. The
344
332
  * liveness check, the stamp, the signature and the membership notify are
345
- * {@link ControlDatabase.insertOwnerKeyVouched}'s; what stays here is the owner-key
333
+ * {@link ControlDatabase.insertOwnerKeyVouched}'s; what stays here is the owner-signer
346
334
  * precondition, checked before any read (see {@link removePeer}).
347
335
  *
336
+ * @param label - the user's name for the key, bound into the signature
337
+ * ({@link ControlDatabase.insertOwnerKey} has the rule).
348
338
  * @returns `true` when the row was seated, `false` when the key is already a live owner.
349
339
  */
350
- addOwner(ownerPublicKey: string): Promise<boolean>;
340
+ addOwner(ownerPublicKey: string, label?: string, sign?: OwnerSign): Promise<boolean>;
351
341
  /**
352
- * Remove `ownerPublicKey` from the owners, signed by this service's owner key: the delete
342
+ * Remove `ownerPublicKey` from the owners, signed by this service's owner signer: the delete
353
343
  * and its `Revocation` tombstone in one transaction. The by-name refusals (self-removal,
354
344
  * the last owner), the digests and the membership notify are
355
- * {@link ControlDatabase.deleteOwnerKey}'s; what stays here is the owner-key precondition,
345
+ * {@link ControlDatabase.deleteOwnerKey}'s; what stays here is the owner-signer precondition,
356
346
  * checked before any read (see {@link removePeer}).
357
347
  *
358
348
  * @returns `true` when the row was removed, `false` when the key is not a live owner here.
359
349
  */
360
- removeOwner(ownerPublicKey: string): Promise<boolean>;
350
+ removeOwner(ownerPublicKey: string, sign?: OwnerSign): Promise<boolean>;
361
351
  /**
362
- * Re-vouch another owner's row under this service's owner key, so it stands on a remaining
352
+ * Re-vouch another owner's row under this service's owner signer, so it stands on a remaining
363
353
  * owner's signature before the owner that added it is removed
364
- * ({@link ControlDatabase.revouchOwnerKey}; for `CadreNode.removeMachine` only). What stays
365
- * here is the owner-key precondition, checked before any read (see {@link removePeer}).
354
+ * ({@link ControlDatabase.revouchOwnerKey}; for `CadreNode.removeOwner` only). What stays
355
+ * here is the owner-signer precondition, checked before any read (see {@link removePeer}).
366
356
  *
367
357
  * @returns `true` when the row was re-vouched, `false` when the key is not a live owner here.
368
358
  */
369
- revouchOwner(ownerPublicKey: string): Promise<boolean>;
359
+ revouchOwner(ownerPublicKey: string, sign?: OwnerSign): Promise<boolean>;
370
360
  /**
371
- * Remove a machine completely, signed by this service's owner key: its `CadrePeer` row, its
372
- * `OwnerKey` row when `target.ownerKey` names one, its `DeviceToken` row, and the
373
- * withdrawal of every invitation in `target.inviteKeys`, all in one transaction
374
- * ({@link ControlDatabase.removeMachine}). What stays here is the owner-key precondition,
375
- * checked before any read (see {@link removePeer}).
361
+ * Remove a machine completely, signed by this service's owner signer: its `CadrePeer` row, its
362
+ * `DeviceToken` row, and the withdrawal of every invitation in `target.inviteKeys`, all in one
363
+ * transaction ({@link ControlDatabase.removeMachine}). What stays here is the owner-signer
364
+ * precondition, checked before any read (see {@link removePeer}).
376
365
  */
377
- removeMachine(target: Parameters<ControlDatabase['removeMachine']>[0]): Promise<MachineRemovalWrites>;
366
+ removeMachine(target: Parameters<ControlDatabase['removeMachine']>[0], sign?: OwnerSign): Promise<MachineRemovalResult>;
378
367
  /**
379
368
  * Owner "re-touch" of an existing `CadrePeer` membership row: bump `UpdatedAt` and
380
369
  * re-vouch the row so it is re-emitted as a fresh, broadcasting transaction. This is
@@ -384,16 +373,16 @@ export declare class SeedBootstrapService {
384
373
  *
385
374
  * The stamp read, the voucher signature, the write lock and the membership notify are
386
375
  * {@link ControlDatabase.reauthorizeCadrePeer}'s — including the caveat that it rebinds
387
- * `VouchOwner` to this node's owner key. What stays here is the owner-key precondition.
376
+ * `VouchOwner` to this node's owner key. What stays here is the owner-signer precondition.
388
377
  *
389
378
  * A no-op (no throw, no notify) when the row does not exist.
390
379
  *
391
380
  * @param peerId - the membership row to re-touch.
392
381
  * @param updatedAt - the strictly-increasing freshness stamp to write.
393
- * @throws if no owner private key is configured (a non-owner cannot
382
+ * @throws if no owner signer is configured (a non-owner cannot
394
383
  * re-sign another peer's row) or the control database is not initialized.
395
384
  */
396
- reauthorizePeer(peerId: string, updatedAt: number): Promise<void>;
385
+ reauthorizePeer(peerId: string, updatedAt: number, sign?: OwnerSign): Promise<void>;
397
386
  /**
398
387
  * Owner re-issue of a batch of `Revocation` tombstones: bump each row's
399
388
  * `ReissuedAt` so the storage layer re-broadcasts a tombstone that committed
@@ -404,51 +393,80 @@ export declare class SeedBootstrapService {
404
393
  *
405
394
  * The signatures, the single transaction, and the strictly-monotonic
406
395
  * `reissuedAt` contract are {@link ControlDatabase.reissueRevocations}'s. What
407
- * stays here is the owner-key precondition.
396
+ * stays here is the owner-signer precondition.
408
397
  *
409
398
  * @returns how many tombstones were re-issued (`rows.length` on success).
410
- * @throws if no owner private key is configured or the control database is not
399
+ * @throws if no owner signer is configured or the control database is not
411
400
  * initialized.
412
401
  */
413
- reissueRevocations(rows: readonly RevocationRow[], reissuedAt: number): Promise<number>;
402
+ reissueRevocations(rows: readonly RevocationRow[], reissuedAt: number, sign?: OwnerSign): Promise<number>;
414
403
  /**
415
404
  * Owner filing of the singleton `Revocation` ledger marker, so the table is never a
416
405
  * never-written block that the storage layer re-consults on every read.
417
406
  *
418
407
  * The signature, the insert-if-absent guard and the `'already-open'` mapping are
419
- * {@link ControlDatabase.openRevocationLedger}'s. What stays here is the owner-key
408
+ * {@link ControlDatabase.openRevocationLedger}'s. What stays here is the owner-signer
420
409
  * precondition.
421
410
  *
422
- * @throws if no owner private key is configured or the control database is not
411
+ * @throws if no owner signer is configured or the control database is not
423
412
  * initialized.
424
413
  */
425
- openRevocationLedger(): Promise<RevocationLedgerOpenResult>;
414
+ openRevocationLedger(sign?: OwnerSign): Promise<RevocationLedgerOpenResult>;
426
415
  /**
427
416
  * Owner-signed INSERT of a `CadreInvite` row — an invitation any member machine can redeem
428
417
  * on this owner's behalf ({@link ControlDatabase.insertCadreInvite}). The keypair mint, the
429
418
  * expiry and use-count defaults, and the bundle the holder receives are
430
- * `CadreNode.createCadreInvitation`'s; what stays here is the owner-key precondition and
431
- * the signature, as {@link insertSelfDeviceToken} keeps them for device tokens.
419
+ * `CadreNode.createCadreInvitation`'s; what stays here is the owner-signer precondition and
420
+ * the approval, as {@link insertSelfDeviceToken} keeps them for device tokens.
432
421
  *
433
422
  * @returns the row as stored, which the invitation bundle carries verbatim.
434
- * @throws if no owner private key is configured or the control database is not initialized.
423
+ * @throws if no owner signer is configured or the control database is not initialized.
435
424
  */
436
- insertCadreInvite(invite: Parameters<ControlDatabase['insertCadreInvite']>[0]): Promise<CadreInviteRow>;
425
+ insertCadreInvite(invite: Parameters<ControlDatabase['insertCadreInvite']>[0], sign?: OwnerSign): Promise<CadreInviteRow>;
437
426
  /**
438
427
  * Owner-signed WITHDRAWAL of a `CadreInvite`: the `Revocation` tombstone over its stamp, with
439
428
  * the row kept ({@link ControlDatabase.withdrawCadreInvite}). What stays here is the
440
- * owner-key precondition.
429
+ * owner-signer precondition.
430
+ *
431
+ * @returns `true` when this call filed the tombstone, `false` when the row is absent here or
432
+ * already withdrawn.
433
+ * @throws if no owner signer is configured or the control database is not initialized.
434
+ */
435
+ withdrawCadreInvite(key: string, sign?: OwnerSign): Promise<boolean>;
436
+ /**
437
+ * Owner-signed INSERT of an `OwnerGrant` row — a grant of ownership any member machine can
438
+ * redeem on this owner's behalf ({@link ControlDatabase.insertOwnerGrant}). The keypair mint,
439
+ * the lifetime default and the bundle the holder receives are `CadreNode.createOwnerGrant`'s;
440
+ * what stays here is the owner-signer precondition and the approval, as for
441
+ * {@link insertCadreInvite}. The label is checked before the approval opens, so one the row
442
+ * would refuse never prompts the user.
443
+ *
444
+ * @returns the row as stored, which the grant bundle carries verbatim.
445
+ * @throws if no owner signer is configured, the control database is not initialized, or the label is too long.
446
+ */
447
+ insertOwnerGrant(grant: Parameters<ControlDatabase['insertOwnerGrant']>[0], sign?: OwnerSign): Promise<OwnerGrantRow>;
448
+ /**
449
+ * Owner-signed WITHDRAWAL of an `OwnerGrant`: the `Revocation` tombstone over its stamp, with
450
+ * the row kept ({@link ControlDatabase.withdrawOwnerGrant}). What stays here is the
451
+ * owner-signer precondition.
441
452
  *
442
453
  * @returns `true` when this call filed the tombstone, `false` when the row is absent here or
443
454
  * already withdrawn.
444
- * @throws if no owner private key is configured or the control database is not initialized.
455
+ * @throws if no owner signer is configured or the control database is not initialized.
445
456
  */
446
- withdrawCadreInvite(key: string): Promise<boolean>;
457
+ withdrawOwnerGrant(key: string, sign?: OwnerSign): Promise<boolean>;
447
458
  /**
448
459
  * Create a seed from the current control network state.
449
- * The seed contains peer information and is signed by an owner.
460
+ * The seed contains peer information and is signed by an owner; its `signerKey` is the
461
+ * owner signer's key.
462
+ */
463
+ createSeed(sign?: OwnerSign): Promise<ControlNetworkSeed>;
464
+ /**
465
+ * {@link createSeed}, with `selfRelayAddr` added to this node's own peer entry before the
466
+ * seed is signed: the signature covers every address ({@link seedDigest}), so nothing may
467
+ * be added after it.
450
468
  */
451
- createSeed(): Promise<ControlNetworkSeed>;
469
+ private mintSeed;
452
470
  /**
453
471
  * Apply a seed to populate the peer cache and enable connections.
454
472
  *
@@ -456,9 +474,8 @@ export declare class SeedBootstrapService {
456
474
  * `signerKey` that does NOT come from the seed body: the receiver's
457
475
  * node-local {@link SeedBootstrapConfig.trustedOwners} anchor, optionally
458
476
  * augmented by pinned keys or TOFU via the configured/overriding
459
- * `SeedTrustPolicy`. A forged self-asserting seed — one that merely lists its
460
- * own signer as an owner peer — no longer passes, and neither does one signed
461
- * by a key a stranger genesis-inserted into the replicated `OwnerKey` table.
477
+ * `SeedTrustPolicy`. A forged self-signed seed does not pass, and neither does
478
+ * one signed by a key a stranger genesis-inserted into the replicated `OwnerKey` table.
462
479
  *
463
480
  * A key accepted via a pin/TOFU is persisted into the anchor (the policy says
464
481
  * so via `SeedTrustDecision.anchorAs`), so the next seed from that owner is
@@ -475,22 +492,23 @@ export declare class SeedBootstrapService {
475
492
  /**
476
493
  * The first half of {@link applySeed}: check the signature and the signer's trust, then merge
477
494
  * the seed's peer addresses into the peer store. Every rejection happens here, so this result is
478
- * what the inbound handler acks with; the owner dials that follow cannot change it.
495
+ * what the inbound handler acks with; the dials that follow cannot change it.
479
496
  */
480
497
  private verifyAndMergeSeed;
481
498
  /**
482
- * The second half of {@link applySeed}: dial the seed's owner peers to establish connections.
499
+ * The second half of {@link applySeed}: dial the seed's preferred peers (`SeedPeer.preferred`:
500
+ * the machine that minted it and every always-on machine) to establish connections.
483
501
  *
484
- * Best-effort and COUNTED: an owner that is momentarily down leaves this node seeded but
502
+ * Best-effort and COUNTED: a peer that is momentarily down leaves this node seeded but
485
503
  * unconnected, which the caller can only see if the outcome is reported (see
486
- * `ApplySeedResult.ownerDialsFailed`). Recovery is not this loop's job —
504
+ * `ApplySeedResult.preferredDialsFailed`). Recovery is not this loop's job —
487
505
  * `CadreNode.dialColdStartBootstrap` retries these same addresses on every control-cohort
488
506
  * reconcile pass until the control database has siblings.
489
507
  *
490
508
  * The inbound handler runs this AFTER it has acked and closed the stream
491
- * ({@link handleSeedStream}), because an unreachable owner can take `dialBudget.totalMs`.
509
+ * ({@link handleSeedStream}), because an unreachable peer can take `dialBudget.totalMs`.
492
510
  */
493
- private dialSeedOwners;
511
+ private dialPreferredPeers;
494
512
  /**
495
513
  * Persist a signer that a pin/TOFU accepted into the node-local anchor, so a
496
514
  * later seed from the same owner is anchored without re-supplying the pin
@@ -603,10 +621,10 @@ export declare class SeedBootstrapService {
603
621
  */
604
622
  validateSeedSignature(seed: ControlNetworkSeed, digestB64?: string): boolean;
605
623
  /**
606
- * The peers a seed carries: the control database's own projection
624
+ * The peers a seed `minterPeerId` mints carries: the control database's own projection
607
625
  * ({@link ControlDatabase.querySeedPeers}, shared with the cadre invitation redemption
608
- * reply), which reads through the revocation filter and flags owners from the replicated
609
- * `OwnerKey` table — a dial hint, not a trust decision (the NOTE at `projectSeedPeers`).
626
+ * reply), which reads through the revocation filter and marks the minter and every
627
+ * always-on machine preferred — a dial hint, not a trust decision.
610
628
  */
611
629
  private queryPeers;
612
630
  /**
@@ -617,14 +635,14 @@ export declare class SeedBootstrapService {
617
635
  private registerProtocolHandler;
618
636
  /**
619
637
  * Read one inbound seed frame, verify and merge it, write the ack and close the
620
- * stream, then dial the seed's owners.
638
+ * stream, then dial the seed's preferred peers.
621
639
  *
622
- * The ack and the close come BEFORE the owner dials: an unreachable owner can hold
640
+ * The ack and the close come BEFORE those dials: an unreachable peer can hold
623
641
  * a dial for `dialBudget.totalMs` (86 s at the default declared link), which is not
624
642
  * link time and does not belong inside the sender's delivery deadline. The close is
625
643
  * what releases the sender, which reads the ack to end-of-stream. The stream stays
626
644
  * counted in {@link activeStreams} through the dials, so {@link maxConcurrentSeeds}
627
- * still bounds concurrent owner-dial phases.
645
+ * still bounds concurrent dial phases.
628
646
  *
629
647
  * Hardened against a buggy/compromised own-cadre node: a concurrency cap (over
630
648
  * {@link maxConcurrentSeeds}, reply without applying), a read timeout (a peer
@@ -664,10 +682,12 @@ export declare class SeedBootstrapService {
664
682
  * does not listen. Call `CadreNode.reconcileControlCohort()` after delivering
665
683
  * the seed to dial at once.
666
684
  *
685
+ * The vouch and the seed are one owner approval.
686
+ *
667
687
  * @param options - Drone peer info from provider API
668
688
  * @returns Seed and encoded seed for drone initialization
669
689
  */
670
- addDrone(options: AddDroneOptions): Promise<DroneInitResult>;
690
+ addDrone(options: AddDroneOptions, sign?: OwnerSign): Promise<DroneInitResult>;
671
691
  /**
672
692
  * Add a phone to the cadre with relay support.
673
693
  *
@@ -675,8 +695,10 @@ export declare class SeedBootstrapService {
675
695
  * 1. Authorizes the new phone peer
676
696
  * 2. Creates a seed with relay addresses for dialing
677
697
  *
698
+ * The vouch and the seed are one owner approval.
699
+ *
678
700
  * @param phonePeerId - Peer ID of the new phone
679
701
  * @returns Seed with relay addresses for out-of-band delivery
680
702
  */
681
- addPhoneWithRelay(phonePeerId: string): Promise<DroneInitResult>;
703
+ addPhoneWithRelay(phonePeerId: string, sign?: OwnerSign): Promise<DroneInitResult>;
682
704
  }