@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,99 @@
|
|
|
1
|
+
export interface BaoSshRoleProps {
|
|
2
|
+
/** Role name, as `<mount>/roles/{name}` takes it. */
|
|
3
|
+
name: string;
|
|
4
|
+
/** Secrets engine mount, no trailing slash. Defaults to `ssh`; `ssh-host` also exists. */
|
|
5
|
+
mount?: string;
|
|
6
|
+
/** Principals a certificate may carry. `['*']` is OpenBao's wildcard. */
|
|
7
|
+
allowedUsers: readonly string[];
|
|
8
|
+
/** Principal used when a sign request does not name one. */
|
|
9
|
+
defaultUser?: string;
|
|
10
|
+
/** Sign user certificates. Defaults to false — OpenBao's own default. */
|
|
11
|
+
allowUserCertificates?: boolean;
|
|
12
|
+
/** Sign host certificates. Defaults to false — OpenBao's own default. */
|
|
13
|
+
allowHostCertificates?: boolean;
|
|
14
|
+
/** Host-cert roles only: the domains a host certificate may claim. */
|
|
15
|
+
allowedDomains?: readonly string[];
|
|
16
|
+
/** Host-cert roles only: allow `host.example.com` under an allowed `example.com`. */
|
|
17
|
+
allowSubdomains?: boolean;
|
|
18
|
+
/** Host-cert roles only: allow the bare allowed domain itself. */
|
|
19
|
+
allowBareDomains?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Extensions a REQUESTER may ask for — not the ones that get stamped on. `[]` means none
|
|
22
|
+
* may be requested, which is what all three live roles carry; `['*']` allows any.
|
|
23
|
+
*/
|
|
24
|
+
allowedExtensions?: readonly string[];
|
|
25
|
+
/** Extensions stamped on every certificate, e.g. `{ 'permit-pty': '' }`. */
|
|
26
|
+
defaultExtensions?: Readonly<Record<string, string>>;
|
|
27
|
+
/** Critical options a requester may ask for — `force-command`, `source-address`. */
|
|
28
|
+
allowedCriticalOptions?: readonly string[];
|
|
29
|
+
/** Critical options stamped on every certificate. See the ⛔ on erasure in ssh-role.ts. */
|
|
30
|
+
defaultCriticalOptions?: Readonly<Record<string, string>>;
|
|
31
|
+
/** ⛔ Skeleton-key flag — read the ⛔ in ssh-role.ts before setting this true. */
|
|
32
|
+
allowEmptyPrincipals?: boolean;
|
|
33
|
+
/** Certificate lifetime, e.g. `4h`. ⛔ `0` is NOT "no expiry" — see ssh-role.ts. */
|
|
34
|
+
ttl: string;
|
|
35
|
+
/** Ceiling on a requested lifetime, e.g. `8h`. */
|
|
36
|
+
maxTtl: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Every prop resolved to the value the full-replace write will actually produce.
|
|
40
|
+
*
|
|
41
|
+
* ★ Derived rather than retyped: `Required` is the whole difference between the two, and a
|
|
42
|
+
* hand-copied twin is the kind of thing that silently loses a field in review.
|
|
43
|
+
*/
|
|
44
|
+
export type BaoSshRoleForm = Required<BaoSshRoleProps>;
|
|
45
|
+
/** ⛔ NO SECRET HERE — names, flags, TTLs and a digest. The CA key is out of scope. */
|
|
46
|
+
export interface BaoSshRoleAttributes extends Omit<BaoSshRoleForm, 'maxTtl' | 'ttl'> {
|
|
47
|
+
/**
|
|
48
|
+
* ⚠️ SECONDS, NOT A DURATION STRING. OpenBao stores seconds and a plan should show what is
|
|
49
|
+
* stored; `4h` in the props and 14400 here are the same role. Rendering seconds back
|
|
50
|
+
* into `4h` needs a table of unit rules that mount-form.ts and auth-role-form.ts each
|
|
51
|
+
* already carry a private copy of — a third copy is how the three drift apart.
|
|
52
|
+
*/
|
|
53
|
+
ttlSeconds: number;
|
|
54
|
+
maxTtlSeconds: number;
|
|
55
|
+
/** Always `ca` on a healthy role. Stored so an otp/dynamic role reads as a mismatch. */
|
|
56
|
+
keyType: string;
|
|
57
|
+
/** SHA-256 of the managed fields — safe to persist; see policy.ts. */
|
|
58
|
+
digest: string;
|
|
59
|
+
}
|
|
60
|
+
/** Mount path without a trailing slash; `ssh` when the caller did not say. */
|
|
61
|
+
export declare const mountOf: (mount: string | undefined) => string;
|
|
62
|
+
export declare const readPath: (mount: string | undefined, name: string) => string;
|
|
63
|
+
/**
|
|
64
|
+
* ⚠️ SETS, NOT SEQUENCES. OpenBao stores these as one comma-separated string and promises
|
|
65
|
+
* nothing about the order it hands back. Sorting and de-duplicating on BOTH sides is what
|
|
66
|
+
* stops every plan from reporting an update on a role nobody touched.
|
|
67
|
+
*/
|
|
68
|
+
export declare const csvList: (value: unknown) => readonly string[];
|
|
69
|
+
/**
|
|
70
|
+
* ⚠️ A MAP WITH NO PROMISED ORDER. Key order also decides the digest and the argv, so it is
|
|
71
|
+
* normalised once here and everything else reads the sorted copy.
|
|
72
|
+
*/
|
|
73
|
+
export declare const extMap: (value: unknown) => Record<string, string>;
|
|
74
|
+
/** ⛔ Every default here is what the WRITE produces, not what felt sensible. */
|
|
75
|
+
export declare const resolve: (props: BaoSshRoleProps) => BaoSshRoleForm;
|
|
76
|
+
export declare const attributesOf: (form: BaoSshRoleForm, live: Record<string, unknown>) => BaoSshRoleAttributes;
|
|
77
|
+
/**
|
|
78
|
+
* ⛔ EVERY MANAGED FIELD IS SENT ON EVERY WRITE, INCLUDING THE FALSE ONES. The role write is
|
|
79
|
+
* a full replace (see ssh-role.ts), so an omitted field is not "left alone" — it is reset.
|
|
80
|
+
* Sending the resolved form makes the body and the declaration the same statement. All
|
|
81
|
+
* strings, exactly the `k=v` pairs `bao write` sent, for `PUT <mount>/roles/<name>`.
|
|
82
|
+
*/
|
|
83
|
+
export declare const writeBody: (form: BaoSshRoleForm) => Record<string, string>;
|
|
84
|
+
/**
|
|
85
|
+
* ⛔ AN UNPARSEABLE TTL IS A PLAN THAT NEVER GOES GREEN. parseDuration understands `0` and
|
|
86
|
+
* `<n>[smhd]` and nothing else. A prop like `4h30m` writes fine, reads back as 16200,
|
|
87
|
+
* fails to parse on the want side, and reports `update` forever — and a resource that is
|
|
88
|
+
* always dirty is one nobody reads the plan for any more.
|
|
89
|
+
*/
|
|
90
|
+
export declare const badDurations: (form: BaoSshRoleForm) => readonly string[];
|
|
91
|
+
/**
|
|
92
|
+
* True when live already matches the declaration on every managed field.
|
|
93
|
+
* ⛔ NOTHING IS SKIPPED FOR BEING UNDEFINED. mount.ts and auth-role.ts can treat an absent
|
|
94
|
+
* prop as "don't care" because their writes are partial tunes; this one cannot, because
|
|
95
|
+
* the write resets whatever it omits. Comparing the resolved form is what keeps a
|
|
96
|
+
* hand-edited role from surviving forever behind a green plan.
|
|
97
|
+
*/
|
|
98
|
+
export declare const matches: (attributes: BaoSshRoleAttributes, form: BaoSshRoleForm) => boolean;
|
|
99
|
+
//# sourceMappingURL=ssh-role-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/ssh-role-form.ts"],"names":[],"mappings":"AAgBA,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,IAAI,EAAE,MAAM,CAAC;IACb,0FAA0F;IAC1F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,yEAAyE;IACzE,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,sEAAsE;IACtE,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,qFAAqF;IACrF,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,oFAAoF;IACpF,sBAAsB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,0FAA0F;IAC1F,sBAAsB,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC1D,gFAAgF;IAChF,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,mFAAmF;IACnF,GAAG,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;AAEvD,sFAAsF;AACtF,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,cAAc,EAAE,QAAQ,GAAG,KAAK,CAAC;IAClF;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,wFAAwF;IACxF,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,8EAA8E;AAC9E,eAAO,MAAM,OAAO,UAAW,MAAM,GAAG,SAAS,WAAyC,CAAC;AAE3F,eAAO,MAAM,QAAQ,UAAW,MAAM,GAAG,SAAS,QAAQ,MAAM,WAC7B,CAAC;AAMpC;;;;GAIG;AACH,eAAO,MAAM,OAAO,UAAW,OAAO,KAAG,SAAS,MAAM,EAOvD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,MAAM,UAAW,OAAO,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAM5D,CAAC;AAEF,+EAA+E;AAC/E,eAAO,MAAM,OAAO,UAAW,eAAe,KAAG,cAiB/C,CAAC;AAEH,eAAO,MAAM,YAAY,SACjB,cAAc,QACd,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,oBAqBF,CAAC;AAgBF;;;;;GAKG;AACH,eAAO,MAAM,SAAS,SAAU,cAAc,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBpE,CAAC;AAoBH;;;;;GAKG;AACH,eAAO,MAAM,YAAY,SAAU,cAAc,KAAG,SAAS,MAAM,EACuB,CAAC;AAE3F;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,eAAgB,oBAAoB,QAAQ,cAAc,YAe9B,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An OpenBao SSH secrets-engine role — `<mount>/roles/{name}`, `key_type=ca`. METADATA ONLY.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ THE SIGNING KEY IS OUT OF SCOPE AND MUST STAY OUT. `ssh/config/ca` holds (or generates)
|
|
5
|
+
* the CA private key and `ssh/sign/<role>` issues certificates. Neither is declarable
|
|
6
|
+
* here, for the reason policy.ts spells out: Alchemy persists attributes to its state
|
|
7
|
+
* store WITHOUT encrypting them, that store is the `alchemy` Postgres, and pg-backup.sh
|
|
8
|
+
* dumps it nightly to CT100. A CA key there would outlive the vault that guards it. This
|
|
9
|
+
* resource declares the SHAPE a signature must satisfy — principals, extensions, TTLs.
|
|
10
|
+
*
|
|
11
|
+
* ⛔ THE ROLE WRITE IS A FULL REPLACE, NOT A PATCH. OpenBao rebuilds the role entry from the
|
|
12
|
+
* request body, so any field the write omits reverts to its ZERO value. Two things follow:
|
|
13
|
+
* `resolve` in ssh-role-form.ts fills every optional prop with the default the write would
|
|
14
|
+
* produce and `matches` compares all of it (an omitted prop asserts the default, it never
|
|
15
|
+
* means "don't care"); and `wouldErase` below names the fields this resource does NOT
|
|
16
|
+
* declare, so reconcile can REFUSE instead of quietly dropping someone's key-length floor.
|
|
17
|
+
*
|
|
18
|
+
* ⛔ `allowEmptyPrincipals: true` WITH AN EMPTY `allowedUsers` IS A SKELETON KEY. OpenBao
|
|
19
|
+
* will sign a certificate carrying no principals, and OpenSSH accepts such a certificate
|
|
20
|
+
* for ANY user on ANY host that trusts the CA. It defaults to false, and all three roles
|
|
21
|
+
* on the live engine carry false (MEASURED). Do not set it true without pinning
|
|
22
|
+
* `defaultCriticalOptions['force-command']` in the same declaration.
|
|
23
|
+
*
|
|
24
|
+
* ⛔ `ttl: '0'` DOES NOT MEAN "NO EXPIRY" AND IT DOES NOT MEAN "SHORT". It means the mount's
|
|
25
|
+
* `default_lease_ttl`, and the live `ssh` mount leaves that at 0, which means the SYSTEM
|
|
26
|
+
* default — 768h. A 32-day SSH certificate is the opposite of what an SSH CA is for.
|
|
27
|
+
* State the duration. MEASURED: the live `node-admin` and `tim` roles are ttl 14400 /
|
|
28
|
+
* max_ttl 28800 (4h / 8h); `ssh-host/roles/host` is 31536000 (365d), as a host cert should.
|
|
29
|
+
*
|
|
30
|
+
* ★ `defaultRemovalPolicy: 'retain'`. Deleting this role locks every operator out of the
|
|
31
|
+
* estate — the memory note "operator SSH is homeflare-bot@<host>" is this CA. Opt in with
|
|
32
|
+
* `.pipe(RemovalPolicy.destroy())`; see resource.ts in house/proxmox.
|
|
33
|
+
*/
|
|
34
|
+
import { Resource } from 'alchemy';
|
|
35
|
+
import * as Provider from 'alchemy/Provider';
|
|
36
|
+
import type * as HttpClient from 'effect/unstable/http/HttpClient';
|
|
37
|
+
import { type BaoSshRoleAttributes, type BaoSshRoleProps } from './ssh-role-form.ts';
|
|
38
|
+
export type { BaoSshRoleAttributes, BaoSshRoleProps };
|
|
39
|
+
export interface BaoSshRole extends Resource<'Bao.SshRole', BaoSshRoleProps, BaoSshRoleAttributes, never, HttpClient.HttpClient> {
|
|
40
|
+
}
|
|
41
|
+
export declare const BaoSshRole: import("alchemy").ResourceClass<BaoSshRole>;
|
|
42
|
+
export declare const BaoSshRoleProvider: () => import("effect/Layer").Layer<Provider.Provider<BaoSshRole>, never, HttpClient.HttpClient>;
|
|
43
|
+
//# sourceMappingURL=ssh-role.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssh-role.d.ts","sourceRoot":"","sources":["../../src/openbao/ssh-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EACL,KAAK,oBAAoB,EAEzB,KAAK,eAAe,EAQrB,MAAM,oBAAoB,CAAC;AAE5B,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,CAAC;AAEtD,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,UAAU,6CAErB,CAAC;AAyDH,eAAO,MAAM,kBAAkB,iGAsH5B,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.Acl` — one grant: a role bound to a subject at a path. The line that makes a role real.
|
|
3
|
+
*
|
|
4
|
+
* ★ USERS, GROUPS, ROLES AND TOKENS ARE ALL INERT UNTIL AN ACL BINDS THEM. A role is a named list
|
|
5
|
+
* of privileges that grants nothing by itself; a user can do nothing until
|
|
6
|
+
* {path, user|group|token, role} exists. That is what makes those four families worth declaring,
|
|
7
|
+
* and why the grants that let OpenBao mint a PVE token today are only clicks somebody made once.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ THE ONE FAMILY THAT DOES NOT FIT `pveOperations` AS WRITTEN, AND THE MISFIT IS MEASURED. The
|
|
10
|
+
* cluster's own schema — `/pve-docs/api-viewer/apidoc.js`, read unauthenticated 2026-09-13 —
|
|
11
|
+
* lists exactly TWO methods on `/access/acl`: GET and PUT. There is no DELETE (it answers
|
|
12
|
+
* "Method 'DELETE /access/acl' not implemented") and, less famously, NO POST either. So create
|
|
13
|
+
* and update are the SAME call, `PUT /access/acl`, and removal is that PUT with `delete=1` —
|
|
14
|
+
* which is why `delete` below does not use `ops.destroy`.
|
|
15
|
+
* ⚠️ THE REAL FIX BELONGS IN `resource.ts`: give `PveSpec` a `deleteForm` and the method to go
|
|
16
|
+
* with it, and this override disappears. Until then it is local and loud — do not "restore
|
|
17
|
+
* symmetry" by pointing `delete` at `ops.destroy`, which reports every removal as a 501 while
|
|
18
|
+
* the grant stays exactly where it was.
|
|
19
|
+
*
|
|
20
|
+
* ⛔ A SINGLE GRANT HAS NO URL OF ITS OWN. `GET /access/acl` answers ONE FLAT LIST for the whole
|
|
21
|
+
* cluster, so identity is the tuple (path, type, ugid, roleid), matched client-side in
|
|
22
|
+
* `attributes` below. Two resources declaring the SAME tuple are the SAME grant — Alchemy sees
|
|
23
|
+
* two resource ids, not one collision — and deleting either takes the access away from both,
|
|
24
|
+
* the same hazard as two guests declaring one vmid.
|
|
25
|
+
*
|
|
26
|
+
* ⛔ AND THAT LIST IS FILTERED BY WHO IS ASKING. The schema's own words for GET: "The returned list
|
|
27
|
+
* is restricted to objects where you have rights to modify permissions." A credential that can
|
|
28
|
+
* read the cluster but not modify permissions is answered `[]` — not a 403, not an error — so
|
|
29
|
+
* the mount's `read` role needs permission-modify rights on the declared path exactly as
|
|
30
|
+
* `provision` does. Work reported on a grant that is plainly there means that role is too narrow.
|
|
31
|
+
*/
|
|
32
|
+
import { Resource } from 'alchemy';
|
|
33
|
+
import * as Provider from 'alchemy/Provider';
|
|
34
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
35
|
+
/** PVE's three kinds of subject. The read answers this word; the write wants its plural. */
|
|
36
|
+
export type AclSubjectType = 'user' | 'group' | 'token';
|
|
37
|
+
export interface AclProps extends WithTarget {
|
|
38
|
+
/**
|
|
39
|
+
* The PVE object path the grant is ON — `/`, `/pool/house`, `/vms/101`, `/storage/local-zfs`.
|
|
40
|
+
* ⚠️ NOT THE API PATH: `spec.path` below is the endpoint (`access/acl`). PVE's API overloads the
|
|
41
|
+
* word, so this file does too rather than renaming a field the cluster calls `path`.
|
|
42
|
+
*/
|
|
43
|
+
path: string;
|
|
44
|
+
/** Which kind of subject `ugid` names. Identity: changing it is a different grant. */
|
|
45
|
+
type: AclSubjectType;
|
|
46
|
+
/** `tim@pve`, `admins`, `hf-provision@pve!hf-provision-…`. Identity. */
|
|
47
|
+
ugid: string;
|
|
48
|
+
/** The role bound here, e.g. `PVEAuditor`. ⚠️ It must exist — PVE refuses an unknown roleid. */
|
|
49
|
+
roleid: string;
|
|
50
|
+
/** Inherit down the path. PVE's default is ON, and this is the grant's ONLY mutable field. */
|
|
51
|
+
propagate?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface AclAttributes {
|
|
54
|
+
path: string;
|
|
55
|
+
type: AclSubjectType;
|
|
56
|
+
ugid: string;
|
|
57
|
+
roleid: string;
|
|
58
|
+
propagate: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* ⛔ PRESENCE AS AN ATTRIBUTE, AND IT IS WHAT MAKES THE FACTORY WORK HERE. `attributes` never
|
|
61
|
+
* returns undefined: if it did, `reconcile` would take the create branch and POST to
|
|
62
|
+
* `access/acl`, which PVE does not implement. Present-but-unbound sends every write down the
|
|
63
|
+
* PUT branch instead, the only branch PVE has. The cost: the factory's read-back guard cannot
|
|
64
|
+
* fire, so `reconcile` below re-checks this field itself and dies with the same honesty.
|
|
65
|
+
*/
|
|
66
|
+
bound: boolean;
|
|
67
|
+
}
|
|
68
|
+
export interface ProxmoxAcl extends Resource<'Proxmox.Acl', AclProps, AclAttributes, never, PveRequirements> {
|
|
69
|
+
}
|
|
70
|
+
export declare const ProxmoxAcl: import("alchemy").ResourceClass<ProxmoxAcl>;
|
|
71
|
+
export declare const ProxmoxAclProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxAcl>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
72
|
+
//# sourceMappingURL=acl.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acl.d.ts","sourceRoot":"","sources":["../../src/proxmox/acl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAiB,MAAM,eAAe,CAAC;AAErF,4FAA4F;AAC5F,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAExD,MAAM,WAAW,QAAS,SAAQ,UAAU;IAC1C;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,IAAI,EAAE,cAAc,CAAC;IACrB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,gGAAgG;IAChG,MAAM,EAAE,MAAM,CAAC;IACf,8FAA8F;IAC9F,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,cAAc,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,UAAW,SAAQ,QAAQ,CAC1C,aAAa,EACb,QAAQ,EACR,aAAa,EACb,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,UAAU,6CAAsC,CAAC;AAmH9D,eAAO,MAAM,kBAAkB,gIAqE5B,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { ApiTokenAttributes, ApiTokenProps } from './api-token.ts';
|
|
2
|
+
/**
|
|
3
|
+
* What a write to a PVE API token actually does, and the form that does it.
|
|
4
|
+
*
|
|
5
|
+
* ★ SPLIT OUT OF api-token.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the one
|
|
6
|
+
* metric-server-form.ts draws: this file answers "what does PVE store when this form arrives",
|
|
7
|
+
* api-token.ts answers "what a token is and when it has changed". Nothing here reads the
|
|
8
|
+
* cluster and nothing here decides a diff.
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ THE `import type` BACK TO api-token.ts IS A CYCLE ON PAPER ONLY. It is type-only, so it is
|
|
11
|
+
* erased before anything runs and `ApiTokenProps` stays in the file that declares the resource.
|
|
12
|
+
*
|
|
13
|
+
* ⛔ THE WRITE SEMANTICS BELOW ARE READ FROM THE CLUSTER'S OWN PERL RATHER THAN INFERRED FROM THE
|
|
14
|
+
* API SCHEMA — `/usr/share/perl5/PVE/API2/User.pm`, subs `generate_token` and
|
|
15
|
+
* `update_token_info`, read over SSH on n2 on 2026-09-13. The two disagree in exactly the place
|
|
16
|
+
* that costs a forever-diff: the schema gives `expire` the default "same as user", and the code
|
|
17
|
+
* never implements it. api-token.ts records what that means.
|
|
18
|
+
*/
|
|
19
|
+
import type { PveSpec } from './resource.ts';
|
|
20
|
+
/**
|
|
21
|
+
* `<userid>!<tokenid>` — the name the rest of PVE calls this object by.
|
|
22
|
+
*
|
|
23
|
+
* ★ IT IS WHAT AN ACL BINDS. `Proxmox.Acl` with `type: 'token'` wants exactly this string as its
|
|
24
|
+
* `ugid`, so reporting it as an attribute is what lets a privilege-separated token be granted
|
|
25
|
+
* anything at all without somebody retyping the join by hand.
|
|
26
|
+
*
|
|
27
|
+
* ⚠️ DERIVED, NOT READ. MEASURED: `GET /access/users/monitoring@pve/token/exporter` answers
|
|
28
|
+
* `{"expire":0,"privsep":0}` and echoes back neither half of its own identity. So this is built
|
|
29
|
+
* from props, cannot disagree with the path the read used, and is therefore true by
|
|
30
|
+
* construction — which is why it is reported and never compared.
|
|
31
|
+
*/
|
|
32
|
+
export declare const fullTokenid: (props: ApiTokenProps) => string;
|
|
33
|
+
/**
|
|
34
|
+
* Everything a token has, in the form PVE wants. Create and update send exactly this.
|
|
35
|
+
*
|
|
36
|
+
* ⛔ `userid` AND `tokenid` ARE NOT IN IT, and that is not an omission. Both are path segments of
|
|
37
|
+
* `access/users/{userid}/token/{tokenid}` — which is the path the POST goes to as well as the
|
|
38
|
+
* PUT — so a second copy in the body can only ever disagree with the path it was sent to.
|
|
39
|
+
* metric-server.ts omits its `id` for the same reason and says so there.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ EVERY FIELD IS SENT ON EVERY WRITE, THE EMPTY COMMENT INCLUDED, BECAUSE A PUT MERGES.
|
|
42
|
+
* MEASURED in `update_token_info`: `$token->{comment} = $param->{comment} if defined(...)`
|
|
43
|
+
* followed by `delete $token->{comment} if (!length $token->{comment})`. So `comment=` is how a
|
|
44
|
+
* comment is CLEARED; omitting it leaves the old text in place while `matches` goes on asking
|
|
45
|
+
* for an update the update cannot make. user.ts writes the same reasoning out for `groups`.
|
|
46
|
+
* ⚠️ THE `delete=` PARAMETER IS NOT USED AND WOULD NOT HELP. Its whitelist in that sub is
|
|
47
|
+
* literally `my $deletable = { comment => 1 };`, and anything else answers
|
|
48
|
+
* "unknown option '<k>'" — so `expire` and `privsep` have no clear path at all, which is the
|
|
49
|
+
* other half of why both are required props rather than optional ones.
|
|
50
|
+
*
|
|
51
|
+
* ⚠️ `flag()` FROM values.ts IS DELIBERATELY NOT USED FOR `privsep`. Its entire job is turning an
|
|
52
|
+
* UNDECLARED boolean into `undefined` so the field is omitted from the form; `privsep` is a
|
|
53
|
+
* required prop precisely so that it is never undeclared, so there is nothing for it to do and
|
|
54
|
+
* its `string | undefined` return would have to be asserted away to fit `Record<string,string>`.
|
|
55
|
+
* An assertion here would be a claim about the prop that the type already makes properly.
|
|
56
|
+
*/
|
|
57
|
+
export declare const shape: (props: ApiTokenProps) => Record<string, string>;
|
|
58
|
+
export declare const apiTokenSpec: PveSpec<ApiTokenProps, ApiTokenAttributes>;
|
|
59
|
+
//# sourceMappingURL=api-token-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-token-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/api-token-form.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACxE;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAG7C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,UAAW,aAAa,WAAuC,CAAC;AAExF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,KAAK,UAAW,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAIhE,CAAC;AAEH,eAAO,MAAM,YAAY,EAAE,OAAO,CAAC,aAAa,EAAE,kBAAkB,CAwDnE,CAAC"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.ApiToken` — a PVE API token: the thing a service actually authenticates as.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ THE SECRET EXISTS FOR ONE HTTP RESPONSE AND THEN NOWHERE. MEASURED from the cluster's own
|
|
5
|
+
* schema (`/usr/share/pve-docs/api-viewer/apidoc.js` on n2, 2026-09-13): POST returns
|
|
6
|
+
* `["full-tokenid","info","value"]`; PUT returns `["comment","expire","full-tokenid","privsep",
|
|
7
|
+
* "value"]` with `value` present ONLY when `regenerate` was set; GET returns
|
|
8
|
+
* `["comment","expire","privsep"]` and never the secret. A live token agrees —
|
|
9
|
+
* `GET /access/users/monitoring@pve/token/exporter` answers `{"expire":0,"privsep":0}`. PVE says
|
|
10
|
+
* it plainly on the POST: the value "needs to be stored as it cannot be retrieved afterwards".
|
|
11
|
+
*
|
|
12
|
+
* ⛔ SO A TOKEN THIS RESOURCE CREATES IS UNUSABLE, AND SAYING SO IS THE POINT OF THIS BLOCK. The
|
|
13
|
+
* secret may not become an attribute: Alchemy persists attributes UNENCRYPTED, and this estate's
|
|
14
|
+
* state store is a Postgres dumped nightly (the ⛔ in credentials.ts). So `reconcile` drops the
|
|
15
|
+
* value on the floor, and what lands is a live credential in `/etc/pve/user.cfg` whose secret
|
|
16
|
+
* nobody holds. No `regenerate` rescues it, because a regenerated value is discarded the same
|
|
17
|
+
* way. The only escape is an operator who captured the value out of band — which this provider
|
|
18
|
+
* gives them no way to do.
|
|
19
|
+
* ★ SO POINT THIS FAMILY AT TOKENS THAT ALREADY EXIST. `comment`, `expire` and `privsep` are the
|
|
20
|
+
* whole of a token's policy, and declaring them is real work: it is how `expire` stops being
|
|
21
|
+
* whatever somebody typed in 2024. Mint NEW tokens where the secret can be caught — OpenBao's
|
|
22
|
+
* `proxmox-tb4` mount, or a human at `pveum user token add`.
|
|
23
|
+
* ⚠️ THE CREATE PATH IS LEFT REACHABLE RATHER THAN STUBBED. A create that silently did nothing
|
|
24
|
+
* would be the same lie as the stubbed delete resource.ts's ★ refuses; the honest arrangement
|
|
25
|
+
* is a create that works and a header that says what it produces.
|
|
26
|
+
*
|
|
27
|
+
* ⛔ THERE IS NO `regenerate` PROP, AND IT IS NOT AN OVERSIGHT. PVE's own description: "All users
|
|
28
|
+
* of the previous secret will lose access after this operation." A prop for it would revoke a
|
|
29
|
+
* live credential during a deploy whose plan said `update`, and hand back a replacement this
|
|
30
|
+
* provider is obliged to throw away — breakage with no recovery. Rotation belongs to the mount
|
|
31
|
+
* that owns the lease, or to a human who is watching.
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ THERE IS A SECOND WRITER TO THESE OBJECTS AND IT IS THE ONE THIS PROVIDER RUNS ON. OpenBao's
|
|
34
|
+
* `proxmox-tb4` engine mints PVE tokens under `hf-read@pve` and `hf-provision@pve` — read from
|
|
35
|
+
* `house/platform/secrets/vault/plugin-proxmox/proxmox/`: `privsep=0` (client.go), `expire` set
|
|
36
|
+
* to the lease deadline, and an id the plugin CHOOSES,
|
|
37
|
+
* `hf-<role>-<actor>-<entity6>-<stamp>-<nonce>` (tokenname.go). Revocation and WAL rollback look
|
|
38
|
+
* up that one exact id (wal.go); neither sweeps a prefix. So the two systems cannot collide on
|
|
39
|
+
* one object by accident — but they share the USER: `hf-read@pve` held three live leases when
|
|
40
|
+
* this was written, and `Proxmox.User` deleting that account takes every one of them with it.
|
|
41
|
+
* ⛔ DO NOT DECLARE A TOKEN UNDER A MINT USER. `expire` is what makes that dangerous rather than
|
|
42
|
+
* untidy: point this resource at a lease token with the wrong expire and a 300-second
|
|
43
|
+
* credential becomes a permanent one — and on `hf-provision@pve` that credential carries
|
|
44
|
+
* `Permissions.Modify` at `/`. docs/privileges.md records what those 27 privileges buy.
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ PRIVILEGES, AND THE READ LANE CANNOT DO IT. MEASURED: all four methods on this path check
|
|
47
|
+
* `['or', ['userid-param','self'], ['userid-group', ['User.Modify']]]` — with no `Sys.Audit`
|
|
48
|
+
* alternative, unlike `GET /access/users/{userid}`, which has one. And
|
|
49
|
+
* `pvesh get /access/permissions --userid hf-read@pve --path /access/groups` answers the seven
|
|
50
|
+
* PVEAuditor audit privileges and no `User.Modify`, while `hf-provision@pve` has it. So the
|
|
51
|
+
* 3600s read lease reaches at most its OWN account's tokens and is refused for every other
|
|
52
|
+
* userid — which `read` in resource.ts folds into "absent", so the plan says create and the POST
|
|
53
|
+
* then fails with "Token already exists". Hence `readRole: 'provision'` below, for the same
|
|
54
|
+
* reason storage.ts, sdn-zone.ts and sdn-vnet.ts set it, at the same cost: every plan mints a
|
|
55
|
+
* 300s non-renewable provision lease just to read.
|
|
56
|
+
*/
|
|
57
|
+
import { Resource } from 'alchemy';
|
|
58
|
+
import * as Provider from 'alchemy/Provider';
|
|
59
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
60
|
+
export interface ApiTokenProps extends WithTarget {
|
|
61
|
+
/**
|
|
62
|
+
* The account that owns the token, realm-qualified: `tofu@pve`, `root@pam`. Identity.
|
|
63
|
+
*
|
|
64
|
+
* ⚠️ THE ACCOUNT MUST ALREADY EXIST. Every method here runs `check_user_exist` first, so a
|
|
65
|
+
* token named under a missing user fails the READ (folded to "absent") and then fails the
|
|
66
|
+
* create with "no such user" — the honest error, arriving one step after the misleading plan.
|
|
67
|
+
* Sequence a `Proxmox.User` ahead of it and the ordering takes care of itself.
|
|
68
|
+
*/
|
|
69
|
+
userid: string;
|
|
70
|
+
/**
|
|
71
|
+
* The token's own name, unique within the account. Identity.
|
|
72
|
+
*
|
|
73
|
+
* ⚠️ PVE HAS NO RENAME, AND HERE THAT COSTS MORE THAN IT DOES FOR A USER. Editing this prop (or
|
|
74
|
+
* `userid`) points `path` at a DIFFERENT token, which reads as absent and is then created —
|
|
75
|
+
* leaving the old token LIVE, with its secret still working, invisible to the plan, while the
|
|
76
|
+
* new one is the unusable kind described at the top. Rename by declaring a delete and a
|
|
77
|
+
* create, the way user.ts says for an account.
|
|
78
|
+
* ⛔ `diff` IS NOT OVERRIDDEN TO SAY `replace` THE WAY acl.ts DOES, AND THE DIFFERENCE IS THE
|
|
79
|
+
* OBJECT. There, replace removes a grant that can be rebuilt from its own tuple; here it
|
|
80
|
+
* would DELETE a live token — destroying an irrecoverable secret, and everything using it —
|
|
81
|
+
* to tidy up a name. Leaving the old token standing is the lesser harm, and saying so here
|
|
82
|
+
* is what keeps it from looking like an omission.
|
|
83
|
+
* ⚠️ PATTERN `[A-Za-z][A-Za-z0-9.\-_]+`: a letter first, two characters minimum. The schema
|
|
84
|
+
* declares no maxLength; the OpenBao plugin assumes a conservative 64 rather than finding the
|
|
85
|
+
* real limit in production, and so should anything else.
|
|
86
|
+
*/
|
|
87
|
+
tokenid: string;
|
|
88
|
+
/**
|
|
89
|
+
* Free text in `pveum user token list` and the UI. Empty is how it is cleared — see `shape`.
|
|
90
|
+
* ⚠️ ANY CHARACTER IS SAFE HERE, MEASURED, and it was worth checking: `user.cfg` is a
|
|
91
|
+
* colon-delimited line, so a comment holding a colon or a newline is the obvious place for a
|
|
92
|
+
* round-trip to lose a character and diff forever. PVE escapes it — `encode_text` turns
|
|
93
|
+
* `a:b\nc%d` into `a%3Ab%0Ac%25d` and `decode_text` gives it back byte for byte.
|
|
94
|
+
*/
|
|
95
|
+
comment?: string;
|
|
96
|
+
/**
|
|
97
|
+
* Seconds since the epoch, or 0 for "never expires".
|
|
98
|
+
*
|
|
99
|
+
* ⛔ REQUIRED, AND THE SCHEMA WILL TELL YOU IT NEED NOT BE. It declares the default "same as
|
|
100
|
+
* user", and the code never implements it: `generate_token` writes `expire` only when the
|
|
101
|
+
* parameter is defined, `user.cfg` stores a token as `token:<id>:<expire>:<privsep>:<comment>:`
|
|
102
|
+
* with no absent state, and the parser does `$expire = 0 if !$expire` (AccessControl.pm:1624).
|
|
103
|
+
* MEASURED end to end: every token on this cluster that was created without an expire reads
|
|
104
|
+
* back `"expire":0`. So "same as user" is documentation of an intention, not behaviour.
|
|
105
|
+
* ⛔ WHICH IS WHY IT IS NOT OPTIONAL-DEFAULTING-TO-0. That spelling reads identically and is far
|
|
106
|
+
* worse: an omitted `expire` would then quietly WRITE 0 over a live deadline, turning an
|
|
107
|
+
* expiring credential into a permanent one, and the plan would call it an update. Requiring
|
|
108
|
+
* the field makes "this token never expires" a sentence somebody had to type.
|
|
109
|
+
*/
|
|
110
|
+
expire: number;
|
|
111
|
+
/**
|
|
112
|
+
* True keeps the token's privileges separate from its owner's — it gets NOTHING until an ACL
|
|
113
|
+
* names `fullTokenid`. False gives it the owner's privileges entire.
|
|
114
|
+
*
|
|
115
|
+
* ⛔ REQUIRED FOR THE SAME REASON AS `expire`, AND THE BLAST RADIUS IS LARGER. PVE's API default
|
|
116
|
+
* is 1, every token on this cluster is 0 (measured), and `user.cfg` materialises it either way
|
|
117
|
+
* — `$privsep = $privsep ? 1 : 0` (AccessControl.pm:1622), so there is no unset state to
|
|
118
|
+
* preserve. Were this optional, adopting a live token without mentioning `privsep` would plan
|
|
119
|
+
* an update to 1 and STRIP a working credential of every privilege it has, silently: PVE
|
|
120
|
+
* returns 401/403 to the service, nothing errors here, and `tofu@pve!apply` simply stops
|
|
121
|
+
* working. Required, so adopting a token is a sentence that states what it is.
|
|
122
|
+
* ⚠️ AND `false` IS NOT A SHRUG. It is a token with its owner's whole privilege set; under a
|
|
123
|
+
* provisioning account that is the account's full authority with a separate secret.
|
|
124
|
+
*/
|
|
125
|
+
privsep: boolean;
|
|
126
|
+
}
|
|
127
|
+
export interface ApiTokenAttributes {
|
|
128
|
+
userid: string;
|
|
129
|
+
tokenid: string;
|
|
130
|
+
/** `<userid>!<tokenid>` — what `Proxmox.Acl` binds as its `ugid`. Derived; never compared. */
|
|
131
|
+
fullTokenid: string;
|
|
132
|
+
comment: string;
|
|
133
|
+
expire: number;
|
|
134
|
+
privsep: boolean;
|
|
135
|
+
}
|
|
136
|
+
export interface ProxmoxApiToken extends Resource<'Proxmox.ApiToken', ApiTokenProps, ApiTokenAttributes, never, PveRequirements> {
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* ★ `retain` BY DEFAULT, AND THIS IS THE PLAINEST CASE FOR IT IN THE PACKAGE. A token's contents
|
|
140
|
+
* are one irreplaceable secret: delete it and the value is gone, a replacement is a DIFFERENT
|
|
141
|
+
* value, and every holder loses access the instant `cfs_write_file` returns — with no error
|
|
142
|
+
* raised anywhere near them. The live cluster's tokens are `monitoring@pve!exporter` (the PVE
|
|
143
|
+
* exporter feeding VictoriaMetrics), `mcp@pve!executor`, `tofu@pve!apply`, `tofu@pve!ro`,
|
|
144
|
+
* `sablier@pve!sablier` and `vaultmint@pve!engine` — the last being the parent credential the
|
|
145
|
+
* OpenBao mount itself authenticates with, so orphaning that one would stop every plan in this
|
|
146
|
+
* package, this resource included. `delete` is FULLY IMPLEMENTED (DELETE is a real method on
|
|
147
|
+
* this path, measured) and runs the moment a caller opts in with `.pipe(RemovalPolicy.destroy())`.
|
|
148
|
+
* See the ★ in resource.ts, which explains the convention once.
|
|
149
|
+
*/
|
|
150
|
+
export declare const ProxmoxApiToken: import("alchemy").ResourceClass<ProxmoxApiToken>;
|
|
151
|
+
/**
|
|
152
|
+
* ⛔ `list` IS EMPTY, AND FOR THIS FAMILY THAT MATTERS MORE THAN THE GENERIC ARGUMENT IN
|
|
153
|
+
* resource.ts. A token index handed to Alchemy would offer up `vaultmint@pve!engine` and every
|
|
154
|
+
* live OpenBao lease for adoption — and adoption is what makes a later plan willing to delete.
|
|
155
|
+
* The mount's leases in particular appear and vanish on their own; anything that adopted one
|
|
156
|
+
* would report drift against a credential that was never its to hold.
|
|
157
|
+
*/
|
|
158
|
+
export declare const ProxmoxApiTokenProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxApiToken>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
159
|
+
//# sourceMappingURL=api-token.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-token.d.ts","sourceRoot":"","sources":["../../src/proxmox/api-token.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAA8B,MAAM,eAAe,CAAC;AAElG,MAAM,WAAW,aAAc,SAAQ,UAAU;IAC/C;;;;;;;OAOG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;;;;;;;;;OAaG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;;;;;;OAaG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,8FAA8F;IAC9F,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAOlB;AAED,MAAM,WAAW,eAAgB,SAAQ,QAAQ,CAC/C,kBAAkB,EAClB,aAAa,EACb,kBAAkB,EAClB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,eAAe,kDAE1B,CAAC;AA6CH;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,qIACqD,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.BackupJob` — the vzdump schedule, declared. The one object whose drift nothing else on
|
|
3
|
+
* the cluster reveals.
|
|
4
|
+
*
|
|
5
|
+
* ★ WHY THIS ONE EARNS ITS PLACE. A hand-made vzdump job drifts silently: a schedule moved by an
|
|
6
|
+
* hour, a retention trimmed to `keep-last=1`, or `enabled 0` set during a maintenance window and
|
|
7
|
+
* never set back. All three look like a healthy cluster from every angle except a restore.
|
|
8
|
+
* Declared here, `diff` reads the LIVE job on every plan, so a hand edit surfaces as
|
|
9
|
+
* `1 to update` that day rather than on the day somebody needs last night's backup and finds a
|
|
10
|
+
* week-old one, or none.
|
|
11
|
+
*
|
|
12
|
+
* ⛔ PVE GENERATES `id` WHEN YOU OMIT IT, WHICH MAKES OMITTING IT THE WORST BUG THIS RESOURCE
|
|
13
|
+
* COULD CARRY. PVE's own API schema says so in one parenthesis — `id`: "Job ID (will be
|
|
14
|
+
* autogenerated)." — and a generated id looks like `backup-f56d8ab7-d524`. A resource that
|
|
15
|
+
* POSTed without an id would create a SECOND job beside the first on EVERY reconcile, and the
|
|
16
|
+
* read-back in `pveOperations` would still pass, because a job did appear: that check asks "is
|
|
17
|
+
* the object there", not "is it the one I meant". So `id` is a REQUIRED prop and is always sent
|
|
18
|
+
* on create. To adopt a job that already exists, declare its existing id; any other id is a new
|
|
19
|
+
* job beside it, not an adoption.
|
|
20
|
+
* ★ AN EXPLICIT id ALSO CLOSES A SECOND HOLE. `pveOperations.read` turns ANY failed GET into
|
|
21
|
+
* "absent" — an expired lease, a 403, a node that did not answer — and reconcile then POSTs.
|
|
22
|
+
* With an id, PVE refuses a duplicate section id and the deploy fails loudly; without one, that
|
|
23
|
+
* same swallowed error would have quietly doubled the cluster's backup jobs.
|
|
24
|
+
*
|
|
25
|
+
* ⚠️ `Sys.Modify` ON `/` IS THE GATE AND IT IS NOT A SMALL GRANT. PVE has no Backup.* privilege:
|
|
26
|
+
* create, update and delete of a vzdump job are all checked against `Sys.Modify` at `/` — the
|
|
27
|
+
* same privilege that covers node network configuration. A credential that can declare backup
|
|
28
|
+
* jobs can also rewrite the cluster's networking. That is PVE's granularity rather than a choice
|
|
29
|
+
* made here, and it is exactly why widening a role for this should be a decision rather than a
|
|
30
|
+
* reaction to a 403.
|
|
31
|
+
*/
|
|
32
|
+
import { Resource } from 'alchemy';
|
|
33
|
+
import * as Provider from 'alchemy/Provider';
|
|
34
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
35
|
+
/**
|
|
36
|
+
* ⚠️ THE PROPS ARE PVE'S OWN KEY NAMES, DASHES INCLUDED. `notes-template` rather than
|
|
37
|
+
* `notesTemplate` is deliberate: `createForm` is then a copy rather than a translation table,
|
|
38
|
+
* and a translation table is one more place for a key to be renamed and silently never sent. A
|
|
39
|
+
* field that is never sent reads back as whatever it already was, which is indistinguishable
|
|
40
|
+
* from "PVE ignored me".
|
|
41
|
+
*/
|
|
42
|
+
export interface BackupJobProps extends WithTarget {
|
|
43
|
+
/** ⛔ The primary key, REQUIRED. Never let PVE generate it — see the ⛔ above. */
|
|
44
|
+
id: string;
|
|
45
|
+
/** A subset of systemd calendar events, e.g. `01:00` or `mon..fri 02:30`. */
|
|
46
|
+
schedule: string;
|
|
47
|
+
/** Target storage id. That storage's own retention applies unless `prune-backups` overrides it. */
|
|
48
|
+
storage?: string;
|
|
49
|
+
mode?: 'snapshot' | 'suspend' | 'stop';
|
|
50
|
+
/** PVE's default is enabled. A disabled job is a backup that silently is not happening. */
|
|
51
|
+
enabled?: boolean;
|
|
52
|
+
/** Back up every guest. ⚠️ Pick exactly one of `all`, `vmid` and `pool` — see `shape` below. */
|
|
53
|
+
all?: boolean;
|
|
54
|
+
vmid?: readonly number[];
|
|
55
|
+
pool?: string;
|
|
56
|
+
/** Guest ids to skip. PVE reads this as "all guests except these", so it belongs with `all`. */
|
|
57
|
+
exclude?: readonly number[];
|
|
58
|
+
/** Retention, as a property string: `keep-daily=7,keep-weekly=4`. */
|
|
59
|
+
'prune-backups'?: string;
|
|
60
|
+
/** Supports {{cluster}}, {{guestname}}, {{node}} and {{vmid}}. Single line only. */
|
|
61
|
+
'notes-template'?: string;
|
|
62
|
+
'notification-mode'?: 'auto' | 'legacy-sendmail' | 'notification-system';
|
|
63
|
+
/** Run a missed job as soon as the scheduler is back. */
|
|
64
|
+
'repeat-missed'?: boolean;
|
|
65
|
+
/** VM-only fleecing, as a property string: `enabled=1,storage=<id>`. See `propertyString`. */
|
|
66
|
+
fleecing?: string;
|
|
67
|
+
comment?: string;
|
|
68
|
+
}
|
|
69
|
+
export interface BackupJobAttributes {
|
|
70
|
+
id: string;
|
|
71
|
+
schedule: string;
|
|
72
|
+
storage: string;
|
|
73
|
+
mode: string;
|
|
74
|
+
enabled: boolean;
|
|
75
|
+
all: boolean;
|
|
76
|
+
/** Sorted and comma-joined, so re-ordering a declaration is not a diff. See `guestList`. */
|
|
77
|
+
vmid: string;
|
|
78
|
+
pool: string;
|
|
79
|
+
exclude: string;
|
|
80
|
+
/** Flattened and sorted — never the raw value PVE returned. See `propertyString`. */
|
|
81
|
+
'prune-backups': string;
|
|
82
|
+
'notes-template': string;
|
|
83
|
+
'notification-mode': string;
|
|
84
|
+
'repeat-missed': boolean;
|
|
85
|
+
fleecing: string;
|
|
86
|
+
comment: string;
|
|
87
|
+
/**
|
|
88
|
+
* PVE's own estimate, epoch seconds. REPORTED, NEVER COMPARED: `next-run` in `matches` would
|
|
89
|
+
* report an update every time the clock passed a scheduled run. It is here so a plan can show
|
|
90
|
+
* that a job is still due — a `next-run` of 0 is a job the scheduler will not run.
|
|
91
|
+
*/
|
|
92
|
+
'next-run': number;
|
|
93
|
+
}
|
|
94
|
+
export interface ProxmoxBackupJob extends Resource<'Proxmox.BackupJob', BackupJobProps, BackupJobAttributes, never, PveRequirements> {
|
|
95
|
+
}
|
|
96
|
+
export declare const ProxmoxBackupJob: import("alchemy").ResourceClass<ProxmoxBackupJob>;
|
|
97
|
+
/**
|
|
98
|
+
* ⛔ Empty for the same reason as every other resource here: adoption must be explicit.
|
|
99
|
+
* `GET /cluster/backup` would hand back the job somebody made by hand, and adopting it
|
|
100
|
+
* implicitly would give Alchemy the right to DELETE a cluster's only backup schedule the
|
|
101
|
+
* first time a line left a stack file.
|
|
102
|
+
|
|
103
|
+
*
|
|
104
|
+
* ⚠️ DELETING THE JOB DOES NOT DELETE THE BACKUPS, AND THAT CUTS BOTH WAYS. The archives
|
|
105
|
+
* stay on the storage, so this looks harmless today; it is destructive in six months,
|
|
106
|
+
* when the schedule that should have replaced them has not run since. PVE will not
|
|
107
|
+
* refuse this the way it refuses to delete a pool still holding guests — nothing depends
|
|
108
|
+
* on a backup job — so the plan line is the only warning anyone gets.
|
|
109
|
+
|
|
110
|
+
*/
|
|
111
|
+
export declare const ProxmoxBackupJobProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxBackupJob>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
112
|
+
//# sourceMappingURL=backup-job.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup-job.d.ts","sourceRoot":"","sources":["../../src/proxmox/backup-job.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;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;;;;;;GAMG;AACH,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAC;IACX,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,mGAAmG;IACnG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IACvC,2FAA2F;IAC3F,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gGAAgG;IAChG,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gGAAgG;IAChG,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oFAAoF;IACpF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,GAAG,iBAAiB,GAAG,qBAAqB,CAAC;IACzE,yDAAyD;IACzD,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,4FAA4F;IAC5F,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,qFAAqF;IACrF,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,OAAO,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAiB,SAAQ,QAAQ,CAChD,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,gBAAgB,mDAAkD,CAAC;AA2FhF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,sIACsD,CAAC"}
|