chainlesschain 0.166.83 → 0.166.85

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 (179) hide show
  1. package/README.md +9 -4
  2. package/package.json +1 -1
  3. package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
  4. package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
  5. package/src/assets/web-panel/assets/{AIOps-D5a4i3N7.js → AIOps-Cgo7OFpi.js} +1 -1
  6. package/src/assets/web-panel/assets/{ActionButton-DxsRk5n1.js → ActionButton-lUMnE2Ln.js} +1 -1
  7. package/src/assets/web-panel/assets/{Analytics-D-7EvH4S.js → Analytics-CsvKzTot.js} +3 -3
  8. package/src/assets/web-panel/assets/{AppLayout-DDEWVr-d.js → AppLayout-C2z1h1Pv.js} +3 -3
  9. package/src/assets/web-panel/assets/{Artifacts-CAlJAjRB.js → Artifacts-C5Q0ZhNb.js} +1 -1
  10. package/src/assets/web-panel/assets/{Audit-tuaWDOAM.js → Audit-DXnfPKsI.js} +1 -1
  11. package/src/assets/web-panel/assets/{BackgroundAgents-CIwRFJ50.js → BackgroundAgents-DjbGcLm3.js} +1 -1
  12. package/src/assets/web-panel/assets/{Backup-CpBepJlH.js → Backup-9bVrKvXE.js} +1 -1
  13. package/src/assets/web-panel/assets/{BaseInput-eKguCzQc.js → BaseInput-bx5ri7BD.js} +1 -1
  14. package/src/assets/web-panel/assets/{Chat-CFrUfXDV.js → Chat-CCFmeUMY.js} +4 -4
  15. package/src/assets/web-panel/assets/ChatBubbleRenderer-ClqsIfTT.js +1 -0
  16. package/src/assets/web-panel/assets/{Checkbox-o5FEn6a1.js → Checkbox-ei7MQXe9.js} +1 -1
  17. package/src/assets/web-panel/assets/{Codegen-Dih8HogC.js → Codegen-CXgBJ9iu.js} +1 -1
  18. package/src/assets/web-panel/assets/{Col-D5r-6WZe.js → Col-DMkMsizS.js} +1 -1
  19. package/src/assets/web-panel/assets/{Community-jeQ6bGey.js → Community-CvpB_UFR.js} +1 -1
  20. package/src/assets/web-panel/assets/{Compact-CEP7QsWp.js → Compact-gay5hbFX.js} +1 -1
  21. package/src/assets/web-panel/assets/{Compliance-B8NydOHd.js → Compliance-dG8JQyRL.js} +1 -1
  22. package/src/assets/web-panel/assets/{Cowork-Bmi1zuPy.js → Cowork-BA3xfaTW.js} +4 -4
  23. package/src/assets/web-panel/assets/{Cron-BAL9bwSp.js → Cron-D1_HsxyY.js} +2 -2
  24. package/src/assets/web-panel/assets/{Crosschain-D8WUi2dS.js → Crosschain-DZqoqoy5.js} +1 -1
  25. package/src/assets/web-panel/assets/{DID-CQCkYcLY.js → DID-Cwnr8hCO.js} +2 -2
  26. package/src/assets/web-panel/assets/{Dashboard-DogXg6kh.js → Dashboard-CxevypiI.js} +2 -2
  27. package/src/assets/web-panel/assets/{Dropdown-BJBJyQJn.js → Dropdown-Bs7-yWgr.js} +1 -1
  28. package/src/assets/web-panel/assets/{EmailListRenderer-CxPWquJx.js → EmailListRenderer-CcEs2Puo.js} +1 -1
  29. package/src/assets/web-panel/assets/{EvolutionSettings-BijH1x8G.js → EvolutionSettings-C9ipBzAQ.js} +1 -1
  30. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DStwf-00.js → FamilyGuardDashboard-Dt6--mGn.js} +1 -1
  31. package/src/assets/web-panel/assets/{Federation-LU9_sNxC.js → Federation-CD0JN5FF.js} +1 -1
  32. package/src/assets/web-panel/assets/{FormItemContext-DNfWhj7P.js → FormItemContext-POypz3Xv.js} +1 -1
  33. package/src/assets/web-panel/assets/{GenericCardRenderer-DWYsPjzk.js → GenericCardRenderer-Dv9f-0F5.js} +1 -1
  34. package/src/assets/web-panel/assets/{Git-CA0O9ulB.js → Git-BbTh5Atv.js} +2 -2
  35. package/src/assets/web-panel/assets/{Governance-ECShL7BJ.js → Governance-C71dgoir.js} +1 -1
  36. package/src/assets/web-panel/assets/{Inference-9GA2542e.js → Inference-DwSHxbJB.js} +1 -1
  37. package/src/assets/web-panel/assets/{KnowledgeGraph-nC5mTbEH.js → KnowledgeGraph-Cx1oQYQZ.js} +1 -1
  38. package/src/assets/web-panel/assets/{Logs-B7IqgnDy.js → Logs-BAJWe00o.js} +2 -2
  39. package/src/assets/web-panel/assets/MarkdownRenderer-CD8a7lHT.js +1 -0
  40. package/src/assets/web-panel/assets/{Marketplace-CEr0suM8.js → Marketplace-fwuY7P7Y.js} +1 -1
  41. package/src/assets/web-panel/assets/{McpTools-DhTPQKOE.js → McpTools-Di4693uc.js} +3 -3
  42. package/src/assets/web-panel/assets/{Memory-mHRZP1w4.js → Memory-Cz3L-GUB.js} +2 -2
  43. package/src/assets/web-panel/assets/{MobileBridge-BNfX6Rdo.js → MobileBridge-BuXKIkSb.js} +1 -1
  44. package/src/assets/web-panel/assets/MobileProjects-Bk-lK4Um.js +1 -0
  45. package/src/assets/web-panel/assets/{Mtc-CzkV3wsn.js → Mtc-LnACMFd0.js} +5 -5
  46. package/src/assets/web-panel/assets/{MtcAudit-DWYljJik.js → MtcAudit-BZy7yE0t.js} +2 -2
  47. package/src/assets/web-panel/assets/{Multisig-kHgtvZY_.js → Multisig-BFG5iVrE.js} +3 -3
  48. package/src/assets/web-panel/assets/{NLProgramming-56uQiPiY.js → NLProgramming-DgXi04vJ.js} +1 -1
  49. package/src/assets/web-panel/assets/{Notes-DxGOZqRf.js → Notes-BAj0Kgg4.js} +4 -4
  50. package/src/assets/web-panel/assets/{NotificationSettings-DabaCpmo.js → NotificationSettings-AixrsHw5.js} +1 -1
  51. package/src/assets/web-panel/assets/{OrderTableRenderer-SzeQNlGT.js → OrderTableRenderer-Dwqk3FlG.js} +1 -1
  52. package/src/assets/web-panel/assets/{Organization-D9gnPpK2.js → Organization-UNCBC3ux.js} +4 -4
  53. package/src/assets/web-panel/assets/{Overflow-Cqp_bXm4.js → Overflow-Drh8oAFT.js} +1 -1
  54. package/src/assets/web-panel/assets/{P2P-DTeK-r0G.js → P2P-bc2q0xEB.js} +2 -2
  55. package/src/assets/web-panel/assets/{PdhVaultBrowser-CsjG85GT.js → PdhVaultBrowser-BGwKxcDM.js} +3 -3
  56. package/src/assets/web-panel/assets/{Permissions-B4ofZq3j.js → Permissions-DOnUxUzu.js} +3 -3
  57. package/src/assets/web-panel/assets/{PersonalDataHub-Cvas5cYd.js → PersonalDataHub-B3qPZkrr.js} +4 -4
  58. package/src/assets/web-panel/assets/{Pipeline-Er3wmAew.js → Pipeline-B6hfa4-i.js} +1 -1
  59. package/src/assets/web-panel/assets/{Privacy-DMFYeZox.js → Privacy-Bun3c35F.js} +1 -1
  60. package/src/assets/web-panel/assets/{ProjectInit-DaVaYPy0.js → ProjectInit-CCObdzk9.js} +2 -2
  61. package/src/assets/web-panel/assets/{ProjectSettings-dPx5q2Ac.js → ProjectSettings-C6lKrSXY.js} +2 -2
  62. package/src/assets/web-panel/assets/{Projects-N0JNeCui.js → Projects-DYcta7MZ.js} +1 -1
  63. package/src/assets/web-panel/assets/{Providers-BPQo_gQa.js → Providers-BtykxKCe.js} +1 -1
  64. package/src/assets/web-panel/assets/{QrScannerModal-CD-sEsWh.js → QrScannerModal-D9CDyroY.js} +1 -1
  65. package/src/assets/web-panel/assets/{QuickAsk-LOKRJiN_.js → QuickAsk-CJauCxXx.js} +1 -1
  66. package/src/assets/web-panel/assets/{Recommend-BwPXqcWy.js → Recommend-BbiYZofR.js} +1 -1
  67. package/src/assets/web-panel/assets/{RemoteSession-BjotndxF.js → RemoteSession-BymCrgFW.js} +3 -3
  68. package/src/assets/web-panel/assets/{Reputation-BlZ2gcbU.js → Reputation-BWrGpMpF.js} +1 -1
  69. package/src/assets/web-panel/assets/{Row-D399HM-d.js → Row-9pOYIhiW.js} +1 -1
  70. package/src/assets/web-panel/assets/{RssFeed-C7H-ibnY.js → RssFeed-D3bQJ4qB.js} +2 -2
  71. package/src/assets/web-panel/assets/{Search-CNHCVmM1.js → Search-BpKOOFE0.js} +1 -1
  72. package/src/assets/web-panel/assets/{Security-C70eDGYY.js → Security-DC6C0NSz.js} +4 -4
  73. package/src/assets/web-panel/assets/{Services-6xfq9G-H.js → Services-Lkf1046J.js} +2 -2
  74. package/src/assets/web-panel/assets/{Skeleton-BbY2zeZV.js → Skeleton-eCQ_SsDb.js} +1 -1
  75. package/src/assets/web-panel/assets/{Skills-DYtyZ7_6.js → Skills-BvDSQ3-d.js} +1 -1
  76. package/src/assets/web-panel/assets/{Sla-CkWcl5OH.js → Sla-BQbKy8XJ.js} +1 -1
  77. package/src/assets/web-panel/assets/{SpeechSettings-CiknYk2P.js → SpeechSettings-DHsRx-XA.js} +1 -1
  78. package/src/assets/web-panel/assets/{SyncSettings-D-8JVip4.js → SyncSettings-CC281X_J.js} +2 -2
  79. package/src/assets/web-panel/assets/{Tasks-B2KnR9XC.js → Tasks-CHHSWHom.js} +1 -1
  80. package/src/assets/web-panel/assets/{Templates-CipXkOE-.js → Templates-Cr2vC0tg.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tenant-DbwgIMmx.js → Tenant-DeYO5pIG.js} +1 -1
  82. package/src/assets/web-panel/assets/{Terminal-OtCriHEE.js → Terminal-DBNgbfPr.js} +2 -2
  83. package/src/assets/web-panel/assets/TimelineRenderer-MoxgaF3x.js +1 -0
  84. package/src/assets/web-panel/assets/{Tokens-B4iFVxvH.js → Tokens-CT2QfAN7.js} +1 -1
  85. package/src/assets/web-panel/assets/{Trigger-BegM2v8q.js → Trigger-DPUnomxs.js} +1 -1
  86. package/src/assets/web-panel/assets/{Trust-COkqnnFj.js → Trust-DnufuaTx.js} +1 -1
  87. package/src/assets/web-panel/assets/{UkeySign-BzqKPChj.js → UkeySign-CJ6iOdPs.js} +1 -1
  88. package/src/assets/web-panel/assets/{VideoEditing-D-F2XL3C.js → VideoEditing-xBWkH8bL.js} +1 -1
  89. package/src/assets/web-panel/assets/{Wallet-22PzRY9T.js → Wallet-WYV-4hMR.js} +3 -3
  90. package/src/assets/web-panel/assets/{WebAuthn-eSMq_qtz.js → WebAuthn-DJ5Mhd9L.js} +4 -4
  91. package/src/assets/web-panel/assets/{WorkflowEditor-BvdAakpq.js → WorkflowEditor-B__UdOjN.js} +1 -1
  92. package/src/assets/web-panel/assets/{chat-C6jhpF7E.js → chat-CLlmBVhp.js} +1 -1
  93. package/src/assets/web-panel/assets/{colors-BbbW2Pfv.js → colors-B8zoPQYm.js} +1 -1
  94. package/src/assets/web-panel/assets/{compact-item-CVFeLTqt.js → compact-item-CvJ-dUsH.js} +1 -1
  95. package/src/assets/web-panel/assets/{createContext-DS0m7NZc.js → createContext-LAtMm8SC.js} +1 -1
  96. package/src/assets/web-panel/assets/devWarning-DgZ4fdGI.js +1 -0
  97. package/src/assets/web-panel/assets/{hasIn-DVouP0t6.js → hasIn-CLYvg42H.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-Be12HoCS.js → index-09YzRb6Z.js} +3 -3
  99. package/src/assets/web-panel/assets/{index-CPS8E-J6.js → index-4rr5jFOL.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-BXw6ivB8.js → index-B02qgCao.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BaUah6TO.js → index-B1hHp9_M.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-Ds4wbrgk.js → index-B6zlMzZz.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-JeJqXbUU.js → index-BCzyJ1xS.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-54aHB4FL.js → index-BGrQ2uEl.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-eaXEQFuP.js → index-BM2HqHdT.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-C74emdKD.js → index-BUVrYDNV.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-BrIv7C9a.js → index-B_RPWP_5.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-DGYX17YW.js → index-BkcQJvsz.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-DotRL126.js → index-Bu7-I2L4.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-BvCgzl0X.js → index-C2KFnUsv.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-C4xmLgLa.js → index-C3uD5H7u.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-BwozzG6k.js → index-C5v7cEtm.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-C6q2MkIG.js → index-C6PETyel.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-DUBkae39.js → index-CUcE6rIa.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-BwZnPiFd.js → index-CVDcRQuX.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-CIk7mg6G.js → index-C_dq-6_3.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-B8a_c_qZ.js → index-CnLXLaEF.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-CIg4nqyp.js → index-Cpda0SAz.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BhvnNFfd.js → index-Cv7vwX3z.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CpTbnbUI.js → index-CxJ4NPVq.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BzL3LWFX.js → index-D0Z13xcZ.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-BE_u-rXr.js → index-DDm9m4vC.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-ByLsH9CL.js → index-DHq2Of6z.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-BOOsA080.js → index-DNQUdPam.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-SdFkCtiC.js → index-DX0aHEHL.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-Bw-ZGQCH.js → index-DX99e8Xy.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-CkdYB0_g.js → index-DYW4Z3y-.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DuVjTDcW.js → index-DfOOACfd.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-DpReoAGb.js → index-Di9vKira.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-CwYFDMn_.js → index-DpeV-6y4.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-DR6TDz5H.js → index-GR3omApz.js} +1 -1
  132. package/src/assets/web-panel/assets/{index--AdzSlH7.js → index-f1ptYBMr.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-8QbwhE3i.js → index-iJ6s24bA.js} +1 -1
  134. package/src/assets/web-panel/assets/index-jLEdASOV.js +1 -0
  135. package/src/assets/web-panel/assets/index-jesGo4rl.js +1 -0
  136. package/src/assets/web-panel/assets/{index-B5JRIZXi.js → index-oX216HRH.js} +1 -1
  137. package/src/assets/web-panel/assets/{initDefaultProps-CB5cUnQ5.js → initDefaultProps-QD-koeaw.js} +1 -1
  138. package/src/assets/web-panel/assets/{motion-bn_fWhQV.js → motion-CTItLW4-.js} +1 -1
  139. package/src/assets/web-panel/assets/{move-DY4-Odtu.js → move-CGsdTOdN.js} +1 -1
  140. package/src/assets/web-panel/assets/{mtc-parser-B5l5mjoh.js → mtc-parser-C1kW_CNU.js} +1 -1
  141. package/src/assets/web-panel/assets/{omit-rJ-Ci8CW.js → omit-B4PilaYy.js} +1 -1
  142. package/src/assets/web-panel/assets/{pickAttrs-BMwGJd6C.js → pickAttrs-DBGUE12e.js} +1 -1
  143. package/src/assets/web-panel/assets/{placementArrow-CaiQyGXG.js → placementArrow-DZxCLjuo.js} +1 -1
  144. package/src/assets/web-panel/assets/{responsiveObserve-DQTO5wDH.js → responsiveObserve-CZjQiocN.js} +1 -1
  145. package/src/assets/web-panel/assets/{slide-Dg6ZDtbz.js → slide-Btz5hh2_.js} +1 -1
  146. package/src/assets/web-panel/assets/{statusUtils-bvjrktKc.js → statusUtils-CfuxuLIt.js} +1 -1
  147. package/src/assets/web-panel/assets/{styleChecker-C1geIU_x.js → styleChecker-DdO0Obo_.js} +1 -1
  148. package/src/assets/web-panel/assets/{useFlexGapSupport-BzU3RJRi.js → useFlexGapSupport-CxftJ-Zs.js} +1 -1
  149. package/src/assets/web-panel/assets/{useFs-B7l6OwLe.js → useFs-pVJChh6K.js} +1 -1
  150. package/src/assets/web-panel/assets/{usePersonalDataHub-BYF1L_8x.js → usePersonalDataHub-wv7Q0uNE.js} +1 -1
  151. package/src/assets/web-panel/assets/{vnode-6c2GVkT-.js → vnode-DVOVfAqC.js} +1 -1
  152. package/src/assets/web-panel/assets/{zoom-BYAY6QeD.js → zoom-B5tebZuT.js} +1 -1
  153. package/src/assets/web-panel/index.html +1 -1
  154. package/src/commands/permissions.js +27 -2
  155. package/src/data/changelog.json +26 -0
  156. package/src/lib/background-interaction-resolver.js +4 -0
  157. package/src/lib/context-memory-kernel/durable-memory-port.js +68 -15
  158. package/src/lib/packer/pack-update-applier.js +28 -9
  159. package/src/lib/packer/pkg-config-generator.js +119 -2
  160. package/src/lib/packer/pkg-runner.js +7 -0
  161. package/src/lib/packer/pkg-targets.js +83 -0
  162. package/src/lib/packer/web-panel-builder.js +7 -0
  163. package/src/lib/permission-authority.js +56 -7
  164. package/src/lib/process-execution-broker/index.js +11 -0
  165. package/src/lib/process-execution-broker/linux-subreaper-helper.js +87 -13
  166. package/src/lib/process-execution-broker/packer-build-policy.js +31 -0
  167. package/src/lib/process-execution-broker/platform-sandbox.js +11 -3
  168. package/src/lib/scoped-permission-store.js +241 -121
  169. package/src/lib/settings-authority-domain.cjs +966 -0
  170. package/src/lib/settings-authority-record.cjs +701 -0
  171. package/src/lib/settings-loader.cjs +45 -8
  172. package/src/lib/settings-source-observation.cjs +8 -0
  173. package/src/assets/web-panel/assets/ChatBubbleRenderer-DaRtY87l.js +0 -1
  174. package/src/assets/web-panel/assets/MarkdownRenderer-BqlqrJkY.js +0 -1
  175. package/src/assets/web-panel/assets/MobileProjects-8zG8f5li.js +0 -1
  176. package/src/assets/web-panel/assets/TimelineRenderer-B9LU5qrl.js +0 -1
  177. package/src/assets/web-panel/assets/devWarning-D50gzPS9.js +0 -1
  178. package/src/assets/web-panel/assets/index-CgensV-n.js +0 -1
  179. package/src/assets/web-panel/assets/index-Ctkf06Z_.js +0 -1
