@ottochain/sdk 2.3.0 → 2.5.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 (287) hide show
  1. package/dist/cjs/apps/compute/index.js +33 -0
  2. package/dist/cjs/apps/compute/state-machines/compute-rule110.js +164 -0
  3. package/dist/cjs/apps/compute/state-machines/index.js +9 -0
  4. package/dist/cjs/apps/contracts/index.js +16 -9
  5. package/dist/cjs/apps/contracts/state-machines/contract-agreement.js +134 -161
  6. package/dist/cjs/apps/contracts/state-machines/contract-escrow.js +192 -170
  7. package/dist/cjs/apps/contracts/state-machines/contract-universal.js +46 -58
  8. package/dist/cjs/apps/corporate/index.js +2 -2
  9. package/dist/cjs/apps/corporate/state-machines/corp-board.js +500 -456
  10. package/dist/cjs/apps/corporate/state-machines/corp-entity.js +487 -361
  11. package/dist/cjs/apps/corporate/state-machines/corp-securities.js +769 -576
  12. package/dist/cjs/apps/corporate/state-machines/corp-shareholders.js +567 -557
  13. package/dist/cjs/apps/governance/index.js +21 -21
  14. package/dist/cjs/apps/governance/state-machines/dao-multisig.js +188 -209
  15. package/dist/cjs/apps/governance/state-machines/dao-reputation.js +181 -228
  16. package/dist/cjs/apps/governance/state-machines/dao-single.js +105 -88
  17. package/dist/cjs/apps/governance/state-machines/dao-token.js +199 -227
  18. package/dist/cjs/apps/governance/state-machines/governance-simple.js +231 -231
  19. package/dist/cjs/apps/governance/state-machines/governance-universal.js +64 -64
  20. package/dist/cjs/apps/identity/constants.js +55 -22
  21. package/dist/cjs/apps/identity/index.js +28 -12
  22. package/dist/cjs/apps/identity/state-machines/identity-agent.js +124 -158
  23. package/dist/cjs/apps/identity/state-machines/identity-oracle.js +158 -173
  24. package/dist/cjs/apps/identity/state-machines/identity-registry.js +262 -0
  25. package/dist/cjs/apps/identity/state-machines/identity-universal.js +51 -57
  26. package/dist/cjs/apps/identity/state-machines/index.js +3 -1
  27. package/dist/cjs/apps/index.js +22 -8
  28. package/dist/cjs/apps/lending/assets.js +247 -0
  29. package/dist/cjs/apps/lending/credit-scoring.js +87 -0
  30. package/dist/cjs/apps/lending/eligibility.js +105 -0
  31. package/dist/cjs/apps/lending/index.js +91 -0
  32. package/dist/cjs/apps/lending/state-machines/index.js +9 -0
  33. package/dist/cjs/apps/lending/state-machines/lending-zk-loan.js +433 -0
  34. package/dist/cjs/apps/markets/index.js +22 -12
  35. package/dist/cjs/apps/markets/state-machines/market-auction.js +116 -134
  36. package/dist/cjs/apps/markets/state-machines/market-crowdfund.js +131 -141
  37. package/dist/cjs/apps/markets/state-machines/market-group-buy.js +163 -184
  38. package/dist/cjs/apps/markets/state-machines/market-prediction.js +226 -233
  39. package/dist/cjs/apps/markets/state-machines/market-universal.js +65 -80
  40. package/dist/cjs/apps/oracles/index.js +3 -3
  41. package/dist/cjs/apps/privacy/index.js +67 -0
  42. package/dist/cjs/apps/privacy/state-machines/index.js +17 -0
  43. package/dist/cjs/apps/privacy/state-machines/mixer-ddhRing.js +286 -0
  44. package/dist/cjs/apps/privacy/state-machines/note-pool.js +476 -0
  45. package/dist/cjs/apps/staked-pool/assets.js +103 -0
  46. package/dist/cjs/apps/staked-pool/base.js +498 -0
  47. package/dist/cjs/apps/staked-pool/consumer.js +55 -0
  48. package/dist/cjs/apps/staked-pool/index.js +83 -0
  49. package/dist/cjs/apps/staked-pool/state-machines/index.js +12 -0
  50. package/dist/cjs/apps/staked-pool/state-machines/staked-pool-base.js +49 -0
  51. package/dist/cjs/apps/staked-pool/state-machines/staked-pool-oracle.js +131 -0
  52. package/dist/cjs/errors.js +3 -3
  53. package/dist/cjs/generated/google/protobuf/struct.js +5 -5
  54. package/dist/cjs/generated/google/protobuf/timestamp.js +1 -1
  55. package/dist/cjs/generated/index.js +11 -7
  56. package/dist/cjs/generated/openapi.js +6 -0
  57. package/dist/cjs/generated/ottochain/apps/contracts/v1/contract.js +51 -51
  58. package/dist/cjs/generated/ottochain/apps/corporate/v1/corporate.js +20 -21
  59. package/dist/cjs/generated/ottochain/apps/governance/v1/governance.js +14 -14
  60. package/dist/cjs/generated/ottochain/apps/identity/v1/attestation.js +5 -5
  61. package/dist/cjs/generated/ottochain/apps/identity/v1/identity.js +125 -131
  62. package/dist/cjs/generated/ottochain/apps/markets/v1/market.js +108 -108
  63. package/dist/cjs/generated/ottochain/v1/common.js +5 -5
  64. package/dist/cjs/generated/ottochain/v1/fiber.js +44 -6
  65. package/dist/cjs/generated/ottochain/v1/messages.js +1 -1
  66. package/dist/cjs/generated/ottochain/v1/records.js +1 -1
  67. package/dist/cjs/index.js +58 -7
  68. package/dist/cjs/openapi.js +2 -0
  69. package/dist/cjs/ottochain/drop-nulls.js +1 -2
  70. package/dist/cjs/ottochain/genesis-manifest.js +17 -11
  71. package/dist/cjs/ottochain/index.js +23 -13
  72. package/dist/cjs/ottochain/metagraph-client.js +81 -16
  73. package/dist/cjs/ottochain/snapshot.js +13 -16
  74. package/dist/cjs/ottochain/transaction.js +67 -14
  75. package/dist/cjs/ottochain/types.js +16 -2
  76. package/dist/cjs/privacy/index.js +14 -0
  77. package/dist/cjs/privacy/sealed-bid.js +213 -0
  78. package/dist/cjs/privacy/shield-app.js +131 -0
  79. package/dist/cjs/schema/effects.js +156 -0
  80. package/dist/cjs/schema/fiber-app.js +150 -15
  81. package/dist/cjs/schema/guard-lint.js +343 -0
  82. package/dist/cjs/schema/guards.js +246 -0
  83. package/dist/cjs/signing.js +80 -0
  84. package/dist/cjs/templates/asset-policy.js +190 -0
  85. package/dist/cjs/templates/index.js +86 -0
  86. package/dist/cjs/templates/machine.js +130 -0
  87. package/dist/cjs/templates/migration.js +39 -0
  88. package/dist/cjs/templates/state-shape.js +38 -0
  89. package/dist/cjs/types.js +2 -3
  90. package/dist/cjs/validation.js +9 -11
  91. package/dist/cjs/verify.js +9 -4
  92. package/dist/cjs/zk/commit.js +53 -0
  93. package/dist/cjs/zk/guard.js +107 -0
  94. package/dist/cjs/zk/index.js +48 -0
  95. package/dist/cjs/zk/preimage.js +37 -0
  96. package/dist/cjs/zk/prover.js +87 -0
  97. package/dist/cjs/zk/registry.js +62 -0
  98. package/dist/cjs/zk/types.js +28 -0
  99. package/dist/cjs/zk/witness.js +19 -0
  100. package/dist/esm/apps/compute/index.js +29 -0
  101. package/dist/esm/apps/compute/state-machines/compute-rule110.js +161 -0
  102. package/dist/esm/apps/compute/state-machines/index.js +5 -0
  103. package/dist/esm/apps/contracts/index.js +9 -5
  104. package/dist/esm/apps/contracts/state-machines/contract-agreement.js +135 -162
  105. package/dist/esm/apps/contracts/state-machines/contract-escrow.js +193 -171
  106. package/dist/esm/apps/contracts/state-machines/contract-universal.js +47 -59
  107. package/dist/esm/apps/corporate/index.js +2 -2
  108. package/dist/esm/apps/corporate/state-machines/corp-board.js +501 -457
  109. package/dist/esm/apps/corporate/state-machines/corp-entity.js +488 -362
  110. package/dist/esm/apps/corporate/state-machines/corp-securities.js +770 -577
  111. package/dist/esm/apps/corporate/state-machines/corp-shareholders.js +568 -558
  112. package/dist/esm/apps/governance/index.js +10 -10
  113. package/dist/esm/apps/governance/state-machines/dao-multisig.js +189 -210
  114. package/dist/esm/apps/governance/state-machines/dao-reputation.js +182 -229
  115. package/dist/esm/apps/governance/state-machines/dao-single.js +106 -89
  116. package/dist/esm/apps/governance/state-machines/dao-token.js +200 -228
  117. package/dist/esm/apps/governance/state-machines/governance-simple.js +232 -232
  118. package/dist/esm/apps/governance/state-machines/governance-universal.js +65 -65
  119. package/dist/esm/apps/identity/constants.js +52 -21
  120. package/dist/esm/apps/identity/index.js +14 -9
  121. package/dist/esm/apps/identity/state-machines/identity-agent.js +125 -159
  122. package/dist/esm/apps/identity/state-machines/identity-oracle.js +159 -174
  123. package/dist/esm/apps/identity/state-machines/identity-registry.js +259 -0
  124. package/dist/esm/apps/identity/state-machines/identity-universal.js +52 -58
  125. package/dist/esm/apps/identity/state-machines/index.js +1 -0
  126. package/dist/esm/apps/index.js +9 -5
  127. package/dist/esm/apps/lending/assets.js +231 -0
  128. package/dist/esm/apps/lending/credit-scoring.js +81 -0
  129. package/dist/esm/apps/lending/eligibility.js +99 -0
  130. package/dist/esm/apps/lending/index.js +69 -0
  131. package/dist/esm/apps/lending/state-machines/index.js +5 -0
  132. package/dist/esm/apps/lending/state-machines/lending-zk-loan.js +430 -0
  133. package/dist/esm/apps/markets/index.js +11 -7
  134. package/dist/esm/apps/markets/state-machines/market-auction.js +117 -135
  135. package/dist/esm/apps/markets/state-machines/market-crowdfund.js +132 -142
  136. package/dist/esm/apps/markets/state-machines/market-group-buy.js +164 -185
  137. package/dist/esm/apps/markets/state-machines/market-prediction.js +227 -234
  138. package/dist/esm/apps/markets/state-machines/market-universal.js +66 -81
  139. package/dist/esm/apps/oracles/index.js +3 -3
  140. package/dist/esm/apps/privacy/index.js +56 -0
  141. package/dist/esm/apps/privacy/state-machines/index.js +6 -0
  142. package/dist/esm/apps/privacy/state-machines/mixer-ddhRing.js +282 -0
  143. package/dist/esm/apps/privacy/state-machines/note-pool.js +470 -0
  144. package/dist/esm/apps/staked-pool/assets.js +91 -0
  145. package/dist/esm/apps/staked-pool/base.js +493 -0
  146. package/dist/esm/apps/staked-pool/consumer.js +49 -0
  147. package/dist/esm/apps/staked-pool/index.js +60 -0
  148. package/dist/esm/apps/staked-pool/state-machines/index.js +6 -0
  149. package/dist/esm/apps/staked-pool/state-machines/staked-pool-base.js +46 -0
  150. package/dist/esm/apps/staked-pool/state-machines/staked-pool-oracle.js +127 -0
  151. package/dist/esm/generated/google/protobuf/struct.js +1 -1
  152. package/dist/esm/generated/google/protobuf/timestamp.js +1 -1
  153. package/dist/esm/generated/index.js +9 -5
  154. package/dist/esm/generated/openapi.js +5 -0
  155. package/dist/esm/generated/ottochain/apps/contracts/v1/contract.js +47 -47
  156. package/dist/esm/generated/ottochain/apps/corporate/v1/corporate.js +1 -1
  157. package/dist/esm/generated/ottochain/apps/governance/v1/governance.js +1 -1
  158. package/dist/esm/generated/ottochain/apps/identity/v1/attestation.js +1 -1
  159. package/dist/esm/generated/ottochain/apps/identity/v1/identity.js +115 -121
  160. package/dist/esm/generated/ottochain/apps/markets/v1/market.js +101 -101
  161. package/dist/esm/generated/ottochain/v1/common.js +1 -1
  162. package/dist/esm/generated/ottochain/v1/fiber.js +40 -2
  163. package/dist/esm/generated/ottochain/v1/messages.js +1 -1
  164. package/dist/esm/generated/ottochain/v1/records.js +1 -1
  165. package/dist/esm/index.js +28 -3
  166. package/dist/esm/openapi.js +1 -0
  167. package/dist/esm/ottochain/genesis-manifest.js +15 -9
  168. package/dist/esm/ottochain/index.js +2 -3
  169. package/dist/esm/ottochain/metagraph-client.js +81 -16
  170. package/dist/esm/ottochain/snapshot.js +6 -8
  171. package/dist/esm/ottochain/transaction.js +51 -3
  172. package/dist/esm/ottochain/types.js +15 -1
  173. package/dist/esm/privacy/index.js +6 -0
  174. package/dist/esm/privacy/sealed-bid.js +209 -0
  175. package/dist/esm/privacy/shield-app.js +127 -0
  176. package/dist/esm/schema/effects.js +145 -0
  177. package/dist/esm/schema/fiber-app.js +139 -8
  178. package/dist/esm/schema/guard-lint.js +337 -0
  179. package/dist/esm/schema/guards.js +228 -0
  180. package/dist/esm/signing.js +74 -0
  181. package/dist/esm/templates/asset-policy.js +182 -0
  182. package/dist/esm/templates/index.js +41 -0
  183. package/dist/esm/templates/machine.js +125 -0
  184. package/dist/esm/templates/migration.js +34 -0
  185. package/dist/esm/templates/state-shape.js +34 -0
  186. package/dist/esm/validation.js +1 -3
  187. package/dist/esm/verify.js +8 -2
  188. package/dist/esm/zk/commit.js +44 -0
  189. package/dist/esm/zk/guard.js +99 -0
  190. package/dist/esm/zk/index.js +19 -0
  191. package/dist/esm/zk/preimage.js +30 -0
  192. package/dist/esm/zk/prover.js +82 -0
  193. package/dist/esm/zk/registry.js +55 -0
  194. package/dist/esm/zk/types.js +25 -0
  195. package/dist/esm/zk/witness.js +15 -0
  196. package/dist/types/apps/compute/index.d.ts +176 -0
  197. package/dist/types/apps/compute/state-machines/compute-rule110.d.ts +157 -0
  198. package/dist/types/apps/compute/state-machines/index.d.ts +5 -0
  199. package/dist/types/apps/contracts/index.d.ts +81 -221
  200. package/dist/types/apps/contracts/state-machines/contract-agreement.d.ts +23 -98
  201. package/dist/types/apps/contracts/state-machines/contract-escrow.d.ts +49 -115
  202. package/dist/types/apps/contracts/state-machines/contract-universal.d.ts +4 -4
  203. package/dist/types/apps/corporate/index.d.ts +1176 -440
  204. package/dist/types/apps/corporate/state-machines/corp-board.d.ts +271 -135
  205. package/dist/types/apps/corporate/state-machines/corp-entity.d.ts +279 -74
  206. package/dist/types/apps/corporate/state-machines/corp-securities.d.ts +373 -149
  207. package/dist/types/apps/corporate/state-machines/corp-shareholders.d.ts +252 -80
  208. package/dist/types/apps/governance/index.d.ts +368 -470
  209. package/dist/types/apps/governance/state-machines/dao-multisig.d.ts +92 -107
  210. package/dist/types/apps/governance/state-machines/dao-reputation.d.ts +61 -85
  211. package/dist/types/apps/governance/state-machines/dao-single.d.ts +25 -47
  212. package/dist/types/apps/governance/state-machines/dao-token.d.ts +79 -68
  213. package/dist/types/apps/governance/state-machines/governance-simple.d.ts +95 -125
  214. package/dist/types/apps/governance/state-machines/governance-universal.d.ts +6 -6
  215. package/dist/types/apps/identity/constants.d.ts +33 -5
  216. package/dist/types/apps/identity/index.d.ts +415 -131
  217. package/dist/types/apps/identity/state-machines/identity-agent.d.ts +23 -23
  218. package/dist/types/apps/identity/state-machines/identity-oracle.d.ts +27 -98
  219. package/dist/types/apps/identity/state-machines/identity-registry.d.ts +379 -0
  220. package/dist/types/apps/identity/state-machines/identity-universal.d.ts +4 -4
  221. package/dist/types/apps/identity/state-machines/index.d.ts +1 -0
  222. package/dist/types/apps/index.d.ts +9 -5
  223. package/dist/types/apps/lending/assets.d.ts +258 -0
  224. package/dist/types/apps/lending/credit-scoring.d.ts +80 -0
  225. package/dist/types/apps/lending/eligibility.d.ts +83 -0
  226. package/dist/types/apps/lending/index.d.ts +556 -0
  227. package/dist/types/apps/lending/state-machines/index.d.ts +5 -0
  228. package/dist/types/apps/lending/state-machines/lending-zk-loan.d.ts +536 -0
  229. package/dist/types/apps/markets/index.d.ts +181 -276
  230. package/dist/types/apps/markets/state-machines/market-auction.d.ts +25 -49
  231. package/dist/types/apps/markets/state-machines/market-crowdfund.d.ts +35 -53
  232. package/dist/types/apps/markets/state-machines/market-group-buy.d.ts +35 -61
  233. package/dist/types/apps/markets/state-machines/market-prediction.d.ts +65 -86
  234. package/dist/types/apps/markets/state-machines/market-universal.d.ts +12 -12
  235. package/dist/types/apps/oracles/index.d.ts +1 -1
  236. package/dist/types/apps/privacy/index.d.ts +328 -0
  237. package/dist/types/apps/privacy/state-machines/index.d.ts +6 -0
  238. package/dist/types/apps/privacy/state-machines/mixer-ddhRing.d.ts +337 -0
  239. package/dist/types/apps/privacy/state-machines/note-pool.d.ts +196 -0
  240. package/dist/types/apps/staked-pool/assets.d.ts +53 -0
  241. package/dist/types/apps/staked-pool/base.d.ts +80 -0
  242. package/dist/types/apps/staked-pool/consumer.d.ts +29 -0
  243. package/dist/types/apps/staked-pool/index.d.ts +51 -0
  244. package/dist/types/apps/staked-pool/state-machines/index.d.ts +6 -0
  245. package/dist/types/apps/staked-pool/state-machines/staked-pool-base.d.ts +8 -0
  246. package/dist/types/apps/staked-pool/state-machines/staked-pool-oracle.d.ts +34 -0
  247. package/dist/types/generated/index.d.ts +5 -5
  248. package/dist/types/generated/openapi.d.ts +1270 -0
  249. package/dist/types/generated/ottochain/apps/contracts/v1/contract.d.ts +5 -5
  250. package/dist/types/generated/ottochain/apps/identity/v1/identity.d.ts +12 -12
  251. package/dist/types/generated/ottochain/apps/markets/v1/market.d.ts +11 -11
  252. package/dist/types/generated/ottochain/v1/fiber.d.ts +7 -0
  253. package/dist/types/index.d.ts +7 -3
  254. package/dist/types/openapi.d.ts +24 -0
  255. package/dist/types/ottochain/genesis-manifest.d.ts +11 -5
  256. package/dist/types/ottochain/index.d.ts +5 -6
  257. package/dist/types/ottochain/metagraph-client.d.ts +49 -7
  258. package/dist/types/ottochain/snapshot.d.ts +6 -6
  259. package/dist/types/ottochain/transaction.d.ts +41 -1
  260. package/dist/types/ottochain/types.d.ts +336 -31
  261. package/dist/types/privacy/index.d.ts +6 -0
  262. package/dist/types/privacy/sealed-bid.d.ts +348 -0
  263. package/dist/types/privacy/shield-app.d.ts +48 -0
  264. package/dist/types/schema/effects.d.ts +141 -0
  265. package/dist/types/schema/fiber-app.d.ts +282 -2
  266. package/dist/types/schema/guard-lint.d.ts +111 -0
  267. package/dist/types/schema/guards.d.ts +146 -0
  268. package/dist/types/signing.d.ts +45 -0
  269. package/dist/types/templates/asset-policy.d.ts +119 -0
  270. package/dist/types/templates/index.d.ts +26 -0
  271. package/dist/types/templates/machine.d.ts +139 -0
  272. package/dist/types/templates/migration.d.ts +34 -0
  273. package/dist/types/templates/state-shape.d.ts +30 -0
  274. package/dist/types/validation.d.ts +22 -217
  275. package/dist/types/verify.d.ts +4 -0
  276. package/dist/types/zk/commit.d.ts +28 -0
  277. package/dist/types/zk/guard.d.ts +46 -0
  278. package/dist/types/zk/index.d.ts +19 -0
  279. package/dist/types/zk/preimage.d.ts +10 -0
  280. package/dist/types/zk/prover.d.ts +34 -0
  281. package/dist/types/zk/registry.d.ts +34 -0
  282. package/dist/types/zk/types.d.ts +33 -0
  283. package/dist/types/zk/witness.d.ts +40 -0
  284. package/package.json +47 -15
  285. package/dist/cjs/ottochain/normalize.js +0 -186
  286. package/dist/esm/ottochain/normalize.js +0 -179
  287. package/dist/types/ottochain/normalize.d.ts +0 -79
