@serfab/cadre-core 0.8.1 → 0.9.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 (93) hide show
  1. package/README.md +2 -2
  2. package/dist/cadre-node.d.ts +101 -61
  3. package/dist/cadre-node.js +165 -124
  4. package/dist/cadre-node.js.map +1 -1
  5. package/dist/control-cohort.d.ts +15 -15
  6. package/dist/control-cohort.js +14 -14
  7. package/dist/control-cohort.js.map +1 -1
  8. package/dist/control-database.d.ts +51 -34
  9. package/dist/control-database.js +72 -56
  10. package/dist/control-database.js.map +1 -1
  11. package/dist/control-formation-recorder.d.ts +2 -2
  12. package/dist/control-formation-recorder.js +2 -2
  13. package/dist/control-schema.d.ts +1 -1
  14. package/dist/control-schema.js +57 -33
  15. package/dist/control-schema.js.map +1 -1
  16. package/dist/device-token.d.ts +1 -1
  17. package/dist/device-token.js +1 -1
  18. package/dist/{authority-key.d.ts → ed25519-key.d.ts} +11 -10
  19. package/dist/{authority-key.js → ed25519-key.js} +9 -10
  20. package/dist/ed25519-key.js.map +1 -0
  21. package/dist/index.d.ts +3 -3
  22. package/dist/index.js +8 -8
  23. package/dist/index.js.map +1 -1
  24. package/dist/key-store.d.ts +1 -1
  25. package/dist/key-store.js +1 -1
  26. package/dist/peer-authorization.d.ts +28 -6
  27. package/dist/peer-authorization.js +33 -7
  28. package/dist/peer-authorization.js.map +1 -1
  29. package/dist/peer-record.d.ts +3 -3
  30. package/dist/peer-record.js +3 -3
  31. package/dist/push-node.d.ts +38 -0
  32. package/dist/push-node.js +48 -0
  33. package/dist/push-node.js.map +1 -0
  34. package/dist/push-notifier-apns.d.ts +10 -2
  35. package/dist/push-notifier-apns.js +14 -10
  36. package/dist/push-notifier-apns.js.map +1 -1
  37. package/dist/push-notifier-fcm.d.ts +7 -1
  38. package/dist/push-notifier-fcm.js +9 -10
  39. package/dist/push-notifier-fcm.js.map +1 -1
  40. package/dist/push-notifier.d.ts +16 -35
  41. package/dist/push-notifier.js +16 -44
  42. package/dist/push-notifier.js.map +1 -1
  43. package/dist/seed-bootstrap.d.ts +70 -51
  44. package/dist/seed-bootstrap.js +168 -106
  45. package/dist/seed-bootstrap.js.map +1 -1
  46. package/dist/seed-trust-policy.d.ts +11 -11
  47. package/dist/seed-trust-policy.js +11 -11
  48. package/dist/seed-trust-policy.js.map +1 -1
  49. package/dist/strand-database.d.ts +5 -5
  50. package/dist/strand-database.js +4 -4
  51. package/dist/strand-database.js.map +1 -1
  52. package/dist/strand-formation-protocol.d.ts +1 -1
  53. package/dist/strand-formation-protocol.js +1 -1
  54. package/dist/strand-instance-manager.d.ts +1 -1
  55. package/dist/strand-instance-manager.js +1 -1
  56. package/dist/strand-instance-manager.js.map +1 -1
  57. package/dist/strand-member-key.d.ts +6 -6
  58. package/dist/strand-member-key.js +6 -6
  59. package/dist/strand-member-key.js.map +1 -1
  60. package/dist/strand-member-registry.d.ts +7 -7
  61. package/dist/strand-member-registry.js +5 -5
  62. package/dist/strand-member-registry.js.map +1 -1
  63. package/dist/strand-membership-writer.d.ts +84 -84
  64. package/dist/strand-membership-writer.js +90 -90
  65. package/dist/strand-membership-writer.js.map +1 -1
  66. package/dist/types.d.ts +52 -32
  67. package/dist/types.js.map +1 -1
  68. package/package.json +7 -3
  69. package/src/cadre-node.ts +170 -126
  70. package/src/control-cohort.ts +17 -17
  71. package/src/control-database.ts +76 -59
  72. package/src/control-formation-recorder.ts +2 -2
  73. package/src/control-schema.ts +57 -33
  74. package/src/device-token.ts +1 -1
  75. package/src/{authority-key.ts → ed25519-key.ts} +12 -11
  76. package/src/index.ts +21 -19
  77. package/src/key-store.ts +1 -1
  78. package/src/peer-authorization.ts +35 -7
  79. package/src/peer-record.ts +3 -3
  80. package/src/push-node.ts +64 -0
  81. package/src/push-notifier-apns.ts +16 -12
  82. package/src/push-notifier-fcm.ts +9 -10
  83. package/src/push-notifier.ts +16 -57
  84. package/src/seed-bootstrap.ts +179 -115
  85. package/src/seed-trust-policy.ts +18 -18
  86. package/src/strand-database.ts +6 -6
  87. package/src/strand-formation-protocol.ts +1 -1
  88. package/src/strand-instance-manager.ts +2 -2
  89. package/src/strand-member-key.ts +7 -7
  90. package/src/strand-member-registry.ts +10 -10
  91. package/src/strand-membership-writer.ts +127 -127
  92. package/src/types.ts +52 -32
  93. package/dist/authority-key.js.map +0 -1
package/src/cadre-node.ts CHANGED
@@ -34,7 +34,7 @@ import type {
34
34
  } from './types.js';
35
35
  import { DEFAULT_CHECKIN_WINDOW_MS } from './types.js';
36
36
  import { sign } from '@optimystic/quereus-plugin-crypto';
37
- import { authorityKeyFromLibp2p, type AuthorityKeyPair } from './authority-key.js';
37
+ import { ed25519KeyPairFromLibp2p, type Ed25519KeyPair } from './ed25519-key.js';
38
38
  import { DEFAULT_IDENTITY_KEY_ID } from './key-store.js';
39
39
  import { ed25519PublicKeyB64FromPeerId } from './seed-bootstrap.js';
