@serfab/cadre-core 1.16.0 → 1.18.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 (268) hide show
  1. package/README.md +4 -3
  2. package/dist/bootstrap-peer-store-file.js +1 -1
  3. package/dist/bootstrap-peer-store.d.ts +1 -1
  4. package/dist/bootstrap-peer-store.js +4 -4
  5. package/dist/cadre-invite-protocol.d.ts +81 -142
  6. package/dist/cadre-invite-protocol.js +168 -458
  7. package/dist/cadre-invite-protocol.js.map +1 -1
  8. package/dist/cadre-join-request.d.ts +15 -2
  9. package/dist/cadre-join-request.js +22 -3
  10. package/dist/cadre-join-request.js.map +1 -1
  11. package/dist/cadre-node.d.ts +1089 -475
  12. package/dist/cadre-node.js +2170 -1037
  13. package/dist/cadre-node.js.map +1 -1
  14. package/dist/control-authorization.d.ts +36 -14
  15. package/dist/control-authorization.js +20 -3
  16. package/dist/control-authorization.js.map +1 -1
  17. package/dist/control-cohort.d.ts +88 -32
  18. package/dist/control-cohort.js +91 -33
  19. package/dist/control-cohort.js.map +1 -1
  20. package/dist/control-database.d.ts +856 -318
  21. package/dist/control-database.js +1422 -701
  22. package/dist/control-database.js.map +1 -1
  23. package/dist/control-formation-recorder.d.ts +46 -43
  24. package/dist/control-formation-recorder.js +77 -78
  25. package/dist/control-formation-recorder.js.map +1 -1
  26. package/dist/control-protocol-guard.d.ts +11 -4
  27. package/dist/control-protocol-guard.js +20 -5
  28. package/dist/control-protocol-guard.js.map +1 -1
  29. package/dist/control-schema-version.d.ts +97 -0
  30. package/dist/control-schema-version.js +225 -0
  31. package/dist/control-schema-version.js.map +1 -0
  32. package/dist/control-schema.d.ts +45 -5
  33. package/dist/control-schema.js +262 -78
  34. package/dist/control-schema.js.map +1 -1
  35. package/dist/control-write-retry.d.ts +45 -10
  36. package/dist/control-write-retry.js +92 -22
  37. package/dist/control-write-retry.js.map +1 -1
  38. package/dist/delegate-admission.d.ts +13 -0
  39. package/dist/delegate-admission.js +27 -0
  40. package/dist/delegate-admission.js.map +1 -1
  41. package/dist/ed25519-key.d.ts +10 -7
  42. package/dist/ed25519-key.js +23 -20
  43. package/dist/ed25519-key.js.map +1 -1
  44. package/dist/edge-node/config.d.ts +122 -0
  45. package/dist/edge-node/config.js +199 -0
  46. package/dist/edge-node/config.js.map +1 -0
  47. package/dist/edge-node/dissolve.d.ts +95 -0
  48. package/dist/edge-node/dissolve.js +184 -0
  49. package/dist/edge-node/dissolve.js.map +1 -0
  50. package/dist/edge-node/edge-join.d.ts +44 -0
  51. package/dist/edge-node/edge-join.js +227 -0
  52. package/dist/edge-node/edge-join.js.map +1 -0
  53. package/dist/edge-node/edge-node.d.ts +295 -0
  54. package/dist/edge-node/edge-node.js +704 -0
  55. package/dist/edge-node/edge-node.js.map +1 -0
  56. package/dist/edge-node/index.d.ts +18 -0
  57. package/dist/edge-node/index.js +18 -0
  58. package/dist/edge-node/index.js.map +1 -0
  59. package/dist/edge-node/join-credential.d.ts +11 -0
  60. package/dist/edge-node/join-credential.js +11 -0
  61. package/dist/edge-node/join-credential.js.map +1 -0
  62. package/dist/edge-node/lazy-raw-storage.d.ts +40 -0
  63. package/dist/edge-node/lazy-raw-storage.js +134 -0
  64. package/dist/edge-node/lazy-raw-storage.js.map +1 -0
  65. package/dist/edge-node/node-local.d.ts +60 -0
  66. package/dist/edge-node/node-local.js +58 -0
  67. package/dist/edge-node/node-local.js.map +1 -0
  68. package/dist/edge-node/options.d.ts +83 -0
  69. package/dist/edge-node/options.js +110 -0
  70. package/dist/edge-node/options.js.map +1 -0
  71. package/dist/edge-node/owner-grant.d.ts +41 -0
  72. package/dist/edge-node/owner-grant.js +38 -0
  73. package/dist/edge-node/owner-grant.js.map +1 -0
  74. package/dist/edge-node/platform.d.ts +140 -0
  75. package/dist/edge-node/platform.js +12 -0
  76. package/dist/edge-node/platform.js.map +1 -0
  77. package/dist/edge-node/strand-move.d.ts +115 -0
  78. package/dist/edge-node/strand-move.js +223 -0
  79. package/dist/edge-node/strand-move.js.map +1 -0
  80. package/dist/edge-node/strands.d.ts +28 -0
  81. package/dist/edge-node/strands.js +60 -0
  82. package/dist/edge-node/strands.js.map +1 -0
  83. package/dist/enrolled-machine-store.d.ts +3 -3
  84. package/dist/enrolled-machine-store.js +3 -3
  85. package/dist/formation-token.d.ts +48 -0
  86. package/dist/formation-token.js +26 -0
  87. package/dist/formation-token.js.map +1 -0
  88. package/dist/index.d.ts +27 -17
  89. package/dist/index.js +39 -17
  90. package/dist/index.js.map +1 -1
  91. package/dist/joined-strand-store.d.ts +29 -12
  92. package/dist/joined-strand-store.js +66 -27
  93. package/dist/joined-strand-store.js.map +1 -1
  94. package/dist/key-store.d.ts +9 -4
  95. package/dist/key-store.js +9 -4
  96. package/dist/key-store.js.map +1 -1
  97. package/dist/link-budget.d.ts +9 -7
  98. package/dist/link-budget.js +9 -7
  99. package/dist/link-budget.js.map +1 -1
  100. package/dist/machine-removal.d.ts +19 -39
  101. package/dist/machine-removal.js +24 -98
  102. package/dist/machine-removal.js.map +1 -1
  103. package/dist/member-redemption.d.ts +256 -0
  104. package/dist/member-redemption.js +490 -0
  105. package/dist/member-redemption.js.map +1 -0
  106. package/dist/membership-view.d.ts +91 -0
  107. package/dist/membership-view.js +110 -0
  108. package/dist/membership-view.js.map +1 -0
  109. package/dist/owner-anchor-sync.d.ts +4 -4
  110. package/dist/owner-anchor-sync.js +16 -15
  111. package/dist/owner-anchor-sync.js.map +1 -1
  112. package/dist/owner-grant-protocol.d.ts +251 -0
  113. package/dist/owner-grant-protocol.js +344 -0
  114. package/dist/owner-grant-protocol.js.map +1 -0
  115. package/dist/owner-key-export.d.ts +70 -0
  116. package/dist/owner-key-export.js +279 -0
  117. package/dist/owner-key-export.js.map +1 -0
  118. package/dist/owner-key-flows.d.ts +55 -0
  119. package/dist/owner-key-flows.js +60 -0
  120. package/dist/owner-key-flows.js.map +1 -0
  121. package/dist/owner-key-store.d.ts +31 -0
  122. package/dist/owner-key-store.js +81 -0
  123. package/dist/owner-key-store.js.map +1 -0
  124. package/dist/owner-keys.d.ts +78 -0
  125. package/dist/owner-keys.js +133 -0
  126. package/dist/owner-keys.js.map +1 -0
  127. package/dist/owner-signer.d.ts +54 -0
  128. package/dist/owner-signer.js +51 -0
  129. package/dist/owner-signer.js.map +1 -0
  130. package/dist/peer-addr-book.d.ts +10 -1
  131. package/dist/peer-addr-book.js +4 -3
  132. package/dist/peer-addr-book.js.map +1 -1
  133. package/dist/peer-authorization.d.ts +83 -18
  134. package/dist/peer-authorization.js +123 -53
  135. package/dist/peer-authorization.js.map +1 -1
  136. package/dist/peer-join-backfill.d.ts +89 -20
  137. package/dist/peer-join-backfill.js +265 -108
  138. package/dist/peer-join-backfill.js.map +1 -1
  139. package/dist/pending-join-runner.d.ts +48 -18
  140. package/dist/pending-join-runner.js +74 -56
  141. package/dist/pending-join-runner.js.map +1 -1
  142. package/dist/provider/rent-node.js +1 -1
  143. package/dist/provider/rent-node.js.map +1 -1
  144. package/dist/seed-bootstrap.d.ts +162 -133
  145. package/dist/seed-bootstrap.js +300 -283
  146. package/dist/seed-bootstrap.js.map +1 -1
  147. package/dist/seed-trust-policy.js +4 -4
  148. package/dist/strand-addr-protocol.d.ts +4 -1
  149. package/dist/strand-addr-protocol.js +8 -2
  150. package/dist/strand-addr-protocol.js.map +1 -1
  151. package/dist/strand-cohort.d.ts +3 -0
  152. package/dist/strand-cohort.js.map +1 -1
  153. package/dist/strand-database.d.ts +24 -2
  154. package/dist/strand-database.js +29 -6
  155. package/dist/strand-database.js.map +1 -1
  156. package/dist/strand-first-sync-gate.d.ts +176 -88
  157. package/dist/strand-first-sync-gate.js +288 -121
  158. package/dist/strand-first-sync-gate.js.map +1 -1
  159. package/dist/strand-formation-deadlines.d.ts +9 -9
  160. package/dist/strand-formation-deadlines.js +9 -9
  161. package/dist/strand-formation-manager.d.ts +23 -17
  162. package/dist/strand-formation-manager.js +46 -44
  163. package/dist/strand-formation-manager.js.map +1 -1
  164. package/dist/strand-formation-protocol.d.ts +5 -8
  165. package/dist/strand-formation-protocol.js +101 -16
  166. package/dist/strand-formation-protocol.js.map +1 -1
  167. package/dist/strand-instance-manager.d.ts +27 -18
  168. package/dist/strand-instance-manager.js +119 -50
  169. package/dist/strand-instance-manager.js.map +1 -1
  170. package/dist/strand-membership-reconciler.d.ts +16 -13
  171. package/dist/strand-membership-reconciler.js +30 -22
  172. package/dist/strand-membership-reconciler.js.map +1 -1
  173. package/dist/strand-membership-writer.d.ts +45 -22
  174. package/dist/strand-membership-writer.js +226 -131
  175. package/dist/strand-membership-writer.js.map +1 -1
  176. package/dist/strand-move.d.ts +6 -5
  177. package/dist/strand-move.js +36 -32
  178. package/dist/strand-move.js.map +1 -1
  179. package/dist/strand-peer-redial.js +2 -3
  180. package/dist/strand-peer-redial.js.map +1 -1
  181. package/dist/strand-revocation-enforcer.d.ts +134 -60
  182. package/dist/strand-revocation-enforcer.js +144 -71
  183. package/dist/strand-revocation-enforcer.js.map +1 -1
  184. package/dist/strand-solicitation.d.ts +50 -39
  185. package/dist/strand-solicitation.js +10 -3
  186. package/dist/strand-solicitation.js.map +1 -1
  187. package/dist/strand-watcher.d.ts +80 -13
  188. package/dist/strand-watcher.js +152 -48
  189. package/dist/strand-watcher.js.map +1 -1
  190. package/dist/table-change-watch.d.ts +63 -0
  191. package/dist/table-change-watch.js +209 -0
  192. package/dist/table-change-watch.js.map +1 -0
  193. package/dist/timeout-scheduler.d.ts +4 -3
  194. package/dist/timeout-scheduler.js +4 -3
  195. package/dist/timeout-scheduler.js.map +1 -1
  196. package/dist/types.d.ts +374 -150
  197. package/dist/types.js +4 -4
  198. package/dist/types.js.map +1 -1
  199. package/package.json +17 -8
  200. package/src/bootstrap-peer-store-file.ts +44 -44
  201. package/src/bootstrap-peer-store.ts +214 -214
  202. package/src/cadre-invite-protocol.ts +740 -1052
  203. package/src/cadre-join-request.ts +24 -3
  204. package/src/cadre-node.ts +2402 -1079
  205. package/src/control-authorization.ts +47 -14
  206. package/src/control-cohort.ts +204 -128
  207. package/src/control-database.ts +1749 -820
  208. package/src/control-formation-recorder.ts +359 -353
  209. package/src/control-protocol-guard.ts +23 -6
  210. package/src/control-schema-version.ts +301 -0
  211. package/src/control-schema.ts +273 -78
  212. package/src/control-write-retry.ts +641 -567
  213. package/src/delegate-admission.ts +351 -314
  214. package/src/ed25519-key.ts +174 -171
  215. package/src/edge-node/config.ts +277 -0
  216. package/src/edge-node/dissolve.ts +251 -0
  217. package/src/edge-node/edge-join.ts +319 -0
  218. package/src/edge-node/edge-node.ts +1045 -0
  219. package/src/edge-node/index.ts +59 -0
  220. package/src/edge-node/join-credential.ts +16 -0
  221. package/src/edge-node/lazy-raw-storage.ts +178 -0
  222. package/src/edge-node/node-local.ts +84 -0
  223. package/src/edge-node/options.ts +179 -0
  224. package/src/edge-node/owner-grant.ts +63 -0
  225. package/src/edge-node/platform.ts +151 -0
  226. package/src/edge-node/strand-move.ts +270 -0
  227. package/src/edge-node/strands.ts +72 -0
  228. package/src/enrolled-machine-store.ts +3 -3
  229. package/src/formation-token.ts +63 -0
  230. package/src/index.ts +899 -789
  231. package/src/joined-strand-store.ts +73 -28
  232. package/src/key-store.ts +10 -4
  233. package/src/link-budget.ts +9 -7
  234. package/src/machine-removal.ts +26 -111
  235. package/src/member-redemption.ts +649 -0
  236. package/src/membership-view.ts +144 -0
  237. package/src/owner-anchor-sync.ts +19 -18
  238. package/src/owner-grant-protocol.ts +547 -0
  239. package/src/owner-key-export.ts +348 -0
  240. package/src/owner-key-flows.ts +109 -0
  241. package/src/owner-key-store.ts +85 -0
  242. package/src/owner-keys.ts +178 -0
  243. package/src/owner-signer.ts +94 -0
  244. package/src/peer-addr-book.ts +4 -3
  245. package/src/peer-authorization.ts +558 -488
  246. package/src/peer-join-backfill.ts +971 -761
  247. package/src/pending-join-runner.ts +76 -53
  248. package/src/provider/rent-node.ts +1 -1
  249. package/src/seed-bootstrap.ts +343 -349
  250. package/src/seed-trust-policy.ts +4 -4
  251. package/src/strand-addr-protocol.ts +547 -538
  252. package/src/strand-cohort.ts +4 -0
  253. package/src/strand-database.ts +352 -319
  254. package/src/strand-first-sync-gate.ts +334 -133
  255. package/src/strand-formation-deadlines.ts +227 -227
  256. package/src/strand-formation-manager.ts +731 -725
  257. package/src/strand-formation-protocol.ts +122 -24
  258. package/src/strand-instance-manager.ts +2092 -2026
  259. package/src/strand-membership-reconciler.ts +33 -25
  260. package/src/strand-membership-writer.ts +2093 -1974
  261. package/src/strand-move.ts +50 -38
  262. package/src/strand-peer-redial.ts +2 -3
  263. package/src/strand-revocation-enforcer.ts +740 -654
  264. package/src/strand-solicitation.ts +61 -34
  265. package/src/strand-watcher.ts +175 -56
  266. package/src/table-change-watch.ts +285 -0
  267. package/src/timeout-scheduler.ts +29 -28
  268. package/src/types.ts +391 -152
@@ -1,12 +1,52 @@
1
1
  /**
2
- * Embedded control schema for cross-platform compatibility.
2
+ * The version of the declared control schema, carried in the schema text as the
3
+ * {@link CONTROL_SCHEMA_VERSION_TAG} tag on the `OwnerKey` table. A warm start compares
4
+ * it with the version the store records before applying anything, and the apply then
5
+ * migrates the store to this version and records it (`control-schema-version.ts`).
3
6
  *
4
- * This is the authoritative runtime copy of the CadreControl authorization
5
- * schema. It is duplicated from `schemas/control.qsql` so that React Native and
6
- * other filesystem-less environments get the schema without a runtime file read.
7
+ * When it changes, what a new version may change, and the fixture each version keeps:
8
+ * docs/architecture.md → "Control schema versions and migrations", enforced by
9
+ * `control-schema-versions.spec.ts`.
10
+ */
11
+ export declare const CONTROL_SCHEMA_VERSION = 3;
12
+ /**
13
+ * The first control schema version whose fixture is kept, and the oldest a store can be
14
+ * migrated from. A store that records a version below it (written by 1.16.0 and earlier,
15
+ * which recorded none) is refused with reason `before-migrations`; docs/architecture.md
16
+ * says why it is not migrated.
17
+ */
18
+ export declare const CONTROL_SCHEMA_BASELINE_VERSION = 3;
19
+ /** The `OwnerKey` table tag that records {@link CONTROL_SCHEMA_VERSION} in the schema and in every store. */
20
+ export declare const CONTROL_SCHEMA_VERSION_TAG = "sereus.controlSchemaVersion";
21
+ /**
22
+ * The `declare schema CadreControl { … }` statement, with its header comment.
23
+ *
24
+ * Together with {@link APPLY_CONTROL_SCHEMA} this is the authoritative runtime copy of
25
+ * the CadreControl authorization schema ({@link CONTROL_SCHEMA}). It is duplicated from
26
+ * `schemas/control.qsql` so that React Native and other filesystem-less environments
27
+ * get the schema without a runtime file read. Held apart from the apply because
28
+ * `ControlDatabase.loadSchema` declares, checks the pending changes, and only then
29
+ * applies.
7
30
  *
8
31
  * The two copies MUST stay identical — `control-schema-drift.spec.ts` fails the
9
32
  * build if they drift. Any edit here must be mirrored in `schemas/control.qsql`
10
33
  * and vice versa.
11
34
  */
