@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
@@ -1,6 +1,6 @@
1
1
  import type { Database } from '@quereus/quereus';
2
2
  import type { SAppConfig } from './types.js';
3
- import type { AuthorityKeyPair } from './authority-key.js';
3
+ import type { Ed25519KeyPair } from './ed25519-key.js';
4
4
  /**
5
5
  * The engine (rules-logic system) assumed to manage every strand. Persisted in
6
6
  * `Strand.Header.Engine`.
@@ -23,7 +23,7 @@ export declare const STRAND_ENGINE_VERSION = "0.1.0";
23
23
  *
24
24
  * `schemas/strand.qsql` signs a single SHA-256 digest over a `'|'`-joined payload
25
25
  * (e.g. `Member.Authorized` verifies
26
- * `verify(digest(new.Key), context.AuthoritySignature, A.MemberKey, 'ed25519')`).
26
+ * `verify(digest(new.Key), context.ManagerSignature, A.MemberKey, 'ed25519')`).
27
27
  * So the signer hashes the payload to raw bytes and ed25519-signs *those bytes*:
28
28
  * SQL `digest(...)`'s default output is base64url and `verify(...)`'s default
29
29
  * `inputEncoding` is base64url, so signer and verifier operate on identical bytes.
@@ -61,16 +61,16 @@ export declare function verifyStrandPayload(payload: string, signatureB64: strin
61
61
  export interface FounderBootstrapParams {
62
62
  /** The strand id — written to `Header.Id`. */
63
63
  strandId: string;
64
- /** Strand type: `'o'` (open, Header only) or `'c'` (closed, Header+Member+Authority). */
64
+ /** Strand type: `'o'` (open, Header only) or `'c'` (closed, Header+Member+Manager). */
65
65
  type: 'o' | 'c';
66
66
  /** The sApp config whose id/version/schema/signature populate the Header. */
67
67
  sApp: SAppConfig;
68
68
  /**
69
69
  * The founder's derived strand keypair (from {@link strandMemberKeyPair}). Its
70
- * `publicKeyB64` becomes the founding `Member.Key` and `Authority.MemberKey`.
70
+ * `publicKeyB64` becomes the founding `Member.Key` and `Manager.MemberKey`.
71
71
  * Required for a closed strand; ignored for an open strand.
72
72
  */
73
- founderKeyPair?: AuthorityKeyPair;
73
+ founderKeyPair?: Ed25519KeyPair;
74
74
  }
75
75
  /**
76
76
  * Founder-only one-time bootstrap of a strand's `Strand.*` membership/RBAC rows.
@@ -81,16 +81,16 @@ export interface FounderBootstrapParams {
81
81
  * restart / founder re-`addStrand` is a no-op and never double-inserts.
82
82
  *
83
83
  * Behavior by strand type:
84
- * - **Open (`'o'`)**: insert `Header` only. `Member`/`Authority`/`Invite` are
84
+ * - **Open (`'o'`)**: insert `Header` only. `Member`/`Manager`/`Invite` are
85
85
  * `OnlyClosed` and would trip that constraint — they are skipped entirely.
86
86
  * - **Closed (`'c'`)**: insert `Header(Type='c')`, then the founding `Member`
87
- * (`Key = founderKeyPair.publicKeyB64`), then the founding `Authority`
87
+ * (`Key = founderKeyPair.publicKeyB64`), then the founding `Manager`
88
88
  * (`MemberKey = founderKeyPair.publicKeyB64`). Insert order matters: the
89
- * Header must commit before the deferred `OnlyClosed` checks on Member/Authority
89
+ * Header must commit before the deferred `OnlyClosed` checks on Member/Manager
90
90
  * evaluate at commit.
91
91
  *
92
92
  * A closed strand with no `founderKeyPair` throws: a closed strand with no founding
93
- * Authority could never admit anyone, so failing loudly here (which propagates out
93
+ * Manager could never admit anyone, so failing loudly here (which propagates out
94
94
  * of `StrandDatabase.initialize()` and triggers the runtime's rollback) is correct.
95
95
  *
96
96
  * @param db - The strand's Quereus database (schema already applied).
@@ -102,10 +102,10 @@ export declare function bootstrapFounderMembership(db: Database, params: Founder
102
102
  /** Parameters for {@link issueInvite}. */
