@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,126 @@
1
+ /**
2
+ * `Proxmox.SdnZone` — the network a declared guest sits on, finally declared too.
3
+ *
4
+ * ★ WHY THIS ONE MATTERS MORE THAN IT LOOKS. Every guest NIC here names a bridge —
5
+ * `net0=name=eth0,bridge=vmbr0` — that nothing in this package declares: a hand-built interface,
6
+ * repeated per node, that a container silently depends on. A zone is the first piece of that
7
+ * dependency PVE will let a plan own.
8
+ *
9
+ * ⛔ SDN WRITES ARE STAGED, NOT APPLIED, AND THAT DEFEATS THE FACTORY'S READ-BACK GUARANTEE.
10
+ * POST and PUT under `cluster/sdn/zones` edit `/etc/pve/sdn/zones.cfg` and change NOTHING on any
11
+ * node. The plain GET reads that same staged file back, so `pveOperations` sees its own write,
12
+ * finds the object present, and reports a success in which no node gained an interface. The
13
+ * applied state is a different document — `.running-config` beside it — and `?pending=1` is the
14
+ * read that tells them apart: it returns a merged view with a `state` of new/changed/deleted.
15
+ *
16
+ * ⛔ THE APPLY CANNOT LIVE IN THIS RECONCILE, so it is absent rather than forgotten.
17
+ * `PUT /cluster/sdn` is the atomic pending-to-apply and it applies the WHOLE staged config —
18
+ * every zone, vnet and subnet, on every node. Called from a per-resource reconcile it would push
19
+ * some OTHER resource's half-written zone out to the cluster at whatever point the plan ordering
20
+ * reached this one, and PVE has no pre-apply hook to veto that. The apply belongs to ONE object
21
+ * that depends on all of them — a sibling `Proxmox.SdnApply` over `cluster/sdn`, or a stack-level
22
+ * hook that runs last — so that "declared" and "live on the nodes" stay two visible steps.
23
+ *
24
+ * ⚠️ SO THESE ATTRIBUTES DESCRIBE THE STAGED ZONE, and there is deliberately no `applied` flag:
25
+ * the factory uses ONE path for GET, PUT and DELETE, and PVE's parameter schemas are closed, so
26
+ * an unexpected `pending` on the PUT is a 400 rather than an ignored hint. Reading `?pending=1`
27
+ * means re-implementing the operations this file exists to share, and an `applied: true` that
28
+ * was never measured is worse than an absent one. Deploying this resource does not change the
29
+ * network; it changes what the next apply will do.
30
+ *
31
+ * ⛔ THE READ THIS RESOURCE PERFORMS NEEDS SDN.Allocate, NOT SDN.Audit, AND AN EARLIER DRAFT OF
32
+ * THIS COMMENT SAID OTHERWISE. Read off the cluster's own schema on 2026-09-13:
33
+ *
34
+ * GET /cluster/sdn/zones -> "list entries where you have SDN.Audit or SDN.Allocate"
35
+ * GET /cluster/sdn/zones/{zone} -> {"check": ["perm", "/sdn/zones/{zone}", ["SDN.Allocate"]]}
36
+ *
37
+ * `pveOperations.read` calls the ITEM endpoint, so an auditor-shaped `read` lease is REFUSED —
38
+ * and the refusal does not surface as a 403. `read` folds every failure into `undefined`, which
39
+ * the factory reads as "absent", so the plan says create, the POST goes out, and PVE answers
40
+ * that the zone already exists. This is the same class as Proxmox.Storage; see
41
+ * docs/privileges.md, "The failure that does not look like a permission problem".
42
+ *
43
+ * ⚠️ THE REST OF THE PRIVILEGES. Writes need SDN.Allocate (`/sdn/zones` to create,
44
+ * `/sdn/zones/{zone}` to update or delete); the apply — `Proxmox.SdnApply`, which is what
45
+ * actually publishes any of this — needs SDN.Allocate on `/sdn` itself. SDN.Use is NOT among
46
+ * them: it permits attaching a guest to an existing vnet and nothing else.
47
+ */
48
+ import { Resource } from 'alchemy';
49
+ import * as Provider from 'alchemy/Provider';
50
+ import { type PveRequirements, type WithTarget } from './resource.ts';
51
+ /** PVE's zone plugins. Which one a zone is decides which fields below it will even accept. */
52
+ export type SdnZoneType = 'evpn' | 'qinq' | 'simple' | 'vlan' | 'vxlan';
53
+ export interface SdnZoneProps extends WithTarget {
54
+ /**
55
+ * PVE's primary key. ⚠️ AT MOST EIGHT LOWERCASE ALPHANUMERICS, first one a letter: the id lands
56
+ * in generated interface names, so PVE enforces it with a regex whose rejection reads as
57
+ * "value does not match the regex pattern" rather than as "your name is too long".
58
+ */
59
+ zone: string;
60
+ /**
61
+ * ⛔ CREATE-TIME ONLY. `PUT /cluster/sdn/zones/{zone}` has no `type` parameter at all — a
62
+ * section's type is fixed when it is written. Changing it here is not an update PVE can
63
+ * perform; the ⛔ in `attributes` turns that edit into a refusal instead of a silent no-op.
64
+ */
65
+ type: SdnZoneType;
66
+ /** Accepted by every zone type. Unset means the zone inherits the underlying bridge's MTU. */
67
+ mtu?: number;
68
+ /** ⚠️ WHICH NODES THE ZONE IS DEPLOYED TO. Unset means EVERY node, not none. */
69
+ nodes?: string[];
70
+ /** IPAM plugin id. Unset is the built-in `pve` one — the two are one zone, see `sameIpam`. */
71
+ ipam?: string;
72
+ /** DNS plugin id used to register guest records. */
73
+ dns?: string;
74
+ /** The domain those records are registered under, e.g. `example.internal`. */
75
+ dnszone?: string;
76
+ /** vxlan only, and REQUIRED there: the peer addresses the tunnel mesh is built from. */
77
+ peers?: string[];
78
+ /** evpn only, and required there. PVE's own hyphenated name, so no mapping to get wrong. */
79
+ 'vrf-vxlan'?: number;
80
+ /** qinq only, and required there: the service VLAN tag the zone's vnets are stacked inside. */
81
+ tag?: number;
82
+ /** vlan and qinq only, and required in both: the existing bridge the zone is carved out of. */
83
+ bridge?: string;
84
+ }
85
+ /**
86
+ * ⛔ NO `digest` HERE, ON PURPOSE. PVE returns one, but it is the digest of the WHOLE zones.cfg
87
+ * file rather than of this section — declaring a SECOND zone would rewrite this one's stored
88
+ * attributes, and comparing it would report an update on a zone nobody touched.
89
+ */
90
+ export interface SdnZoneAttributes {
91
+ zone: string;
92
+ type: SdnZoneType;
93
+ /** 0 when unset, which is PVE's "inherit from the bridge" rather than an MTU of zero. */
94
+ mtu: number;
95
+ /** Normalised: split, sorted, rejoined. Never compare a raw PVE string to it — see `csv`. */
96
+ nodes: string;
97
+ /** The EFFECTIVE ipam: an absent one is reported as `pve`, because that is what will be used. */
98
+ ipam: string;
99
+ dns: string;
100
+ dnszone: string;
101
+ peers: string;
102
+ 'vrf-vxlan': number;
103
+ tag: number;
104
+ bridge: string;
105
+ /** ⚠️ THE REMINDER IN THE STATE ITSELF: staged config. No value here says a node has the zone. */
106
+ readonly staged: true;
107
+ }
108
+ export interface ProxmoxSdnZone extends Resource<'Proxmox.SdnZone', SdnZoneProps, SdnZoneAttributes, never, PveRequirements> {
109
+ }
110
+ export declare const ProxmoxSdnZone: import("alchemy").ResourceClass<ProxmoxSdnZone>;
111
+ /**
112
+ * ⛔ Empty for the same reason as every other resource here, and with extra force for SDN:
113
+ * `GET /cluster/sdn/zones` answers with the zones the cluster's whole network already
114
+ * runs on. Adopting those would make a later `alchemy destroy` a cluster-wide outage.
115
+
116
+ *
117
+ * ⛔ A DELETE IS STAGED TOO, AND THAT IS THE HALF PEOPLE FORGET. The section leaves
118
+ * zones.cfg and Alchemy drops the resource, but the zone KEEPS RUNNING on every node
119
+ * until something applies `PUT /cluster/sdn`: a destroy nobody applies reads as complete
120
+ * in the plan and has not happened on the cluster.
121
+ * ⚠️ PVE also refuses to remove a zone that still holds vnets. That refusal is kept — it is
122
+ * the cluster declining to orphan a network because a line left a file.
123
+
124
+ */
125
+ export declare const ProxmoxSdnZoneProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxSdnZone>, never, import("effect/unstable/http/HttpClient").HttpClient>;
126
+ //# sourceMappingURL=sdn-zone.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdn-zone.d.ts","sourceRoot":"","sources":["../../src/proxmox/sdn-zone.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF,8FAA8F;AAC9F,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,CAAC;AAExE,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,IAAI,EAAE,WAAW,CAAC;IAClB,8FAA8F;IAC9F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wFAAwF;IACxF,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,4FAA4F;IAC5F,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAC;IACZ,6FAA6F;IAC7F,KAAK,EAAE,MAAM,CAAC;IACd,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,kGAAkG;IAClG,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,cAAc,iDAA8C,CAAC;AA6G1E;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * `Proxmox.Storage` — one entry in `/etc/pve/storage.cfg`, declared. Cluster-scoped like a pool,
3
+ * which is why `path` and `collection` carry no node segment though a storage is used per node.
4
+ *
5
+ * ★ THIS IS THE OBJECT EVERY CONTAINER ALREADY DEPENDS ON AND NOTHING DECLARES. `LxcProps.storage`
6
+ * and the prefix of `LxcProps.ostemplate` (`local:vztmpl/...`) are bare strings, so a container's
7
+ * rootfs points at something outside the graph: rebuild a node and the plan still reads `noop`
8
+ * right up to the create that fails because the storage is not there. Passing this resource's
9
+ * `storage` attribute into `LxcProps.storage` makes the dependency real — Alchemy orders by data
10
+ * flow, so the storage reconciles first because the container reads a value out of it.
11
+ *
12
+ * ⛔ ONE PRIVILEGE COVERS THIS WHOLE FAMILY AND IT IS `Datastore.Allocate` ON `/storage`. Create,
13
+ * update, delete AND the single-object read all check it — `Datastore.Audit` is not enough for
14
+ * any of them. MEASURED: `GET /storage/local` under the auditor-scoped `read` lease answers 403
15
+ * "Permission check failed (/storage/local, Datastore.Allocate)". `pveOperations.read` folds
16
+ * every failure into `undefined`, i.e. "absent", because a 404 is a legitimate answer there and
17
+ * it cannot tell the two apart — so under that lease this resource NEVER CONVERGES, in two
18
+ * directions: reconcile reads "absent" and POSTs a create over a storage that is already defined,
19
+ * and where a create does land the read-back is 403 too, so reconcile dies with "the write
20
+ * returned no error but the object is still absent" about an object it has just built. Both
21
+ * messages point away from the cause.
22
+ * ★ SO WIDEN OR RE-POINT BEFORE THE FIRST PLAN, outside this file: give the credential mount's
23
+ * `read` role `Datastore.Allocate` on `/storage`, or read this family with `provision`. The
24
+ * provision role in the estate this was written for holds `Datastore.AllocateSpace` and
25
+ * `Datastore.Audit`, and NEITHER IS IT — AllocateSpace writes volumes INTO a storage, Allocate
26
+ * DEFINES one — so it 403s here until widened the way `Pool.Allocate` was for `Proxmox.Pool`.
27
+ *
28
+ * ⛔ THERE IS NO `password` PROP AND THERE MUST NEVER BE ONE. PBS and CIFS storages take one at
29
+ * create time, and Alchemy persists attributes UNENCRYPTED — a secret in props is one careless
30
+ * line away from being an attribute, and from there it is in the state store's nightly dump. So
31
+ * `StorageLocator` types `password`, `keyring` and `encryption-key` as `never`: declaring one is
32
+ * a COMPILE ERROR rather than a leak somebody finds in a backup months later. Set it once out of
33
+ * band (`pvesm set <id> --password`); PVE never returns it on read, so nothing here could diff it
34
+ * anyway.
35
+ */
36
+ import { Resource } from 'alchemy';
37
+ import * as Provider from 'alchemy/Provider';
38
+ import { type PveRequirements, type WithTarget } from './resource.ts';
39
+ /**
40
+ * The create-time, plugin-specific half of a storage: `dir` wants `path`, `nfs` `server`+`export`,
41
+ * `cifs` `server`+`share`, `pbs` `server`+`datastore`, `rbd` `pool`+`monhost`, `lvmthin`
42
+ * `vgname`+`thinpool`, `zfspool` `pool`. A bag rather than two dozen named fields, because PVE
43
+ * ships about that many plugins and each brings its own locator. ⚠️ Never diffed — see `matches`.
44
+ *
45
+ * ⛔ `password`, `keyring` AND `encryption-key` ARE `never` ON PURPOSE — see the ⛔ in the header.
46
+ * POSITIVE-CONTROLLED rather than assumed: all three are a compile error, `TS2322: Type 'string'
47
+ * is not assignable to type 'undefined'`.
48
+ */
49
+ export type StorageLocator = Record<string, string> & {
50
+ password?: never;
51
+ keyring?: never;
52
+ 'encryption-key'?: never;
53
+ };
54
+ export interface StorageProps extends WithTarget {
55
+ /** PVE's primary key, cluster-wide — the string an LXC's `storage` and `ostemplate` name. */
56
+ storage: string;
57
+ /**
58
+ * `dir` | `nfs` | `cifs` | `pbs` | `zfspool` | `lvmthin` | `rbd` | … ⚠️ CREATE-ONLY, so a changed
59
+ * `type` plans as `noop` (see `matches`). PVE cannot retype a storage: remove and redeclare.
60
+ */
61
+ type: string;
62
+ /** ⚠️ Create-only, one shape per plugin. See `StorageLocator`. */
63
+ locator?: StorageLocator;
64
+ /** `images,rootdir,vztmpl,iso,backup,snippets`. PVE reports `none` for an empty set. */
65
+ content?: string;
66
+ /**
67
+ * Node restriction, e.g. `n1,n2`. ⚠️ A STORAGE WITH A `nodes` LIST DOES NOT EXIST ANYWHERE ELSE:
68
+ * an LXC declared on a node outside it fails at create with "storage not available on node".
69
+ */
70
+ nodes?: string;
71
+ /** Keeps the definition but stops PVE using it. */
72
+ disable?: boolean;
73
+ /** Tells PVE the same volumes are visible from every node — a claim, not a mechanism. */
74
+ shared?: boolean;
75
+ /** `off` | `metadata` | `falloc` | `full`. File-based plugins only. */
76
+ preallocation?: string;
77
+ /**
78
+ * `keep-last=3,keep-daily=7,…`. ⚠️ THE WIRE NAME IS KEPT, HYPHEN AND ALL: a `pruneBackups` that
79
+ * quietly becomes `prune-backups` in the form body is a second name for one thing.
80
+ */
81
+ 'prune-backups'?: string;
82
+ comment?: string;
83
+ /**
84
+ * ⚠️ THE DEPRECATED PREDECESSOR OF `prune-backups`, and the two do not mix — declare one or the
85
+ * other. `0` means UNLIMITED to PVE rather than "unset", hence the `-1` in the attribute.
86
+ */
87
+ maxfiles?: number;
88
+ }
89
+ export interface StorageAttributes {
90
+ storage: string;
91
+ /** Reported so a plan can show what it points at; never diffed — see `type` above. */
92
+ type: string;
93
+ content: string;
94
+ nodes: string;
95
+ disable: boolean;
96
+ shared: boolean;
97
+ preallocation: string;
98
+ 'prune-backups': string;
99
+ comment: string;
100
+ /** ⚠️ `-1` means "no maxfiles in storage.cfg". `0` is a real setting meaning unlimited. */
101
+ maxfiles: number;
102
+ }
103
+ export interface ProxmoxStorage extends Resource<'Proxmox.Storage', StorageProps, StorageAttributes, never, PveRequirements> {
104
+ }
105
+ /** ★ `retain` by default — a storage holding volumes cannot be rebuilt. See the ★ in resource.ts. */
106
+ export declare const ProxmoxStorage: import("alchemy").ResourceClass<ProxmoxStorage>;
107
+ /**
108
+ * ⛔ EMPTY, AND HERE IT MATTERS MORE THAN ANYWHERE ELSE IN THIS PACKAGE. `GET /storage`
109
+ * returns every definition on the cluster, `local` and `local-lvm`/`local-zfs` included:
110
+ * the installer's own, that every guest's rootfs sits on. Adopting those would put
111
+ * Alchemy one `delete` away from the cluster's disks. Adoption is explicit, always.
112
+
113
+ *
114
+ * ⚠️ THIS REMOVES THE DEFINITION, NOT THE DATA. PVE drops the section from storage.cfg and
115
+ * leaves the volumes on disk untouched — so the loss is not the bytes, it is that guests
116
+ * whose config still names this storage can no longer resolve their own disks.
117
+
118
+ */
119
+ export declare const ProxmoxStorageProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxStorage>, never, import("effect/unstable/http/HttpClient").HttpClient>;
120
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../src/proxmox/storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG;IACpD,QAAQ,CAAC,EAAE,KAAK,CAAC;IACjB,OAAO,CAAC,EAAE,KAAK,CAAC;IAChB,gBAAgB,CAAC,EAAE,KAAK,CAAC;CAC1B,CAAC;AAEF,MAAM,WAAW,YAAa,SAAQ,UAAU;IAC9C,6FAA6F;IAC7F,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,kEAAkE;IAClE,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yFAAyF;IACzF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,YAAY,EACZ,iBAAiB,EACjB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,qGAAqG;AACrG,eAAO,MAAM,cAAc,iDAEzB,CAAC;AAgHH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,sBAAsB,oIACoD,CAAC"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * `Proxmox.User` — a PVE account. The identity every other resource in this package borrows.
3
+ *
4
+ * ★ THIS IS THE ONE OBJECT THAT CAN CUT THE BRANCH IT SITS ON. The user the credential mount vends
5
+ * tokens for, and the group whose members are allowed to mint, are what `src/credentials.ts`
6
+ * ultimately asks OpenBao for. A cluster rebuilt without them cannot plan ANY resource here —
7
+ * including the plan that would recreate them, which needs a credential to run. So this resource
8
+ * is drift repair for an account that already exists (comment, email, enable, expire, groups),
9
+ * not a bootstrap: the first mint user is made on a node with `pveum user add`, by a human, once.
10
+ *
11
+ * ⛔ THERE IS NO `password` PROP, AND ADDING ONE WOULD BE A LEAK RATHER THAN A FEATURE. Alchemy
12
+ * writes resource state WITHOUT encryption — see the ⛔ in `credentials.ts` — so a password prop
13
+ * would sit in clear in whatever database the stack points `state` at, and in every backup of it.
14
+ * It is also unnecessary: `password` is accepted only by the CREATE call, changing one afterwards
15
+ * is `PUT /access/password` (a different endpoint, a different privilege), and an account in a
16
+ * realm such as `@pam`, `@ldap` or `@openid` has no PVE-side password at all — the realm holds
17
+ * it. Out of scope by design. `keys` (TFA) is omitted for the same reason.
18
+ *
19
+ * ⚠️ `groups` COMES BACK AS AN ARRAY AND GOES OUT AS A COMMA STRING, and the asymmetry is the whole
20
+ * trap. GET answers `["a","b"]`, POST/PUT want `a,b`, and PVE returns the list in ITS order, not
21
+ * the declared one. Compared naively, `matches` reads ['b','a'] against 'a,b' and every plan for
22
+ * the rest of time says "1 to update". Both sides are normalised to a sorted, deduplicated set
23
+ * below. Joining is a safe comparison precisely because a group id cannot contain a comma — the
24
+ * wire format is comma-separated, so PVE refuses one.
25
+ *
26
+ * ⚠️ `tokens` IS NESTED AND IS NOT SETTABLE THROUGH THIS ENDPOINT. The read hands back a map of the
27
+ * account's API tokens; `PUT /access/users/{userid}` has no parameter for them, because they are
28
+ * their own objects under `.../token/{tokenid}`. So they are reported as an attribute — a plan
29
+ * can then say what a delete would take with it — and kept OUT of `matches`, exactly like
30
+ * `pool.members`. Reported, never declared.
31
+ *
32
+ * ⚠️ RECONCILE NEEDS PRIVILEGES `LXCProvisioner` DOES NOT HOLD, so state them rather than meet a
33
+ * 403 in the middle of a deploy (pool.ts records how that went the last time):
34
+ * · `Realm.AllocateUser` on `/access/realm/<realm>` — create and delete.
35
+ * · `User.Modify` on `/access/groups`, and on `/access/groups/<group>` for EVERY group named in
36
+ * `groups` — create, update and delete. PVE checks the groups you are granting, not just the
37
+ * user, so a role wide enough to edit the account can still be refused for one group in the
38
+ * list.
39
+ * · `Sys.Audit` (or `User.Modify`) for the read, which the `read` role's auditor already has.
40
+ * `Permissions.Modify` is NOT on this list: that governs ACL entries, which are a different PVE
41
+ * object and would be a different resource here.
42
+ *
43
+ * ⚠️ A MISSING USER IS A 500, NOT A 404 — "no such user ('x@pve')" — and the factory turns any
44
+ * failed read into "absent". A 403 therefore also reads as absent, and the honest error arrives
45
+ * one step later, from the create: "Permission check failed (/access/groups, User.Modify)". That
46
+ * string is the one to grep for when a plan insists on creating an account that plainly exists.
47
+ */
48
+ import { Resource } from 'alchemy';
49
+ import * as Provider from 'alchemy/Provider';
50
+ import { type PveRequirements, type WithTarget } from './resource.ts';
51
+ export interface UserProps extends WithTarget {
52
+ /**
53
+ * ⛔ REALM-QUALIFIED, ALWAYS: `someone@pve`, `someone@pam`. The realm is part of PVE's primary
54
+ * key, and a bare name is refused with "value does not look like a valid user id".
55
+ *
56
+ * ⚠️ PVE HAS NO RENAME. Editing this prop makes the path point at a DIFFERENT account, which
57
+ * reads as absent and is then created — while the old one stays on the cluster with its tokens
58
+ * and its ACL entries. Renaming is a delete and a create, and should be declared as one.
59
+ */
60
+ userid: string;
61
+ /** Free text shown in the UI. */
62
+ comment?: string;
63
+ /** PVE accepts the empty string here; that is how an address is cleared. */
64
+ email?: string;
65
+ /** Default true, matching PVE's own `enable=1`. False disables login without deleting anything. */
66
+ enable?: boolean;
67
+ /** Seconds since the epoch. 0 — the default — means the account never expires. */
68
+ expire?: number;
69
+ /** Group memberships. Order and duplicates carry no meaning — see the ⚠️ in the header. */
70
+ groups?: string[];
71
+ firstname?: string;
72
+ lastname?: string;
73
+ }
74
+ export interface UserAttributes {
75
+ userid: string;
76
+ comment: string;
77
+ email: string;
78
+ enable: boolean;
79
+ expire: number;
80
+ /** Normalised: sorted, deduplicated, whatever shape the cluster used on the wire. */
81
+ groups: string[];
82
+ firstname: string;
83
+ lastname: string;
84
+ /**
85
+ * API token names this account owns. Reported so a plan can show what a delete would revoke;
86
+ * never compared, because this endpoint cannot set them.
87
+ */
88
+ tokens: string[];
89
+ }
90
+ export interface ProxmoxUser extends Resource<'Proxmox.User', UserProps, UserAttributes, never, PveRequirements> {
91
+ }
92
+ export declare const ProxmoxUser: import("alchemy").ResourceClass<ProxmoxUser>;
93
+ /**
94
+ * ⛔ EMPTY, AND MORE POINTEDLY SO HERE THAN ANYWHERE ELSE. `GET /access/users` answers with
95
+ * every account on the cluster: `root@pam`, every human who logs in, every service
96
+ * identity someone made years ago. Handing that list to Alchemy would invite it to adopt
97
+ * — and therefore one day DELETE — people. Adoption is an explicit act.
98
+
99
+ *
100
+ * ⛔ DELETING A USER TAKES ITS API TOKENS AND ITS ACL ENTRIES WITH IT, in one call and
101
+ * without a confirmation. For the identity a credential mount vends from, that revokes
102
+ * every outstanding lease at once and every plan in this package stops working — this one
103
+ * included. PVE refuses only for `root@pam`; for everyone else it simply obeys.
104
+
105
+ */
106
+ export declare const ProxmoxUserProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxUser>, never, import("effect/unstable/http/HttpClient").HttpClient>;
107
+ //# sourceMappingURL=user.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../src/proxmox/user.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C;;;;;;;OAOG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mGAAmG;IACnG,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,kFAAkF;IAClF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,qFAAqF;IACrF,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,SAAS,EACT,cAAc,EACd,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAAwC,CAAC;AAiGjE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,mBAAmB,iIACiD,CAAC"}
@@ -0,0 +1,134 @@
1
+ /**
2
+ * How PVE spells a value on the wire, and how to get it back to something comparable.
3
+ *
4
+ * ⛔ EVERY FUNCTION HERE WAS FOUND IN TWO OR MORE RESOURCE FILES, AND THE COPIES HAD DRIFTED.
5
+ * `num` was byte-identical in three files, so merging it is free. The boolean was NOT: four
6
+ * files declared `const flag` and meant four different things by it, including one that ran the
7
+ * other way round. That is the whole reason this module exists — not to save lines, but because
8
+ * a name that means four things is a bug waiting for the person who assumes it means the first
9
+ * one they read.
10
+ *
11
+ * ★ THE TWO DIRECTIONS ARE NAMED APART, DELIBERATELY. `bool` reads the wire and answers a
12
+ * TypeScript boolean; `flag` writes a form field and answers PVE's `'1'`/`'0'`. `storage.ts`
13
+ * held both under one name — `flag` there was the write direction while `flag` in three other
14
+ * files was the read direction — which typechecks in both files and is exactly the confusion
15
+ * that survives review.
16
+ *
17
+ * ⚠️ NOTHING HERE IS RESOURCE-SPECIFIC. A coercion that only one PVE object needs belongs in that
18
+ * object's file, where its reasoning sits next to the field it serves. `role.ts` keeps its own
19
+ * `canonical` for the same reason: that one sorts a privilege SET, which is a different idea
20
+ * from `canonicalToken` below however similar the names read.
21
+ */
22
+ /** ⚠️ `typeof value === 'number'` and not `Number(value)`: PVE sends `null` for an unset field. */
23
+ export declare const num: (value: unknown, fallback: number) => number;
24
+ export declare const text: (value: unknown, fallback?: string) => string;
25
+ /**
26
+ * A PVE boolean off the wire.
27
+ *
28
+ * ⛔ THE FOUR SPELLINGS THIS REPLACES, AND WHY THIS ONE IS A SUPERSET OF ALL OF THEM. Measured in
29
+ * the files they came from: `backup-job` accepted `1 | true | '1'` with a caller's fallback;
30
+ * `sdn-vnet` the same three with no fallback; `storage` accepted only `1 | true`; `user` ran the
31
+ * value through `Number()` and asked `!== 0`. For the values PVE actually returns on these
32
+ * endpoints — `0` and `1` — all four agree, so accepting the union changes no live answer.
33
+ *
34
+ * ⚠️ THE ONE REAL DIFFERENCE IS `2`. `user.ts` would have read any non-zero number as true; this
35
+ * reads only `1`. No PVE field in this package is documented to return anything but 0 or 1, and
36
+ * a field that did would be a flag with more than two states — which is not a boolean, and
37
+ * should be read with `num` and named for what it is rather than quietly coerced here.
38
+ *
39
+ * ⚠️ `null`, `undefined` AND `''` ALL TAKE THE FALLBACK. An absent key means PVE's documented
40
+ * default for that field, which is not always `false` — so the default is the CALLER's to state,
41
+ * and `false` is only the fallback's own default for fields that really do default off.
42
+ *
43
+ * ⛔ THE EMPTY STRING IS ABSENT, NOT FALSE, AND GETTING THAT WRONG WRITES THE WRONG VALUE. A
44
+ * metric server is a SectionConfig section and can hand back `''` for an unset flag. Read as
45
+ * `false`, the two fields in this package that default ON — `backup-job.enabled` and
46
+ * `user.enable` — would come back disabled, `matches` would report an update nobody asked for,
47
+ * and the deploy would then WRITE `enabled=0` onto a live job. An empty string is not a
48
+ * boolean; it is the absence of one.
49
+ */
50
+ export declare const bool: (value: unknown, fallback?: boolean) => boolean;
51
+ /**
52
+ * A boolean on its way INTO a PVE form.
53
+ *
54
+ * ⚠️ `undefined` IN, `undefined` OUT — an undeclared field must not be sent at all. Sending `'0'`
55
+ * for a prop the caller never set would write PVE's default over whatever is live, turning an
56
+ * omission into an edit.
57
+ */
58
+ export declare const flag: (value?: boolean) => "0" | "1" | undefined;
59
+ /**
60
+ * A comma list PVE does not promise to give back in the order it was handed.
61
+ *
62
+ * ⚠️ `nodes` IS A SET AND PVE DOES NOT PRESERVE ITS ORDER. The zone plugin decodes the list into a
63
+ * hash and re-encodes it by joining that hash's keys, so the string you get back is not the one
64
+ * you sent. Sorting both sides is what stops a plan reporting an update because two names came
65
+ * back the other way round. `peers` gets the same treatment: it is a mesh, not a queue.
66
+ */
67
+ export declare const csv: (value: readonly string[] | string | undefined) => string;
68
+ /**
69
+ * A comma list of guest IDs, in numeric order.
70
+ *
71
+ * ⚠️ ORDER IS NOT MEANING IN A GUEST LIST, SO IT MUST NOT BE A DIFF. PVE stores `vmid` as the
72
+ * comma-separated string it was handed and gives it back the same way, so a declaration listing
73
+ * the same guests in another order would otherwise be an update that rewrites the job to say
74
+ * exactly what it already said. Both sides are sorted numerically before they meet.
75
+ *
76
+ * ⚠️ NUMERIC, NOT LEXICAL — this is why it is not `csv`. Sorted as text, `101` precedes `99`.
77
+ */
78
+ export declare const guestList: (value: unknown) => string;
79
+ /** `yes`/`true`/`on` and their opposites are the same value as 1/0 to PVE; make them one string. */
80
+ export declare const canonicalToken: (raw: string) => string;
81
+ /**
82
+ * A PVE property string, flattened to one comparable form.
83
+ *
84
+ * ⚠️ A PROPERTY STRING AND ITS PARSED FORM ARE ONE VALUE IN TWO SHAPES, AND PVE HANDS BACK
85
+ * WHICHEVER IT LIKES. MEASURED: `fleecing` is WRITTEN as the property string `fleecing=enabled=0`
86
+ * and READ BACK as the nested object {"enabled":0}. Comparing raw values would report an update
87
+ * on every plan for a job nobody touched — the same forever-diff a create-only field causes. So
88
+ * both sides are flattened here, sorted, and only then compared. `prune-backups` goes through
89
+ * the same funnel: same kind of value, the normalisation is free, and guessing which shape it
90
+ * comes back in is exactly the guess that costs a permanent diff.
91
+ *
92
+ * ⚠️ `defaultKey` EXISTS BECAUSE `fleecing=1` IS SHORTHAND FOR `fleecing=enabled=1`. PVE's format
93
+ * for it is written `[enabled=]<1|0>`, so a bare token is legal on write and comes back expanded
94
+ * on read; unexpanded, the short and long spellings never compare equal. `prune-backups` has no
95
+ * default key, so a bare token there is passed through as-is rather than invented into one.
96
+ */
97
+ export declare const propertyString: (value: unknown, defaultKey?: string) => string;
98
+ /**
99
+ * An integer PVE may have spelled as a string.
100
+ *
101
+ * ⚠️ PVE MAY HAND BACK EITHER `8086` OR `"8086"`. These objects are SectionConfig sections, and the
102
+ * integer check VALIDATES the parsed string rather than converting it, so whether a number
103
+ * arrives as a number is a property of the version you are talking to. `'8086' === 8086` is
104
+ * false, and that is an update consisting of nothing, reported on every plan forever.
105
+ */
106
+ export declare const int: (value: unknown, fallback: number) => number;
107
+ /**
108
+ * A PVE update form with its clear-list attached, and the two guaranteed not to overlap.
109
+ *
110
+ * ⛔ PVE DIES IF A KEY IS SET AND DELETED IN ONE CALL, AND SIX FAMILIES HERE BUILT THAT FORM BY
111
+ * HAND. Measured in the shipped Perl on n2 — `PVE::SectionConfig::delete_from_config`, line 1860:
112
+ *
113
+ * die "cannot set and delete property '$k' at the same time!\n"
114
+ * if defined($new_options->{$k});
115
+ *
116
+ * `defined` is the test, not truth, so `strict=0` or `comment=''` in the body counts as SET. The
117
+ * concrete failure: a live node-affinity rule with `strict 1` and a declaration carrying
118
+ * `strict: false`. `matches` reports an update; the clear-list has to name `strict`, because a
119
+ * PUT of `strict=0` merges a falsy value the plugin still stores; and if the body also emits
120
+ * `strict: '0'` the PUT is refused. The update never lands, the next plan reports the same
121
+ * update, and every deploy 500s — forever.
122
+ *
123
+ * ★ SO DISJOINTNESS IS ENFORCED HERE RATHER THAN REASONED ABOUT SIX TIMES. A cleared key is
124
+ * dropped from the body: CLEAR WINS. That is the right way round because a clear-list entry is
125
+ * computed from "this prop is absent, and absent means remove it", which is a deliberate
126
+ * statement, while the body's value for the same key is the coercion of that same absence —
127
+ * `flag(false)` is `'0'`, and `'0'` is exactly what must not be sent.
128
+ *
129
+ * ⚠️ UNDEFINED VALUES ARE DROPPED TOO, so a form builder may emit `undefined` for a field it does
130
+ * not set without the caller filtering first. `new URLSearchParams` would otherwise stringify it
131
+ * to the literal text "undefined".
132
+ */
133
+ export declare const withClears: (fields: Record<string, string | undefined>, clear: readonly string[]) => Record<string, string>;
134
+ //# sourceMappingURL=values.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../src/proxmox/values.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,mGAAmG;AACnG,eAAO,MAAM,GAAG,UAAW,OAAO,YAAY,MAAM,WACN,CAAC;AAE/C,eAAO,MAAM,IAAI,UAAW,OAAO,8BACW,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,IAAI,UAAW,OAAO,gCAGiB,CAAC;AAErD;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,WAAY,OAAO,0BAA0D,CAAC;AAE/F;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,UAAW,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,WAKnD,CAAC;AAEf;;;;;;;;;GASG;AACH,eAAO,MAAM,SAAS,UAAW,OAAO,KAAG,MAa1C,CAAC;AAEF,oGAAoG;AACpG,eAAO,MAAM,cAAc,QAAS,MAAM,WAKzC,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,UAAW,OAAO,eAAe,MAAM,KAAG,MAkBpE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,GAAG,UAAW,OAAO,YAAY,MAAM,WAInD,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,UAAU,WACb,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,SACnC,SAAS,MAAM,EAAE,KACvB,MAAM,CAAC,MAAM,EAAE,MAAM,CAOvB,CAAC"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * The create lane for `Proxmox.ZfsPool`: the form PVE wants, and the wait for the worker it forks.
3
+ *
4
+ * ★ SPLIT OUT OF zfs-pool.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is a real one
5
+ * rather than a convenient line number. This file answers "how does a declaration become a pool
6
+ * on disks, and how do we know it landed". zfs-pool.ts answers "what is a zpool, what can be
7
+ * said about a live one, and when has it changed" — and on this family the answer to the last of
8
+ * those is "never", which is why the two halves have so little to say to each other.
9
+ *
10
+ * ⚠️ THE `import type` BACK TO zfs-pool.ts IS A CYCLE ON PAPER ONLY — it is type-only, so it is
11
+ * erased before anything runs, and the resource's public shape stays in the file that declares
12
+ * the resource rather than being moved somewhere odd to dodge the arrow. Same as
13
+ * metric-server-form.ts.
14
+ *
15
+ * ⚠️ `createPool` TAKES THE READ AS A PARAMETER, GENERIC IN ITS ERROR AND REQUIREMENT CHANNELS,
16
+ * rather than importing `pveOperations` and rebuilding the spec here. Two `pveOperations` over
17
+ * one spec would be two closures nobody can prove are the same; passing the one the resource
18
+ * already built means this file cannot accidentally read the cluster differently from `diff`.
19
+ */
20
+ import * as Effect from 'effect/Effect';
21
+ import type { ZfsPoolAttributes, ZfsPoolProps } from './zfs-pool.ts';
22
+ /**
23
+ * The POST body. Every field in it is write-only — nothing here is ever read back.
24
+ *
25
+ * ⚠️ `node` IS NOT IN THE BODY: it is already the `nodes/{node}` segment of the URL being POSTed
26
+ * to, and a second copy can only disagree with it. Same reasoning as `id` in metric-server.ts.
27
+ * ⚠️ `devices` IS JOINED IN DECLARED ORDER AND NOT PUT THROUGH `csv()` FROM values.ts. `csv` sorts,
28
+ * and PVE walks this list two at a time to pair `raid10` mirrors — MEASURED in the node's own
29
+ * `PVE::API2::Disks::ZFS` — so sorting it builds a different pool from the one declared. The
30
+ * list is never compared against anything, so it needs no normalisation, only faithful order.
31
+ * ⚠️ AN OMITTED OPTIONAL IS NOT SENT. PVE's own defaults (ashift 12, compression on) are then
32
+ * applied by the node. Sending a guessed default would be indistinguishable here — the values
33
+ * are unreadable afterwards either way — but it would put a number in the request that no
34
+ * declaration asked for, and this is the one call that writes to physical disks.
35
+ */
36
+ export declare const createForm: (props: ZfsPoolProps) => Record<string, string>;
37
+ /**
38
+ * Create the pool if it is absent, and otherwise leave it completely alone.
39
+ *
40
+ * ⛔ THE POST ONLY FORKS A WORKER. MEASURED in `/usr/share/perl5/PVE/API2/Disks/ZFS.pm` on n2: the
41
+ * create handler ends in `$rpcenv->fork_worker('zfscreate', ...)` and its HTTP answer is a UPID
42
+ * returned the instant the worker is forked — before `zpool create` has run, let alone finished.
43
+ * A read-back taken immediately, as `pveOperations.reconcile` takes it, finds nothing and would
44
+ * report a successful create as the failure "the write returned no error but the object is still
45
+ * absent". THAT is why this function exists instead of the factory's reconcile.
46
+ *
47
+ * ⚠️ THERE IS NO UPDATE BRANCH BECAUSE PVE HAS NO PUT HERE. An existing pool is returned exactly as
48
+ * read, unwritten: the only write this resource ever makes is the create of a pool that is not
49
+ * there. Do not add a "repair" write; there is no field it could set.
50
+ *
51
+ * ⛔ AND IF THE READ IS WRONG, THE POST IS STILL SAFE — measured, not hoped for. `read` folds a 403
52
+ * or an unreachable node into "absent", so a too-narrow lease would send this down the create
53
+ * path over a live pool. PVE's handler calls `get_pool_data()` before it forks anything and dies
54
+ * with "pool '<name>' already exists on node '<node>'", and `assert_disk_unused` refuses every
55
+ * device that pool is holding. A create aimed at a pool that is really there fails loudly rather
56
+ * than wiping it.
57
+ *
58
+ * ⚠️ SHORT POLLS WITH A HARD CAP, NOT ONE LONG WAIT. `read` cannot tell "not yet" from "forbidden"
59
+ * — both are `undefined` — so the loop gives up with the UPID rather than retrying forever, and
60
+ * the UPID is the only thing that leads to the worker's real error.
61
+ */
62
+ export declare const createPool: <E, R>(props: ZfsPoolProps, collection: string, path: string, read: (props: ZfsPoolProps) => Effect.Effect<ZfsPoolAttributes | undefined, E, R>) => Effect.Effect<ZfsPoolAttributes, E | Error, R | import("effect/unstable/http/HttpClient").HttpClient>;
63
+ /**
64
+ * ⛔ `DELETE /nodes/{node}/disks/zfs/{name}` RUNS `zpool destroy` AND THE DATASETS GO WITH IT.
65
+ * It is implemented rather than stubbed, because a `delete` that silently does nothing lies to
66
+ * whoever reads the plan. What keeps a deleted line from destroying a pool is the resource's
67
+ * `defaultRemovalPolicy: 'retain'` above — an orphaned pool is forgotten, not destroyed, and a
68
+ * caller who means it says `.pipe(RemovalPolicy.destroy())`.
69
+ *
70
+ * ⚠️ `cleanup-config` IS NOT SENT. It would additionally remove the PVE storage entry pointing
71
+ * at this pool, which is a different object with its own resource (`Proxmox.Storage`); one
72
+ * resource reaching over to delete another's is how a stack ends up with state describing
73
+ * something that is gone.
74
+ */
75
+ export declare const destroyPool: (olds: ZfsPoolProps, path: string) => Effect.Effect<unknown, Error, import("effect/unstable/http/HttpClient").HttpClient>;
76
+ //# sourceMappingURL=zfs-pool-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zfs-pool-write.d.ts","sourceRoot":"","sources":["../../src/proxmox/zfs-pool-write.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAErE;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,UAAU,UAAW,YAAY,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAOpE,CAAC;AAKH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,CAAC,SACtB,YAAY,cACP,MAAM,QACZ,MAAM,QACN,CAAC,KAAK,EAAE,YAAY,KAAK,MAAM,CAAC,MAAM,CAAC,iBAAiB,GAAG,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,0GAmC/E,CAAC;AAEL;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,SAAU,YAAY,QAAQ,MAAM,wFACb,CAAC"}