12
- export declare const CONTROL_SCHEMA = "-- A Sereus party's cadre (its nodes) and their participation in strands (networks).\n--\n-- Every write is authorized by a check: an owner signs an action-tagged digest of the row\n-- (cadre-core control-authorization.ts), or a peer signs its own record. Each digest leads\n-- with a 'CadreControl.<Table>' domain tag, an action tag and party_id(), the party this\n-- machine is configured for (a zero-argument function ControlDatabase registers on its own\n-- engine, never read from a context value or a replicated table), so an approval verifies\n-- only against the one rule, in the one party, it was minted for: two parties that share\n-- an owner key never accept each other's approvals. FormationUsage is the exception, its\n-- two digests scoped by the invitation token instead (FormationUsage.Authorized).\n--\n-- Rows of the guarded tables (OwnerKey, ValidationKey, Strand, StrandPartyKey, JoinedStrand,\n-- JoinRequest, CadrePeer, DeviceToken, CadreInvite) carry a one-off StampId bound into their\n-- add and remove digests. `unique` holds over live rows only, so a delete retires the stamp\n-- into Revocation in the same transaction (RevocationRecorded) and an insert refuses a retired\n-- stamp (NotRevoked): the never-expiring add approval cannot re-seat a removed row. A\n-- legitimate re-add mints a fresh stamp and a fresh signature. CadreInvite is the one guarded\n-- table that is never deleted: its tombstone withdraws the invitation while the row stays,\n-- because the rows it admitted prove their membership through it.\n--\n-- A check whose expression evaluates to null passes (SQL three-valued logic), so every\n-- comparison over a nullable column that is meant to pin a value is written null-safe\n-- (`coalesce(col, '')` or an explicit `is null`); a bare `new.Col = context.X` over a null\n-- column would pass with nothing pinned.\n--\n-- A check with a subquery defers to commit, so a sibling row written in the same transaction\n-- is visible to it; `committed.<Table>` reads the pre-transaction snapshot instead. Every\n-- check reads the rows this node holds: a node that has not converged on a Revocation row\n-- still accepts the replayed add, and the resurrected row then coexists with its tombstone.\n-- ControlDatabase drops retired stamps on the reads that matter (queryCadrePeers,\n-- queryPendingJoins, getOwnerKeys), and every owner lookup in the checks below drops them\n-- too (the `not exists ... Revocation` conjunct beside each OwnerKey read, reading live\n-- Revocation so a tombstone filed in the same transaction counts), so a removal is\n-- effective at the writer as soon as its tombstone arrives, whether or not the row's delete\n-- has. Only the founding count in OwnerKey.Authorized stays raw.\ndeclare schema CadreControl {\n -- A key that can authorize control changes. A row is seated one of three ways, told apart\n -- by the vouch columns: the founding row (all three null), an owner-signed add (VouchOwner\n -- and VouchSig pinned to the insert context, VouchUsage null), or an owner-granting\n -- invitation redeemed by consent (VouchOwner = the invitation's IssuerKey, VouchSig null,\n -- VouchUsage = the CadreInviteUsage row that admitted it). A reader judges the stored proof\n -- against its node-local anchor, never against this replicated table.\n table OwnerKey (\n Key text primary key,\n StampId text unique,\n VouchOwner text null,\n VouchSig text null,\n VouchUsage text null,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = new.StampId)\n ),\n -- The tombstone must name the removed row, not just its stamp (Revocation.RowKey).\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = old.StampId and R.RowKey = old.Key)\n ),\n -- Every other table's checks require an owner, so an empty table is a permanent denial of\n -- the party's control plane. Counted post-delete, on this node's rows only: two partitioned\n -- nodes removing different owners can still converge to zero.\n constraint MinOneOwner check on delete (\n (select count(1) from OwnerKey) >= 1\n ),\n -- Rotation is add-then-remove. An update branch would let the sole owner be re-pointed at\n -- another key under a post-image count of one.\n constraint NoUpdate check on update (false),\n -- The authorizer is read from the pre-transaction snapshot: the check defers to commit, by\n -- which point the inserted row (and any sibling) is live, so a plain OwnerKey read would let\n -- a row authorize itself or two strangers seat each other. The founding transaction, whose\n -- pre-transaction owner set is empty, needs no authorization; gating on the pre-transaction\n -- count keeps a same-transaction swap of the sole owner off that branch. That count is\n -- deliberately raw, the one owner read here that ignores Revocation: a machine holding only\n -- a retired owner row must still be refused a second genesis, or a removed device could\n -- re-found the party on its own copy. An owner cannot sign its own removal.\n -- The consent branch seats an owner with no signature at all: a CadreInviteUsage row\n -- written in the same transaction names this exact row (Key and stamp), and the\n -- invitation it redeemed grants ownership and was issued by the stored VouchOwner. The\n -- invitation is read from committed.CadreInvite, so an invitation cannot be seated and\n -- redeemed in one transaction; the usage row carries the liveness conditions.\n constraint Authorized check on insert, delete (\n (old.Key is null and (select count(1) from committed.OwnerKey) = 0\n and new.VouchOwner is null and new.VouchSig is null and new.VouchUsage is null)\n or (old.Key is null and exists (select 1 from committed.OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.OwnerKey', 'add', party_id(), new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null)\n or (old.Key is null and new.VouchSig is null and new.VouchUsage is not null\n and exists (\n select 1 from CadreInviteUsage U\n where U.UsageStampId = new.VouchUsage\n and U.PeerKey = new.Key\n and U.OwnerStampId = new.StampId\n and exists (select 1 from committed.CadreInvite I where I.Key = U.InviteKey and I.GrantsOwner = 1 and I.IssuerKey = new.VouchOwner)\n ))\n or (new.Key is null and exists (select 1 from committed.OwnerKey A where A.Key = context.OwnerKey and A.Key <> old.Key and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.OwnerKey', 'remove', party_id(), old.Key, old.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 (FormationUsage.Authorized).\n table ValidationKey (\n Key text primary key,\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'ValidationKey' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'ValidationKey' and R.StampId = old.StampId and R.RowKey = old.Key)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.ValidationKey', 'add', party_id(), new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- Owner-signed over the stored row, or a reap: a committed tombstone naming this exact row\n -- incarnation lets a node that was offline at removal time delete the stale row unsigned\n -- (ControlDatabase.reapRevokedRow). committed.*, so a tombstone filed in this transaction\n -- cannot stand in for the signature; the stamp clause, so a tombstone of an earlier\n -- incarnation cannot remove the current one.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.ValidationKey', 'remove', party_id(), old.Key, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'ValidationKey' and R.RowKey = old.Key and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A network of members sharing an sApp database, each contributing its cadre's peers. This\n -- party's own strands; a strand joined from another party is a JoinedStrand row.\n table Strand (\n Id text primary key, -- UUID\n MemberPrivateKey text null unique, -- the strand's shared read secret; closed strands only\n Type text, -- 'o' open | 'c' closed: open gates writes in the sApp, only closed gates reads\n FounderOwnerKey text null, -- owner key of the machine that published the row, the founding machine\n -- (cadre-node.ts launchStrand); null on a consent-seated strand, whose\n -- insert carries no signature. Provenance, not content (strandRowMismatches).\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'Strand' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'Strand' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n -- The consent branch below carries no signature, so an update rule would let anyone rewrite\n -- a consent-seated strand.\n constraint NoUpdate check on update (false),\n -- Owner-signed, with FounderOwnerKey pinned to the verified signer; or consent-seated by a\n -- FormationUsage row naming this exact row (id and stamp), written in the same transaction\n -- (ControlDatabase.redeemInvitation). Binding the stamp is what makes a removal stick:\n -- the usage row outlives the strand, and keyed on the id alone it would re-authorize an\n -- unsigned re-seat under a fresh stamp. The consent branch is narrow, each clause closing\n -- one door:\n -- * open and keyless: a closed strand with a caller-chosen read secret has no legitimate\n -- producer;\n -- * through an unbound invite only: a bound invite's host strand is owner-provisioned,\n -- and consent-seating its id would downgrade the real row on a node it has not reached;\n -- * no usage row under another stamp, and no tombstone for the id: an id is consent-seated\n -- once, ever, and never after any removal. Re-joining a removed id is owner-signed.\n -- A standalone tombstone (no accompanying delete, Revocation.Authorized) lets an owner\n -- foreclose consent-seating of an id that never existed: owner-only, and ids are random.\n constraint AuthorizedInsert check on insert (\n (exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Strand', 'add', party_id(), new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n and new.FounderOwnerKey = context.OwnerKey)\n or (\n new.Type = 'o'\n and new.MemberPrivateKey is null\n and new.FounderOwnerKey is null\n and exists (\n select 1 from FormationUsage FU\n where FU.StrandId = new.Id\n and FU.StrandStampId = new.StampId\n and exists (select 1 from FormationInvite FI where FI.Token = FU.Token and FI.StrandId is null)\n )\n and not exists (\n select 1 from FormationUsage FU2\n where FU2.StrandId = new.Id and FU2.StrandStampId <> new.StampId\n )\n and not exists (\n select 1 from Revocation R\n where R.TableName = 'Strand' and R.RowKey = new.Id\n )\n )\n ),\n -- No consent branch (an invitation forms a strand, never destroys one) and no reap branch:\n -- MemberPrivateKey is stored nowhere else, so this delete is unrecoverable\n -- (tickets/backlog/debt-strand-tombstone-reap.md owns any change).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Strand', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- An open strand has no membership gate, so no member key.\n constraint MemberKeyClosedOnly check (\n new.MemberPrivateKey is null or new.Type = 'c'\n )\n ) with context (OwnerKey text, Signature text);\n\n -- This party's own membership identity for one strand: the private key whose public half\n -- seats its Strand.Member / Strand.Manager rows. Separate from Strand.MemberPrivateKey, which\n -- every joining party receives and so could forge (gotchoices/sereus#4). A side table because\n -- a joiner holds no Strand row. Replicated in plaintext to every machine the party owns\n -- (docs/strands.md \u2192 \"Closed-Strand Member Key Handling\") but never put on the formation wire.\n table StrandPartyKey (\n Id text primary key, -- the strand id\n PrivateKey text, -- ed25519, base64 protobuf, as Strand.MemberPrivateKey (cadre-core strandMemberKeyPair)\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n -- Binding PrivateKey means a captured approval can only reproduce the key it approved.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.StrandPartyKey', 'add', party_id(), new.Id, new.PrivateKey, new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- No reap branch: the key is stored nowhere else (as Strand.AuthorizedDelete).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.StrandPartyKey', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A strand this party joined from another party, whose control database holds the Strand\n -- row. Every machine of the party reads it, so a replica host keeps a copy and the party's\n -- other machines can attach. A separate table because every reader of Strand takes it to\n -- mean this party's own strand. MemberPrivateKey is held by the founding party and every\n -- member, so a removed row is recoverable by re-forming, which is why this table has a reap\n -- branch (unlike Strand and StrandPartyKey).\n table JoinedStrand (\n Id text primary key, -- the founder's Strand.Id\n Type text, -- 'o' | 'c', as Strand.Type\n MemberPrivateKey text null, -- the closed strand's shared read secret\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'JoinedStrand' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinedStrand' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n -- Readers cast Type (ControlDatabase.queryJoinedStrands).\n constraint KnownType check (new.Type = 'o' or new.Type = 'c'),\n constraint MemberKeyClosedOnly check (new.MemberPrivateKey is null or new.Type = 'c'),\n -- Owner-signed only: every always-on machine of the party downloads the strands named\n -- here, so a machine that is not an owner keeps its joins machine-local.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinedStrand', 'add', party_id(), new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinedStrand', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'JoinedStrand' and R.RowKey = old.Id and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A join this party asked for by redeeming another party's invitation, kept party-wide so\n -- the request outlives the device that asked and any owner machine can carry it on\n -- (docs/strands.md \u2192 \"Joining while the inviter is offline\"). The row is the request alone\n -- and never changes; its outcome is a JoinSuccess or JoinFailure row keyed by this row's\n -- stamp, so asking again (a fresh stamp) starts clean and a dismissal's tombstone retires\n -- the outcome with the request. Invitation is a bearer credential, replicated in plaintext\n -- to every machine the party owns, under the same accepted risk as JoinedStrand.\n -- Owner-signed only; whether a machine that is not an owner may write here is\n -- tickets/blocked/decide-non-owner-machine-completes-a-pending-join.md.\n -- The three tables reference only tables declared before them, so an apply that fails\n -- midway can drop them in reverse order. The two rules that would close a cycle are the\n -- writer's (ControlDatabase): a request's delete takes its outcome row with it, and a join\n -- recorded over a failure deletes the failure. An outcome row left behind is unreadable,\n -- since readers start from the request.\n table JoinRequest (\n Id text primary key, -- base64url sha256 of the invitation token (cadre-core pendingJoinId),\n -- so the token never reaches Revocation\n Invitation text, -- CadreNode.encodeInvitation\n Disclosure text, -- canonicalJson of the disclosure the requester gave\n RequestedAt integer, -- epoch ms\n ExpiresAt integer, -- epoch ms; no attempt starts at or after it\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinRequest', 'add', party_id(), new.Id, new.Invitation, new.Disclosure, cast(new.RequestedAt as text), cast(new.ExpiresAt as text), new.StampId),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Reapable: the inviting party and the user hold the invitation too.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinRequest', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'JoinRequest' and R.RowKey = old.Id and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- The outcome of a JoinRequest that joined: the strand the formation returned and, for a\n -- closed strand, the single-use membership invitation it delivered. Keyed by the request's\n -- stamp, so it names one request incarnation and a replayed approval can only re-seat the\n -- row it approved, against the request it approved. A join is final: it may be recorded\n -- over a failure (whose row the writer deletes with it), and nothing is recorded over it.\n table JoinSuccess (\n RequestStampId text primary key,\n RecordedAt integer, -- epoch ms\n StrandId text,\n MembershipInvite text null, -- JSON {inviteKey, invitePrivateKey}, the StrandMembershipInvite; closed strands only\n constraint RequestExists check on insert (\n exists (select 1 from JoinRequest R where R.StampId = new.RequestStampId)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinSuccess', 'add', party_id(), new.RequestStampId, cast(new.RecordedAt as text), new.StrandId, coalesce(new.MembershipInvite, '')),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Removed only with its request: the tombstone retiring the request's stamp (owner-signed,\n -- or already committed for a reap) authorizes this delete.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.RequestStampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- The outcome of a JoinRequest that failed for good. Keyed as JoinSuccess is.\n table JoinFailure (\n RequestStampId text primary key,\n RecordedAt integer, -- epoch ms\n Code text, -- a FormationRejectionCode, 'expired' or 'local'\n Reason text, -- human-readable\n constraint RequestExists check on insert (\n exists (select 1 from JoinRequest R where R.StampId = new.RequestStampId)\n ),\n -- A join is final: nothing records a failure over it.\n constraint NotJoined check on insert (\n not exists (select 1 from JoinSuccess S where S.RequestStampId = new.RequestStampId)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinFailure', 'add', party_id(), new.RequestStampId, cast(new.RecordedAt as text), new.Code, new.Reason),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Removed with its request (JoinSuccess.AuthorizedDelete), or superseded by a join\n -- recorded in the same transaction.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.RequestStampId)\n or exists (select 1 from JoinSuccess S where S.RequestStampId = old.RequestStampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- A node of the cadre, carrying its self-published, freshness-stamped, self-signed address\n -- record (cadre-core PeerAddressRecord): a resolver re-verifies Sig against PublicKey,\n -- checks UpdatedAt and trust, then dials Multiaddr.\n table CadrePeer (\n PeerId text primary key,\n PublicKey text null, -- ed25519 (base64url) behind PeerId; null if not ed25519\n Multiaddr text, -- comma-joined current addrs (signaling / p2p-circuit first)\n UpdatedAt int null, -- epoch ms; strictly increases per self-update\n Sig text null, -- self-signature over the 'publish' digest; null until self-published\n StampId text unique,\n VouchOwner text null, -- the owner key that vouched this membership (== insert context.OwnerKey), or\n -- the issuer of the invitation that admitted it (CadreInvite.IssuerKey)\n VouchSig text null, -- that owner's 'vouch' signature (== insert context.Signature); null on an\n -- invitation-admitted row. A reader checks VouchOwner against its node-local\n -- trusted-owner anchor, not against this replicated table, which a self-owner\n -- can pollute.\n VouchUsage text null, -- the CadreInviteUsage.UsageStampId that admitted this row; null on an\n -- owner-vouched row. A row carries exactly one proof: a signature or a usage.\n Profile text null, -- the machine's own node profile (cadre-core NodeProfile): 'transaction' is an\n -- app (phone, browser), 'storage' an always-on node; null until it self-publishes.\n -- Self-declared and covered by Sig. No insert rule constrains it: an owner could\n -- only mislabel a machine it already has full authority over, and that machine's\n -- next self-publish corrects it.\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'CadrePeer' and R.StampId = new.StampId)\n ),\n constraint KnownProfile check (new.Profile is null or new.Profile in ('transaction', 'storage')),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'CadrePeer' and R.StampId = old.StampId and R.RowKey = old.PeerId)\n ),\n -- The owner vouches membership and the PublicKey<->PeerId binding (cadre-core derives\n -- PublicKey from PeerId) over (PeerId, StampId); the stored voucher must be the pair\n -- verified here. The same 'vouch' digest serves the owner branch of AuthorizedUpdate:\n -- both mean \"this owner vouches this membership row\".\n -- Or admitted by consent: a CadreInviteUsage row written in the same transaction names\n -- this exact row incarnation (PeerId, StampId and PublicKey), and the invitation it\n -- redeemed, read from committed.CadreInvite, was issued by the stored VouchOwner. The\n -- usage row carries the liveness conditions (unexpired, not withdrawn, uses left,\n -- issuer still an owner); this branch only requires that it exists. A reader verifies\n -- the chain row -> usage -> invitation against its anchor (cadre-core\n -- verifyInvitationAdmission), so the invitation row must outlive the rows it admitted.\n -- A removed device may redeem a still-live invitation again and come back: keeping a\n -- device out is removing its row AND withdrawing the invitation.\n constraint AuthorizedInsert check on insert (\n (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null\n )\n or (\n new.VouchSig is null\n and new.VouchOwner is not null\n and new.VouchUsage is not null\n and exists (\n select 1 from CadreInviteUsage U\n where U.UsageStampId = new.VouchUsage\n and U.PeerId = new.PeerId\n and U.PeerStampId = new.StampId\n and U.PeerKey = new.PublicKey\n and exists (select 1 from committed.CadreInvite I where I.Key = U.InviteKey and I.IssuerKey = new.VouchOwner)\n )\n )\n ),\n -- The stored 'vouch' voucher never authorizes a delete; the 'remove' signature rides in\n -- context and is never stored. Or a reap (ValidationKey.AuthorizedDelete).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'remove', party_id(), old.PeerId, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'CadrePeer' and R.RowKey = old.PeerId and R.StampId = old.StampId)\n ),\n -- A peer re-publishes its own addrs, profile and freshness under its own key (cadre-core\n -- peer-record.ts); everything else is immutable and UpdatedAt must increase. Or an owner\n -- re-vouches the row over its current stamp, which also clears VouchUsage: the row then\n -- stands on the signature and its invitation is no longer consulted for it. The owner\n -- branch keeps what the machine itself declares (PublicKey, Multiaddr, Sig, Profile): the\n -- row's stored VouchSig is a valid re-vouch signature, so any machine holding the row can\n -- take this branch. NOTE: it may still bump UpdatedAt (reauthorizeCadrePeer's re-broadcast\n -- needs it), which on a self-published row leaves Sig unverifiable until the machine's\n -- next heartbeat re-signs past it; if that window ever matters, pin UpdatedAt here when\n -- old.Sig is not null. StampId never\n -- changes on update: an update that rotated it would leave the old stamp free and\n -- untombstoned, so retiring a stamp is delete plus tombstone. The proof columns are\n -- compared null-safe: VouchSig and VouchUsage are null on one of the two row kinds, and a\n -- bare `new.X = old.X` over a null would let a self-publish fill the column in.\n constraint AuthorizedUpdate check on update (\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 coalesce(new.VouchSig, '') = coalesce(old.VouchSig, '')\n and coalesce(new.VouchUsage, '') = coalesce(old.VouchUsage, '')\n and new.UpdatedAt > coalesce(old.UpdatedAt, 0)\n and verify(\n digest('CadreControl.CadrePeer', 'publish', party_id(), new.PeerId, new.Multiaddr, cast(new.UpdatedAt as text), coalesce(new.Profile, '')),\n new.Sig, new.PublicKey, 'ed25519')\n )\n or (\n new.StampId = old.StampId\n and exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null\n and coalesce(new.PublicKey, '') = coalesce(old.PublicKey, '')\n and coalesce(new.Multiaddr, '') = coalesce(old.Multiaddr, '')\n and coalesce(new.Sig, '') = coalesce(old.Sig, '')\n and coalesce(new.Profile, '') = coalesce(old.Profile, '')\n )\n )\n ) with context (OwnerKey text null, Signature text);\n\n -- A mobile peer's platform push token, self-published so a server peer can push-wake a\n -- suspended app. A resolver re-verifies Sig against the CadrePeer.PublicKey for PeerId\n -- (cadre-core device-token.ts), checks freshness, then hands the token to the push sender.\n table DeviceToken (\n PeerId text primary key,\n Platform text, -- 'fcm' | 'apns'\n Token text, -- opaque platform device/registration token\n UpdatedAt int null, -- epoch ms; strictly increases per self-update\n Sig text null, -- self-signature over the 'publish' digest\n StampId text unique,\n -- A resurrected token has no freshness ceiling to retire it (resolveDeviceToken defaults\n -- maxAgeMs to infinity), so stamp retirement is what makes a clear stick.\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'DeviceToken' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'DeviceToken' and R.StampId = old.StampId and R.RowKey = old.PeerId)\n ),\n -- The owner signs the whole row, nullable columns as '', so a replayed approval can only\n -- reproduce the row it approved (cadre-core peer-authorization.ts deviceTokenAddDigest).\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.DeviceToken', 'add', party_id(),\n new.PeerId,\n new.Platform,\n new.Token,\n coalesce(cast(new.UpdatedAt as text), ''),\n coalesce(new.Sig, ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.DeviceToken', 'remove', party_id(), old.PeerId, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'DeviceToken' and R.RowKey = old.PeerId and R.StampId = old.StampId)\n ),\n -- Self-update only: Platform and Token may change, PeerId and StampId may not, UpdatedAt\n -- must increase. No owner re-touch branch: one outside the monotonicity rule would let a\n -- captured approval roll UpdatedAt back; an owner correcting a row deletes and re-inserts.\n constraint AuthorizedUpdate check on update (\n new.PeerId = old.PeerId\n and new.StampId = old.StampId\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('CadreControl.DeviceToken', 'publish', party_id(), new.PeerId, new.Platform, new.Token, cast(new.UpdatedAt as text)),\n new.Sig, P.PublicKey, '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, -- a random string\n sAppId text, -- the app of the strand to be formed\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n ValidationUrl text null, -- web hook sent the disclosure; a ValidationKey then signs off each redemption\n StrandId text null, -- bound invite: the host strand consent is recorded against;\n -- null: the responder provisions a fresh open strand per redemption\n StampId text unique,\n -- The owner signs the whole row, nullable columns as ''.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.FormationInvite', 'add', party_id(),\n new.Token,\n new.sAppId,\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n coalesce(new.ValidationUrl, ''),\n coalesce(new.StrandId, ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.FormationInvite', 'remove', party_id(),\n old.Token,\n old.sAppId,\n coalesce(cast(old.ExpiresAt as text), ''),\n coalesce(cast(old.TotalUses as text), ''),\n coalesce(old.ValidationUrl, ''),\n coalesce(old.StrandId, ''),\n old.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Insert and delete only, so the consent parameters cannot change under a signature.\n constraint Immutable check on update (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of formation invite redemptions, one row per redemption.\n table FormationUsage (\n Token text, -- the FormationInvite redeemed\n UsageStampId text, -- single-use nonce minted by the joining peer, signed into its 'consent' digest\n -- and the approver's 'vouch' digest. The primary key: this table never\n -- deletes, so the key alone makes a nonce permanent and there is no\n -- NotRevoked pair here. Two nodes that have not converged can each admit\n -- the same nonce: a duplicated audit row, never a join nobody approved.\n PeerKey text, -- the joining peer's ed25519 public key (base64url); its peer id derives from it\n PeerSig text, -- the joiner's signature over the 'consent' digest (PeerConsented)\n Disclosure text,\n StrandId text,\n StrandStampId text, -- the Strand.StampId this consent names: one strand row, not an id forever\n -- (Strand.AuthorizedInsert). Not unique: a bound invite's redeemers share\n -- a host strand.\n primary key (UsageStampId),\n constraint InsertOnly check on update, delete (false),\n -- The use cap counts the pre-transaction snapshot, so concurrent redeemers on separate\n -- nodes can over-admit by their number; both rows survive and removal is owner-gated. A\n -- strict per-token sequence was rejected: a lost race would silently drop a consented\n -- join. Two usage rows for one token in one transaction read the same count; no writer\n -- does that.\n -- A ValidationUrl invite needs a sign-off verified against a STORED ValidationKey row;\n -- context.ValidationKey only selects which row, so a redeemer cannot approve itself with\n -- a throwaway key. The sign-off covers (Token, UsageStampId, StrandId, PeerKey,\n -- Disclosure), so it buys exactly one row; StrandStampId is not bound because the\n -- unbound path mints it inside the redeeming transaction. Removing the key later does\n -- not revisit rows it approved.\n -- A bound invite may only name its own host strand. An unbound invite may name any\n -- existing strand, which burns a use and forecloses that id's consent-seating: harmless.\n -- Neither digest of this table binds party_id(), alone in this schema: the joiner signs\n -- its consent before the responder's party id reaches it (it arrives in the result\n -- frame) and with a key made fresh for this one formation, and the outside approver\n -- behind ValidationUrl is sent only the five fields below. Both digests bind the\n -- invitation Token, a random secret seated in one party's FormationInvite table, so\n -- replaying either in another party needs that party's owner to seat the same token in\n -- its own database, and the only database harmed would be its own.\n constraint Authorized check on insert (\n exists (\n select 1 from FormationInvite FI\n where FI.Token = new.Token\n and (FI.TotalUses is null or FI.TotalUses > (select count(1) from committed.FormationUsage U where U.Token = new.Token))\n and (FI.ExpiresAt is null or FI.ExpiresAt > context.Now)\n and (FI.ValidationUrl is null or exists (\n select 1 from ValidationKey VK\n where VK.Key = context.ValidationKey\n and verify(digest('CadreControl.FormationUsage', 'vouch', new.Token, new.UsageStampId, new.StrandId, new.PeerKey, new.Disclosure), context.ValidationSignature, VK.Key, 'ed25519')))\n and (FI.StrandId is null or FI.StrandId = new.StrandId)\n )\n ),\n -- A usage row may only name a strand row that exists, so a consent record cannot be held\n -- in reserve to re-seat an id after its removal.\n constraint StrandExists check (exists (select 1 from Strand S where S.Id = new.StrandId and S.StampId = new.StrandStampId)),\n -- The joiner proves it agreed. PeerKey is its own key, so there is nothing for a writer to\n -- substitute; stored on the row so any reader can re-check it (verifyFormationConsent).\n -- StrandId and the party id are not signed: the joiner cannot know them when it signs\n -- (Authorized, above).\n constraint PeerConsented check on insert (\n verify(digest('CadreControl.FormationUsage', 'consent',\n new.Token, new.UsageStampId, new.PeerKey, new.Disclosure),\n new.PeerSig, new.PeerKey, 'ed25519')\n )\n ) with context (Now datetime, ValidationKey text null, ValidationSignature text null);\n\n -- Serves the per-token reads (the cap count above; cadre-core's countFormationUsage,\n -- isTokenUsed and hasOutstandingFormationInvite) on an append-only table. Its cross-machine\n -- convergence is guarded by the integration scenario strand-formation-concurrent-redemption.\n index FormationUsageByToken on FormationUsage (Token);\n\n -- An owner-signed invitation to join this cadre, redeemable on the owner's behalf by any\n -- member machine (ControlDatabase.redeemCadreInvite). The invitation is a keypair, not a\n -- bearer token: the row holds the public half and the holder proves possession by signing\n -- with the private half (CadreInviteUsage.InvitePossessed), so the member that redeems it\n -- can verify but never redeem it for itself. Rows are never deleted: a device admitted\n -- through one carries no owner signature of its own, and its membership is the chain\n -- invitation -> usage -> peer row, verified at read time, so the row must outlive every\n -- device it admitted. Withdrawing an invitation is an owner-signed Revocation tombstone\n -- over the row's stamp with the row kept (Revocation.RowIsGone); liveness is read as no\n -- tombstone, unexpired, uses left, issuer still an owner (CadreInviteUsage.Authorized).\n -- NOTE: expired and withdrawn rows accumulate for the life of the party; the same tripwire\n -- as hasOutstandingFormationInvite applies (ControlDatabase.hasLiveCadreInvite). An owner\n -- retires a device's dependency on its invitation by re-vouching the row (the owner branch\n -- of CadrePeer.AuthorizedUpdate).\n table CadreInvite (\n Key text primary key, -- the invitation's ed25519 public key (base64url)\n PeerId text null, -- the device this invitation is for; null: whoever redeems first designates it\n GrantsOwner integer check (GrantsOwner = 0 or GrantsOwner = 1), -- 1: redemption also seats an OwnerKey row\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n IssuerKey text, -- the owner that signed the row (== insert context.OwnerKey). A reader checks it\n -- against its node-local anchor, never against the replicated OwnerKey table.\n IssuerSig text, -- that owner's 'add' signature (== insert context.Signature), stored so any reader\n -- can re-verify the row from the holder's copy or the replicated one\n StampId text unique,\n -- A withdrawn invitation cannot be re-seated from the signed copy its holder keeps, on\n -- any node that holds the tombstone (schema header for the node that does not).\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'CadreInvite' and R.StampId = new.StampId)\n ),\n -- The owner signs the whole row, nullable columns as '' (cadre-core cadreInviteRowFields),\n -- and the stored issuer pair must be the pair verified here, as CadrePeer pins its voucher.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.CadreInvite', 'add', party_id(),\n new.Key,\n coalesce(new.PeerId, ''),\n cast(new.GrantsOwner as text),\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n and coalesce(new.IssuerKey, '') = context.OwnerKey\n and coalesce(new.IssuerSig, '') = context.Signature\n ),\n constraint NoUpdate check on update (false),\n -- Never deleted (table comment). Withdrawal is the tombstone alone.\n constraint NoDelete check on delete (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of cadre invitation redemptions, one row per redemption: the proof a\n -- CadrePeer row (and, for an owner-granting invitation, an OwnerKey row) was admitted by\n -- consent rather than by an owner signature.\n table CadreInviteUsage (\n UsageStampId text primary key, -- nonce minted by the redeeming device and signed into both signatures\n -- below. The primary key: this table never deletes, so the key alone\n -- makes a nonce permanent and there is no NotRevoked pair here. Two nodes\n -- that have not converged can each admit the same nonce (FormationUsage).\n InviteKey text, -- the CadreInvite redeemed\n PeerId text, -- the redeeming device: the identity multihash of PeerKey. The writer\n -- asserts the pair and a reader re-derives it (verifyInvitationAdmission);\n -- the schema cannot unwrap a multihash.\n PeerKey text, -- the redeeming device's ed25519 public key (base64url)\n PeerStampId text, -- the CadrePeer.StampId this usage seats: one row incarnation, not a peer id\n -- forever. Minted by the writer inside the redeeming transaction and not\n -- signed (FormationUsage.StrandStampId).\n OwnerStampId text null, -- the OwnerKey.StampId this usage seats when the invitation grants ownership\n InviteSig text, -- by the invitation's private key over the 'redeem' digest: proof of possession\n PeerSig text, -- by PeerKey over the 'consent' digest: the device agreed to join\n constraint InsertOnly check on update, delete (false),\n -- The invitation is read from the pre-transaction snapshot, so an invitation cannot be\n -- seated and redeemed in one transaction (the OwnerKey consent branch rests on that).\n -- NOTE: accepted tradeoff \u2014 the use cap counts committed usage, so two redeemers racing\n -- for a single-use invitation on different machines may both be admitted until merge,\n -- and after merge both rows stand (FormationUsage.Authorized), for owner grants too.\n -- Serializing would need cross-machine coordination the control plane does not have;\n -- the mitigations are the defaults the redemption protocol sets (untargeted owner\n -- invitations single-use with a short expiry), the targeted form for owner grants, and\n -- the owner seeing the extra owner and removing it. Revisit if the control plane gains\n -- a cross-machine sequencer. An issuer removed since issuing takes its outstanding\n -- invitations with it (the committed.OwnerKey clause, which a tombstone alone\n -- satisfies); devices it already admitted stay members.\n constraint Authorized check on insert (\n exists (\n select 1 from committed.CadreInvite I\n where I.Key = new.InviteKey\n and not exists (select 1 from Revocation R where R.TableName = 'CadreInvite' and R.StampId = I.StampId)\n and (I.ExpiresAt is null or I.ExpiresAt > context.Now)\n and (I.TotalUses is null or I.TotalUses > (select count(1) from committed.CadreInviteUsage U where U.InviteKey = new.InviteKey))\n and (I.PeerId is null or I.PeerId = new.PeerId)\n and exists (select 1 from committed.OwnerKey K where K.Key = I.IssuerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = K.StampId))\n )\n ),\n -- Verified against the invitation key itself: the member writing the row can check this\n -- signature but, holding only the public half, cannot produce it.\n constraint InvitePossessed check on insert (\n verify(digest('CadreControl.CadreInviteUsage', 'redeem', party_id(), new.InviteKey, new.UsageStampId, new.PeerKey),\n new.InviteSig, new.InviteKey, 'ed25519')\n ),\n -- The device proves it agreed (FormationUsage.PeerConsented).\n constraint PeerConsented check on insert (\n verify(digest('CadreControl.CadreInviteUsage', 'consent', party_id(), new.InviteKey, new.UsageStampId, new.PeerKey),\n new.PeerSig, new.PeerKey, 'ed25519')\n ),\n -- Deferred, so the same-transaction peer insert counts: a usage row can never be held in\n -- reserve to seat a row later (FormationUsage.StrandExists). PeerKey must be the key the\n -- peer row stores: a redemption that only completes an already-admitted device with its\n -- OwnerKey row (no peer insert to pin it) cannot name a key other than that device's.\n constraint PeerExists check (exists (\n select 1 from CadrePeer P where P.PeerId = new.PeerId and P.StampId = new.PeerStampId and P.PublicKey = new.PeerKey\n )),\n constraint OwnerExists check (\n new.OwnerStampId is null\n or exists (select 1 from OwnerKey K where K.Key = new.PeerKey and K.StampId = new.OwnerStampId)\n )\n ) with context (Now datetime);\n\n -- Serves the cap count above and the liveness reads (cadre-core countCadreInviteUsage,\n -- hasLiveCadreInvite) on an append-only table, as FormationUsageByToken does.\n index CadreInviteUsageByInvite on CadreInviteUsage (InviteKey);\n\n -- Append-only retirement record for the stamps of removed rows of every guarded table, with\n -- the removed row's key. Every append is owner-signed, so the growth surface is the owner\n -- keys. One singleton marker row, ('Revocation', 'ledger', 'opened'), retires nothing: an\n -- owner's connected reconcile pass files it (ControlDatabase.openRevocationLedger) so this\n -- table is never a never-written block, which the storage layer would consult the cohort\n -- for on every guarded insert and membership lookup. Readers filter on their own guarded\n -- TableName, and queryRevocations skips it. A 'CadreInvite' row withdraws an invitation\n -- whose row stays (CadreInvite table comment).\n table Revocation (\n TableName text, -- 'OwnerKey' | 'ValidationKey' | 'Strand' | 'StrandPartyKey' | 'JoinedStrand' |\n -- 'JoinRequest' | 'CadrePeer' | 'DeviceToken' | 'CadreInvite', or 'Revocation'\n -- for the marker\n RowKey text, -- primary key of the removed row\n StampId text, -- the retired stamp\n ReissuedAt integer default 0, -- bumped by an owner-signed re-issue so a tombstone committed while the\n -- node was alone can be re-written and so re-broadcast; nothing reads it\n SignerKey text null, -- the owner that filed the tombstone (== insert context.OwnerKey)\n SignerSig text null, -- that owner's 'remove' signature (== insert context.Signature). A node that\n -- received the row by replication re-verifies the pair and judges SignerKey\n -- against its node-local trusted-owner anchor (cadre-core\n -- verifyRevocationSigner), not against this replicated table.\n -- Keyed on the stamp: one row key may carry several tombstones over its life.\n primary key (TableName, StampId),\n -- Retirement is permanent.\n constraint NoDelete check on delete (false),\n -- Pinned at 0 so an owner cannot seat a tombstone at a counter its own re-issues cannot pass.\n constraint FreshTombstone check on insert (new.ReissuedAt = 0),\n -- A re-issue moves nothing but the counter, and only upward. The signer pair stays the\n -- filing owner's; the re-issuing owner's signature rides in context only.\n constraint ReissueOnly check on update (\n new.TableName = old.TableName and new.RowKey = old.RowKey\n and new.StampId = old.StampId and new.ReissuedAt > old.ReissuedAt\n and coalesce(new.SignerKey, '') = coalesce(old.SignerKey, '')\n and coalesce(new.SignerSig, '') = coalesce(old.SignerSig, '')\n ),\n -- A stamp is retired only once its row is gone (deferred, so the same-transaction delete\n -- counts). Keyed on the stamp, not RowKey: a node that converges on a re-admission before\n -- the earlier removal's tombstone must still accept that tombstone. Retiring a stamp that\n -- never existed is harmless: stamps carry 128 random bits (generateStampId). For\n -- 'CadreInvite' there is no row-gone test: a tombstone there is a withdrawal, and the\n -- row stays (CadreInvite table comment). The last branch admits the marker row and\n -- nothing else under 'Revocation'.\n constraint RowIsGone check on insert (\n (new.TableName = 'OwnerKey' and not exists (select 1 from OwnerKey K where K.StampId = new.StampId))\n or (new.TableName = 'CadrePeer' and not exists (select 1 from CadrePeer P where P.StampId = new.StampId))\n or (new.TableName = 'ValidationKey' and not exists (select 1 from ValidationKey V where V.StampId = new.StampId))\n or (new.TableName = 'Strand' and not exists (select 1 from Strand S where S.StampId = new.StampId))\n or (new.TableName = 'StrandPartyKey' and not exists (select 1 from StrandPartyKey K where K.StampId = new.StampId))\n or (new.TableName = 'JoinedStrand' and not exists (select 1 from JoinedStrand J where J.StampId = new.StampId))\n or (new.TableName = 'JoinRequest' and not exists (select 1 from JoinRequest J where J.StampId = new.StampId))\n or (new.TableName = 'DeviceToken' and not exists (select 1 from DeviceToken D where D.StampId = new.StampId))\n or (new.TableName = 'CadreInvite')\n or (new.TableName = 'Revocation' and new.RowKey = 'ledger' and new.StampId = 'opened')\n ),\n -- Owner-signed over the whole row under its own domain tag, so the delete's 'remove'\n -- signature filed in the same transaction never doubles as the tombstone's. Ungated, any\n -- writer could evict a peer party-wide and block its re-admission. RowKey is not checked\n -- against the retired row here (that would reject a tombstone re-issued later, or filed on\n -- a node that never held the row); each guarded table's RevocationRecorded binds it\n -- instead. Reads live OwnerKey like the sibling tables. The marker is signed under this\n -- rule too, over ('Revocation', 'ledger', 'opened'). The stored signer pair must be the\n -- pair verified here.\n constraint Authorized check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Revocation', 'remove', party_id(), new.TableName, new.RowKey, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.SignerKey, '') = context.OwnerKey\n and coalesce(new.SignerSig, '') = context.Signature\n ),\n -- Distinct 'reissue' tag, binding ReissuedAt, so neither approval replays as the other.\n constraint AuthorizedReissue check on update (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey\n and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId)\n and verify(digest('CadreControl.Revocation', 'reissue', party_id(),\n new.TableName, new.RowKey, new.StampId, cast(new.ReissuedAt as text)),\n context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text, Signature text);\n}\n\napply schema CadreControl;\n";
35
+ export declare const CONTROL_SCHEMA_DECLARATION = "-- A Sereus party's cadre (its nodes) and their participation in strands (networks).\n--\n-- Every write is authorized by a check: an owner signs an action-tagged digest of the row\n-- (cadre-core control-authorization.ts), or a peer signs its own record. Each digest leads\n-- with a 'CadreControl.<Table>' domain tag, an action tag and party_id(), the party this\n-- machine is configured for (a zero-argument function ControlDatabase registers on its own\n-- engine, never read from a context value or a replicated table), so an approval verifies\n-- only against the one rule, in the one party, it was minted for: two parties that share\n-- an owner key never accept each other's approvals. FormationUsage is the exception, its\n-- two digests scoped by the invitation token instead (FormationUsage.Authorized).\n--\n-- Rows of the guarded tables (OwnerKey, ValidationKey, Strand, StrandPartyKey, JoinedStrand,\n-- JoinRequest, CadrePeer, DeviceToken, CadreInvite, OwnerGrant) carry a one-off StampId bound\n-- into their add and remove digests. `unique` holds over live rows only, so a delete retires\n-- the stamp into Revocation in the same transaction (RevocationRecorded) and an insert refuses\n-- a retired stamp (NotRevoked): the never-expiring add approval cannot re-seat a removed row.\n-- A legitimate re-add mints a fresh stamp and a fresh signature. CadreInvite and OwnerGrant\n-- are the two guarded tables that are never deleted: a tombstone there withdraws the\n-- invitation or grant while the row stays, because the rows it admitted prove themselves\n-- through it.\n--\n-- A check whose expression evaluates to null passes (SQL three-valued logic), so every\n-- comparison over a nullable column that is meant to pin a value is written null-safe\n-- (`coalesce(col, '')` or an explicit `is null`); a bare `new.Col = context.X` over a null\n-- column would pass with nothing pinned.\n--\n-- A check with a subquery defers to commit, so a sibling row written in the same transaction\n-- is visible to it; `committed.<Table>` reads the pre-transaction snapshot instead. Every\n-- check reads the rows this node holds: a node that has not converged on a Revocation row\n-- still accepts the replayed add, and the resurrected row then coexists with its tombstone.\n-- ControlDatabase drops retired stamps on the reads that matter (queryCadrePeers,\n-- queryPendingJoins, getOwnerKeys), and every owner lookup in the checks below drops them\n-- too (the `not exists ... Revocation` conjunct beside each OwnerKey read, reading live\n-- Revocation so a tombstone filed in the same transaction counts), so a removal is\n-- effective at the writer as soon as its tombstone arrives, whether or not the row's delete\n-- has. Only the founding count in OwnerKey.Authorized stays raw.\ndeclare schema CadreControl {\n -- A key that can authorize control changes. A row is seated one of three ways, told apart\n -- by the vouch columns: the founding row (all three null), an owner-signed add (VouchOwner\n -- and VouchSig pinned to the insert context, VouchUsage null), or an owner grant redeemed\n -- by consent (VouchOwner = the grant's IssuerKey, VouchSig null, VouchUsage = the\n -- OwnerGrantUsage row that seated it). A machine invitation (CadreInvite) admits a machine\n -- and never seats a row here: a machine's identity key is never an owner key. A reader\n -- judges the stored proof against its node-local anchor, never against this replicated\n -- table.\n table OwnerKey (\n Key text primary key,\n Label text null, -- the user's name for the key (\"phone\", \"paper backup\"); bound into the\n -- signed add, so a relayed approval cannot seat the key under another name\n StampId text unique,\n VouchOwner text null,\n VouchSig text null,\n VouchUsage text null,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = new.StampId)\n ),\n -- Never empty: the add digest binds coalesce(Label, ''), so an empty label would sign as none.\n constraint LabelLength check (\n Label is null or length(Label) between 1 and 64\n ),\n -- The tombstone must name the removed row, not just its stamp (Revocation.RowKey).\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = old.StampId and R.RowKey = old.Key)\n ),\n -- Every other table's checks require an owner, so an empty table is a permanent denial of\n -- the party's control plane. Counted post-delete, on this node's rows only: two partitioned\n -- nodes removing different owners can still converge to zero.\n constraint MinOneOwner check on delete (\n (select count(1) from OwnerKey) >= 1\n ),\n -- Rotation is add-then-remove. An update branch would let the sole owner be re-pointed at\n -- another key under a post-image count of one.\n constraint NoUpdate check on update (false),\n -- The authorizer is read from the pre-transaction snapshot: the check defers to commit, by\n -- which point the inserted row (and any sibling) is live, so a plain OwnerKey read would let\n -- a row authorize itself or two strangers seat each other. The founding transaction, whose\n -- pre-transaction owner set is empty, needs no authorization; gating on the pre-transaction\n -- count keeps a same-transaction swap of the sole owner off that branch. That count is\n -- deliberately raw, the one owner read here that ignores Revocation: a machine holding only\n -- a retired owner row must still be refused a second genesis, or a removed device could\n -- re-found the party on its own copy. An owner cannot sign its own removal.\n -- The consent branch seats an owner with no signature at all: an OwnerGrantUsage row\n -- written in the same transaction names this exact row (Key and stamp), and the grant\n -- it redeemed was issued by the stored VouchOwner and carries the row's label (the\n -- issuer's choice, bound into the grant's signature). The grant is read from its\n -- committed table, so it cannot be seated and redeemed in one transaction; the usage\n -- row carries the liveness conditions.\n constraint Authorized check on insert, delete (\n (old.Key is null and (select count(1) from committed.OwnerKey) = 0\n and new.VouchOwner is null and new.VouchSig is null and new.VouchUsage is null)\n or (old.Key is null and exists (select 1 from committed.OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.OwnerKey', 'add', party_id(), new.Key, coalesce(new.Label, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null)\n or (old.Key is null and new.VouchSig is null and new.VouchUsage is not null\n and exists (\n select 1 from OwnerGrantUsage U\n where U.UsageStampId = new.VouchUsage\n and U.SeatedKey = new.Key\n and U.SeatedStampId = new.StampId\n and exists (select 1 from committed.OwnerGrant G\n where G.Key = U.GrantKey and G.IssuerKey = new.VouchOwner\n and coalesce(G.Label, '') = coalesce(new.Label, ''))\n ))\n or (new.Key is null and exists (select 1 from committed.OwnerKey A where A.Key = context.OwnerKey and A.Key <> old.Key and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.OwnerKey', 'remove', party_id(), old.Key, old.StampId), context.Signature, A.Key, 'ed25519')))\n )\n -- The tag is the version of this whole schema (cadre-core CONTROL_SCHEMA_VERSION): every edit\n -- to the schema bumps it, and a warm start reads the store's copy before applying anything.\n ) with tags (\"sereus.controlSchemaVersion\" = 3) with context (OwnerKey text null, Signature text null);\n\n -- A key that can validate a strand formation disclosure (FormationUsage.Authorized).\n table ValidationKey (\n Key text primary key,\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'ValidationKey' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'ValidationKey' and R.StampId = old.StampId and R.RowKey = old.Key)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.ValidationKey', 'add', party_id(), new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- Owner-signed over the stored row, or a reap: a committed tombstone naming this exact row\n -- incarnation lets a node that was offline at removal time delete the stale row unsigned\n -- (ControlDatabase.reapRevokedRow). committed.*, so a tombstone filed in this transaction\n -- cannot stand in for the signature; the stamp clause, so a tombstone of an earlier\n -- incarnation cannot remove the current one.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.ValidationKey', 'remove', party_id(), old.Key, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'ValidationKey' and R.RowKey = old.Key and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A network of members sharing an sApp database, each contributing its cadre's peers. This\n -- party's own strands; a strand joined from another party is a JoinedStrand row.\n table Strand (\n Id text primary key, -- UUID\n MemberPrivateKey text null unique, -- the strand's shared read secret; closed strands only\n Type text, -- 'o' open | 'c' closed: open gates writes in the sApp, only closed gates reads\n FounderOwnerKey text null, -- the owner key that signed the insert; null on a consent-seated strand,\n -- whose insert carries no signature. Provenance, not content (strandRowMismatches).\n FounderPeerId text null, -- peer id of the machine that runs the founder bootstrap (cadre-node.ts\n -- launchStrand), bound into the signed add; null on a consent-seated strand.\n -- Not pinned to a CadrePeer row: a solo founder publishes before its own\n -- row exists, and only an owner can name it. Provenance, as FounderOwnerKey.\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'Strand' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'Strand' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n -- The consent branch below carries no signature, so an update rule would let anyone rewrite\n -- a consent-seated strand.\n constraint NoUpdate check on update (false),\n -- Owner-signed, with FounderOwnerKey pinned to the verified signer and FounderPeerId bound\n -- into the signature; or consent-seated by a FormationUsage row naming this exact row (id\n -- and stamp), written in the same transaction (ControlDatabase.redeemInvitation). Binding\n -- the stamp is what makes a removal stick:\n -- the usage row outlives the strand, and keyed on the id alone it would re-authorize an\n -- unsigned re-seat under a fresh stamp. The consent branch is narrow, each clause closing\n -- one door:\n -- * open and keyless: a closed strand with a caller-chosen read secret has no legitimate\n -- producer;\n -- * through an unbound invite only: a bound invite's host strand is owner-provisioned,\n -- and consent-seating its id would downgrade the real row on a node it has not reached;\n -- * no usage row under another stamp, and no tombstone for the id: an id is consent-seated\n -- once, ever, and never after any removal. Re-joining a removed id is owner-signed.\n -- A standalone tombstone (no accompanying delete, Revocation.Authorized) lets an owner\n -- foreclose consent-seating of an id that never existed: owner-only, and ids are random.\n constraint AuthorizedInsert check on insert (\n (exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Strand', 'add', party_id(), new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.FounderPeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and new.FounderOwnerKey = context.OwnerKey\n and new.FounderPeerId is not null)\n or (\n new.Type = 'o'\n and new.MemberPrivateKey is null\n and new.FounderOwnerKey is null\n and new.FounderPeerId is null\n and exists (\n select 1 from FormationUsage FU\n where FU.StrandId = new.Id\n and FU.StrandStampId = new.StampId\n and exists (select 1 from FormationInvite FI where FI.Token = FU.Token and FI.StrandId is null)\n )\n and not exists (\n select 1 from FormationUsage FU2\n where FU2.StrandId = new.Id and FU2.StrandStampId <> new.StampId\n )\n and not exists (\n select 1 from Revocation R\n where R.TableName = 'Strand' and R.RowKey = new.Id\n )\n )\n ),\n -- No consent branch (an invitation forms a strand, never destroys one) and no reap branch:\n -- MemberPrivateKey is stored nowhere else, so this delete is unrecoverable\n -- (tickets/backlog/debt-strand-tombstone-reap.md owns any change).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Strand', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- An open strand has no membership gate, so no member key.\n constraint MemberKeyClosedOnly check (\n new.MemberPrivateKey is null or new.Type = 'c'\n )\n ) with context (OwnerKey text, Signature text);\n\n -- This party's own membership identity for one strand: the private key whose public half\n -- seats its Strand.Member / Strand.Manager rows. Separate from Strand.MemberPrivateKey, which\n -- every joining party receives and so could forge (gotchoices/sereus#4). A side table because\n -- a joiner holds no Strand row. Replicated in plaintext to every machine the party owns\n -- (docs/strands.md \u2192 \"Closed-Strand Member Key Handling\") but never put on the formation wire.\n table StrandPartyKey (\n Id text primary key, -- the strand id\n PrivateKey text, -- ed25519, base64 protobuf, as Strand.MemberPrivateKey (cadre-core strandMemberKeyPair)\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n -- Binding PrivateKey means a captured approval can only reproduce the key it approved.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.StrandPartyKey', 'add', party_id(), new.Id, new.PrivateKey, new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- No reap branch: the key is stored nowhere else (as Strand.AuthorizedDelete).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.StrandPartyKey', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A strand this party joined from another party, whose control database holds the Strand\n -- row. Every machine of the party reads it, so a replica host keeps a copy and the party's\n -- other machines can attach. A separate table because every reader of Strand takes it to\n -- mean this party's own strand. MemberPrivateKey is held by the founding party and every\n -- member, so a removed row is recoverable by re-forming, which is why this table has a reap\n -- branch (unlike Strand and StrandPartyKey).\n table JoinedStrand (\n Id text primary key, -- the founder's Strand.Id\n Type text, -- 'o' | 'c', as Strand.Type\n MemberPrivateKey text null, -- the closed strand's shared read secret\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'JoinedStrand' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinedStrand' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n -- Readers cast Type (ControlDatabase.queryJoinedStrands).\n constraint KnownType check (new.Type = 'o' or new.Type = 'c'),\n constraint MemberKeyClosedOnly check (new.MemberPrivateKey is null or new.Type = 'c'),\n -- Owner-signed only: every always-on machine of the party downloads the strands named\n -- here, so a machine that is not an owner keeps its joins machine-local.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinedStrand', 'add', party_id(), new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinedStrand', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'JoinedStrand' and R.RowKey = old.Id and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A join this party asked for by redeeming another party's invitation, kept party-wide so\n -- the request outlives the device that asked and any owner machine can carry it on\n -- (docs/strands.md \u2192 \"Joining while the inviter is offline\"). The row is the request alone\n -- and never changes; its outcome is a JoinSuccess or JoinFailure row keyed by this row's\n -- stamp, so asking again (a fresh stamp) starts clean and a dismissal's tombstone retires\n -- the outcome with the request. Invitation is a bearer credential, replicated in plaintext\n -- to every machine the party owns, under the same accepted risk as JoinedStrand.\n -- Owner-signed only; whether a machine that is not an owner may write here is\n -- tickets/blocked/decide-non-owner-machine-completes-a-pending-join.md.\n -- The three tables reference only tables declared before them, so an apply that fails\n -- midway can drop them in reverse order. The two rules that would close a cycle are the\n -- writer's (ControlDatabase): a request's delete takes its outcome row with it, and a join\n -- recorded over a failure deletes the failure. An outcome row left behind is unreadable,\n -- since readers start from the request.\n table JoinRequest (\n Id text primary key, -- base64url sha256 of the invitation token (cadre-core pendingJoinId),\n -- so the token never reaches Revocation\n Invitation text, -- CadreNode.encodeInvitation\n Disclosure text, -- canonicalJson of the disclosure the requester gave\n RequestedAt integer, -- epoch ms\n ExpiresAt integer, -- epoch ms; no attempt starts at or after it\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinRequest', 'add', party_id(), new.Id, new.Invitation, new.Disclosure, cast(new.RequestedAt as text), cast(new.ExpiresAt as text), new.StampId),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Reapable: the inviting party and the user hold the invitation too.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinRequest', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'JoinRequest' and R.RowKey = old.Id and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- The outcome of a JoinRequest that joined: the strand the formation returned and, for a\n -- closed strand, the single-use membership invitation it delivered. Keyed by the request's\n -- stamp, so it names one request incarnation and a replayed approval can only re-seat the\n -- row it approved, against the request it approved. A join is final: it may be recorded\n -- over a failure (whose row the writer deletes with it), and nothing is recorded over it.\n table JoinSuccess (\n RequestStampId text primary key,\n RecordedAt integer, -- epoch ms\n StrandId text,\n MembershipInvite text null, -- JSON {inviteKey, invitePrivateKey}, the StrandMembershipInvite; closed strands only\n constraint RequestExists check on insert (\n exists (select 1 from JoinRequest R where R.StampId = new.RequestStampId)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinSuccess', 'add', party_id(), new.RequestStampId, cast(new.RecordedAt as text), new.StrandId, coalesce(new.MembershipInvite, '')),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Removed only with its request: the tombstone retiring the request's stamp (owner-signed,\n -- or already committed for a reap) authorizes this delete.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.RequestStampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- The outcome of a JoinRequest that failed for good. Keyed as JoinSuccess is.\n table JoinFailure (\n RequestStampId text primary key,\n RecordedAt integer, -- epoch ms\n Code text, -- a FormationRejectionCode, 'expired' or 'local'\n Reason text, -- human-readable\n constraint RequestExists check on insert (\n exists (select 1 from JoinRequest R where R.StampId = new.RequestStampId)\n ),\n -- A join is final: nothing records a failure over it.\n constraint NotJoined check on insert (\n not exists (select 1 from JoinSuccess S where S.RequestStampId = new.RequestStampId)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinFailure', 'add', party_id(), new.RequestStampId, cast(new.RecordedAt as text), new.Code, new.Reason),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Removed with its request (JoinSuccess.AuthorizedDelete), or superseded by a join\n -- recorded in the same transaction.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.RequestStampId)\n or exists (select 1 from JoinSuccess S where S.RequestStampId = old.RequestStampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- A node of the cadre, carrying its self-published, freshness-stamped, self-signed address\n -- record (cadre-core PeerAddressRecord): a resolver re-verifies Sig against PublicKey,\n -- checks UpdatedAt and trust, then dials Multiaddr.\n table CadrePeer (\n PeerId text primary key,\n PublicKey text null, -- ed25519 (base64url) behind PeerId; null if not ed25519\n Multiaddr text, -- comma-joined current addrs (signaling / p2p-circuit first)\n UpdatedAt int null, -- epoch ms; strictly increases per self-update\n Sig text null, -- self-signature over the 'publish' digest; null until self-published\n StampId text unique,\n VouchOwner text null, -- the owner key that vouched this membership (== insert context.OwnerKey), or\n -- the issuer of the invitation that admitted it (CadreInvite.IssuerKey)\n VouchSig text null, -- that owner's 'vouch' signature (== insert context.Signature); null on an\n -- invitation-admitted row. A reader checks VouchOwner against its node-local\n -- trusted-owner anchor, not against this replicated table, which a self-owner\n -- can pollute.\n VouchUsage text null, -- the CadreInviteUsage.UsageStampId that admitted this row; null on an\n -- owner-vouched row. A row carries exactly one proof: a signature or a usage.\n Profile text null, -- the machine's own node profile (cadre-core NodeProfile): 'transaction' is an\n -- app (phone, browser), 'storage' an always-on node; null until it self-publishes.\n -- Self-declared and covered by Sig. No insert rule constrains it: an owner could\n -- only mislabel a machine it already has full authority over, and that machine's\n -- next self-publish corrects it.\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'CadrePeer' and R.StampId = new.StampId)\n ),\n constraint KnownProfile check (new.Profile is null or new.Profile in ('transaction', 'storage')),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'CadrePeer' and R.StampId = old.StampId and R.RowKey = old.PeerId)\n ),\n -- The owner vouches membership and the PublicKey<->PeerId binding (cadre-core derives\n -- PublicKey from PeerId) over (PeerId, StampId); the stored voucher must be the pair\n -- verified here. The same 'vouch' digest serves the owner branch of AuthorizedUpdate:\n -- both mean \"this owner vouches this membership row\".\n -- Or admitted by consent: a CadreInviteUsage row written in the same transaction names\n -- this exact row incarnation (PeerId, StampId and PublicKey), and the invitation it\n -- redeemed, read from committed.CadreInvite, was issued by the stored VouchOwner. The\n -- usage row carries the liveness conditions (unexpired, not withdrawn, uses left,\n -- issuer still an owner); this branch only requires that it exists. A reader verifies\n -- the chain row -> usage -> invitation against its anchor (cadre-core\n -- verifyInvitationAdmission), so the invitation row must outlive the rows it admitted.\n -- A removed device may redeem a still-live invitation again and come back: keeping a\n -- device out is removing its row AND withdrawing the invitation.\n constraint AuthorizedInsert check on insert (\n (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null\n )\n or (\n new.VouchSig is null\n and new.VouchOwner is not null\n and new.VouchUsage is not null\n and exists (\n select 1 from CadreInviteUsage U\n where U.UsageStampId = new.VouchUsage\n and U.PeerId = new.PeerId\n and U.PeerStampId = new.StampId\n and U.PeerKey = new.PublicKey\n and exists (select 1 from committed.CadreInvite I where I.Key = U.InviteKey and I.IssuerKey = new.VouchOwner)\n )\n )\n ),\n -- The stored 'vouch' voucher never authorizes a delete; the 'remove' signature rides in\n -- context and is never stored. Or a reap (ValidationKey.AuthorizedDelete).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'remove', party_id(), old.PeerId, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'CadrePeer' and R.RowKey = old.PeerId and R.StampId = old.StampId)\n ),\n -- A peer re-publishes its own addrs, profile and freshness under its own key (cadre-core\n -- peer-record.ts); everything else is immutable and UpdatedAt must increase. Or an owner\n -- re-vouches the row over its current stamp, which also clears VouchUsage: the row then\n -- stands on the signature and its invitation is no longer consulted for it. The owner\n -- branch keeps what the machine itself declares (PublicKey, Multiaddr, Sig, Profile): the\n -- row's stored VouchSig is a valid re-vouch signature, so any machine holding the row can\n -- take this branch. NOTE: it may still bump UpdatedAt (reauthorizeCadrePeer's re-broadcast\n -- needs it), which on a self-published row leaves Sig unverifiable until the machine's\n -- next heartbeat re-signs past it; if that window ever matters, pin UpdatedAt here when\n -- old.Sig is not null. StampId never\n -- changes on update: an update that rotated it would leave the old stamp free and\n -- untombstoned, so retiring a stamp is delete plus tombstone. The proof columns are\n -- compared null-safe: VouchSig and VouchUsage are null on one of the two row kinds, and a\n -- bare `new.X = old.X` over a null would let a self-publish fill the column in.\n constraint AuthorizedUpdate check on update (\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 coalesce(new.VouchSig, '') = coalesce(old.VouchSig, '')\n and coalesce(new.VouchUsage, '') = coalesce(old.VouchUsage, '')\n and new.UpdatedAt > coalesce(old.UpdatedAt, 0)\n and verify(\n digest('CadreControl.CadrePeer', 'publish', party_id(), new.PeerId, new.Multiaddr, cast(new.UpdatedAt as text), coalesce(new.Profile, '')),\n new.Sig, new.PublicKey, 'ed25519')\n )\n or (\n new.StampId = old.StampId\n and exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null\n and coalesce(new.PublicKey, '') = coalesce(old.PublicKey, '')\n and coalesce(new.Multiaddr, '') = coalesce(old.Multiaddr, '')\n and coalesce(new.Sig, '') = coalesce(old.Sig, '')\n and coalesce(new.Profile, '') = coalesce(old.Profile, '')\n )\n )\n ) with context (OwnerKey text null, Signature text);\n\n -- A mobile peer's platform push token, self-published so a server peer can push-wake a\n -- suspended app. A resolver re-verifies Sig against the CadrePeer.PublicKey for PeerId\n -- (cadre-core device-token.ts), checks freshness, then hands the token to the push sender.\n table DeviceToken (\n PeerId text primary key,\n Platform text, -- 'fcm' | 'apns'\n Token text, -- opaque platform device/registration token\n UpdatedAt int null, -- epoch ms; strictly increases per self-update\n Sig text null, -- self-signature over the 'publish' digest\n StampId text unique,\n -- A resurrected token has no freshness ceiling to retire it (resolveDeviceToken defaults\n -- maxAgeMs to infinity), so stamp retirement is what makes a clear stick.\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'DeviceToken' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'DeviceToken' and R.StampId = old.StampId and R.RowKey = old.PeerId)\n ),\n -- The owner signs the whole row, nullable columns as '', so a replayed approval can only\n -- reproduce the row it approved (cadre-core peer-authorization.ts deviceTokenAddDigest).\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.DeviceToken', 'add', party_id(),\n new.PeerId,\n new.Platform,\n new.Token,\n coalesce(cast(new.UpdatedAt as text), ''),\n coalesce(new.Sig, ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.DeviceToken', 'remove', party_id(), old.PeerId, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'DeviceToken' and R.RowKey = old.PeerId and R.StampId = old.StampId)\n ),\n -- Self-update only: Platform and Token may change, PeerId and StampId may not, UpdatedAt\n -- must increase. No owner re-touch branch: one outside the monotonicity rule would let a\n -- captured approval roll UpdatedAt back; an owner correcting a row deletes and re-inserts.\n constraint AuthorizedUpdate check on update (\n new.PeerId = old.PeerId\n and new.StampId = old.StampId\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('CadreControl.DeviceToken', 'publish', party_id(), new.PeerId, new.Platform, new.Token, cast(new.UpdatedAt as text)),\n new.Sig, P.PublicKey, '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, -- a random string\n sAppId text, -- the app of the strand to be formed\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n ValidationUrl text null, -- web hook sent the disclosure; a ValidationKey then signs off each redemption\n StrandId text null, -- bound invite: the host strand consent is recorded against;\n -- null: the responder provisions a fresh open strand per redemption\n StampId text unique,\n -- The owner signs the whole row, nullable columns as ''.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.FormationInvite', 'add', party_id(),\n new.Token,\n new.sAppId,\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n coalesce(new.ValidationUrl, ''),\n coalesce(new.StrandId, ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.FormationInvite', 'remove', party_id(),\n old.Token,\n old.sAppId,\n coalesce(cast(old.ExpiresAt as text), ''),\n coalesce(cast(old.TotalUses as text), ''),\n coalesce(old.ValidationUrl, ''),\n coalesce(old.StrandId, ''),\n old.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Insert and delete only, so the consent parameters cannot change under a signature.\n constraint Immutable check on update (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of formation invite redemptions, one row per redemption.\n table FormationUsage (\n Token text, -- the FormationInvite redeemed\n UsageStampId text, -- single-use nonce minted by the joining peer, signed into its 'consent' digest\n -- and the approver's 'vouch' digest. The primary key: this table never\n -- deletes, so the key alone makes a nonce permanent and there is no\n -- NotRevoked pair here. Two nodes that have not converged can each admit\n -- the same nonce: a duplicated audit row, never a join nobody approved.\n PeerKey text, -- the joining peer's ed25519 public key (base64url); its peer id derives from it\n PeerSig text, -- the joiner's signature over the 'consent' digest (PeerConsented)\n Disclosure text,\n StrandId text,\n StrandStampId text, -- the Strand.StampId this consent names: one strand row, not an id forever\n -- (Strand.AuthorizedInsert). Not unique: a bound invite's redeemers share\n -- a host strand.\n primary key (UsageStampId),\n constraint InsertOnly check on update, delete (false),\n -- The use cap counts the pre-transaction snapshot, so concurrent redeemers on separate\n -- nodes can over-admit by their number; both rows survive and removal is owner-gated. A\n -- strict per-token sequence was rejected: a lost race would silently drop a consented\n -- join. Two usage rows for one token in one transaction read the same count; no writer\n -- does that.\n -- A ValidationUrl invite needs a sign-off verified against a STORED ValidationKey row;\n -- context.ValidationKey only selects which row, so a redeemer cannot approve itself with\n -- a throwaway key. The sign-off covers (Token, UsageStampId, StrandId, PeerKey,\n -- Disclosure), so it buys exactly one row; StrandStampId is not bound because the\n -- unbound path mints it inside the redeeming transaction. Removing the key later does\n -- not revisit rows it approved.\n -- A bound invite may only name its own host strand. An unbound invite may name any\n -- existing strand, which burns a use and forecloses that id's consent-seating: harmless.\n -- Neither digest of this table binds party_id(), alone in this schema: the joiner signs\n -- its consent before the responder's party id reaches it (it arrives in the result\n -- frame) and with a key made fresh for this one formation, and the outside approver\n -- behind ValidationUrl is sent only the five fields below. Both digests bind the\n -- invitation Token, a random secret seated in one party's FormationInvite table, so\n -- replaying either in another party needs that party's owner to seat the same token in\n -- its own database, and the only database harmed would be its own.\n constraint Authorized check on insert (\n exists (\n select 1 from FormationInvite FI\n where FI.Token = new.Token\n and (FI.TotalUses is null or FI.TotalUses > (select count(1) from committed.FormationUsage U where U.Token = new.Token))\n and (FI.ExpiresAt is null or FI.ExpiresAt > context.Now)\n and (FI.ValidationUrl is null or exists (\n select 1 from ValidationKey VK\n where VK.Key = context.ValidationKey\n and verify(digest('CadreControl.FormationUsage', 'vouch', new.Token, new.UsageStampId, new.StrandId, new.PeerKey, new.Disclosure), context.ValidationSignature, VK.Key, 'ed25519')))\n and (FI.StrandId is null or FI.StrandId = new.StrandId)\n )\n ),\n -- A usage row may only name a strand row that exists, so a consent record cannot be held\n -- in reserve to re-seat an id after its removal.\n constraint StrandExists check (exists (select 1 from Strand S where S.Id = new.StrandId and S.StampId = new.StrandStampId)),\n -- The joiner proves it agreed. PeerKey is its own key, so there is nothing for a writer to\n -- substitute; stored on the row so any reader can re-check it (verifyFormationConsent).\n -- StrandId and the party id are not signed: the joiner cannot know them when it signs\n -- (Authorized, above).\n constraint PeerConsented check on insert (\n verify(digest('CadreControl.FormationUsage', 'consent',\n new.Token, new.UsageStampId, new.PeerKey, new.Disclosure),\n new.PeerSig, new.PeerKey, 'ed25519')\n )\n ) with context (Now datetime, ValidationKey text null, ValidationSignature text null);\n\n -- Serves the per-token reads (the cap count above; cadre-core's countFormationUsage,\n -- readToken and hasOutstandingFormationInvite) on an append-only table. Its cross-machine\n -- convergence is guarded by the integration scenario strand-formation-concurrent-redemption.\n index FormationUsageByToken on FormationUsage (Token);\n\n -- An owner-signed invitation to join this cadre as a machine, redeemable on the owner's\n -- behalf by any member machine (ControlDatabase.redeemCadreInvite). It admits a machine\n -- and grants nothing else; ownership is an OwnerGrant. The invitation is a keypair, not a\n -- bearer token: the row holds the public half and the holder proves possession by signing\n -- with the private half (CadreInviteUsage.InvitePossessed), so a member that only relays\n -- it can neither admit a machine of its own choosing with it nor spend its uses. Rows are\n -- never deleted: a device admitted\n -- through one carries no owner signature of its own, and its membership is the chain\n -- invitation -> usage -> peer row, verified at read time, so the row must outlive every\n -- device it admitted. Withdrawing an invitation is an owner-signed Revocation tombstone\n -- over the row's stamp with the row kept (Revocation.RowIsGone); liveness is read as no\n -- tombstone, unexpired, uses left, issuer still an owner (CadreInviteUsage.Authorized).\n -- NOTE: expired and withdrawn rows accumulate for the life of the party; the same tripwire\n -- as hasOutstandingFormationInvite applies (ControlDatabase.hasLiveCadreInvite). An owner\n -- retires a device's dependency on its invitation by re-vouching the row (the owner branch\n -- of CadrePeer.AuthorizedUpdate).\n table CadreInvite (\n Key text primary key, -- the invitation's ed25519 public key (base64url)\n PeerId text null, -- the device this invitation is for; null: whoever redeems first designates it\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n IssuerKey text, -- the owner that signed the row (== insert context.OwnerKey). A reader checks it\n -- against its node-local anchor, never against the replicated OwnerKey table.\n IssuerSig text, -- that owner's 'add' signature (== insert context.Signature), stored so any reader\n -- can re-verify the row from the holder's copy or the replicated one\n StampId text unique,\n -- A withdrawn invitation cannot be re-seated from the signed copy its holder keeps, on\n -- any node that holds the tombstone (schema header for the node that does not).\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'CadreInvite' and R.StampId = new.StampId)\n ),\n -- The owner signs the whole row, nullable columns as '' (cadre-core cadreInviteRowFields),\n -- and the stored issuer pair must be the pair verified here, as CadrePeer pins its voucher.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.CadreInvite', 'add', party_id(),\n new.Key,\n coalesce(new.PeerId, ''),\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n and coalesce(new.IssuerKey, '') = context.OwnerKey\n and coalesce(new.IssuerSig, '') = context.Signature\n ),\n constraint NoUpdate check on update (false),\n -- Never deleted (table comment). Withdrawal is the tombstone alone.\n constraint NoDelete check on delete (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of cadre invitation redemptions, one row per redemption: the proof a\n -- CadrePeer row was admitted by consent rather than by an owner signature.\n table CadreInviteUsage (\n UsageStampId text primary key, -- nonce minted by the redeeming device and signed into both signatures\n -- below. The primary key: this table never deletes, so the key alone\n -- makes a nonce permanent and there is no NotRevoked pair here. Two nodes\n -- that have not converged can each admit the same nonce (FormationUsage).\n InviteKey text, -- the CadreInvite redeemed\n PeerId text, -- the redeeming device: the identity multihash of PeerKey. The writer\n -- asserts the pair and a reader re-derives it (verifyInvitationAdmission);\n -- the schema cannot unwrap a multihash.\n PeerKey text, -- the redeeming device's ed25519 public key (base64url)\n PeerStampId text, -- the CadrePeer.StampId this usage seats: one row incarnation, not a peer id\n -- forever. Minted by the writer inside the redeeming transaction and not\n -- signed (FormationUsage.StrandStampId).\n InviteSig text, -- by the invitation's private key over the 'redeem' digest: proof of possession\n PeerSig text, -- by PeerKey over the 'consent' digest: the device agreed to join\n constraint InsertOnly check on update, delete (false),\n -- The invitation is read from the pre-transaction snapshot, so an invitation cannot be\n -- seated and redeemed in one transaction (the CadrePeer consent branch rests on that).\n -- NOTE: accepted tradeoff \u2014 the use cap counts committed usage, so two redeemers racing\n -- for a single-use machine invitation on different machines may both be admitted until\n -- merge, and after merge both rows stand (FormationUsage.Authorized). Serializing would\n -- need cross-machine coordination the control plane does not have; the mitigations are\n -- the single-use default, the targeted form, and the owner seeing the extra machine and\n -- removing it. Revisit if the control plane gains a cross-machine sequencer. The owner\n -- grant's half of this tradeoff is at OwnerGrantUsage.Authorized. An issuer removed\n -- since issuing takes its outstanding invitations with it (the committed.OwnerKey\n -- clause, which a tombstone alone satisfies); devices it already admitted stay members.\n constraint Authorized check on insert (\n exists (\n select 1 from committed.CadreInvite I\n where I.Key = new.InviteKey\n and not exists (select 1 from Revocation R where R.TableName = 'CadreInvite' and R.StampId = I.StampId)\n and (I.ExpiresAt is null or I.ExpiresAt > context.Now)\n and (I.TotalUses is null or I.TotalUses > (select count(1) from committed.CadreInviteUsage U where U.InviteKey = new.InviteKey))\n and (I.PeerId is null or I.PeerId = new.PeerId)\n and exists (select 1 from committed.OwnerKey K where K.Key = I.IssuerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = K.StampId))\n )\n ),\n -- Verified against the invitation key itself: the member writing the row can check this\n -- signature but, holding only the public half, cannot produce it.\n constraint InvitePossessed check on insert (\n verify(digest('CadreControl.CadreInviteUsage', 'redeem', party_id(), new.InviteKey, new.UsageStampId, new.PeerKey),\n new.InviteSig, new.InviteKey, 'ed25519')\n ),\n -- The device proves it agreed (FormationUsage.PeerConsented).\n constraint PeerConsented check on insert (\n verify(digest('CadreControl.CadreInviteUsage', 'consent', party_id(), new.InviteKey, new.UsageStampId, new.PeerKey),\n new.PeerSig, new.PeerKey, 'ed25519')\n ),\n -- Deferred, so the same-transaction peer insert counts: a usage row can never be held in\n -- reserve to seat a row later (FormationUsage.StrandExists). PeerKey must be the key the\n -- peer row stores, so the usage names the admitted machine's own key and no other.\n constraint PeerExists check (exists (\n select 1 from CadrePeer P where P.PeerId = new.PeerId and P.StampId = new.PeerStampId and P.PublicKey = new.PeerKey\n ))\n ) with context (Now datetime);\n\n -- Serves the cap count above and the liveness reads (cadre-core countCadreInviteUsage,\n -- hasLiveCadreInvite) on an append-only table, as FormationUsageByToken does.\n index CadreInviteUsageByInvite on CadreInviteUsage (InviteKey);\n\n -- An owner-signed grant of ownership to whoever holds the grant's private half: it seats an\n -- OwnerKey row for a key the holder generates or already holds, and admits no machine (a\n -- machine is admitted by a CadreInvite; peer identity and ownership are unrelated, so the\n -- row names no peer id). The grant is a keypair as the invitation is: the row holds the\n -- public half, the holder proves possession by signing with the private half\n -- (OwnerGrantUsage.GrantPossessed), and the member machine that writes the redemption can\n -- verify that signature but never produce it, so it cannot seat an owner for itself. Rows\n -- are never deleted: a key seated through one carries no owner signature of its own, and\n -- its proof is the chain grant -> usage -> OwnerKey row, verified at read time, so the row\n -- must outlive every key it seated. Withdrawal is an owner-signed Revocation tombstone over\n -- the row's stamp with the row kept (Revocation.RowIsGone); liveness is read as no\n -- tombstone, unexpired, uses left, issuer still an owner (OwnerGrantUsage.Authorized).\n -- NOTE: expired and withdrawn rows accumulate for the life of the party, as CadreInvite's.\n -- The label is the issuer's: it is bound into the grant's signature and the seated OwnerKey\n -- row must carry it (the grant branch of OwnerKey.Authorized), so a key seated on a fresh\n -- device during recovery needs no naming prompt, and nothing the holder writes names it.\n table OwnerGrant (\n Key text primary key, -- the grant's ed25519 public key (base64url)\n Label text null, -- the label the seated OwnerKey row carries, chosen by the issuer\n ExpiresAt datetime null, -- null: never. The writer defaults to a short expiry; a lifetime with\n -- no end is the issuer's explicit choice (a backup grant)\n TotalUses int null check (TotalUses >= 0),\n IssuerKey text, -- the owner that signed the row (== insert context.OwnerKey); a reader checks\n -- it against its node-local anchor, never against the replicated OwnerKey table\n IssuerSig text, -- that owner's 'add' signature (== insert context.Signature), stored so any\n -- reader can re-verify the row from the holder's copy or the replicated one\n StampId text unique,\n -- A withdrawn grant cannot be re-seated from the signed copy its holder keeps, on any\n -- node that holds the tombstone (schema header for the node that does not).\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'OwnerGrant' and R.StampId = new.StampId)\n ),\n -- Never empty, as OwnerKey.LabelLength: the add digest binds coalesce(Label, '').\n constraint LabelLength check (\n Label is null or length(Label) between 1 and 64\n ),\n -- The owner signs the whole row, nullable columns as '' (cadre-core ownerGrantRowFields),\n -- and the stored issuer pair must be the pair verified here, as CadreInvite pins its issuer.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.OwnerGrant', 'add', party_id(),\n new.Key,\n coalesce(new.Label, ''),\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n and coalesce(new.IssuerKey, '') = context.OwnerKey\n and coalesce(new.IssuerSig, '') = context.Signature\n ),\n constraint NoUpdate check on update (false),\n -- Never deleted (table comment). Withdrawal is the tombstone alone.\n constraint NoDelete check on delete (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of owner-grant redemptions, one row per redemption: the proof an\n -- OwnerKey row was seated by consent rather than by an owner signature.\n table OwnerGrantUsage (\n UsageStampId text primary key, -- nonce minted by the holder and signed into both signatures below. The\n -- primary key: this table never deletes, so the key alone makes a nonce\n -- permanent and there is no NotRevoked pair here (CadreInviteUsage).\n GrantKey text, -- the OwnerGrant redeemed\n SeatedKey text, -- the owner key seated (base64url ed25519): the holder's, fresh or already held\n SeatedStampId text, -- the OwnerKey.StampId this usage seats: one row incarnation, not a key\n -- forever. Minted by the writer inside the redeeming transaction and not\n -- signed (CadreInviteUsage.PeerStampId).\n GrantSig text, -- by the grant's private key over the 'redeem' digest: proof of possession\n OwnerSig text, -- by SeatedKey over the 'consent' digest: the holder holds the key it seats\n constraint InsertOnly check on update, delete (false),\n -- The grant is read from the pre-transaction snapshot, so a grant cannot be seated and\n -- redeemed in one transaction (the grant branch of OwnerKey.Authorized rests on that).\n -- NOTE: accepted tradeoff \u2014 the use cap counts committed usage, so two holders of one\n -- single-use grant redeeming on different machines may both be seated until merge, and\n -- after merge both rows stand (CadreInviteUsage.Authorized). Serializing would need\n -- cross-machine coordination the control plane does not have; the mitigations are the\n -- defaults the writer sets (single use and a 15-minute expiry, a longer lifetime only\n -- when the user chooses one) and the owner seeing the extra key in the owner-key list\n -- and removing it. Revisit if the control plane gains a cross-machine sequencer. An\n -- issuer removed since issuing takes its outstanding grants with it (the\n -- committed.OwnerKey clause, which a tombstone alone satisfies); keys it already\n -- seated stay owners.\n constraint Authorized check on insert (\n exists (\n select 1 from committed.OwnerGrant G\n where G.Key = new.GrantKey\n and not exists (select 1 from Revocation R where R.TableName = 'OwnerGrant' and R.StampId = G.StampId)\n and (G.ExpiresAt is null or G.ExpiresAt > context.Now)\n and (G.TotalUses is null or G.TotalUses > (select count(1) from committed.OwnerGrantUsage U where U.GrantKey = new.GrantKey))\n and exists (select 1 from committed.OwnerKey K where K.Key = G.IssuerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = K.StampId))\n )\n ),\n -- Verified against the grant key itself: the member writing the row can check this\n -- signature but, holding only the public half, cannot produce it.\n constraint GrantPossessed check on insert (\n verify(digest('CadreControl.OwnerGrantUsage', 'redeem', party_id(), new.GrantKey, new.UsageStampId, new.SeatedKey),\n new.GrantSig, new.GrantKey, 'ed25519')\n ),\n -- The holder proves it holds the key being seated (CadreInviteUsage.PeerConsented).\n constraint OwnerConsented check on insert (\n verify(digest('CadreControl.OwnerGrantUsage', 'consent', party_id(), new.GrantKey, new.UsageStampId, new.SeatedKey),\n new.OwnerSig, new.SeatedKey, 'ed25519')\n ),\n -- Deferred, so the same-transaction OwnerKey insert counts: a usage row can never be\n -- held in reserve to seat a row later (CadreInviteUsage.PeerExists).\n constraint SeatedExists check (\n exists (select 1 from OwnerKey K where K.Key = new.SeatedKey and K.StampId = new.SeatedStampId)\n )\n ) with context (Now datetime);\n\n -- Serves the cap count above and the liveness reads (cadre-core countOwnerGrantUsage,\n -- listOwnerGrantStatuses) on an append-only table, as CadreInviteUsageByInvite does.\n index OwnerGrantUsageByGrant on OwnerGrantUsage (GrantKey);\n\n -- Append-only retirement record for the stamps of removed rows of every guarded table, with\n -- the removed row's key. Every append is owner-signed, so the growth surface is the owner\n -- keys. A party founded before genesis created every control collection may hold one marker\n -- row, ('Revocation', 'ledger', 'opened'), which retires nothing: an owner filed it so this\n -- table had a collection. Nothing files it now. Readers filter on their own guarded\n -- TableName, and queryRevocations skips it. A 'CadreInvite' or 'OwnerGrant' row withdraws\n -- an invitation or a grant whose row stays (the table comments).\n table Revocation (\n TableName text, -- 'OwnerKey' | 'ValidationKey' | 'Strand' | 'StrandPartyKey' | 'JoinedStrand' |\n -- 'JoinRequest' | 'CadrePeer' | 'DeviceToken' | 'CadreInvite' | 'OwnerGrant', or\n -- 'Revocation' for an older party's marker\n RowKey text, -- primary key of the removed row\n StampId text, -- the retired stamp\n ReissuedAt integer default 0, -- bumped by an owner-signed re-issue so a tombstone committed while the\n -- node was alone can be re-written and so re-broadcast; nothing reads it\n SignerKey text null, -- the owner that filed the tombstone (== insert context.OwnerKey)\n SignerSig text null, -- that owner's 'remove' signature (== insert context.Signature). A node that\n -- received the row by replication re-verifies the pair and judges SignerKey\n -- against its node-local trusted-owner anchor (cadre-core\n -- verifyRevocationSigner), not against this replicated table.\n -- Keyed on the stamp: one row key may carry several tombstones over its life.\n primary key (TableName, StampId),\n -- Retirement is permanent.\n constraint NoDelete check on delete (false),\n -- Pinned at 0 so an owner cannot seat a tombstone at a counter its own re-issues cannot pass.\n constraint FreshTombstone check on insert (new.ReissuedAt = 0),\n -- A re-issue moves nothing but the counter, and only upward. The signer pair stays the\n -- filing owner's; the re-issuing owner's signature rides in context only.\n constraint ReissueOnly check on update (\n new.TableName = old.TableName and new.RowKey = old.RowKey\n and new.StampId = old.StampId and new.ReissuedAt > old.ReissuedAt\n and coalesce(new.SignerKey, '') = coalesce(old.SignerKey, '')\n and coalesce(new.SignerSig, '') = coalesce(old.SignerSig, '')\n ),\n -- A stamp is retired only once its row is gone (deferred, so the same-transaction delete\n -- counts). Keyed on the stamp, not RowKey: a node that converges on a re-admission before\n -- the earlier removal's tombstone must still accept that tombstone. Retiring a stamp that\n -- never existed is harmless: stamps carry 128 random bits (generateStampId). For\n -- 'CadreInvite' and 'OwnerGrant' there is no row-gone test: a tombstone there is a\n -- withdrawal, and the row stays (the table comments). The last branch admits the marker\n -- row an older party may hold, and nothing else under 'Revocation'; nothing files it now.\n -- NOTE: drop this branch, REVOCATION_LEDGER_MARKER and the readers' skip together with the\n -- next CONTROL_SCHEMA_VERSION change that already needs a migration.\n constraint RowIsGone check on insert (\n (new.TableName = 'OwnerKey' and not exists (select 1 from OwnerKey K where K.StampId = new.StampId))\n or (new.TableName = 'CadrePeer' and not exists (select 1 from CadrePeer P where P.StampId = new.StampId))\n or (new.TableName = 'ValidationKey' and not exists (select 1 from ValidationKey V where V.StampId = new.StampId))\n or (new.TableName = 'Strand' and not exists (select 1 from Strand S where S.StampId = new.StampId))\n or (new.TableName = 'StrandPartyKey' and not exists (select 1 from StrandPartyKey K where K.StampId = new.StampId))\n or (new.TableName = 'JoinedStrand' and not exists (select 1 from JoinedStrand J where J.StampId = new.StampId))\n or (new.TableName = 'JoinRequest' and not exists (select 1 from JoinRequest J where J.StampId = new.StampId))\n or (new.TableName = 'DeviceToken' and not exists (select 1 from DeviceToken D where D.StampId = new.StampId))\n or (new.TableName = 'CadreInvite')\n or (new.TableName = 'OwnerGrant')\n or (new.TableName = 'Revocation' and new.RowKey = 'ledger' and new.StampId = 'opened')\n ),\n -- Owner-signed over the whole row under its own domain tag, so the delete's 'remove'\n -- signature filed in the same transaction never doubles as the tombstone's. Ungated, any\n -- writer could evict a peer party-wide and block its re-admission. RowKey is not checked\n -- against the retired row here (that would reject a tombstone re-issued later, or filed on\n -- a node that never held the row); each guarded table's RevocationRecorded binds it\n -- instead. Reads live OwnerKey like the sibling tables. An older party's marker was signed\n -- under this rule too. The stored signer pair must be the pair verified here.\n constraint Authorized check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Revocation', 'remove', party_id(), new.TableName, new.RowKey, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.SignerKey, '') = context.OwnerKey\n and coalesce(new.SignerSig, '') = context.Signature\n ),\n -- Distinct 'reissue' tag, binding ReissuedAt, so neither approval replays as the other.\n constraint AuthorizedReissue check on update (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey\n and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId)\n and verify(digest('CadreControl.Revocation', 'reissue', party_id(),\n new.TableName, new.RowKey, new.StampId, cast(new.ReissuedAt as text)),\n context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text, Signature text);\n}";
36
+ /** The name the control schema is declared under; Quereus compares schema names without regard to case. */
37
+ export declare const CONTROL_SCHEMA_NAME = "CadreControl";
38
+ /** The statement that applies {@link CONTROL_SCHEMA_DECLARATION}, run only after the store has been checked against it. */
39
+ export declare const APPLY_CONTROL_SCHEMA = "apply schema CadreControl;";
40
+ /** The whole control schema as `schemas/control.qsql` holds it: the declaration, then the apply. */
41
+ export declare const CONTROL_SCHEMA = "-- A Sereus party's cadre (its nodes) and their participation in strands (networks).\n--\n-- Every write is authorized by a check: an owner signs an action-tagged digest of the row\n-- (cadre-core control-authorization.ts), or a peer signs its own record. Each digest leads\n-- with a 'CadreControl.<Table>' domain tag, an action tag and party_id(), the party this\n-- machine is configured for (a zero-argument function ControlDatabase registers on its own\n-- engine, never read from a context value or a replicated table), so an approval verifies\n-- only against the one rule, in the one party, it was minted for: two parties that share\n-- an owner key never accept each other's approvals. FormationUsage is the exception, its\n-- two digests scoped by the invitation token instead (FormationUsage.Authorized).\n--\n-- Rows of the guarded tables (OwnerKey, ValidationKey, Strand, StrandPartyKey, JoinedStrand,\n-- JoinRequest, CadrePeer, DeviceToken, CadreInvite, OwnerGrant) carry a one-off StampId bound\n-- into their add and remove digests. `unique` holds over live rows only, so a delete retires\n-- the stamp into Revocation in the same transaction (RevocationRecorded) and an insert refuses\n-- a retired stamp (NotRevoked): the never-expiring add approval cannot re-seat a removed row.\n-- A legitimate re-add mints a fresh stamp and a fresh signature. CadreInvite and OwnerGrant\n-- are the two guarded tables that are never deleted: a tombstone there withdraws the\n-- invitation or grant while the row stays, because the rows it admitted prove themselves\n-- through it.\n--\n-- A check whose expression evaluates to null passes (SQL three-valued logic), so every\n-- comparison over a nullable column that is meant to pin a value is written null-safe\n-- (`coalesce(col, '')` or an explicit `is null`); a bare `new.Col = context.X` over a null\n-- column would pass with nothing pinned.\n--\n-- A check with a subquery defers to commit, so a sibling row written in the same transaction\n-- is visible to it; `committed.<Table>` reads the pre-transaction snapshot instead. Every\n-- check reads the rows this node holds: a node that has not converged on a Revocation row\n-- still accepts the replayed add, and the resurrected row then coexists with its tombstone.\n-- ControlDatabase drops retired stamps on the reads that matter (queryCadrePeers,\n-- queryPendingJoins, getOwnerKeys), and every owner lookup in the checks below drops them\n-- too (the `not exists ... Revocation` conjunct beside each OwnerKey read, reading live\n-- Revocation so a tombstone filed in the same transaction counts), so a removal is\n-- effective at the writer as soon as its tombstone arrives, whether or not the row's delete\n-- has. Only the founding count in OwnerKey.Authorized stays raw.\ndeclare schema CadreControl {\n -- A key that can authorize control changes. A row is seated one of three ways, told apart\n -- by the vouch columns: the founding row (all three null), an owner-signed add (VouchOwner\n -- and VouchSig pinned to the insert context, VouchUsage null), or an owner grant redeemed\n -- by consent (VouchOwner = the grant's IssuerKey, VouchSig null, VouchUsage = the\n -- OwnerGrantUsage row that seated it). A machine invitation (CadreInvite) admits a machine\n -- and never seats a row here: a machine's identity key is never an owner key. A reader\n -- judges the stored proof against its node-local anchor, never against this replicated\n -- table.\n table OwnerKey (\n Key text primary key,\n Label text null, -- the user's name for the key (\"phone\", \"paper backup\"); bound into the\n -- signed add, so a relayed approval cannot seat the key under another name\n StampId text unique,\n VouchOwner text null,\n VouchSig text null,\n VouchUsage text null,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = new.StampId)\n ),\n -- Never empty: the add digest binds coalesce(Label, ''), so an empty label would sign as none.\n constraint LabelLength check (\n Label is null or length(Label) between 1 and 64\n ),\n -- The tombstone must name the removed row, not just its stamp (Revocation.RowKey).\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = old.StampId and R.RowKey = old.Key)\n ),\n -- Every other table's checks require an owner, so an empty table is a permanent denial of\n -- the party's control plane. Counted post-delete, on this node's rows only: two partitioned\n -- nodes removing different owners can still converge to zero.\n constraint MinOneOwner check on delete (\n (select count(1) from OwnerKey) >= 1\n ),\n -- Rotation is add-then-remove. An update branch would let the sole owner be re-pointed at\n -- another key under a post-image count of one.\n constraint NoUpdate check on update (false),\n -- The authorizer is read from the pre-transaction snapshot: the check defers to commit, by\n -- which point the inserted row (and any sibling) is live, so a plain OwnerKey read would let\n -- a row authorize itself or two strangers seat each other. The founding transaction, whose\n -- pre-transaction owner set is empty, needs no authorization; gating on the pre-transaction\n -- count keeps a same-transaction swap of the sole owner off that branch. That count is\n -- deliberately raw, the one owner read here that ignores Revocation: a machine holding only\n -- a retired owner row must still be refused a second genesis, or a removed device could\n -- re-found the party on its own copy. An owner cannot sign its own removal.\n -- The consent branch seats an owner with no signature at all: an OwnerGrantUsage row\n -- written in the same transaction names this exact row (Key and stamp), and the grant\n -- it redeemed was issued by the stored VouchOwner and carries the row's label (the\n -- issuer's choice, bound into the grant's signature). The grant is read from its\n -- committed table, so it cannot be seated and redeemed in one transaction; the usage\n -- row carries the liveness conditions.\n constraint Authorized check on insert, delete (\n (old.Key is null and (select count(1) from committed.OwnerKey) = 0\n and new.VouchOwner is null and new.VouchSig is null and new.VouchUsage is null)\n or (old.Key is null and exists (select 1 from committed.OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.OwnerKey', 'add', party_id(), new.Key, coalesce(new.Label, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null)\n or (old.Key is null and new.VouchSig is null and new.VouchUsage is not null\n and exists (\n select 1 from OwnerGrantUsage U\n where U.UsageStampId = new.VouchUsage\n and U.SeatedKey = new.Key\n and U.SeatedStampId = new.StampId\n and exists (select 1 from committed.OwnerGrant G\n where G.Key = U.GrantKey and G.IssuerKey = new.VouchOwner\n and coalesce(G.Label, '') = coalesce(new.Label, ''))\n ))\n or (new.Key is null and exists (select 1 from committed.OwnerKey A where A.Key = context.OwnerKey and A.Key <> old.Key and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.OwnerKey', 'remove', party_id(), old.Key, old.StampId), context.Signature, A.Key, 'ed25519')))\n )\n -- The tag is the version of this whole schema (cadre-core CONTROL_SCHEMA_VERSION): every edit\n -- to the schema bumps it, and a warm start reads the store's copy before applying anything.\n ) with tags (\"sereus.controlSchemaVersion\" = 3) with context (OwnerKey text null, Signature text null);\n\n -- A key that can validate a strand formation disclosure (FormationUsage.Authorized).\n table ValidationKey (\n Key text primary key,\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'ValidationKey' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'ValidationKey' and R.StampId = old.StampId and R.RowKey = old.Key)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.ValidationKey', 'add', party_id(), new.Key, new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- Owner-signed over the stored row, or a reap: a committed tombstone naming this exact row\n -- incarnation lets a node that was offline at removal time delete the stale row unsigned\n -- (ControlDatabase.reapRevokedRow). committed.*, so a tombstone filed in this transaction\n -- cannot stand in for the signature; the stamp clause, so a tombstone of an earlier\n -- incarnation cannot remove the current one.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.ValidationKey', 'remove', party_id(), old.Key, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'ValidationKey' and R.RowKey = old.Key and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A network of members sharing an sApp database, each contributing its cadre's peers. This\n -- party's own strands; a strand joined from another party is a JoinedStrand row.\n table Strand (\n Id text primary key, -- UUID\n MemberPrivateKey text null unique, -- the strand's shared read secret; closed strands only\n Type text, -- 'o' open | 'c' closed: open gates writes in the sApp, only closed gates reads\n FounderOwnerKey text null, -- the owner key that signed the insert; null on a consent-seated strand,\n -- whose insert carries no signature. Provenance, not content (strandRowMismatches).\n FounderPeerId text null, -- peer id of the machine that runs the founder bootstrap (cadre-node.ts\n -- launchStrand), bound into the signed add; null on a consent-seated strand.\n -- Not pinned to a CadrePeer row: a solo founder publishes before its own\n -- row exists, and only an owner can name it. Provenance, as FounderOwnerKey.\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'Strand' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'Strand' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n -- The consent branch below carries no signature, so an update rule would let anyone rewrite\n -- a consent-seated strand.\n constraint NoUpdate check on update (false),\n -- Owner-signed, with FounderOwnerKey pinned to the verified signer and FounderPeerId bound\n -- into the signature; or consent-seated by a FormationUsage row naming this exact row (id\n -- and stamp), written in the same transaction (ControlDatabase.redeemInvitation). Binding\n -- the stamp is what makes a removal stick:\n -- the usage row outlives the strand, and keyed on the id alone it would re-authorize an\n -- unsigned re-seat under a fresh stamp. The consent branch is narrow, each clause closing\n -- one door:\n -- * open and keyless: a closed strand with a caller-chosen read secret has no legitimate\n -- producer;\n -- * through an unbound invite only: a bound invite's host strand is owner-provisioned,\n -- and consent-seating its id would downgrade the real row on a node it has not reached;\n -- * no usage row under another stamp, and no tombstone for the id: an id is consent-seated\n -- once, ever, and never after any removal. Re-joining a removed id is owner-signed.\n -- A standalone tombstone (no accompanying delete, Revocation.Authorized) lets an owner\n -- foreclose consent-seating of an id that never existed: owner-only, and ids are random.\n constraint AuthorizedInsert check on insert (\n (exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Strand', 'add', party_id(), new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.FounderPeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and new.FounderOwnerKey = context.OwnerKey\n and new.FounderPeerId is not null)\n or (\n new.Type = 'o'\n and new.MemberPrivateKey is null\n and new.FounderOwnerKey is null\n and new.FounderPeerId is null\n and exists (\n select 1 from FormationUsage FU\n where FU.StrandId = new.Id\n and FU.StrandStampId = new.StampId\n and exists (select 1 from FormationInvite FI where FI.Token = FU.Token and FI.StrandId is null)\n )\n and not exists (\n select 1 from FormationUsage FU2\n where FU2.StrandId = new.Id and FU2.StrandStampId <> new.StampId\n )\n and not exists (\n select 1 from Revocation R\n where R.TableName = 'Strand' and R.RowKey = new.Id\n )\n )\n ),\n -- No consent branch (an invitation forms a strand, never destroys one) and no reap branch:\n -- MemberPrivateKey is stored nowhere else, so this delete is unrecoverable\n -- (tickets/backlog/debt-strand-tombstone-reap.md owns any change).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Strand', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- An open strand has no membership gate, so no member key.\n constraint MemberKeyClosedOnly check (\n new.MemberPrivateKey is null or new.Type = 'c'\n )\n ) with context (OwnerKey text, Signature text);\n\n -- This party's own membership identity for one strand: the private key whose public half\n -- seats its Strand.Member / Strand.Manager rows. Separate from Strand.MemberPrivateKey, which\n -- every joining party receives and so could forge (gotchoices/sereus#4). A side table because\n -- a joiner holds no Strand row. Replicated in plaintext to every machine the party owns\n -- (docs/strands.md \u2192 \"Closed-Strand Member Key Handling\") but never put on the formation wire.\n table StrandPartyKey (\n Id text primary key, -- the strand id\n PrivateKey text, -- ed25519, base64 protobuf, as Strand.MemberPrivateKey (cadre-core strandMemberKeyPair)\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'StrandPartyKey' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n -- Binding PrivateKey means a captured approval can only reproduce the key it approved.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.StrandPartyKey', 'add', party_id(), new.Id, new.PrivateKey, new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n -- No reap branch: the key is stored nowhere else (as Strand.AuthorizedDelete).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.StrandPartyKey', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A strand this party joined from another party, whose control database holds the Strand\n -- row. Every machine of the party reads it, so a replica host keeps a copy and the party's\n -- other machines can attach. A separate table because every reader of Strand takes it to\n -- mean this party's own strand. MemberPrivateKey is held by the founding party and every\n -- member, so a removed row is recoverable by re-forming, which is why this table has a reap\n -- branch (unlike Strand and StrandPartyKey).\n table JoinedStrand (\n Id text primary key, -- the founder's Strand.Id\n Type text, -- 'o' | 'c', as Strand.Type\n MemberPrivateKey text null, -- the closed strand's shared read secret\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'JoinedStrand' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinedStrand' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n -- Readers cast Type (ControlDatabase.queryJoinedStrands).\n constraint KnownType check (new.Type = 'o' or new.Type = 'c'),\n constraint MemberKeyClosedOnly check (new.MemberPrivateKey is null or new.Type = 'c'),\n -- Owner-signed only: every always-on machine of the party downloads the strands named\n -- here, so a machine that is not an owner keeps its joins machine-local.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinedStrand', 'add', party_id(), new.Id, new.Type, coalesce(new.MemberPrivateKey, ''), new.StampId), context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinedStrand', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'JoinedStrand' and R.RowKey = old.Id and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text, Signature text);\n\n -- A join this party asked for by redeeming another party's invitation, kept party-wide so\n -- the request outlives the device that asked and any owner machine can carry it on\n -- (docs/strands.md \u2192 \"Joining while the inviter is offline\"). The row is the request alone\n -- and never changes; its outcome is a JoinSuccess or JoinFailure row keyed by this row's\n -- stamp, so asking again (a fresh stamp) starts clean and a dismissal's tombstone retires\n -- the outcome with the request. Invitation is a bearer credential, replicated in plaintext\n -- to every machine the party owns, under the same accepted risk as JoinedStrand.\n -- Owner-signed only; whether a machine that is not an owner may write here is\n -- tickets/blocked/decide-non-owner-machine-completes-a-pending-join.md.\n -- The three tables reference only tables declared before them, so an apply that fails\n -- midway can drop them in reverse order. The two rules that would close a cycle are the\n -- writer's (ControlDatabase): a request's delete takes its outcome row with it, and a join\n -- recorded over a failure deletes the failure. An outcome row left behind is unreadable,\n -- since readers start from the request.\n table JoinRequest (\n Id text primary key, -- base64url sha256 of the invitation token (cadre-core pendingJoinId),\n -- so the token never reaches Revocation\n Invitation text, -- CadreNode.encodeInvitation\n Disclosure text, -- canonicalJson of the disclosure the requester gave\n RequestedAt integer, -- epoch ms\n ExpiresAt integer, -- epoch ms; no attempt starts at or after it\n StampId text unique,\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.StampId and R.RowKey = old.Id)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinRequest', 'add', party_id(), new.Id, new.Invitation, new.Disclosure, cast(new.RequestedAt as text), cast(new.ExpiresAt as text), new.StampId),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Reapable: the inviting party and the user hold the invitation too.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.JoinRequest', 'remove', party_id(), old.Id, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'JoinRequest' and R.RowKey = old.Id and R.StampId = old.StampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- The outcome of a JoinRequest that joined: the strand the formation returned and, for a\n -- closed strand, the single-use membership invitation it delivered. Keyed by the request's\n -- stamp, so it names one request incarnation and a replayed approval can only re-seat the\n -- row it approved, against the request it approved. A join is final: it may be recorded\n -- over a failure (whose row the writer deletes with it), and nothing is recorded over it.\n table JoinSuccess (\n RequestStampId text primary key,\n RecordedAt integer, -- epoch ms\n StrandId text,\n MembershipInvite text null, -- JSON {inviteKey, invitePrivateKey}, the StrandMembershipInvite; closed strands only\n constraint RequestExists check on insert (\n exists (select 1 from JoinRequest R where R.StampId = new.RequestStampId)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinSuccess', 'add', party_id(), new.RequestStampId, cast(new.RecordedAt as text), new.StrandId, coalesce(new.MembershipInvite, '')),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Removed only with its request: the tombstone retiring the request's stamp (owner-signed,\n -- or already committed for a reap) authorizes this delete.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.RequestStampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- The outcome of a JoinRequest that failed for good. Keyed as JoinSuccess is.\n table JoinFailure (\n RequestStampId text primary key,\n RecordedAt integer, -- epoch ms\n Code text, -- a FormationRejectionCode, 'expired' or 'local'\n Reason text, -- human-readable\n constraint RequestExists check on insert (\n exists (select 1 from JoinRequest R where R.StampId = new.RequestStampId)\n ),\n -- A join is final: nothing records a failure over it.\n constraint NotJoined check on insert (\n not exists (select 1 from JoinSuccess S where S.RequestStampId = new.RequestStampId)\n ),\n constraint NoUpdate check on update (false),\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest('CadreControl.JoinFailure', 'add', party_id(), new.RequestStampId, cast(new.RecordedAt as text), new.Code, new.Reason),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Removed with its request (JoinSuccess.AuthorizedDelete), or superseded by a join\n -- recorded in the same transaction.\n constraint AuthorizedDelete check on delete (\n exists (select 1 from Revocation R where R.TableName = 'JoinRequest' and R.StampId = old.RequestStampId)\n or exists (select 1 from JoinSuccess S where S.RequestStampId = old.RequestStampId)\n )\n ) with context (OwnerKey text null, Signature text null);\n\n -- A node of the cadre, carrying its self-published, freshness-stamped, self-signed address\n -- record (cadre-core PeerAddressRecord): a resolver re-verifies Sig against PublicKey,\n -- checks UpdatedAt and trust, then dials Multiaddr.\n table CadrePeer (\n PeerId text primary key,\n PublicKey text null, -- ed25519 (base64url) behind PeerId; null if not ed25519\n Multiaddr text, -- comma-joined current addrs (signaling / p2p-circuit first)\n UpdatedAt int null, -- epoch ms; strictly increases per self-update\n Sig text null, -- self-signature over the 'publish' digest; null until self-published\n StampId text unique,\n VouchOwner text null, -- the owner key that vouched this membership (== insert context.OwnerKey), or\n -- the issuer of the invitation that admitted it (CadreInvite.IssuerKey)\n VouchSig text null, -- that owner's 'vouch' signature (== insert context.Signature); null on an\n -- invitation-admitted row. A reader checks VouchOwner against its node-local\n -- trusted-owner anchor, not against this replicated table, which a self-owner\n -- can pollute.\n VouchUsage text null, -- the CadreInviteUsage.UsageStampId that admitted this row; null on an\n -- owner-vouched row. A row carries exactly one proof: a signature or a usage.\n Profile text null, -- the machine's own node profile (cadre-core NodeProfile): 'transaction' is an\n -- app (phone, browser), 'storage' an always-on node; null until it self-publishes.\n -- Self-declared and covered by Sig. No insert rule constrains it: an owner could\n -- only mislabel a machine it already has full authority over, and that machine's\n -- next self-publish corrects it.\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'CadrePeer' and R.StampId = new.StampId)\n ),\n constraint KnownProfile check (new.Profile is null or new.Profile in ('transaction', 'storage')),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'CadrePeer' and R.StampId = old.StampId and R.RowKey = old.PeerId)\n ),\n -- The owner vouches membership and the PublicKey<->PeerId binding (cadre-core derives\n -- PublicKey from PeerId) over (PeerId, StampId); the stored voucher must be the pair\n -- verified here. The same 'vouch' digest serves the owner branch of AuthorizedUpdate:\n -- both mean \"this owner vouches this membership row\".\n -- Or admitted by consent: a CadreInviteUsage row written in the same transaction names\n -- this exact row incarnation (PeerId, StampId and PublicKey), and the invitation it\n -- redeemed, read from committed.CadreInvite, was issued by the stored VouchOwner. The\n -- usage row carries the liveness conditions (unexpired, not withdrawn, uses left,\n -- issuer still an owner); this branch only requires that it exists. A reader verifies\n -- the chain row -> usage -> invitation against its anchor (cadre-core\n -- verifyInvitationAdmission), so the invitation row must outlive the rows it admitted.\n -- A removed device may redeem a still-live invitation again and come back: keeping a\n -- device out is removing its row AND withdrawing the invitation.\n constraint AuthorizedInsert check on insert (\n (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null\n )\n or (\n new.VouchSig is null\n and new.VouchOwner is not null\n and new.VouchUsage is not null\n and exists (\n select 1 from CadreInviteUsage U\n where U.UsageStampId = new.VouchUsage\n and U.PeerId = new.PeerId\n and U.PeerStampId = new.StampId\n and U.PeerKey = new.PublicKey\n and exists (select 1 from committed.CadreInvite I where I.Key = U.InviteKey and I.IssuerKey = new.VouchOwner)\n )\n )\n ),\n -- The stored 'vouch' voucher never authorizes a delete; the 'remove' signature rides in\n -- context and is never stored. Or a reap (ValidationKey.AuthorizedDelete).\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'remove', party_id(), old.PeerId, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'CadrePeer' and R.RowKey = old.PeerId and R.StampId = old.StampId)\n ),\n -- A peer re-publishes its own addrs, profile and freshness under its own key (cadre-core\n -- peer-record.ts); everything else is immutable and UpdatedAt must increase. Or an owner\n -- re-vouches the row over its current stamp, which also clears VouchUsage: the row then\n -- stands on the signature and its invitation is no longer consulted for it. The owner\n -- branch keeps what the machine itself declares (PublicKey, Multiaddr, Sig, Profile): the\n -- row's stored VouchSig is a valid re-vouch signature, so any machine holding the row can\n -- take this branch. NOTE: it may still bump UpdatedAt (reauthorizeCadrePeer's re-broadcast\n -- needs it), which on a self-published row leaves Sig unverifiable until the machine's\n -- next heartbeat re-signs past it; if that window ever matters, pin UpdatedAt here when\n -- old.Sig is not null. StampId never\n -- changes on update: an update that rotated it would leave the old stamp free and\n -- untombstoned, so retiring a stamp is delete plus tombstone. The proof columns are\n -- compared null-safe: VouchSig and VouchUsage are null on one of the two row kinds, and a\n -- bare `new.X = old.X` over a null would let a self-publish fill the column in.\n constraint AuthorizedUpdate check on update (\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 coalesce(new.VouchSig, '') = coalesce(old.VouchSig, '')\n and coalesce(new.VouchUsage, '') = coalesce(old.VouchUsage, '')\n and new.UpdatedAt > coalesce(old.UpdatedAt, 0)\n and verify(\n digest('CadreControl.CadrePeer', 'publish', party_id(), new.PeerId, new.Multiaddr, cast(new.UpdatedAt as text), coalesce(new.Profile, '')),\n new.Sig, new.PublicKey, 'ed25519')\n )\n or (\n new.StampId = old.StampId\n and exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.CadrePeer', 'vouch', party_id(), new.PeerId, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.VouchOwner, '') = context.OwnerKey\n and coalesce(new.VouchSig, '') = context.Signature\n and new.VouchUsage is null\n and coalesce(new.PublicKey, '') = coalesce(old.PublicKey, '')\n and coalesce(new.Multiaddr, '') = coalesce(old.Multiaddr, '')\n and coalesce(new.Sig, '') = coalesce(old.Sig, '')\n and coalesce(new.Profile, '') = coalesce(old.Profile, '')\n )\n )\n ) with context (OwnerKey text null, Signature text);\n\n -- A mobile peer's platform push token, self-published so a server peer can push-wake a\n -- suspended app. A resolver re-verifies Sig against the CadrePeer.PublicKey for PeerId\n -- (cadre-core device-token.ts), checks freshness, then hands the token to the push sender.\n table DeviceToken (\n PeerId text primary key,\n Platform text, -- 'fcm' | 'apns'\n Token text, -- opaque platform device/registration token\n UpdatedAt int null, -- epoch ms; strictly increases per self-update\n Sig text null, -- self-signature over the 'publish' digest\n StampId text unique,\n -- A resurrected token has no freshness ceiling to retire it (resolveDeviceToken defaults\n -- maxAgeMs to infinity), so stamp retirement is what makes a clear stick.\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'DeviceToken' and R.StampId = new.StampId)\n ),\n constraint RevocationRecorded check on delete (\n exists (select 1 from Revocation R where R.TableName = 'DeviceToken' and R.StampId = old.StampId and R.RowKey = old.PeerId)\n ),\n -- The owner signs the whole row, nullable columns as '', so a replayed approval can only\n -- reproduce the row it approved (cadre-core peer-authorization.ts deviceTokenAddDigest).\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.DeviceToken', 'add', party_id(),\n new.PeerId,\n new.Platform,\n new.Token,\n coalesce(cast(new.UpdatedAt as text), ''),\n coalesce(new.Sig, ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.DeviceToken', 'remove', party_id(), old.PeerId, old.StampId), context.Signature, A.Key, 'ed25519'))\n or exists (select 1 from committed.Revocation R where R.TableName = 'DeviceToken' and R.RowKey = old.PeerId and R.StampId = old.StampId)\n ),\n -- Self-update only: Platform and Token may change, PeerId and StampId may not, UpdatedAt\n -- must increase. No owner re-touch branch: one outside the monotonicity rule would let a\n -- captured approval roll UpdatedAt back; an owner correcting a row deletes and re-inserts.\n constraint AuthorizedUpdate check on update (\n new.PeerId = old.PeerId\n and new.StampId = old.StampId\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('CadreControl.DeviceToken', 'publish', party_id(), new.PeerId, new.Platform, new.Token, cast(new.UpdatedAt as text)),\n new.Sig, P.PublicKey, '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, -- a random string\n sAppId text, -- the app of the strand to be formed\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n ValidationUrl text null, -- web hook sent the disclosure; a ValidationKey then signs off each redemption\n StrandId text null, -- bound invite: the host strand consent is recorded against;\n -- null: the responder provisions a fresh open strand per redemption\n StampId text unique,\n -- The owner signs the whole row, nullable columns as ''.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.FormationInvite', 'add', party_id(),\n new.Token,\n new.sAppId,\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n coalesce(new.ValidationUrl, ''),\n coalesce(new.StrandId, ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n constraint AuthorizedDelete check on delete (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.FormationInvite', 'remove', party_id(),\n old.Token,\n old.sAppId,\n coalesce(cast(old.ExpiresAt as text), ''),\n coalesce(cast(old.TotalUses as text), ''),\n coalesce(old.ValidationUrl, ''),\n coalesce(old.StrandId, ''),\n old.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n ),\n -- Insert and delete only, so the consent parameters cannot change under a signature.\n constraint Immutable check on update (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of formation invite redemptions, one row per redemption.\n table FormationUsage (\n Token text, -- the FormationInvite redeemed\n UsageStampId text, -- single-use nonce minted by the joining peer, signed into its 'consent' digest\n -- and the approver's 'vouch' digest. The primary key: this table never\n -- deletes, so the key alone makes a nonce permanent and there is no\n -- NotRevoked pair here. Two nodes that have not converged can each admit\n -- the same nonce: a duplicated audit row, never a join nobody approved.\n PeerKey text, -- the joining peer's ed25519 public key (base64url); its peer id derives from it\n PeerSig text, -- the joiner's signature over the 'consent' digest (PeerConsented)\n Disclosure text,\n StrandId text,\n StrandStampId text, -- the Strand.StampId this consent names: one strand row, not an id forever\n -- (Strand.AuthorizedInsert). Not unique: a bound invite's redeemers share\n -- a host strand.\n primary key (UsageStampId),\n constraint InsertOnly check on update, delete (false),\n -- The use cap counts the pre-transaction snapshot, so concurrent redeemers on separate\n -- nodes can over-admit by their number; both rows survive and removal is owner-gated. A\n -- strict per-token sequence was rejected: a lost race would silently drop a consented\n -- join. Two usage rows for one token in one transaction read the same count; no writer\n -- does that.\n -- A ValidationUrl invite needs a sign-off verified against a STORED ValidationKey row;\n -- context.ValidationKey only selects which row, so a redeemer cannot approve itself with\n -- a throwaway key. The sign-off covers (Token, UsageStampId, StrandId, PeerKey,\n -- Disclosure), so it buys exactly one row; StrandStampId is not bound because the\n -- unbound path mints it inside the redeeming transaction. Removing the key later does\n -- not revisit rows it approved.\n -- A bound invite may only name its own host strand. An unbound invite may name any\n -- existing strand, which burns a use and forecloses that id's consent-seating: harmless.\n -- Neither digest of this table binds party_id(), alone in this schema: the joiner signs\n -- its consent before the responder's party id reaches it (it arrives in the result\n -- frame) and with a key made fresh for this one formation, and the outside approver\n -- behind ValidationUrl is sent only the five fields below. Both digests bind the\n -- invitation Token, a random secret seated in one party's FormationInvite table, so\n -- replaying either in another party needs that party's owner to seat the same token in\n -- its own database, and the only database harmed would be its own.\n constraint Authorized check on insert (\n exists (\n select 1 from FormationInvite FI\n where FI.Token = new.Token\n and (FI.TotalUses is null or FI.TotalUses > (select count(1) from committed.FormationUsage U where U.Token = new.Token))\n and (FI.ExpiresAt is null or FI.ExpiresAt > context.Now)\n and (FI.ValidationUrl is null or exists (\n select 1 from ValidationKey VK\n where VK.Key = context.ValidationKey\n and verify(digest('CadreControl.FormationUsage', 'vouch', new.Token, new.UsageStampId, new.StrandId, new.PeerKey, new.Disclosure), context.ValidationSignature, VK.Key, 'ed25519')))\n and (FI.StrandId is null or FI.StrandId = new.StrandId)\n )\n ),\n -- A usage row may only name a strand row that exists, so a consent record cannot be held\n -- in reserve to re-seat an id after its removal.\n constraint StrandExists check (exists (select 1 from Strand S where S.Id = new.StrandId and S.StampId = new.StrandStampId)),\n -- The joiner proves it agreed. PeerKey is its own key, so there is nothing for a writer to\n -- substitute; stored on the row so any reader can re-check it (verifyFormationConsent).\n -- StrandId and the party id are not signed: the joiner cannot know them when it signs\n -- (Authorized, above).\n constraint PeerConsented check on insert (\n verify(digest('CadreControl.FormationUsage', 'consent',\n new.Token, new.UsageStampId, new.PeerKey, new.Disclosure),\n new.PeerSig, new.PeerKey, 'ed25519')\n )\n ) with context (Now datetime, ValidationKey text null, ValidationSignature text null);\n\n -- Serves the per-token reads (the cap count above; cadre-core's countFormationUsage,\n -- readToken and hasOutstandingFormationInvite) on an append-only table. Its cross-machine\n -- convergence is guarded by the integration scenario strand-formation-concurrent-redemption.\n index FormationUsageByToken on FormationUsage (Token);\n\n -- An owner-signed invitation to join this cadre as a machine, redeemable on the owner's\n -- behalf by any member machine (ControlDatabase.redeemCadreInvite). It admits a machine\n -- and grants nothing else; ownership is an OwnerGrant. The invitation is a keypair, not a\n -- bearer token: the row holds the public half and the holder proves possession by signing\n -- with the private half (CadreInviteUsage.InvitePossessed), so a member that only relays\n -- it can neither admit a machine of its own choosing with it nor spend its uses. Rows are\n -- never deleted: a device admitted\n -- through one carries no owner signature of its own, and its membership is the chain\n -- invitation -> usage -> peer row, verified at read time, so the row must outlive every\n -- device it admitted. Withdrawing an invitation is an owner-signed Revocation tombstone\n -- over the row's stamp with the row kept (Revocation.RowIsGone); liveness is read as no\n -- tombstone, unexpired, uses left, issuer still an owner (CadreInviteUsage.Authorized).\n -- NOTE: expired and withdrawn rows accumulate for the life of the party; the same tripwire\n -- as hasOutstandingFormationInvite applies (ControlDatabase.hasLiveCadreInvite). An owner\n -- retires a device's dependency on its invitation by re-vouching the row (the owner branch\n -- of CadrePeer.AuthorizedUpdate).\n table CadreInvite (\n Key text primary key, -- the invitation's ed25519 public key (base64url)\n PeerId text null, -- the device this invitation is for; null: whoever redeems first designates it\n ExpiresAt datetime null,\n TotalUses int null check (TotalUses >= 0),\n IssuerKey text, -- the owner that signed the row (== insert context.OwnerKey). A reader checks it\n -- against its node-local anchor, never against the replicated OwnerKey table.\n IssuerSig text, -- that owner's 'add' signature (== insert context.Signature), stored so any reader\n -- can re-verify the row from the holder's copy or the replicated one\n StampId text unique,\n -- A withdrawn invitation cannot be re-seated from the signed copy its holder keeps, on\n -- any node that holds the tombstone (schema header for the node that does not).\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'CadreInvite' and R.StampId = new.StampId)\n ),\n -- The owner signs the whole row, nullable columns as '' (cadre-core cadreInviteRowFields),\n -- and the stored issuer pair must be the pair verified here, as CadrePeer pins its voucher.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.CadreInvite', 'add', party_id(),\n new.Key,\n coalesce(new.PeerId, ''),\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n and coalesce(new.IssuerKey, '') = context.OwnerKey\n and coalesce(new.IssuerSig, '') = context.Signature\n ),\n constraint NoUpdate check on update (false),\n -- Never deleted (table comment). Withdrawal is the tombstone alone.\n constraint NoDelete check on delete (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of cadre invitation redemptions, one row per redemption: the proof a\n -- CadrePeer row was admitted by consent rather than by an owner signature.\n table CadreInviteUsage (\n UsageStampId text primary key, -- nonce minted by the redeeming device and signed into both signatures\n -- below. The primary key: this table never deletes, so the key alone\n -- makes a nonce permanent and there is no NotRevoked pair here. Two nodes\n -- that have not converged can each admit the same nonce (FormationUsage).\n InviteKey text, -- the CadreInvite redeemed\n PeerId text, -- the redeeming device: the identity multihash of PeerKey. The writer\n -- asserts the pair and a reader re-derives it (verifyInvitationAdmission);\n -- the schema cannot unwrap a multihash.\n PeerKey text, -- the redeeming device's ed25519 public key (base64url)\n PeerStampId text, -- the CadrePeer.StampId this usage seats: one row incarnation, not a peer id\n -- forever. Minted by the writer inside the redeeming transaction and not\n -- signed (FormationUsage.StrandStampId).\n InviteSig text, -- by the invitation's private key over the 'redeem' digest: proof of possession\n PeerSig text, -- by PeerKey over the 'consent' digest: the device agreed to join\n constraint InsertOnly check on update, delete (false),\n -- The invitation is read from the pre-transaction snapshot, so an invitation cannot be\n -- seated and redeemed in one transaction (the CadrePeer consent branch rests on that).\n -- NOTE: accepted tradeoff \u2014 the use cap counts committed usage, so two redeemers racing\n -- for a single-use machine invitation on different machines may both be admitted until\n -- merge, and after merge both rows stand (FormationUsage.Authorized). Serializing would\n -- need cross-machine coordination the control plane does not have; the mitigations are\n -- the single-use default, the targeted form, and the owner seeing the extra machine and\n -- removing it. Revisit if the control plane gains a cross-machine sequencer. The owner\n -- grant's half of this tradeoff is at OwnerGrantUsage.Authorized. An issuer removed\n -- since issuing takes its outstanding invitations with it (the committed.OwnerKey\n -- clause, which a tombstone alone satisfies); devices it already admitted stay members.\n constraint Authorized check on insert (\n exists (\n select 1 from committed.CadreInvite I\n where I.Key = new.InviteKey\n and not exists (select 1 from Revocation R where R.TableName = 'CadreInvite' and R.StampId = I.StampId)\n and (I.ExpiresAt is null or I.ExpiresAt > context.Now)\n and (I.TotalUses is null or I.TotalUses > (select count(1) from committed.CadreInviteUsage U where U.InviteKey = new.InviteKey))\n and (I.PeerId is null or I.PeerId = new.PeerId)\n and exists (select 1 from committed.OwnerKey K where K.Key = I.IssuerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = K.StampId))\n )\n ),\n -- Verified against the invitation key itself: the member writing the row can check this\n -- signature but, holding only the public half, cannot produce it.\n constraint InvitePossessed check on insert (\n verify(digest('CadreControl.CadreInviteUsage', 'redeem', party_id(), new.InviteKey, new.UsageStampId, new.PeerKey),\n new.InviteSig, new.InviteKey, 'ed25519')\n ),\n -- The device proves it agreed (FormationUsage.PeerConsented).\n constraint PeerConsented check on insert (\n verify(digest('CadreControl.CadreInviteUsage', 'consent', party_id(), new.InviteKey, new.UsageStampId, new.PeerKey),\n new.PeerSig, new.PeerKey, 'ed25519')\n ),\n -- Deferred, so the same-transaction peer insert counts: a usage row can never be held in\n -- reserve to seat a row later (FormationUsage.StrandExists). PeerKey must be the key the\n -- peer row stores, so the usage names the admitted machine's own key and no other.\n constraint PeerExists check (exists (\n select 1 from CadrePeer P where P.PeerId = new.PeerId and P.StampId = new.PeerStampId and P.PublicKey = new.PeerKey\n ))\n ) with context (Now datetime);\n\n -- Serves the cap count above and the liveness reads (cadre-core countCadreInviteUsage,\n -- hasLiveCadreInvite) on an append-only table, as FormationUsageByToken does.\n index CadreInviteUsageByInvite on CadreInviteUsage (InviteKey);\n\n -- An owner-signed grant of ownership to whoever holds the grant's private half: it seats an\n -- OwnerKey row for a key the holder generates or already holds, and admits no machine (a\n -- machine is admitted by a CadreInvite; peer identity and ownership are unrelated, so the\n -- row names no peer id). The grant is a keypair as the invitation is: the row holds the\n -- public half, the holder proves possession by signing with the private half\n -- (OwnerGrantUsage.GrantPossessed), and the member machine that writes the redemption can\n -- verify that signature but never produce it, so it cannot seat an owner for itself. Rows\n -- are never deleted: a key seated through one carries no owner signature of its own, and\n -- its proof is the chain grant -> usage -> OwnerKey row, verified at read time, so the row\n -- must outlive every key it seated. Withdrawal is an owner-signed Revocation tombstone over\n -- the row's stamp with the row kept (Revocation.RowIsGone); liveness is read as no\n -- tombstone, unexpired, uses left, issuer still an owner (OwnerGrantUsage.Authorized).\n -- NOTE: expired and withdrawn rows accumulate for the life of the party, as CadreInvite's.\n -- The label is the issuer's: it is bound into the grant's signature and the seated OwnerKey\n -- row must carry it (the grant branch of OwnerKey.Authorized), so a key seated on a fresh\n -- device during recovery needs no naming prompt, and nothing the holder writes names it.\n table OwnerGrant (\n Key text primary key, -- the grant's ed25519 public key (base64url)\n Label text null, -- the label the seated OwnerKey row carries, chosen by the issuer\n ExpiresAt datetime null, -- null: never. The writer defaults to a short expiry; a lifetime with\n -- no end is the issuer's explicit choice (a backup grant)\n TotalUses int null check (TotalUses >= 0),\n IssuerKey text, -- the owner that signed the row (== insert context.OwnerKey); a reader checks\n -- it against its node-local anchor, never against the replicated OwnerKey table\n IssuerSig text, -- that owner's 'add' signature (== insert context.Signature), stored so any\n -- reader can re-verify the row from the holder's copy or the replicated one\n StampId text unique,\n -- A withdrawn grant cannot be re-seated from the signed copy its holder keeps, on any\n -- node that holds the tombstone (schema header for the node that does not).\n constraint NotRevoked check on insert (\n not exists (select 1 from Revocation R where R.TableName = 'OwnerGrant' and R.StampId = new.StampId)\n ),\n -- Never empty, as OwnerKey.LabelLength: the add digest binds coalesce(Label, '').\n constraint LabelLength check (\n Label is null or length(Label) between 1 and 64\n ),\n -- The owner signs the whole row, nullable columns as '' (cadre-core ownerGrantRowFields),\n -- and the stored issuer pair must be the pair verified here, as CadreInvite pins its issuer.\n constraint AuthorizedInsert check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(\n digest(\n 'CadreControl.OwnerGrant', 'add', party_id(),\n new.Key,\n coalesce(new.Label, ''),\n coalesce(cast(new.ExpiresAt as text), ''),\n coalesce(cast(new.TotalUses as text), ''),\n new.StampId\n ),\n context.Signature, A.Key, 'ed25519'))\n and coalesce(new.IssuerKey, '') = context.OwnerKey\n and coalesce(new.IssuerSig, '') = context.Signature\n ),\n constraint NoUpdate check on update (false),\n -- Never deleted (table comment). Withdrawal is the tombstone alone.\n constraint NoDelete check on delete (false)\n ) with context (OwnerKey text, Signature text);\n\n -- Append-only record of owner-grant redemptions, one row per redemption: the proof an\n -- OwnerKey row was seated by consent rather than by an owner signature.\n table OwnerGrantUsage (\n UsageStampId text primary key, -- nonce minted by the holder and signed into both signatures below. The\n -- primary key: this table never deletes, so the key alone makes a nonce\n -- permanent and there is no NotRevoked pair here (CadreInviteUsage).\n GrantKey text, -- the OwnerGrant redeemed\n SeatedKey text, -- the owner key seated (base64url ed25519): the holder's, fresh or already held\n SeatedStampId text, -- the OwnerKey.StampId this usage seats: one row incarnation, not a key\n -- forever. Minted by the writer inside the redeeming transaction and not\n -- signed (CadreInviteUsage.PeerStampId).\n GrantSig text, -- by the grant's private key over the 'redeem' digest: proof of possession\n OwnerSig text, -- by SeatedKey over the 'consent' digest: the holder holds the key it seats\n constraint InsertOnly check on update, delete (false),\n -- The grant is read from the pre-transaction snapshot, so a grant cannot be seated and\n -- redeemed in one transaction (the grant branch of OwnerKey.Authorized rests on that).\n -- NOTE: accepted tradeoff \u2014 the use cap counts committed usage, so two holders of one\n -- single-use grant redeeming on different machines may both be seated until merge, and\n -- after merge both rows stand (CadreInviteUsage.Authorized). Serializing would need\n -- cross-machine coordination the control plane does not have; the mitigations are the\n -- defaults the writer sets (single use and a 15-minute expiry, a longer lifetime only\n -- when the user chooses one) and the owner seeing the extra key in the owner-key list\n -- and removing it. Revisit if the control plane gains a cross-machine sequencer. An\n -- issuer removed since issuing takes its outstanding grants with it (the\n -- committed.OwnerKey clause, which a tombstone alone satisfies); keys it already\n -- seated stay owners.\n constraint Authorized check on insert (\n exists (\n select 1 from committed.OwnerGrant G\n where G.Key = new.GrantKey\n and not exists (select 1 from Revocation R where R.TableName = 'OwnerGrant' and R.StampId = G.StampId)\n and (G.ExpiresAt is null or G.ExpiresAt > context.Now)\n and (G.TotalUses is null or G.TotalUses > (select count(1) from committed.OwnerGrantUsage U where U.GrantKey = new.GrantKey))\n and exists (select 1 from committed.OwnerKey K where K.Key = G.IssuerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = K.StampId))\n )\n ),\n -- Verified against the grant key itself: the member writing the row can check this\n -- signature but, holding only the public half, cannot produce it.\n constraint GrantPossessed check on insert (\n verify(digest('CadreControl.OwnerGrantUsage', 'redeem', party_id(), new.GrantKey, new.UsageStampId, new.SeatedKey),\n new.GrantSig, new.GrantKey, 'ed25519')\n ),\n -- The holder proves it holds the key being seated (CadreInviteUsage.PeerConsented).\n constraint OwnerConsented check on insert (\n verify(digest('CadreControl.OwnerGrantUsage', 'consent', party_id(), new.GrantKey, new.UsageStampId, new.SeatedKey),\n new.OwnerSig, new.SeatedKey, 'ed25519')\n ),\n -- Deferred, so the same-transaction OwnerKey insert counts: a usage row can never be\n -- held in reserve to seat a row later (CadreInviteUsage.PeerExists).\n constraint SeatedExists check (\n exists (select 1 from OwnerKey K where K.Key = new.SeatedKey and K.StampId = new.SeatedStampId)\n )\n ) with context (Now datetime);\n\n -- Serves the cap count above and the liveness reads (cadre-core countOwnerGrantUsage,\n -- listOwnerGrantStatuses) on an append-only table, as CadreInviteUsageByInvite does.\n index OwnerGrantUsageByGrant on OwnerGrantUsage (GrantKey);\n\n -- Append-only retirement record for the stamps of removed rows of every guarded table, with\n -- the removed row's key. Every append is owner-signed, so the growth surface is the owner\n -- keys. A party founded before genesis created every control collection may hold one marker\n -- row, ('Revocation', 'ledger', 'opened'), which retires nothing: an owner filed it so this\n -- table had a collection. Nothing files it now. Readers filter on their own guarded\n -- TableName, and queryRevocations skips it. A 'CadreInvite' or 'OwnerGrant' row withdraws\n -- an invitation or a grant whose row stays (the table comments).\n table Revocation (\n TableName text, -- 'OwnerKey' | 'ValidationKey' | 'Strand' | 'StrandPartyKey' | 'JoinedStrand' |\n -- 'JoinRequest' | 'CadrePeer' | 'DeviceToken' | 'CadreInvite' | 'OwnerGrant', or\n -- 'Revocation' for an older party's marker\n RowKey text, -- primary key of the removed row\n StampId text, -- the retired stamp\n ReissuedAt integer default 0, -- bumped by an owner-signed re-issue so a tombstone committed while the\n -- node was alone can be re-written and so re-broadcast; nothing reads it\n SignerKey text null, -- the owner that filed the tombstone (== insert context.OwnerKey)\n SignerSig text null, -- that owner's 'remove' signature (== insert context.Signature). A node that\n -- received the row by replication re-verifies the pair and judges SignerKey\n -- against its node-local trusted-owner anchor (cadre-core\n -- verifyRevocationSigner), not against this replicated table.\n -- Keyed on the stamp: one row key may carry several tombstones over its life.\n primary key (TableName, StampId),\n -- Retirement is permanent.\n constraint NoDelete check on delete (false),\n -- Pinned at 0 so an owner cannot seat a tombstone at a counter its own re-issues cannot pass.\n constraint FreshTombstone check on insert (new.ReissuedAt = 0),\n -- A re-issue moves nothing but the counter, and only upward. The signer pair stays the\n -- filing owner's; the re-issuing owner's signature rides in context only.\n constraint ReissueOnly check on update (\n new.TableName = old.TableName and new.RowKey = old.RowKey\n and new.StampId = old.StampId and new.ReissuedAt > old.ReissuedAt\n and coalesce(new.SignerKey, '') = coalesce(old.SignerKey, '')\n and coalesce(new.SignerSig, '') = coalesce(old.SignerSig, '')\n ),\n -- A stamp is retired only once its row is gone (deferred, so the same-transaction delete\n -- counts). Keyed on the stamp, not RowKey: a node that converges on a re-admission before\n -- the earlier removal's tombstone must still accept that tombstone. Retiring a stamp that\n -- never existed is harmless: stamps carry 128 random bits (generateStampId). For\n -- 'CadreInvite' and 'OwnerGrant' there is no row-gone test: a tombstone there is a\n -- withdrawal, and the row stays (the table comments). The last branch admits the marker\n -- row an older party may hold, and nothing else under 'Revocation'; nothing files it now.\n -- NOTE: drop this branch, REVOCATION_LEDGER_MARKER and the readers' skip together with the\n -- next CONTROL_SCHEMA_VERSION change that already needs a migration.\n constraint RowIsGone check on insert (\n (new.TableName = 'OwnerKey' and not exists (select 1 from OwnerKey K where K.StampId = new.StampId))\n or (new.TableName = 'CadrePeer' and not exists (select 1 from CadrePeer P where P.StampId = new.StampId))\n or (new.TableName = 'ValidationKey' and not exists (select 1 from ValidationKey V where V.StampId = new.StampId))\n or (new.TableName = 'Strand' and not exists (select 1 from Strand S where S.StampId = new.StampId))\n or (new.TableName = 'StrandPartyKey' and not exists (select 1 from StrandPartyKey K where K.StampId = new.StampId))\n or (new.TableName = 'JoinedStrand' and not exists (select 1 from JoinedStrand J where J.StampId = new.StampId))\n or (new.TableName = 'JoinRequest' and not exists (select 1 from JoinRequest J where J.StampId = new.StampId))\n or (new.TableName = 'DeviceToken' and not exists (select 1 from DeviceToken D where D.StampId = new.StampId))\n or (new.TableName = 'CadreInvite')\n or (new.TableName = 'OwnerGrant')\n or (new.TableName = 'Revocation' and new.RowKey = 'ledger' and new.StampId = 'opened')\n ),\n -- Owner-signed over the whole row under its own domain tag, so the delete's 'remove'\n -- signature filed in the same transaction never doubles as the tombstone's. Ungated, any\n -- writer could evict a peer party-wide and block its re-admission. RowKey is not checked\n -- against the retired row here (that would reject a tombstone re-issued later, or filed on\n -- a node that never held the row); each guarded table's RevocationRecorded binds it\n -- instead. Reads live OwnerKey like the sibling tables. An older party's marker was signed\n -- under this rule too. The stored signer pair must be the pair verified here.\n constraint Authorized check on insert (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId) and verify(digest('CadreControl.Revocation', 'remove', party_id(), new.TableName, new.RowKey, new.StampId), context.Signature, A.Key, 'ed25519'))\n and coalesce(new.SignerKey, '') = context.OwnerKey\n and coalesce(new.SignerSig, '') = context.Signature\n ),\n -- Distinct 'reissue' tag, binding ReissuedAt, so neither approval replays as the other.\n constraint AuthorizedReissue check on update (\n exists (select 1 from OwnerKey A where A.Key = context.OwnerKey\n and not exists (select 1 from Revocation R where R.TableName = 'OwnerKey' and R.StampId = A.StampId)\n and verify(digest('CadreControl.Revocation', 'reissue', party_id(),\n new.TableName, new.RowKey, new.StampId, cast(new.ReissuedAt as text)),\n context.Signature, A.Key, 'ed25519'))\n )\n ) with context (OwnerKey text, Signature text);\n}\n\napply schema CadreControl;\n";
42
+ /**
43
+ * The declaration statement of a control schema file (`ControlDatabaseConfig.schemaPath`),
44
+ * cut from the file's own text.
45
+ *
46
+ * Refuses a file that is anything other than one `declare schema CadreControl { … }`
47
+ * followed by one plain `apply schema CadreControl` — the shape of {@link CONTROL_SCHEMA}.
48
+ * `loadSchema` runs its own {@link APPLY_CONTROL_SCHEMA} once the store has been checked,
49
+ * so any other statement in the file, or an option on its apply, would be dropped or run
50
+ * unchecked.
51
+ */
52
+ export declare function controlSchemaFileDeclaration(source: string): string;