@homeflare/alchemy 0.1.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 (417) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/cloudflare/client.d.ts +45 -0
  4. package/dist/cloudflare/client.d.ts.map +1 -0
  5. package/dist/cloudflare/index.d.ts +11 -0
  6. package/dist/cloudflare/index.d.ts.map +1 -0
  7. package/dist/cloudflare/index.js +129 -0
  8. package/dist/cloudflare/index.js.map +13 -0
  9. package/dist/cloudflare/lock-rules.d.ts +50 -0
  10. package/dist/cloudflare/lock-rules.d.ts.map +1 -0
  11. package/dist/cloudflare/providers.d.ts +19 -0
  12. package/dist/cloudflare/providers.d.ts.map +1 -0
  13. package/dist/cloudflare/r2-bucket-lock.d.ts +100 -0
  14. package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -0
  15. package/dist/forgejo/branch-protection-form.d.ts +19 -0
  16. package/dist/forgejo/branch-protection-form.d.ts.map +1 -0
  17. package/dist/forgejo/branch-protection.d.ts +47 -0
  18. package/dist/forgejo/branch-protection.d.ts.map +1 -0
  19. package/dist/forgejo/client.d.ts +37 -0
  20. package/dist/forgejo/client.d.ts.map +1 -0
  21. package/dist/forgejo/index.d.ts +18 -0
  22. package/dist/forgejo/index.d.ts.map +1 -0
  23. package/dist/forgejo/index.js +479 -0
  24. package/dist/forgejo/index.js.map +21 -0
  25. package/dist/forgejo/org-actions-secrets.d.ts +45 -0
  26. package/dist/forgejo/org-actions-secrets.d.ts.map +1 -0
  27. package/dist/forgejo/org-label.d.ts +46 -0
  28. package/dist/forgejo/org-label.d.ts.map +1 -0
  29. package/dist/forgejo/org-team.d.ts +42 -0
  30. package/dist/forgejo/org-team.d.ts.map +1 -0
  31. package/dist/forgejo/repo-webhook-form.d.ts +27 -0
  32. package/dist/forgejo/repo-webhook-form.d.ts.map +1 -0
  33. package/dist/forgejo/repo-webhook.d.ts +46 -0
  34. package/dist/forgejo/repo-webhook.d.ts.map +1 -0
  35. package/dist/forgejo/repository.d.ts +45 -0
  36. package/dist/forgejo/repository.d.ts.map +1 -0
  37. package/dist/forgejo/resource.d.ts +83 -0
  38. package/dist/forgejo/resource.d.ts.map +1 -0
  39. package/dist/forgejo/team-member.d.ts +39 -0
  40. package/dist/forgejo/team-member.d.ts.map +1 -0
  41. package/dist/forgejo/values.d.ts +24 -0
  42. package/dist/forgejo/values.d.ts.map +1 -0
  43. package/dist/index.d.ts +18 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +3 -0
  46. package/dist/index.js.map +9 -0
  47. package/dist/openbao/auth-role-form.d.ts +38 -0
  48. package/dist/openbao/auth-role-form.d.ts.map +1 -0
  49. package/dist/openbao/auth-role.d.ts +20 -0
  50. package/dist/openbao/auth-role.d.ts.map +1 -0
  51. package/dist/openbao/bao-address.d.ts +72 -0
  52. package/dist/openbao/bao-address.d.ts.map +1 -0
  53. package/dist/openbao/bao-http.d.ts +71 -0
  54. package/dist/openbao/bao-http.d.ts.map +1 -0
  55. package/dist/openbao/bao-status.d.ts +74 -0
  56. package/dist/openbao/bao-status.d.ts.map +1 -0
  57. package/dist/openbao/cloudflare-group-scope.d.ts +73 -0
  58. package/dist/openbao/cloudflare-group-scope.d.ts.map +1 -0
  59. package/dist/openbao/cloudflare-parity-catalog.d.ts +28 -0
  60. package/dist/openbao/cloudflare-parity-catalog.d.ts.map +1 -0
  61. package/dist/openbao/cloudflare-parity-snapshot.d.ts +31 -0
  62. package/dist/openbao/cloudflare-parity-snapshot.d.ts.map +1 -0
  63. package/dist/openbao/cloudflare-parity.d.ts +14 -0
  64. package/dist/openbao/cloudflare-parity.d.ts.map +1 -0
  65. package/dist/openbao/cloudflare-permission-groups-solve.d.ts +85 -0
  66. package/dist/openbao/cloudflare-permission-groups-solve.d.ts.map +1 -0
  67. package/dist/openbao/cloudflare-permission-groups.d.ts +87 -0
  68. package/dist/openbao/cloudflare-permission-groups.d.ts.map +1 -0
  69. package/dist/openbao/cloudflare-policy.d.ts +65 -0
  70. package/dist/openbao/cloudflare-policy.d.ts.map +1 -0
  71. package/dist/openbao/cloudflare-role-form.d.ts +110 -0
  72. package/dist/openbao/cloudflare-role-form.d.ts.map +1 -0
  73. package/dist/openbao/cloudflare-role-wire.d.ts +27 -0
  74. package/dist/openbao/cloudflare-role-wire.d.ts.map +1 -0
  75. package/dist/openbao/cloudflare-role.d.ts +35 -0
  76. package/dist/openbao/cloudflare-role.d.ts.map +1 -0
  77. package/dist/openbao/cloudflare-roles-config.d.ts +67 -0
  78. package/dist/openbao/cloudflare-roles-config.d.ts.map +1 -0
  79. package/dist/openbao/cloudflare-roles-expand.d.ts +76 -0
  80. package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -0
  81. package/dist/openbao/digest.d.ts +24 -0
  82. package/dist/openbao/digest.d.ts.map +1 -0
  83. package/dist/openbao/fake-bao.d.ts +40 -0
  84. package/dist/openbao/fake-bao.d.ts.map +1 -0
  85. package/dist/openbao/forgejo-bootstrap-parse.d.ts +46 -0
  86. package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -0
  87. package/dist/openbao/forgejo-bootstrap.d.ts +29 -0
  88. package/dist/openbao/forgejo-bootstrap.d.ts.map +1 -0
  89. package/dist/openbao/index.d.ts +18 -0
  90. package/dist/openbao/index.d.ts.map +1 -0
  91. package/dist/openbao/index.js +1158 -0
  92. package/dist/openbao/index.js.map +33 -0
  93. package/dist/openbao/mount-form.d.ts +60 -0
  94. package/dist/openbao/mount-form.d.ts.map +1 -0
  95. package/dist/openbao/mount-wire.d.ts +24 -0
  96. package/dist/openbao/mount-wire.d.ts.map +1 -0
  97. package/dist/openbao/mount.d.ts +20 -0
  98. package/dist/openbao/mount.d.ts.map +1 -0
  99. package/dist/openbao/pki-role-form.d.ts +118 -0
  100. package/dist/openbao/pki-role-form.d.ts.map +1 -0
  101. package/dist/openbao/pki-role.d.ts +50 -0
  102. package/dist/openbao/pki-role.d.ts.map +1 -0
  103. package/dist/openbao/policy-assembly.d.ts +13 -0
  104. package/dist/openbao/policy-assembly.d.ts.map +1 -0
  105. package/dist/openbao/policy-wire.d.ts +41 -0
  106. package/dist/openbao/policy-wire.d.ts.map +1 -0
  107. package/dist/openbao/policy.d.ts +83 -0
  108. package/dist/openbao/policy.d.ts.map +1 -0
  109. package/dist/openbao/proxmox-role-form.d.ts +92 -0
  110. package/dist/openbao/proxmox-role-form.d.ts.map +1 -0
  111. package/dist/openbao/proxmox-role.d.ts +47 -0
  112. package/dist/openbao/proxmox-role.d.ts.map +1 -0
  113. package/dist/openbao/ssh-role-form.d.ts +99 -0
  114. package/dist/openbao/ssh-role-form.d.ts.map +1 -0
  115. package/dist/openbao/ssh-role.d.ts +43 -0
  116. package/dist/openbao/ssh-role.d.ts.map +1 -0
  117. package/dist/proxmox/acl.d.ts +72 -0
  118. package/dist/proxmox/acl.d.ts.map +1 -0
  119. package/dist/proxmox/api-token-form.d.ts +59 -0
  120. package/dist/proxmox/api-token-form.d.ts.map +1 -0
  121. package/dist/proxmox/api-token.d.ts +159 -0
  122. package/dist/proxmox/api-token.d.ts.map +1 -0
  123. package/dist/proxmox/backup-job.d.ts +112 -0
  124. package/dist/proxmox/backup-job.d.ts.map +1 -0
  125. package/dist/proxmox/ceph-daemon-form.d.ts +70 -0
  126. package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -0
  127. package/dist/proxmox/ceph-daemon.d.ts +162 -0
  128. package/dist/proxmox/ceph-daemon.d.ts.map +1 -0
  129. package/dist/proxmox/ceph-flag.d.ts +131 -0
  130. package/dist/proxmox/ceph-flag.d.ts.map +1 -0
  131. package/dist/proxmox/ceph-fs-wire.d.ts +106 -0
  132. package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -0
  133. package/dist/proxmox/ceph-fs.d.ts +124 -0
  134. package/dist/proxmox/ceph-fs.d.ts.map +1 -0
  135. package/dist/proxmox/ceph-osd-tree.d.ts +154 -0
  136. package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -0
  137. package/dist/proxmox/ceph-osd-write.d.ts +36 -0
  138. package/dist/proxmox/ceph-osd-write.d.ts.map +1 -0
  139. package/dist/proxmox/ceph-osd.d.ts +102 -0
  140. package/dist/proxmox/ceph-osd.d.ts.map +1 -0
  141. package/dist/proxmox/ceph-pool-form.d.ts +217 -0
  142. package/dist/proxmox/ceph-pool-form.d.ts.map +1 -0
  143. package/dist/proxmox/ceph-pool-settle.d.ts +39 -0
  144. package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -0
  145. package/dist/proxmox/ceph-pool.d.ts +48 -0
  146. package/dist/proxmox/ceph-pool.d.ts.map +1 -0
  147. package/dist/proxmox/client.d.ts +64 -0
  148. package/dist/proxmox/client.d.ts.map +1 -0
  149. package/dist/proxmox/credentials.d.ts +153 -0
  150. package/dist/proxmox/credentials.d.ts.map +1 -0
  151. package/dist/proxmox/firewall-alias-form.d.ts +98 -0
  152. package/dist/proxmox/firewall-alias-form.d.ts.map +1 -0
  153. package/dist/proxmox/firewall-alias.d.ts +141 -0
  154. package/dist/proxmox/firewall-alias.d.ts.map +1 -0
  155. package/dist/proxmox/generated/pbs.d.ts +388 -0
  156. package/dist/proxmox/generated/pbs.d.ts.map +1 -0
  157. package/dist/proxmox/generated/pve.d.ts +4713 -0
  158. package/dist/proxmox/generated/pve.d.ts.map +1 -0
  159. package/dist/proxmox/group.d.ts +104 -0
  160. package/dist/proxmox/group.d.ts.map +1 -0
  161. package/dist/proxmox/ha-resource.d.ts +104 -0
  162. package/dist/proxmox/ha-resource.d.ts.map +1 -0
  163. package/dist/proxmox/ha-rule-form.d.ts +103 -0
  164. package/dist/proxmox/ha-rule-form.d.ts.map +1 -0
  165. package/dist/proxmox/ha-rule.d.ts +156 -0
  166. package/dist/proxmox/ha-rule.d.ts.map +1 -0
  167. package/dist/proxmox/index.d.ts +43 -0
  168. package/dist/proxmox/index.d.ts.map +1 -0
  169. package/dist/proxmox/index.js +2322 -0
  170. package/dist/proxmox/index.js.map +70 -0
  171. package/dist/proxmox/lease-cache.d.ts +98 -0
  172. package/dist/proxmox/lease-cache.d.ts.map +1 -0
  173. package/dist/proxmox/lxc.d.ts +59 -0
  174. package/dist/proxmox/lxc.d.ts.map +1 -0
  175. package/dist/proxmox/members.d.ts +56 -0
  176. package/dist/proxmox/members.d.ts.map +1 -0
  177. package/dist/proxmox/metric-server-form.d.ts +53 -0
  178. package/dist/proxmox/metric-server-form.d.ts.map +1 -0
  179. package/dist/proxmox/metric-server-otel.d.ts +49 -0
  180. package/dist/proxmox/metric-server-otel.d.ts.map +1 -0
  181. package/dist/proxmox/metric-server.d.ts +123 -0
  182. package/dist/proxmox/metric-server.d.ts.map +1 -0
  183. package/dist/proxmox/network-apply-read.d.ts +93 -0
  184. package/dist/proxmox/network-apply-read.d.ts.map +1 -0
  185. package/dist/proxmox/network-apply.d.ts +102 -0
  186. package/dist/proxmox/network-apply.d.ts.map +1 -0
  187. package/dist/proxmox/node-network-form.d.ts +128 -0
  188. package/dist/proxmox/node-network-form.d.ts.map +1 -0
  189. package/dist/proxmox/node-network.d.ts +173 -0
  190. package/dist/proxmox/node-network.d.ts.map +1 -0
  191. package/dist/proxmox/notification-target-form.d.ts +44 -0
  192. package/dist/proxmox/notification-target-form.d.ts.map +1 -0
  193. package/dist/proxmox/notification-target.d.ts +146 -0
  194. package/dist/proxmox/notification-target.d.ts.map +1 -0
  195. package/dist/proxmox/pbs-datastore-form.d.ts +147 -0
  196. package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -0
  197. package/dist/proxmox/pbs-datastore-guard.d.ts +55 -0
  198. package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -0
  199. package/dist/proxmox/pbs-datastore.d.ts +140 -0
  200. package/dist/proxmox/pbs-datastore.d.ts.map +1 -0
  201. package/dist/proxmox/pbs-prune-job-form.d.ts +97 -0
  202. package/dist/proxmox/pbs-prune-job-form.d.ts.map +1 -0
  203. package/dist/proxmox/pbs-prune-job.d.ts +168 -0
  204. package/dist/proxmox/pbs-prune-job.d.ts.map +1 -0
  205. package/dist/proxmox/pbs-sync-job-form.d.ts +83 -0
  206. package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -0
  207. package/dist/proxmox/pbs-sync-job.d.ts +246 -0
  208. package/dist/proxmox/pbs-sync-job.d.ts.map +1 -0
  209. package/dist/proxmox/pbs-verify-job-form.d.ts +114 -0
  210. package/dist/proxmox/pbs-verify-job-form.d.ts.map +1 -0
  211. package/dist/proxmox/pbs-verify-job.d.ts +145 -0
  212. package/dist/proxmox/pbs-verify-job.d.ts.map +1 -0
  213. package/dist/proxmox/pool.d.ts +42 -0
  214. package/dist/proxmox/pool.d.ts.map +1 -0
  215. package/dist/proxmox/qemu.d.ts +46 -0
  216. package/dist/proxmox/qemu.d.ts.map +1 -0
  217. package/dist/proxmox/replication-job-form.d.ts +71 -0
  218. package/dist/proxmox/replication-job-form.d.ts.map +1 -0
  219. package/dist/proxmox/replication-job.d.ts +162 -0
  220. package/dist/proxmox/replication-job.d.ts.map +1 -0
  221. package/dist/proxmox/resource.d.ts +157 -0
  222. package/dist/proxmox/resource.d.ts.map +1 -0
  223. package/dist/proxmox/role.d.ts +97 -0
  224. package/dist/proxmox/role.d.ts.map +1 -0
  225. package/dist/proxmox/sdn-apply-read.d.ts +80 -0
  226. package/dist/proxmox/sdn-apply-read.d.ts.map +1 -0
  227. package/dist/proxmox/sdn-apply.d.ts +63 -0
  228. package/dist/proxmox/sdn-apply.d.ts.map +1 -0
  229. package/dist/proxmox/sdn-subnet-form.d.ts +111 -0
  230. package/dist/proxmox/sdn-subnet-form.d.ts.map +1 -0
  231. package/dist/proxmox/sdn-subnet.d.ts +161 -0
  232. package/dist/proxmox/sdn-subnet.d.ts.map +1 -0
  233. package/dist/proxmox/sdn-vnet.d.ts +113 -0
  234. package/dist/proxmox/sdn-vnet.d.ts.map +1 -0
  235. package/dist/proxmox/sdn-zone.d.ts +126 -0
  236. package/dist/proxmox/sdn-zone.d.ts.map +1 -0
  237. package/dist/proxmox/storage.d.ts +120 -0
  238. package/dist/proxmox/storage.d.ts.map +1 -0
  239. package/dist/proxmox/user.d.ts +107 -0
  240. package/dist/proxmox/user.d.ts.map +1 -0
  241. package/dist/proxmox/values.d.ts +134 -0
  242. package/dist/proxmox/values.d.ts.map +1 -0
  243. package/dist/proxmox/zfs-pool-write.d.ts +76 -0
  244. package/dist/proxmox/zfs-pool-write.d.ts.map +1 -0
  245. package/dist/proxmox/zfs-pool.d.ts +146 -0
  246. package/dist/proxmox/zfs-pool.d.ts.map +1 -0
  247. package/dist/talos/credentials.d.ts +30 -0
  248. package/dist/talos/credentials.d.ts.map +1 -0
  249. package/dist/talos/index.d.ts +15 -0
  250. package/dist/talos/index.d.ts.map +1 -0
  251. package/dist/talos/index.js +392 -0
  252. package/dist/talos/index.js.map +16 -0
  253. package/dist/talos/kubeconfig.d.ts +53 -0
  254. package/dist/talos/kubeconfig.d.ts.map +1 -0
  255. package/dist/talos/resource.d.ts +32 -0
  256. package/dist/talos/resource.d.ts.map +1 -0
  257. package/dist/talos/talos-bootstrap.d.ts +27 -0
  258. package/dist/talos/talos-bootstrap.d.ts.map +1 -0
  259. package/dist/talos/talos-cluster-health.d.ts +36 -0
  260. package/dist/talos/talos-cluster-health.d.ts.map +1 -0
  261. package/dist/talos/talos-machine-config.d.ts +41 -0
  262. package/dist/talos/talos-machine-config.d.ts.map +1 -0
  263. package/dist/talos/talosctl.d.ts +29 -0
  264. package/dist/talos/talosctl.d.ts.map +1 -0
  265. package/dist/talos/values.d.ts +20 -0
  266. package/dist/talos/values.d.ts.map +1 -0
  267. package/package.json +57 -0
  268. package/src/cloudflare/client.ts +76 -0
  269. package/src/cloudflare/index.ts +10 -0
  270. package/src/cloudflare/lock-rules.ts +60 -0
  271. package/src/cloudflare/providers.ts +23 -0
  272. package/src/cloudflare/r2-bucket-lock.ts +220 -0
  273. package/src/forgejo/branch-protection-form.ts +29 -0
  274. package/src/forgejo/branch-protection.ts +101 -0
  275. package/src/forgejo/client.ts +136 -0
  276. package/src/forgejo/index.ts +17 -0
  277. package/src/forgejo/org-actions-secrets.ts +99 -0
  278. package/src/forgejo/org-label.ts +100 -0
  279. package/src/forgejo/org-team.ts +105 -0
  280. package/src/forgejo/repo-webhook-form.ts +47 -0
  281. package/src/forgejo/repo-webhook.ts +118 -0
  282. package/src/forgejo/repository.ts +126 -0
  283. package/src/forgejo/resource.ts +171 -0
  284. package/src/forgejo/team-member.ts +97 -0
  285. package/src/forgejo/values.test.ts +48 -0
  286. package/src/forgejo/values.ts +61 -0
  287. package/src/index.ts +18 -0
  288. package/src/openbao/auth-role-form.ts +112 -0
  289. package/src/openbao/auth-role.ts +101 -0
  290. package/src/openbao/bao-address.test.ts +89 -0
  291. package/src/openbao/bao-address.ts +97 -0
  292. package/src/openbao/bao-http-gate.test.ts +88 -0
  293. package/src/openbao/bao-http-unix.test.ts +70 -0
  294. package/src/openbao/bao-http.test.ts +161 -0
  295. package/src/openbao/bao-http.ts +185 -0
  296. package/src/openbao/bao-status.test.ts +114 -0
  297. package/src/openbao/bao-status.ts +137 -0
  298. package/src/openbao/cloudflare-group-scope.test.ts +103 -0
  299. package/src/openbao/cloudflare-group-scope.ts +83 -0
  300. package/src/openbao/cloudflare-parity-catalog.ts +104 -0
  301. package/src/openbao/cloudflare-parity-snapshot.ts +84 -0
  302. package/src/openbao/cloudflare-parity.ts +152 -0
  303. package/src/openbao/cloudflare-permission-groups-solve.test.ts +171 -0
  304. package/src/openbao/cloudflare-permission-groups-solve.ts +197 -0
  305. package/src/openbao/cloudflare-permission-groups.test.ts +369 -0
  306. package/src/openbao/cloudflare-permission-groups.ts +299 -0
  307. package/src/openbao/cloudflare-policy.ts +180 -0
  308. package/src/openbao/cloudflare-role-form.test.ts +177 -0
  309. package/src/openbao/cloudflare-role-form.ts +183 -0
  310. package/src/openbao/cloudflare-role-wire.test.ts +132 -0
  311. package/src/openbao/cloudflare-role-wire.ts +41 -0
  312. package/src/openbao/cloudflare-role.ts +189 -0
  313. package/src/openbao/cloudflare-roles-config.ts +230 -0
  314. package/src/openbao/cloudflare-roles-expand-bucket.test.ts +134 -0
  315. package/src/openbao/cloudflare-roles-expand.ts +231 -0
  316. package/src/openbao/digest.ts +26 -0
  317. package/src/openbao/fake-bao.ts +84 -0
  318. package/src/openbao/forgejo-bootstrap-parse.test.ts +76 -0
  319. package/src/openbao/forgejo-bootstrap-parse.ts +90 -0
  320. package/src/openbao/forgejo-bootstrap.ts +181 -0
  321. package/src/openbao/index.ts +17 -0
  322. package/src/openbao/mount-form.ts +147 -0
  323. package/src/openbao/mount-wire.test.ts +108 -0
  324. package/src/openbao/mount-wire.ts +79 -0
  325. package/src/openbao/mount.ts +132 -0
  326. package/src/openbao/pki-role-form.ts +248 -0
  327. package/src/openbao/pki-role.ts +202 -0
  328. package/src/openbao/policy-assembly.test.ts +28 -0
  329. package/src/openbao/policy-assembly.ts +13 -0
  330. package/src/openbao/policy-wire.test.ts +90 -0
  331. package/src/openbao/policy-wire.ts +53 -0
  332. package/src/openbao/policy.ts +212 -0
  333. package/src/openbao/proxmox-role-form.ts +174 -0
  334. package/src/openbao/proxmox-role.ts +214 -0
  335. package/src/openbao/ssh-role-form.ts +248 -0
  336. package/src/openbao/ssh-role.ts +240 -0
  337. package/src/proxmox/acl.ts +267 -0
  338. package/src/proxmox/api-token-form.ts +122 -0
  339. package/src/proxmox/api-token.ts +222 -0
  340. package/src/proxmox/backup-job.ts +213 -0
  341. package/src/proxmox/ceph-daemon-form.ts +84 -0
  342. package/src/proxmox/ceph-daemon.ts +243 -0
  343. package/src/proxmox/ceph-flag.ts +249 -0
  344. package/src/proxmox/ceph-fs-wire.ts +246 -0
  345. package/src/proxmox/ceph-fs.ts +249 -0
  346. package/src/proxmox/ceph-osd-tree.ts +193 -0
  347. package/src/proxmox/ceph-osd-write.ts +76 -0
  348. package/src/proxmox/ceph-osd.ts +244 -0
  349. package/src/proxmox/ceph-pool-form.ts +249 -0
  350. package/src/proxmox/ceph-pool-settle.ts +70 -0
  351. package/src/proxmox/ceph-pool.ts +212 -0
  352. package/src/proxmox/client.test.ts +185 -0
  353. package/src/proxmox/client.ts +240 -0
  354. package/src/proxmox/credentials.test.ts +144 -0
  355. package/src/proxmox/credentials.ts +254 -0
  356. package/src/proxmox/firewall-alias-form.ts +112 -0
  357. package/src/proxmox/firewall-alias.ts +208 -0
  358. package/src/proxmox/generated/pbs.ts +461 -0
  359. package/src/proxmox/generated/pve.ts +7344 -0
  360. package/src/proxmox/group.ts +199 -0
  361. package/src/proxmox/ha-resource.ts +169 -0
  362. package/src/proxmox/ha-rule-form.ts +185 -0
  363. package/src/proxmox/ha-rule.ts +240 -0
  364. package/src/proxmox/index.ts +42 -0
  365. package/src/proxmox/lease-cache.test.ts +163 -0
  366. package/src/proxmox/lease-cache.ts +131 -0
  367. package/src/proxmox/lxc.ts +117 -0
  368. package/src/proxmox/members.test.ts +54 -0
  369. package/src/proxmox/members.ts +174 -0
  370. package/src/proxmox/metric-server-form.ts +128 -0
  371. package/src/proxmox/metric-server-otel.ts +99 -0
  372. package/src/proxmox/metric-server.ts +214 -0
  373. package/src/proxmox/network-apply-read.ts +228 -0
  374. package/src/proxmox/network-apply.ts +244 -0
  375. package/src/proxmox/node-network-form.ts +213 -0
  376. package/src/proxmox/node-network.ts +243 -0
  377. package/src/proxmox/notification-target-form.ts +104 -0
  378. package/src/proxmox/notification-target.ts +222 -0
  379. package/src/proxmox/pbs-datastore-form.ts +248 -0
  380. package/src/proxmox/pbs-datastore-guard.ts +105 -0
  381. package/src/proxmox/pbs-datastore.ts +271 -0
  382. package/src/proxmox/pbs-prune-job-form.ts +208 -0
  383. package/src/proxmox/pbs-prune-job.ts +227 -0
  384. package/src/proxmox/pbs-sync-job-form.ts +234 -0
  385. package/src/proxmox/pbs-sync-job.ts +258 -0
  386. package/src/proxmox/pbs-verify-job-form.ts +131 -0
  387. package/src/proxmox/pbs-verify-job.ts +227 -0
  388. package/src/proxmox/pool.test.ts +42 -0
  389. package/src/proxmox/pool.ts +80 -0
  390. package/src/proxmox/qemu.ts +89 -0
  391. package/src/proxmox/replication-job-form.ts +89 -0
  392. package/src/proxmox/replication-job.ts +248 -0
  393. package/src/proxmox/resource.ts +257 -0
  394. package/src/proxmox/role.ts +161 -0
  395. package/src/proxmox/sdn-apply-read.test.ts +152 -0
  396. package/src/proxmox/sdn-apply-read.ts +189 -0
  397. package/src/proxmox/sdn-apply.ts +154 -0
  398. package/src/proxmox/sdn-subnet-form.ts +156 -0
  399. package/src/proxmox/sdn-subnet.ts +238 -0
  400. package/src/proxmox/sdn-vnet.ts +228 -0
  401. package/src/proxmox/sdn-zone.ts +246 -0
  402. package/src/proxmox/storage.ts +245 -0
  403. package/src/proxmox/user.ts +214 -0
  404. package/src/proxmox/values.test.ts +193 -0
  405. package/src/proxmox/values.ts +203 -0
  406. package/src/proxmox/zfs-pool-write.ts +130 -0
  407. package/src/proxmox/zfs-pool.ts +253 -0
  408. package/src/talos/credentials.ts +136 -0
  409. package/src/talos/index.ts +14 -0
  410. package/src/talos/kubeconfig.ts +170 -0
  411. package/src/talos/resource.ts +31 -0
  412. package/src/talos/talos-bootstrap.ts +109 -0
  413. package/src/talos/talos-cluster-health.ts +119 -0
  414. package/src/talos/talos-machine-config.ts +146 -0
  415. package/src/talos/talosctl.ts +94 -0
  416. package/src/talos/values.test.ts +80 -0
  417. package/src/talos/values.ts +70 -0
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Wire coercions shared across Forgejo resources.
3
+ *
4
+ * ★ GITEA/ FORGEJO JSON USES snake_case ON THE WIRE and camelCase in props — each resource
5
+ * translates at the form boundary; these helpers only normalise values for comparison.
6
+ */
7
+ export declare const text: (value: unknown, fallback?: string) => string;
8
+ /** Forgejo returns label colours without `#`; declarations may include it. */
9
+ export declare const color: (value: unknown) => string;
10
+ export declare const bool: (value: unknown, fallback?: boolean) => boolean;
11
+ export declare const int: (value: unknown, fallback?: number) => number;
12
+ /** Wire arrays compared in sorted order — Gitea reshuffles nothing but callers should not depend on order. */
13
+ export declare const stringArray: (value: unknown) => string[];
14
+ export declare const stringRecord: (value: unknown) => Record<string, string>;
15
+ export declare const recordEqual: (left: Record<string, string>, right: Record<string, string>) => boolean;
16
+ /** Hook `config` without `secret` — the value must never land in Alchemy attributes. */
17
+ export declare const hookConfigPublic: (value: unknown) => {
18
+ [x: string]: string;
19
+ };
20
+ /** Env key for a write-only org actions secret — value read at PUT time only, never stored. */
21
+ export declare const orgSecretEnvKey: (org: string, name: string) => string;
22
+ /** Env key for a write-only repo webhook HMAC secret — omitted from attributes and props. */
23
+ export declare const hookSecretEnvKey: (owner: string, repo: string, name: string) => string;
24
+ //# sourceMappingURL=values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../src/forgejo/values.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,eAAO,MAAM,IAAI,UAAW,OAAO,8BACW,CAAC;AAE/C,8EAA8E;AAC9E,eAAO,MAAM,KAAK,UAAW,OAAO,WAAgD,CAAC;AAErF,eAAO,MAAM,IAAI,UAAW,OAAO,gCACgE,CAAC;AAEpG,eAAO,MAAM,GAAG,UAAW,OAAO,8BAOjC,CAAC;AAEF,8GAA8G;AAC9G,eAAO,MAAM,WAAW,UAAW,OAAO,aAGlC,CAAC;AAET,eAAO,MAAM,YAAY,UAAW,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAOlE,CAAC;AAEF,eAAO,MAAM,WAAW,SAAU,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,YAKtF,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,gBAAgB,UAAW,OAAO;;CAI9C,CAAC;AAEF,+FAA+F;AAC/F,eAAO,MAAM,eAAe,QAAS,MAAM,QAAQ,MAAM,WACyB,CAAC;AAEnF,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,UAAW,MAAM,QAAQ,MAAM,QAAQ,MAAM,WACmB,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @homeflare/alchemy — custom Alchemy providers for systems the vendor has none for.
3
+ *
4
+ * ⛔ IMPORT A SUBPATH, NOT THIS FILE. Each system carries its own peers and its own
5
+ * client; a root barrel would pull Proxmox into a stack that only wanted Forgejo.
6
+ *
7
+ * import { R2BucketLock } from '@homeflare/alchemy/cloudflare';
8
+ * import { BranchProtection } from '@homeflare/alchemy/forgejo';
9
+ * import { BaoMount } from '@homeflare/alchemy/openbao';
10
+ * import { TalosCluster } from '@homeflare/alchemy/talos';
11
+ * import { ProxmoxAcl } from '@homeflare/alchemy/proxmox';
12
+ *
13
+ * ★ WHY THESE EXIST AT ALL. When Alchemy has no resource for something, the alternative
14
+ * is a runbook step a human runs once — and a plan can never show a missing runbook
15
+ * step. A custom provider makes the drift visible in `plan` like everything else.
16
+ */
17
+ export {};
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+
2
+ //# debugId=1B152D275122871864756E2164756E21
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,9 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [
5
+ ],
6
+ "mappings": "",
7
+ "debugId": "1B152D275122871864756E2164756E21",
8
+ "names": []
9
+ }
@@ -0,0 +1,38 @@
1
+ export interface BaoAuthRoleProps {
2
+ /** AppRole name, as `auth/approle/role/{name}` takes it. */
3
+ name: string;
4
+ /** Policies bound to tokens minted through this role. Names only — never a secret. */
5
+ tokenPolicies: readonly string[];
6
+ /** Token TTL, e.g. `15m`. */
7
+ tokenTtl: string;
8
+ /** Token max TTL, e.g. `1h`. */
9
+ tokenMaxTtl: string;
10
+ /** Secret ID TTL — `0` means non-expiring, as reconcile.sh and apply-consumers.py set. */
11
+ secretIdTtl: string;
12
+ /** Whether login requires a secret_id. Defaults to true when omitted on write. */
13
+ bindSecretId?: boolean;
14
+ /** `0` means unlimited uses — the shape reconcile.sh writes for headless roles. */
15
+ secretIdNumUses?: number;
16
+ }
17
+ export interface BaoAuthRoleAttributes {
18
+ name: string;
19
+ tokenPolicies: readonly string[];
20
+ tokenTtl: string;
21
+ tokenMaxTtl: string;
22
+ secretIdTtl: string;
23
+ bindSecretId: boolean;
24
+ secretIdNumUses: number;
25
+ /** SHA-256 of the managed fields — safe to persist; see policy.ts. */
26
+ digest: string;
27
+ }
28
+ export declare const attributesOf: (props: BaoAuthRoleProps, live: Record<string, unknown>) => BaoAuthRoleAttributes;
29
+ export declare const readPath: (name: string) => string;
30
+ /**
31
+ * The body for `PUT auth/approle/role/<name>`.
32
+ *
33
+ * ★ ALL STRINGS, EXACTLY THE `k=v` PAIRS `bao write` USED TO SEND — see `baoWrite` in bao-http.ts.
34
+ */
35
+ export declare const writeBody: (props: BaoAuthRoleProps) => Record<string, string>;
36
+ /** True when live already matches the declaration on every managed field. */
37
+ export declare const matches: (attributes: BaoAuthRoleAttributes, props: BaoAuthRoleProps) => boolean;
38
+ //# sourceMappingURL=auth-role-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role-form.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,eAAO,MAAM,YAAY,UAChB,gBAAgB,QACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,qBAWF,CAAC;AAEF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAgC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAYxE,CAAC;AAWF,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,qBAAqB,SAAS,gBAAgB,YAUjF,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * An OpenBao AppRole auth role — token_policies, TTLs and bind flags. METADATA ONLY.
3
+ *
4
+ * ⛔ NO SECRET IN PROPS OR ATTRIBUTES — see policy.ts for why. `role_id` and `secret_id`
5
+ * are credential issuance, not configuration, and are OUT OF SCOPE. This declares the
6
+ * role shape an approle login inherits — policy names and TTL strings only.
7
+ *
8
+ * ★ `defaultRemovalPolicy: 'retain'` — deleting a role revokes every token bound to it.
9
+ * Opt in with `.pipe(RemovalPolicy.destroy())`; see resource.ts in house/proxmox.
10
+ */
11
+ import { Resource } from 'alchemy';
12
+ import * as Provider from 'alchemy/Provider';
13
+ import type * as HttpClient from 'effect/unstable/http/HttpClient';
14
+ import { type BaoAuthRoleAttributes, type BaoAuthRoleProps } from './auth-role-form.ts';
15
+ export type { BaoAuthRoleAttributes, BaoAuthRoleProps };
16
+ export interface BaoAuthRole extends Resource<'Bao.AuthRole', BaoAuthRoleProps, BaoAuthRoleAttributes, never, HttpClient.HttpClient> {
17
+ }
18
+ export declare const BaoAuthRole: import("alchemy").ResourceClass<BaoAuthRole>;
19
+ export declare const BaoAuthRoleProvider: () => import("effect/Layer").Layer<Provider.Provider<BaoAuthRole>, never, HttpClient.HttpClient>;
20
+ //# sourceMappingURL=auth-role.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-role.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAKtB,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;AAExD,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AASH,eAAO,MAAM,mBAAmB,kGAsD7B,CAAC"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Where an OpenBao call goes and which headers it carries — resolved from the environment the way
3
+ * the `bao` CLI resolves it, so the provider and `bao kv get` can never disagree about which server
4
+ * or namespace this machine is talking to.
5
+ *
6
+ * ★ READ FROM openbao v2.6.2, NOT RECALLED:
7
+ * · api/client.go:34-66 — BAO_ADDR, BAO_AGENT_ADDR, BAO_NAMESPACE, BAO_TOKEN, and the header
8
+ * names `X-Vault-Namespace` and `X-Vault-Token`.
9
+ * · api/client.go:370 — the default address, `https://127.0.0.1:8200`.
10
+ * · api/client.go:503-507, 617-618, 761-762 — BAO_AGENT_ADDR, when set, WINS over BAO_ADDR.
11
+ * · api/env.go:22-34 — every BAO_* falls back to its VAULT_* twin, but a BAO_* that is present
12
+ * and EMPTY still wins over the VAULT_* one.
13
+ * · api/client.go:776-785 — the token and namespace are applied only when non-empty, and every
14
+ * request carries `X-Vault-Request: true`.
15
+ * · api/client.go:1349-1354 — the request path is joined onto the address's own path.
16
+ *
17
+ * ⛔ WHAT THE CLI DOES AND THIS DELIBERATELY DOES NOT: with BAO_TOKEN unset, `bao` falls back to its
18
+ * token helper (a token file in $HOME). This never reads that file. No BAO_TOKEN means AGENT
19
+ * MODE — see `headersFor`.
20
+ * ⚠️ NOT CARRIED OVER: BAO_CACERT / BAO_SKIP_VERIFY / client certificates, SRV lookup, and the CLI's
21
+ * retries on 5xx. An https address needs a certificate the runtime already trusts; a TLS failure
22
+ * fails the call loudly rather than being skipped.
23
+ */
24
+ export type BaoEnvironment = Readonly<Record<string, string | undefined>>;
25
+ export type BaoAddress = {
26
+ /** Scheme, host and any path prefix, no trailing slash. `http://localhost` for a socket. */
27
+ readonly base: string;
28
+ /** The socket path when the address is `unix:///path`, otherwise undefined. */
29
+ readonly socket: string | undefined;
30
+ /** The namespace to send, when there is one. */
31
+ readonly namespace: string | undefined;
32
+ };
33
+ export declare const DEFAULT_ADDRESS = "https://127.0.0.1:8200";
34
+ /** api/env.go ReadBaoVariable: present (even empty) BAO_* wins, else the VAULT_* twin. */
35
+ export declare const readBaoVariable: (env: BaoEnvironment, name: `BAO_${string}`) => string;
36
+ /**
37
+ * The address and namespace for this environment. Carries no token, so it is safe to log.
38
+ *
39
+ * ★ UNIX SOCKETS, BECAUSE AN AGENT LISTENER CAN BE ONE. OpenBao Agent's `listener "unix"` serves
40
+ * the same API on a socket (website/content/docs/agent-and-proxy/agent/index.mdx:382-384), and the
41
+ * agent advertises such a listener as `unix://<path>` (command/agent.go:565-566). The CLI handles
42
+ * that address by dialling the socket and speaking plain HTTP to host `localhost`
43
+ * (api/client.go:654-667); `base` and `socket` are exactly those two halves.
44
+ */
45
+ export declare const resolveAddress: (env: BaoEnvironment) => BaoAddress;
46
+ /**
47
+ * The headers one call sends.
48
+ *
49
+ * ★ AGENT MODE IS "NO TOKEN HEADER AT ALL", AND THAT IS THE AGENT'S OWN CONTRACT. Read in openbao
50
+ * v2.6.2 command/agentproxyshared/cache/handler.go:29-38: the proxy handler takes the request's
51
+ * `X-Vault-Token`, and only when it is EMPTY and an auto-auth sink exists does it substitute the
52
+ * agent's own token. command/agent.go:517-541 creates that sink exactly when
53
+ * `api_proxy { use_auto_auth_token = true }` is set; command/agent/config/config.go:288-292 and
54
+ * 635-637 map the older `cache { use_auto_auth_token }` onto the same setting; and
55
+ * command/proxy.go:495-518 wires OpenBao Proxy identically. The docs say the same in words
56
+ * (website/content/docs/agent-and-proxy/agent/apiproxy.mdx:57-62): a request without a token is
57
+ * forwarded with the auto-auth token, a request that bears one keeps its own, and `"force"`
58
+ * overwrites it.
59
+ * ⛔ SO AN EMPTY `X-Vault-Token: ` WOULD BE WRONG IN BOTH DIRECTIONS: harmless to the agent,
60
+ * which reads empty as absent, but an explicit unauthenticated request to a server. The header
61
+ * is omitted, never blanked.
62
+ * ★ THE NAMESPACE RIDES THROUGH THE AGENT UNTOUCHED. The proxy clones its client and replaces that
63
+ * client's headers with the incoming request's (cache/api_proxy.go:50-54 and :78), so the
64
+ * `X-Vault-Namespace` sent here is the one OpenBao sees.
65
+ * ⚠️ `X-Vault-Request: true` IS NOT DECORATION. An agent listener with `require_request_header`
66
+ * refuses any request without it (command/agent.go:544-548); the CLI always sends it.
67
+ *
68
+ * ⛔ THE RETURN VALUE HOLDS THE TOKEN. Hand it to the request and nowhere else: never to an error,
69
+ * a log line, an attribute, or a span — bao-http.ts adds the header to the redacted names.
70
+ */
71
+ export declare const headersFor: (address: BaoAddress, env: BaoEnvironment) => Record<string, string>;
72
+ //# sourceMappingURL=bao-address.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bao-address.d.ts","sourceRoot":"","sources":["../../src/openbao/bao-address.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAE1E,MAAM,MAAM,UAAU,GAAG;IACvB,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IACpC,gDAAgD;IAChD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,CAAC;AAEF,eAAO,MAAM,eAAe,2BAA2B,CAAC;AAIxD,0FAA0F;AAC1F,eAAO,MAAM,eAAe,QAAS,cAAc,QAAQ,OAAO,MAAM,EAAE,KAAG,MACf,CAAC;AAE/D;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,QAAS,cAAc,KAAG,UAWpD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,UAAU,YAAa,UAAU,OAAO,cAAc,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAM1F,CAAC"}
@@ -0,0 +1,71 @@
1
+ /**
2
+ * One call to OpenBao's HTTP API through Effect's `HttpClient` — the transport every Bao.* family
3
+ * uses since it stopped shelling out to `bao`.
4
+ *
5
+ * ★ `HttpClient`, THE SAME PATTERN house/proxmox and house/forgejo USE AGAINST THEIR APIs, decided
6
+ * 2026-09-14. It buys a typed error channel, interruption when a plan is cancelled, the runtime's
7
+ * tracing, and a client a test can point at a fake server — and it needs no Alchemy session, so
8
+ * every call here works from all four operations. (`CommandExecutor.run` was tried on Bao.Policy
9
+ * first and blocked read/diff: Alchemy passes a session to reconcile/delete but NOT to read/diff,
10
+ * Provider.ts:258-289. The ChildProcessSpawner that fixed that is gone with the CLI.)
11
+ * ⚠️ THE STACK MUST PROVIDE `FetchHttpClient.layer`. alchemy.run.ts does, the way
12
+ * house/proxmox/alchemy.run.ts does.
13
+ */
14
+ import * as Context from 'effect/Context';
15
+ import * as Effect from 'effect/Effect';
16
+ import * as Semaphore from 'effect/Semaphore';
17
+ import * as HttpClient from 'effect/unstable/http/HttpClient';
18
+ import { BaoError, type BaoIntent } from './bao-status.ts';
19
+ /**
20
+ * The environment a call resolves BAO_ADDR, BAO_NAMESPACE and BAO_TOKEN from — `process.env`,
21
+ * read at call time, unless a test provides another.
22
+ */
23
+ export declare const BaoEnv: Context.Reference<Readonly<Record<string, string | undefined>>>;
24
+ /**
25
+ * ⛔ AT MOST EIGHT EXCHANGES WITH OPENBAO AT ONCE — ACROSS EVERY Bao.* FAMILY, IN ONE PROCESS.
26
+ *
27
+ * 🔴 MEASURED 2026-09-14 10:41: A DEPLOY OF THIS STACK TOOK OPENBAO's API OFF THE MINI. Alchemy fans
28
+ * resources out with a hard-coded `concurrency: "unbounded"` (alchemy Plan.ts:476, Apply.ts:259,
29
+ * :263, :303 — no option reaches it), and 555 Bao.CloudflareRole resources each read, diff,
30
+ * reconcile and read back: well over a thousand requests at one loopback listener within seconds.
31
+ * OpenBao logged
32
+ * HTTP server (listening on 127.0.0.1:8200) exited with error: set tcp
33
+ * 127.0.0.1:8200->127.0.0.1:57974: setsockopt: invalid argument
34
+ * and KEPT RUNNING with 8200 closed. launchd saw a live process and restarted nothing. Everything
35
+ * on the mini that talks to 127.0.0.1:8200 was cut off, starting with the agent's templates, until
36
+ * a manual restart.
37
+ * ★ THE LISTENER DYING IS A GO-ON-DARWIN FAILURE, NOT OURS TO FIX. Traefik lost its entrypoints the
38
+ * same way on darwin/arm64 (traefik issue 8841). The BURST is ours. The adoption deploy an hour
39
+ * earlier made the same calls and survived, so a cap narrows a race; it does not prove it closed.
40
+ * ★ A SEMAPHORE, NOT `HttpClient.withRateLimiter`. That limits requests per window and needs a
41
+ * RateLimiter service. The failure is simultaneous connections, and only a cap on in-flight
42
+ * exchanges bounds those whatever each one's latency.
43
+ * ⚠️ A Context.Reference SO A TEST CAN PROVIDE ITS OWN GATE. The default is built ONCE and cached on
44
+ * the reference (effect Context.ts:1582-1588), so every call in the process shares one gate.
45
+ * ⚠️ EIGHT IS A CHOICE, NOT A MEASUREMENT OF WHAT THE LISTENER TOLERATES. Nothing here can find that
46
+ * number without re-running the failure against production.
47
+ */
48
+ export declare const MAX_IN_FLIGHT = 8;
49
+ export declare const BaoGate: Context.Reference<Semaphore.Semaphore>;
50
+ /** One exchange, classified. The body is the parsed JSON object, or undefined for absent / 204. */
51
+ export declare const baoCall: (intent: BaoIntent, method: 'DELETE' | 'GET' | 'POST' | 'PUT', path: string, body?: Readonly<Record<string, unknown>>) => Effect.Effect<Record<string, unknown> | undefined, BaoError, HttpClient.HttpClient>;
52
+ /**
53
+ * The `data` of a read, or undefined when OpenBao says the object is ABSENT (404).
54
+ *
55
+ * ★ `bao read -format=json` printed this same response body; the old helper took `data` when it was
56
+ * an object and the whole body otherwise, and so does this.
57
+ */
58
+ export declare const baoRead: (path: string) => Effect.Effect<Record<string, unknown> | undefined, BaoError, HttpClient.HttpClient>;
59
+ /**
60
+ * A write whose SUCCESS MATTERS. Any non-2xx fails, 404 included.
61
+ *
62
+ * ★ `PUT` IS WHAT `bao write` SENT (openbao v2.6.2 api/logical.go Write); `sys/mounts` enable and
63
+ * tune are `POST` in the same client (api/sys_mounts.go:57, :201).
64
+ * ⚠️ ROLE BODIES STAY ALL-STRING ON PURPOSE. `bao write k=v` put every value in the JSON body as a
65
+ * string — MEASURED with `-output-curl-string`, see ssh-role-form.ts — and the form files still
66
+ * build exactly that body, so the server parses the same request it was always sent.
67
+ */
68
+ export declare const baoWrite: (method: 'POST' | 'PUT', path: string, body: Readonly<Record<string, unknown>>) => Effect.Effect<void, BaoError, HttpClient.HttpClient>;
69
+ /** A delete: idempotent — already gone (404) is success — and refused (403, 5xx) is a failure. */
70
+ export declare const baoDelete: (path: string) => Effect.Effect<void, BaoError, HttpClient.HttpClient>;
71
+ //# sourceMappingURL=bao-http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bao-http.d.ts","sourceRoot":"","sources":["../../src/openbao/bao-http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAC;AAG9C,OAAO,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAG9D,OAAO,EAAE,QAAQ,EAAE,KAAK,SAAS,EAAU,MAAM,iBAAiB,CAAC;AAEnE;;;GAGG;AACH,eAAO,MAAM,MAAM,iEAEjB,CAAC;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,aAAa,IAAI,CAAC;AAE/B,eAAO,MAAM,OAAO,wCAElB,CAAC;AAoDH,mGAAmG;AACnG,eAAO,MAAM,OAAO,WACV,SAAS,UACT,QAAQ,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,QACnC,MAAM,SACL,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,wFAkCtC,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,OAAO,SAAU,MAAM,wFAOhC,CAAC;AAEL;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,WACX,MAAM,GAAG,KAAK,QAChB,MAAM,QACN,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,yDACe,CAAC;AAEzD,kGAAkG;AAClG,eAAO,MAAM,SAAS,SAAU,MAAM,yDAAqD,CAAC"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * How a finished OpenBao HTTP exchange reads: a body, a genuinely absent object, or an error.
3
+ *
4
+ * ★ STATUS CODES, NEVER WORDS. The `bao` CLI this replaced reported every outcome as stderr text,
5
+ * and deciding by that text broke twice: every failure once read as absent (a bad token planned 33
6
+ * updates), and then `bao policy read` turned out to say "absent" in different words from
7
+ * `bao read`, so a new policy could not be created (b3c18a33e). The API itself answers 404 for
8
+ * absent, 403 for denied and 503 for sealed — openbao v2.6.2 sdk/logical/response_util.go:
9
+ * RespondErrorCommon (a read that finds nothing is 404; ErrPermissionDenied is 403) and
10
+ * AdjustErrorStatusCode (ErrSealed is 503).
11
+ */
12
+ export type BaoIntent = 'read' | 'write' | 'delete';
13
+ /**
14
+ * A failed call, naming the method, the path and OpenBao's own `errors` array.
15
+ *
16
+ * ⛔ IT NEVER CARRIES THE TOKEN, AND IT NEVER CARRIES A RESPONSE BODY IT DID NOT PARSE. It is built
17
+ * from the method, the API path and OpenBao's error strings only — no request, no headers, no
18
+ * `cause` — because an Effect `HttpClientRequest` inspects its headers through a redaction list
19
+ * that does not name `x-vault-token`. A 2xx body can hold a credential, and a foreign error page
20
+ * can echo the request back, so a body that is not OpenBao's is described, never quoted.
21
+ */
22
+ export declare class BaoError extends Error {
23
+ readonly status: number;
24
+ readonly method: string;
25
+ readonly path: string;
26
+ readonly errors: readonly string[];
27
+ constructor(status: number, method: string, path: string, errors: readonly string[]);
28
+ }
29
+ export type Settled = {
30
+ readonly body: Record<string, unknown> | undefined;
31
+ } | {
32
+ readonly error: BaoError;
33
+ };
34
+ /**
35
+ * Classify one exchange.
36
+ *
37
+ * ★ PURE AND EXPORTED SO THE ONE DECISION EVERY READ AND DELETE HERE RESTS ON CAN BE TESTED WITHOUT
38
+ * A VAULT. It has been wrong in both directions already — every failure once read as absent, and
39
+ * one command's absence wording was assumed for another's — see bao-status.test.ts.
40
+ *
41
+ * ⛔ 404 IS ABSENCE ONLY ON A READ, AND ONLY WHEN OPENBAO SAID IT. MEASURED 2026-09-13 with the CLI,
42
+ * and the discriminator is real but NARROW: a genuinely absent object, read by a token that IS
43
+ * allowed to read that path, is a 404. ⛔ AND THE SAME READ UNDER A NARROWER TOKEN IS A DENIAL:
44
+ * under the agent lane the identical read answered `Code: 403 … permission denied`, because the
45
+ * policy grants named paths and an unnamed one is refused rather than reported missing. So
46
+ * "absent" is only knowable to a caller that could have read it — which is exactly why 403 is an
47
+ * ERROR rather than a shrug. A resource that cannot tell absence from denial plans CREATE for
48
+ * something that exists, which is the failure this estate has now found four separate times.
49
+ * ⚠️ A 404 WHOSE BODY IS NOT `{"errors": […strings]}` IS NOT OPENBAO. A BAO_ADDR pointing at the
50
+ * wrong server — a reverse proxy's HTML 404, a gateway's JSON one — must not read as "nothing".
51
+ *
52
+ * 🔴 THE SHELL HELPER USED TO RETURN `''` FOR EVERY NON-ZERO EXIT, AND THAT IS A LIE WITH
53
+ * CONSEQUENCES. An expired BAO_TOKEN, a sealed server or a missing grant all came back as "the
54
+ * mount is not there", so `diff` answered `update`, `reconcile` ran `secrets enable`, and the plan
55
+ * reported a CREATE on infrastructure that exists. The failure the operator sees is about the
56
+ * wrong thing entirely, at the worst possible moment — a sealed vault. ⚠️ SO 403, 5xx AND ANYTHING
57
+ * ELSE THAT IS NOT 2xx FAILS. The cost is that a genuinely-absent read under a too-narrow token
58
+ * becomes an error instead of a create, and that is the right way round: "I could not tell" is a
59
+ * true statement, and "it is not there" was not.
60
+ *
61
+ * 🔴 THE DELETES ALL USED THE READ HELPER, WHICH DISCARDED THE EXIT CODE. A refused
62
+ * `secrets disable` was reported as a successful delete: the resource left Alchemy's state while
63
+ * the mount stayed live, so the next plan showed nothing and the object was orphaned in silence.
64
+ * ⚠️ ABSENCE IS STILL NOT AN ERROR FOR A DELETE, because a delete must be idempotent: Alchemy
65
+ * retries, and the second attempt legitimately finds nothing. So a delete's 404 is success and
66
+ * its 403 is not.
67
+ * ★ DISABLING A MOUNT THAT IS ALREADY GONE IS NOT EVEN A 404. A review on 2026-09-14 said it failed
68
+ * with `no matching mount` and would wedge a retried delete. The server says otherwise: it answers
69
+ * success for a missing mount so as not to reveal whether one existed — openbao v2.6.2
70
+ * vault/logical_system.go:1211-1216 — and the CLI printed "Disabled the secrets engine (if it
71
+ * existed)" (command/secrets_disable.go:87).
72
+ */
73
+ export declare const settle: (intent: BaoIntent, method: string, path: string, status: number, text: string) => Settled;
74
+ //# sourceMappingURL=bao-status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bao-status.d.ts","sourceRoot":"","sources":["../../src/openbao/bao-status.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEpD;;;;;;;;GAQG;AACH,qBAAa,QAAS,SAAQ,KAAK;IAE/B,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE;IAJpC,YACW,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,MAAM,EAAE,EAKnC;CACF;AAED,MAAM,MAAM,OAAO,GACf;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC;AA+BjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,eAAO,MAAM,MAAM,WACT,SAAS,UACT,MAAM,QACR,MAAM,UACJ,MAAM,QACR,MAAM,KACX,OAsBF,CAAC"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * The three resources a Cloudflare policy entry can name, and the SCOPE its permission groups are
3
+ * looked up at — one vocabulary, both directions.
4
+ *
5
+ * ★ EXTRACTED FROM cloudflare-roles-expand.ts 2026-09-15, which had reached 270 lines (AGENTS.md
6
+ * rule 5, code ≤250). The split is by CONCERN, not by size: expand.ts turns roles.yaml into
7
+ * roles, this answers "which of the two ids named `Logs Read` does this entry mean".
8
+ */
9
+ /** cfhf.py:172-173. */
10
+ export declare const zoneResource: (zoneId: string) => string;
11
+ export declare const accountResource: (accountId: string) => string;
12
+ /**
13
+ * One R2 bucket, which is a DIFFERENT RESOURCE NAMESPACE from the two above.
14
+ *
15
+ * ⛔ `com.cloudflare.edge.r2.bucket.…`, NOT `com.cloudflare.api.account.…`, and the three parts are
16
+ * joined by UNDERSCORES inside the last segment — `<accountId>_<jurisdiction>_<bucket>`. Getting
17
+ * the separator wrong produces a syntactically fine resource that matches no bucket, and the
18
+ * token mints successfully and is refused on first use, which reads exactly like a missing
19
+ * permission group. Per Cloudflare's R2 API-token documentation, read 2026-09-15.
20
+ * ⚠️ AND IT ONLY BINDS THE `Bucket Item` GROUPS. `Workers R2 Storage Write` / `… Read` are gated at
21
+ * the ACCOUNT scope; the bucket-scoped pair is `Workers R2 Storage Bucket Item Write` / `… Read`.
22
+ * Pairing an account group with this resource is the same silent 403.
23
+ */
24
+ export declare const bucketResource: (accountId: string, jurisdiction: string, bucket: string) => string;
25
+ /**
26
+ * The three scopes this expansion can ask for — the namespaces of the three resources above, which
27
+ * is exactly what the engine reports as a group's `scopes`.
28
+ *
29
+ * ★ MEASURED on `cloudflare-<account>-platform/permission-groups`, 2026-09-15 12:10: 401 groups,
30
+ * every one with exactly ONE scope, over five values — 297 `com.cloudflare.api.account`, 94
31
+ * `com.cloudflare.api.account.zone`, 2 `com.cloudflare.edge.r2.bucket` and four each of
32
+ * `…account.flagship.app` and `com.cloudflare.edge.worker.script`, which no resource here builds.
33
+ * The testing account's list is byte-identical, so the ids are Cloudflare's, not an account's.
34
+ */
35
+ export declare const GROUP_SCOPES: {
36
+ readonly account: 'com.cloudflare.api.account';
37
+ readonly bucket: 'com.cloudflare.edge.r2.bucket';
38
+ readonly zone: 'com.cloudflare.api.account.zone';
39
+ };
40
+ /**
41
+ * The short word roles.yaml writes in `groups_scope:` → the namespace: `zone` becomes
42
+ * `com.cloudflare.api.account.zone`. Undefined for a word that is not one of the three KEYS above.
43
+ *
44
+ * ⛔ THE yaml SAYS THE SHORT WORD AND NEVER THE NAMESPACE. A namespace typed into roles.yaml would
45
+ * be accepted by any check that only asked for a string, and a misspelt one resolves no group at
46
+ * all — an unknown-group failure naming a scope nobody wrote on purpose, instead of the line.
47
+ * ⚠️ `Object.entries(…).find`, NOT an index with a narrowing `as`: codex standard 2 bans the cast,
48
+ * and the lookup has three entries.
49
+ */
50
+ export declare const groupScopeNamed: (word: string) => string | undefined;
51
+ /**
52
+ * The scope the groups of one policy entry are gated at: its resource namespace, the instance id
53
+ * dropped. A zone entry asks for zone groups, an account entry for account groups, a bucket entry
54
+ * for the two `com.cloudflare.edge.r2.bucket` ones.
55
+ *
56
+ * ⛔ ZONE IS TESTED BEFORE ACCOUNT, because `com.cloudflare.api.account.zone.<id>` also starts with
57
+ * the account namespace, and the wrong order would call every zone entry an account one.
58
+ * ⚠️ A RESOURCE IN NO KNOWN NAMESPACE BECOMES ITS OWN SCOPE, which matches no group, so it fails as
59
+ * an unresolvable name naming the scope it asked for — never by borrowing another scope's id.
60
+ * ⚠️ IT IS THE DEFAULT, NOT THE LAST WORD. An entry carrying an explicit `scope` overrides it —
61
+ * see DeclaredPolicy.scope (cloudflare-roles-expand.ts) and resolvePolicies.
62
+ */
63
+ export declare const scopeOfResource: (resource: string) => string;
64
+ /**
65
+ * What a resolved group is filed under.
66
+ *
67
+ * ⛔ A NAME ALONE IS NOT A KEY. MEASURED 2026-09-15 on the live list: SEVEN names are listed twice,
68
+ * once at `com.cloudflare.api.account` and once at `com.cloudflare.api.account.zone` — `Access:
69
+ * Apps and Policies Read` / `… Revoke` / `… Write`, `Disable ESC Read` / `… Write`, `Logs Read`
70
+ * and `Logs Write`. cfhf.py:148 keyed by name and the last one read won in silence.
71
+ */
72
+ export declare const groupKey: (name: string, scope: string) => string;
73
+ //# sourceMappingURL=cloudflare-group-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-group-scope.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-group-scope.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uBAAuB;AACvB,eAAO,MAAM,YAAY,WAAY,MAAM,WAAgD,CAAC;AAC5F,eAAO,MAAM,eAAe,cAAe,MAAM,WAA8C,CAAC;AAEhG;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,cAAe,MAAM,gBAAgB,MAAM,UAAU,MAAM,WACd,CAAC;AAEzE;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY;aACvB,OAAO,EAAE,4BAA4B;aACrC,MAAM,EAAE,+BAA+B;aACvC,IAAI,EAAE,iCAAiC;CAC/B,CAAC;AAEX;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,SAAU,MAAM,KAAG,MAAM,GAAG,SACW,CAAC;AAEpE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,aAAc,MAAM,WAG/C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,SAAS,MAAM,WAAuB,CAAC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * The two things apply-roles.py wrote BESIDE the roles, checked against the expansion: each mount's
3
+ * KV catalog document (apply-roles.py:48-82) and each mount's description (apply-roles.py:85-95).
4
+ *
5
+ * ★ WHY THE CATALOG IS CHECKED AT ALL: IT IS THE BOOTSTRAP'S INDEPENDENT WITNESS. Its `permissions`
6
+ * are the NAMES apply-roles.py resolved against Cloudflare's own permission-group list at its last
7
+ * --apply — resolve_groups fails the role on any name Cloudflare does not know
8
+ * (apply-roles.py:29-45, :144-149). So a catalog that agrees with roles.yaml means the names the
9
+ * bootstrap pairs with live IDs were real Cloudflare names then, which closes the blind spot
10
+ * cloudflare-permission-groups-solve.ts names.
11
+ *
12
+ * ⚠️ `generated` IS NOT COMPARED. It is the wall-clock time of the last --apply
13
+ * (apply-roles.py:55), so it differs on every run by construction — a note for whoever declares
14
+ * the catalog next: as written it can never plan noop.
15
+ */
16
+ import type { SnapshotMount } from './cloudflare-parity-snapshot.ts';
17
+ import type { ExpandedRole } from './cloudflare-roles-expand.ts';
18
+ export declare const catalogProblems: (mount: string, declared: readonly ExpandedRole[], snapshot: SnapshotMount | undefined) => string[];
19
+ /**
20
+ * apply-roles.py:91-95 — `Cloudflare <surface> credentials, <n> roles. Catalog: …`, n being the
21
+ * EXPANDED count, not the live one.
22
+ *
23
+ * ⚠️ `observedCount` IS ADDED BACK IN. The Python expanded over every zone, anyauth included, so its
24
+ * count carries the roles Alchemy now only observes (OBSERVED_ROLE_PREFIXES in cloudflare-zones.ts).
25
+ * Leaving them out would report a description drift that is really a scope decision.
26
+ */
27
+ export declare const descriptionProblem: (mount: string, declared: readonly ExpandedRole[], snapshot: SnapshotMount | undefined, observedCount?: number) => string | undefined;
28
+ //# sourceMappingURL=cloudflare-parity-catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-parity-catalog.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-parity-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AACrE,OAAO,KAAK,EAAe,YAAY,EAAE,MAAM,8BAA8B,CAAC;AA0B9E,eAAO,MAAM,eAAe,UACnB,MAAM,YACH,SAAS,YAAY,EAAE,YACvB,aAAa,GAAG,SAAS,KAClC,MAAM,EAkCR,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,UACtB,MAAM,YACH,SAAS,YAAY,EAAE,YACvB,aAAa,GAAG,SAAS,6BAElC,MAAM,GAAG,SAQX,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * A directory of `bao read` exports, loaded — the offline stand-in for production that
3
+ * cloudflare/parity.ts checks the declaration against.
4
+ *
5
+ * The layout, as exported on 2026-09-14 (all of it non-secret):
6
+ * <dir>/<mount>/<role>.json `bao read <mount>/roles/<role>` data, `policies` a JSON string
7
+ * <dir>/<mount>/_catalog.json the `kv/cloudflare/catalog/<mount>` document apply-roles.py wrote
8
+ * <dir>/_sys-mounts.json `sys/mounts`, for each mount's description
9
+ *
10
+ * ⛔ READ-ONLY AND OFFLINE. Nothing here opens a socket or reads a credential; every other `_*`
11
+ * file in a mount directory is ignored, whatever it holds.
12
+ */
13
+ import { type LiveCloudflareRole } from './cloudflare-role-form.ts';
14
+ type Json = Readonly<Record<string, unknown>>;
15
+ export interface CatalogDocument {
16
+ /** Every field of the KV document except `roles`. */
17
+ readonly fields: Json;
18
+ /** `roles`, which apply-roles.py stored as a JSON string (apply-roles.py:68-76), parsed. */
19
+ readonly roles: ReadonlyMap<string, Json>;
20
+ }
21
+ export interface SnapshotMount {
22
+ readonly mount: string;
23
+ readonly roles: ReadonlyMap<string, LiveCloudflareRole>;
24
+ readonly catalog: CatalogDocument | undefined;
25
+ /** `sys/mounts/<mount>/` description, when the export has one. */
26
+ readonly description: string | undefined;
27
+ }
28
+ /** Load every `cloudflare-*` mount directory under `dir`. */
29
+ export declare const loadSnapshot: (dir: string) => Promise<SnapshotMount[]>;
30
+ export {};
31
+ //# sourceMappingURL=cloudflare-parity-snapshot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-parity-snapshot.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-parity-snapshot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,EAAE,KAAK,kBAAkB,EAAc,MAAM,2BAA2B,CAAC;AAEhF,KAAK,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE9C,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IACtB,4FAA4F;IAC5F,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IACxD,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAAC;IAC9C,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CAC1C;AA0BD,6DAA6D;AAC7D,eAAO,MAAM,YAAY,QAAe,MAAM,KAAG,OAAO,CAAC,aAAa,EAAE,CA2BvE,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { SnapshotMount } from './cloudflare-parity-snapshot.ts';
2
+ import type { ExpandedRole } from './cloudflare-roles-expand.ts';
3
+ export interface ParityReport {
4
+ readonly lines: string[];
5
+ readonly problems: number;
6
+ }
7
+ /**
8
+ * ★ `observedPrefixes` NAMES ROLES THAT ARE READ, NEVER DECLARED OR WRITTEN — anyauth's, by Tim's
9
+ * call on 2026-09-14 ("read only, in case we decide to adopt it later"). A live role under one of
10
+ * them is listed in its own section and is not a problem; one that is NOT under a prefix and not
11
+ * declared still is, so the carve-out cannot hide an unrelated stray.
12
+ */
13
+ export declare const parityReport: (declared: ReadonlyMap<string, readonly ExpandedRole[]>, snapshot: readonly SnapshotMount[], observedPrefixes?: readonly string[]) => ParityReport;
14
+ //# sourceMappingURL=cloudflare-parity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cloudflare-parity.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-parity.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iCAAiC,CAAC;AAQrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEjE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAiDD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,aACb,WAAW,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAAC,YAC5C,SAAS,aAAa,EAAE,qBAChB,SAAS,MAAM,EAAE,KAClC,YAkEF,CAAC"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * ⚠️ TEMPORARY — DELETE WITH `permissionGroupsFromLiveRoles` (cloudflare-permission-groups.ts) ONCE
3
+ * `GET <mount>/permission-groups` IS DEPLOYED ON THE CLOUDFLARE ENGINE.
4
+ *
5
+ * The pure half of the bootstrap: recover permission-group name → ID from roles whose names are
6
+ * declared (roles.yaml) and whose IDs are live (the engine).
7
+ *
8
+ * ★ WHY IT EXISTS AT ALL. apply-roles.py resolved names with
9
+ * `GET /accounts/<id>/tokens/permission_groups` (cfhf.py:147-148) under a Cloudflare parent
10
+ * credential, which agents may not use. The engine is gaining an endpoint that answers under its
11
+ * own credential; until it ships, the only other witness is the roles apply-roles.py already
12
+ * wrote, each pairing a list of names with a list of IDs.
13
+ *
14
+ * ⛔ NEVER A GUESS.
15
+ * 1. Every entry with exactly one name and one ID binds that pair.
16
+ * 2. Then, until nothing changes: an entry left with exactly ONE name and ONE ID not yet known
17
+ * binds those two.
18
+ * 3. Once settled, every entry is checked against the map: one that declares a known name without
19
+ * carrying that name's ID CONTRADICTS it.
20
+ * POSITIONS ARE NEVER USED, even where the Python kept yaml order — a hand-edited role would
21
+ * otherwise teach the map a wrong pair in silence. A name bound to two IDs, or an ID bound to two
22
+ * names, is AMBIGUOUS; a name nothing isolates is UNRESOLVED; and a contradiction is exactly
23
+ * that. Each is an error for the whole account, not a best effort.
24
+ *
25
+ * ⛔ THE CONTRADICTION CHECK IS THE ONE THAT BITES, AND IT WAS FOUND BY A TEST, NOT BY DESIGN. When a
26
+ * name has ONE single-group witness and that role was edited by hand, step 1 learns the edited ID,
27
+ * and the multi-group roles carrying the real one merely fail to eliminate. Unchecked, diff would
28
+ * then plan every one of those roles as an update TO THE EDITED ID — a hand edit propagated by the
29
+ * tool meant to catch it. Which side is wrong is unknowable here; only Cloudflare knows.
30
+ * ⚠️ THE PRICE: SOURCE (b) CANNOT PLAN A GROUP ADDED TO, OR SWAPPED IN, AN EXISTING ROLE. That edit
31
+ * looks exactly like the hand edit above — declared names disagreeing with live IDs — so it fails
32
+ * the account. New roles and removed groups still plan. (b) is for ADOPTION; changes wait for (a).
33
+ *
34
+ * ⛔ IT KEYS BY NAME AND THE ENGINE SOURCE KEYS BY (NAME, SCOPE) — DELIBERATELY UNCHANGED, 2026-09-15.
35
+ * A live role is a list of ids with no scopes, so a name is all this can key on; seven names the
36
+ * engine lists TWICE (once account-scoped, once zone-scoped) are one entry here, holding whichever
37
+ * id the estate happens to carry. cloudflare-policy.ts `atEveryScope` is where that map is widened
38
+ * to the scoped key the resolver now uses, and it is the honest shape of this blind spot: this file
39
+ * is the OFFLINE PARITY PROOF against a production export, and rewriting it to a key it cannot
40
+ * observe would make it agree with the engine by construction instead of by measurement.
41
+ *
42
+ * ⚠️ WHAT IT CANNOT CATCH. The names come from roles.yaml itself, so a group misnamed CONSISTENTLY
43
+ * — every use of `DNS Read` spelled `DNS Reed` — pairs the wrong name with the right ID and
44
+ * resolves cleanly, where Cloudflare would have refused it. cloudflare/parity.ts cross-checks the
45
+ * catalog documents apply-roles.py wrote, which hold names that DID resolve against Cloudflare at
46
+ * the last apply, for exactly that reason. The engine endpoint has no such blind spot.
47
+ */
48
+ import type { WirePolicy } from './cloudflare-policy.ts';
49
+ import type { ExpandedRole } from './cloudflare-roles-expand.ts';
50
+ export interface Observation {
51
+ /** `<mount>/<role>[<entry>]`, for messages. */
52
+ readonly where: string;
53
+ readonly names: readonly string[];
54
+ readonly ids: readonly string[];
55
+ }
56
+ export interface Conflict {
57
+ /** `name` bound to several IDs, or `id` bound to several names. */
58
+ readonly kind: 'id' | 'name';
59
+ readonly value: string;
60
+ readonly bound: readonly string[];
61
+ }
62
+ /** An entry declaring `name`, whose ID the map knows as `id`, without carrying `id`. */
63
+ export interface Contradiction {
64
+ readonly where: string;
65
+ readonly name: string;
66
+ readonly id: string;
67
+ }
68
+ export interface Solved {
69
+ readonly groups: ReadonlyMap<string, string>;
70
+ readonly ambiguous: readonly Conflict[];
71
+ readonly unresolved: readonly string[];
72
+ readonly contradicted: readonly Contradiction[];
73
+ }
74
+ /**
75
+ * One observation per declared entry whose resource matches exactly one live entry of that role.
76
+ *
77
+ * ★ PAIRED BY RESOURCE, NOT BY POSITION. A zone role's account groups sit on the account resource
78
+ * and nowhere else, whatever order the entries were stored in. An entry that shares its resource
79
+ * with a sibling, or finds no single live match, observes nothing — the diff reports it instead.
80
+ */
81
+ export declare const observationsOf: (declared: readonly ExpandedRole[], live: ReadonlyMap<string, readonly WirePolicy[]>) => Observation[];
82
+ export declare const solveGroups: (observations: readonly Observation[]) => Solved;
83
+ /** Every conflict, unresolved name and contradiction, one line each. */
84
+ export declare const describeProblems: (solved: Solved) => string[];
85
+ //# sourceMappingURL=cloudflare-permission-groups-solve.d.ts.map