40
40
  import {
@@ -109,7 +109,7 @@ export class CadreNode implements SAppIdLookup {
109
109
  * The resolved node identity key, set once by {@link resolveIdentityKey}
110
110
  * during {@link start} (from `config.keyStore`, else `config.privateKey`).
111
111
  * Left undefined when neither is configured — libp2p then generates an
112
- * ephemeral key internally and there is no exposed authority key. Every
112
+ * ephemeral key internally and there is no exposed owner key. Every
113
113
  * identity-dependent path (control node creation, self-record signing, strand
114
114
  * launch) reads this resolved field, never `config.privateKey` directly.
115
115
  */
@@ -132,9 +132,9 @@ export class CadreNode implements SAppIdLookup {
132
132
  private strandAddrService: StrandAddrService | null = null;
133
133
  /**
134
134
  * Server-side push-wake fan-out. Constructed by {@link start} only when
135
- * `config.push` (FCM/APNs credentials) is present — without it the node behaves
136
- * exactly as before (no notifier, no fan-out). Owns who/when to wake
137
- * hibernating mobile peers on strand activity.
135
+ * `config.push` (an injected `PushNotifier` + policy) is present — without it
136
+ * the node behaves exactly as before (no notifier, no fan-out). Owns who/when
137
+ * to wake hibernating mobile peers on strand activity.
138
138
  */
139
139
  private pushFanoutService: PushFanoutService | null = null;
140
140
  /** Backing field for the {@link running} / {@link isRunning} getters. */
@@ -187,7 +187,7 @@ export class CadreNode implements SAppIdLookup {
187
187
  private reconcileControlCohortInFlight: Promise<void> | null = null;
188
188
  /**
189
189
  * Single-flight guard for {@link registerSelf}. Concurrent callers (the explicit
190
- * CLI `--authority` publish, the 1s startup timer, the TTL heartbeat, and the
190
+ * CLI `--owner` publish, the 1s startup timer, the TTL heartbeat, and the
191
191
  * address-change listener) share one in-flight publish so two of them can never
192
192
  * both read "no row yet" and race a duplicate INSERT (a `CadrePeer` PK conflict).
193
193
  */
@@ -195,7 +195,7 @@ export class CadreNode implements SAppIdLookup {
195
195
 
196
196
  // ── Write-while-alone re-replication (control-write-ensure-replicated) ──────
197
197
  /**
198
- * Re-replication queue for authority control writes that committed while this
198
+ * Re-replication queue for owner control writes that committed while this
199
199
  * node was alone — `controlNode.getConnections().length === 0` at write time
200
200
  * means the Optimystic commit was local-only (the block's cluster was ≤1) and
201
201
  * never broadcast. Maps the affected subject peerId → the write to re-issue once
@@ -209,7 +209,7 @@ export class CadreNode implements SAppIdLookup {
209
209
  /** This node's own `DeviceToken` self-write committed local-only (re-touched on growth). */
210
210
  private pendingSelfDeviceWrite = false;
211
211
  /**
212
- * Guards the one-shot, first-cohort-growth reconstruction: an authority re-touches
212
+ * Guards the one-shot, first-cohort-growth reconstruction: an owner re-touches
213
213
  * every membership row it may have authored that could be unreplicated (covering
214
214
  * writes made before this process started, which the in-memory queue cannot know).
215
215
  * Set true after the first drain so later passes only drain the in-memory queue.
@@ -440,10 +440,11 @@ export class CadreNode implements SAppIdLookup {
440
440
  this.hibernationManager.start();
441
441
 
442
442
  // Register the control-network push-wake receiver: a same-cadre peer can
443
- // signal us to bring a hibernating strand online. Gated on CadrePeer
444
- // membership; the wake routes through the same path as a local wake.
443
+ // signal us to bring a hibernating strand online. Gated on AUTHORIZED
444
+ // membership (not the addressable surface); the wake routes through the same
445
+ // path as a local wake.
445
446
  this.strandWakeService = new StrandWakeService({
446
- isMember: (peerId) => this.isMember(peerId),
447
+ isMember: (peerId) => this.isAuthorizedMember(peerId),
447
448
  getStrand: (strandId) => this.strandManager.getInstance(strandId),
448
449
  wake: (strandId) => this.wakeStrand(strandId),
449
450
  });
@@ -452,33 +453,17 @@ export class CadreNode implements SAppIdLookup {
452
453
  // Register the control-network strand-address responder: a same-cadre peer
453
454
  // resolving a strand's bootstrap seed asks us for our live strand-network
454
455
  // multiaddrs (its CadrePeer row only knows our *control* address). Gated on
455
- // CadrePeer membership; answers only for strands we are actively meshing.
456
+ // AUTHORIZED membership; answers only for strands we are actively meshing.
456
457
  this.strandAddrService = new StrandAddrService({
457
- isMember: (peerId) => this.isMember(peerId),
458
+ isMember: (peerId) => this.isAuthorizedMember(peerId),
458
459
  getStrandMultiaddrs: (strandId) => this.getStrandMultiaddrs(strandId),
459
460
  });
460
461
  this.strandAddrService.initialize(this.controlNode);
461
462
 
462
- // Server-side push-wake fan-out: only when push credentials are configured.
463
- // The PushNotifier reaches for node:http2/node:crypto, so it is loaded via a
464
- // guarded DYNAMIC import — a cross-platform (RN/browser) node that never sets
465
- // config.push never pulls those modules into its static graph. The fan-out
466
- // service itself is import-clean (PushNotifier type only) and statically imported.
463
+ // Server-side push-wake fan-out: only when push is configured.
467
464
  if (this.config.push) {
468
- const { createPushNotifier } = await import('./push-notifier.js');
469
- const notifier = createPushNotifier(this.config.push);
470
- this.pushFanoutService = new PushFanoutService({
471
- listMembers: () => this.listMembers(),
472
- getStrand: (strandId) => this.strandManager.getInstance(strandId),
473
- selfPeerId: () => this.controlNode?.peerId.toString(),
474
- pushWake: (peerId, strandId, reason) => this.pushWake(peerId, strandId, reason),
475
- resolveDeviceToken: (peerId) => this.resolveDeviceToken(peerId),
476
- expireDeviceToken: (peerId) => this.expireDeviceToken(peerId),
477
- notifier,
478
- cooldownMs: this.config.push.cooldownMs,
479
- debounceMs: this.config.push.debounceMs,
480
- });
481
- log('Push-wake fan-out enabled (FCM=%s, APNs=%s)', !!this.config.push.fcm, !!this.config.push.apns);
465
+ this.pushFanoutService = this.buildPushFanout(this.config.push);
466
+ log('Push-wake fan-out enabled (injected notifier)');
482
467
  }
483
468
 
484
469
  this._running = true;
@@ -501,6 +486,32 @@ export class CadreNode implements SAppIdLookup {
501
486
  }
502
487
  }
503
488
 
489
+ /**
490
+ * Build the server-side push-wake fan-out over an injected {@link PushNotifier}.
491
+ *
492
+ * The notifier reaches for `node:http2`/`node:crypto`, so the cross-platform
493
+ * core never constructs it — the Node host builds it from
494
+ * `@serfab/cadre-core/push-node` and passes the instance in
495
+ * `CadreNodeConfig.push`. This node owns that instance's lifecycle from here:
496
+ * {@link cleanup} closes the fan-out, which closes the notifier (freeing the
497
+ * APNs HTTP/2 session). Every other primitive the fan-out needs (member
498
+ * enumeration, participation, direct dial, token resolve/expire) is an
499
+ * import-clean closure over this node.
500
+ */
501
+ private buildPushFanout(push: NonNullable<CadreNodeConfig['push']>): PushFanoutService {
502
+ return new PushFanoutService({
503
+ listMembers: () => this.listMembers(),
504
+ getStrand: (strandId) => this.strandManager.getInstance(strandId),
505
+ selfPeerId: () => this.controlNode?.peerId.toString(),
506
+ pushWake: (peerId, strandId, reason) => this.pushWake(peerId, strandId, reason),
507
+ resolveDeviceToken: (peerId) => this.resolveDeviceToken(peerId),
508
+ expireDeviceToken: (peerId) => this.expireDeviceToken(peerId),
509
+ notifier: push.notifier,
510
+ cooldownMs: push.cooldownMs,
511
+ debounceMs: push.debounceMs,
512
+ });
513
+ }
514
+
504
515
  /**
505
516
  * Stop the cadre node
506
517
  */
@@ -657,7 +668,7 @@ export class CadreNode implements SAppIdLookup {
657
668
  /**
658
669
  * Schedule the node's initial self-record publish + ongoing refresh shortly
659
670
  * after start (non-blocking). {@link registerSelf} is idempotent and safely
660
- * no-ops when it cannot yet sign/insert (e.g. authority key not installed),
671
+ * no-ops when it cannot yet sign/insert (e.g. owner key not installed),
661
672
  * so the timer is harmless even when registration only becomes possible later.
662
673
  */
663
674
  private scheduleSelfRegistration(): void {
@@ -690,10 +701,10 @@ export class CadreNode implements SAppIdLookup {
690
701
  * (signaling/`p2p-circuit` first), signed with the ed25519 key behind its
691
702
  * PeerId (the resolved node identity from `keyStore`/`config.privateKey`).
692
703
  * - If the row already exists: a self-signed UPDATE bumping `UpdatedAt`.
693
- * - If not, and the node is its own authority: an authority-signed INSERT that
704
+ * - If not, and the node is its own owner: an owner-signed INSERT that
694
705
  * also carries the self-signature.
695
- * - Otherwise: logs and returns (a non-authority node with no row yet must
696
- * wait for an authority to insert it; it can then self-refresh).
706
+ * - Otherwise: logs and returns (a non-owner node with no row yet must
707
+ * wait for an owner to insert it; it can then self-refresh).
697
708
  *
698
709
  * Safe to call repeatedly (heartbeat / address-change driven); each successful
699
710
  * publish strictly increases `UpdatedAt`. Concurrent calls are collapsed into a
@@ -749,14 +760,14 @@ export class CadreNode implements SAppIdLookup {
749
760
  return 'refreshed';
750
761
  }
751
762
  if (this.seedBootstrapService) {
752
- // First-time row: requires an authority signature (the node is its own
753
- // authority). insertSelfPeerRecord throws if no authority key is present.
763
+ // First-time row: requires an owner signature (the node is its own
764
+ // owner). insertSelfPeerRecord throws if no owner key is present.
754
765
  await this.seedBootstrapService.insertSelfPeerRecord(record);
755
766
  if (this.committedAlone()) this.pendingSelfPeerWrite = true;
756
- log('registerSelf: inserted own CadrePeer record (authority-signed, updatedAt=%d, %d addrs)', updatedAt, addrs.length);
767
+ log('registerSelf: inserted own CadrePeer record (owner-signed, updatedAt=%d, %d addrs)', updatedAt, addrs.length);
757
768
  return 'inserted';
758
769
  }
759
- log('registerSelf: not yet a CadrePeer member and no authority service to self-insert; skipping (an authority must add this peer first)');
770
+ log('registerSelf: not yet a CadrePeer member and no owner service to self-insert; skipping (an owner must add this peer first)');
760
771
  return 'skipped';
761
772
  }
762
773
 
@@ -774,43 +785,43 @@ export class CadreNode implements SAppIdLookup {
774
785
  return null;
775
786
  }
776
787
  try {
777
- const { privateKeyB64, publicKeyB64 } = authorityKeyFromLibp2p(this.identityKey);
788
+ const { privateKeyB64, publicKeyB64 } = ed25519KeyPairFromLibp2p(this.identityKey);
778
789
  if (publicKeyB64 === ed25519PublicKeyB64FromPeerId(peerId)) {
779
790
  return { privateKeyB64, publicKeyB64 };
780
791
  }
781
792
  log('getSelfSigningKey: resolved identity key does not match control node peerId; cannot self-sign');
782
793
  } catch (error) {
783
- // Logs the error shape only; authorityKeyFromLibp2p never embeds key material.
794
+ // Logs the error shape only; ed25519KeyPairFromLibp2p never embeds key material.
784
795
  log('getSelfSigningKey: failed to derive ed25519 key from identity key: %o', error);
785
796
  }
786
797
  return null;
787
798
  }
788
799
 
789
800
  /**
790
- * The authority keypair (base64url Ed25519) derived from this node's resolved
791
- * identity key. In the single-key reference model the authority signing key is
792
- * *derived from* the node identity (see {@link authorityKeyFromLibp2p}), so the
801
+ * The owner keypair (base64url Ed25519) derived from this node's resolved
802
+ * identity key. In the single-key reference model the owner signing key is
803
+ * *derived from* the node identity (see {@link ed25519KeyPairFromLibp2p}), so the
793
804
  * same key material protected in a secure enclave backs both.
794
805
  *
795
- * Exposed so the hosting app retains control of authority genesis: cadre-core
806
+ * Exposed so the hosting app retains control of owner genesis: cadre-core
796
807
  * resolves + protects the identity, then the app sources this pair to drive
797
- * `ensureAuthorityKey(pub)` + `initializeSeedBootstrap(priv)` itself — cadre-core
798
- * never silently runs genesis. A future separate-authority slot would return a
808
+ * `ensureOwnerKey(pub)` + `initializeSeedBootstrap(priv)` itself — cadre-core
809
+ * never silently runs genesis. A future separate-owner slot would return a
799
810
  * distinct key here instead of the identity-derived one.
800
811
  *
801
- * @returns The base64url seed/public-key authority pair.
812
+ * @returns The base64url seed/public-key owner pair.
802
813
  * @throws If called before {@link start} has resolved the identity, or when the
803
814
  * node runs on an ephemeral libp2p key (no `keyStore`/`privateKey` configured),
804
815
  * since that key is internal to libp2p and not exposed.
805
816
  */
806
- getIdentityAuthorityKey(): AuthorityKeyPair {
817
+ getIdentityOwnerKey(): Ed25519KeyPair {
807
818
  if (!this.identityKey) {
808
819
  throw new Error(
809
- 'getIdentityAuthorityKey: node identity not resolved — call start() first, and ' +
810
- 'configure `keyStore` or `privateKey` (an ephemeral libp2p identity exposes no authority key)'
820
+ 'getIdentityOwnerKey: node identity not resolved — call start() first, and ' +
821
+ 'configure `keyStore` or `privateKey` (an ephemeral libp2p identity exposes no owner key)'
811
822
  );
812
823
  }
813
- return authorityKeyFromLibp2p(this.identityKey);
824
+ return ed25519KeyPairFromLibp2p(this.identityKey);
814
825
  }
815
826
 
816
827
  /**
@@ -904,7 +915,7 @@ export class CadreNode implements SAppIdLookup {
904
915
 
905
916
  // Reset the write-while-alone re-replication state so a stop()→start() cycle
906
917
  // re-arms the growth edge and re-runs the first-growth reconstruction. Any
907
- // queued local-only writes are moot once the node is torn down; authority
918
+ // queued local-only writes are moot once the node is torn down; owner
908
919
  // inserts are re-covered by reconstruction on the next start, and the
909
920
  // (already-loudly-logged) delete gap is tracked for follow-up.
910
921
  this.hasControlConnection = false;
@@ -1062,18 +1073,18 @@ export class CadreNode implements SAppIdLookup {
1062
1073
  return;
1063
1074
  }
1064
1075
 
1065
- // 2. Classify backbone (authority) members and select a bounded dial set.
1066
- const authorityKeys = await this.controlDatabase.getAuthorityKeys();
1076
+ // 2. Classify backbone (owner) members and select a bounded dial set.
1077
+ const ownerKeys = await this.controlDatabase.getOwnerKeys();
1067
1078
  if (!this._running || !this.controlNode) {
1068
1079
  return;
1069
1080
  }
1070
1081
  const targetDegree = this.config.network?.controlCohort?.targetDegree
1071
1082
  ?? DEFAULT_CONTROL_COHORT_TARGET_DEGREE;
1072
- const { dials, cappedNonAuthority } = selectControlCohortDials(siblings, authorityKeys, targetDegree);
1073
- if (cappedNonAuthority > 0) {
1083
+ const { dials, cappedNonOwner } = selectControlCohortDials(siblings, ownerKeys, targetDegree);
1084
+ if (cappedNonOwner > 0) {
1074
1085
  // Don't silently bound coverage — surface what the out-degree cap dropped.
1075
- log('reconcileControlCohort: capped %d non-authority sibling(s) at targetDegree=%d',
1076
- cappedNonAuthority, targetDegree);
1086
+ log('reconcileControlCohort: capped %d non-owner sibling(s) at targetDegree=%d',
1087
+ cappedNonOwner, targetDegree);
1077
1088
  }
1078
1089
 
1079
1090
  // 3. Skip already-connected peers (no re-dial / churn for live connections).
@@ -1102,7 +1113,7 @@ export class CadreNode implements SAppIdLookup {
1102
1113
  *
1103
1114
  * A per-peer failure (NAT, offline, relay down, connection-gater denial) is
1104
1115
  * logged and swallowed so one unreachable sibling never aborts the pass —
1105
- * exactly like {@link SeedBootstrapService.applySeed}'s authority-dial loop. A
1116
+ * exactly like {@link SeedBootstrapService.applySeed}'s owner-dial loop. A
1106
1117
  * failed dial is simply retried on the next pass.
1107
1118
  */
1108
1119
  private async dialControlSibling(sibling: CohortPeerRow): Promise<boolean> {
@@ -1166,7 +1177,7 @@ export class CadreNode implements SAppIdLookup {
1166
1177
  // Write-while-alone re-replication (control-write-ensure-replicated)
1167
1178
  //
1168
1179
  // Optimystic's coordinator commits a control write WITHOUT broadcasting when the
1169
- // block's cluster has ≤1 member. An authority that authorizes/removes a peer or
1180
+ // block's cluster has ≤1 member. An owner that authorizes/removes a peer or
1170
1181
  // (re)publishes its own record while no sibling is connected therefore writes a
1171
1182
  // row that exists ONLY in its local control DB — a sibling that connects later
1172
1183
  // never observes it (pull-on-read routes to the block's cluster, which never
@@ -1193,7 +1204,7 @@ export class CadreNode implements SAppIdLookup {
1193
1204
  }
1194
1205
 
1195
1206
  /**
1196
- * Record (or clear) a just-committed authority membership write in the
1207
+ * Record (or clear) a just-committed owner membership write in the
1197
1208
  * write-while-alone re-replication queue. If the control node had no connections
1198
1209
  * at commit, the Optimystic write was local-only and must be re-issued once the
1199
1210
  * cohort grows; otherwise the write replicated and any stale queue entry for this
@@ -1267,7 +1278,7 @@ export class CadreNode implements SAppIdLookup {
1267
1278
  this.pendingSelfDeviceWrite = false;
1268
1279
  }
1269
1280
 
1270
- // 2. First growth: an authority reconstructs membership rows it may have
1281
+ // 2. First growth: an owner reconstructs membership rows it may have
1271
1282
  // authored that never replicated (covers writes from before this process
1272
1283
  // started). Rows already tracked in the in-memory queue are handled by
1273
1284
  // step 3 — skipped here to avoid a double re-touch.
@@ -1276,7 +1287,7 @@ export class CadreNode implements SAppIdLookup {
1276
1287
  await this.reconstructAuthoredMembership();
1277
1288
  }
1278
1289
 
1279
- // 3. In-session pending authority membership writes (authorize / remove).
1290
+ // 3. In-session pending owner membership writes (authorize / remove).
1280
1291
  await this.drainPendingPeerWrites();
1281
1292
  }
1282
1293
 
@@ -1305,16 +1316,16 @@ export class CadreNode implements SAppIdLookup {
1305
1316
 
1306
1317
  /**
1307
1318
  * One-shot, first-cohort-growth reconstruction of the write-while-alone queue for
1308
- * an AUTHORITY node: re-touch every membership row that may be an unreplicated
1309
- * authority insert. A row is a candidate iff it is not self (handled by
1319
+ * an OWNER node: re-touch every membership row that may be an unreplicated
1320
+ * owner insert. A row is a candidate iff it is not self (handled by
1310
1321
  * {@link registerSelf}), is not already tracked in the in-memory queue (handled by
1311
1322
  * {@link drainPendingPeerWrites}), and carries no self-`Sig` yet (an
1312
- * authority-authored row the peer has not self-published — the only kind safe to
1323
+ * owner-authored row the peer has not self-published — the only kind safe to
1313
1324
  * bump without invalidating a self-signature). O(rows) on the small control
1314
- * tables; safe to over-apply (a monotonic authority bump on an already-replicated
1325
+ * tables; safe to over-apply (a monotonic owner bump on an already-replicated
1315
1326
  * row is a no-op-equivalent).
1316
1327
  *
1317
- * A node with no authority private key skips this entirely — it cannot re-sign
1328
+ * A node with no owner private key skips this entirely — it cannot re-sign
1318
1329
  * rows for other peers, and rows it merely holds are not its to re-issue. DELETEs
1319
1330
  * cannot be reconstructed here (a removed row leaves no local trace and the schema
1320
1331
  * carries no tombstone) — see the delete-path note in {@link drainPendingPeerWrites}.
@@ -1349,17 +1360,17 @@ export class CadreNode implements SAppIdLookup {
1349
1360
  }
1350
1361
  }
1351
1362
  if (touched > 0) {
1352
- log('drain: reconstructed %d authority-authored membership row(s) on first cohort growth', touched);
1363
+ log('drain: reconstructed %d owner-authored membership row(s) on first cohort growth', touched);
1353
1364
  }
1354
1365
  }
1355
1366
 
1356
1367
  /**
1357
- * Drain the in-session write-while-alone queue: re-issue each pending authority
1368
+ * Drain the in-session write-while-alone queue: re-issue each pending owner
1358
1369
  * membership write now that the cohort can broadcast it. Sequential (no fan-out)
1359
1370
  * to avoid a thundering re-touch; an entry is cleared only on success, so a
1360
1371
  * failure (or a still-alone re-commit) leaves it queued for the next growth.
1361
1372
  *
1362
- * - `authorize`: re-issue as an idempotent monotonic authority UPDATE
1373
+ * - `authorize`: re-issue as an idempotent monotonic owner UPDATE
1363
1374
  * ({@link reissuePeerAuthorize}). Skipped (and cleared) if the row vanished
1364
1375
  * (raced a delete) or now carries a self-`Sig` (the peer self-published and owns
1365
1376
  * its republish).
@@ -1375,7 +1386,7 @@ export class CadreNode implements SAppIdLookup {
1375
1386
  return;
1376
1387
  }
1377
1388
  if (!this.seedBootstrapService?.canAuthorize()) {
1378
- // A non-authority node cannot have authored these writes; drop any stray entries.
1389
+ // A non-owner node cannot have authored these writes; drop any stray entries.
1379
1390
  this.pendingPeerWrites.clear();
1380
1391
  return;
1381
1392
  }
@@ -1387,7 +1398,7 @@ export class CadreNode implements SAppIdLookup {
1387
1398
  if (kind === 'authorize') {
1388
1399
  const record = await this.controlDatabase.queryPeerRecord(peerId);
1389
1400
  if (!record || record.sig) {
1390
- // Removed since, or peer self-published — nothing for the authority to re-issue.
1401
+ // Removed since, or peer self-published — nothing for the owner to re-issue.
1391
1402
  this.pendingPeerWrites.delete(peerId);
1392
1403
  continue;
1393
1404
  }
@@ -1405,9 +1416,9 @@ export class CadreNode implements SAppIdLookup {
1405
1416
  }
1406
1417
 
1407
1418
  /**
1408
- * Re-issue an authority membership row as a monotonic authority UPDATE: bump
1419
+ * Re-issue an owner membership row as a monotonic owner UPDATE: bump
1409
1420
  * `UpdatedAt` strictly above the stored value (and the wall clock) and re-sign via
1410
- * the authority branch of `CadrePeer.AuthorizedUpdate`. The row already exists
1421
+ * the owner branch of `CadrePeer.AuthorizedUpdate`. The row already exists
1411
1422
  * locally (it committed there, just local-only), so this is an UPDATE, not the
1412
1423
  * original INSERT.
1413
1424
  */
@@ -1526,11 +1537,11 @@ export class CadreNode implements SAppIdLookup {
1526
1537
  * for any member — the `AuthorizedUpdate` self-branch verifies the new `Sig`
1527
1538
  * against the bound `CadrePeer.PublicKey`). Platform/Token may change here
1528
1539
  * (rotation / platform switch / reinstall are all normal self-updates).
1529
- * - If not, and the node holds an authority service: an authority-signed INSERT
1540
+ * - If not, and the node holds an owner service: an owner-signed INSERT
1530
1541
  * that also carries the self-signature.
1531
1542
  * - Otherwise: throws. Like `CadrePeer`, the first `DeviceToken` row requires an
1532
- * authority signature; a non-authority peer (e.g. a phone) must have its row
1533
- * seeded by an authority — typically the server it enrolled with — before it
1543
+ * owner signature; a non-owner peer (e.g. a phone) must have its row
1544
+ * seeded by an owner — typically the server it enrolled with — before it
1534
1545
  * can self-refresh. (Establishing that phone→server registration handshake is
1535
1546
  * the downstream "RN registration" ticket; this node only owns the cadre-core
1536
1547
  * write path.)
@@ -1541,7 +1552,7 @@ export class CadreNode implements SAppIdLookup {
1541
1552
  * @param platform - `'fcm'` (Android/Firebase) or `'apns'` (Apple).
1542
1553
  * @param token - the opaque platform device/registration token.
1543
1554
  * @throws if the node is not started, exposes no self-signing key, or has no
1544
- * existing row and no authority service to self-insert.
1555
+ * existing row and no owner service to self-insert.
1545
1556
  */
1546
1557
  async registerDeviceToken(platform: PushPlatform, token: string): Promise<void> {
1547
1558
  if (!this._running || !this.controlNode || !this.controlDatabase) {
@@ -1573,12 +1584,12 @@ export class CadreNode implements SAppIdLookup {
1573
1584
  if (this.seedBootstrapService) {
1574
1585
  await this.seedBootstrapService.insertSelfDeviceToken(record);
1575
1586
  if (this.committedAlone()) this.pendingSelfDeviceWrite = true;
1576
- log('registerDeviceToken: inserted own DeviceToken (authority-signed, platform=%s, updatedAt=%d)', platform, updatedAt);
1587
+ log('registerDeviceToken: inserted own DeviceToken (owner-signed, platform=%s, updatedAt=%d)', platform, updatedAt);
1577
1588
  return;
1578
1589
  }
1579
1590
  throw new Error(
1580
1591
  `Cannot register device token for ${peerId}: no existing row to self-update and no ` +
1581
- 'authority service to self-insert. An authority must seed this peer\'s DeviceToken ' +
1592
+ 'owner service to self-insert. An owner must seed this peer\'s DeviceToken ' +
1582
1593
  'row first (mirrors CadrePeer enrollment).'
1583
1594
  );
1584
1595
  }
@@ -1649,11 +1660,11 @@ export class CadreNode implements SAppIdLookup {
1649
1660
  /**
1650
1661
  * Delete this node's own `DeviceToken` row (logout / token invalidation). No-op
1651
1662
  * when no row exists. Like {@link registerDeviceToken}'s first insert, the delete
1652
- * is gated on an authority signature (`DeviceToken.AuthorizedInsert` covers insert
1653
- * AND delete), so it requires this node's authority service; a non-authority peer
1654
- * must route the clear through its authority (downstream RN registration path).
1663
+ * is gated on an owner signature (`DeviceToken.AuthorizedInsert` covers insert
1664
+ * AND delete), so it requires this node's owner service; a non-owner peer
1665
+ * must route the clear through its owner (downstream RN registration path).
1655
1666
  *
1656
- * @throws if the node is not started, or a row exists but no authority service is
1667
+ * @throws if the node is not started, or a row exists but no owner service is
1657
1668
  * available to sign the delete.
1658
1669
  */
1659
1670
  async clearDeviceToken(): Promise<void> {
@@ -1668,8 +1679,8 @@ export class CadreNode implements SAppIdLookup {
1668
1679
  }
1669
1680
  if (!this.seedBootstrapService) {
1670
1681
  throw new Error(
1671
- `Cannot clear device token for ${peerId}: delete requires an authority signature ` +
1672
- 'and no authority service is initialized.'
1682
+ `Cannot clear device token for ${peerId}: delete requires an owner signature ` +
1683
+ 'and no owner service is initialized.'
1673
1684
  );
1674
1685
  }
1675
1686
  await this.seedBootstrapService.deleteDeviceToken(peerId);
@@ -1681,10 +1692,10 @@ export class CadreNode implements SAppIdLookup {
1681
1692
  * unregistered during a push-wake fan-out. Unlike {@link clearDeviceToken}
1682
1693
  * (self-only — it hardcodes the local peerId), this takes an arbitrary peerId.
1683
1694
  *
1684
- * - When this node holds an authority seed service, it deletes the row
1685
- * (`deleteDeviceToken` is authority-gated and accepts any peerId), so the peer
1695
+ * - When this node holds an owner seed service, it deletes the row
1696
+ * (`deleteDeviceToken` is owner-gated and accepts any peerId), so the peer
1686
1697
  * is not retried until it re-registers.
1687
- * - When this node is NOT an authority it cannot delete the row, so it only logs
1698
+ * - When this node is NOT an owner it cannot delete the row, so it only logs
1688
1699
  * that a re-registration is needed. The fan-out's own in-memory dead-token set
1689
1700
  * is what actually stops re-pushing to the dead token this process — see
1690
1701
  * {@link PushFanoutService}. That set is acceptably lossy across restarts (a
@@ -1696,13 +1707,13 @@ export class CadreNode implements SAppIdLookup {
1696
1707
  if (this.seedBootstrapService) {
1697
1708
  try {
1698
1709
  await this.seedBootstrapService.deleteDeviceToken(peerId);
1699
- log('expireDeviceToken: authority-deleted stale DeviceToken for %s', peerId);
1710
+ log('expireDeviceToken: owner-deleted stale DeviceToken for %s', peerId);
1700
1711
  } catch (error) {
1701
- log('expireDeviceToken: authority delete for %s failed: %o', peerId, error);
1712
+ log('expireDeviceToken: owner delete for %s failed: %o', peerId, error);
1702
1713
  }
1703
1714
  return;
1704
1715
  }
1705
- log('expireDeviceToken: %s token is stale but this node is not an authority; re-registration required', peerId);
1716
+ log('expireDeviceToken: %s token is stale but this node is not an owner; re-registration required', peerId);
1706
1717
  }
1707
1718
 
1708
1719
  private async handleStrandAdded(strand: StrandRow): Promise<void> {
@@ -2070,19 +2081,19 @@ export class CadreNode implements SAppIdLookup {
2070
2081
 
2071
2082
  /**
2072
2083
  * Publish a strand row to the shared control database under this node's own
2073
- * authority identity, so other cadre members discover it via control-network
2084
+ * owner identity, so other cadre members discover it via control-network
2074
2085
  * sync (their {@link StrandWatcher} fires `strand:discovered`).
2075
2086
  *
2076
- * This is the authority-signed `Strand` INSERT that {@link addStrand}
2087
+ * This is the owner-signed `Strand` INSERT that {@link addStrand}
2077
2088
  * deliberately omits: `addStrand` only starts the LOCAL strand instance,
2078
2089
  * whereas publishing makes the strand visible cadre-wide. A typical creator
2079
2090
  * does both (start locally + publish); a discovering peer only does
2080
2091
  * `addStrand` (the row already exists).
2081
2092
  *
2082
2093
  * The insert is signed with the ed25519 key behind this node's PeerId — which
2083
- * {@link authorityKeyFromLibp2p} also exposes as the node's authority keypair,
2084
- * so peer identity and authority key are one and the same. That key must be
2085
- * enrolled in `AuthorityKey` (e.g. via {@link ControlDatabase.ensureAuthorityKey}
2094
+ * {@link ed25519KeyPairFromLibp2p} also exposes as the node's owner keypair,
2095
+ * so peer identity and owner key are one and the same. That key must be
2096
+ * enrolled in `OwnerKey` (e.g. via {@link ControlDatabase.ensureOwnerKey}
2086
2097
  * at genesis) or the schema's `Strand.Authorized` constraint rejects the write.
2087
2098
  * Failing loudly here is intentional: a silently-unpublished strand would run
2088
2099
  * as a local-only island that no peer could ever discover or join.
@@ -2091,7 +2102,7 @@ export class CadreNode implements SAppIdLookup {
2091
2102
  * {@link addStrand}).
2092
2103
  * @param type - `'o'` for open (default) or `'c'` for closed.
2093
2104
  * @param memberPrivateKey - Optional membership key for a closed strand.
2094
- * @throws if the node is not started, exposes no authority signing key, or the
2105
+ * @throws if the node is not started, exposes no owner signing key, or the
2095
2106
  * control DB rejects the (unauthorized) insert.
2096
2107
  */
2097
2108
  async publishStrand(strandId: string, type: 'o' | 'c' = 'o', memberPrivateKey?: string): Promise<void> {
@@ -2101,22 +2112,22 @@ export class CadreNode implements SAppIdLookup {
2101
2112
  const signingKey = this.getSelfSigningKey();
2102
2113
  if (!signingKey) {
2103
2114
  throw new Error(
2104
- `Cannot publish strand ${strandId}: no authority signing key available ` +
2105
- '(node identity is unavailable or does not match the node PeerId). Run authority ' +
2106
- 'genesis (ensureAuthorityKey + initializeSeedBootstrap) before publishing.'
2115
+ `Cannot publish strand ${strandId}: no owner signing key available ` +
2116
+ '(node identity is unavailable or does not match the node PeerId). Run owner ' +
2117
+ 'genesis (ensureOwnerKey + initializeSeedBootstrap) before publishing.'
2107
2118
  );
2108
2119
  }
2109
2120
  // insertStrand hands this callback the canonical row-bound message BYTES (see
2110
2121
  // buildAuthorizationMessage); ed25519-sign them directly (no pre-hash) with
2111
- // the authority private key, returning a base64url signature.
2122
+ // the owner private key, returning a base64url signature.
2112
2123
  const signMessage = (message: Uint8Array): string =>
2113
2124
  sign(message, signingKey.privateKeyB64, 'ed25519', 'bytes', 'base64url', 'base64url') as string;
2114
2125
  await this.controlDatabase.insertStrand(strandId, type, signingKey.publicKeyB64, signMessage, memberPrivateKey);
2115
- log('Published strand %s (type %s) to control DB under authority %s', strandId, type, signingKey.publicKeyB64);
2126
+ log('Published strand %s (type %s) to control DB under owner %s', strandId, type, signingKey.publicKeyB64);
2116
2127
  }
2117
2128
 
2118
2129
  /**
2119
- * Publish an authority-signed `FormationInvite` (open-invitation token) to the
2130
+ * Publish an owner-signed `FormationInvite` (open-invitation token) to the
2120
2131
  * shared control database, so a later {@link formStrand} redemption can be
2121
2132
  * validated against it (the consent branch of `Strand.Authorized`).
2122
2133
  *
@@ -2127,8 +2138,8 @@ export class CadreNode implements SAppIdLookup {
2127
2138
  * from this row. A host minting a closed-strand invite does both (mint +
2128
2139
  * publish), exactly as the integration harness's `createInvitation` does.
2129
2140
  *
2130
- * Signs with the same self-authority key as {@link publishStrand} (the ed25519
2131
- * key behind this node's PeerId, which must be an enrolled `AuthorityKey`).
2141
+ * Signs with the same self-owner key as {@link publishStrand} (the ed25519
2142
+ * key behind this node's PeerId, which must be an enrolled `OwnerKey`).
2132
2143
  * Throws loudly if the node isn't started or exposes no signing key.
2133
2144
  *
2134
2145
  * @param token - Invitation token (the `FormationInvite` primary key); use the
@@ -2148,15 +2159,15 @@ export class CadreNode implements SAppIdLookup {
2148
2159
  const signingKey = this.getSelfSigningKey();
2149
2160
  if (!signingKey) {
2150
2161
  throw new Error(
2151
- `Cannot publish formation invite ${token}: no authority signing key available ` +
2152
- '(node identity is unavailable or does not match the node PeerId). Run authority ' +
2153
- 'genesis (ensureAuthorityKey + initializeSeedBootstrap) before publishing.'
2162
+ `Cannot publish formation invite ${token}: no owner signing key available ` +
2163
+ '(node identity is unavailable or does not match the node PeerId). Run owner ' +
2164
+ 'genesis (ensureOwnerKey + initializeSeedBootstrap) before publishing.'
2154
2165
  );
2155
2166
  }
2156
2167
  const signMessage = (message: Uint8Array): string =>
2157
2168
  sign(message, signingKey.privateKeyB64, 'ed25519', 'bytes', 'base64url', 'base64url') as string;
2158
2169
  await this.controlDatabase.insertFormationInvite(token, sAppId, signingKey.publicKeyB64, signMessage, options);
2159
- log('Published formation invite %s (sApp %s) under authority %s', token, sAppId, signingKey.publicKeyB64);
2170
+ log('Published formation invite %s (sApp %s) under owner %s', token, sAppId, signingKey.publicKeyB64);
2160
2171
  }
2161
2172
 
2162
2173
  /**
@@ -2475,19 +2486,19 @@ export class CadreNode implements SAppIdLookup {
2475
2486
  // ============================================================================
2476
2487
 
2477
2488
  /**
2478
- * Initialize the seed bootstrap service with an authority key.
2489
+ * Initialize the seed bootstrap service with an owner key.
2479
2490
  * Must be called before using seed-related methods that require signing.
2480
2491
  *
2481
- * @param authorityPrivateKey - The authority's private key (base64url encoded)
2492
+ * @param ownerPrivateKey - The owner's private key (base64url encoded)
2482
2493
  */
2483
- initializeSeedBootstrap(authorityPrivateKey: string): void {
2494
+ initializeSeedBootstrap(ownerPrivateKey: string): void {
2484
2495
  if (!this.controlNode || !this.controlDatabase) {
2485
2496
  throw new Error('CadreNode must be started before initializing seed bootstrap');
2486
2497
  }
2487
2498
 
2488
2499
  this.seedBootstrapService = new SeedBootstrapService({
2489
2500
  partyId: this.config.controlNetwork.partyId,
2490
- authorityPrivateKey,
2501
+ ownerPrivateKey,
2491
2502
  inviteAddressResolver: () => this.resolveInviteAddresses(),
2492
2503
  trustPolicy: this.config.seedTrustPolicy,
2493
2504
  });
@@ -2538,12 +2549,45 @@ export class CadreNode implements SAppIdLookup {
2538
2549
 
2539
2550
  /**
2540
2551
  * Probe whether a given peer is a `CadrePeer` member.
2552
+ *
2553
+ * This is the ADDRESSABLE surface ("do I have a dialable address record for this
2554
+ * peer") — it includes this node's own self-published row. Address resolution and
2555
+ * push fan-out use this. The trust-facing gate is {@link isAuthorizedMember}.
2541
2556
  */
2542
2557
  async isMember(peerId: string): Promise<boolean> {
2543
2558
  const members = await this.listMembers();
2544
2559
  return members.some(m => m.peerId === peerId);
2545
2560
  }
2546
2561
 
2562
+ /**
2563
+ * Enumerate the party's AUTHORIZED members — the trust-facing set, distinct from
2564
+ * the addressable set ({@link listMembers}). Excludes this node itself: a node
2565
+ * publishes its own `CadrePeer` address row so its dialable address rides in seeds,
2566
+ * but "self" is not a peer this node authorized. The control-network wake and
2567
+ * strand-address gates consult this set, NOT the addressable one.
2568
+ *
2569
+ * NOTE: this is the addressable set minus self for now. The real trust predicate
2570
+ * (voucher recorded on the row ∈ node-local trusted-owner anchor, signature
2571
+ * verified) lands with the `membership-node-local-owner-anchor` /
2572
+ * `membership-authorized-predicate-and-gates` tickets; keeping the filter in this one
2573
+ * method means that change touches a single place.
2574
+ */
2575
+ async listAuthorizedMembers(): Promise<Array<{ peerId: string; multiaddr: string | null }>> {
2576
+ const selfPeerId = this.peerId?.toString();
2577
+ const members = await this.listMembers();
2578
+ return members.filter(m => m.peerId !== selfPeerId);
2579
+ }
2580
+
2581
+ /**
2582
+ * Probe whether a given peer is an AUTHORIZED party member (see
2583
+ * {@link listAuthorizedMembers}) — the gate the control-network wake and
2584
+ * strand-address responders consult, NOT {@link isMember} (the addressable surface).
2585
+ */
2586
+ async isAuthorizedMember(peerId: string): Promise<boolean> {
2587
+ const members = await this.listAuthorizedMembers();
2588
+ return members.some(m => m.peerId === peerId);
2589
+ }
2590
+
2547
2591
  /**
2548
2592
  * Push-wake a hibernating cadre peer over the control network.
2549
2593
  *
@@ -2573,8 +2617,8 @@ export class CadreNode implements SAppIdLookup {
2573
2617
 
2574
2618
  /**
2575
2619
  * Enable the seed listener for receiving seeds via the /sereus/seed/1.0.0 protocol.
2576
- * This is for drone nodes that need to receive seeds without being an authority.
2577
- * Does not require an authority key.
2620
+ * This is for drone nodes that need to receive seeds without being an owner.
2621
+ * Does not require an owner key.
2578
2622
  */
2579
2623
  enableSeedListener(): void {
2580
2624
  if (!this.controlNode || !this.controlDatabase) {
@@ -2589,7 +2633,7 @@ export class CadreNode implements SAppIdLookup {
2589
2633
 
2590
2634
  this.seedBootstrapService = new SeedBootstrapService({
2591
2635
  partyId: this.config.controlNetwork.partyId,
2592
- // No authority key - this node only receives seeds
2636
+ // No owner key - this node only receives seeds
2593
2637
  inviteAddressResolver: () => this.resolveInviteAddresses(),
2594
2638
  trustPolicy: this.config.seedTrustPolicy,
2595
2639
  });
@@ -2613,7 +2657,7 @@ export class CadreNode implements SAppIdLookup {
2613
2657
 
2614
2658
  /**
2615
2659
  * Authorize a new peer to join the cadre.
2616
- * Signs the peer ID with the authority key and inserts into CadrePeer table.
2660
+ * Signs the peer ID with the owner key and inserts into CadrePeer table.
2617
2661
  *
2618
2662
  * @param peerId - The peer ID to authorize
2619
2663
  * @param multiaddrs - Optional multiaddrs for the peer
@@ -2629,7 +2673,7 @@ export class CadreNode implements SAppIdLookup {
2629
2673
 
2630
2674
  /**
2631
2675
  * Remove a previously-authorized peer from the cadre.
2632
- * Signs the peer ID with the authority key and deletes the CadrePeer row.
2676
+ * Signs the peer ID with the owner key and deletes the CadrePeer row.
2633
2677
  *
2634
2678
  * @param peerId - The peer ID to remove
2635
2679
  */
@@ -2644,7 +2688,7 @@ export class CadreNode implements SAppIdLookup {
2644
2688
 
2645
2689
  /**
2646
2690
  * Create a seed from the current control network state.
2647
- * The seed contains peer information and is signed by an authority.
2691
+ * The seed contains peer information and is signed by an owner.
2648
2692
  */
2649
2693
  async createSeed(): Promise<ControlNetworkSeed> {
2650
2694
  if (!this.seedBootstrapService) {
@@ -2659,7 +2703,7 @@ export class CadreNode implements SAppIdLookup {
2659
2703
  * Validates the seed signature, then evaluates a trust anchor for the signer
2660
2704
  * key (see `SeedTrustPolicy`). An enrollment caller can pass a per-seed
2661
2705
  * `trustPolicy` override — e.g. a `pinnedKeyTrustPolicy` built from a
2662
- * `CadreInvite.authorityKeys` — so a cold-start node can accept its first
2706
+ * `CadreInvite.ownerKeys` — so a cold-start node can accept its first
2663
2707
  * seed without reconfiguring the service.
2664
2708
  */
2665
2709
  async applySeed(
@@ -2667,7 +2711,7 @@ export class CadreNode implements SAppIdLookup {
2667
2711
  options?: { trustPolicy?: SeedTrustPolicy }
2668
2712
  ): Promise<ApplySeedResult> {
2669
2713
  if (!this.seedBootstrapService) {
2670
- // Create a temporary service for applying seeds (doesn't need authority key).
2714
+ // Create a temporary service for applying seeds (doesn't need owner key).
2671
2715
  // partyId is the attacker-influenced seed.partyId — it only labels logs; the
2672
2716
  // trust decision rests solely on signerKey vs the anchor set (configured
2673
2717
  // default below, or the per-call options.trustPolicy override).
@@ -2799,7 +2843,7 @@ export class CadreNode implements SAppIdLookup {
2799
2843
  }
2800
2844
 
2801
2845
  /**
2802
- * Dial an authority from an invite (for phone joining via invite).
2846
+ * Dial an owner from an invite (for phone joining via invite).
2803
2847
  */
2804
2848
  async dialInvite(invite: CadreInvite): Promise<void> {
2805
2849
  if (!this.seedBootstrapService) {