@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
@@ -10,7 +10,7 @@ const NEVER_EXPIRES = new Date(8640000000000000);
10
10
  * usage questions, and writes the consent row that records a redemption. Two
11
11
  * provisioning shapes are supported, keyed on whether the invite binds a host strand:
12
12
  *
13
- * - **Bound (provision-then-record):** the host strand already exists (authority-signed
13
+ * - **Bound (provision-then-record):** the host strand already exists (owner-signed
14
14
  * up front and named by the invite's `StrandId`), so {@link resolveStrand} reports it
15
15
  * and {@link recordUsage} writes the consent row against that pre-existing strand
16
16
  * (record-only) rather than inserting a new `Strand`.
@@ -67,7 +67,7 @@ export class ControlFormationUsageRecorder {
67
67
  * Record consent against an **already-existing** host strand (record-only): the
68
68
  * single `FormationUsage` insert auto-commits and the deferred `StrandExists`
69
69
  * CHECK is satisfied by the pre-existing strand. This is the provision-then-record
70
- * commitment — the strand was minted authority-signed up front, so we do NOT
70
+ * commitment — the strand was minted owner-signed up front, so we do NOT
71
71
  * re-insert it (which would double-insert the same PK). `initiatorKey` is carried
72
72
  * as the usage `PeerId` (advisory). Use {@link ControlDatabase.redeemInvitation}
73
73
  * for the consent-creates-strand path instead.
@@ -9,4 +9,4 @@
9
9
  * build if they drift. Any edit here must be mirrored in `schemas/control.qsql`
10
10
  * and vice versa.
11
11
  */
12
- export declare const CONTROL_SCHEMA = "-- This manages a Sereus party's cadre, or set of nodes, and their participation in strands (networks)\ndeclare schema CadreControl {\n -- A key that can authorize various control changes\n table AuthorityKey (\n Key text primary key,\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint Authorized check (\n -- Bootstrap: first authority key needs no existing authorization\n (select count(1) from AuthorityKey) <= 1\n\n -- Old authority authorizes by signing over THIS row (Key, StampId); single-use via unique StampId\n or (old.Key is not null and old.Key = context.AuthorityKey and verify(digest(new.Key, new.StampId), context.Signature, old.Key, 'ed25519'))\n\n -- or other authorities authorize by signing over THIS row (Key, StampId); single-use via unique StampId\n or exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (AuthorityKey text null, Signature text null);\n\n -- A key that can validate a strand formation disclosure\n table ValidationKey (\n Key text primary key,\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint Authorized check (\n -- Authorities authorize by signing over THIS row (Key, StampId); single-use via unique StampId\n exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (AuthorityKey text, Signature text);\n\n -- A network of members sharing an sApp database, each contributing peer nodes (their cadre) to the overall cohort\n -- Cadre peers should participate in each of these strands\n table Strand (\n Id text primary key, -- UUID\n MemberPrivateKey text null unique, -- Our private key as a member of this strand\n Type text, -- Types: 'o' = Open, 'c' = Closed -- Open can still control writes in the sApp, but only Closed controls reads\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint Authorized check (\n -- Authorized by an authority signing over THIS row (Id, Type, MemberPrivateKey, StampId); single-use via unique StampId\n exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n\n -- or authorized by a cadre peer who has received a valid formation invitation.\n -- That path has no signed writer yet; when one is built it must still supply a\n -- fresh, unique StampId to satisfy the not-null/unique anti-replay column.\n or exists (select 1 from FormationUsage FU where FU.StrandId = new.Id)\n ),\n constraint MemberKeyClosedOnly check (\n -- An open strand ('o') has no membership gate, so it must not carry a\n -- member key. A non-null MemberPrivateKey requires a closed strand ('c').\n new.MemberPrivateKey is null or new.Type = 'c'\n )\n ) with context (AuthorityKey text, Signature text);\n\n -- A peer (node) that is part of the cadre, carrying a self-published,\n -- freshness-stamped, self-signed address record (see PeerAddressRecord in cadre-core).\n -- The row IS the peer-address record: a resolver reads it, re-verifies Sig against\n -- PublicKey, checks freshness (UpdatedAt) and trust, then dials the addrs.\n table CadrePeer (\n PeerId text primary key,\n PublicKey text null, -- ed25519 (base64url) whose libp2p identity == PeerId (null if non-Ed25519)\n Multiaddr text, -- comma-joined current addrs (signaling / p2p-circuit first)\n UpdatedAt int null, -- epoch ms; strictly increases per self-update (replay/rollback guard)\n Sig text null, -- ed25519 self-signature over the signed payload (base64url); null until self-published\n constraint AuthorizedInsert check on insert, delete (\n -- Authorized by an authority key, which vouches both membership and the\n -- PublicKey<->PeerId binding (cadre-core derives PublicKey from PeerId before insert).\n -- The single-field digest frames the base58btc PeerId as TEXT, matching\n -- cadre-core peer-authorization.ts:peerAuthorizationDigest.\n exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(coalesce(new.PeerId, old.PeerId)), context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedUpdate check on update (\n -- Peer self-updates its own addrs + freshness, signing with its OWN ed25519 key\n -- (the key behind PeerId). PeerId/PublicKey are immutable on self-update, UpdatedAt\n -- must strictly increase (replay guard), and Sig is verified against the stored\n -- PublicKey over the same payload the publish path signs\n -- (cadre-core peer-record.ts:peerRecordSignedPayload).\n (\n new.PeerId = old.PeerId\n and new.PublicKey = old.PublicKey\n and new.UpdatedAt > coalesce(old.UpdatedAt, 0)\n and verify(\n digest(new.PeerId || '|' || new.Multiaddr || '|' || cast(new.UpdatedAt as text)),\n new.Sig, new.PublicKey, 'ed25519')\n )\n -- or an authority re-authorizes (rotation / correction)\n or exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.PeerId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (AuthorityKey text null, Signature text);\n\n -- A mobile cadre peer's platform push token (FCM/APNs), self-published so a\n -- server peer can deliver a push-wake to a suspended app over the platform push\n -- channel (a control-network dial cannot reach an OS-suspended process). The row\n -- mirrors CadrePeer: a resolver re-verifies Sig against the CadrePeer.PublicKey for\n -- this PeerId (see device-token.ts:deviceTokenSignedPayload), checks freshness, then\n -- hands the token to the push sender.\n table DeviceToken (\n PeerId text primary key, -- the CadrePeer this token belongs to\n Platform text not null, -- 'fcm' | 'apns'\n Token text not null, -- opaque platform device/registration token\n UpdatedAt int null, -- epoch ms; strictly increases per self-update (replay guard)\n Sig text null, -- ed25519 self-sig over (PeerId|Platform|Token|UpdatedAt)\n constraint AuthorizedInsert check on insert, delete (\n -- Authorized by an authority key (membership vouch), exactly like CadrePeer.\n -- The single-field digest frames the base58btc PeerId as TEXT.\n exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(coalesce(new.PeerId, old.PeerId)), context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedUpdate check on update (\n -- Peer self-updates its own token, signing with its OWN ed25519 key (the key\n -- behind PeerId). PeerId is immutable, UpdatedAt must strictly increase (replay\n -- guard), and Sig is verified against the stored CadrePeer.PublicKey over the\n -- signed payload (cadre-core device-token.ts:deviceTokenSignedPayload). Platform\n -- and Token may change on self-update (platform switch / reinstall / rotation).\n (\n new.PeerId = old.PeerId\n and new.UpdatedAt > coalesce(old.UpdatedAt, 0)\n and exists (select 1 from CadrePeer P where P.PeerId = new.PeerId and verify(\n digest(new.PeerId || '|' || new.Platform || '|' || new.Token || '|' || cast(new.UpdatedAt as text)),\n new.Sig, P.PublicKey, 'ed25519'))\n )\n -- or an authority re-authorizes (rotation / correction)\n or exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.PeerId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (AuthorityKey text null, Signature text);\n\n -- An open invitation to form a strand with this party\n table FormationInvite (\n Token text primary key, -- Just a random string\n sAppId text, -- The app for the strand that will be formed\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n ValidationUrl text null, -- Web hook - send disclosure, IP address...\n StrandId text null, -- Host strand this invite binds to (provision-then-record). Non-null => the responder records consent against this pre-existing strand; null => legacy responder-provisions path.\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint AuthorizedAddOrRemove check on insert, delete (\n -- Authorized by an authority signing over THIS row\n -- (Token, sAppId, ExpiresAt, TotalUses, ValidationUrl, StrandId, StampId); single-use via unique StampId.\n -- coalesce(new.F, old.F) binds the NEW row on insert and the OLD row on delete (cf. CadrePeer).\n -- StrandId is a nullable bound field: it signs as '' when absent (cf. ValidationUrl).\n exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(\n digest(\n coalesce(new.Token, old.Token),\n coalesce(new.sAppId, old.sAppId),\n coalesce(cast(coalesce(new.ExpiresAt, old.ExpiresAt) as text), ''),\n coalesce(cast(coalesce(new.TotalUses, old.TotalUses) as text), ''),\n coalesce(coalesce(new.ValidationUrl, old.ValidationUrl), ''),\n coalesce(coalesce(new.StrandId, old.StrandId), ''),\n coalesce(new.StampId, old.StampId)\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Invites are insert/delete only: forbid in-place mutation so the row-bound\n -- AuthorizedAddOrRemove signature cannot be sidestepped by updating consent\n -- parameters (TotalUses/ExpiresAt/...) after a legitimate insert. A bare check\n -- defaults to insert+update; the check on insert, delete above excludes update,\n -- so this guard is required. Mirrors FormationUsage.InsertOnly.\n constraint Immutable check on update (false)\n ) with context (AuthorityKey text, Signature text);\n\n table FormationUsage (\n Token text,\n UseNumber int,\n Disclosure text,\n StrandId text,\n primary key (Token, UseNumber),\n constraint InsertOnly check on update, delete (false),\n constraint Monotonic check (\n -- Read the PRE-transaction snapshot (committed.*): this CHECK auto-defers\n -- to commit (it has a subquery), by which point the row being inserted is\n -- live, so a plain from-FormationUsage would count this row itself and make\n -- UseNumber = max+1 unsatisfiable. committed.* excludes the in-flight row,\n -- giving sequential 1,2,3... per token (cross-transaction; concurrent\n -- redemptions of the same token collide on the (Token, UseNumber) PK).\n new.UseNumber = coalesce((select max(UseNumber) from committed.FormationUsage U where U.Token = new.Token), 0) + 1\n ),\n constraint Authorized check on insert (\n -- Satisfies an invitation\n exists (\n select 1 from FormationInvite FI\n where FI.Token = new.Token\n and (FI.TotalUses is null or FI.TotalUses >= new.UseNumber)\n and (FI.ExpiresAt is null or FI.ExpiresAt > context.Now)\n and (FI.ValidationUrl is null or verify(digest(new.Token || new.Disclosure), context.ValidationSignature, context.ValidationKey, 'ed25519'))\n )\n ),\n constraint StrandExists check (exists (select 1 from Strand S where S.Id = new.StrandId)),\n ) with context (PeerId text, PeerSignature text, Now datetime, ValidationKey text null, ValidationSignature text null);\n}\n\napply schema CadreControl;";
12
+ export declare const CONTROL_SCHEMA = "-- This manages a Sereus party's cadre, or set of nodes, and their participation in strands (networks)\ndeclare schema CadreControl {\n -- A key that can authorize various control changes\n table OwnerKey (\n Key text primary key,\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint Authorized check (\n -- Bootstrap: first owner key needs no existing authorization\n (select count(1) from OwnerKey) <= 1\n\n -- Old owner authorizes by signing over THIS row (Key, StampId); single-use via unique StampId\n or (old.Key is not null and old.Key = context.OwnerKey and verify(digest(new.Key, new.StampId), context.Signature, old.Key, 'ed25519'))\n\n -- or other owners authorize by signing over THIS row (Key, StampId); single-use via unique StampId\n or exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- A key that can validate a strand formation disclosure\n table ValidationKey (\n Key text primary key,\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint Authorized check (\n -- Owners authorize by signing over THIS row (Key, StampId); single-use via unique StampId\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A network of members sharing an sApp database, each contributing peer nodes (their cadre) to the overall cohort\n -- Cadre peers should participate in each of these strands\n table Strand (\n Id text primary key, -- UUID\n MemberPrivateKey text null unique, -- Our private key as a member of this strand\n Type text, -- Types: 'o' = Open, 'c' = Closed -- Open can still control writes in the sApp, but only Closed controls reads\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint Authorized check (\n -- Authorized by an owner signing over THIS row (Id, Type, MemberPrivateKey, StampId); single-use via unique StampId\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n\n -- or authorized by a cadre peer who has received a valid formation invitation.\n -- That path has no signed writer yet; when one is built it must still supply a\n -- fresh, unique StampId to satisfy the not-null/unique anti-replay column.\n or exists (select 1 from FormationUsage FU where FU.StrandId = new.Id)\n ),\n constraint MemberKeyClosedOnly check (\n -- An open strand ('o') has no membership gate, so it must not carry a\n -- member key. A non-null MemberPrivateKey requires a closed strand ('c').\n new.MemberPrivateKey is null or new.Type = 'c'\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A peer (node) that is part of the cadre, carrying a self-published,\n -- freshness-stamped, self-signed address record (see PeerAddressRecord in cadre-core).\n -- The row IS the peer-address record: a resolver reads it, re-verifies Sig against\n -- PublicKey, checks freshness (UpdatedAt) and trust, then dials the addrs.\n table CadrePeer (\n PeerId text primary key,\n PublicKey text null, -- ed25519 (base64url) whose libp2p identity == PeerId (null if non-Ed25519)\n Multiaddr text, -- comma-joined current addrs (signaling / p2p-circuit first)\n UpdatedAt int null, -- epoch ms; strictly increases per self-update (replay/rollback guard)\n Sig text null, -- ed25519 self-signature over the signed payload (base64url); null until self-published\n StampId text not null unique, -- single-use authorization nonce (anti-replay), bound into the voucher/remove digests; rotates on (re)insert\n VouchOwner text null, -- ed25519 (base64url) owner key that vouched this membership (== insert context.OwnerKey)\n VouchSig text null, -- that owner's signature over digest(PeerId, StampId) (== insert context.Signature). A reader checks VouchOwner against its NODE-LOCAL trusted-owner anchor (not this replicated table, which a self-owner can pollute) to decide authorized membership.\n constraint AuthorizedInsert check on insert (\n -- Authorized by an owner key, which vouches both membership and the\n -- PublicKey<->PeerId binding (cadre-core derives PublicKey from PeerId before insert).\n -- The digest binds PeerId + the single-use StampId nonce (two TEXT fields),\n -- matching cadre-core peer-authorization.ts:cadrePeerVoucherDigest.\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n -- Persist the vouching (owner, signature) onto the row so a reader can later\n -- re-check VouchOwner against its node-local anchor. The stored pair MUST equal\n -- the pair the verify above validated, so a writer cannot store a voucher it did\n -- not actually receive a signature for.\n and new.VouchOwner = context.OwnerKey\n and new.VouchSig = context.Signature\n ),\n constraint AuthorizedDelete check on delete (\n -- Delete is authorized by a signature over a DISTINCT 'remove'-scoped digest bound\n -- to the row's StampId (cadre-core peer-authorization.ts:cadrePeerRemoveDigest), so\n -- the stored voucher (a signature over digest(PeerId, StampId)) can NEVER be replayed\n -- to authorize a delete. The remove signature rides in context and is never stored.\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(old.PeerId, old.StampId, 'remove'), context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedUpdate check on update (\n -- Peer self-updates its own addrs + freshness, signing with its OWN ed25519 key\n -- (the key behind PeerId). PeerId/PublicKey/StampId/voucher are immutable on\n -- self-update, UpdatedAt must strictly increase (replay guard), and Sig is verified\n -- against the stored PublicKey over the same payload the publish path signs\n -- (cadre-core peer-record.ts:peerRecordSignedPayload).\n (\n new.PeerId = old.PeerId\n and new.PublicKey = old.PublicKey\n and new.StampId = old.StampId\n and new.VouchOwner = old.VouchOwner\n and new.VouchSig = old.VouchSig\n and new.UpdatedAt > coalesce(old.UpdatedAt, 0)\n and verify(\n digest(new.PeerId || '|' || new.Multiaddr || '|' || cast(new.UpdatedAt as text)),\n new.Sig, new.PublicKey, 'ed25519')\n )\n -- or an owner re-authorizes (rotation / correction): re-vouches the row over\n -- its current StampId, so the stored voucher is re-bound to the re-authorizing owner.\n or (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and new.VouchOwner = context.OwnerKey\n and new.VouchSig = context.Signature\n )\n )\n ) with context (OwnerKey text null, Signature text);\n\n -- A mobile cadre peer's platform push token (FCM/APNs), self-published so a\n -- server peer can deliver a push-wake to a suspended app over the platform push\n -- channel (a control-network dial cannot reach an OS-suspended process). The row\n -- mirrors CadrePeer: a resolver re-verifies Sig against the CadrePeer.PublicKey for\n -- this PeerId (see device-token.ts:deviceTokenSignedPayload), checks freshness, then\n -- hands the token to the push sender.\n table DeviceToken (\n PeerId text primary key, -- the CadrePeer this token belongs to\n Platform text not null, -- 'fcm' | 'apns'\n Token text not null, -- opaque platform device/registration token\n UpdatedAt int null, -- epoch ms; strictly increases per self-update (replay guard)\n Sig text null, -- ed25519 self-sig over (PeerId|Platform|Token|UpdatedAt)\n constraint AuthorizedInsert check on insert, delete (\n -- Authorized by an owner key (membership vouch), exactly like CadrePeer.\n -- The single-field digest frames the base58btc PeerId as TEXT.\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(coalesce(new.PeerId, old.PeerId)), context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedUpdate check on update (\n -- Peer self-updates its own token, signing with its OWN ed25519 key (the key\n -- behind PeerId). PeerId is immutable, UpdatedAt must strictly increase (replay\n -- guard), and Sig is verified against the stored CadrePeer.PublicKey over the\n -- signed payload (cadre-core device-token.ts:deviceTokenSignedPayload). Platform\n -- and Token may change on self-update (platform switch / reinstall / rotation).\n (\n new.PeerId = old.PeerId\n and new.UpdatedAt > coalesce(old.UpdatedAt, 0)\n and exists (select 1 from CadrePeer P where P.PeerId = new.PeerId and verify(\n digest(new.PeerId || '|' || new.Platform || '|' || new.Token || '|' || cast(new.UpdatedAt as text)),\n new.Sig, P.PublicKey, 'ed25519'))\n )\n -- or an owner re-authorizes (rotation / correction)\n or exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.PeerId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text null, Signature text);\n\n -- An open invitation to form a strand with this party\n table FormationInvite (\n Token text primary key, -- Just a random string\n sAppId text, -- The app for the strand that will be formed\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n ValidationUrl text null, -- Web hook - send disclosure, IP address...\n StrandId text null, -- Host strand this invite binds to (provision-then-record). Non-null => the responder records consent against this pre-existing strand; null => legacy responder-provisions path.\n StampId text not null unique, -- single-use authorization nonce (anti-replay)\n constraint AuthorizedAddOrRemove check on insert, delete (\n -- Authorized by an owner signing over THIS row\n -- (Token, sAppId, ExpiresAt, TotalUses, ValidationUrl, StrandId, StampId); single-use via unique StampId.\n -- coalesce(new.F, old.F) binds the NEW row on insert and the OLD row on delete (cf. CadrePeer).\n -- StrandId is a nullable bound field: it signs as '' when absent (cf. ValidationUrl).\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(\n digest(\n coalesce(new.Token, old.Token),\n coalesce(new.sAppId, old.sAppId),\n coalesce(cast(coalesce(new.ExpiresAt, old.ExpiresAt) as text), ''),\n coalesce(cast(coalesce(new.TotalUses, old.TotalUses) as text), ''),\n coalesce(coalesce(new.ValidationUrl, old.ValidationUrl), ''),\n coalesce(coalesce(new.StrandId, old.StrandId), ''),\n coalesce(new.StampId, old.StampId)\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Invites are insert/delete only: forbid in-place mutation so the row-bound\n -- AuthorizedAddOrRemove signature cannot be sidestepped by updating consent\n -- parameters (TotalUses/ExpiresAt/...) after a legitimate insert. A bare check\n -- defaults to insert+update; the check on insert, delete above excludes update,\n -- so this guard is required. Mirrors FormationUsage.InsertOnly.\n constraint Immutable check on update (false)\n ) with context (OwnerKey text, Signature text);\n\n table FormationUsage (\n Token text,\n UseNumber int,\n Disclosure text,\n StrandId text,\n primary key (Token, UseNumber),\n constraint InsertOnly check on update, delete (false),\n constraint Monotonic check (\n -- Read the PRE-transaction snapshot (committed.*): this CHECK auto-defers\n -- to commit (it has a subquery), by which point the row being inserted is\n -- live, so a plain from-FormationUsage would count this row itself and make\n -- UseNumber = max+1 unsatisfiable. committed.* excludes the in-flight row,\n -- giving sequential 1,2,3... per token (cross-transaction; concurrent\n -- redemptions of the same token collide on the (Token, UseNumber) PK).\n new.UseNumber = coalesce((select max(UseNumber) from committed.FormationUsage U where U.Token = new.Token), 0) + 1\n ),\n constraint Authorized check on insert (\n -- Satisfies an invitation\n exists (\n select 1 from FormationInvite FI\n where FI.Token = new.Token\n and (FI.TotalUses is null or FI.TotalUses >= new.UseNumber)\n and (FI.ExpiresAt is null or FI.ExpiresAt > context.Now)\n and (FI.ValidationUrl is null or verify(digest(new.Token || new.Disclosure), context.ValidationSignature, context.ValidationKey, 'ed25519'))\n )\n ),\n constraint StrandExists check (exists (select 1 from Strand S where S.Id = new.StrandId)),\n ) with context (PeerId text, PeerSignature text, Now datetime, ValidationKey text null, ValidationSignature text null);\n}\n\napply schema CadreControl;";
@@ -12,30 +12,30 @@
12
12
  export const CONTROL_SCHEMA = `-- This manages a Sereus party's cadre, or set of nodes, and their participation in strands (networks)
13
13
  declare schema CadreControl {
14
14
  -- A key that can authorize various control changes
15
- table AuthorityKey (
15
+ table OwnerKey (
16
16
  Key text primary key,
17
17
  StampId text not null unique, -- single-use authorization nonce (anti-replay)
18
18
  constraint Authorized check (
19
- -- Bootstrap: first authority key needs no existing authorization
20
- (select count(1) from AuthorityKey) <= 1
19
+ -- Bootstrap: first owner key needs no existing authorization
20
+ (select count(1) from OwnerKey) <= 1
21
21
 
22
- -- Old authority authorizes by signing over THIS row (Key, StampId); single-use via unique StampId
23
- or (old.Key is not null and old.Key = context.AuthorityKey and verify(digest(new.Key, new.StampId), context.Signature, old.Key, 'ed25519'))
22
+ -- Old owner authorizes by signing over THIS row (Key, StampId); single-use via unique StampId
23
+ or (old.Key is not null and old.Key = context.OwnerKey and verify(digest(new.Key, new.StampId), context.Signature, old.Key, 'ed25519'))
24
24
 
25
- -- or other authorities authorize by signing over THIS row (Key, StampId); single-use via unique StampId
26
- or exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))
25
+ -- or other owners authorize by signing over THIS row (Key, StampId); single-use via unique StampId
26
+ or exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))
27
27
  )
28
- ) with context (AuthorityKey text null, Signature text null);
28
+ ) with context (OwnerKey text null, Signature text null);
29
29
 
30
30
  -- A key that can validate a strand formation disclosure
31
31
  table ValidationKey (
32
32
  Key text primary key,
33
33
  StampId text not null unique, -- single-use authorization nonce (anti-replay)
34
34
  constraint Authorized check (
35
- -- Authorities authorize by signing over THIS row (Key, StampId); single-use via unique StampId
36
- exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))
35
+ -- Owners authorize by signing over THIS row (Key, StampId); single-use via unique StampId
36
+ exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))
37
37
  )
38
- ) with context (AuthorityKey text, Signature text);
38
+ ) with context (OwnerKey text, Signature text);
39
39
 
40
40
  -- A network of members sharing an sApp database, each contributing peer nodes (their cadre) to the overall cohort
41
41
  -- Cadre peers should participate in each of these strands
@@ -45,8 +45,8 @@ declare schema CadreControl {
45
45
  Type text, -- Types: 'o' = Open, 'c' = Closed -- Open can still control writes in the sApp, but only Closed controls reads
46
46
  StampId text not null unique, -- single-use authorization nonce (anti-replay)
47
47
  constraint Authorized check (
48
- -- Authorized by an authority signing over THIS row (Id, Type, MemberPrivateKey, StampId); single-use via unique StampId
49
- exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))
48
+ -- Authorized by an owner signing over THIS row (Id, Type, MemberPrivateKey, StampId); single-use via unique StampId
49
+ exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))
50
50
 
51
51
  -- or authorized by a cadre peer who has received a valid formation invitation.
52
52
  -- That path has no signed writer yet; when one is built it must still supply a
@@ -58,7 +58,7 @@ declare schema CadreControl {
58
58
  -- member key. A non-null MemberPrivateKey requires a closed strand ('c').
59
59
  new.MemberPrivateKey is null or new.Type = 'c'
60
60
  )
61
- ) with context (AuthorityKey text, Signature text);
61
+ ) with context (OwnerKey text, Signature text);
62
62
 
63
63
  -- A peer (node) that is part of the cadre, carrying a self-published,
64
64
  -- freshness-stamped, self-signed address record (see PeerAddressRecord in cadre-core).
@@ -70,31 +70,55 @@ declare schema CadreControl {
70
70
  Multiaddr text, -- comma-joined current addrs (signaling / p2p-circuit first)
71
71
  UpdatedAt int null, -- epoch ms; strictly increases per self-update (replay/rollback guard)
72
72
  Sig text null, -- ed25519 self-signature over the signed payload (base64url); null until self-published
73
- constraint AuthorizedInsert check on insert, delete (
74
- -- Authorized by an authority key, which vouches both membership and the
73
+ StampId text not null unique, -- single-use authorization nonce (anti-replay), bound into the voucher/remove digests; rotates on (re)insert
74
+ VouchOwner text null, -- ed25519 (base64url) owner key that vouched this membership (== insert context.OwnerKey)
75
+ VouchSig text null, -- that owner's signature over digest(PeerId, StampId) (== insert context.Signature). A reader checks VouchOwner against its NODE-LOCAL trusted-owner anchor (not this replicated table, which a self-owner can pollute) to decide authorized membership.
76
+ constraint AuthorizedInsert check on insert (
77
+ -- Authorized by an owner key, which vouches both membership and the
75
78
  -- PublicKey<->PeerId binding (cadre-core derives PublicKey from PeerId before insert).
76
- -- The single-field digest frames the base58btc PeerId as TEXT, matching
77
- -- cadre-core peer-authorization.ts:peerAuthorizationDigest.
78
- exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(coalesce(new.PeerId, old.PeerId)), context.Signature, A.Key, 'ed25519'))
79
+ -- The digest binds PeerId + the single-use StampId nonce (two TEXT fields),
80
+ -- matching cadre-core peer-authorization.ts:cadrePeerVoucherDigest.
81
+ exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))
82
+ -- Persist the vouching (owner, signature) onto the row so a reader can later
83
+ -- re-check VouchOwner against its node-local anchor. The stored pair MUST equal
84
+ -- the pair the verify above validated, so a writer cannot store a voucher it did
85
+ -- not actually receive a signature for.
86
+ and new.VouchOwner = context.OwnerKey
87
+ and new.VouchSig = context.Signature
88
+ ),
89
+ constraint AuthorizedDelete check on delete (
90
+ -- Delete is authorized by a signature over a DISTINCT 'remove'-scoped digest bound
91
+ -- to the row's StampId (cadre-core peer-authorization.ts:cadrePeerRemoveDigest), so
92
+ -- the stored voucher (a signature over digest(PeerId, StampId)) can NEVER be replayed
93
+ -- to authorize a delete. The remove signature rides in context and is never stored.
94
+ exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(old.PeerId, old.StampId, 'remove'), context.Signature, A.Key, 'ed25519'))
79
95
  ),
80
96
  constraint AuthorizedUpdate check on update (
81
97
  -- Peer self-updates its own addrs + freshness, signing with its OWN ed25519 key
82
- -- (the key behind PeerId). PeerId/PublicKey are immutable on self-update, UpdatedAt
83
- -- must strictly increase (replay guard), and Sig is verified against the stored
84
- -- PublicKey over the same payload the publish path signs
98
+ -- (the key behind PeerId). PeerId/PublicKey/StampId/voucher are immutable on
99
+ -- self-update, UpdatedAt must strictly increase (replay guard), and Sig is verified
100
+ -- against the stored PublicKey over the same payload the publish path signs
85
101
  -- (cadre-core peer-record.ts:peerRecordSignedPayload).
86
102
  (
87
103
  new.PeerId = old.PeerId
88
104
  and new.PublicKey = old.PublicKey
105
+ and new.StampId = old.StampId
106
+ and new.VouchOwner = old.VouchOwner
107
+ and new.VouchSig = old.VouchSig
89
108
  and new.UpdatedAt > coalesce(old.UpdatedAt, 0)
90
109
  and verify(
91
110
  digest(new.PeerId || '|' || new.Multiaddr || '|' || cast(new.UpdatedAt as text)),
92
111
  new.Sig, new.PublicKey, 'ed25519')
93
112
  )
94
- -- or an authority re-authorizes (rotation / correction)
95
- or exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.PeerId), context.Signature, A.Key, 'ed25519'))
113
+ -- or an owner re-authorizes (rotation / correction): re-vouches the row over
114
+ -- its current StampId, so the stored voucher is re-bound to the re-authorizing owner.
115
+ or (
116
+ exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))
117
+ and new.VouchOwner = context.OwnerKey
118
+ and new.VouchSig = context.Signature
119
+ )
96
120
  )
97
- ) with context (AuthorityKey text null, Signature text);
121
+ ) with context (OwnerKey text null, Signature text);
98
122
 
99
123
  -- A mobile cadre peer's platform push token (FCM/APNs), self-published so a
100
124
  -- server peer can deliver a push-wake to a suspended app over the platform push
@@ -109,9 +133,9 @@ declare schema CadreControl {
109
133
  UpdatedAt int null, -- epoch ms; strictly increases per self-update (replay guard)
110
134
  Sig text null, -- ed25519 self-sig over (PeerId|Platform|Token|UpdatedAt)
111
135
  constraint AuthorizedInsert check on insert, delete (
112
- -- Authorized by an authority key (membership vouch), exactly like CadrePeer.
136
+ -- Authorized by an owner key (membership vouch), exactly like CadrePeer.
113
137
  -- The single-field digest frames the base58btc PeerId as TEXT.
114
- exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(coalesce(new.PeerId, old.PeerId)), context.Signature, A.Key, 'ed25519'))
138
+ exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(coalesce(new.PeerId, old.PeerId)), context.Signature, A.Key, 'ed25519'))
115
139
  ),
116
140
  constraint AuthorizedUpdate check on update (
117
141
  -- Peer self-updates its own token, signing with its OWN ed25519 key (the key
@@ -126,10 +150,10 @@ declare schema CadreControl {
126
150
  digest(new.PeerId || '|' || new.Platform || '|' || new.Token || '|' || cast(new.UpdatedAt as text)),
127
151
  new.Sig, P.PublicKey, 'ed25519'))
128
152
  )
129
- -- or an authority re-authorizes (rotation / correction)
130
- or exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(digest(new.PeerId), context.Signature, A.Key, 'ed25519'))
153
+ -- or an owner re-authorizes (rotation / correction)
154
+ or exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(digest(new.PeerId), context.Signature, A.Key, 'ed25519'))
131
155
  )
132
- ) with context (AuthorityKey text null, Signature text);
156
+ ) with context (OwnerKey text null, Signature text);
133
157
 
134
158
  -- An open invitation to form a strand with this party
135
159
  table FormationInvite (
@@ -141,11 +165,11 @@ declare schema CadreControl {
141
165
  StrandId text null, -- Host strand this invite binds to (provision-then-record). Non-null => the responder records consent against this pre-existing strand; null => legacy responder-provisions path.
142
166
  StampId text not null unique, -- single-use authorization nonce (anti-replay)
143
167
  constraint AuthorizedAddOrRemove check on insert, delete (
144
- -- Authorized by an authority signing over THIS row
168
+ -- Authorized by an owner signing over THIS row
145
169
  -- (Token, sAppId, ExpiresAt, TotalUses, ValidationUrl, StrandId, StampId); single-use via unique StampId.
146
170
  -- coalesce(new.F, old.F) binds the NEW row on insert and the OLD row on delete (cf. CadrePeer).
147
171
  -- StrandId is a nullable bound field: it signs as '' when absent (cf. ValidationUrl).
148
- exists (select 1 from AuthorityKey A where A.Key = context.AuthorityKey and verify(
172
+ exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and verify(
149
173
  digest(
150
174
  coalesce(new.Token, old.Token),
151
175
  coalesce(new.sAppId, old.sAppId),
@@ -163,7 +187,7 @@ declare schema CadreControl {
163
187
  -- defaults to insert+update; the check on insert, delete above excludes update,
164
188
  -- so this guard is required. Mirrors FormationUsage.InsertOnly.
165
189
  constraint Immutable check on update (false)
166
- ) with context (AuthorityKey text, Signature text);
190
+ ) with context (OwnerKey text, Signature text);
167
191
 
168
192
  table FormationUsage (
169
193
  Token text,
@@ -1 +1 @@
1
- {"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BA0LH,CAAC"}
1
+ {"version":3,"file":"control-schema.js","sourceRoot":"","sources":["../src/control-schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2BAkNH,CAAC"}
@@ -36,7 +36,7 @@ export declare function deviceTokenSignedPayload(record: Omit<DeviceTokenRecord,
36
36
  * Returns a fully-populated {@link DeviceTokenRecord}.
37
37
  *
38
38
  * @param fields - the record fields to sign
39
- * @param privateKeyB64 - base64url ed25519 seed (see `authorityKeyFromLibp2p`)
39
+ * @param privateKeyB64 - base64url ed25519 seed (see `ed25519KeyPairFromLibp2p`)
40
40
  */
41
41
  export declare function signDeviceTokenRecord(fields: Omit<DeviceTokenRecord, 'sig'>, privateKeyB64: string): DeviceTokenRecord;
42
42
  /**
@@ -39,7 +39,7 @@ export function deviceTokenSignedPayload(record) {
39
39
  * Returns a fully-populated {@link DeviceTokenRecord}.
40
40
  *
41
41
  * @param fields - the record fields to sign
42
- * @param privateKeyB64 - base64url ed25519 seed (see `authorityKeyFromLibp2p`)
42
+ * @param privateKeyB64 - base64url ed25519 seed (see `ed25519KeyPairFromLibp2p`)
43
43
  */
44
44
  export function signDeviceTokenRecord(fields, privateKeyB64) {
45
45
  const payloadDigest = deviceTokenSignedPayload(fields);
@@ -1,9 +1,11 @@
1
1
  import type { PrivateKey } from '@libp2p/interface';
2
2
  /**
3
- * A household authority keypair expressed in the base64url Ed25519 form that
3
+ * An Ed25519 keypair expressed in the base64url form that
4
4
  * `@optimystic/quereus-plugin-crypto` (`sign`/`verify`/`getPublicKey`) consumes.
5
+ * Generic bridge type — reused for owner keys, member keys, and any other
6
+ * seed→public keypair the control/strand databases sign with.
5
7
  */
6
- export interface AuthorityKeyPair {
8
+ export interface Ed25519KeyPair {
7
9
  /** 32-byte Ed25519 seed, base64url-encoded — the crypto-plugin private key. */
8
10
  privateKeyB64: string;
9
11
  /** 32-byte Ed25519 public key, base64url-encoded. */
@@ -11,31 +13,30 @@ export interface AuthorityKeyPair {
11
13
  }
12
14
  /**
13
15
  * Bridge a libp2p Ed25519 private key into the base64url keypair used by the
14
- * control-database authority constraints.
16
+ * control- and strand-database signing constraints.
15
17
  *
16
18
  * libp2p stores an Ed25519 private key as 64 raw bytes: the first 32 are the
17
19
  * seed (the actual scalar source), the last 32 are the public key — see
18
20
  * `@libp2p/crypto`'s `Ed25519PrivateKey`. `@optimystic/quereus-plugin-crypto`
19
21
  * (via `@noble/curves`) treats the 32-byte seed *as* the private key and
20
22
  * derives the public key from it with standard Ed25519. The two derivations
21
- * agree, so the node's peer identity and its authority key are one keypair:
23
+ * agree, so the node's peer identity and its signing key are one keypair:
22
24
  * `getPublicKey(privateKeyB64)` === `publicKeyB64`.
23
25
  *
24
26
  * @param privateKey - The node's libp2p Ed25519 private key.
25
- * @returns The base64url seed/public-key pair for authority operations.
27
+ * @returns The base64url seed/public-key pair for signing operations.
26
28
  * @throws If the key is not Ed25519 or the raw bytes aren't the expected length.
27
29
  */
28
- export declare function authorityKeyFromLibp2p(privateKey: PrivateKey): AuthorityKeyPair;
30
+ export declare function ed25519KeyPairFromLibp2p(privateKey: PrivateKey): Ed25519KeyPair;
29
31
  /**
30
32
  * Derive the base64url Ed25519 public key from a base64url 32-byte private seed
31
33
  * — the same derivation the seed-bootstrap signer uses internally
32
34
  * (`SeedBootstrapService` constructor). Use this to enroll a standalone
33
- * (non-libp2p) authority key into the control DB via
34
- * `ControlDatabase.ensureAuthorityKey` before minting an invite, when the
35
- * authority key is *not* the node's peer identity (so `authorityKeyFromLibp2p`,
35
+ * (non-libp2p) key into the control DB before minting an invite, when the
36
+ * key is *not* the node's peer identity (so `ed25519KeyPairFromLibp2p`,
36
37
  * which needs a libp2p key object, does not apply).
37
38
  *
38
39
  * @param privateKeyB64 - The base64url-encoded 32-byte Ed25519 seed.
39
40
  * @returns The base64url-encoded Ed25519 public key.
40
41
  */
41
- export declare function authorityPublicKeyFromPrivate(privateKeyB64: string): string;
42
+ export declare function ed25519PublicKeyFromPrivate(privateKeyB64: string): string;
@@ -2,23 +2,23 @@ import { toString as uint8ArrayToString } from 'uint8arrays';
2
2
  import { getPublicKey } from '@optimystic/quereus-plugin-crypto';
3
3
  /**
4
4
  * Bridge a libp2p Ed25519 private key into the base64url keypair used by the
5
- * control-database authority constraints.
5
+ * control- and strand-database signing constraints.
6
6
  *
7
7
  * libp2p stores an Ed25519 private key as 64 raw bytes: the first 32 are the
8
8
  * seed (the actual scalar source), the last 32 are the public key — see
9
9
  * `@libp2p/crypto`'s `Ed25519PrivateKey`. `@optimystic/quereus-plugin-crypto`
10
10
  * (via `@noble/curves`) treats the 32-byte seed *as* the private key and
11
11
  * derives the public key from it with standard Ed25519. The two derivations
12
- * agree, so the node's peer identity and its authority key are one keypair:
12
+ * agree, so the node's peer identity and its signing key are one keypair:
13
13
  * `getPublicKey(privateKeyB64)` === `publicKeyB64`.
14
14
  *
15
15
  * @param privateKey - The node's libp2p Ed25519 private key.
16
- * @returns The base64url seed/public-key pair for authority operations.
16
+ * @returns The base64url seed/public-key pair for signing operations.
17
17
  * @throws If the key is not Ed25519 or the raw bytes aren't the expected length.
18
18
  */
19
- export function authorityKeyFromLibp2p(privateKey) {
19
+ export function ed25519KeyPairFromLibp2p(privateKey) {
20
20
  if (privateKey.type !== 'Ed25519') {
21
- throw new Error(`authorityKeyFromLibp2p requires an Ed25519 key, got ${privateKey.type}`);
21
+ throw new Error(`ed25519KeyPairFromLibp2p requires an Ed25519 key, got ${privateKey.type}`);
22
22
  }
23
23
  const raw = privateKey.raw;
24
24
  if (raw.length !== 64) {
@@ -38,15 +38,14 @@ export function authorityKeyFromLibp2p(privateKey) {
38
38
  * Derive the base64url Ed25519 public key from a base64url 32-byte private seed
39
39
  * — the same derivation the seed-bootstrap signer uses internally
40
40
  * (`SeedBootstrapService` constructor). Use this to enroll a standalone
41
- * (non-libp2p) authority key into the control DB via
42
- * `ControlDatabase.ensureAuthorityKey` before minting an invite, when the
43
- * authority key is *not* the node's peer identity (so `authorityKeyFromLibp2p`,
41
+ * (non-libp2p) key into the control DB before minting an invite, when the
42
+ * key is *not* the node's peer identity (so `ed25519KeyPairFromLibp2p`,
44
43
  * which needs a libp2p key object, does not apply).
45
44
  *
46
45
  * @param privateKeyB64 - The base64url-encoded 32-byte Ed25519 seed.
47
46
  * @returns The base64url-encoded Ed25519 public key.
48
47
  */
49
- export function authorityPublicKeyFromPrivate(privateKeyB64) {
48
+ export function ed25519PublicKeyFromPrivate(privateKeyB64) {
50
49
  return getPublicKey(privateKeyB64, 'ed25519', 'base64url', 'base64url');
51
50
  }
52
- //# sourceMappingURL=authority-key.js.map
51
+ //# sourceMappingURL=ed25519-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ed25519-key.js","sourceRoot":"","sources":["../src/ed25519-key.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAC;AAgBjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,wBAAwB,CAAC,UAAsB;IAC7D,IAAI,UAAU,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,yDAAyD,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC;IAC3B,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,kEAAkE,GAAG,CAAC,MAAM,QAAQ,CAAC,CAAC;IACxG,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACjC,MAAM,YAAY,GAAG,UAAU,CAAC,SAAS,CAAC,GAAG,CAAC;IAC9C,IAAI,YAAY,CAAC,MAAM,KAAK,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,8CAA8C,YAAY,CAAC,MAAM,QAAQ,CAAC,CAAC;IAC7F,CAAC;IAED,OAAO;QACL,aAAa,EAAE,kBAAkB,CAAC,IAAI,EAAE,WAAW,CAAC;QACpD,YAAY,EAAE,kBAAkB,CAAC,YAAY,EAAE,WAAW,CAAC;KAC5D,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,2BAA2B,CAAC,aAAqB;IAC/D,OAAO,YAAY,CAAC,aAAa,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,CAAW,CAAC;AACpF,CAAC"}
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ export * from './types.js';
2
2
  export { canonicalJson } from './canonical-json.js';
3
3
  export { CadreNode } from './cadre-node.js';
4
4
  export { ControlDatabase, buildAuthorizationMessage, type ControlDatabaseConfig, type ControlTable } from './control-database.js';
5
- export { authorityKeyFromLibp2p, authorityPublicKeyFromPrivate, type AuthorityKeyPair } from './authority-key.js';
5
+ export { ed25519KeyPairFromLibp2p, ed25519PublicKeyFromPrivate, type Ed25519KeyPair } from './ed25519-key.js';
6
6
  export { InMemoryKeyStore, KeyStoreAccessError, DEFAULT_IDENTITY_KEY_ID, type KeyStore, type KeyId } from './key-store.js';
7
7
  export { StrandDatabase, type StrandDatabaseConfig } from './strand-database.js';
8
8
  export { StrandWatcher, type StrandWatcherCallbacks, type StrandQueryable, type SAppIdLookup, type StrandRowWithApp } from './strand-watcher.js';
@@ -13,11 +13,11 @@ export { EnrollmentService, type MemberVerifier, type MemberRegistry } from './e
13
13
  export { StrandSolicitationService, createDefaultFormationResponseValidator, type DisclosureValidator, type FormationUsageRecorder, type ResolvedHostStrand, type StrandProvisioner, type FormationSigner, type FormationResponseValidator, type StrandSolicitationServiceOptions } from './strand-solicitation.js';
14
14
  export { ControlFormationUsageRecorder } from './control-formation-recorder.js';
15
15
  export { generateStrandMemberKey, strandMemberKeyPair } from './strand-member-key.js';
16
- export { signStrandPayload, verifyStrandPayload, bootstrapFounderMembership, issueInvite, consumeInvite, addMemberByAuthority, registerMemberPeer, addAuthority, removeAuthority, STRAND_ENGINE, STRAND_ENGINE_VERSION, type FounderBootstrapParams, type IssueInviteParams, type IssuedInvite, type ConsumeInviteParams, type AddMemberByAuthorityParams, type RegisterMemberPeerParams, type AddAuthorityParams, type RemoveAuthorityParams } from './strand-membership-writer.js';
16
+ export { signStrandPayload, verifyStrandPayload, bootstrapFounderMembership, issueInvite, consumeInvite, addMemberByManager, registerMemberPeer, addManager, removeManager, STRAND_ENGINE, STRAND_ENGINE_VERSION, type FounderBootstrapParams, type IssueInviteParams, type IssuedInvite, type ConsumeInviteParams, type AddMemberByManagerParams, type RegisterMemberPeerParams, type AddManagerParams, type RemoveManagerParams } from './strand-membership-writer.js';
17
17
  export { canonicalDatetime } from './canonical-datetime.js';
18
18
  export { StrandMemberRegistry, StrandMemberVerifier, memberRegistrationPayload, type StrandAdmission } from './strand-member-registry.js';
19
19
  export { SeedBootstrapService, SEED_PROTOCOL, ed25519PublicKeyB64FromPeerId, type SeedBootstrapConfig, type SeedEventCallbacks } from './seed-bootstrap.js';
20
- export { peerAuthorizationDigest, verifyPeerAuthorization } from './peer-authorization.js';
20
+ export { peerAuthorizationDigest, cadrePeerVoucherDigest, cadrePeerRemoveDigest, verifyPeerAuthorization } from './peer-authorization.js';
21
21
  export { StrandWakeService, dialWake, WAKE_PROTOCOL, type StrandWakeServiceOptions, type DialWakeOptions } from './strand-wake-protocol.js';
22
22
  export { StrandAddrService, collectStrandAddrs, STRAND_ADDR_PROTOCOL, type StrandAddrServiceOptions, type StrandAddrPeer, type CollectStrandAddrsOptions } from './strand-addr-protocol.js';
23
23
  export { peerRecordSignedPayload, signPeerRecord, verifyPeerRecordSignature, isPeerRecordFresh, isSignalingAddr, orderSignalingFirst, currentMemberTrustPolicy, DEFAULT_PEER_RECORD_MAX_AGE_MS, DEFAULT_PEER_RECORD_HEARTBEAT_MS } from './peer-record.js';
package/dist/index.js CHANGED
@@ -6,9 +6,9 @@ export { canonicalJson } from './canonical-json.js';
6
6
  export { CadreNode } from './cadre-node.js';
7
7
  // Control database
8
8
  export { ControlDatabase, buildAuthorizationMessage } from './control-database.js';
9
- // Authority key bridge (libp2p Ed25519 -> base64url authority keypair)
10
- export { authorityKeyFromLibp2p, authorityPublicKeyFromPrivate } from './authority-key.js';
11
- // Pluggable key store (backend-agnostic identity/authority key material seam).
9
+ // Ed25519 key bridge (libp2p Ed25519 -> base64url keypair)
10
+ export { ed25519KeyPairFromLibp2p, ed25519PublicKeyFromPrivate } from './ed25519-key.js';
11
+ // Pluggable key store (backend-agnostic identity/owner key material seam).
12
12
  // Dependency-free: the interface, error, default slot id, and in-memory backend
13
13
  // are safe in every (RN/browser/Node) entry graph. The Node FileKeyStore is a
14
14
  // separate subpath module ('@serfab/cadre-core/key-store-file') so its node:fs
@@ -30,12 +30,12 @@ export { StrandSolicitationService, createDefaultFormationResponseValidator } fr
30
30
  // DB-backed FormationUsageRecorder (reads/writes the real CadreControl tables)
31
31
  export { ControlFormationUsageRecorder } from './control-formation-recorder.js';
32
32
  // Closed-strand member key generation (ed25519 protobuf, base64) + the
33
- // protobuf->base64url bridge that derives the founding Member/Authority key.
33
+ // protobuf->base64url bridge that derives the founding Member/Manager key.
34
34
  export { generateStrandMemberKey, strandMemberKeyPair } from './strand-member-key.js';
35
35
  // Strand membership writer (founder bootstrap, invite issuance/consumption,
36
- // authority-admit, member-peer registration, authority rotation, + shared signing
36
+ // manager-admit, member-peer registration, manager rotation, + shared signing
37
37
  // primitives reused across the flows).
38
- export { signStrandPayload, verifyStrandPayload, bootstrapFounderMembership, issueInvite, consumeInvite, addMemberByAuthority, registerMemberPeer, addAuthority, removeAuthority, STRAND_ENGINE, STRAND_ENGINE_VERSION } from './strand-membership-writer.js';
38
+ export { signStrandPayload, verifyStrandPayload, bootstrapFounderMembership, issueInvite, consumeInvite, addMemberByManager, registerMemberPeer, addManager, removeManager, STRAND_ENGINE, STRAND_ENGINE_VERSION } from './strand-membership-writer.js';
39
39
  // Engine-canonical datetime helper (shared by control + strand signed-write flows
40
40
  // so a signed timestamp byte-matches the datetime-coerced column the CHECK sees).
41
41
  export { canonicalDatetime } from './canonical-datetime.js';
@@ -44,8 +44,8 @@ export { canonicalDatetime } from './canonical-datetime.js';
44
44
  export { StrandMemberRegistry, StrandMemberVerifier, memberRegistrationPayload } from './strand-member-registry.js';
45
45
  // Seed Bootstrap
46
46
  export { SeedBootstrapService, SEED_PROTOCOL, ed25519PublicKeyB64FromPeerId } from './seed-bootstrap.js';
47
- // Peer Authorization (shared authority-signature digest + offline verifier)
48
- export { peerAuthorizationDigest, verifyPeerAuthorization } from './peer-authorization.js';
47
+ // Peer Authorization (shared owner-signature digest + offline verifier)
48
+ export { peerAuthorizationDigest, cadrePeerVoucherDigest, cadrePeerRemoveDigest, verifyPeerAuthorization } from './peer-authorization.js';
49
49
  // Strand Wake (control-network push-wake protocol)
50
50
  export { StrandWakeService, dialWake, WAKE_PROTOCOL } from './strand-wake-protocol.js';
51
51
  // Strand Address (control-network strand-address RPC) — wire types come via
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
  * Backend-agnostic store for raw private key material.
3
3
  *
4
4
  * Mobile cadre nodes hold sensitive key material at rest: the libp2p
5
- * peer/node identity key and (in the single-key reference model) the authority
5
+ * peer/node identity key and (in the single-key reference model) the owner
6
6
  * signing key derived from it. This module defines the seam those keys flow
7
7
  * through so a platform-secure backend (iOS Keychain / Android Keystore) can be
8
8
  * plugged in without `@serfab/cadre-core` taking any platform dependency.
package/dist/key-store.js CHANGED
@@ -2,7 +2,7 @@
2
2
  * Backend-agnostic store for raw private key material.
3
3
  *
4
4
  * Mobile cadre nodes hold sensitive key material at rest: the libp2p
5
- * peer/node identity key and (in the single-key reference model) the authority
5
+ * peer/node identity key and (in the single-key reference model) the owner
6
6
  * signing key derived from it. This module defines the seam those keys flow
7
7
  * through so a platform-secure backend (iOS Keychain / Android Keystore) can be
8
8
  * plugged in without `@serfab/cadre-core` taking any platform dependency.
@@ -1,26 +1,48 @@
1
1
  /**
2
- * Canonical digest an authority signs to authorize a peer.
2
+ * Canonical digest an owner signs to authorize a peer.
3
3
  *
4
4
  * This is the exact byte construction {@link SeedBootstrapService.authorizePeer}
5
5
  * signs over when it inserts an authorized `CadrePeer` row:
6
6
  * `digest([peerId], 'sha256', 'base64url')`. Factored into one place so the
7
- * producer (authority signing) and the verifier (the offline `cadre enroll
7
+ * producer (owner signing) and the verifier (the offline `cadre enroll
8
8
  * register` check) can never drift apart — change the digest here and both move
9
9
  * together. The SQL mirror is the bare single-field `digest(coalesce(new.PeerId, old.PeerId))`.
10
10
  */
11
11
  export declare function peerAuthorizationDigest(peerId: string): string;
12
12
  /**
13
- * Verify that `signature` is a valid authority ed25519 signature over `peerId`'s
14
- * authorization digest, using `authorityPublicKey` (base64url).
13
+ * Canonical digest an owner signs to VOUCH a `CadrePeer` membership row (insert
14
+ * and the owner re-touch update). Binds the peer id to the row's single-use
15
+ * `StampId` nonce, so a captured signed insert cannot be replayed (the `StampId` is
16
+ * unique) and — because {@link cadrePeerRemoveDigest} scopes a DIFFERENT payload — the
17
+ * stored voucher (`VouchSig`) cannot be replayed to authorize a delete.
18
+ *
19
+ * The SQL mirror is `digest(new.PeerId, new.StampId)` (two TEXT fields), matching the
20
+ * canonical multi-field framing in control-database.ts:buildAuthorizationMessage.
21
+ */
22
+ export declare function cadrePeerVoucherDigest(peerId: string, stampId: string): string;
23
+ /**
24
+ * Canonical digest an owner signs to REMOVE a `CadrePeer` row. Deliberately a
25
+ * distinct payload from {@link cadrePeerVoucherDigest} (adds the `'remove'` action
26
+ * tag) so the row's stored voucher — a signature over the voucher digest — can never
27
+ * satisfy this delete check. The signature is supplied in write context and never
28
+ * stored, so no reader can replay it; binding the live row's `StampId` also invalidates
29
+ * any captured remove after a delete+reinsert (the nonce rotates).
30
+ *
31
+ * SQL mirror: `digest(old.PeerId, old.StampId, 'remove')`.
32
+ */
33
+ export declare function cadrePeerRemoveDigest(peerId: string, stampId: string): string;
34
+ /**
35
+ * Verify that `signature` is a valid owner ed25519 signature over `peerId`'s
36
+ * authorization digest, using `ownerPublicKey` (base64url).
15
37
  *
16
38
  * This is the mirror of the signing done in
17
39
  * {@link SeedBootstrapService.authorizePeer}: it checks the signature against
18
40
  * {@link peerAuthorizationDigest}. A `true` result means the holder of the
19
- * authority private key vouched for this peer ID — it does NOT mean the peer is
41
+ * owner private key vouched for this peer ID — it does NOT mean the peer is
20
42
  * registered anywhere.
21
43
  *
22
44
  * Returns a boolean and never throws: malformed base64url, a bad/garbage key, or
23
45
  * any crypto failure resolves to `false` (callers want a verdict, not an
24
46
  * exception). The catch is logged at debug.
25
47
  */
26
- export declare function verifyPeerAuthorization(peerId: string, authorityPublicKey: string, signature: string): boolean;
48
+ export declare function verifyPeerAuthorization(peerId: string, ownerPublicKey: string, signature: string): boolean;