@@ -0,0 +1,343 @@
1
+ "use strict";
2
+ /**
3
+ * Regression-prevention lint for OttoChain std-app fiber definitions.
4
+ *
5
+ * Purpose
6
+ * -------
7
+ * Catch — at *authoring* time — the classes of SDK↔chain drift that the chain
8
+ * accepts structurally but then silently mis-parses, drops, or evaluates to a
9
+ * dead (always-true / always-false / always-throw) guard. Without this lint
10
+ * those mistakes ship as live, exploitable holes or as silent data corruption
11
+ * because nothing in `defineFiberApp` / `toProtoDefinition` rejects them.
12
+ *
13
+ * The drift classes (cross-referenced to docs/reviews/fiber-app-alignment-audit-2026-06.md):
14
+ *
15
+ * A1 — `$timestamp` (and any non-injected `$`-key). `ReservedKeys` injects ONLY
16
+ * `$ordinal`, `$lastSnapshotHash`, `$epochProgress`. `{"var":"$timestamp"}`
17
+ * resolves to null → `0` in numeric contexts → deadline guards die silently.
18
+ *
19
+ * A2 — Nonexistent JLVM opcodes used as operator tags: `size`, `getKey`,
20
+ * `setKey`, `deleteKey`. metakit decodes an unknown single-key object as a
21
+ * LITERAL Map, not an operator, so the guard is always-true/false and the
22
+ * effect writes a junk key. (Canonical good set: `KNOWN_OPERATORS`.)
23
+ *
24
+ * A3 — SDK directives the chain's `toProtoDefinition` silently drops:
25
+ * transition-level `emits` / `spawns`, and object-form `dependencies`
26
+ * entries `{machine,instanceRef,requiredState}` (chain parses `Set[UUID]`).
27
+ * Authors believe these gate/emit; on-chain they evaporate.
28
+ *
29
+ * per-context — `{"var":"witness.*"}` in a fiber TRANSITION. `witness` is only
30
+ * injected in ASSET-guard contexts; a transition gets `event` (the proof
31
+ * rides under `event.witness.*`). Reading `witness.*` resolves to null →
32
+ * fail-closed (zk gate un-passable) or silently bypassed.
33
+ *
34
+ * leading-dot — `{"var":".foo"}` resolves to null on chain.
35
+ *
36
+ * This is a STANDALONE validator. It is deliberately NOT wired into
37
+ * `defineFiberApp` / `toProtoDefinition`: doing so would break the build until
38
+ * every app is remediated. Run it via `scripts/lint-apps.mjs`.
39
+ */
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.KNOWN_BAD_OPERATORS = exports.INJECTED_RESERVED_VARS = exports.LINT_CODES = void 0;
42
+ exports.lintGuardExpression = lintGuardExpression;
43
+ exports.lintFiberApp = lintFiberApp;
44
+ exports.lintFiberApps = lintFiberApps;
45
+ const metagraph_sdk_jlvm_1 = require("@constellation-network/metagraph-sdk-jlvm");
46
+ /**
47
+ * Canonical set of valid JLVM operator tags, imported from metakit (the same
48
+ * `KNOWN_OPERATORS` the on-chain evaluator is built from). Widened to
49
+ * `ReadonlySet<string>` so we can membership-test arbitrary object keys.
50
+ */
51
+ const KNOWN_OPERATORS = metagraph_sdk_jlvm_1.KNOWN_OPERATORS;
52
+ /** Stable rule codes — referenced by tests and by report tooling. */
53
+ exports.LINT_CODES = {
54
+ UNKNOWN_RESERVED_VAR: 'unknown-reserved-var', // rule 1 (A1)
55
+ UNKNOWN_OPERATOR: 'unknown-operator', // rule 2 (A2)
56
+ WITNESS_IN_TRANSITION: 'witness-in-transition', // rule 3
57
+ DROPPED_DIRECTIVE: 'dropped-directive', // rule 4 (A3)
58
+ LEADING_DOT_VAR: 'leading-dot-var', // rule 5
59
+ };
60
+ // =============================================================================
61
+ // Rule data
62
+ // =============================================================================
63
+ /**
64
+ * The ONLY `$`-prefixed keys the engine injects (`ReservedKeys`). Anything else
65
+ * (`$timestamp`, `$now`, ...) resolves to null on chain.
66
+ */
67
+ exports.INJECTED_RESERVED_VARS = new Set(['$ordinal', '$lastSnapshotHash', '$epochProgress']);
68
+ /**
69
+ * Tags that LOOK like opcodes and appear in real definitions but are NOT valid
70
+ * JLVM operators — metakit mis-decodes each as a literal Map. These are always
71
+ * flagged as errors regardless of position (they are not plausible data fields).
72
+ * Mapped to the correct replacement opcode for the message.
73
+ */
74
+ exports.KNOWN_BAD_OPERATORS = new Map([
75
+ ['size', 'length (collections) or count; for a Map use length[keys[...]]'],
76
+ ['getKey', 'get (value) / has (membership)'],
77
+ ['setKey', 'merge (or restructure the field as an array appended with cat/merge)'],
78
+ ['deleteKey', 'filter the collection (model the field as an array of records)'],
79
+ ]);
80
+ /**
81
+ * Structural keys that JSON-Logic uses as single-key objects but are NOT
82
+ * operator tags. Used to avoid false-positive "unknown operator" warnings.
83
+ * `var` is the data-access form; the rest are common rule scaffolding seen in
84
+ * std apps that are handled by the evaluator or are intentional literals.
85
+ */
86
+ const NON_OPERATOR_SINGLE_KEYS = new Set([
87
+ 'var', // data access — handled explicitly
88
+ ]);
89
+ /**
90
+ * Recursively lint a single JSON-Logic expression (a guard or an effect, or any
91
+ * sub-node). Walks every nested operand. Pushes a {@link LintViolation} for each
92
+ * rule-1 / rule-2 / rule-3 / rule-5 hit found at or below `node`.
93
+ *
94
+ * `inDataLiteral` tracks whether `node` sits in a position the evaluator treats
95
+ * as DATA rather than as an expression to evaluate (e.g. the 2nd operand of
96
+ * `merge`/`cat`, or a value nested inside such an operand). In a data-literal
97
+ * position an unknown single-key object is a legitimate field map, so the
98
+ * *generic* unknown-operator warning is suppressed there — but `$`-var keys,
99
+ * leading-dot vars, witness reads, and the known-BAD opcode tags are still
100
+ * flagged (those are bugs wherever they appear).
101
+ */
102
+ function lintGuardExpression(node, ctx, path, inDataLiteral = false) {
103
+ const out = [];
104
+ // Arrays: walk each element, preserving the data-literal flag.
105
+ if (Array.isArray(node)) {
106
+ node.forEach((child, i) => {
107
+ out.push(...lintGuardExpression(child, ctx, `${path}[${i}]`, inDataLiteral));
108
+ });
109
+ return out;
110
+ }
111
+ // Primitives carry no structure to lint.
112
+ if (node === null || typeof node !== 'object') {
113
+ return out;
114
+ }
115
+ const obj = node;
116
+ const keys = Object.keys(obj);
117
+ // ---- `var` access: rules 1, 3, 5 -------------------------------------
118
+ if (keys.length === 1 && keys[0] === 'var') {
119
+ const ref = obj.var;
120
+ // The reference may be a string, or `[path, default]`, or (rarely) a
121
+ // nested expression; only string refs carry the patterns we check.
122
+ const refStr = typeof ref === 'string' ? ref : Array.isArray(ref) && typeof ref[0] === 'string' ? ref[0] : undefined;
123
+ if (refStr !== undefined) {
124
+ // rule 1 — unknown reserved `$`-key
125
+ if (refStr.startsWith('$') && !exports.INJECTED_RESERVED_VARS.has(refStr)) {
126
+ out.push({
127
+ app: ctx.app,
128
+ transition: ctx.transition,
129
+ severity: 'error',
130
+ code: exports.LINT_CODES.UNKNOWN_RESERVED_VAR,
131
+ message: `{"var":"${refStr}"} references a reserved key the engine never injects. ` +
132
+ `Only ${[...exports.INJECTED_RESERVED_VARS].join(', ')} are injected; ` +
133
+ `"${refStr}" resolves to null (→ 0 in numeric contexts, defeating deadline guards). ` +
134
+ `Use $ordinal and model time fields as ordinal deltas/bounds.`,
135
+ path: `${path}.var`,
136
+ });
137
+ }
138
+ // rule 5 — leading-dot relative path
139
+ if (refStr.startsWith('.')) {
140
+ out.push({
141
+ app: ctx.app,
142
+ transition: ctx.transition,
143
+ severity: 'error',
144
+ code: exports.LINT_CODES.LEADING_DOT_VAR,
145
+ message: `{"var":"${refStr}"} uses a leading-dot relative path, which resolves to null on chain. ` +
146
+ `Use the bare element field name ("${refStr.slice(1)}") inside map/filter/some scopes.`,
147
+ path: `${path}.var`,
148
+ });
149
+ }
150
+ // rule 3 — `witness.*` read inside a fiber transition
151
+ if (ctx.flagWitness && (refStr === 'witness' || refStr.startsWith('witness.'))) {
152
+ out.push({
153
+ app: ctx.app,
154
+ transition: ctx.transition,
155
+ severity: 'error',
156
+ code: exports.LINT_CODES.WITNESS_IN_TRANSITION,
157
+ message: `{"var":"${refStr}"} reads the 'witness' context, which is injected ONLY in asset-guard ` +
158
+ `contexts — never in a fiber transition (the engine injects 'event'). It resolves to null ` +
159
+ `→ the zk gate is un-passable (fail-closed). Read 'event.${refStr}' instead, or move the ` +
160
+ `semi-private guard onto the asset mintPolicy/burnPolicy.`,
161
+ path: `${path}.var`,
162
+ });
163
+ }
164
+ }
165
+ // The `var` default operand (ref[1]) may itself be an expression; walk it.
166
+ if (Array.isArray(ref) && ref.length > 1) {
167
+ out.push(...lintGuardExpression(ref[1], ctx, `${path}.var[1]`, inDataLiteral));
168
+ }
169
+ return out;
170
+ }
171
+ // ---- single-key object: candidate operator (rule 2) ------------------
172
+ if (keys.length === 1) {
173
+ const tag = keys[0];
174
+ const operand = obj[tag];
175
+ // rule 2a — known-BAD opcode tag. Always an error (these are never plausible
176
+ // data field names, and metakit silently mis-decodes them as a literal Map).
177
+ if (exports.KNOWN_BAD_OPERATORS.has(tag)) {
178
+ out.push({
179
+ app: ctx.app,
180
+ transition: ctx.transition,
181
+ severity: 'error',
182
+ code: exports.LINT_CODES.UNKNOWN_OPERATOR,
183
+ message: `'${tag}' is not a JLVM operator — metakit decodes {"${tag}":...} as a literal Map, ` +
184
+ `so this guard is always-true/false and any effect writes a junk key. ` +
185
+ `Use ${exports.KNOWN_BAD_OPERATORS.get(tag)}.`,
186
+ path: `${path}.${tag}`,
187
+ });
188
+ // Still descend into the operand (it may carry further violations).
189
+ out.push(...lintGuardExpression(operand, ctx, `${path}.${tag}`, inDataLiteral));
190
+ return out;
191
+ }
192
+ // Recognized operator: descend. `merge`/`cat` 2nd+ operands are DATA literals
193
+ // (field maps to write), so mark them to suppress the generic op warning below.
194
+ if (KNOWN_OPERATORS.has(tag)) {
195
+ const literalDataOp = tag === 'merge' || tag === 'cat';
196
+ if (literalDataOp && Array.isArray(operand)) {
197
+ operand.forEach((child, i) => {
198
+ // First operand is the base collection (an expression); the rest are
199
+ // the literal field maps merged/appended onto it.
200
+ out.push(...lintGuardExpression(child, ctx, `${path}.${tag}[${i}]`, i > 0 ? true : inDataLiteral));
201
+ });
202
+ }
203
+ else {
204
+ out.push(...lintGuardExpression(operand, ctx, `${path}.${tag}`, inDataLiteral));
205
+ }
206
+ return out;
207
+ }
208
+ // rule 2b (heuristic warn) — an unknown single-key tag that LOOKS like an
209
+ // operator (no spaces, not `var`, not a known data scaffold) and is NOT in a
210
+ // data-literal position. Keep this a WARNING to hold false positives low:
211
+ // in a data-literal position a one-key object is a legitimate `{field: ...}`.
212
+ if (!inDataLiteral &&
213
+ !NON_OPERATOR_SINGLE_KEYS.has(tag) &&
214
+ !tag.includes(' ') &&
215
+ !tag.startsWith('$') &&
216
+ !tag.startsWith('_') // reserved effect directives (_emit/_spawn/...) are handled structurally
217
+ ) {
218
+ out.push({
219
+ app: ctx.app,
220
+ transition: ctx.transition,
221
+ severity: 'warn',
222
+ code: exports.LINT_CODES.UNKNOWN_OPERATOR,
223
+ message: `single-key object {"${tag}":...} in an expression position has a key that is not a known ` +
224
+ `JLVM operator. If '${tag}' was meant as an operator it will mis-decode as a literal Map; ` +
225
+ `if it is a literal field, ignore. Known operators: see KNOWN_OPERATORS.`,
226
+ path: `${path}.${tag}`,
227
+ });
228
+ }
229
+ out.push(...lintGuardExpression(operand, ctx, `${path}.${tag}`, inDataLiteral));
230
+ return out;
231
+ }
232
+ // ---- multi-key object: a DATA literal (a field map). Descend into values,
233
+ // marking children as data-literal so their inner one-key objects are not
234
+ // mistaken for operators. (`$`/leading-dot/witness `var`s inside are still
235
+ // flagged by the recursion.)
236
+ for (const k of keys) {
237
+ out.push(...lintGuardExpression(obj[k], ctx, `${path}.${k}`, true));
238
+ }
239
+ return out;
240
+ }
241
+ // =============================================================================
242
+ // Transition-level structural rules (rule 4 / A3)
243
+ // =============================================================================
244
+ function isObjectDependency(dep) {
245
+ return typeof dep === 'object' && dep !== null && !Array.isArray(dep);
246
+ }
247
+ function lintTransitionStructure(t, app, transition, path) {
248
+ const out = [];
249
+ const raw = t;
250
+ // rule 4a — transition-level `emits`
251
+ if (raw.emits !== undefined) {
252
+ out.push({
253
+ app,
254
+ transition,
255
+ severity: 'error',
256
+ code: exports.LINT_CODES.DROPPED_DIRECTIVE,
257
+ message: `transition-level 'emits' is silently dropped by toProtoDefinition (the chain Transition has ` +
258
+ `no such field). Emit from INSIDE the effect result under the reserved key '_emit' ` +
259
+ `({name,data,destination}), or '_triggers' for a state-changing cross-machine call.`,
260
+ path: `${path}.emits`,
261
+ });
262
+ }
263
+ // rule 4b — transition-level `spawns`
264
+ if (raw.spawns !== undefined) {
265
+ out.push({
266
+ app,
267
+ transition,
268
+ severity: 'error',
269
+ code: exports.LINT_CODES.DROPPED_DIRECTIVE,
270
+ message: `transition-level 'spawns' is silently dropped by toProtoDefinition — the child machine is ` +
271
+ `never created. Spawn from INSIDE the effect result under the reserved key '_spawn' ` +
272
+ `(with a full inline definition, a distinct childId, and initialData).`,
273
+ path: `${path}.spawns`,
274
+ });
275
+ }
276
+ // rule 4c — object-form `dependencies` entries (chain accepts Set[UUID] only)
277
+ if (Array.isArray(t.dependencies)) {
278
+ t.dependencies.forEach((dep, i) => {
279
+ if (isObjectDependency(dep)) {
280
+ out.push({
281
+ app,
282
+ transition,
283
+ severity: 'error',
284
+ code: exports.LINT_CODES.DROPPED_DIRECTIVE,
285
+ message: `dependencies[${i}] is an object ({machine,instanceRef,requiredState}); the chain parses ` +
286
+ `'dependencies' as Set[UUID] and drops the object, so 'requiredState' gating NEVER happens. ` +
287
+ `Pass a bare fiber-UUID string and assert machines.<uuid>.currentStateId=="<state>" in the guard.`,
288
+ path: `${path}.dependencies[${i}]`,
289
+ });
290
+ }
291
+ });
292
+ }
293
+ return out;
294
+ }
295
+ // =============================================================================
296
+ // Top-level entry point
297
+ // =============================================================================
298
+ function appLabel(def) {
299
+ const m = def.metadata;
300
+ if (!m)
301
+ return undefined;
302
+ if (m.app && m.type)
303
+ return `${m.app}/${m.type}`;
304
+ return m.name ?? m.app ?? m.type ?? undefined;
305
+ }
306
+ /**
307
+ * Lint a complete fiber app definition. Walks every transition guard + effect
308
+ * (rules 1/2/3/5) and every transition's structural directives (rule 4), plus a
309
+ * light pass over `states` to catch stray reserved-key / operator drift embedded
310
+ * in state metadata. Returns ALL violations (errors and warnings); callers
311
+ * decide the exit policy (e.g. fail on any `error`).
312
+ */
313
+ function lintFiberApp(def) {
314
+ const out = [];
315
+ const app = appLabel(def);
316
+ // Transitions.
317
+ (def.transitions ?? []).forEach((t, i) => {
318
+ const transition = t.eventName ?? `${t.from}->${t.to}`;
319
+ const tPath = `transitions[${i}]`;
320
+ const ctx = { app, transition, flagWitness: true };
321
+ if (t.guard !== undefined) {
322
+ out.push(...lintGuardExpression(t.guard, ctx, `${tPath}.guard`));
323
+ }
324
+ if (t.effect !== undefined) {
325
+ out.push(...lintGuardExpression(t.effect, ctx, `${tPath}.effect`));
326
+ }
327
+ out.push(...lintTransitionStructure(t, app, transition, tPath));
328
+ });
329
+ // States — usually inert metadata, but guard against reserved-key drift here
330
+ // too (states never run in an asset context, so witness reads would be bugs).
331
+ Object.entries(def.states ?? {}).forEach(([name, state]) => {
332
+ const ctx = { app, transition: `state:${name}`, flagWitness: true };
333
+ out.push(...lintGuardExpression(state, ctx, `states.${name}`));
334
+ });
335
+ return out;
336
+ }
337
+ /**
338
+ * Convenience: lint many apps at once, returning a flat list. Each app's
339
+ * violations already carry its `app` label.
340
+ */
341
+ function lintFiberApps(defs) {
342
+ return defs.flatMap((d) => lintFiberApp(d));
343
+ }
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ /**
3
+ * Canonical authorization-guard builders — bind "who may act" to the CHAIN-VERIFIED signer of the op,
4
+ * never to an attacker-supplied payload field.
5
+ *
6
+ * The forgeable anti-pattern (security audit class F1): `{"===":[{"var":"event.agent"}, {"var":"state.party"}]}`.
7
+ * The fiber engine injects `event` as the RAW transition payload (ContextProvider: `EVENT -> payload`),
8
+ * so `event.agent` is whatever the submitter wrote — a different party's address forges authorization.
9
+ * The chain ALSO injects `proofs` = `[{address, id, signature}]` where `address = id.toAddress` (the
10
+ * VERIFIED signer addresses). Authorization must bind to THAT. For ASSET ops (mint/burn/morphism) the
11
+ * context has no `event`/`proofs`; it injects `signers` (an array of verified address strings) instead.
12
+ *
13
+ * Use these so the safe pattern is consistent, greppable, and impossible to get subtly wrong.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.depInState = exports.actorNotInArray = exports.signerHasRoleVia = exports.signerHasReputationVia = exports.signerHasRole = exports.signerHasReputation = exports.actorHasEntry = exports.actorInSet = exports.actorIsSigner = exports.assetSignerIs = exports.signerHasEntry = exports.signerIsNotParty = exports.signerInSet = exports.signerIsAnyParty = exports.signerIsParty = void 0;
17
+ /**
18
+ * FIBER-transition authorization: the pinned `partyVar` (a `state` path holding an address, e.g.
19
+ * `"state.borrower"`) MUST be among the op's verified signers (`proofs[].address`). The replay-safe
20
+ * replacement for `{"===":[{"var":"event.agent"}, {"var":partyVar}]}`.
21
+ */
22
+ const signerIsParty = (partyVar) => ({
23
+ in: [{ var: partyVar }, { map: [{ var: 'proofs' }, { var: 'address' }] }],
24
+ });
25
+ exports.signerIsParty = signerIsParty;
26
+ /** FIBER-transition authorization where ANY of the pinned parties signed (e.g. borrower OR lender). */
27
+ const signerIsAnyParty = (partyVars) => ({
28
+ or: partyVars.map(exports.signerIsParty),
29
+ });
30
+ exports.signerIsAnyParty = signerIsAnyParty;
31
+ /**
32
+ * FIBER-transition authorization where the actor must be a MEMBER of a pinned set: at least one
33
+ * VERIFIED signer is in the `setVar` array (e.g. `"state.signers"`, `"state.members"`, `"state.oracles"`).
34
+ * The replay-safe replacement for `{"in":[{"var":"event.agent"}, {"var":setVar}]}`.
35
+ */
36
+ const signerInSet = (setVar) => ({
37
+ some: [{ map: [{ var: 'proofs' }, { var: 'address' }] }, { in: [{ var: '' }, { var: setVar }] }],
38
+ });
39
+ exports.signerInSet = signerInSet;
40
+ /**
41
+ * FIBER-transition ANTI-SELF guard: NO verified signer is the pinned party (e.g. a proposal author may
42
+ * not vote on their own proposal). The replay-safe replacement for `{"!==":[{"var":"event.agent"}, {"var":partyVar}]}`.
43
+ */
44
+ const signerIsNotParty = (partyVar) => ({
45
+ '!': [(0, exports.signerIsParty)(partyVar)],
46
+ });
47
+ exports.signerIsNotParty = signerIsNotParty;
48
+ /**
49
+ * FIBER-transition authorization / dedup over a state MAP keyed by address: at least one VERIFIED signer
50
+ * is a key in `mapVar` (e.g. `"state.members"`, `"state.balances"`). The replay-safe replacement for the
51
+ * forgeable `{"getKey":[{"var":mapVar}, {"var":"event.agent"}]}` membership/presence check (note metakit
52
+ * has no `getKey`; use `get`/`has`). For a "no signer has acted yet" dedup, negate: `{"!":[signerHasEntry(...)]}`.
53
+ * For a per-actor VALUE threshold (e.g. balance >= N), build the `some` directly:
54
+ * `{"some":[{"map":[proofs,address]},{">=":[{"get":[mapVar,{"var":""}]}, N]}]}`.
55
+ */
56
+ const signerHasEntry = (mapVar) => ({
57
+ some: [{ map: [{ var: 'proofs' }, { var: 'address' }] }, { has: [{ var: mapVar }, { var: '' }] }],
58
+ });
59
+ exports.signerHasEntry = signerHasEntry;
60
+ /**
61
+ * ASSET-op authorization (mintPolicy / burnPolicy / MorphismSpec.guard): the address at `addressVar`
62
+ * (e.g. `"holder.Wallet.address"`) MUST be among the op's verified `signers`. The asset context has no
63
+ * `event` or `proofs` — it injects `signers` directly.
64
+ */
65
+ const assetSignerIs = (addressVar) => ({
66
+ in: [{ var: addressVar }, { var: 'signers' }],
67
+ });
68
+ exports.assetSignerIs = assetSignerIs;
69
+ /**
70
+ * EFFECT-KEY BINDING: prove the event's claimed actor (`actorVar`, default `event.agent`) is a
71
+ * CHAIN-VERIFIED signer, so that field is SAFE to use as a dynamic map key / array element in the
72
+ * EFFECT (`{"set":[map,{"var":"event.agent"},v]}`, `{"cat":[arr,[{"var":"event.agent"}]]}`).
73
+ *
74
+ * This is the coupling clause for the map-write remediation: a guard may authorize via membership /
75
+ * balance / reputation, but if the EFFECT writes under `event.agent` WITHOUT this clause, an attacker
76
+ * sets `event.agent` to a victim's address and writes under the victim's key (security class S1).
77
+ * Pair it with the authorization check expressed on the SAME `actorVar` — together they prove the actor
78
+ * both signed and is authorized, and the effect can only write under that one verified key.
79
+ * Structurally identical to {@link signerIsParty}; named for intent + greppability at write sites.
80
+ */
81
+ const actorIsSigner = (actorVar = 'event.agent') => (0, exports.signerIsParty)(actorVar);
82
+ exports.actorIsSigner = actorIsSigner;
83
+ /**
84
+ * EFFECT-KEY-BINDING membership: the event's claimed actor (`actorVar`, default `event.agent`) is BOTH
85
+ * a CHAIN-VERIFIED signer AND a member of the pinned set `setVar` (e.g. `"state.signers"`,
86
+ * `"state.members"`). Use when the EFFECT writes a map/array keyed by that actor and authorization is
87
+ * set-membership. It proves the EXACT actor whose key is written both signed and is authorized — closing
88
+ * the S1 forge AND the subtler gap {@link signerInSet} leaves open: `signerInSet` only proves SOME
89
+ * verified signer is a member, so an op co-signed by an authorized signer could still write a DIFFERENT
90
+ * verified-but-unauthorized address as the key (padding a signature/vote tally). `actorInSet` pins both
91
+ * to the same `actorVar`.
92
+ */
93
+ const actorInSet = (setVar, actorVar = 'event.agent') => ({
94
+ and: [(0, exports.actorIsSigner)(actorVar), { in: [{ var: actorVar }, { var: setVar }] }],
95
+ });
96
+ exports.actorInSet = actorInSet;
97
+ /**
98
+ * EFFECT-KEY-BINDING map membership: the claimed actor (`actorVar`, default `event.agent`) is BOTH a
99
+ * CHAIN-VERIFIED signer AND a key in the pinned state MAP `mapVar` (e.g. `"state.members"`,
100
+ * `"state.balances"`). The map analog of {@link actorInSet} — use it when the membership set is a dict
101
+ * keyed by address, where `in` does not apply and `has` checks key presence. Use when the EFFECT writes
102
+ * `mapVar` (or a per-actor tally) keyed by that actor: it proves the EXACT written key both signed and
103
+ * is an authorized member, closing the vote/signature-stuffing gap that bare {@link signerHasEntry}
104
+ * leaves open (which only proves SOME verified signer is a key, not that the written key is).
105
+ */
106
+ const actorHasEntry = (mapVar, actorVar = 'event.agent') => ({
107
+ and: [(0, exports.actorIsSigner)(actorVar), { has: [{ var: mapVar }, { var: actorVar }] }],
108
+ });
109
+ exports.actorHasEntry = actorHasEntry;
110
+ /**
111
+ * IDENTITY-REGISTRY reputation gate: at least one VERIFIED signer has a reputation in the registry map
112
+ * at `repMapVar` (a declared-dependency read, e.g. `"machines.<registryDep>.state.reputations"`, shaped
113
+ * `{ <address>: int }`) that is `>=` the bar read from `thresholdVar` (a state path, e.g.
114
+ * `"state.voteThreshold"`). A missing entry reads as null → 0 numerically, so unregistered signers
115
+ * fail-closed for any positive bar. The replay-safe replacement for the forgeable
116
+ * `{">=":[{"var":"event.agentReputation"}, bar]}` (security class S1). See
117
+ * docs/design/app-hardening-identity-integration.md §3–§4.1.
118
+ */
119
+ const signerHasReputation = (repMapVar, thresholdVar) => ({
120
+ some: [
121
+ { map: [{ var: 'proofs' }, { var: 'address' }] },
122
+ {
123
+ '>=': [{ get: [{ var: repMapVar }, { var: '' }] }, { var: thresholdVar }],
124
+ },
125
+ ],
126
+ });
127
+ exports.signerHasReputation = signerHasReputation;
128
+ /**
129
+ * IDENTITY-REGISTRY role gate: at least one VERIFIED signer holds an active role attestation, i.e. is a
130
+ * key in the registry's flat per-role map at `roleMapVar` (a declared-dependency read, e.g.
131
+ * `"machines.<registryDep>.state.arbiters"` / `".slashers"` / `".issuers"` / `".boardMembers"`, each
132
+ * shaped `{ <address>: true }`). The replay-safe replacement for bare `event.judicialRuling` / role
133
+ * escapes and `{"==":[1,1]}` missing-auth (security class S2). Roles are FLAT per-role maps (not a
134
+ * nested `roles[addr][ROLE]`) because metakit `get`/`has` on a null inner map ERROR rather than
135
+ * returning null; a flat map keeps the read total + fail-closed. See app-hardening §4.2.
136
+ */
137
+ const signerHasRole = (roleMapVar) => (0, exports.signerHasEntry)(roleMapVar);
138
+ exports.signerHasRole = signerHasRole;
139
+ /**
140
+ * DYNAMIC identity-registry reputation gate — for when the registry instance is bound at RUNTIME via the
141
+ * fiber-engine `_addDependency` directive (#24) rather than hardcoded into the `machines.<uuid>` path.
142
+ * `registryIdVar` is a state/event path holding the registry fiber id (e.g. `"state.registryId"`); the
143
+ * read addresses `machines[<that id>].state.reputations[<signer>]` with dynamic `get`s. Guarded by a
144
+ * presence check so an UNBOUND registry yields a clean `false` (fail-closed) instead of an evaluation
145
+ * error. REQUIRES the registry dependency to have been added in a PRIOR transition (`_addDependency`),
146
+ * because the `machines` context is built before the effect runs (two-phase: bind, then read).
147
+ */
148
+ const signerHasReputationVia = (registryIdVar, thresholdVar) => ({
149
+ if: [
150
+ { has: [{ var: 'machines' }, { var: registryIdVar }] },
151
+ {
152
+ some: [
153
+ { map: [{ var: 'proofs' }, { var: 'address' }] },
154
+ {
155
+ '>=': [
156
+ {
157
+ get: [
158
+ {
159
+ get: [
160
+ {
161
+ get: [{ get: [{ var: 'machines' }, { var: registryIdVar }] }, 'state'],
162
+ },
163
+ 'reputations',
164
+ ],
165
+ },
166
+ { var: '' },
167
+ ],
168
+ },
169
+ { var: thresholdVar },
170
+ ],
171
+ },
172
+ ],
173
+ },
174
+ false,
175
+ ],
176
+ });
177
+ exports.signerHasReputationVia = signerHasReputationVia;
178
+ /**
179
+ * DYNAMIC identity-registry role gate — the runtime-bound (`_addDependency`, #24) counterpart of
180
+ * {@link signerHasRole}. `registryIdVar` is a state/event path holding the registry fiber id;
181
+ * `roleField` is the registry's flat per-role state map name (`"arbiters"` / `"slashers"` / `"issuers"` /
182
+ * `"boardMembers"` — see REGISTRY_ROLE_MAP). Reads `machines[<id>].state.<roleField>[<signer>]`, guarded
183
+ * by a presence check so an UNBOUND registry yields a clean `false`. Same two-phase requirement as
184
+ * {@link signerHasReputationVia}.
185
+ */
186
+ const signerHasRoleVia = (registryIdVar, roleField) => ({
187
+ if: [
188
+ { has: [{ var: 'machines' }, { var: registryIdVar }] },
189
+ {
190
+ some: [
191
+ { map: [{ var: 'proofs' }, { var: 'address' }] },
192
+ {
193
+ has: [
194
+ {
195
+ get: [
196
+ {
197
+ get: [{ get: [{ var: 'machines' }, { var: registryIdVar }] }, 'state'],
198
+ },
199
+ roleField,
200
+ ],
201
+ },
202
+ { var: '' },
203
+ ],
204
+ },
205
+ ],
206
+ },
207
+ false,
208
+ ],
209
+ });
210
+ exports.signerHasRoleVia = signerHasRoleVia;
211
+ /**
212
+ * CROSS-FIBER STATE GATE — assert a RUNTIME-bound dependency fiber is in a required lifecycle state. The
213
+ * replay-safe replacement for the dropped object-form dependency
214
+ * `{machine, instanceRef, requiredState}` (which the chain silently drops — `requiredState` gating never
215
+ * happens). `refVar` is a state/event path holding the dependency's fiber id; reads
216
+ * `machines[<refVar>].currentStateId == requiredState`, guarded by a presence check so an UNBOUND
217
+ * dependency yields a clean `false` (fail-closed). TWO-PHASE (#24): the dependency fiber must have been
218
+ * bound by an `_addDependency` in a PRIOR transition (the `machines` context is built before the effect),
219
+ * so a single gated transition is split into a bind step then this assert step. See
220
+ * docs/design/app-hardening-identity-integration.md §5–§6.
221
+ */
222
+ /**
223
+ * EFFECT-KEY-BINDING dedup over an ARRAY of records: no existing element of the array at `arrayVar`
224
+ * (e.g. `"state.submissions"`, shaped `[ { addr, value }, … ]`) has its `field` (default `"addr"`)
225
+ * equal to the actor at `actorVar` (default `"event.agent"`). The array analog of the map-keyed
226
+ * `{"!":[has(...)]}` dedup — used where the collection is an append-only array of `{addr,…}` records
227
+ * (there is no `(array,string)` key check, but `none` over the array with a per-element `===` is total).
228
+ *
229
+ * The bare-element callback context inside `none` sees each record map directly, so `{get:[{var:""},field]}`
230
+ * is a valid `(map,string)` read. Pair with an `actorIsSigner`/`actorHasEntry` clause on the same
231
+ * `actorVar` (the dedup proves "not yet present", not "is the verified signer").
232
+ */
233
+ const actorNotInArray = (arrayVar, field = 'addr', actorVar = 'event.agent') => ({
234
+ none: [{ var: arrayVar }, { '===': [{ get: [{ var: '' }, field] }, { var: actorVar }] }],
235
+ });
236
+ exports.actorNotInArray = actorNotInArray;
237
+ const depInState = (refVar, requiredState) => ({
238
+ if: [
239
+ { has: [{ var: 'machines' }, { var: refVar }] },
240
+ {
241
+ '==': [{ get: [{ get: [{ var: 'machines' }, { var: refVar }] }, 'currentStateId'] }, requiredState],
242
+ },
243
+ false,
244
+ ],
245
+ });
246
+ exports.depInState = depInState;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ /**
3
+ * DataUpdate Signing Wrappers — dropNulls applied internally
4
+ *
5
+ * Metakit (>= 1.8) computes content hashes by recursively dropping
6
+ * null-valued OBJECT fields (array nulls are preserved) before RFC 8785
7
+ * canonicalization (`JsonBinaryCodec.dropNulls`). This makes
8
+ * `Option = None` indistinguishable from an absent field, so schema
9
+ * evolution never changes prior hashes.
10
+ *
11
+ * The upstream `@constellation-network/metagraph-sdk` signs over the
12
+ * payload as-is, so a payload containing explicit nulls would be signed
13
+ * over different bytes than the metagraph hashes — and the node rejects
14
+ * the signature (HTTP 400).
15
+ *
16
+ * These wrappers shadow the upstream dataUpdate signing surface and apply
17
+ * `dropNulls` internally, unconditionally. Standard-mode signing
18
+ * (currency transactions etc.) is passed through unchanged.
19
+ */
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.signDataUpdate = signDataUpdate;
22
+ exports.createSignedObject = createSignedObject;
23
+ exports.addSignature = addSignature;
24
+ exports.batchSign = batchSign;
25
+ const metagraph_sdk_1 = require("@constellation-network/metagraph-sdk");
26
+ const drop_nulls_js_1 = require("./ottochain/drop-nulls.js");
27
+ function resolveMode(options = {}) {
28
+ if (options.mode)
29
+ return options.mode;
30
+ if (options.isDataUpdate)
31
+ return 'dataUpdate';
32
+ return 'standard';
33
+ }
34
+ /**
35
+ * Sign data as a DataUpdate, hashing over null-dropped canonical bytes.
36
+ *
37
+ * Applies `dropNulls` internally so that explicit-null and absent optional
38
+ * fields produce identical signatures, matching metakit's content-hash rule
39
+ * (drop null object fields, preserve array nulls, then RFC 8785).
40
+ */
41
+ function signDataUpdate(data, privateKey) {
42
+ return (0, metagraph_sdk_1.signDataUpdate)((0, drop_nulls_js_1.dropNulls)(data), privateKey);
43
+ }
44
+ /**
45
+ * Create a signed object. In `dataUpdate` mode the signature is computed
46
+ * over the null-dropped canonical bytes (the returned `value` is the
47
+ * caller's original payload, untouched).
48
+ */
49
+ function createSignedObject(value, privateKey, options = {}) {
50
+ if (resolveMode(options) === 'dataUpdate') {
51
+ const signed = (0, metagraph_sdk_1.createSignedObject)((0, drop_nulls_js_1.dropNulls)(value), privateKey, options);
52
+ return { ...signed, value };
53
+ }
54
+ return (0, metagraph_sdk_1.createSignedObject)(value, privateKey, options);
55
+ }
56
+ /**
57
+ * Add an additional signature to an existing signed object. In
58
+ * `dataUpdate` mode the new signature is computed over the null-dropped
59
+ * canonical bytes of `signed.value`.
60
+ */
61
+ function addSignature(signed, privateKey, options) {
62
+ const mode = options ? resolveMode(options) : (signed.mode ?? 'standard');
63
+ if (mode === 'dataUpdate') {
64
+ const result = (0, metagraph_sdk_1.addSignature)({ ...signed, value: (0, drop_nulls_js_1.dropNulls)(signed.value) }, privateKey, options);
65
+ return { ...result, value: signed.value };
66
+ }
67
+ return (0, metagraph_sdk_1.addSignature)(signed, privateKey, options);
68
+ }
69
+ /**
70
+ * Create a signed object with multiple signatures at once. In
71
+ * `dataUpdate` mode all signatures are computed over the null-dropped
72
+ * canonical bytes (the returned `value` is the caller's original payload).
73
+ */
74
+ function batchSign(value, privateKeys, options = {}) {
75
+ if (resolveMode(options) === 'dataUpdate') {
76
+ const signed = (0, metagraph_sdk_1.batchSign)((0, drop_nulls_js_1.dropNulls)(value), privateKeys, options);
77
+ return { ...signed, value };
78
+ }
79
+ return (0, metagraph_sdk_1.batchSign)(value, privateKeys, options);
80
+ }