@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.
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/dist/cloudflare/client.d.ts +45 -0
- package/dist/cloudflare/client.d.ts.map +1 -0
- package/dist/cloudflare/index.d.ts +11 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +129 -0
- package/dist/cloudflare/index.js.map +13 -0
- package/dist/cloudflare/lock-rules.d.ts +50 -0
- package/dist/cloudflare/lock-rules.d.ts.map +1 -0
- package/dist/cloudflare/providers.d.ts +19 -0
- package/dist/cloudflare/providers.d.ts.map +1 -0
- package/dist/cloudflare/r2-bucket-lock.d.ts +100 -0
- package/dist/cloudflare/r2-bucket-lock.d.ts.map +1 -0
- package/dist/forgejo/branch-protection-form.d.ts +19 -0
- package/dist/forgejo/branch-protection-form.d.ts.map +1 -0
- package/dist/forgejo/branch-protection.d.ts +47 -0
- package/dist/forgejo/branch-protection.d.ts.map +1 -0
- package/dist/forgejo/client.d.ts +37 -0
- package/dist/forgejo/client.d.ts.map +1 -0
- package/dist/forgejo/index.d.ts +18 -0
- package/dist/forgejo/index.d.ts.map +1 -0
- package/dist/forgejo/index.js +479 -0
- package/dist/forgejo/index.js.map +21 -0
- package/dist/forgejo/org-actions-secrets.d.ts +45 -0
- package/dist/forgejo/org-actions-secrets.d.ts.map +1 -0
- package/dist/forgejo/org-label.d.ts +46 -0
- package/dist/forgejo/org-label.d.ts.map +1 -0
- package/dist/forgejo/org-team.d.ts +42 -0
- package/dist/forgejo/org-team.d.ts.map +1 -0
- package/dist/forgejo/repo-webhook-form.d.ts +27 -0
- package/dist/forgejo/repo-webhook-form.d.ts.map +1 -0
- package/dist/forgejo/repo-webhook.d.ts +46 -0
- package/dist/forgejo/repo-webhook.d.ts.map +1 -0
- package/dist/forgejo/repository.d.ts +45 -0
- package/dist/forgejo/repository.d.ts.map +1 -0
- package/dist/forgejo/resource.d.ts +83 -0
- package/dist/forgejo/resource.d.ts.map +1 -0
- package/dist/forgejo/team-member.d.ts +39 -0
- package/dist/forgejo/team-member.d.ts.map +1 -0
- package/dist/forgejo/values.d.ts +24 -0
- package/dist/forgejo/values.d.ts.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +9 -0
- package/dist/openbao/auth-role-form.d.ts +38 -0
- package/dist/openbao/auth-role-form.d.ts.map +1 -0
- package/dist/openbao/auth-role.d.ts +20 -0
- package/dist/openbao/auth-role.d.ts.map +1 -0
- package/dist/openbao/bao-address.d.ts +72 -0
- package/dist/openbao/bao-address.d.ts.map +1 -0
- package/dist/openbao/bao-http.d.ts +71 -0
- package/dist/openbao/bao-http.d.ts.map +1 -0
- package/dist/openbao/bao-status.d.ts +74 -0
- package/dist/openbao/bao-status.d.ts.map +1 -0
- package/dist/openbao/cloudflare-group-scope.d.ts +73 -0
- package/dist/openbao/cloudflare-group-scope.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity-catalog.d.ts +28 -0
- package/dist/openbao/cloudflare-parity-catalog.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity-snapshot.d.ts +31 -0
- package/dist/openbao/cloudflare-parity-snapshot.d.ts.map +1 -0
- package/dist/openbao/cloudflare-parity.d.ts +14 -0
- package/dist/openbao/cloudflare-parity.d.ts.map +1 -0
- package/dist/openbao/cloudflare-permission-groups-solve.d.ts +85 -0
- package/dist/openbao/cloudflare-permission-groups-solve.d.ts.map +1 -0
- package/dist/openbao/cloudflare-permission-groups.d.ts +87 -0
- package/dist/openbao/cloudflare-permission-groups.d.ts.map +1 -0
- package/dist/openbao/cloudflare-policy.d.ts +65 -0
- package/dist/openbao/cloudflare-policy.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role-form.d.ts +110 -0
- package/dist/openbao/cloudflare-role-form.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role-wire.d.ts +27 -0
- package/dist/openbao/cloudflare-role-wire.d.ts.map +1 -0
- package/dist/openbao/cloudflare-role.d.ts +35 -0
- package/dist/openbao/cloudflare-role.d.ts.map +1 -0
- package/dist/openbao/cloudflare-roles-config.d.ts +67 -0
- package/dist/openbao/cloudflare-roles-config.d.ts.map +1 -0
- package/dist/openbao/cloudflare-roles-expand.d.ts +76 -0
- package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -0
- package/dist/openbao/digest.d.ts +24 -0
- package/dist/openbao/digest.d.ts.map +1 -0
- package/dist/openbao/fake-bao.d.ts +40 -0
- package/dist/openbao/fake-bao.d.ts.map +1 -0
- package/dist/openbao/forgejo-bootstrap-parse.d.ts +46 -0
- package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -0
- package/dist/openbao/forgejo-bootstrap.d.ts +29 -0
- package/dist/openbao/forgejo-bootstrap.d.ts.map +1 -0
- package/dist/openbao/index.d.ts +18 -0
- package/dist/openbao/index.d.ts.map +1 -0
- package/dist/openbao/index.js +1158 -0
- package/dist/openbao/index.js.map +33 -0
- package/dist/openbao/mount-form.d.ts +60 -0
- package/dist/openbao/mount-form.d.ts.map +1 -0
- package/dist/openbao/mount-wire.d.ts +24 -0
- package/dist/openbao/mount-wire.d.ts.map +1 -0
- package/dist/openbao/mount.d.ts +20 -0
- package/dist/openbao/mount.d.ts.map +1 -0
- package/dist/openbao/pki-role-form.d.ts +118 -0
- package/dist/openbao/pki-role-form.d.ts.map +1 -0
- package/dist/openbao/pki-role.d.ts +50 -0
- package/dist/openbao/pki-role.d.ts.map +1 -0
- package/dist/openbao/policy-assembly.d.ts +13 -0
- package/dist/openbao/policy-assembly.d.ts.map +1 -0
- package/dist/openbao/policy-wire.d.ts +41 -0
- package/dist/openbao/policy-wire.d.ts.map +1 -0
- package/dist/openbao/policy.d.ts +83 -0
- package/dist/openbao/policy.d.ts.map +1 -0
- package/dist/openbao/proxmox-role-form.d.ts +92 -0
- package/dist/openbao/proxmox-role-form.d.ts.map +1 -0
- package/dist/openbao/proxmox-role.d.ts +47 -0
- package/dist/openbao/proxmox-role.d.ts.map +1 -0
- package/dist/openbao/ssh-role-form.d.ts +99 -0
- package/dist/openbao/ssh-role-form.d.ts.map +1 -0
- package/dist/openbao/ssh-role.d.ts +43 -0
- package/dist/openbao/ssh-role.d.ts.map +1 -0
- package/dist/proxmox/acl.d.ts +72 -0
- package/dist/proxmox/acl.d.ts.map +1 -0
- package/dist/proxmox/api-token-form.d.ts +59 -0
- package/dist/proxmox/api-token-form.d.ts.map +1 -0
- package/dist/proxmox/api-token.d.ts +159 -0
- package/dist/proxmox/api-token.d.ts.map +1 -0
- package/dist/proxmox/backup-job.d.ts +112 -0
- package/dist/proxmox/backup-job.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon-form.d.ts +70 -0
- package/dist/proxmox/ceph-daemon-form.d.ts.map +1 -0
- package/dist/proxmox/ceph-daemon.d.ts +162 -0
- package/dist/proxmox/ceph-daemon.d.ts.map +1 -0
- package/dist/proxmox/ceph-flag.d.ts +131 -0
- package/dist/proxmox/ceph-flag.d.ts.map +1 -0
- package/dist/proxmox/ceph-fs-wire.d.ts +106 -0
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -0
- package/dist/proxmox/ceph-fs.d.ts +124 -0
- package/dist/proxmox/ceph-fs.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd-tree.d.ts +154 -0
- package/dist/proxmox/ceph-osd-tree.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd-write.d.ts +36 -0
- package/dist/proxmox/ceph-osd-write.d.ts.map +1 -0
- package/dist/proxmox/ceph-osd.d.ts +102 -0
- package/dist/proxmox/ceph-osd.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool-form.d.ts +217 -0
- package/dist/proxmox/ceph-pool-form.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool-settle.d.ts +39 -0
- package/dist/proxmox/ceph-pool-settle.d.ts.map +1 -0
- package/dist/proxmox/ceph-pool.d.ts +48 -0
- package/dist/proxmox/ceph-pool.d.ts.map +1 -0
- package/dist/proxmox/client.d.ts +64 -0
- package/dist/proxmox/client.d.ts.map +1 -0
- package/dist/proxmox/credentials.d.ts +153 -0
- package/dist/proxmox/credentials.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias-form.d.ts +98 -0
- package/dist/proxmox/firewall-alias-form.d.ts.map +1 -0
- package/dist/proxmox/firewall-alias.d.ts +141 -0
- package/dist/proxmox/firewall-alias.d.ts.map +1 -0
- package/dist/proxmox/generated/pbs.d.ts +388 -0
- package/dist/proxmox/generated/pbs.d.ts.map +1 -0
- package/dist/proxmox/generated/pve.d.ts +4713 -0
- package/dist/proxmox/generated/pve.d.ts.map +1 -0
- package/dist/proxmox/group.d.ts +104 -0
- package/dist/proxmox/group.d.ts.map +1 -0
- package/dist/proxmox/ha-resource.d.ts +104 -0
- package/dist/proxmox/ha-resource.d.ts.map +1 -0
- package/dist/proxmox/ha-rule-form.d.ts +103 -0
- package/dist/proxmox/ha-rule-form.d.ts.map +1 -0
- package/dist/proxmox/ha-rule.d.ts +156 -0
- package/dist/proxmox/ha-rule.d.ts.map +1 -0
- package/dist/proxmox/index.d.ts +43 -0
- package/dist/proxmox/index.d.ts.map +1 -0
- package/dist/proxmox/index.js +2322 -0
- package/dist/proxmox/index.js.map +70 -0
- package/dist/proxmox/lease-cache.d.ts +98 -0
- package/dist/proxmox/lease-cache.d.ts.map +1 -0
- package/dist/proxmox/lxc.d.ts +59 -0
- package/dist/proxmox/lxc.d.ts.map +1 -0
- package/dist/proxmox/members.d.ts +56 -0
- package/dist/proxmox/members.d.ts.map +1 -0
- package/dist/proxmox/metric-server-form.d.ts +53 -0
- package/dist/proxmox/metric-server-form.d.ts.map +1 -0
- package/dist/proxmox/metric-server-otel.d.ts +49 -0
- package/dist/proxmox/metric-server-otel.d.ts.map +1 -0
- package/dist/proxmox/metric-server.d.ts +123 -0
- package/dist/proxmox/metric-server.d.ts.map +1 -0
- package/dist/proxmox/network-apply-read.d.ts +93 -0
- package/dist/proxmox/network-apply-read.d.ts.map +1 -0
- package/dist/proxmox/network-apply.d.ts +102 -0
- package/dist/proxmox/network-apply.d.ts.map +1 -0
- package/dist/proxmox/node-network-form.d.ts +128 -0
- package/dist/proxmox/node-network-form.d.ts.map +1 -0
- package/dist/proxmox/node-network.d.ts +173 -0
- package/dist/proxmox/node-network.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +44 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +146 -0
- package/dist/proxmox/notification-target.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-form.d.ts +147 -0
- package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-guard.d.ts +55 -0
- package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore.d.ts +140 -0
- package/dist/proxmox/pbs-datastore.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-form.d.ts +97 -0
- package/dist/proxmox/pbs-prune-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +168 -0
- package/dist/proxmox/pbs-prune-job.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts +83 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job.d.ts +246 -0
- package/dist/proxmox/pbs-sync-job.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-form.d.ts +114 -0
- package/dist/proxmox/pbs-verify-job-form.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job.d.ts +145 -0
- package/dist/proxmox/pbs-verify-job.d.ts.map +1 -0
- package/dist/proxmox/pool.d.ts +42 -0
- package/dist/proxmox/pool.d.ts.map +1 -0
- package/dist/proxmox/qemu.d.ts +46 -0
- package/dist/proxmox/qemu.d.ts.map +1 -0
- package/dist/proxmox/replication-job-form.d.ts +71 -0
- package/dist/proxmox/replication-job-form.d.ts.map +1 -0
- package/dist/proxmox/replication-job.d.ts +162 -0
- package/dist/proxmox/replication-job.d.ts.map +1 -0
- package/dist/proxmox/resource.d.ts +157 -0
- package/dist/proxmox/resource.d.ts.map +1 -0
- package/dist/proxmox/role.d.ts +97 -0
- package/dist/proxmox/role.d.ts.map +1 -0
- package/dist/proxmox/sdn-apply-read.d.ts +80 -0
- package/dist/proxmox/sdn-apply-read.d.ts.map +1 -0
- package/dist/proxmox/sdn-apply.d.ts +63 -0
- package/dist/proxmox/sdn-apply.d.ts.map +1 -0
- package/dist/proxmox/sdn-subnet-form.d.ts +111 -0
- package/dist/proxmox/sdn-subnet-form.d.ts.map +1 -0
- package/dist/proxmox/sdn-subnet.d.ts +161 -0
- package/dist/proxmox/sdn-subnet.d.ts.map +1 -0
- package/dist/proxmox/sdn-vnet.d.ts +113 -0
- package/dist/proxmox/sdn-vnet.d.ts.map +1 -0
- package/dist/proxmox/sdn-zone.d.ts +126 -0
- package/dist/proxmox/sdn-zone.d.ts.map +1 -0
- package/dist/proxmox/storage.d.ts +120 -0
- package/dist/proxmox/storage.d.ts.map +1 -0
- package/dist/proxmox/user.d.ts +107 -0
- package/dist/proxmox/user.d.ts.map +1 -0
- package/dist/proxmox/values.d.ts +134 -0
- package/dist/proxmox/values.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-write.d.ts +76 -0
- package/dist/proxmox/zfs-pool-write.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool.d.ts +146 -0
- package/dist/proxmox/zfs-pool.d.ts.map +1 -0
- package/dist/talos/credentials.d.ts +30 -0
- package/dist/talos/credentials.d.ts.map +1 -0
- package/dist/talos/index.d.ts +15 -0
- package/dist/talos/index.d.ts.map +1 -0
- package/dist/talos/index.js +392 -0
- package/dist/talos/index.js.map +16 -0
- package/dist/talos/kubeconfig.d.ts +53 -0
- package/dist/talos/kubeconfig.d.ts.map +1 -0
- package/dist/talos/resource.d.ts +32 -0
- package/dist/talos/resource.d.ts.map +1 -0
- package/dist/talos/talos-bootstrap.d.ts +27 -0
- package/dist/talos/talos-bootstrap.d.ts.map +1 -0
- package/dist/talos/talos-cluster-health.d.ts +36 -0
- package/dist/talos/talos-cluster-health.d.ts.map +1 -0
- package/dist/talos/talos-machine-config.d.ts +41 -0
- package/dist/talos/talos-machine-config.d.ts.map +1 -0
- package/dist/talos/talosctl.d.ts +29 -0
- package/dist/talos/talosctl.d.ts.map +1 -0
- package/dist/talos/values.d.ts +20 -0
- package/dist/talos/values.d.ts.map +1 -0
- package/package.json +57 -0
- package/src/cloudflare/client.ts +76 -0
- package/src/cloudflare/index.ts +10 -0
- package/src/cloudflare/lock-rules.ts +60 -0
- package/src/cloudflare/providers.ts +23 -0
- package/src/cloudflare/r2-bucket-lock.ts +220 -0
- package/src/forgejo/branch-protection-form.ts +29 -0
- package/src/forgejo/branch-protection.ts +101 -0
- package/src/forgejo/client.ts +136 -0
- package/src/forgejo/index.ts +17 -0
- package/src/forgejo/org-actions-secrets.ts +99 -0
- package/src/forgejo/org-label.ts +100 -0
- package/src/forgejo/org-team.ts +105 -0
- package/src/forgejo/repo-webhook-form.ts +47 -0
- package/src/forgejo/repo-webhook.ts +118 -0
- package/src/forgejo/repository.ts +126 -0
- package/src/forgejo/resource.ts +171 -0
- package/src/forgejo/team-member.ts +97 -0
- package/src/forgejo/values.test.ts +48 -0
- package/src/forgejo/values.ts +61 -0
- package/src/index.ts +18 -0
- package/src/openbao/auth-role-form.ts +112 -0
- package/src/openbao/auth-role.ts +101 -0
- package/src/openbao/bao-address.test.ts +89 -0
- package/src/openbao/bao-address.ts +97 -0
- package/src/openbao/bao-http-gate.test.ts +88 -0
- package/src/openbao/bao-http-unix.test.ts +70 -0
- package/src/openbao/bao-http.test.ts +161 -0
- package/src/openbao/bao-http.ts +185 -0
- package/src/openbao/bao-status.test.ts +114 -0
- package/src/openbao/bao-status.ts +137 -0
- package/src/openbao/cloudflare-group-scope.test.ts +103 -0
- package/src/openbao/cloudflare-group-scope.ts +83 -0
- package/src/openbao/cloudflare-parity-catalog.ts +104 -0
- package/src/openbao/cloudflare-parity-snapshot.ts +84 -0
- package/src/openbao/cloudflare-parity.ts +152 -0
- package/src/openbao/cloudflare-permission-groups-solve.test.ts +171 -0
- package/src/openbao/cloudflare-permission-groups-solve.ts +197 -0
- package/src/openbao/cloudflare-permission-groups.test.ts +369 -0
- package/src/openbao/cloudflare-permission-groups.ts +299 -0
- package/src/openbao/cloudflare-policy.ts +180 -0
- package/src/openbao/cloudflare-role-form.test.ts +177 -0
- package/src/openbao/cloudflare-role-form.ts +183 -0
- package/src/openbao/cloudflare-role-wire.test.ts +132 -0
- package/src/openbao/cloudflare-role-wire.ts +41 -0
- package/src/openbao/cloudflare-role.ts +189 -0
- package/src/openbao/cloudflare-roles-config.ts +230 -0
- package/src/openbao/cloudflare-roles-expand-bucket.test.ts +134 -0
- package/src/openbao/cloudflare-roles-expand.ts +231 -0
- package/src/openbao/digest.ts +26 -0
- package/src/openbao/fake-bao.ts +84 -0
- package/src/openbao/forgejo-bootstrap-parse.test.ts +76 -0
- package/src/openbao/forgejo-bootstrap-parse.ts +90 -0
- package/src/openbao/forgejo-bootstrap.ts +181 -0
- package/src/openbao/index.ts +17 -0
- package/src/openbao/mount-form.ts +147 -0
- package/src/openbao/mount-wire.test.ts +108 -0
- package/src/openbao/mount-wire.ts +79 -0
- package/src/openbao/mount.ts +132 -0
- package/src/openbao/pki-role-form.ts +248 -0
- package/src/openbao/pki-role.ts +202 -0
- package/src/openbao/policy-assembly.test.ts +28 -0
- package/src/openbao/policy-assembly.ts +13 -0
- package/src/openbao/policy-wire.test.ts +90 -0
- package/src/openbao/policy-wire.ts +53 -0
- package/src/openbao/policy.ts +212 -0
- package/src/openbao/proxmox-role-form.ts +174 -0
- package/src/openbao/proxmox-role.ts +214 -0
- package/src/openbao/ssh-role-form.ts +248 -0
- package/src/openbao/ssh-role.ts +240 -0
- package/src/proxmox/acl.ts +267 -0
- package/src/proxmox/api-token-form.ts +122 -0
- package/src/proxmox/api-token.ts +222 -0
- package/src/proxmox/backup-job.ts +213 -0
- package/src/proxmox/ceph-daemon-form.ts +84 -0
- package/src/proxmox/ceph-daemon.ts +243 -0
- package/src/proxmox/ceph-flag.ts +249 -0
- package/src/proxmox/ceph-fs-wire.ts +246 -0
- package/src/proxmox/ceph-fs.ts +249 -0
- package/src/proxmox/ceph-osd-tree.ts +193 -0
- package/src/proxmox/ceph-osd-write.ts +76 -0
- package/src/proxmox/ceph-osd.ts +244 -0
- package/src/proxmox/ceph-pool-form.ts +249 -0
- package/src/proxmox/ceph-pool-settle.ts +70 -0
- package/src/proxmox/ceph-pool.ts +212 -0
- package/src/proxmox/client.test.ts +185 -0
- package/src/proxmox/client.ts +240 -0
- package/src/proxmox/credentials.test.ts +144 -0
- package/src/proxmox/credentials.ts +254 -0
- package/src/proxmox/firewall-alias-form.ts +112 -0
- package/src/proxmox/firewall-alias.ts +208 -0
- package/src/proxmox/generated/pbs.ts +461 -0
- package/src/proxmox/generated/pve.ts +7344 -0
- package/src/proxmox/group.ts +199 -0
- package/src/proxmox/ha-resource.ts +169 -0
- package/src/proxmox/ha-rule-form.ts +185 -0
- package/src/proxmox/ha-rule.ts +240 -0
- package/src/proxmox/index.ts +42 -0
- package/src/proxmox/lease-cache.test.ts +163 -0
- package/src/proxmox/lease-cache.ts +131 -0
- package/src/proxmox/lxc.ts +117 -0
- package/src/proxmox/members.test.ts +54 -0
- package/src/proxmox/members.ts +174 -0
- package/src/proxmox/metric-server-form.ts +128 -0
- package/src/proxmox/metric-server-otel.ts +99 -0
- package/src/proxmox/metric-server.ts +214 -0
- package/src/proxmox/network-apply-read.ts +228 -0
- package/src/proxmox/network-apply.ts +244 -0
- package/src/proxmox/node-network-form.ts +213 -0
- package/src/proxmox/node-network.ts +243 -0
- package/src/proxmox/notification-target-form.ts +104 -0
- package/src/proxmox/notification-target.ts +222 -0
- package/src/proxmox/pbs-datastore-form.ts +248 -0
- package/src/proxmox/pbs-datastore-guard.ts +105 -0
- package/src/proxmox/pbs-datastore.ts +271 -0
- package/src/proxmox/pbs-prune-job-form.ts +208 -0
- package/src/proxmox/pbs-prune-job.ts +227 -0
- package/src/proxmox/pbs-sync-job-form.ts +234 -0
- package/src/proxmox/pbs-sync-job.ts +258 -0
- package/src/proxmox/pbs-verify-job-form.ts +131 -0
- package/src/proxmox/pbs-verify-job.ts +227 -0
- package/src/proxmox/pool.test.ts +42 -0
- package/src/proxmox/pool.ts +80 -0
- package/src/proxmox/qemu.ts +89 -0
- package/src/proxmox/replication-job-form.ts +89 -0
- package/src/proxmox/replication-job.ts +248 -0
- package/src/proxmox/resource.ts +257 -0
- package/src/proxmox/role.ts +161 -0
- package/src/proxmox/sdn-apply-read.test.ts +152 -0
- package/src/proxmox/sdn-apply-read.ts +189 -0
- package/src/proxmox/sdn-apply.ts +154 -0
- package/src/proxmox/sdn-subnet-form.ts +156 -0
- package/src/proxmox/sdn-subnet.ts +238 -0
- package/src/proxmox/sdn-vnet.ts +228 -0
- package/src/proxmox/sdn-zone.ts +246 -0
- package/src/proxmox/storage.ts +245 -0
- package/src/proxmox/user.ts +214 -0
- package/src/proxmox/values.test.ts +193 -0
- package/src/proxmox/values.ts +203 -0
- package/src/proxmox/zfs-pool-write.ts +130 -0
- package/src/proxmox/zfs-pool.ts +253 -0
- package/src/talos/credentials.ts +136 -0
- package/src/talos/index.ts +14 -0
- package/src/talos/kubeconfig.ts +170 -0
- package/src/talos/resource.ts +31 -0
- package/src/talos/talos-bootstrap.ts +109 -0
- package/src/talos/talos-cluster-health.ts +119 -0
- package/src/talos/talos-machine-config.ts +146 -0
- package/src/talos/talosctl.ts +94 -0
- package/src/talos/values.test.ts +80 -0
- package/src/talos/values.ts +70 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A PBS datastore on the wire: the transport that reaches PBS, the form it wants, and the coercion
|
|
3
|
+
* back to something comparable.
|
|
4
|
+
*
|
|
5
|
+
* ★ SPLIT OUT OF pbs-datastore.ts FOR THE 250-LINE CAP, the seam notification-target-form.ts uses:
|
|
6
|
+
* this file is everything that touches PBS's encoding, and the resource file is the declaration
|
|
7
|
+
* and the five handlers. The `import type` back is erased, so it is a cycle on paper only.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ THIS FILE IS OVER THE 250-LINE CAP AND THE EXTRACTION IS NAMED RATHER THAN FUDGED. `PbsTarget`,
|
|
10
|
+
* `PbsRole`, `PbsError`, `pbsAuthorization`, `PveForm`, `encode`, `pbs` and `settle` — about 150
|
|
11
|
+
* lines — are a PBS CLIENT, not a datastore form, and belong in `pbs-client.ts`. Lifting them
|
|
12
|
+
* leaves this file near 200 and costs nothing but a move. It was not done here only because the
|
|
13
|
+
* family was commissioned as a two-file split; ceph-pool is the in-package precedent for three
|
|
14
|
+
* (ceph-pool.ts + ceph-pool-form.ts + ceph-pool-settle.ts, 532 lines for one family). Everything
|
|
15
|
+
* in that list is also what Pbs.SyncJob, Pbs.PruneJob and Pbs.Remote will each need unchanged,
|
|
16
|
+
* so the second PBS family forces the move anyway. ⛔ DO NOT MEET THE CAP BY CUTTING COMMENTS.
|
|
17
|
+
*/
|
|
18
|
+
import * as Effect from 'effect/Effect';
|
|
19
|
+
import { type PveForm } from './client.ts';
|
|
20
|
+
import type { PbsTarget as ApiPbsTarget } from './credentials.ts';
|
|
21
|
+
import type { PbsDatastoreProps } from './pbs-datastore.ts';
|
|
22
|
+
/**
|
|
23
|
+
* ★ PBS REUSES THE PVE CLIENT RATHER THAN OWNING ONE. An earlier draft of this file carried its
|
|
24
|
+
* own `PbsTarget`, `PbsError`, `pbsAuthorization`, form encoder and `pve()` — a near-copy of
|
|
25
|
+
* client.ts differing in one header. They are gone: `PveTarget` now carries a `scheme`, and
|
|
26
|
+
* `authorization()` spells the header `PBSAPIToken=<id>:<secret>` when it is `'pbs'`. The
|
|
27
|
+
* insight that survived is the FORM TYPE — PBS decodes a multi-valued field from repeated keys
|
|
28
|
+
* where PVE wants a comma string — and that now lives on `PveForm` in client.ts, so both
|
|
29
|
+
* products share one encoder and neither can reach for the other's convention by accident.
|
|
30
|
+
*
|
|
31
|
+
* 🔴 THIS ALIAS SAID `= PveTarget` AND IT WAS WRONG — Pbs.Datastore REQUIRED A PVE TARGET. When
|
|
32
|
+
* `credentials.ts` grew a real `PbsTarget` with `scheme: 'pbs'`, this line was left pointing at
|
|
33
|
+
* the PVE type, which pins `scheme: 'pve'`. So the one family that most needs the discriminant
|
|
34
|
+
* was the one family that refused the correct value: declaring a datastore against the PBS host
|
|
35
|
+
* failed to compile with `Type '"pbs"' is not assignable to type 'Input<"pve">'`, and passing a
|
|
36
|
+
* PVE target compiled fine and would have 401'd every call — which `read` folds into "absent",
|
|
37
|
+
* so the plan would say CREATE for a datastore holding the estate's backups.
|
|
38
|
+
* ⛔ IT SURVIVED BECAUSE NOTHING EVER DECLARED ONE. The provider was registered in
|
|
39
|
+
* alchemy.run.ts from the day it was written and `pveHandlers`' `list` answers empty, so
|
|
40
|
+
* registration exercises no call site at all. Three sibling families import `PbsTarget` from
|
|
41
|
+
* credentials.ts directly and were always correct; only the one with its own alias drifted.
|
|
42
|
+
* ★ THE LESSON, WHICH IS NOT ABOUT THIS TYPE: a provider nobody has declared an instance of is
|
|
43
|
+
* not tested by anything, including tsc.
|
|
44
|
+
*/
|
|
45
|
+
export type PbsTarget = ApiPbsTarget;
|
|
46
|
+
export interface PbsDatastoreAttributes {
|
|
47
|
+
name: string;
|
|
48
|
+
/**
|
|
49
|
+
* ⚠️ READ FROM PBS, NEVER ECHOED FROM PROPS. Echoing the declared path would make a divergent or
|
|
50
|
+
* unreadable datastore look like agreement, and this is the field whose divergence means the
|
|
51
|
+
* backups are going somewhere nobody declared. `''` when PBS did not say.
|
|
52
|
+
*/
|
|
53
|
+
path: string;
|
|
54
|
+
comment: string;
|
|
55
|
+
'gc-schedule': string;
|
|
56
|
+
'prune-schedule': string;
|
|
57
|
+
'keep-last': number;
|
|
58
|
+
'keep-hourly': number;
|
|
59
|
+
'keep-daily': number;
|
|
60
|
+
'keep-weekly': number;
|
|
61
|
+
'keep-monthly': number;
|
|
62
|
+
'keep-yearly': number;
|
|
63
|
+
/** Normalised property strings — sorted and canonicalised, so key order is never a diff. */
|
|
64
|
+
notify: string;
|
|
65
|
+
'notify-user': string;
|
|
66
|
+
'verify-new': boolean;
|
|
67
|
+
tuning: string;
|
|
68
|
+
'maintenance-mode': string;
|
|
69
|
+
/** ⚠️ Create-only. Canonicalised property string; `''` on a plain local datastore. */
|
|
70
|
+
backend: string;
|
|
71
|
+
'counter-reset-schedule': string;
|
|
72
|
+
'notification-thresholds': string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* ⚠️ `digest` IS DELIBERATELY NOT AN ATTRIBUTE, for storage.ts's reason exactly: PBS returns one on
|
|
76
|
+
* every read and it covers `datastore.cfg` as a FILE, not this section of it, so keeping it would
|
|
77
|
+
* rewrite this resource's state whenever an unrelated datastore was edited — churn that reads
|
|
78
|
+
* like drift. It is not sent on writes either, so this family never joins PBS's optimistic-locking
|
|
79
|
+
* dance and last write wins, as everywhere else in this package.
|
|
80
|
+
*/
|
|
81
|
+
export declare const attributes: (live: Record<string, unknown>, props: PbsDatastoreProps) => PbsDatastoreAttributes;
|
|
82
|
+
/**
|
|
83
|
+
* ⛔ DECLARING WHAT IS LIVE MUST PLAN noop, AND EVERY OMISSION IS DELIBERATE. Out: `name` (the
|
|
84
|
+
* address the read was made at — true by construction, never a diff); `path` (create-only, so
|
|
85
|
+
* comparing it could only plan an update no PUT can apply — pbs-datastore.ts guards it by dying
|
|
86
|
+
* instead, see the ⛔ on the prop); `digest` (not an attribute, above); and every field the
|
|
87
|
+
* declaration does not mention, because undeclared is UNMANAGED here.
|
|
88
|
+
* ⚠️ A SCHEDULE THAT NEVER SETTLES MEANS PBS NORMALISED IT. Calendar events are compared as trimmed
|
|
89
|
+
* strings; if a version rewrites `sat 18:15` as `Sat 18:15` the plan asks for the same update
|
|
90
|
+
* forever. The fix is to declare the spelling PBS stores, never to stop comparing it.
|
|
91
|
+
*/
|
|
92
|
+
export declare const matches: (live: PbsDatastoreAttributes, props: PbsDatastoreProps) => boolean;
|
|
93
|
+
/**
|
|
94
|
+
* ⚠️ `path` IS SENT ONCE, HERE, AND NEVER AGAIN — the fact the whole `path` guard hangs off.
|
|
95
|
+
* ⚠️ `reuse-datastore` IS NOT SENT, AND THE REFUSAL IT CAUSES IS A FEATURE: without it PBS refuses
|
|
96
|
+
* a create against a directory that already holds a chunk store, which is the difference between
|
|
97
|
+
* building a new store and silently adopting somebody else's backups. Adopt out of band.
|
|
98
|
+
* ⚠️ NEITHER IS `backing-device`. A removable datastore's UUID is a property of the hardware in the
|
|
99
|
+
* slot, not of the declaration; one that could re-point it is one that can send backups to the
|
|
100
|
+
* wrong drive.
|
|
101
|
+
*/
|
|
102
|
+
export declare const createForm: (props: PbsDatastoreProps) => PveForm;
|
|
103
|
+
/**
|
|
104
|
+
* ⛔ THERE IS NO `delete` LIST HERE, AND THAT IS A CHOICE ABOUT BACKUPS RATHER THAN AN OMISSION.
|
|
105
|
+
* sdn-vnet.ts derives one from absent props, which is right for an alias and wrong for a
|
|
106
|
+
* retention policy: a declaration that simply fails to mention `keep-daily` would strip it, and a
|
|
107
|
+
* prune job whose keeps have all been stripped is one PBS version away from a prune job that
|
|
108
|
+
* keeps nothing. So undeclared is UNMANAGED, as in storage.ts and backup-job.ts.
|
|
109
|
+
* ⚠️ THE PRICE IS THAT DROPPING A LINE DOES NOT CLEAR A FIELD. Clear one out of band:
|
|
110
|
+
* `proxmox-backup-manager datastore update <name> --delete keep-daily`. It does not cost a
|
|
111
|
+
* forever-diff, because an undeclared field is not compared either.
|
|
112
|
+
* ⛔ WHEN SOMEBODY ADDS ONE, IT TAKES TWO STEPS AND SKIPPING EITHER IS A SILENT BUG. First build
|
|
113
|
+
* it with `values.withClears`, which drops a cleared key from the body — CLEAR WINS — because
|
|
114
|
+
* PBS applies its deletes BEFORE its sets, so a key both set and deleted keeps its value: the
|
|
115
|
+
* declaration would say "unset this", the PUT would succeed, and the next plan would ask for
|
|
116
|
+
* the very same update forever. (PVE instead DIES on that pair; the two behaviours want the
|
|
117
|
+
* same guard.) Second, split the comma-joined value `withClears` produces into a real array —
|
|
118
|
+
* PBS's `delete` is `type: array` and wants REPEATED KEYS, and a comma string arrives as one
|
|
119
|
+
* element that fails the enum check. `PveForm` and `encode` already carry arrays for exactly
|
|
120
|
+
* this. ⚠️ REASONED FROM THE PUBLISHED SCHEMA, NOT PROVEN ON A WIRE.
|
|
121
|
+
*/
|
|
122
|
+
export declare const updateForm: (props: PbsDatastoreProps) => PveForm;
|
|
123
|
+
export declare const object: (props: PbsDatastoreProps) => string;
|
|
124
|
+
/**
|
|
125
|
+
* The live datastore, or undefined.
|
|
126
|
+
*
|
|
127
|
+
* ⛔ `name` IS THE PRESENCE TEST, NOT THE STATUS CODE — PBS does not answer 404 for a section that
|
|
128
|
+
* is missing from `datastore.cfg`, it answers a generic error, so there is no code to key off.
|
|
129
|
+
* ⛔ A MISSING READ PRIVILEGE THEREFORE LOOKS EXACTLY LIKE ABSENCE, because the fold below cannot
|
|
130
|
+
* tell them apart. The signature is a plan that says CREATE followed by PBS answering
|
|
131
|
+
* "datastore '<name>' already exists." — that pair means Datastore.Audit, not a missing
|
|
132
|
+
* datastore. It is loud and non-destructive, which is the only reason the fold is tolerable here;
|
|
133
|
+
* see the ⛔ on `readRole` in resource.ts for the PVE families where it is not.
|
|
134
|
+
* ⛔ ON THE DELETE PATH THE SAME FOLD IS A LIE, AND THIS IS THE ONE HAZARD IN THIS FAMILY I COULD
|
|
135
|
+
* NOT CLOSE. `delete` settles by polling this read until it answers `undefined` — and a `read`
|
|
136
|
+
* role that lacks `Datastore.Audit` answers `undefined` from the very first poll, so EVERY delete
|
|
137
|
+
* would report success while the datastore stood. Worse, the two lanes are different leases: the
|
|
138
|
+
* DELETE goes out under `provision` and may genuinely fail, and this read cannot see that it did.
|
|
139
|
+
* ★ THE SHAPE OF THE FIX IS ALREADY IN THIS PACKAGE: `confirmAbsent` in ceph-pool-settle.ts asks
|
|
140
|
+
* a SECOND, DIFFERENT question — does the collection LIST this object? — and lets a failure
|
|
141
|
+
* PROPAGATE rather than folding it, so "I could not ask" fails the deploy instead of passing
|
|
142
|
+
* it. Add the same over `GET /config/datastore` before anyone relies on a reported deletion.
|
|
143
|
+
* ⚠️ UNTIL THEN THE PRECONDITION IS EXPLICIT: the `read` role MUST hold `Datastore.Audit` on the
|
|
144
|
+
* datastore, or this provider cannot tell a deletion from a refusal.
|
|
145
|
+
*/
|
|
146
|
+
export declare const readOne: (props: PbsDatastoreProps) => Effect.Effect<PbsDatastoreAttributes | undefined, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
147
|
+
//# sourceMappingURL=pbs-datastore-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pbs-datastore-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-datastore-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,OAAO,EAAO,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,IAAI,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAG5D;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC;AAErC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,4FAA4F;IAC5F,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB,EAAE,MAAM,CAAC;IAC3B,sFAAsF;IACtF,OAAO,EAAE,MAAM,CAAC;IAChB,wBAAwB,EAAE,MAAM,CAAC;IACjC,yBAAyB,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,SACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SACtB,iBAAiB,KACvB,sBAoBD,CAAC;AAIH;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,SAAU,sBAAsB,SAAS,iBAAiB,YAkBK,CAAC;AAqCpF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,UAAW,iBAAiB,KAAG,OASpD,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,UAAW,iBAAiB,KAAG,OAAyB,CAAC;AAEhF,eAAO,MAAM,MAAM,UAAW,iBAAiB,WAAqC,CAAC;AAErF;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,eAAO,MAAM,OAAO,UAAW,iBAAiB,mHAM7C,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two things a datastore write needs that are not form-building: refusing a moved `path`, and
|
|
3
|
+
* waiting for PBS to finish.
|
|
4
|
+
*
|
|
5
|
+
* ★ SPLIT OUT OF pbs-datastore-form.ts FOR THE 250-LINE CAP, and the seam is real. That file turns
|
|
6
|
+
* a declaration into a form; this one is the SAFETY GUARD and the POLLING. A reader asking "can
|
|
7
|
+
* this destroy my backups" has one short file to read rather than a long one to search.
|
|
8
|
+
*/
|
|
9
|
+
import * as Effect from 'effect/Effect';
|
|
10
|
+
import { type PbsDatastoreAttributes } from './pbs-datastore-form.ts';
|
|
11
|
+
import type { PbsDatastoreProps } from './pbs-datastore.ts';
|
|
12
|
+
/**
|
|
13
|
+
* ⛔ THE `path` GUARD, YIELDED FROM BOTH `diff` AND `reconcile`. Both need it: `diff` catches a
|
|
14
|
+
* changed declaration, and `reconcile` catches an ADOPTION, which Alchemy routes straight past
|
|
15
|
+
* `diff` (Apply.ts:980, :1045). `''` is skipped — an unreadable path is not a divergent one.
|
|
16
|
+
*/
|
|
17
|
+
export declare const guardPath: (live: PbsDatastoreAttributes, props: PbsDatastoreProps) => Effect.Effect<void, never, never>;
|
|
18
|
+
/**
|
|
19
|
+
* ⛔ THE `backend` GUARD, AND IT MATTERS MORE THAN `path`. A wrong `path` sends backups to the
|
|
20
|
+
* wrong directory on the same machine; a wrong `backend` is the difference between a datastore
|
|
21
|
+
* on local disk and one in a Cloudflare R2 bucket. MEASURED on 4.2: `datastore create` takes
|
|
22
|
+
* `--backend`, `datastore update` does not, and `backend` is not in update's `--delete` enum —
|
|
23
|
+
* so divergence CANNOT be reconciled, and the only honest options are to refuse or to lie.
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ AN UNDECLARED `backend` IS NOT A DIVERGENCE. Undeclared is unmanaged everywhere in this
|
|
26
|
+
* family, and this guard keeps that rule: it fires only when the declaration says something and
|
|
27
|
+
* PBS says something else. Adopting `r2-offsite` without mentioning `backend` still plans noop.
|
|
28
|
+
*
|
|
29
|
+
* ⚠️ COMPARED AS A CANONICAL PROPERTY STRING, so `bucket=x,type=s3` and `type=s3,bucket=x` are the
|
|
30
|
+
* same backend and key order cannot deadlock a plan — the reason `samePath` trims a slash.
|
|
31
|
+
*/
|
|
32
|
+
export declare const guardBackend: (live: PbsDatastoreAttributes, props: PbsDatastoreProps) => Effect.Effect<void, never, never>;
|
|
33
|
+
/**
|
|
34
|
+
* Wait for PBS to finish, then answer the last thing it said.
|
|
35
|
+
*
|
|
36
|
+
* ⛔ IT EXISTS BECAUSE A PBS CREATE IS A FORKED WORKER AND THE CONFIG SECTION IS WRITTEN LAST.
|
|
37
|
+
* `POST /config/datastore` answers with a UPID while a `create-datastore` task builds the chunk
|
|
38
|
+
* store's 65536 directories; only when that finishes does the section reach `datastore.cfg`. An
|
|
39
|
+
* immediate read-back therefore sees a datastore that is not there yet, and the factory's "the
|
|
40
|
+
* write returned no error but the object is still absent" would fire on a create going perfectly.
|
|
41
|
+
* The DELETE forks a worker too. ⚠️ DOCUMENTED FROM THE PUBLISHED SCHEMA, NOT MEASURED — I hold
|
|
42
|
+
* no PBS token. If a create turns out to be synchronous, the first poll simply succeeds.
|
|
43
|
+
* ★ IT TAKES THE READ AS AN EFFECT RATHER THAN A FUNCTION — the one simplification over
|
|
44
|
+
* ceph-pool-settle.ts: an Effect is a description, so re-running it re-reads and no dependency
|
|
45
|
+
* has to be threaded back in to avoid a cycle.
|
|
46
|
+
* ⚠️ 2s APART, 60 TRIES, ABOUT TWO MINUTES — longer than ceph-pool's minute, and REASONED rather
|
|
47
|
+
* than measured: 65536 `mkdir`s is seconds on NVMe and can be a minute on a slow or USB disk.
|
|
48
|
+
* Anything still unsettled after two minutes is a fault to surface, not a wait to lengthen, and
|
|
49
|
+
* the caller's message points at the task log, which is where the real answer is. `Effect.sleep`
|
|
50
|
+
* yields between polls; nothing here parks a call on one long wait.
|
|
51
|
+
* ⚠️ POLLING THE TASK STATUS INSTEAD WOULD BE WORSE, for ceph-pool-settle.ts's reason: every call
|
|
52
|
+
* mints a FRESH token, so the token asking about the task is not the one that started it.
|
|
53
|
+
*/
|
|
54
|
+
export declare const settle: <A, E, R>(read: Effect.Effect<A, E, R>, done: (live: A) => boolean) => Effect.Effect<A, E, R>;
|
|
55
|
+
//# sourceMappingURL=pbs-datastore-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pbs-datastore-guard.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-datastore-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,sBAAsB,EAAU,MAAM,yBAAyB,CAAC;AAC9E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAmB5D;;;;GAIG;AACH,eAAO,MAAM,SAAS,SAAU,sBAAsB,SAAS,iBAAiB,sCAWzE,CAAC;AAER;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,YAAY,SAAU,sBAAsB,SAAS,iBAAiB,sCAY5E,CAAC;AAER;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,OAAO,2BAQpF,CAAC"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Pbs.Datastore` — one section of Proxmox Backup Server's `datastore.cfg`, declared.
|
|
3
|
+
*
|
|
4
|
+
* ★ PBS IS A SIBLING OF PVE, NOT A PART OF IT. The estate runs both — PVE carries a storage of
|
|
5
|
+
* `type: pbs` — so declaring the datastore turns `Proxmox.Storage`'s `locator.datastore` from a
|
|
6
|
+
* string nothing checks into a reference Alchemy can order, as `Proxmox.SdnVnet` did for a bridge.
|
|
7
|
+
*
|
|
8
|
+
* ⛔ DIFFERENT HOST, DIFFERENT REALM, DIFFERENT AUTH HEADER. MEASURED 2026-09-13, strict TLS, no
|
|
9
|
+
* `-k` anywhere: `GET https://pbs.example.com:8007/api2/json/ping` answers 200
|
|
10
|
+
* `{"data":{"pong":true}}` against a Let's Encrypt certificate for CN=pbs.example.com;
|
|
11
|
+
* `/version` and `/config/datastore` answer 401 "authentication failed";
|
|
12
|
+
* `pbs.mgmt.example.com:8007` answers 401 and its certificate verifies too; and
|
|
13
|
+
* `/access/domains` lists the realms `pbs`, `pam` and an openid realm `Schenanigans`. So the
|
|
14
|
+
* prefix, the `{"data": …}` envelope and the strict-TLS story are PVE's exactly — and only the
|
|
15
|
+
* credential differs. There is no `pve` realm here; a PVE token cannot authenticate at all.
|
|
16
|
+
* The header differs too, which is why `pve()` exists rather than `pve()` being reused — see the
|
|
17
|
+
* ⛔ on `pbsAuthorization` in pbs-datastore-form.ts.
|
|
18
|
+
*
|
|
19
|
+
* ⛔ THERE IS NO OPENBAO MOUNT FOR PBS TODAY, SO NOTHING HERE CAN DEPLOY YET. `PbsTarget.mount` is
|
|
20
|
+
* a parameter for the reason `PveTarget.mount` is — the provider must be able to leave this
|
|
21
|
+
* estate — but the estate has nothing to put in it. MEASURED: under the `claude-code` approle,
|
|
22
|
+
* `bao token capabilities` answers `deny` for `proxmox-tb4/creds/{read,provision}`,
|
|
23
|
+
* `proxmox-ops/creds/read` and every `…pbs…` path tried, and `sys/mounts` is 403 — so I could
|
|
24
|
+
* not enumerate the mounts to PROVE a PBS one is absent. That it is absent is the brief's
|
|
25
|
+
* statement, not my measurement.
|
|
26
|
+
* ★ WHAT THE MOUNT MUST VEND, so `mint()` is reused unchanged: `bao read -format=json
|
|
27
|
+
* <mount>/creds/<role>` answering `{"data":{"token_id":…,"secret":…}}`, where `token_id` is a
|
|
28
|
+
* PBS token id `user@realm!tokenname` — `hf-read@pbs!…` and `hf-provision@pbs!…`. Same short,
|
|
29
|
+
* non-renewable lease contract as `proxmox-tb4`.
|
|
30
|
+
* ⚠️ PBS HAS NO DYNAMIC-SECRETS PLUGIN OF ITS OWN — OpenBao's Proxmox support is a PVE thing — so
|
|
31
|
+
* the mount is a small custom vendor against `POST /access/users/{id}/token/{name}`. Say which
|
|
32
|
+
* mount and role you needed rather than reaching for the read-only `monitoring@pbs` token on
|
|
33
|
+
* the kv shelf: widening that would delete the outer lock for every reader of the shelf.
|
|
34
|
+
*
|
|
35
|
+
* ⛔ A DATASTORE HOLDS THE BACKUPS, SO `retain` IS THE DEFAULT AND `delete` IS FULLY IMPLEMENTED —
|
|
36
|
+
* the ★ on removal policy in resource.ts has the whole reasoning. A stub returning `Effect.void`
|
|
37
|
+
* would report a deletion that never happened, and this is the family where that lie costs most.
|
|
38
|
+
*
|
|
39
|
+
* ⚠️ PRIVILEGES, READ OFF THE PUBLISHED SCHEMA RATHER THAN OFF THIS HOST:
|
|
40
|
+
* read GET config/datastore/{name} Datastore.Audit on /datastore/{name}
|
|
41
|
+
* create POST config/datastore Datastore.Allocate on /datastore
|
|
42
|
+
* update PUT config/datastore/{name} Datastore.Modify on /datastore/{name}
|
|
43
|
+
* delete DELETE config/datastore/{name} Datastore.Allocate on /datastore/{name}
|
|
44
|
+
* Built-in `DatastoreAdmin` at `/datastore` covers all four; `DatastoreAudit` covers the read
|
|
45
|
+
* lane alone, which is what the `read` role should hold.
|
|
46
|
+
*
|
|
47
|
+
* ⛔ NO SECRET IS A PROP OR AN ATTRIBUTE, AND THIS FAMILY IS CLEAN BY CONSTRUCTION: a datastore
|
|
48
|
+
* section holds no password, token or key — encryption keys belong to the backup CLIENT. Every
|
|
49
|
+
* field below is a name, a path, a schedule, a count or a policy string, all of which are safe in
|
|
50
|
+
* a state store Alchemy writes UNENCRYPTED and this estate dumps to CT100 nightly.
|
|
51
|
+
*/
|
|
52
|
+
import { Resource } from 'alchemy';
|
|
53
|
+
import * as Provider from 'alchemy/Provider';
|
|
54
|
+
import { type PbsDatastoreAttributes, type PbsTarget } from './pbs-datastore-form.ts';
|
|
55
|
+
import type { PveRequirements } from './resource.ts';
|
|
56
|
+
export type { PbsDatastoreAttributes, PbsTarget };
|
|
57
|
+
/**
|
|
58
|
+
* ⚠️ PBS'S OWN KEY NAMES, HYPHENS INCLUDED — backup-job.ts's rule: the form is then a copy rather
|
|
59
|
+
* than a translation table, and a translation table is one more place for a key to be renamed and
|
|
60
|
+
* silently never sent.
|
|
61
|
+
* ⚠️ AN UNDECLARED FIELD IS NEITHER SENT NOR COMPARED — storage.ts's rule, not sdn-vnet.ts's, and
|
|
62
|
+
* for a retention policy the choice is not close: see the ⛔ on `updateForm`.
|
|
63
|
+
*/
|
|
64
|
+
export interface PbsDatastoreProps {
|
|
65
|
+
target: PbsTarget;
|
|
66
|
+
/** PBS's primary key. Create-only: there is no rename, and a new name is a new datastore. */
|
|
67
|
+
name: string;
|
|
68
|
+
/**
|
|
69
|
+
* Absolute path to the datastore directory.
|
|
70
|
+
*
|
|
71
|
+
* ⛔ CREATE-ONLY, AND THE ONE FIELD THIS FILE REFUSES TO LET DRIFT QUIETLY. PBS's update endpoint
|
|
72
|
+
* does not accept `path`, so comparing it in `matches` could only plan an update no PUT can
|
|
73
|
+
* apply — the forever-diff storage.ts avoids by letting its create-only `type` plan as NOOP.
|
|
74
|
+
* Here a silent noop is the wrong trade: the operator would read a green plan while PBS kept
|
|
75
|
+
* writing backups to the old directory. So `diff` and `reconcile` compare it themselves and DIE
|
|
76
|
+
* with both values named. A path change is remove-and-redeclare, deliberately.
|
|
77
|
+
*/
|
|
78
|
+
path: string;
|
|
79
|
+
comment?: string;
|
|
80
|
+
/** Calendar event for garbage collection, e.g. `daily` or `sat 18:15`. */
|
|
81
|
+
'gc-schedule'?: string;
|
|
82
|
+
/** Calendar event for the built-in prune job. Retention itself is the `keep-*` fields below. */
|
|
83
|
+
'prune-schedule'?: string;
|
|
84
|
+
/** ⚠️ PBS's minimum is 1 for every keep-*. A declared `0` is refused, and does not mean "off". */
|
|
85
|
+
'keep-last'?: number;
|
|
86
|
+
'keep-hourly'?: number;
|
|
87
|
+
'keep-daily'?: number;
|
|
88
|
+
'keep-weekly'?: number;
|
|
89
|
+
'keep-monthly'?: number;
|
|
90
|
+
'keep-yearly'?: number;
|
|
91
|
+
/**
|
|
92
|
+
* Legacy notification routing: `gc=<never|always|error>`, and the same for `verify` and `sync`.
|
|
93
|
+
* ⚠️ PBS 3.2+ picks between this and the notification system with `notification-mode`, which is
|
|
94
|
+
* NOT declarable here: flipping a live datastore between the two silently changes who hears
|
|
95
|
+
* about a failed verify, and silence is the failure mode that costs most (notification-target.ts).
|
|
96
|
+
*/
|
|
97
|
+
notify?: string;
|
|
98
|
+
/** PBS user id legacy notifications go to. PBS's schema default is `root@pam`. */
|
|
99
|
+
'notify-user'?: string;
|
|
100
|
+
/** Verify every new backup the moment it completes. PBS's default is false. */
|
|
101
|
+
'verify-new'?: boolean;
|
|
102
|
+
/** Property string: `chunk-order=<none|inode>,sync-level=<none|file|filesystem>`, and more. */
|
|
103
|
+
tuning?: string;
|
|
104
|
+
/**
|
|
105
|
+
* Takes the datastore out of service: `type=<offline|read-only|unmount>`, optional
|
|
106
|
+
* `message=<text>`. PBS's default key is `type`, so the bare `offline` normalises to the same
|
|
107
|
+
* string here and either spelling is safe.
|
|
108
|
+
* ⛔ NEVER DECLARE `type=delete`. PBS sets that itself while destroying a datastore's contents.
|
|
109
|
+
* ⚠️ A `message` CONTAINING A COMMA BREAKS THE COMPARISON, not the write: `propertyString` splits
|
|
110
|
+
* on commas and does not honour PBS's quoting, so the two sides never compare equal and the
|
|
111
|
+
* plan asks for the same update forever. Keep the message comma-free.
|
|
112
|
+
*/
|
|
113
|
+
'maintenance-mode'?: string;
|
|
114
|
+
/**
|
|
115
|
+
* ⛔ CREATE-ONLY, LIKE `path`, AND FOR A SHARPER REASON. MEASURED on the live 4.2 host:
|
|
116
|
+
* `datastore create --help` offers `--backend`, `datastore update --help` does NOT, and
|
|
117
|
+
* `backend` is absent from update's `--delete` enum. PBS will not move a datastore between
|
|
118
|
+
* local disk and object storage, ever.
|
|
119
|
+
* ★ IT IS A PROP AT ALL BECAUSE THE ESTATE HAS ONE. `r2-offsite` holds
|
|
120
|
+
* `type=s3,client=cloudflare-r2,bucket=homeflare-pbs`. Leaving it undeclared would still plan
|
|
121
|
+
* noop — undeclared is unmanaged here — but the declaration would then describe a LOCAL
|
|
122
|
+
* datastore, and anyone recreating from it would get exactly that: an empty directory on the
|
|
123
|
+
* mini's disk where the offsite copy used to be, behind a green plan the whole way.
|
|
124
|
+
* ⚠️ A property string. Compared canonically, so key order is never a diff.
|
|
125
|
+
*/
|
|
126
|
+
backend?: string;
|
|
127
|
+
/** ⚠️ S3 request counters reset on this calendar event. Mutable; in update's `--delete` enum. */
|
|
128
|
+
'counter-reset-schedule'?: string;
|
|
129
|
+
/**
|
|
130
|
+
* ⚠️ S3 request-count thresholds that raise a notification, e.g. `s3-put=700000,s3-get=200000`.
|
|
131
|
+
* A property string, compared canonically. Mutable.
|
|
132
|
+
*/
|
|
133
|
+
'notification-thresholds'?: string;
|
|
134
|
+
}
|
|
135
|
+
export interface PbsDatastore extends Resource<'Pbs.Datastore', PbsDatastoreProps, PbsDatastoreAttributes, never, PveRequirements> {
|
|
136
|
+
}
|
|
137
|
+
/** ★ `retain` by default — a datastore holds the backups. See the ★ in resource.ts. */
|
|
138
|
+
export declare const PbsDatastore: import("alchemy").ResourceClass<PbsDatastore>;
|
|
139
|
+
export declare const PbsDatastoreProvider: () => import("effect/Layer").Layer<Provider.Provider<PbsDatastore>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
140
|
+
//# sourceMappingURL=pbs-datastore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pbs-datastore.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-datastore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,SAAS,EAMf,MAAM,yBAAyB,CAAC;AAEjC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,YAAY,EAAE,sBAAsB,EAAE,SAAS,EAAE,CAAC;AAElD;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,SAAS,CAAC;IAClB,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;;;OASG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0EAA0E;IAC1E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gGAAgG;IAChG,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kFAAkF;IAClF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,+FAA+F;IAC/F,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;OAQG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iGAAiG;IACjG,wBAAwB,CAAC,EAAE,MAAM,CAAC;IAClC;;;OAGG;IACH,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED,MAAM,WAAW,YAAa,SAAQ,QAAQ,CAC5C,eAAe,EACf,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,uFAAuF;AACvF,eAAO,MAAM,YAAY,+CAEvB,CAAC;AAEH,eAAO,MAAM,oBAAoB,kIA4G9B,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A prune job's wire half — and, deliberately, EVERY LINE THAT CAN DELETE A BACKUP.
|
|
3
|
+
*
|
|
4
|
+
* ★ SPLIT OUT OF pbs-prune-job.ts ON THE SEAM replication-job-form.ts USES: that file answers
|
|
5
|
+
* "what is a prune job and when has it changed", this one answers "how does a declaration become
|
|
6
|
+
* a PBS write". The one deliberate departure from that seam is `keepsMatch`, which is diff logic
|
|
7
|
+
* and lives here anyway — see the ★ below, which is the reason the cut falls where it does.
|
|
8
|
+
*
|
|
9
|
+
* ★ THE SIX RETENTION WINDOWS ARE TOUCHED IN THREE PLACES AND ALL THREE ARE IN THIS FILE: sent by
|
|
10
|
+
* `keepForm`, read back by `keepAttributes`, compared by `keepsMatch`. The three must stay in
|
|
11
|
+
* step — a window written but not compared is a diff that never settles, and one compared but not
|
|
12
|
+
* written is an update reported on every plan forever — and this is the half of the resource
|
|
13
|
+
* where being wrong costs backups rather than a confusing plan. Keeping all three on one screen
|
|
14
|
+
* is what makes that review possible in one sitting instead of a grep across two files.
|
|
15
|
+
*
|
|
16
|
+
* ⚠️ THE `import type` BACK TO pbs-prune-job.ts IS A CYCLE ON PAPER ONLY. It is type-only, erased
|
|
17
|
+
* before anything runs, so `PruneJobProps` stays the resource's public shape in the file that
|
|
18
|
+
* declares the resource rather than being moved somewhere odd to dodge the arrow.
|
|
19
|
+
*
|
|
20
|
+
* ⚠️ FORM ENCODING IS THE ONE TRANSPORT ASSUMPTION THIS FAMILY RESTS ON, AND IT IS REASONED RATHER
|
|
21
|
+
* THAN MEASURED. `pveWith` sends `application/x-www-form-urlencoded`; PBS's REST server accepts
|
|
22
|
+
* that alongside JSON and parses each value through the parameter schema, which is how its own
|
|
23
|
+
* ExtJS GUI talks to it. I could not prove it: there is no credential for this host (see the ⛔ in
|
|
24
|
+
* pbs-prune-job.ts) and the only unauthenticated endpoint that would have distinguished a 400
|
|
25
|
+
* "bad parameters" from a 401 is the login form, which is not a thing to poke on a live box. If
|
|
26
|
+
* PBS turns out to want JSON here, every key and value below is still right and only the
|
|
27
|
+
* transport is wrong — VERIFY THIS FIRST, before anything else in these two files.
|
|
28
|
+
*/
|
|
29
|
+
import type { PruneJobAttributes, PruneJobProps } from './pbs-prune-job.ts';
|
|
30
|
+
/**
|
|
31
|
+
* The six retention windows, in PBS's own spelling.
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ ONE LIST, READ THREE TIMES — by `keepForm` on the way to the wire, by `keepsMatch` on the way
|
|
34
|
+
* back, and by `keepsSomething` in the guard. Two hand-kept copies of six key names is how a
|
|
35
|
+
* seventh window gets added to one of them and silently never sent, which for this family means
|
|
36
|
+
* never applied and never noticed until a restore.
|
|
37
|
+
*/
|
|
38
|
+
export declare const KEEP_KEYS: readonly ['keep-last', 'keep-hourly', 'keep-daily', 'keep-weekly', 'keep-monthly', 'keep-yearly'];
|
|
39
|
+
export type KeepKey = (typeof KEEP_KEYS)[number];
|
|
40
|
+
/**
|
|
41
|
+
* The six windows off the wire, where `0` MEANS ABSENT.
|
|
42
|
+
*
|
|
43
|
+
* ⛔ ZERO IS A SENTINEL HERE AND CAN NEVER BE A LIVE VALUE, which is the only reason it is safe to
|
|
44
|
+
* use one. MEASURED from the published PBS schema: every `keep-*` carries `minimum: 1`, so PBS
|
|
45
|
+
* cannot store a zero and cannot be sent one — `keep-daily=0` is a 400, not a wipe. A field that
|
|
46
|
+
* comes back missing is a window that is not part of this job's policy, and `keepsMatch` must be
|
|
47
|
+
* able to tell that apart from "the declaration asked for zero", which it cannot express either.
|
|
48
|
+
*/
|
|
49
|
+
export declare const keepAttributes: (live: Record<string, unknown>) => Pick<PruneJobAttributes, KeepKey>;
|
|
50
|
+
/**
|
|
51
|
+
* ⚠️ AN UNDECLARED WINDOW IS NOT COMPARED, AND THAT IS THE SAFETY MODEL RATHER THAN AN OVERSIGHT.
|
|
52
|
+
* A `keep-weekly` an operator set by hand on an adopted job is not drift this stack repairs —
|
|
53
|
+
* repairing it would mean narrowing retention nobody asked to narrow. The cost is stated rather
|
|
54
|
+
* than hidden: deleting a `keep-daily` line from a declaration plans as a NOOP and changes
|
|
55
|
+
* nothing on the server. See the ⛔ on `updateBody` for why clearing is a hand operation.
|
|
56
|
+
*/
|
|
57
|
+
export declare const keepsMatch: (attributes: Pick<PruneJobAttributes, KeepKey>, props: PruneJobProps) => boolean;
|
|
58
|
+
/**
|
|
59
|
+
* ⛔ `id` IS SENT AND IS NOT OPTIONAL, for backup-job.ts's reason sharpened by this API's shape.
|
|
60
|
+
* MEASURED: `id` is a REQUIRED parameter of `POST /config/prune` — PBS does not generate one —
|
|
61
|
+
* so an omitted id is a 400 rather than a duplicate job. That is the good case; the bad one is
|
|
62
|
+
* the id being WRONG. `pveOperations.read` folds every failed GET into "absent", so a 403 or an
|
|
63
|
+
* expired lease reads as "no such job", the plan says create, and a second prune job appears
|
|
64
|
+
* beside the first with this declaration's retention applied to the same datastore. Two prune
|
|
65
|
+
* jobs on one store do not negotiate: each deletes what its own windows do not keep.
|
|
66
|
+
*/
|
|
67
|
+
export declare const createBody: (props: PruneJobProps) => Record<string, string>;
|
|
68
|
+
/**
|
|
69
|
+
* ⛔ THERE IS NO `delete` LIST HERE AND `withClears()` IS DELIBERATELY NOT IMPORTED. Two
|
|
70
|
+
* independent reasons, either of which alone would be enough.
|
|
71
|
+
*
|
|
72
|
+
* FIRST, THE WIRE SHAPE IS NOT PVE'S. MEASURED from the published PBS schema, `PUT
|
|
73
|
+
* /config/prune/{id}` declares `delete` as `{"type":"array","items":{"type":"string","enum":[
|
|
74
|
+
* "comment","disable","ns","max-depth","keep-last","keep-hourly","keep-daily","keep-weekly",
|
|
75
|
+
* "keep-monthly","keep-yearly"]}}` — an ARRAY of enum members. PVE's `delete` is a comma-joined
|
|
76
|
+
* string, which is exactly what `withClears()` builds, and a form-encoded array is built by
|
|
77
|
+
* REPEATING the key. `delete=comment,disable` is therefore ONE item that is in no enum: a 400,
|
|
78
|
+
* not a clear.
|
|
79
|
+
* ⚠️ AND THE CORRECT SHAPE IS NOT EXPRESSIBLE TODAY. `pveWith` takes `Record<string, string>` and
|
|
80
|
+
* runs it through `new URLSearchParams`, which cannot repeat a key from a plain object. A PBS
|
|
81
|
+
* clear list needs a client that can, and that is a change to client.ts rather than to this
|
|
82
|
+
* file — so a clear list could not be shipped here even if it were wanted.
|
|
83
|
+
*
|
|
84
|
+
* SECOND, AND THIS ONE WOULD STAND ANYWAY: CLEARING A `keep-*` IS THE MOST DESTRUCTIVE THING
|
|
85
|
+
* THIS API CAN DO. Removing `keep-last` from a job that had it does not restore a default — PBS
|
|
86
|
+
* has no default retention — it NARROWS what survives the next run. So an undeclared field is
|
|
87
|
+
* unmanaged, and a window this stack never mentioned is one it never widens and never removes.
|
|
88
|
+
* Dropping a `keep-daily` line from a declaration therefore does NOTHING on purpose; clear it by
|
|
89
|
+
* hand, having first looked at what it is holding.
|
|
90
|
+
*
|
|
91
|
+
* ★ THE DISJOINTNESS `withClears()` EXISTS TO ENFORCE IS THEREFORE VACUOUS HERE: with no clear list
|
|
92
|
+
* there is no key that can be set and deleted in one call, which is the failure it guards. If a
|
|
93
|
+
* later change does add one it must go through `withClears()` AND teach the client to repeat the
|
|
94
|
+
* key — half of that is a 400, and the other half is PVE's spelling sent to a PBS host.
|
|
95
|
+
*/
|
|
96
|
+
export declare const updateBody: (props: PruneJobProps) => Record<string, string>;
|
|
97
|
+
//# sourceMappingURL=pbs-prune-job-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pbs-prune-job-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/pbs-prune-job-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAG5E;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,YACpB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,aAAa,EACb,cAAc,EACd,aAAa,CACL,CAAC;AAEX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAYjD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAOjC,CAAC;AAEH;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,eAAgB,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,SAAS,aAAa,YACR,CAAC;AA0EvF;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,UAAW,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAGtE,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,UAAU,UAAW,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAGtE,CAAC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Pbs.PruneJob` — a Proxmox Backup Server retention schedule, declared. PBS is a SIBLING of PVE,
|
|
3
|
+
* so this family reuses `pveHandlers` wholesale rather than growing a second factory.
|
|
4
|
+
*
|
|
5
|
+
* ⛔ THIS IS THE ONE RESOURCE IN THIS PACKAGE THAT DESTROYS DATA ON AN ORDINARY *UPDATE*. CephPool,
|
|
6
|
+
* ZfsPool and Storage can destroy data too — which is exactly why they default to `retain` and
|
|
7
|
+
* make a caller opt in with `destroy()`, and that guard covers DELETE and nothing else. Here the
|
|
8
|
+
* destruction rides a plain `1 to update` that no removal policy has any opinion about. A wrong
|
|
9
|
+
* `keep-*` does not fail: it succeeds. The PUT returns 200, and the NEXT SCHEDULED RUN — minutes
|
|
10
|
+
* or days later, with nobody watching — removes every snapshot that fell outside the new window.
|
|
11
|
+
* There is no undo, PBS does not ask, and nothing about the deploy looks unusual.
|
|
12
|
+
* ★ CONCRETELY, SO NOBODY HAS TO IMAGINE IT: a live job with `keep-daily 30`, a declaration
|
|
13
|
+
* saying `keep-daily: 7`. `matches` reports an update, the PUT lands, and twenty-three days of
|
|
14
|
+
* daily backups are gone at the next run. The plan showed one word. READ THE `keep-*` HALF OF
|
|
15
|
+
* EVERY PLAN TWICE; it is the only warning there is.
|
|
16
|
+
* ⚠️ THE ONE THING THAT CANNOT HAPPEN IS A SILENT ZERO. MEASURED from the published schema: every
|
|
17
|
+
* `keep-*` carries `minimum: 1`, so a literal `keep-daily=0` is a 400 rather than a wipe. And
|
|
18
|
+
* an UNDECLARED window is not sent as zero either — it is not sent at all, because undeclared
|
|
19
|
+
* means unmanaged here exactly as it does in storage.ts. A window this stack never named is one
|
|
20
|
+
* it never narrows. `0` in the attributes below therefore means ABSENT, never a real setting.
|
|
21
|
+
*
|
|
22
|
+
* ⛔ `max-depth` UNSET IS THE WIDEST SCOPE, NOT THE NARROWEST, AND IT IS THE ONE FIELD WHERE
|
|
23
|
+
* "UNMANAGED" IS NOT THE SAFE ANSWER. MEASURED, quoting the published schema's own description:
|
|
24
|
+
* "0 == no recursion, empty == automatic full recursion". So a job that names an `ns` but leaves
|
|
25
|
+
* `max-depth` alone prunes every namespace BENEATH that one too, with these windows. Declare it.
|
|
26
|
+
* `-1` in the attributes means "absent from prune.cfg", the sentinel storage.ts uses for
|
|
27
|
+
* `maxfiles`, because `0` here is a real and very different setting.
|
|
28
|
+
*
|
|
29
|
+
* ⛔ THE CREDENTIAL DOES NOT EXIST YET, SO NOTHING BELOW HAS EVER MADE A CALL. PBS is a different
|
|
30
|
+
* host and a different auth realm from PVE. MEASURED 2026-09-13: pbs.example.com:8007 answers
|
|
31
|
+
* HTTP 401 under STRICT TLS behind a real Let's Encrypt certificate (CN=pbs.example.com, issuer
|
|
32
|
+
* YR1, valid 2026-09-02 to 2026-12-01) — so there is no `-k` to reach for and `client.ts` needs
|
|
33
|
+
* no TLS escape hatch, exactly as on PVE. What it does need is a header it cannot build today.
|
|
34
|
+
* PBS's documented form is `Authorization: PBSAPIToken=TOKENID:TOKENSECRET` (measured from the
|
|
35
|
+
* published PBS documentation, 2026-09-13) while `authorization()` in credentials.ts builds
|
|
36
|
+
* `PVEAPIToken=<tokenid>=<secret>`: BOTH the scheme and the separator differ. A wrong separator
|
|
37
|
+
* answers 401 "authentication failure", which reads as a bad credential rather than as a
|
|
38
|
+
* malformed header — the same trap credentials.ts already records for PVE's colon form.
|
|
39
|
+
* ⛔ AND THERE IS NO MOUNT TO MINT FROM. `mint()` runs `bao read <mount>/creds/<role>`; no
|
|
40
|
+
* OpenBao mount vends PBS tokens — `proxmox-tb4` and `proxmox-ops` vend PVE ones. The
|
|
41
|
+
* `kv/infra/proxmox` shelf holds a READ-ONLY `Audit` token for PBS, and a read-only token
|
|
42
|
+
* cannot create a prune job. I could not confirm that shelf's contents: this machine's
|
|
43
|
+
* claude-code approle answers 403 on `sys/mounts` AND on `kv/metadata/infra/proxmox`
|
|
44
|
+
* (MEASURED). What is needed is a dynamic PBS mount vending a `provision` role with
|
|
45
|
+
* `Datastore.Modify` — say that, rather than reaching for the stored auditor token.
|
|
46
|
+
*
|
|
47
|
+
* ★ WHAT A `PbsTarget` NEEDS, AND WHY IT IS ALMOST `PveTarget`. Structurally `PveTarget` already
|
|
48
|
+
* fits a PBS host: a credential mount name and an `/api2/json` base are the whole of it, and PBS
|
|
49
|
+
* shares the `{"data": …}` envelope, the four-operation shape and the read-back discipline —
|
|
50
|
+
* which is exactly why this file declares a spec and stops. The single thing `PveTarget` cannot
|
|
51
|
+
* carry is WHICH AUTHORIZATION SCHEME the host at the other end speaks, so `PbsTarget` below adds
|
|
52
|
+
* that and nothing else rather than bending the PVE type in silence.
|
|
53
|
+
*
|
|
54
|
+
* ⚠️ WHICH VERSION THIS IS AUTHORED AGAINST, BECAUSE PRUNE JOBS MOVED. The paths and every field
|
|
55
|
+
* below are MEASURED from the PBS API as published at pbs.proxmox.com/docs/api-viewer, fetched
|
|
56
|
+
* 2026-09-13 — current PBS 4.x. Before PBS 2.2 there was no `/config/prune` at all: retention
|
|
57
|
+
* lived on the datastore section itself, as `keep-*` plus `prune-schedule` under
|
|
58
|
+
* `/config/datastore/{name}`. Against anything older every path here 404s, `pveOperations.read`
|
|
59
|
+
* folds that into "absent", and the plan says create forever. Check `GET /api2/json/version`
|
|
60
|
+
* before the first plan.
|
|
61
|
+
*
|
|
62
|
+
* ★ THE READ LANE NEEDS NOTHING EXTRA, UNLIKE THREE PVE FAMILIES — so there is no `readRole:
|
|
63
|
+
* 'provision'` below and there should not be one. MEASURED: `GET /config/prune/{id}` requires
|
|
64
|
+
* "Datastore.Audit or Datastore.Verify on job's datastore", i.e. the AUDIT privilege is enough
|
|
65
|
+
* for the ITEM read. That is precisely what `Proxmox.Storage` and the two SDN families could not
|
|
66
|
+
* say, and it is why the ⛔ on `readRole` in resource.ts does not claim a fourth family here.
|
|
67
|
+
* ⚠️ THE WRITE PRIVILEGES ARE NOT ONE PRIVILEGE, AND DELETE IS THE ODD ONE OUT. MEASURED: POST
|
|
68
|
+
* and PUT require `Datastore.Modify` on the job's datastore; DELETE requires `Datastore.Verify`.
|
|
69
|
+
* A credential that can create and edit a prune job cannot necessarily remove one, and the
|
|
70
|
+
* symptom is a destroy that 403s long after the creates worked.
|
|
71
|
+
*
|
|
72
|
+
* ⚠️ RUNTIME STATUS IS NOT HERE AND WAS NOT FORGOTTEN. `last-run-endtime` and friends come from
|
|
73
|
+
* `/admin/prune`, not from this endpoint: MEASURED, `GET /config/prune/{id}` returns exactly
|
|
74
|
+
* comment, disable, id, the six keep-*, max-depth, ns, schedule and store — no digest, no status.
|
|
75
|
+
* Reporting a last-run time would make a plan's output change when nothing changed, and a plan
|
|
76
|
+
* whose output moves on its own is one people stop reading. `/admin/prune/{id}/run` triggers a
|
|
77
|
+
* prune by hand and is deliberately not modelled: running one is an act, not a declaration.
|
|
78
|
+
*/
|
|
79
|
+
import { Resource } from 'alchemy';
|
|
80
|
+
import * as Provider from 'alchemy/Provider';
|
|
81
|
+
import type { PbsTarget } from './credentials.ts';
|
|
82
|
+
import { type PveRequirements, type WithPbsTarget } from './resource.ts';
|
|
83
|
+
/**
|
|
84
|
+
* ⚠️ THE PROPS ARE PBS'S OWN KEY NAMES, HYPHENS INCLUDED, for backup-job.ts's reason: the form
|
|
85
|
+
* builder is then a copy rather than a translation table, and a translation table is one more
|
|
86
|
+
* place for a key to be renamed and silently never sent. Here a field that is never sent is a
|
|
87
|
+
* retention window that never applies.
|
|
88
|
+
*/
|
|
89
|
+
export interface PruneJobProps extends WithPbsTarget {
|
|
90
|
+
/** ⛔ A PBS host, not a PVE one. See `PbsTarget` and the credential ⛔ in the header. */
|
|
91
|
+
target: PbsTarget;
|
|
92
|
+
/**
|
|
93
|
+
* ⛔ The primary key, REQUIRED, and PBS does not generate one. Measured: 3–32 chars matching
|
|
94
|
+
* `^[A-Za-z0-9_][A-Za-z0-9._\-]*$`. To adopt a job that exists, declare its existing id — any
|
|
95
|
+
* other id is a SECOND prune job on the same datastore, and two prune jobs do not negotiate.
|
|
96
|
+
*/
|
|
97
|
+
id: string;
|
|
98
|
+
/** ⚠️ The datastore this prunes. Changing it re-points the job rather than adding one: the old
|
|
99
|
+
* store stops being pruned and the new one starts, with these windows, at the next run. */
|
|
100
|
+
store: string;
|
|
101
|
+
/**
|
|
102
|
+
* A systemd calendar event, e.g. `daily` or `sat 02:30`. ⛔ REQUIRED — measured, it is the one
|
|
103
|
+
* non-optional field of `POST /config/prune` besides `id` and `store`, and it is NOT in the PUT
|
|
104
|
+
* `delete` enum, so there is no way to un-schedule a job short of disabling or removing it.
|
|
105
|
+
* ⚠️ THIS COMPARES STRINGS, NOT MEANINGS. A calendar event has many equivalent spellings and PBS
|
|
106
|
+
* stores the one it was handed; declare the spelling the server echoes back.
|
|
107
|
+
*/
|
|
108
|
+
schedule: string;
|
|
109
|
+
/** Namespace to prune, `''` or unset for the root one. ⚠️ Scope, so read it with `max-depth`. */
|
|
110
|
+
ns?: string;
|
|
111
|
+
/** ⛔ UNSET MEANS FULL RECURSION, NOT NONE — see the ⛔ in the header. 0–7. Declare it. */
|
|
112
|
+
'max-depth'?: number;
|
|
113
|
+
/** ⚠️ Undeclared is UNMANAGED, so a job somebody disabled by hand stays disabled. That is the
|
|
114
|
+
* deliberate direction for a destructive job: never switch one on that nobody asked for. */
|
|
115
|
+
disable?: boolean;
|
|
116
|
+
comment?: string;
|
|
117
|
+
/** ⚠️ Every window below is `minimum: 1` (measured) and undeclared means unmanaged. */
|
|
118
|
+
'keep-last'?: number;
|
|
119
|
+
'keep-hourly'?: number;
|
|
120
|
+
'keep-daily'?: number;
|
|
121
|
+
'keep-weekly'?: number;
|
|
122
|
+
'keep-monthly'?: number;
|
|
123
|
+
'keep-yearly'?: number;
|
|
124
|
+
}
|
|
125
|
+
export interface PruneJobAttributes {
|
|
126
|
+
id: string;
|
|
127
|
+
store: string;
|
|
128
|
+
schedule: string;
|
|
129
|
+
ns: string;
|
|
130
|
+
/** ⚠️ `-1` is ABSENT — and absent means full recursion. `0` is the real "no recursion" setting. */
|
|
131
|
+
'max-depth': number;
|
|
132
|
+
disable: boolean;
|
|
133
|
+
comment: string;
|
|
134
|
+
/** ⚠️ `0` is ABSENT in all six. PBS's own minimum is 1, so zero can never be a live value. */
|
|
135
|
+
'keep-last': number;
|
|
136
|
+
'keep-hourly': number;
|
|
137
|
+
'keep-daily': number;
|
|
138
|
+
'keep-weekly': number;
|
|
139
|
+
'keep-monthly': number;
|
|
140
|
+
'keep-yearly': number;
|
|
141
|
+
}
|
|
142
|
+
export interface PbsPruneJob extends Resource<'Pbs.PruneJob', PruneJobProps, PruneJobAttributes, never, PveRequirements> {
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* ★ NO `defaultRemovalPolicy: 'retain'`, UNLIKE Storage AND ZfsPool, AND THE REASON IS THE REVERSE
|
|
146
|
+
* OF THEIRS. Destroying this resource destroys a POLICY, and a policy is a line of TypeScript.
|
|
147
|
+
* The archives it would have trimmed are not touched by the DELETE — see the ⚠️ on the provider.
|
|
148
|
+
*/
|
|
149
|
+
export declare const PbsPruneJob: import("alchemy").ResourceClass<PbsPruneJob>;
|
|
150
|
+
/**
|
|
151
|
+
* ⛔ `list` IS EMPTY like every other family here, and the stakes are the usual ones read backwards.
|
|
152
|
+
* `GET /config/prune` hands back every prune job on the server, the ones an operator tuned by
|
|
153
|
+
* hand included. Adopting those would give Alchemy the right to REWRITE their retention the first
|
|
154
|
+
* time a declaration disagreed — and rewriting retention is deleting backups. Adoption stays an
|
|
155
|
+
* explicit act: name the id you mean.
|
|
156
|
+
*
|
|
157
|
+
* ⚠️ THE DELETE IS THE SAFE DIRECTION, AND IT IS STILL NOT FREE. Removing a prune job deletes
|
|
158
|
+
* NOTHING: the archives that survived the last run stay exactly where they are. What goes away is
|
|
159
|
+
* the only thing trimming that datastore, so the failure mode is a datastore that fills up weeks
|
|
160
|
+
* later — an outage with no obvious cause and no backup written during it. Nothing on the server
|
|
161
|
+
* depends on a prune job, so PBS will not refuse this the way PVE refuses to delete a pool still
|
|
162
|
+
* holding guests; the plan line is the only warning anyone gets.
|
|
163
|
+
* ⚠️ DELETE ALSO NEEDS A DIFFERENT PRIVILEGE FROM THE WRITES — `Datastore.Verify`, not
|
|
164
|
+
* `Datastore.Modify`. See the ⚠️ in the header before assuming a working create means a working
|
|
165
|
+
* destroy.
|
|
166
|
+
*/
|
|
167
|
+
export declare const PbsPruneJobProvider: () => import("effect/Layer").Layer<Provider.Provider<PbsPruneJob>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
168
|
+
//# sourceMappingURL=pbs-prune-job.d.ts.map
|