103
103
  export interface IssueInviteParams {
104
104
  /**
105
- * The issuing authority's strand keypair. Its `publicKeyB64` must already be a
106
- * `Strand.Authority` row (the `InviteValid` constraint rejects a non-authority).
105
+ * The issuing manager's strand keypair. Its `publicKeyB64` must already be a
106
+ * `Strand.Manager` row (the `InviteValid` constraint rejects a non-manager).
107
107
  */
108
- authorityKeyPair: AuthorityKeyPair;
108
+ managerKeyPair: Ed25519KeyPair;
109
109
  /**
110
110
  * Optional invite expiry as epoch milliseconds. When set, it is canonicalised
111
111
  * via {@link canonicalDatetime} so the signed payload segment byte-matches the
@@ -131,10 +131,10 @@ export interface IssuedInvite {
131
131
  *
132
132
  * Generates a fresh invite ed25519 keypair (the public key becomes `Invite.Key`),
133
133
  * builds the constraint's payload (`Key || '|' || coalesce(Expiration, '')`), and
134
- * signs it TWICE: with the authority private key (→ `AuthoritySignature`, proving
135
- * an authority issued it) and with the invite private key (→ `InviteSignature`,
134
+ * signs it TWICE: with the manager private key (→ `ManagerSignature`, proving
135
+ * a manager issued it) and with the invite private key (→ `InviteSignature`,
136
136
  * proving the issuer actually holds the invite secret). Both signatures plus the
137
- * authority public key are bound as constraint context for the `Invite` insert.
137
+ * manager public key are bound as constraint context for the `Invite` insert.
138
138
  *
139
139
  * The returned `invitePrivateKey` is the only secret the invitee needs to redeem
140
140
  * the invite — it is NOT stored in the strand (only `Invite.Key`, the public half,
@@ -142,7 +142,7 @@ export interface IssuedInvite {
142
142
  * is `InviteKey`, so a given invite can be consumed at most once.
143
143
  *
144
144
  * @param db - The closed strand's database (founder already bootstrapped).
145
- * @param params - The issuing authority keypair and optional expiry.
145
+ * @param params - The issuing manager keypair and optional expiry.
146
146
  * @returns The invite public key and the out-of-band private seed.
147
147
  */
148
148
  export declare function issueInvite(db: Database, params: IssueInviteParams): Promise<IssuedInvite>;
@@ -204,44 +204,44 @@ export interface ConsumeInviteParams {
204
204
  * `ConsumedInvite` row survives).
205
205
  */
206
206
  export declare function consumeInvite(db: Database, params: ConsumeInviteParams): Promise<void>;
207
- /** Parameters for {@link addMemberByAuthority}. */
208
- export interface AddMemberByAuthorityParams {
207
+ /** Parameters for {@link addMemberByManager}. */
208
+ export interface AddMemberByManagerParams {
209
209
  /**
210
- * The admitting authority's strand keypair. Its `publicKeyB64` must be a
211
- * `Strand.Authority` row; it signs the new member key directly.
210
+ * The admitting manager's strand keypair. Its `publicKeyB64` must be a
211
+ * `Strand.Manager` row; it signs the new member key directly.
212
212
  */
213
- authorityKeyPair: AuthorityKeyPair;
213
+ managerKeyPair: Ed25519KeyPair;
214
214
  /** The joining member's ed25519 PUBLIC key (base64url) — the new `Member.Key`. */
215
215
  memberKey: string;
216
216
  }
217
217
  /**
218
- * Admit a `Member` directly by authority signature — the sibling of the invite
219
- * path on `Member.Authorized`'s direct-authority branch.
218
+ * Admit a `Member` directly by manager signature — the sibling of the invite
219
+ * path on `Member.Authorized`'s direct-manager branch.
220
220
  *
221
- * The constraint verifies `digest(new.Key)` against an `Authority` row matching
222
- * `context.AuthorityKey`, so the payload is just the member key (no `'|'` join).
223
- * No `ConsumedInvite` is involved: this is the path an authority uses to seat a
224
- * member it already trusts (e.g. an authority-side enrollment that admits a
221
+ * The constraint verifies `digest(new.Key)` against a `Manager` row matching
222
+ * `context.ManagerKey`, so the payload is just the member key (no `'|'` join).
223
+ * No `ConsumedInvite` is involved: this is the path a manager uses to seat a
224
+ * member it already trusts (e.g. a manager-side enrollment that admits a
225
225
  * party already authorised out-of-band).
226
226
  *
227
227
  * @param db - The closed strand's database.
228
- * @param params - The admitting authority keypair and the new member key.
229
- * @throws If `Member.Authorized` rejects (e.g. a non-authority key); the insert
228
+ * @param params - The admitting manager keypair and the new member key.
229
+ * @throws If `Member.Authorized` rejects (e.g. a non-manager key); the insert
230
230
  * rolls back, leaving no `Member` row.
231
231
  */
232
- export declare function addMemberByAuthority(db: Database, params: AddMemberByAuthorityParams): Promise<void>;
232
+ export declare function addMemberByManager(db: Database, params: AddMemberByManagerParams): Promise<void>;
233
233
  /** Parameters for {@link registerMemberPeer}. */
234
234
  export interface RegisterMemberPeerParams {
235
235
  /**
236
236
  * The member's OWN strand keypair (`{ privateKeyB64, publicKeyB64 }`). Its
237
237
  * `publicKeyB64` becomes `MemberPeer.MemberKey` and it self-signs the binding —
238
238
  * `MemberPeer.Authorized` verifies the signature against `MemberKey` itself, so a
239
- * peer can only be registered by the very member it belongs to (no authority
239
+ * peer can only be registered by the very member it belongs to (no manager
240
240
  * involved). The founder passes its `strandMemberKeyPair`; an invited member
241
- * passes its own keypair. Typed as {@link AuthorityKeyPair} only for the shared
242
- * base64url keypair shape — no authority privilege is implied.
241
+ * passes its own keypair. Typed as {@link Ed25519KeyPair} only for the shared
242
+ * base64url keypair shape — no manager privilege is implied.
243
243
  */
244
- memberKeyPair: AuthorityKeyPair;
244
+ memberKeyPair: Ed25519KeyPair;
245
245
  /** The peer/node id (libp2p peer id string) to associate with the member. */
246
246
  peerId: string;
247
247
  }
@@ -272,90 +272,90 @@ export interface RegisterMemberPeerParams {
272
272
  * matching `Member` row); the insert rolls back, leaving no `MemberPeer` row.
273
273
  */
274
274
  export declare function registerMemberPeer(db: Database, params: RegisterMemberPeerParams): Promise<void>;
275
- /** Parameters for {@link addAuthority}. */
276
- export interface AddAuthorityParams {
275
+ /** Parameters for {@link addManager}. */
276
+ export interface AddManagerParams {
277
277
  /**
278
- * An EXISTING authority's strand keypair. Its `publicKeyB64` must already be a
279
- * `Strand.Authority` row (the existing-authority branch of `Authority.Authorized`
280
- * rejects a non-authority once the founder authority exists); it signs the new
281
- * authority key and is bound as `context.AuthorityKey`.
278
+ * An EXISTING manager's strand keypair. Its `publicKeyB64` must already be a
279
+ * `Strand.Manager` row (the existing-manager branch of `Manager.Authorized`
280
+ * rejects a non-manager once the founder manager exists); it signs the new
281
+ * manager key and is bound as `context.ManagerKey`.
282
282
  */
283
- byAuthorityKeyPair: AuthorityKeyPair;
284
- /** The member key to promote — the new `Authority.MemberKey` row. */
285
- newAuthorityKey: string;
283
+ byManagerKeyPair: Ed25519KeyPair;
284
+ /** The member key to promote — the new `Manager.MemberKey` row. */
285
+ newManagerKey: string;
286
286
  }
287
287
  /**
288
- * Promote a member to `Authority` on the signature of an existing authority.
288
+ * Promote a member to `Manager` on the signature of an existing manager.
289
289
  *
290
- * The existing-authority branch of `Authority.Authorized` verifies
290
+ * The existing-manager branch of `Manager.Authorized` verifies
291
291
  * `digest(coalesce(new.MemberKey, old.MemberKey))` — `new.MemberKey` =
292
- * `newAuthorityKey` on insert — against an `Authority` row matching
293
- * `context.AuthorityKey`. So the signer signs the new authority key directly (no
294
- * `'|'` join) and binds itself as `context.AuthorityKey`.
292
+ * `newManagerKey` on insert — against a `Manager` row matching
293
+ * `context.ManagerKey`. So the signer signs the new manager key directly (no
294
+ * `'|'` join) and binds itself as `context.ManagerKey`.
295
295
  *
296
- * Once the founder authority exists, the schema's `(select count(1) from Authority)
296
+ * Once the founder manager exists, the schema's `(select count(1) from Manager)
297
297
  * <= 1` bootstrap shortcut no longer applies to a second add (at commit the count
298
298
  * includes the new row, so it is ≥ 2), so this genuinely exercises signature
299
- * verification — a non-authority signer, or a signature over the wrong key, is
299
+ * verification — a non-manager signer, or a signature over the wrong key, is
300
300
  * rejected.
301
301
  *
302
- * @param db - The closed strand's database (founder authority already seated).
303
- * @param params - The authorizing authority's keypair and the new authority key.
304
- * @throws If `Authority.Authorized` rejects; the insert rolls back.
302
+ * @param db - The closed strand's database (founder manager already seated).
303
+ * @param params - The authorizing manager's keypair and the new manager key.
304
+ * @throws If `Manager.Authorized` rejects; the insert rolls back.
305
305
  */
306
- export declare function addAuthority(db: Database, params: AddAuthorityParams): Promise<void>;
307
- /** Parameters for {@link removeAuthority}. */
308
- export interface RemoveAuthorityParams {
306
+ export declare function addManager(db: Database, params: AddManagerParams): Promise<void>;
307
+ /** Parameters for {@link removeManager}. */
308
+ export interface RemoveManagerParams {
309
309
  /**
310
- * The keypair authorizing the removal, bound as `context.AuthorityKey` and
311
- * signing `digest(targetAuthorityKey)`. For an ADMIN removal this is a DIFFERENT
312
- * existing authority (satisfying the existing-authority branch); for a
310
+ * The keypair authorizing the removal, bound as `context.ManagerKey` and
311
+ * signing `digest(targetManagerKey)`. For an ADMIN removal this is a DIFFERENT
312
+ * existing manager (satisfying the existing-manager branch); for a
313
313
  * SELF-resignation it is the target's OWN keypair (`publicKeyB64 ===
314
- * targetAuthorityKey`, satisfying the former-authority self branch). The same
314
+ * targetManagerKey`, satisfying the former-manager self branch). The same
315
315
  * context construction satisfies whichever branch applies — see
316
- * {@link removeAuthority}.
316
+ * {@link removeManager}.
317
317
  */
318
- byAuthorityKeyPair: AuthorityKeyPair;
319
- /** The `Authority.MemberKey` row to delete. */
320
- targetAuthorityKey: string;
318
+ byManagerKeyPair: Ed25519KeyPair;
319
+ /** The `Manager.MemberKey` row to delete. */
320
+ targetManagerKey: string;
321
321
  }
322
322
  /**
323
- * Remove an `Authority` row — either an admin removing a different authority or an
324
- * authority resigning itself.
323
+ * Remove a `Manager` row — either an admin removing a different manager or a
324
+ * manager resigning itself.
325
325
  *
326
326
  * On a DELETE, `coalesce(new.MemberKey, old.MemberKey)` binds `old.MemberKey =
327
- * targetAuthorityKey` (there is no `new` row), so the signed payload is the target
328
- * key for BOTH accepting branches of `Authority.Authorized`, and a single context
327
+ * targetManagerKey` (there is no `new` row), so the signed payload is the target
328
+ * key for BOTH accepting branches of `Manager.Authorized`, and a single context
329
329
  * construction serves both:
330
- * - **Admin removal**: `byAuthorityKeyPair` is a different existing authority; the
331
- * existing-authority branch verifies the signature against `A.MemberKey =
332
- * context.AuthorityKey`.
333
- * - **Self-resignation**: `byAuthorityKeyPair` IS the target (its `publicKeyB64`
334
- * equals `targetAuthorityKey`); the former-authority self branch verifies
335
- * `old.MemberKey = context.AuthorityKey` and the self-signature over `old.MemberKey`.
330
+ * - **Admin removal**: `byManagerKeyPair` is a different existing manager; the
331
+ * existing-manager branch verifies the signature against `A.MemberKey =
332
+ * context.ManagerKey`.
333
+ * - **Self-resignation**: `byManagerKeyPair` IS the target (its `publicKeyB64`
334
+ * equals `targetManagerKey`); the former-manager self branch verifies
335
+ * `old.MemberKey = context.ManagerKey` and the self-signature over `old.MemberKey`.
336
336
  *
337
337
  * The caller selects the case purely by which keypair it passes — no branching here.
338
338
  *
339
339
  * KNOWN PLATFORM GAP (not fixable here): the optimystic bootstrap-mode transactor
340
340
  * evaluates deferred (subquery-bearing) CHECK constraints only on INSERT, not on
341
- * DELETE. `Authority.Authorized` is deferred, so the platform currently accepts ANY
342
- * `Authority` delete regardless of signature — `removeAuthority`'s authorization is
341
+ * DELETE. `Manager.Authorized` is deferred, so the platform currently accepts ANY
342
+ * `Manager` delete regardless of signature — `removeManager`'s authorization is
343
343
  * effectively unenforced at runtime until that gap is closed (filed as
344
344
  * `optimystic-deferred-check-not-enforced-on-delete`). This writer still builds the
345
345
  * correct, signed delete so it works unchanged once enforcement lands — exactly as
346
346
  * the invite path issues correct inserts despite the open PK-uniqueness gap.
347
347
  *
348
- * KNOWN SCHEMA HAZARD (not guarded here): even with enforcement, `Authority.
349
- * Authorized`'s `(select count(1) from Authority) <= 1` bootstrap branch is true at
348
+ * KNOWN SCHEMA HAZARD (not guarded here): even with enforcement, `Manager.
349
+ * Authorized`'s `(select count(1) from Manager) <= 1` bootstrap branch is true at
350
350
  * commit whenever a delete drops the count to ≤ 1, so removing the strand's LAST (or
351
- * second-to-last) authority would be accepted regardless of signature — and removing
351
+ * second-to-last) manager would be accepted regardless of signature — and removing
352
352
  * the last one orphans the strand (no one can ever add another). The schema does not
353
- * prevent this; a "min-one-authority" invariant is deferred to a future schema change
353
+ * prevent this; a "min-one-manager" invariant is deferred to a future schema change
354
354
  * rather than grown into this writer.
355
355
  *
356
356
  * @param db - The closed strand's database.
357
- * @param params - The authorizing keypair and the target authority key.
358
- * @throws If `Authority.Authorized` rejects (e.g. a non-authority admin removal
359
- * while > 2 authorities remain); the delete rolls back.
357
+ * @param params - The authorizing keypair and the target manager key.
358
+ * @throws If `Manager.Authorized` rejects (e.g. a non-manager admin removal
359
+ * while > 2 managers remain); the delete rolls back.
360
360
  */
361
- export declare function removeAuthority(db: Database, params: RemoveAuthorityParams): Promise<void>;
361
+ export declare function removeManager(db: Database, params: RemoveManagerParams): Promise<void>;
@@ -24,7 +24,7 @@ export const STRAND_ENGINE_VERSION = '0.1.0';
24
24
  *
25
25
  * `schemas/strand.qsql` signs a single SHA-256 digest over a `'|'`-joined payload
26
26
  * (e.g. `Member.Authorized` verifies
27
- * `verify(digest(new.Key), context.AuthoritySignature, A.MemberKey, 'ed25519')`).
27
+ * `verify(digest(new.Key), context.ManagerSignature, A.MemberKey, 'ed25519')`).
28
28
  * So the signer hashes the payload to raw bytes and ed25519-signs *those bytes*:
29
29
  * SQL `digest(...)`'s default output is base64url and `verify(...)`'s default
30
30
  * `inputEncoding` is base64url, so signer and verifier operate on identical bytes.
@@ -109,7 +109,7 @@ async function insertHeaderIfAbsent(db, params) {
109
109
  * Insert the founding `Strand.Member` if no member exists yet.
110
110
  *
111
111
  * The empty-table state satisfies the `count(1) from Member <= 1` bootstrap branch
112
- * of `Member.Authorized`, so no authority signature is needed — the context fields
112
+ * of `Member.Authorized`, so no manager signature is needed — the context fields
113
113
  * are explicit nulls. Guarding on the count makes this idempotent: a re-run (the
114
114
  * founder re-`addStrand`/`resumeStrand`) finds the member present and skips.
115
115
  */
@@ -119,28 +119,28 @@ async function insertFounderMemberIfAbsent(db, memberKey, strandId) {
119
119
  return;
120
120
  }
121
121
  await db.exec(`insert into Strand.Member (Key)
122
- with context AuthorityKey = null, AuthoritySignature = null
122
+ with context ManagerKey = null, ManagerSignature = null
123
123
  values (?)`, [memberKey]);
124
124
  log('Inserted founding Member for strand %s', strandId);
125
125
  }
126
126
  /**
127
- * Insert the founding `Strand.Authority` if no authority exists yet.
127
+ * Insert the founding `Strand.Manager` if no manager exists yet.
128
128
  *
129
- * The empty-table state satisfies the `count(1) from Authority <= 1` bootstrap
130
- * branch of `Authority.Authorized`, so no signature is needed. Idempotent via the
131
- * same count guard. Must run AFTER the Header insert: `Authority.OnlyClosed` is a
129
+ * The empty-table state satisfies the `count(1) from Manager <= 1` bootstrap
130
+ * branch of `Manager.Authorized`, so no signature is needed. Idempotent via the
131
+ * same count guard. Must run AFTER the Header insert: `Manager.OnlyClosed` is a
132
132
  * deferred (subquery) check evaluated at commit, and the sequential auto-commit
133
- * Header→Member→Authority order ensures the closed `Header` is committed first.
133
+ * Header→Member→Manager order ensures the closed `Header` is committed first.
134
134
  */
135
- async function insertFounderAuthorityIfAbsent(db, memberKey, strandId) {
136
- if (await strandTableCount(db, 'Authority') > 0) {
137
- log('Authority already present for strand %s; skipping founder Authority', strandId);
135
+ async function insertFounderManagerIfAbsent(db, memberKey, strandId) {
136
+ if (await strandTableCount(db, 'Manager') > 0) {
137
+ log('Manager already present for strand %s; skipping founder Manager', strandId);
138
138
  return;
139
139
  }
140
- await db.exec(`insert into Strand.Authority (MemberKey)
141
- with context AuthorityKey = null, Signature = null
140
+ await db.exec(`insert into Strand.Manager (MemberKey)
141
+ with context ManagerKey = null, Signature = null
142
142
  values (?)`, [memberKey]);
143
- log('Inserted founding Authority for strand %s', strandId);
143
+ log('Inserted founding Manager for strand %s', strandId);
144
144
  }
145
145
  /**
146
146
  * Founder-only one-time bootstrap of a strand's `Strand.*` membership/RBAC rows.
@@ -151,16 +151,16 @@ async function insertFounderAuthorityIfAbsent(db, memberKey, strandId) {
151
151
  * restart / founder re-`addStrand` is a no-op and never double-inserts.
152
152
  *
153
153
  * Behavior by strand type:
154
- * - **Open (`'o'`)**: insert `Header` only. `Member`/`Authority`/`Invite` are
154
+ * - **Open (`'o'`)**: insert `Header` only. `Member`/`Manager`/`Invite` are
155
155
  * `OnlyClosed` and would trip that constraint — they are skipped entirely.
156
156
  * - **Closed (`'c'`)**: insert `Header(Type='c')`, then the founding `Member`
157
- * (`Key = founderKeyPair.publicKeyB64`), then the founding `Authority`
157
+ * (`Key = founderKeyPair.publicKeyB64`), then the founding `Manager`
158
158
  * (`MemberKey = founderKeyPair.publicKeyB64`). Insert order matters: the
159
- * Header must commit before the deferred `OnlyClosed` checks on Member/Authority
159
+ * Header must commit before the deferred `OnlyClosed` checks on Member/Manager
160
160
  * evaluate at commit.
161
161
  *
162
162
  * A closed strand with no `founderKeyPair` throws: a closed strand with no founding
163
- * Authority could never admit anyone, so failing loudly here (which propagates out
163
+ * Manager could never admit anyone, so failing loudly here (which propagates out
164
164
  * of `StrandDatabase.initialize()` and triggers the runtime's rollback) is correct.
165
165
  *
166
166
  * @param db - The strand's Quereus database (schema already applied).
@@ -172,21 +172,21 @@ export async function bootstrapFounderMembership(db, params) {
172
172
  const { strandId, type, founderKeyPair } = params;
173
173
  log('Founder bootstrap for strand %s (type %s)', strandId, type);
174
174
  // Validate BEFORE writing anything: a closed strand with no founder key must
175
- // fail without leaving a closed `Header` that has no founding Member/Authority
175
+ // fail without leaving a closed `Header` that has no founding Member/Manager
176
176
  // (such a strand could never admit anyone).
177
177
  if (type === 'c' && !founderKeyPair) {
178
178
  throw new Error(`Cannot bootstrap closed strand ${strandId}: no founder key pair derived from MemberPrivateKey. ` +
179
- 'A closed strand needs a founding Member + Authority or it can never admit members.');
179
+ 'A closed strand needs a founding Member + Manager or it can never admit members.');
180
180
  }
181
- // Header is written for every strand; Member/Authority are closed-only.
181
+ // Header is written for every strand; Member/Manager are closed-only.
182
182
  await insertHeaderIfAbsent(db, params);
183
183
  if (type === 'o') {
184
- // Open strand: Member/Authority/Invite are OnlyClosed — nothing else to write.
184
+ // Open strand: Member/Manager/Invite are OnlyClosed — nothing else to write.
185
185
  return;
186
186
  }
187
187
  const memberKey = founderKeyPair.publicKeyB64;
188
188
  await insertFounderMemberIfAbsent(db, memberKey, strandId);
189
- await insertFounderAuthorityIfAbsent(db, memberKey, strandId);
189
+ await insertFounderManagerIfAbsent(db, memberKey, strandId);
190
190
  log('Founder bootstrap complete for closed strand %s', strandId);
191
191
  }
192
192
  /**
@@ -194,10 +194,10 @@ export async function bootstrapFounderMembership(db, params) {
194
194
  *
195
195
  * Generates a fresh invite ed25519 keypair (the public key becomes `Invite.Key`),
196
196
  * builds the constraint's payload (`Key || '|' || coalesce(Expiration, '')`), and
197
- * signs it TWICE: with the authority private key (→ `AuthoritySignature`, proving
198
- * an authority issued it) and with the invite private key (→ `InviteSignature`,
197
+ * signs it TWICE: with the manager private key (→ `ManagerSignature`, proving
198
+ * a manager issued it) and with the invite private key (→ `InviteSignature`,
199
199
  * proving the issuer actually holds the invite secret). Both signatures plus the
200
- * authority public key are bound as constraint context for the `Invite` insert.
200
+ * manager public key are bound as constraint context for the `Invite` insert.
201
201
  *
202
202
  * The returned `invitePrivateKey` is the only secret the invitee needs to redeem
203
203
  * the invite — it is NOT stored in the strand (only `Invite.Key`, the public half,
@@ -205,11 +205,11 @@ export async function bootstrapFounderMembership(db, params) {
205
205
  * is `InviteKey`, so a given invite can be consumed at most once.
206
206
  *
207
207
  * @param db - The closed strand's database (founder already bootstrapped).
208
- * @param params - The issuing authority keypair and optional expiry.
208
+ * @param params - The issuing manager keypair and optional expiry.
209
209
  * @returns The invite public key and the out-of-band private seed.
210
210
  */
211
211
  export async function issueInvite(db, params) {
212
- const { authorityKeyPair, expiration } = params;
212
+ const { managerKeyPair, expiration } = params;
213
213
  const invitePrivateKey = generatePrivateKey('ed25519', 'base64url');
214
214
  const inviteKey = getPublicKey(invitePrivateKey, 'ed25519', 'base64url', 'base64url');
215
215
  // Canonicalise the expiry to the engine's stored `datetime` form so the signed
@@ -217,11 +217,11 @@ export async function issueInvite(db, params) {
217
217
  const expirationSegment = expiration == null ? '' : await canonicalDatetime(db, expiration);
218
218
  const expirationColumn = expiration == null ? null : expirationSegment;
219
219
  const payload = `${inviteKey}|${expirationSegment}`;
220
- const authoritySignature = signStrandPayload(payload, authorityKeyPair.privateKeyB64);
220
+ const managerSignature = signStrandPayload(payload, managerKeyPair.privateKeyB64);
221
221
  const inviteSignature = signStrandPayload(payload, invitePrivateKey);
222
222
  await db.exec(`insert into Strand.Invite (Key, Expiration)
223
- with context AuthorityKey = ?, AuthoritySignature = ?, InviteSignature = ?
224
- values (?, ?)`, [authorityKeyPair.publicKeyB64, authoritySignature, inviteSignature, inviteKey, expirationColumn]);
223
+ with context ManagerKey = ?, ManagerSignature = ?, InviteSignature = ?
224
+ values (?, ?)`, [managerKeyPair.publicKeyB64, managerSignature, inviteSignature, inviteKey, expirationColumn]);
225
225
  log('Issued invite %s (expires=%s)', inviteKey, expirationColumn ?? 'never');
226
226
  return { inviteKey, invitePrivateKey };
227
227
  }
@@ -278,9 +278,9 @@ export async function consumeInvite(db, params) {
278
278
  await db.beginTransaction();
279
279
  try {
280
280
  // 1. Member — admitted by the deferred invite branch (the matching
281
- // ConsumedInvite below), so no authority signature is supplied.
281
+ // ConsumedInvite below), so no manager signature is supplied.
282
282
  await db.exec(`insert into Strand.Member (Key)
283
- with context AuthorityKey = null, AuthoritySignature = null
283
+ with context ManagerKey = null, ManagerSignature = null
284
284
  values (?)`, [memberKey]);
285
285
  // 2. ConsumedInvite — proves possession of the invite private key and that the
286
286
  // invite has not expired (NotExpired gate against the canonical Now).
@@ -303,27 +303,27 @@ export async function consumeInvite(db, params) {
303
303
  }
304
304
  }
305
305
  /**
306
- * Admit a `Member` directly by authority signature — the sibling of the invite
307
- * path on `Member.Authorized`'s direct-authority branch.
306
+ * Admit a `Member` directly by manager signature — the sibling of the invite
307
+ * path on `Member.Authorized`'s direct-manager branch.
308
308
  *
309
- * The constraint verifies `digest(new.Key)` against an `Authority` row matching
310
- * `context.AuthorityKey`, so the payload is just the member key (no `'|'` join).
311
- * No `ConsumedInvite` is involved: this is the path an authority uses to seat a
312
- * member it already trusts (e.g. an authority-side enrollment that admits a
309
+ * The constraint verifies `digest(new.Key)` against a `Manager` row matching
310
+ * `context.ManagerKey`, so the payload is just the member key (no `'|'` join).
311
+ * No `ConsumedInvite` is involved: this is the path a manager uses to seat a
312
+ * member it already trusts (e.g. a manager-side enrollment that admits a
313
313
  * party already authorised out-of-band).
314
314
  *
315
315
  * @param db - The closed strand's database.
316
- * @param params - The admitting authority keypair and the new member key.
317
- * @throws If `Member.Authorized` rejects (e.g. a non-authority key); the insert
316
+ * @param params - The admitting manager keypair and the new member key.
317
+ * @throws If `Member.Authorized` rejects (e.g. a non-manager key); the insert
318
318
  * rolls back, leaving no `Member` row.
319
319
  */
320
- export async function addMemberByAuthority(db, params) {
321
- const { authorityKeyPair, memberKey } = params;
322
- const signature = signStrandPayload(memberKey, authorityKeyPair.privateKeyB64);
320
+ export async function addMemberByManager(db, params) {
321
+ const { managerKeyPair, memberKey } = params;
322
+ const signature = signStrandPayload(memberKey, managerKeyPair.privateKeyB64);
323
323
  await db.exec(`insert into Strand.Member (Key)
324
- with context AuthorityKey = ?, AuthoritySignature = ?
325
- values (?)`, [authorityKeyPair.publicKeyB64, signature, memberKey]);
326
- log('Admitted member %s by authority %s', memberKey, authorityKeyPair.publicKeyB64);
324
+ with context ManagerKey = ?, ManagerSignature = ?
325
+ values (?)`, [managerKeyPair.publicKeyB64, signature, memberKey]);
326
+ log('Admitted member %s by manager %s', memberKey, managerKeyPair.publicKeyB64);
327
327
  }
328
328
  /**
329
329
  * Register a network node (`PeerId`) as acting on behalf of a member.
@@ -373,77 +373,77 @@ async function memberPeerExists(db, memberKey, peerId) {
373
373
  return false;
374
374
  }
375
375
  /**
376
- * Promote a member to `Authority` on the signature of an existing authority.
376
+ * Promote a member to `Manager` on the signature of an existing manager.
377
377
  *
378
- * The existing-authority branch of `Authority.Authorized` verifies
378
+ * The existing-manager branch of `Manager.Authorized` verifies
379
379
  * `digest(coalesce(new.MemberKey, old.MemberKey))` — `new.MemberKey` =
380
- * `newAuthorityKey` on insert — against an `Authority` row matching
381
- * `context.AuthorityKey`. So the signer signs the new authority key directly (no
382
- * `'|'` join) and binds itself as `context.AuthorityKey`.
380
+ * `newManagerKey` on insert — against a `Manager` row matching
381
+ * `context.ManagerKey`. So the signer signs the new manager key directly (no
382
+ * `'|'` join) and binds itself as `context.ManagerKey`.
383
383
  *
384
- * Once the founder authority exists, the schema's `(select count(1) from Authority)
384
+ * Once the founder manager exists, the schema's `(select count(1) from Manager)
385
385
  * <= 1` bootstrap shortcut no longer applies to a second add (at commit the count
386
386
  * includes the new row, so it is ≥ 2), so this genuinely exercises signature
387
- * verification — a non-authority signer, or a signature over the wrong key, is
387
+ * verification — a non-manager signer, or a signature over the wrong key, is
388
388
  * rejected.
389
389
  *
390
- * @param db - The closed strand's database (founder authority already seated).
391
- * @param params - The authorizing authority's keypair and the new authority key.
392
- * @throws If `Authority.Authorized` rejects; the insert rolls back.
390
+ * @param db - The closed strand's database (founder manager already seated).
391
+ * @param params - The authorizing manager's keypair and the new manager key.
392
+ * @throws If `Manager.Authorized` rejects; the insert rolls back.
393
393
  */
394
- export async function addAuthority(db, params) {
395
- const { byAuthorityKeyPair, newAuthorityKey } = params;
396
- const signature = signStrandPayload(newAuthorityKey, byAuthorityKeyPair.privateKeyB64);
397
- await db.exec(`insert into Strand.Authority (MemberKey)
398
- with context AuthorityKey = ?, Signature = ?
399
- values (?)`, [byAuthorityKeyPair.publicKeyB64, signature, newAuthorityKey]);
400
- log('Added authority %s by %s', newAuthorityKey, byAuthorityKeyPair.publicKeyB64);
394
+ export async function addManager(db, params) {
395
+ const { byManagerKeyPair, newManagerKey } = params;
396
+ const signature = signStrandPayload(newManagerKey, byManagerKeyPair.privateKeyB64);
397
+ await db.exec(`insert into Strand.Manager (MemberKey)
398
+ with context ManagerKey = ?, Signature = ?
399
+ values (?)`, [byManagerKeyPair.publicKeyB64, signature, newManagerKey]);
400
+ log('Added manager %s by %s', newManagerKey, byManagerKeyPair.publicKeyB64);
401
401
  }
402
402
  /**
403
- * Remove an `Authority` row — either an admin removing a different authority or an
404
- * authority resigning itself.
403
+ * Remove a `Manager` row — either an admin removing a different manager or a
404
+ * manager resigning itself.
405
405
  *
406
406
  * On a DELETE, `coalesce(new.MemberKey, old.MemberKey)` binds `old.MemberKey =
407
- * targetAuthorityKey` (there is no `new` row), so the signed payload is the target
408
- * key for BOTH accepting branches of `Authority.Authorized`, and a single context
407
+ * targetManagerKey` (there is no `new` row), so the signed payload is the target
408
+ * key for BOTH accepting branches of `Manager.Authorized`, and a single context
409
409
  * construction serves both:
410
- * - **Admin removal**: `byAuthorityKeyPair` is a different existing authority; the
411
- * existing-authority branch verifies the signature against `A.MemberKey =
412
- * context.AuthorityKey`.
413
- * - **Self-resignation**: `byAuthorityKeyPair` IS the target (its `publicKeyB64`
414
- * equals `targetAuthorityKey`); the former-authority self branch verifies
415
- * `old.MemberKey = context.AuthorityKey` and the self-signature over `old.MemberKey`.
410
+ * - **Admin removal**: `byManagerKeyPair` is a different existing manager; the
411
+ * existing-manager branch verifies the signature against `A.MemberKey =
412
+ * context.ManagerKey`.
413
+ * - **Self-resignation**: `byManagerKeyPair` IS the target (its `publicKeyB64`
414
+ * equals `targetManagerKey`); the former-manager self branch verifies
415
+ * `old.MemberKey = context.ManagerKey` and the self-signature over `old.MemberKey`.
416
416
  *
417
417
  * The caller selects the case purely by which keypair it passes — no branching here.
418
418
  *
419
419
  * KNOWN PLATFORM GAP (not fixable here): the optimystic bootstrap-mode transactor
420
420
  * evaluates deferred (subquery-bearing) CHECK constraints only on INSERT, not on
421
- * DELETE. `Authority.Authorized` is deferred, so the platform currently accepts ANY
422
- * `Authority` delete regardless of signature — `removeAuthority`'s authorization is
421
+ * DELETE. `Manager.Authorized` is deferred, so the platform currently accepts ANY
422
+ * `Manager` delete regardless of signature — `removeManager`'s authorization is
423
423
  * effectively unenforced at runtime until that gap is closed (filed as
424
424
  * `optimystic-deferred-check-not-enforced-on-delete`). This writer still builds the
425
425
  * correct, signed delete so it works unchanged once enforcement lands — exactly as
426
426
  * the invite path issues correct inserts despite the open PK-uniqueness gap.
427
427
  *
428
- * KNOWN SCHEMA HAZARD (not guarded here): even with enforcement, `Authority.
429
- * Authorized`'s `(select count(1) from Authority) <= 1` bootstrap branch is true at
428
+ * KNOWN SCHEMA HAZARD (not guarded here): even with enforcement, `Manager.
429
+ * Authorized`'s `(select count(1) from Manager) <= 1` bootstrap branch is true at
430
430
  * commit whenever a delete drops the count to ≤ 1, so removing the strand's LAST (or
431
- * second-to-last) authority would be accepted regardless of signature — and removing
431
+ * second-to-last) manager would be accepted regardless of signature — and removing
432
432
  * the last one orphans the strand (no one can ever add another). The schema does not
433
- * prevent this; a "min-one-authority" invariant is deferred to a future schema change
433
+ * prevent this; a "min-one-manager" invariant is deferred to a future schema change
434
434
  * rather than grown into this writer.
435
435
  *
436
436
  * @param db - The closed strand's database.
437
- * @param params - The authorizing keypair and the target authority key.
438
- * @throws If `Authority.Authorized` rejects (e.g. a non-authority admin removal
439
- * while > 2 authorities remain); the delete rolls back.
437
+ * @param params - The authorizing keypair and the target manager key.
438
+ * @throws If `Manager.Authorized` rejects (e.g. a non-manager admin removal
439
+ * while > 2 managers remain); the delete rolls back.
440
440
  */
441
- export async function removeAuthority(db, params) {
442
- const { byAuthorityKeyPair, targetAuthorityKey } = params;
443
- const signature = signStrandPayload(targetAuthorityKey, byAuthorityKeyPair.privateKeyB64);
444
- await db.exec(`delete from Strand.Authority
445
- with context AuthorityKey = ?, Signature = ?
446
- where MemberKey = ?`, [byAuthorityKeyPair.publicKeyB64, signature, targetAuthorityKey]);
447
- log('Removed authority %s by %s', targetAuthorityKey, byAuthorityKeyPair.publicKeyB64);
441
+ export async function removeManager(db, params) {
442
+ const { byManagerKeyPair, targetManagerKey } = params;
443
+ const signature = signStrandPayload(targetManagerKey, byManagerKeyPair.privateKeyB64);
444
+ await db.exec(`delete from Strand.Manager
445
+ with context ManagerKey = ?, Signature = ?
446
+ where MemberKey = ?`, [byManagerKeyPair.publicKeyB64, signature, targetManagerKey]);
447
+ log('Removed manager %s by %s', targetManagerKey, byManagerKeyPair.publicKeyB64);
448
448
  }
449
449
  //# sourceMappingURL=strand-membership-writer.js.map