@@ -0,0 +1,966 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Explicit, cooperative Linux persistence for settings-authority records.
5
+ * This module is not wired into permission admission or the official settings
6
+ * writer. Callers must provide the complete trusted set of rollbackable config
7
+ * and admitted writable roots. There is no default path or implicit enrollment.
8
+ *
9
+ * The existing private directory and its ancestors must already be durably
10
+ * provisioned. Pinning does not prove a preceding mkdir was persisted in its
11
+ * parent. All child
12
+ * I/O uses that dirfd; exported launch data retains its identity and namespace.
13
+ * Ordinary replacement, missing witnesses and unfinished writes fail closed.
14
+ * This is not protection against a hostile same-UID owner deleting/rolling back
15
+ * the whole external domain, or raw settings ABA between observations. Other
16
+ * platforms are refused until equivalent directory durability is implemented.
17
+ */
18
+ const fsDefault = require("node:fs");
19
+ const path = require("node:path");
20
+ const { randomUUID } = require("node:crypto");
21
+ const { TextDecoder, types } = require("node:util");
22
+ const records = require("./settings-authority-record.cjs");
23
+
24
+ const DOMAIN_SCHEMA = "chainlesschain.settings-authority-domain/v1";
25
+ const TRANSACTION_SCHEMA = "chainlesschain.settings-authority-transaction/v1";
26
+ const NAMESPACE = "namespace.json";
27
+ const LEDGER = "ledger.json";
28
+ const GUARD = "guard.json";
29
+ const MAX_BYTES = records.LIMITS.recordBytes;
30
+ const DIRECTORY_FIELDS = ["dev", "ino", "mode", "uid"];
31
+ const FILE_FIELDS = [
32
+ "dev",
33
+ "ino",
34
+ "mode",
35
+ "uid",
36
+ "nlink",
37
+ "size",
38
+ "mtimeNs",
39
+ "ctimeNs",
40
+ ];
41
+ const domains = new WeakMap();
42
+ let lockHelper;
43
+
44
+ function failure(code, cause = null) {
45
+ const error = new Error(
46
+ "Persistent settings authority is unavailable",
47
+ cause ? { cause } : undefined,
48
+ );
49
+ error.name = "SettingsAuthorityDomainError";
50
+ error.code = code;
51
+ return error;
52
+ }
53
+
54
+ function refuse(code, cause) {
55
+ throw failure(code, cause);
56
+ }
57
+
58
+ function sameFields(left, right, fields) {
59
+ return fields.every(
60
+ (field) =>
61
+ left[field] !== undefined &&
62
+ right[field] !== undefined &&
63
+ String(left[field]) === String(right[field]),
64
+ );
65
+ }
66
+
67
+ function freeze(value) {
68
+ if (value && typeof value === "object") {
69
+ for (const child of Object.values(value)) freeze(child);
70
+ Object.freeze(value);
71
+ }
72
+ return value;
73
+ }
74
+
75
+ function canonicalData(value) {
76
+ if (Array.isArray(value)) return value.map(canonicalData);
77
+ if (!value || typeof value !== "object") return value;
78
+ return Object.fromEntries(
79
+ Object.keys(value)
80
+ .sort()
81
+ .map((key) => [key, canonicalData(value[key])]),
82
+ );
83
+ }
84
+
85
+ function sameData(left, right) {
86
+ return (
87
+ JSON.stringify(canonicalData(left)) === JSON.stringify(canonicalData(right))
88
+ );
89
+ }
90
+
91
+ // Launch descriptors are data, never capability callbacks or coercible objects.
92
+ function plainCopy(value, depth = 0) {
93
+ if (depth > 12) refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
94
+ if (value === null || typeof value === "boolean") return value;
95
+ if (typeof value === "string" && Buffer.byteLength(value) <= 4096)
96
+ return value;
97
+ if (typeof value === "number" && Number.isSafeInteger(value)) return value;
98
+ if (!value || typeof value !== "object" || types.isProxy(value))
99
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
100
+ const array = Array.isArray(value);
101
+ const proto = Object.getPrototypeOf(value);
102
+ if (
103
+ array
104
+ ? proto !== Array.prototype
105
+ : proto !== Object.prototype && proto !== null
106
+ )
107
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
108
+ const descriptors = Object.getOwnPropertyDescriptors(value);
109
+ const keys = Reflect.ownKeys(descriptors);
110
+ if (keys.length > 1024 || keys.some((key) => typeof key !== "string"))
111
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
112
+ const result = array ? [] : {};
113
+ for (const key of keys) {
114
+ if (array && key === "length") continue;
115
+ const descriptor = descriptors[key];
116
+ if (
117
+ !Object.hasOwn(descriptor, "value") ||
118
+ !descriptor.enumerable ||
119
+ key === "__proto__" ||
120
+ (array && !/^(0|[1-9][0-9]*)$/.test(key))
121
+ )
122
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
123
+ result[key] = plainCopy(descriptor.value, depth + 1);
124
+ }
125
+ if (
126
+ array &&
127
+ (result.length !== value.length || keys.length !== value.length + 1)
128
+ )
129
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
130
+ return result;
131
+ }
132
+
133
+ function shape(value, expected) {
134
+ if (
135
+ !value ||
136
+ typeof value !== "object" ||
137
+ Array.isArray(value) ||
138
+ Object.keys(value).length !== expected.length ||
139
+ expected.some((key) => !Object.hasOwn(value, key))
140
+ )
141
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
142
+ }
143
+
144
+ function absolute(value) {
145
+ if (
146
+ typeof value !== "string" ||
147
+ !value ||
148
+ value.includes("\0") ||
149
+ !path.isAbsolute(value) ||
150
+ Buffer.byteLength(value) > 4096
151
+ )
152
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
153
+ return path.resolve(value);
154
+ }
155
+
156
+ function inside(parent, child) {
157
+ const relative = path.relative(parent, child);
158
+ return (
159
+ relative === "" ||
160
+ (relative !== ".." &&
161
+ !relative.startsWith(`..${path.sep}`) &&
162
+ !path.isAbsolute(relative))
163
+ );
164
+ }
165
+
166
+ function statOrAbsent(fs, file) {
167
+ try {
168
+ return fs.lstatSync(file, { bigint: true });
169
+ } catch (cause) {
170
+ if (cause?.code === "ENOENT") return null;
171
+ throw cause;
172
+ }
173
+ }
174
+
175
+ function privateStat(stat, directory) {
176
+ if (
177
+ !stat ||
178
+ stat.isSymbolicLink() ||
179
+ (directory ? !stat.isDirectory() : !stat.isFile() || stat.nlink !== 1n) ||
180
+ stat.uid !== BigInt(process.getuid()) ||
181
+ (stat.mode & 0o077n) !== 0n
182
+ )
183
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_UNSAFE");
184
+ }
185
+
186
+ function directoryIdentity(stat) {
187
+ return Object.fromEntries(
188
+ DIRECTORY_FIELDS.map((field) => [field, String(stat[field])]),
189
+ );
190
+ }
191
+
192
+ function rootBinding(fs, root) {
193
+ const logicalPath = absolute(root);
194
+ let parent = logicalPath;
195
+ const remaining = [];
196
+ let stat;
197
+ while (!(stat = statOrAbsent(fs, parent))) {
198
+ const next = path.dirname(parent);
199
+ if (next === parent) refuse("CC_SETTINGS_AUTHORITY_DOMAIN_UNSAFE");
200
+ remaining.unshift(path.basename(parent));
201
+ parent = next;
202
+ }
203
+ if (
204
+ !stat.isDirectory() &&
205
+ !(stat.isSymbolicLink() && fs.statSync(parent).isDirectory())
206
+ )
207
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_UNSAFE");
208
+ const physicalParent = fs.realpathSync(parent);
209
+ const fd = fs.openSync(
210
+ physicalParent,
211
+ fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW,
212
+ );
213
+ try {
214
+ const held = fs.fstatSync(fd, { bigint: true });
215
+ if (!held.isDirectory()) refuse("CC_SETTINGS_AUTHORITY_DOMAIN_UNSAFE");
216
+ return {
217
+ logicalPath,
218
+ physicalPath: path.join(physicalParent, ...remaining),
219
+ parentPath: physicalParent,
220
+ identity: { dev: String(held.dev), ino: String(held.ino) },
221
+ };
222
+ } finally {
223
+ fs.closeSync(fd);
224
+ }
225
+ }
226
+
227
+ function openDirectory({ directory, forbiddenRoots, _fs = fsDefault }) {
228
+ if (process.platform !== "linux")
229
+ refuse("CC_SETTINGS_AUTHORITY_PLATFORM_UNSUPPORTED");
230
+ const fs = _fs;
231
+ const requested = absolute(directory);
232
+ const inputs = plainCopy(forbiddenRoots);
233
+ if (!Array.isArray(inputs) || !inputs.length || inputs.length > 64)
234
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
235
+ const roots = inputs.map((root) => rootBinding(fs, root));
236
+ const entry = fs.lstatSync(requested, { bigint: true });
237
+ privateStat(entry, true);
238
+ const canonicalPath = fs.realpathSync(requested);
239
+ // Use an explicitly provisioned real directory, including its ancestors.
240
+ if (canonicalPath !== requested)
241
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_UNSAFE");
242
+ if (
243
+ roots.some(
244
+ (root) =>
245
+ inside(root.physicalPath, canonicalPath) ||
246
+ inside(canonicalPath, root.physicalPath),
247
+ )
248
+ )
249
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
250
+ let descriptor = null;
251
+ try {
252
+ descriptor = fs.openSync(
253
+ canonicalPath,
254
+ fs.constants.O_RDONLY |
255
+ fs.constants.O_DIRECTORY |
256
+ fs.constants.O_NOFOLLOW,
257
+ );
258
+ const held = fs.fstatSync(descriptor, { bigint: true });
259
+ privateStat(held, true);
260
+ if (!sameFields(entry, held, DIRECTORY_FIELDS))
261
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
262
+ const state = {
263
+ fs,
264
+ descriptor,
265
+ canonicalPath,
266
+ root: `/proc/self/fd/${descriptor}`,
267
+ identity: directoryIdentity(held),
268
+ forbiddenRoots: roots,
269
+ namespace: null,
270
+ busy: false,
271
+ closed: false,
272
+ intern: new Map(),
273
+ lastLocalRevision: null,
274
+ };
275
+ if (
276
+ !sameFields(
277
+ fs.statSync(state.root, { bigint: true }),
278
+ held,
279
+ DIRECTORY_FIELDS,
280
+ )
281
+ )
282
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
283
+ verifyDirectory(state);
284
+ return state;
285
+ } catch (cause) {
286
+ if (descriptor !== null) fs.closeSync(descriptor);
287
+ throw cause;
288
+ }
289
+ }
290
+
291
+ function verifyDirectory(state) {
292
+ if (state.closed) refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CLOSED");
293
+ const { fs } = state;
294
+ const held = fs.fstatSync(state.descriptor, { bigint: true });
295
+ const entry = fs.lstatSync(state.canonicalPath, { bigint: true });
296
+ privateStat(held, true);
297
+ privateStat(entry, true);
298
+ if (
299
+ !sameFields(state.identity, held, DIRECTORY_FIELDS) ||
300
+ !sameFields(held, entry, DIRECTORY_FIELDS) ||
301
+ fs.realpathSync(state.canonicalPath) !== state.canonicalPath
302
+ )
303
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
304
+ for (const root of state.forbiddenRoots) {
305
+ const current = rootBinding(fs, root.logicalPath);
306
+ if (!sameData(current, root))
307
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
308
+ }
309
+ }
310
+
311
+ function child(state, name) {
312
+ return path.posix.join(state.root, name);
313
+ }
314
+
315
+ function readJson(state, name, { absent = false } = {}) {
316
+ const { fs } = state;
317
+ const file = child(state, name);
318
+ const entry = statOrAbsent(fs, file);
319
+ if (!entry) {
320
+ if (absent) return null;
321
+ refuse("CC_SETTINGS_AUTHORITY_WITNESS_MISSING");
322
+ }
323
+ privateStat(entry, false);
324
+ const fd = fs.openSync(
325
+ file,
326
+ fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK,
327
+ );
328
+ try {
329
+ const before = fs.fstatSync(fd, { bigint: true });
330
+ privateStat(before, false);
331
+ if (
332
+ !sameFields(entry, before, FILE_FIELDS) ||
333
+ before.size > BigInt(MAX_BYTES)
334
+ )
335
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
336
+ const buffer = Buffer.alloc(Number(before.size) + 1);
337
+ let length = 0;
338
+ for (;;) {
339
+ const count = fs.readSync(
340
+ fd,
341
+ buffer,
342
+ length,
343
+ buffer.length - length,
344
+ length,
345
+ );
346
+ if (!count) break;
347
+ length += count;
348
+ if (length === buffer.length)
349
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
350
+ }
351
+ const after = fs.fstatSync(fd, { bigint: true });
352
+ const published = fs.lstatSync(file, { bigint: true });
353
+ if (
354
+ !sameFields(before, after, FILE_FIELDS) ||
355
+ !sameFields(after, published, FILE_FIELDS) ||
356
+ BigInt(length) !== after.size
357
+ )
358
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
359
+ const text = new TextDecoder("utf-8", {
360
+ fatal: true,
361
+ ignoreBOM: true,
362
+ }).decode(buffer.subarray(0, length));
363
+ return JSON.parse(text);
364
+ } finally {
365
+ fs.closeSync(fd);
366
+ }
367
+ }
368
+
369
+ function validateNamespace(value) {
370
+ const data = plainCopy(value);
371
+ shape(data, [
372
+ "schema",
373
+ "platform",
374
+ "domainId",
375
+ "epoch",
376
+ "directory",
377
+ "identity",
378
+ "forbiddenRoots",
379
+ ]);
380
+ if (
381
+ data.schema !== DOMAIN_SCHEMA ||
382
+ data.platform !== "linux" ||
383
+ !/^[0-9a-f-]{36}$/.test(data.domainId) ||
384
+ !/^[0-9a-f-]{36}$/.test(data.epoch) ||
385
+ absolute(data.directory) !== data.directory
386
+ )
387
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
388
+ shape(data.identity, DIRECTORY_FIELDS);
389
+ if (
390
+ DIRECTORY_FIELDS.some(
391
+ (field) =>
392
+ typeof data.identity[field] !== "string" ||
393
+ !/^(0|[1-9][0-9]{0,39})$/.test(data.identity[field]),
394
+ )
395
+ )
396
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
397
+ if (
398
+ !Array.isArray(data.forbiddenRoots) ||
399
+ !data.forbiddenRoots.length ||
400
+ data.forbiddenRoots.length > 64
401
+ )
402
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
403
+ for (const root of data.forbiddenRoots) {
404
+ shape(root, ["logicalPath", "physicalPath", "parentPath", "identity"]);
405
+ for (const key of ["logicalPath", "physicalPath", "parentPath"])
406
+ if (absolute(root[key]) !== root[key])
407
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
408
+ shape(root.identity, ["dev", "ino"]);
409
+ if (
410
+ [root.identity.dev, root.identity.ino].some(
411
+ (item) =>
412
+ typeof item !== "string" || !/^(0|[1-9][0-9]{0,39})$/.test(item),
413
+ )
414
+ )
415
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
416
+ }
417
+ return freeze(data);
418
+ }
419
+
420
+ function verifyNamespace(state) {
421
+ verifyDirectory(state);
422
+ const observed = validateNamespace(readJson(state, NAMESPACE));
423
+ if (!sameData(observed, state.namespace))
424
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
425
+ }
426
+
427
+ function authorityLedger(state) {
428
+ const ledger = records.validateLedger({ record: readJson(state, LEDGER) });
429
+ if (ledger.epoch !== state.namespace.epoch || ledger.platform !== "posix")
430
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
431
+ for (const head of ledger.physicalHeads)
432
+ if (inside(state.canonicalPath, head.physicalPath))
433
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
434
+ return ledger;
435
+ }
436
+
437
+ function guardAbsent(state) {
438
+ // Any guard entry blocks: invalid JSON, link, permissions and I/O errors are
439
+ // not positive evidence of absence and must never become a fresh baseline.
440
+ if (statOrAbsent(state.fs, child(state, GUARD)))
441
+ refuse("CC_SETTINGS_AUTHORITY_NOT_READY");
442
+ }
443
+
444
+ function handleFor(state) {
445
+ const handle = Object.freeze({});
446
+ domains.set(handle, state);
447
+ return handle;
448
+ }
449
+
450
+ function stateFor(handle) {
451
+ const state = domains.get(handle);
452
+ if (!state) refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
453
+ if (state.closed) refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CLOSED");
454
+ return state;
455
+ }
456
+
457
+ function pinSettingsAuthorityDomain(options) {
458
+ const state = openDirectory(options);
459
+ try {
460
+ state.namespace = validateNamespace(readJson(state, NAMESPACE));
461
+ if (
462
+ state.namespace.directory !== state.canonicalPath ||
463
+ !sameData(state.namespace.identity, state.identity) ||
464
+ !sameData(state.namespace.forbiddenRoots, state.forbiddenRoots)
465
+ )
466
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
467
+ verifyNamespace(state);
468
+ // Pinning may inspect a pending domain for explicit recovery. It grants no
469
+ // ready snapshot; readSettingsAuthority still rejects guard/prepared.
470
+ return handleFor(state);
471
+ } catch (cause) {
472
+ state.fs.closeSync(state.descriptor);
473
+ throw cause;
474
+ }
475
+ }
476
+
477
+ function exportSettingsAuthorityDomain(handle) {
478
+ const state = stateFor(handle);
479
+ verifyNamespace(state);
480
+ return state.namespace;
481
+ }
482
+
483
+ function reopenSettingsAuthorityDomain({ descriptor, _fs = fsDefault }) {
484
+ const expected = validateNamespace(descriptor);
485
+ const handle = pinSettingsAuthorityDomain({
486
+ directory: expected.directory,
487
+ forbiddenRoots: expected.forbiddenRoots.map((root) => root.logicalPath),
488
+ _fs,
489
+ });
490
+ if (!sameData(exportSettingsAuthorityDomain(handle), expected)) {
491
+ closeSettingsAuthorityDomain(handle);
492
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
493
+ }
494
+ return handle;
495
+ }
496
+
497
+ function closeSettingsAuthorityDomain(handle) {
498
+ const state = stateFor(handle);
499
+ if (state.busy) refuse("CC_SETTINGS_AUTHORITY_REENTRY");
500
+ state.closed = true;
501
+ state.intern.clear();
502
+ state.fs.closeSync(state.descriptor);
503
+ }
504
+
505
+ function locked(state, callback) {
506
+ if (state.busy) refuse("CC_SETTINGS_AUTHORITY_REENTRY");
507
+ state.busy = true;
508
+ try {
509
+ verifyDirectory(state);
510
+ lockHelper ??= require("./with-file-lock.js").withFileLock;
511
+ return lockHelper(
512
+ child(state, LEDGER),
513
+ () => {
514
+ verifyDirectory(state);
515
+ return callback();
516
+ },
517
+ { failIfUnavailable: true, _fs: state.fs, timeoutMs: 5000 },
518
+ );
519
+ } finally {
520
+ state.busy = false;
521
+ }
522
+ }
523
+
524
+ function atomicJson(state, name, value) {
525
+ const { fs } = state;
526
+ const bytes = Buffer.from(`${JSON.stringify(value)}\n`);
527
+ if (bytes.length > MAX_BYTES) refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
528
+ const target = child(state, name);
529
+ const previous = statOrAbsent(fs, target);
530
+ if (previous) privateStat(previous, false);
531
+ const temporary = child(state, `.${name}.${process.pid}.${randomUUID()}.tmp`);
532
+ let fd = null;
533
+ let renamed = false;
534
+ try {
535
+ fd = fs.openSync(
536
+ temporary,
537
+ fs.constants.O_WRONLY |
538
+ fs.constants.O_CREAT |
539
+ fs.constants.O_EXCL |
540
+ fs.constants.O_NOFOLLOW,
541
+ 0o600,
542
+ );
543
+ fs.writeFileSync(fd, bytes);
544
+ fs.fsyncSync(fd);
545
+ fs.closeSync(fd);
546
+ fd = null;
547
+ fs.renameSync(temporary, target);
548
+ renamed = true;
549
+ fs.fsyncSync(state.descriptor);
550
+ verifyDirectory(state);
551
+ } catch (cause) {
552
+ if (fd !== null) {
553
+ try {
554
+ fs.closeSync(fd);
555
+ } catch {
556
+ /* preserve failure */
557
+ }
558
+ }
559
+ if (!renamed) {
560
+ try {
561
+ fs.unlinkSync(temporary);
562
+ } catch {
563
+ /* orphan is not authority */
564
+ }
565
+ }
566
+ const error = failure("CC_SETTINGS_AUTHORITY_PERSIST_FAILED", cause);
567
+ error.authorityCommitState = renamed ? "unknown" : "not-committed";
568
+ throw error;
569
+ }
570
+ }
571
+
572
+ function removeGuard(state) {
573
+ state.fs.unlinkSync(child(state, GUARD));
574
+ state.fs.fsyncSync(state.descriptor);
575
+ verifyNamespace(state);
576
+ }
577
+
578
+ function transactionWitness(pending) {
579
+ const witness = {
580
+ schema: TRANSACTION_SCHEMA,
581
+ guard: pending.guard,
582
+ prepared: pending.prepared,
583
+ };
584
+ // Reject an overlarge transaction before publishing a guard/revoking locally.
585
+ if (Buffer.byteLength(JSON.stringify(witness)) + 1 > MAX_BYTES)
586
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
587
+ return witness;
588
+ }
589
+
590
+ function readTransaction(state) {
591
+ const value = readJson(state, GUARD);
592
+ shape(value, ["schema", "guard", "prepared"]);
593
+ if (value.schema !== TRANSACTION_SCHEMA)
594
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
595
+ const prepared = records.validateLedger({ record: value.prepared });
596
+ const guard = records.validateGuard({ guard: value.guard, prepared });
597
+ if (guard.epoch !== state.namespace.epoch || guard.platform !== "posix")
598
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_CHANGED");
599
+ return { prepared, guard };
600
+ }
601
+
602
+ function initializeSettingsAuthorityDomain({
603
+ directory,
604
+ forbiddenRoots,
605
+ contexts,
606
+ observeContexts,
607
+ _fs = fsDefault,
608
+ }) {
609
+ const state = openDirectory({ directory, forbiddenRoots, _fs });
610
+ try {
611
+ locked(state, () => {
612
+ if (
613
+ [NAMESPACE, LEDGER, GUARD].some((name) =>
614
+ statOrAbsent(state.fs, child(state, name)),
615
+ )
616
+ )
617
+ refuse("CC_SETTINGS_AUTHORITY_ALREADY_INITIALIZED");
618
+ state.namespace = validateNamespace({
619
+ schema: DOMAIN_SCHEMA,
620
+ platform: "linux",
621
+ domainId: randomUUID(),
622
+ epoch: randomUUID(),
623
+ directory: state.canonicalPath,
624
+ identity: state.identity,
625
+ forbiddenRoots: state.forbiddenRoots,
626
+ });
627
+ const ledger = records.createInitialLedger({
628
+ epoch: state.namespace.epoch,
629
+ contexts,
630
+ });
631
+ for (const head of ledger.physicalHeads)
632
+ if (inside(state.canonicalPath, head.physicalPath))
633
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
634
+ observeReady(ledger, observeContexts);
635
+ // A surviving namespace forbids repeating generation-zero enrollment if
636
+ // initial ledger publication fails or is subsequently removed.
637
+ atomicJson(state, NAMESPACE, state.namespace);
638
+ atomicJson(state, LEDGER, ledger);
639
+ verifyNamespace(state);
640
+ observeReady(ledger, observeContexts);
641
+ });
642
+ return handleFor(state);
643
+ } catch (cause) {
644
+ state.fs.closeSync(state.descriptor);
645
+ throw cause;
646
+ }
647
+ }
648
+
649
+ function synchronous(callback, argument) {
650
+ if (
651
+ typeof callback !== "function" ||
652
+ callback.constructor?.name === "AsyncFunction"
653
+ )
654
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
655
+ const result = callback(argument);
656
+ if (result && typeof result.then === "function")
657
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
658
+ return result;
659
+ }
660
+
661
+ function requireSynchronous(callback) {
662
+ if (
663
+ typeof callback !== "function" ||
664
+ callback.constructor?.name === "AsyncFunction"
665
+ )
666
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
667
+ }
668
+
669
+ function observeReady(ledger, observeContexts) {
670
+ const contexts = synchronous(observeContexts, ledger.contexts);
671
+ if (!Array.isArray(contexts) || contexts.length !== ledger.contexts.length)
672
+ refuse("CC_SETTINGS_AUTHORITY_BINDING_CHANGED");
673
+ for (let i = 0; i < contexts.length; i++) {
674
+ if (contexts[i]?.contextId !== ledger.contexts[i].contextId)
675
+ refuse("CC_SETTINGS_AUTHORITY_BINDING_CHANGED");
676
+ records.assertContextMatches({ ledger, manifest: contexts[i] });
677
+ }
678
+ return contexts;
679
+ }
680
+
681
+ function readSettingsAuthority(
682
+ handle,
683
+ { observeContexts, localRevision = null } = {},
684
+ ) {
685
+ const state = stateFor(handle);
686
+ const beforeLocal = localRevision ? synchronous(localRevision) : null;
687
+ verifyNamespace(state);
688
+ guardAbsent(state);
689
+ const before = authorityLedger(state);
690
+ if (before.phase !== "ready") refuse("CC_SETTINGS_AUTHORITY_NOT_READY");
691
+ observeReady(before, observeContexts);
692
+ const after = authorityLedger(state);
693
+ if (
694
+ records.recordDigest({ record: before }) !==
695
+ records.recordDigest({ record: after })
696
+ )
697
+ refuse("CC_SETTINGS_AUTHORITY_CHANGED");
698
+ guardAbsent(state);
699
+ verifyNamespace(state);
700
+ if (localRevision && synchronous(localRevision) !== beforeLocal)
701
+ refuse("CC_SETTINGS_AUTHORITY_CHANGED");
702
+ const key = `${before.epoch}:${before.generation}:${records.recordDigest({ record: before })}`;
703
+ let snapshot = state.intern.get(key);
704
+ if (state.lastLocalRevision !== beforeLocal) snapshot = null;
705
+ if (!snapshot) {
706
+ snapshot = freeze({
707
+ domainId: state.namespace.domainId,
708
+ epoch: before.epoch,
709
+ generation: before.generation,
710
+ digest: records.recordDigest({ record: before }),
711
+ state: "ready",
712
+ });
713
+ // One current tuple is enough for reference comparisons. Old permits keep
714
+ // their own snapshot; every subsequent call rereads the durable authority.
715
+ state.intern.clear();
716
+ state.intern.set(key, snapshot);
717
+ state.lastLocalRevision = beforeLocal;
718
+ }
719
+ return Object.freeze({ ledger: before, contexts: before.contexts, snapshot });
720
+ }
721
+
722
+ function transitionSettingsAuthority(
723
+ handle,
724
+ {
725
+ intent,
726
+ observeContexts,
727
+ replace,
728
+ revokeLocal,
729
+ transactionId = randomUUID(),
730
+ } = {},
731
+ ) {
732
+ const state = stateFor(handle);
733
+ // Snapshot caller data before any callback. These are trusted host APIs, but
734
+ // mutable intents must not turn an already-validated no-op into a mutation.
735
+ intent = plainCopy(intent);
736
+ requireSynchronous(observeContexts);
737
+ requireSynchronous(revokeLocal);
738
+ if (intent.kind === "write") requireSynchronous(replace);
739
+ let settingsCommitState = "not-committed";
740
+ let guarded = false;
741
+ let ready = false;
742
+ let cleanupComplete = false;
743
+ try {
744
+ return locked(state, () => {
745
+ const perform = () => {
746
+ const current = readSettingsAuthority(handle, { observeContexts });
747
+ if (
748
+ intent.kind === "register" &&
749
+ current.ledger.contexts.some(
750
+ (context) => context.contextId === intent.manifest?.contextId,
751
+ )
752
+ ) {
753
+ shape(intent, ["kind", "manifest"]);
754
+ records.assertContextMatches({
755
+ ledger: current.ledger,
756
+ manifest: intent.manifest,
757
+ });
758
+ return {
759
+ changed: false,
760
+ snapshot: current.snapshot,
761
+ commitState: "not-committed",
762
+ };
763
+ }
764
+ const pending = records.prepareTransition({
765
+ ledger: current.ledger,
766
+ transactionId,
767
+ intent,
768
+ });
769
+ if (
770
+ pending.prepared.transition.intent.kind === "register" &&
771
+ pending.prepared.transition.intent.manifest.sources.some((source) =>
772
+ inside(state.canonicalPath, source.physicalPath),
773
+ )
774
+ )
775
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
776
+ const target =
777
+ intent.kind === "write"
778
+ ? current.ledger.physicalHeads.find(
779
+ (head) => head.physicalPath === intent.physicalPath,
780
+ )
781
+ : null;
782
+ if (
783
+ target &&
784
+ ["exists", "byteLength", "digest"].every(
785
+ (key) => target[key] === intent.after[key],
786
+ )
787
+ )
788
+ return {
789
+ changed: false,
790
+ snapshot: current.snapshot,
791
+ commitState: "not-committed",
792
+ };
793
+ const witness = transactionWitness(pending);
794
+ synchronous(revokeLocal);
795
+ atomicJson(state, GUARD, witness);
796
+ guarded = true;
797
+ atomicJson(state, LEDGER, pending.prepared);
798
+ if (pending.prepared.transition.intent.kind === "write") {
799
+ try {
800
+ synchronous(replace, pending.prepared.transition.intent);
801
+ settingsCommitState = "committed";
802
+ } catch (cause) {
803
+ settingsCommitState = [
804
+ "not-committed",
805
+ "committed",
806
+ "unknown",
807
+ ].includes(cause?.commitState)
808
+ ? cause.commitState
809
+ : "unknown";
810
+ throw cause;
811
+ }
812
+ }
813
+ const expected =
814
+ pending.prepared.transition.intent.kind === "register"
815
+ ? [
816
+ ...pending.prepared.contexts,
817
+ pending.prepared.transition.intent.manifest,
818
+ ]
819
+ : pending.prepared.contexts;
820
+ const contexts = synchronous(observeContexts, expected);
821
+ const settled = records.settleTransition({
822
+ ...pending,
823
+ contexts,
824
+ outcome: "after",
825
+ });
826
+ if (
827
+ settled.physicalHeads.some((head) =>
828
+ inside(state.canonicalPath, head.physicalPath),
829
+ )
830
+ )
831
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
832
+ atomicJson(state, LEDGER, settled);
833
+ ready = true;
834
+ removeGuard(state);
835
+ cleanupComplete = true;
836
+ return {
837
+ changed: true,
838
+ generation: settled.generation,
839
+ commitState: settingsCommitState,
840
+ authorityState: "ready",
841
+ };
842
+ };
843
+ if (intent.kind !== "write") return perform();
844
+ const admission = readSettingsAuthority(handle, { observeContexts });
845
+ records.prepareTransition({
846
+ ledger: admission.ledger,
847
+ transactionId,
848
+ intent,
849
+ });
850
+ // Fixed authority -> source lock order. The core refuses writes without
851
+ // an existing direct parent, so no source mkdir happens before the guard.
852
+ const physicalPath = absolute(intent.physicalPath);
853
+ if (physicalPath !== intent.physicalPath)
854
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_INVALID");
855
+ return lockHelper(physicalPath, perform, {
856
+ failIfUnavailable: true,
857
+ _fs: state.fs,
858
+ timeoutMs: 5000,
859
+ });
860
+ });
861
+ } catch (cause) {
862
+ const error = failure("CC_SETTINGS_AUTHORITY_TRANSITION_FAILED", cause);
863
+ error.commitState = settingsCommitState;
864
+ let guardPresent = guarded;
865
+ try {
866
+ guardPresent ||= Boolean(statOrAbsent(state.fs, child(state, GUARD)));
867
+ } catch {
868
+ guardPresent = true;
869
+ }
870
+ error.authorityState = cleanupComplete
871
+ ? "ready"
872
+ : ready
873
+ ? "ready-cleanup-unconfirmed"
874
+ : guardPresent
875
+ ? "blocked"
876
+ : "unchanged";
877
+ throw error;
878
+ }
879
+ }
880
+
881
+ function recoverSettingsAuthority(
882
+ handle,
883
+ { transactionId, observeContexts, outcome } = {},
884
+ ) {
885
+ const state = stateFor(handle);
886
+ return locked(state, () => {
887
+ verifyNamespace(state);
888
+ const { prepared, guard } = readTransaction(state);
889
+ if (guard.transactionId !== transactionId)
890
+ refuse("CC_SETTINGS_AUTHORITY_TRANSACTION_MISMATCH");
891
+ const initial = authorityLedger(state);
892
+ if (initial.phase === "prepared") {
893
+ if (
894
+ records.recordDigest({ record: initial }) !==
895
+ records.recordDigest({ record: prepared })
896
+ )
897
+ refuse("CC_SETTINGS_AUTHORITY_TRANSACTION_MISMATCH");
898
+ } else if (
899
+ initial.generation !== guard.generation &&
900
+ records.recordDigest({ record: initial }) !== guard.beforeDigest
901
+ )
902
+ refuse("CC_SETTINGS_AUTHORITY_TRANSACTION_MISMATCH");
903
+ const perform = () => {
904
+ const ledger = authorityLedger(state);
905
+ const expected =
906
+ prepared.transition.intent.kind === "register" && outcome === "after"
907
+ ? [...prepared.contexts, prepared.transition.intent.manifest]
908
+ : prepared.contexts;
909
+ const contexts = synchronous(observeContexts, expected);
910
+ const settled = records.settleTransition({
911
+ prepared,
912
+ guard,
913
+ contexts,
914
+ outcome,
915
+ });
916
+ if (
917
+ settled.physicalHeads.some((head) =>
918
+ inside(state.canonicalPath, head.physicalPath),
919
+ )
920
+ )
921
+ refuse("CC_SETTINGS_AUTHORITY_DOMAIN_OVERLAP");
922
+ if (ledger.phase === "ready" && ledger.generation === guard.generation) {
923
+ // The guard retains the bounded before record, so cleanup can prove the
924
+ // exact derived ready tuple instead of treating generation alone as proof.
925
+ if (
926
+ records.recordDigest({ record: ledger }) !==
927
+ records.recordDigest({ record: settled })
928
+ )
929
+ refuse("CC_SETTINGS_AUTHORITY_TRANSACTION_MISMATCH");
930
+ } else if (ledger.phase === "ready") {
931
+ if (records.recordDigest({ record: ledger }) !== guard.beforeDigest)
932
+ refuse("CC_SETTINGS_AUTHORITY_TRANSACTION_MISMATCH");
933
+ } else if (
934
+ records.recordDigest({ record: ledger }) !==
935
+ records.recordDigest({ record: prepared })
936
+ )
937
+ refuse("CC_SETTINGS_AUTHORITY_TRANSACTION_MISMATCH");
938
+ // Both guard-only recovery and verified-old recovery consume g + 1.
939
+ atomicJson(state, LEDGER, settled);
940
+ removeGuard(state);
941
+ return {
942
+ generation: settled.generation,
943
+ authorityState: "ready",
944
+ outcome,
945
+ };
946
+ };
947
+ if (guard.intent.kind !== "write") return perform();
948
+ return lockHelper(guard.intent.physicalPath, perform, {
949
+ failIfUnavailable: true,
950
+ _fs: state.fs,
951
+ timeoutMs: 5000,
952
+ });
953
+ });
954
+ }
955
+
956
+ module.exports = {
957
+ DOMAIN_SCHEMA,
958
+ initializeSettingsAuthorityDomain,
959
+ pinSettingsAuthorityDomain,
960
+ exportSettingsAuthorityDomain,
961
+ reopenSettingsAuthorityDomain,
962
+ closeSettingsAuthorityDomain,
963
+ readSettingsAuthority,
964
+ transitionSettingsAuthority,
965
+ recoverSettingsAuthority,
966
+ };