@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,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.NetworkApply` — the call that makes a declared interfaces file real, ON ONE NODE.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ WITHOUT IT EVERY NETWORK DECLARATION IS A GREEN PLAN THAT CHANGES NO NETWORK; WITH IT A GREEN
|
|
5
|
+
* PLAN CAN TAKE A NODE OFF THE NETWORK. Writes under `nodes/{node}/network/{iface}` edit
|
|
6
|
+
* `/etc/network/interfaces.new` and touch no interface. `PUT /nodes/{node}/network` is the apply.
|
|
7
|
+
* MEASURED in the cluster's own source on n2, 2026-09-13 — `PVE/API2/Network.pm`:
|
|
8
|
+
* rename($new_config_file, $current_config_file) if -e $new_config_file;
|
|
9
|
+
* PVE::Tools::run_command(['ifreload', '-a'], errfunc => $err);
|
|
10
|
+
* return $rpcenv->fork_worker('srvreload', 'networking', $authuser, $worker);
|
|
11
|
+
*
|
|
12
|
+
* ⛔ THIS IS THE MOST DANGEROUS RESOURCE IN THE PACKAGE, AND ON TB4 THE DANGER IS NOT ABSTRACT.
|
|
13
|
+
* Ceph runs over `vmbr1.11` on n2, n3 AND n4 (MEASURED 2026-09-13: four pools, size 3 /
|
|
14
|
+
* min_size 2, mon+mgr+mds on all three). `ifreload -a` takes that node's OSDs, mons and MDS off
|
|
15
|
+
* the cluster network for the length of the reload; two nodes at once puts every pool below
|
|
16
|
+
* min_size and blocks IO for every guest with an RBD disk. The management address the API is
|
|
17
|
+
* reached on, `vmbr0.10`, is reloaded by the same call.
|
|
18
|
+
*
|
|
19
|
+
* ⛔ AND UNLIKE `SdnApply` IT IS PER NODE, SO A HALF-RECONFIGURED CLUSTER IS THE DEFAULT FAILURE
|
|
20
|
+
* MODE, NOT AN EDGE CASE. `PUT /cluster/sdn` publishes the whole cluster in one call; this
|
|
21
|
+
* publishes exactly one node, so a stack holding three of these can apply n2, fail, and leave n3
|
|
22
|
+
* and n4 on the old layout with Ceph spanning all three. Four things here exist only for that:
|
|
23
|
+
* 1. `after` SERIALISES THEM — chain n3 after n2 and n4 after n3, so at most one node is ever
|
|
24
|
+
* mid-reload. Three declared in parallel is the outage, and nothing in Alchemy stops you.
|
|
25
|
+
* 2. `reconcile` REFUSES TO START on a cluster that is already degraded.
|
|
26
|
+
* 3. `reconcile` RE-CHECKS quorum and every member's `online` flag AFTER the reload and dies if
|
|
27
|
+
* the cluster came back worse — which stops the chain before it reaches the second node.
|
|
28
|
+
* 4. NOTHING IS EVER APPLIED WITH NOTHING STAGED. See the first branch of `apply`.
|
|
29
|
+
*
|
|
30
|
+
* ⚠️ IF THE MEMBER SERVING THE POLL IS THE NODE BEING RELOADED, the reload can drop the connection
|
|
31
|
+
* carrying its own status poll — see network-apply-read.ts. Every endpoint is `proxyto =>
|
|
32
|
+
* 'node'`, so any cluster member can serve any other node's paths; failover in client.ts picks
|
|
33
|
+
* whichever member is up, not necessarily one you are not applying.
|
|
34
|
+
*
|
|
35
|
+
* ⚠️ `DELETE /nodes/{node}/network` IS THE REVERT, AND IT IS DELIBERATELY NOT WIRED TO `delete`.
|
|
36
|
+
* MEASURED: the whole body of `revert_network_changes` is `unlink "/etc/network/interfaces.new"`.
|
|
37
|
+
* It discards STAGED edits and cannot undo an APPLIED one, so pointing `delete` at it would throw
|
|
38
|
+
* away whatever happened to be staged the moment somebody removed a line from a stack file — the
|
|
39
|
+
* worst possible time to do it. Revert by hand, deliberately: `pvesh delete /nodes/<node>/network`.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ PRIVILEGES, so widening stays a deliberate act: the `read` role needs nothing for the pending
|
|
42
|
+
* read (`GET /nodes/{node}/network` is `"user": "all"`) and `Sys.Audit` on `/` for the health
|
|
43
|
+
* gate; the `provision` role needs `Sys.Modify` on `/nodes/{node}` for the apply, and owns the
|
|
44
|
+
* task it starts so it can poll it without `Sys.Audit`. ⛔ `Sys.Modify` ON A NODE IS NOT A SMALL
|
|
45
|
+
* GRANT — it also carries that node's DNS, hosts file, time and service configuration. Granting
|
|
46
|
+
* it cluster-wide to run this resource widens the provisioning role well beyond networking.
|
|
47
|
+
*/
|
|
48
|
+
import { Resource } from 'alchemy';
|
|
49
|
+
import * as Provider from 'alchemy/Provider';
|
|
50
|
+
import type { PveRequirements } from './resource.ts';
|
|
51
|
+
import type { WithTarget } from './resource.ts';
|
|
52
|
+
export interface NetworkApplyProps extends WithTarget {
|
|
53
|
+
/**
|
|
54
|
+
* The node whose staged interfaces file this applies — `n2`.
|
|
55
|
+
*
|
|
56
|
+
* ⚠️ ONE APPLY PER NODE, AND NEVER TWO FOR THE SAME NODE. Two would each publish the other's
|
|
57
|
+
* staged half, and both would run `ifreload` on a file the other had not finished writing.
|
|
58
|
+
*/
|
|
59
|
+
node: string;
|
|
60
|
+
/**
|
|
61
|
+
* The staged interface changes this apply publishes, and the apply that must go before it.
|
|
62
|
+
*
|
|
63
|
+
* ⚠️ IT IS NEVER READ, AND IT IS NOT DECORATION — the same contract as `SdnApply.after`. Alchemy
|
|
64
|
+
* orders resources by DATA FLOW, so consuming an attribute is the only way to say "after".
|
|
65
|
+
* Pass the interfaces this node declares AND the previous node's apply:
|
|
66
|
+
* `after: [vmbr1.iface, n2Apply.pending]`. Leave the previous apply out and the three nodes
|
|
67
|
+
* reload in parallel, which is the one thing this resource is built to prevent.
|
|
68
|
+
*/
|
|
69
|
+
after?: readonly unknown[];
|
|
70
|
+
}
|
|
71
|
+
export interface NetworkApplyAttributes {
|
|
72
|
+
/** The node this applies. Identity, echoed so a plan line says which member it will reload. */
|
|
73
|
+
node: string;
|
|
74
|
+
/**
|
|
75
|
+
* Changed lines still staged on this node.
|
|
76
|
+
*
|
|
77
|
+
* ⚠️ LINES OF A UNIFIED DIFF, NOT INTERFACES, and the number is reported rather than trusted —
|
|
78
|
+
* `pendingLines` explains why it can undercount by one and never to zero. Zero is the only
|
|
79
|
+
* settled value: anything above it means the running config and the staged config disagree,
|
|
80
|
+
* whether this stack staged it or somebody edited the node in the UI and walked away.
|
|
81
|
+
*
|
|
82
|
+
* ⛔ THE DIFF ITSELF IS NEVER AN ATTRIBUTE. Alchemy writes attributes to its state store
|
|
83
|
+
* UNENCRYPTED, and `/etc/network/interfaces` can carry a `wpa-psk` or a `pre-up` command. Only
|
|
84
|
+
* the count leaves `network-apply-read.ts`.
|
|
85
|
+
*
|
|
86
|
+
* ⚠️ AND NOTHING OUT OF THE INTERFACE ROWS IS AN ATTRIBUTE EITHER, WHICH IS WHAT KEEPS THIS
|
|
87
|
+
* FAMILY NOOP. `GET /nodes/{node}/network` returns eighteen fields it will not accept on write
|
|
88
|
+
* — MEASURED by differencing the schema's GET return properties against its POST parameters:
|
|
89
|
+
* `active`, `exists`, `families`, `method`, `method6`, `options`, `priority`, `link-type` and
|
|
90
|
+
* ten more; `altnames` is returned and is not even in the schema. `priority` is the worst of
|
|
91
|
+
* them: PVE ASSIGNS it from the order of the interfaces file, so the same logical layout
|
|
92
|
+
* carries different numbers on different nodes (MEASURED: n4's differ from n2/n3's by one).
|
|
93
|
+
* Every one of those is a forever-diff waiting for whoever writes `Proxmox.NetworkInterface`.
|
|
94
|
+
* This resource compares a single integer against zero and never looks at a row at all.
|
|
95
|
+
*/
|
|
96
|
+
pending: number;
|
|
97
|
+
}
|
|
98
|
+
export interface ProxmoxNetworkApply extends Resource<'Proxmox.NetworkApply', NetworkApplyProps, NetworkApplyAttributes, never, PveRequirements> {
|
|
99
|
+
}
|
|
100
|
+
export declare const ProxmoxNetworkApply: import("alchemy").ResourceClass<ProxmoxNetworkApply>;
|
|
101
|
+
export declare const ProxmoxNetworkApplyProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxNetworkApply>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
102
|
+
//# sourceMappingURL=network-apply.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-apply.d.ts","sourceRoot":"","sources":["../../src/proxmox/network-apply.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8CG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAK7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD;;;;;OAKG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;OAQG;IACH,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,+FAA+F;IAC/F,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAoB,SAAQ,QAAQ,CACnD,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,mBAAmB,sDAAwD,CAAC;AA8HzF,eAAO,MAAM,2BAA2B,yIACyD,CAAC"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a node interface crosses the wire in both directions — props to a PVE form, a PVE answer to
|
|
3
|
+
* a comparable attribute — and when two of those values count as the same value.
|
|
4
|
+
*
|
|
5
|
+
* ★ SPLIT OUT OF node-network.ts TO KEEP BOTH FILES UNDER THE 250-LINE CAP, and the seam is the
|
|
6
|
+
* same one metric-server-form.ts uses, one field wider: this file owns the COERCIONS, in both
|
|
7
|
+
* directions, because a read that normalises differently from the write that produced it is
|
|
8
|
+
* exactly how a forever-diff is born — keeping `ifaceList` next to both ends is what stops that.
|
|
9
|
+
* node-network.ts answers "what is an interface, and which of its fields are safe to compare at
|
|
10
|
+
* all". Nothing here calls the cluster.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ THE `import type` BACK TO node-network.ts IS A CYCLE ON PAPER ONLY — type-only, erased before
|
|
13
|
+
* anything runs, so `NodeNetworkProps` stays in the file that declares the resource.
|
|
14
|
+
*
|
|
15
|
+
* * ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED, AND EACH ONE IS A MEASURED FOREVER-DIFF.
|
|
16
|
+
* `priority` is assigned by PVE FROM FILE ORDER — `my $priority = 2; ... $d->{priority} =
|
|
17
|
+
* $priority++` — and it is not a POST or PUT parameter at all. MEASURED ACROSS TB4: vmbr1.11 is
|
|
18
|
+
* priority 16 on n2 and n3 and 15 on n4, because n4 has no `wlp91s0` above it. One declaration
|
|
19
|
+
* reused across three nodes would diff on exactly one of them, forever, over nothing.
|
|
20
|
+
* `method` is RECOMPUTED on every write from whether the form carried an address, `families` is
|
|
21
|
+
* recomputed the same way and never written to the file, `active` and `exists` describe the
|
|
22
|
+
* kernel rather than the config, and `bond_miimon`, `bridge_stp` and `bridge_fd` are emitted by
|
|
23
|
+
* PVE's writer with defaults (100, `off`, 0) while appearing in NEITHER write schema —
|
|
24
|
+
* `additionalProperties => 0` means sending one is a 400. Live proof of all three: n2's bond0
|
|
25
|
+
* returns `bond_miimon "100"` and vmbr1 returns `bridge_stp "off"`, `bridge_fd "0"`, none of
|
|
26
|
+
* which any declaration can set.
|
|
27
|
+
*/
|
|
28
|
+
import type { NodeNetworkAttributes, NodeNetworkProps } from './node-network.ts';
|
|
29
|
+
/**
|
|
30
|
+
* "Not set", for the three integers this family reports.
|
|
31
|
+
*
|
|
32
|
+
* ⚠️ 0 CANNOT BE THE ABSENT-MARKER AND -1 CAN. `mtu` is 1280-65520, `vlan-id` 1-4094 and PVE's
|
|
33
|
+
* `priority` starts at 1 (1 is reserved for `lo`), so -1 is outside every one of their ranges
|
|
34
|
+
* while 0 is merely outside today's. metric-server-form.ts keeps its own constant for the same
|
|
35
|
+
* reason and against different ranges; one shared UNSET would have to be right for both.
|
|
36
|
+
*/
|
|
37
|
+
export declare const UNSET = -1;
|
|
38
|
+
/**
|
|
39
|
+
* A space-separated interface list, flattened to one comparable form.
|
|
40
|
+
*
|
|
41
|
+
* ⚠️ `bridge_ports` AND `slaves` ARE SETS, AND PVE NORMALISES THEIR SEPARATORS BUT NOT THEIR ORDER.
|
|
42
|
+
* MEASURED in PVE::Network::Interfaces: the writer does `s/[;,\s]+/ /g` on both before emitting
|
|
43
|
+
* them, and the reader hands the resulting string straight back — so `enp2s0f1np1 enp2s0f0np0`
|
|
44
|
+
* survives a round trip in the order it was written. Order carries no meaning for a bridge's
|
|
45
|
+
* ports or a bond's slaves, so a declaration that lists them the other way round is the SAME
|
|
46
|
+
* declaration; comparing raw strings would report an update that rewrites the file to say
|
|
47
|
+
* exactly what it already said. Both sides are sorted here and only then compared.
|
|
48
|
+
*
|
|
49
|
+
* ⚠️ IT IS NOT `csv` FROM values.ts. That one joins with commas, and a comma in
|
|
50
|
+
* `/etc/network/interfaces` is not a separator — PVE's own comment above the writer says the
|
|
51
|
+
* list "must be space separated! other separators will cause problems". Sending a comma-joined
|
|
52
|
+
* value would be accepted by `split_list` and then written back as spaces, which is a second
|
|
53
|
+
* spelling of one value and therefore a second chance to diff.
|
|
54
|
+
*
|
|
55
|
+
* ⚠️ `bridge_vids` GOES THROUGH THE SAME FUNNEL and its sort is LEXICAL, which is correct only
|
|
56
|
+
* because a vid list is a set: `2-4094` is one token, and `100-200 2` and `2 100-200` allow the
|
|
57
|
+
* same VLANs. Nothing here should be reused for a list whose order is meaning.
|
|
58
|
+
*/
|
|
59
|
+
export declare const ifaceList: (value: unknown) => string;
|
|
60
|
+
/**
|
|
61
|
+
* A comment off the wire, with the newline PVE adds taken back off.
|
|
62
|
+
*
|
|
63
|
+
* ⛔ `comments` NEVER ROUND-TRIPS RAW, AND THAT IS A FOREVER-DIFF IF IT IS NOT NORMALISED.
|
|
64
|
+
* MEASURED in the reader: each `#...` line is appended as `$comment . "\n"`, so a declared
|
|
65
|
+
* `ceph transport` comes back as `ceph transport\n` and the two never compare equal.
|
|
66
|
+
* ⚠️ AND THE READER FOLDS `comments6` INTO `comments` (`$d->{comments} .= $comments6`), so on an
|
|
67
|
+
* interface that carries an IPv6 comment a declared `comments` CANNOT match what comes back.
|
|
68
|
+
* TB4 has none; on an interface that does, leave `comments` undeclared rather than fighting it.
|
|
69
|
+
*/
|
|
70
|
+
export declare const comment: (value: unknown) => string;
|
|
71
|
+
/** Undeclared is unmanaged: neither sent nor compared. See the ⚠️ on `body`. */
|
|
72
|
+
export declare const same: <T>(declared: T | undefined, live: T) => boolean;
|
|
73
|
+
export declare const sameList: (declared: string | undefined, live: string) => boolean;
|
|
74
|
+
export declare const sameComment: (declared: string | undefined, live: string) => boolean;
|
|
75
|
+
/**
|
|
76
|
+
* The fields sent on EVERY write, create and update alike.
|
|
77
|
+
*
|
|
78
|
+
* ⚠️ UNDECLARED IS UNMANAGED — not sent, and not compared either. PVE's PUT MERGES the form into
|
|
79
|
+
* the existing stanza (`foreach my $k (keys %$param) { $ifaces->{$iface}->{$k} = $param->{$k} }`),
|
|
80
|
+
* so an omitted parameter is left exactly as it was. `cidr` is the one exception and it is not
|
|
81
|
+
* a small one — see the ⛔ in node-network.ts.
|
|
82
|
+
*
|
|
83
|
+
* ⛔ `type` IS REQUIRED ON THE PUT, NOT ONLY ON THE POST. It carries no `optional` in either
|
|
84
|
+
* schema, and the merge above writes it straight into the stanza — so a PUT naming the wrong
|
|
85
|
+
* type would RETYPE A LIVE INTERFACE in the file and the writer would then emit a bridge stanza
|
|
86
|
+
* for what is really a vlan. node-network.ts refuses that case before it can reach here.
|
|
87
|
+
*
|
|
88
|
+
* ⛔ `bridge_vlan_aware` IS SENT ONLY WHEN TRUE, AND A FALSE ONE IS A `delete=`. MEASURED in the
|
|
89
|
+
* writer: the test is `if (defined($d->{bridge_vlan_aware}))`, not a truth test, so `0` is
|
|
90
|
+
* defined and PVE writes `bridge-vlan-aware yes` anyway. Sending `0` would therefore turn the
|
|
91
|
+
* flag ON, read back as `1`, and diff forever while lying about which way it went.
|
|
92
|
+
*
|
|
93
|
+
* ⚠️ `netmask` IS A WRITABLE PARAMETER AND IS DELIBERATELY NOT OFFERED. PVE raises
|
|
94
|
+
* "netmask conflicts with cidr" when both are sent, and the reader rewrites whatever was written
|
|
95
|
+
* into prefix form — MEASURED: n2's vmbr1.11 reports `"netmask":"24"`, never `255.255.255.0`.
|
|
96
|
+
* A resource accepting both spellings would let a declaration diff against its own value.
|
|
97
|
+
*/
|
|
98
|
+
export declare const body: (props: NodeNetworkProps) => Record<string, string>;
|
|
99
|
+
/**
|
|
100
|
+
* The update form: `body` plus the two fields that can only be cleared explicitly.
|
|
101
|
+
*
|
|
102
|
+
* ⛔ `delete=cidr` IS SENT WHENEVER `cidr` IS UNDECLARED, AND IT IS THE LESSER OF TWO EVILS RATHER
|
|
103
|
+
* THAN A GOOD OUTCOME. PVE recomputes the method on every write from the form alone —
|
|
104
|
+
* `$param->{method} = $param->{address} ? 'static' : 'manual'` — so a PUT WITHOUT an address
|
|
105
|
+
* makes the interface manual no matter what the caller intended. Left at that, the merge would
|
|
106
|
+
* leave the old `address` and `netmask` in the hash and the writer would emit an `address` line
|
|
107
|
+
* under `iface … inet manual`: a stanza PVE's own UI cannot produce, whose behaviour under
|
|
108
|
+
* `ifreload -a` I did NOT measure. Clearing cidr explicitly at least produces a clean, honest
|
|
109
|
+
* manual interface — and `matches` compares `cidr` UNCONDITIONALLY, so the plan says so first.
|
|
110
|
+
*
|
|
111
|
+
* ⛔ ON vmbr1.11 THAT IS THE CEPH TRANSPORT. A declaration of that interface without its `cidr` is
|
|
112
|
+
* a declaration that it should have no address, applied across three nodes. The plan will read
|
|
113
|
+
* `1 to update` rather than `noop`; do not wave it through.
|
|
114
|
+
*
|
|
115
|
+
* ⚠️ `delete` IS A PUT-ONLY PARAMETER. The POST schema is `additionalProperties => 0` and has no
|
|
116
|
+
* `delete`, so sending it on a create is a 400 — which is why `createForm` calls `body` directly.
|
|
117
|
+
*/
|
|
118
|
+
export declare const updateBody: (props: NodeNetworkProps) => Record<string, string>;
|
|
119
|
+
/**
|
|
120
|
+
* One live interface as attributes, or `undefined` — "this is not really there".
|
|
121
|
+
*
|
|
122
|
+
* ⚠️ EVERY FIELD PVE REPORTS IS CARRIED HERE, INCLUDING THE SEVEN NO DECLARATION CAN SET. A plan
|
|
123
|
+
* that cannot show `priority`, `method` or `bond_miimon` cannot explain why it is ignoring them,
|
|
124
|
+
* and the next person to read a forever-diff would start by adding them to `matches`. They are
|
|
125
|
+
* reported precisely so that they are visibly OUT of it — see the ⛔ above the attribute type.
|
|
126
|
+
*/
|
|
127
|
+
export declare const readAttributes: (live: Record<string, unknown>, props: NodeNetworkProps) => NodeNetworkAttributes | undefined;
|
|
128
|
+
//# sourceMappingURL=node-network-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-network-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAGjF;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,KAAK,CAAC;AAExB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS,UAAW,OAAO,WAK1B,CAAC;AAEf;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,UAAW,OAAO,WAAoC,CAAC;AAE3E,gFAAgF;AAChF,eAAO,MAAM,IAAI,GAAI,CAAC,YAAY,CAAC,GAAG,SAAS,QAAQ,CAAC,YACX,CAAC;AAE9C,eAAO,MAAM,QAAQ,aAAc,MAAM,GAAG,SAAS,QAAQ,MAAM,YACX,CAAC;AAEzD,eAAO,MAAM,WAAW,aAAc,MAAM,GAAG,SAAS,QAAQ,MAAM,YAChB,CAAC;AAKvD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,IAAI,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAgBlE,CAAC;AAEH;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,UAAU,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAOzE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,SACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SACtB,gBAAgB,KACtB,qBAAqB,GAAG,SAuC1B,CAAC"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.NodeNetwork` — one stanza of a node's `/etc/network/interfaces`, declared.
|
|
3
|
+
*
|
|
4
|
+
* ⛔ DECLARING THIS RESOURCE IS SAFE. APPLYING IT IS A SEPARATE, DELIBERATE ACT, AND IT IS NOT
|
|
5
|
+
* PERFORMED HERE. Every write under `/nodes/{node}/network/{iface}` lands in
|
|
6
|
+
* `/etc/network/interfaces.new` and changes no running interface. MEASURED, from PVE::INotify:
|
|
7
|
+
* `$shadowfiles = { '/etc/network/interfaces' => '/etc/network/interfaces.new' }`, and
|
|
8
|
+
* `read_file` reads the SHADOW when it exists. The apply is `PUT /nodes/{node}/network`, which
|
|
9
|
+
* renames the shadow over the real file and runs `ifreload -a`; the revert is
|
|
10
|
+
* `DELETE /nodes/{node}/network`, which simply unlinks the shadow. A sibling resource models
|
|
11
|
+
* that apply — this one never calls it, exactly as `Proxmox.SdnZone` never calls
|
|
12
|
+
* `Proxmox.SdnApply`.
|
|
13
|
+
*
|
|
14
|
+
* ⛔ CEPH ON TB4 RIDES TWO NETWORKS AND THIS API ONLY SEES ONE OF THEM. Measured from
|
|
15
|
+
* `/etc/pve/ceph.conf` on 2026-09-13:
|
|
16
|
+
*
|
|
17
|
+
* public_network = 10.20.11.0/24 -> vmbr1.11, which this resource DOES see
|
|
18
|
+
* cluster_network = 10.100.0.0/24 -> every OSD's cluster_addr: .102 / .103 / .104
|
|
19
|
+
*
|
|
20
|
+
* The cluster network — OSD REPLICATION, the traffic that rebuilds a lost replica — lives on
|
|
21
|
+
* `10.100.0.10X/32` addresses carried by `en05`, `en06` and `dummy_tb4`: the Thunderbolt mesh.
|
|
22
|
+
*
|
|
23
|
+
* ⛔ AND THOSE ADDRESSES ARE INVISIBLE HERE, WHICH IS THE TRAP. They are declared in
|
|
24
|
+
* `/etc/network/interfaces.d/sdn`, and PVE's network API does not parse that directory.
|
|
25
|
+
* MEASURED: `GET /nodes/n2/network/en05` answers `{"method":"manual","type":"eth",…}` with NO
|
|
26
|
+
* address, while `ip -br -4 addr` shows `en05 UP 10.100.0.102/32`. So this resource reports
|
|
27
|
+
* en05 and en06 as address-less manual ports and will plan `noop` over them — implicitly
|
|
28
|
+
* asserting they carry no address, which is FALSE.
|
|
29
|
+
*
|
|
30
|
+
* The consequence, stated plainly: declaring en05/en06 from what this API reports and then
|
|
31
|
+
* DESTROYING or applying over them takes out OSD replication on a cluster whose pools are
|
|
32
|
+
* size 3 / min_size 2. They read as the most boring interfaces on the node and are the two most
|
|
33
|
+
* dangerous. Leave them undeclared; if they must be managed, manage the file that actually
|
|
34
|
+
* owns them, not this resource.
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ Two ways this file limits the damage on the interfaces it CAN see: `cidr` is compared
|
|
37
|
+
* unconditionally (the ⛔ on `matches`), and a live interface whose `type` disagrees with the
|
|
38
|
+
* declaration is reported ABSENT rather than retyped (the ⛔ in `attributes`).
|
|
39
|
+
*
|
|
40
|
+
* ★ THE REASON IT EXISTS IS THAT THE STAGED READ CANNOT TELL YOU THE NETWORK IS RIGHT — it can
|
|
41
|
+
* only tell you the FILE is. Once anything is staged, `GET /nodes/{node}/network/{iface}`
|
|
42
|
+
* answers from `interfaces.new`, so `matches` compares a declaration against a pending file and
|
|
43
|
+
* reports `noop` over a node whose running config has not moved. The apply resource is what
|
|
44
|
+
* closes that gap; this resource's `noop` means "the file says what you said", no more.
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ PRIVILEGES ARE COARSER HERE THAN ANYWHERE ELSE IN THE PACKAGE: reads want `Sys.Audit` on
|
|
47
|
+
* `/nodes/{node}`, every write `Sys.Modify` on it — and there is no `/nodes/{node}/network` ACL
|
|
48
|
+
* object to scope to, so that also buys the node's services, certificates, DNS and time.
|
|
49
|
+
* Collected with every other family in docs/privileges.md.
|
|
50
|
+
*
|
|
51
|
+
* ★ A RECONCILE OVER A MATCHING INTERFACE IS A NO-WRITE, AND AN EARLIER DRAFT OF THIS COMMENT
|
|
52
|
+
* SAID THE OPPOSITE. `pveOperations.reconcile` gates on `matches` BEFORE the PUT
|
|
53
|
+
* (`resource.ts`: `else if (spec.updateForm !== undefined && !spec.matches(live, news))`), and
|
|
54
|
+
* the ⛔ above that line names THIS family as the reason the guard was added — adopting an
|
|
55
|
+
* interface a node already has would otherwise leave it holding a pending network change.
|
|
56
|
+
*
|
|
57
|
+
* ⚠️ WHAT IS STILL TRUE: when `matches` is false the PUT rewrites the whole stanza, and that can
|
|
58
|
+
* ADD lines the file did not have — a `vlan-raw-device vmbr1` under `vmbr1.11`, where PVE had
|
|
59
|
+
* been deriving it from the name. Harmless in content, and it costs nothing until somebody
|
|
60
|
+
* applies; one more reason the apply is a separate resource.
|
|
61
|
+
*
|
|
62
|
+
* ⚠️ `list` IS EMPTY, AND HERE THAT MATTERS MORE THAN ANYWHERE ELSE IN THIS PACKAGE. The index
|
|
63
|
+
* returns every interface the node has, `vmbr0`, `bond0` and the Ceph vlan included. Adopting
|
|
64
|
+
* them would put Alchemy one `destroy` away from staging the removal of a node's uplink — and
|
|
65
|
+
* `delete_network` has NO in-use check at all: MEASURED, it deletes the hash entry and writes
|
|
66
|
+
* the file. Adoption stays explicit.
|
|
67
|
+
*/
|
|
68
|
+
import { Resource } from 'alchemy';
|
|
69
|
+
import * as Provider from 'alchemy/Provider';
|
|
70
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
71
|
+
/**
|
|
72
|
+
* PVE's interface types. `unknown` is what a NIC it does not manage reports — n2 and n3 both
|
|
73
|
+
* report `wlp91s0` that way, and n4 has no such interface at all.
|
|
74
|
+
*
|
|
75
|
+
* ⚠️ THAT ASYMMETRY IS A TRAP FOR A DECLARATION SHARED ACROSS NODES. On n4 the read answers 404,
|
|
76
|
+
* which this provider cannot tell from "deleted", so reconcile POSTs — writing a stanza for a
|
|
77
|
+
* card that is not in the machine. Declare per node what each node actually has; a physical
|
|
78
|
+
* interface is discovered, not decided.
|
|
79
|
+
*/
|
|
80
|
+
export type NodeNetworkType = 'OVSBond' | 'OVSBridge' | 'OVSIntPort' | 'OVSPort' | 'alias' | 'bond' | 'bridge' | 'eth' | 'fabric' | 'unknown' | 'vlan' | 'vnet';
|
|
81
|
+
export interface NodeNetworkProps extends WithTarget {
|
|
82
|
+
/** Which node's file this stanza lives in. Interfaces are per node, never cluster-wide. */
|
|
83
|
+
node: string;
|
|
84
|
+
/** `vmbr0`, `bond0`, `vmbr1.11`. 2-20 characters, PVE's `pve-iface` format. */
|
|
85
|
+
iface: string;
|
|
86
|
+
/** ⛔ REQUIRED ON EVERY WRITE, update included, never used to retype — see `readAttributes`. */
|
|
87
|
+
type: NodeNetworkType;
|
|
88
|
+
/**
|
|
89
|
+
* `10.20.11.12/24`. ⛔ THE ONE FIELD WITH NO UNMANAGED MODE: leaving it out is an instruction
|
|
90
|
+
* to make the interface `manual`, not an instruction to leave its address alone. The ⛔ on
|
|
91
|
+
* `updateBody` in node-network-form.ts has the measurement and the consequence.
|
|
92
|
+
*/
|
|
93
|
+
cidr?: string;
|
|
94
|
+
/** ⚠️ PVE allows exactly ONE default gateway per node and refuses a second with "Default
|
|
95
|
+
* gateway already exists on interface '<other>'". On TB4 it is vmbr0.10's. */
|
|
96
|
+
gateway?: string;
|
|
97
|
+
/**
|
|
98
|
+
* `auto <iface>` in the file. Absent on read means off, which is why `bool`'s fallback is used.
|
|
99
|
+
* ⚠️ UNDECLARED IS UNMANAGED ON AN UPDATE AND OFF ON A CREATE — the same asymmetry storage.ts
|
|
100
|
+
* has, and it bites harder here: an interface created without `autostart` is one the node
|
|
101
|
+
* will not bring up at boot. Every TB4 bridge, bond and vlan carries it.
|
|
102
|
+
*/
|
|
103
|
+
autostart?: boolean;
|
|
104
|
+
/** 1280-65520. Unset leaves the file without an `mtu` line and the kernel default in force. */
|
|
105
|
+
mtu?: number;
|
|
106
|
+
/** ⚠️ Round-trips only after normalisation — see `comment` in node-network-form.ts. */
|
|
107
|
+
comments?: string;
|
|
108
|
+
/** Space-separated, a SET: `enp87s0`, or `bond0`. ⚠️ PVE refuses a port already used elsewhere. */
|
|
109
|
+
bridge_ports?: string;
|
|
110
|
+
/** `2-4094`, or `2 100-200`. Only written when `bridge_vlan_aware` is on. */
|
|
111
|
+
bridge_vids?: string;
|
|
112
|
+
/** ⛔ A `false` here is sent as `delete=`, never as `0` — the ⛔ on `body` says why. */
|
|
113
|
+
bridge_vlan_aware?: boolean;
|
|
114
|
+
/**
|
|
115
|
+
* A bond's members, space separated. ⚠️ THE PARAMETER IS `slaves`, NOT `bond_slaves`, in BOTH
|
|
116
|
+
* directions on this PVE: MEASURED, the POST/PUT schema names only `slaves` and the GET returns
|
|
117
|
+
* `"slaves":"enp2s0f0np0 enp2s0f1np1"`. `bond_slaves` appears nowhere in Network.pm here.
|
|
118
|
+
*/
|
|
119
|
+
slaves?: string;
|
|
120
|
+
bond_mode?: string;
|
|
121
|
+
/** ⚠️ Only written when `bond_mode` is `balance-xor` or `802.3ad`; ignored otherwise. */
|
|
122
|
+
bond_xmit_hash_policy?: string;
|
|
123
|
+
/** active-backup only. Kept hyphenated because that is the wire name. */
|
|
124
|
+
'bond-primary'?: string;
|
|
125
|
+
/** ⚠️ DERIVED FROM A DOTTED NAME. `vmbr1.11` reports `vlan-id` 11 with no such line in the file;
|
|
126
|
+
* declaring it there is harmless but adds a line the file did not have. */
|
|
127
|
+
'vlan-id'?: number;
|
|
128
|
+
'vlan-raw-device'?: string;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* ⛔ THE LAST SEVEN ARE REPORTED AND NEVER COMPARED — each is a MEASURED forever-diff, and the
|
|
132
|
+
* evidence for every one of them is in node-network-form.ts, beside the code that reads them.
|
|
133
|
+
*/
|
|
134
|
+
export interface NodeNetworkAttributes {
|
|
135
|
+
node: string;
|
|
136
|
+
iface: string;
|
|
137
|
+
type: string;
|
|
138
|
+
cidr: string;
|
|
139
|
+
gateway: string;
|
|
140
|
+
autostart: boolean;
|
|
141
|
+
mtu: number;
|
|
142
|
+
comments: string;
|
|
143
|
+
bridge_ports: string;
|
|
144
|
+
bridge_vids: string;
|
|
145
|
+
bridge_vlan_aware: boolean;
|
|
146
|
+
slaves: string;
|
|
147
|
+
bond_mode: string;
|
|
148
|
+
bond_xmit_hash_policy: string;
|
|
149
|
+
'bond-primary': string;
|
|
150
|
+
'vlan-id': number;
|
|
151
|
+
'vlan-raw-device': string;
|
|
152
|
+
priority: number;
|
|
153
|
+
method: string;
|
|
154
|
+
families: string;
|
|
155
|
+
active: boolean;
|
|
156
|
+
exists: boolean;
|
|
157
|
+
bond_miimon: string;
|
|
158
|
+
bridge_stp: string;
|
|
159
|
+
bridge_fd: string;
|
|
160
|
+
}
|
|
161
|
+
export interface ProxmoxNodeNetwork extends Resource<'Proxmox.NodeNetwork', NodeNetworkProps, NodeNetworkAttributes, never, PveRequirements> {
|
|
162
|
+
}
|
|
163
|
+
/** ★ `retain` by default — a node's only uplink cannot be rebuilt. See the ★ in resource.ts. */
|
|
164
|
+
export declare const ProxmoxNodeNetwork: import("alchemy").ResourceClass<ProxmoxNodeNetwork>;
|
|
165
|
+
/**
|
|
166
|
+
* ⚠️ THE DESTROY IS STAGED LIKE EVERY OTHER WRITE, WHICH MAKES IT THE ONE RECOVERABLE DESTROY IN
|
|
167
|
+
* THIS PACKAGE. It removes the stanza from `interfaces.new` and nothing else;
|
|
168
|
+
* `DELETE /nodes/{node}/network` puts the node back by unlinking that file. It becomes
|
|
169
|
+
* irreversible the moment somebody applies — and PVE performs NO in-use check before removing
|
|
170
|
+
* an interface other bridges or Ceph still stand on.
|
|
171
|
+
*/
|
|
172
|
+
export declare const ProxmoxNodeNetworkProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxNodeNetwork>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
173
|
+
//# sourceMappingURL=node-network.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-network.d.ts","sourceRoot":"","sources":["../../src/proxmox/node-network.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAU7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAEnF;;;;;;;;GAQG;AACH,MAAM,MAAM,eAAe,GACvB,SAAS,GACT,WAAW,GACX,YAAY,GACZ,SAAS,GACT,OAAO,GACP,MAAM,GACN,QAAQ,GACR,KAAK,GACL,QAAQ,GACR,SAAS,GACT,MAAM,GACN,MAAM,CAAC;AAEX,MAAM,WAAW,gBAAiB,SAAQ,UAAU;IAClD,2FAA2F;IAC3F,IAAI,EAAE,MAAM,CAAC;IACb,+EAA+E;IAC/E,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,IAAI,EAAE,eAAe,CAAC;IACtB;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;mFAC+E;IAC/E,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,+FAA+F;IAC/F,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mGAAmG;IACnG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sFAAsF;IACtF,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yFAAyF;IACzF,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;gFAC4E;IAC5E,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,qDAE7B,CAAC;AAsCH;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,wIACwD,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A notification target's props, as the form PVE wants — including the one field in this package
|
|
3
|
+
* that cannot be spelled as a comma list.
|
|
4
|
+
*
|
|
5
|
+
* ★ SPLIT OUT OF notification-target.ts TO KEEP BOTH UNDER THE 250-LINE CAP. The seam is the same
|
|
6
|
+
* one metric-server uses: this file turns a declaration into a form, and the resource file says
|
|
7
|
+
* what a target IS and when it has changed. The `import type` back is erased, so it is a cycle
|
|
8
|
+
* on paper only.
|
|
9
|
+
*/
|
|
10
|
+
import type { NotificationTargetProps } from './notification-target.ts';
|
|
11
|
+
/** ⚠️ `0` is "PVE holds no port". Integers have come back as strings elsewhere, so parse both. */
|
|
12
|
+
export declare const portOf: (value: unknown) => number;
|
|
13
|
+
/**
|
|
14
|
+
* ⚠️ ORDER IS NOT MEANING IN A RECIPIENT LIST, SO IT MUST NOT BE A DIFF — backup-job.ts's reasoning
|
|
15
|
+
* about guest ids, applied to addresses. Both sides are sorted before they meet.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ PVE ANSWERS WITH AN ARRAY AND ACCEPTS A DELIMITED STRING, and this package has already been
|
|
18
|
+
* bitten by a list coming back flattened on one version and structured on another (`groups` in
|
|
19
|
+
* user.ts). PVE's UI calls `mailto` "separated by spaces, commas or semicolons", so all three are
|
|
20
|
+
* split here and both shapes funnel into one comparable value.
|
|
21
|
+
*/
|
|
22
|
+
export declare const addressList: (value: unknown) => string;
|
|
23
|
+
/** ⛔ NEVER SPLIT ON A COMMA: a header item contains one. A lone string is one item, not a list. */
|
|
24
|
+
export declare const headerList: (value: unknown) => string;
|
|
25
|
+
/**
|
|
26
|
+
* The form for both create and update.
|
|
27
|
+
*
|
|
28
|
+
* ⛔ A FIELD THE DECLARATION LEAVES OUT IS NOT SENT AND NOT COMPARED — backup-job.ts's rule rather
|
|
29
|
+
* than sdn-vnet.ts's `delete` list, and here the choice is not close. That list would have to
|
|
30
|
+
* name `mailto`, so a target adopted from the UI and declared without its recipients would have
|
|
31
|
+
* them stripped on the first deploy: the one object that makes failures visible, turned back into
|
|
32
|
+
* silence. `delete` on this family is also `type: array` rather than the comma string every other
|
|
33
|
+
* endpoint takes, so sdn-vnet's encoding would not even carry.
|
|
34
|
+
* ⚠️ THE PRICE IS THAT REMOVING A LINE DOES NOT CLEAR A FIELD. Declare it as `''` to clear it,
|
|
35
|
+
* which IS sent: an empty string is a value, an absent prop is no request at all.
|
|
36
|
+
*
|
|
37
|
+
* ⚠️ `disable` IS ALWAYS SENT AND ALWAYS COMPARED, `0` INCLUDED. PVE documents the default as 0, so
|
|
38
|
+
* sending it explicitly is what makes "not declared" and "declared false" one state instead of a
|
|
39
|
+
* plan that asks for an update the PUT never performs. It is also the drift that matters most
|
|
40
|
+
* here: a target disabled by hand during a maintenance window and never re-enabled looks exactly
|
|
41
|
+
* like a healthy cluster.
|
|
42
|
+
*/
|
|
43
|
+
export declare const shape: (props: NotificationTargetProps) => Record<string, string>;
|
|
44
|
+
//# sourceMappingURL=notification-target-form.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-target-form.d.ts","sourceRoot":"","sources":["../../src/proxmox/notification-target-form.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAoBxE,kGAAkG;AAClG,eAAO,MAAM,MAAM,UAAW,OAAO,KAAG,MAGvC,CAAC;AASF;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,UAAW,OAAO,KAAG,MAO1C,CAAC;AAEJ,mGAAmG;AACnG,eAAO,MAAM,UAAU,UAAW,OAAO,KAAG,MAC+C,CAAC;AAK5F;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,KAAK,UAAW,uBAAuB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAkB1E,CAAC"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Proxmox.NotificationTarget` — where a failed backup goes to be seen.
|
|
3
|
+
*
|
|
4
|
+
* ★ THE FAILURE MODE THIS REMOVES IS SILENCE. A failed vzdump, a lost HA service and a broken
|
|
5
|
+
* replication job all reach a human through one mechanism: a notification target. Out of the box
|
|
6
|
+
* a cluster has exactly one, the built-in `mail-to-root` — one mailbox, on one node, delivered by
|
|
7
|
+
* that node's local MTA. Declared, a failure becomes visible; undeclared, nothing looks wrong
|
|
8
|
+
* until somebody goes looking for a backup that was never taken. Every other resource in this
|
|
9
|
+
* package fails loudly. This is the one whose absence fails quietly.
|
|
10
|
+
*
|
|
11
|
+
* ⛔ THE PER-OBJECT READ GOES THROUGH `endpoints/{type}/{name}`, NEVER `targets/{name}`.
|
|
12
|
+
* `GET /cluster/notifications/targets/{name}` answers "Method not implemented" — MEASURED, and
|
|
13
|
+
* corroborated by the cluster's own apidoc, where that path node carries no methods at all.
|
|
14
|
+
* `/targets` is a READ-ONLY UNION VIEW implementing the collection GET and `{name}/test` and
|
|
15
|
+
* nothing else. So `type` is IDENTITY here, not a setting: it appears in both `path()` and
|
|
16
|
+
* `collection()`, and changing it is a replace rather than an update.
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ A BUILT-IN TARGET CAN BE ADOPTED AND STILL NOT BE OWNED. The shipped entries report
|
|
19
|
+
* `origin: builtin` or `modified-builtin`, and a DELETE on one REVERTS it to shipped defaults
|
|
20
|
+
* instead of removing it: Alchemy drops the resource from state while the target is still on the
|
|
21
|
+
* cluster delivering mail. Check `origin` on `/cluster/notifications/targets` before declaring a
|
|
22
|
+
* name you did not create. ⚠️ `origin` IS DELIBERATELY NOT AN ATTRIBUTE — the schema returns it
|
|
23
|
+
* on the collection view only, never on the per-object read, so an attribute would read "unknown"
|
|
24
|
+
* for every object forever, and an attribute that always lies is worse than a true comment.
|
|
25
|
+
*
|
|
26
|
+
* ⛔ NO SECRET IS A PROP, LET ALONE AN ATTRIBUTE. smtp `password`, gotify `token` and webhook
|
|
27
|
+
* `secret` are write-only and none is declarable here. Props are persisted too, not just
|
|
28
|
+
* attributes — `delete` is handed `olds`, which can only have come from the state store — so a
|
|
29
|
+
* secret prop would sit unencrypted in the state Postgres for as long as the resource exists.
|
|
30
|
+
* ⚠️ THE COST IS REAL: `token` is REQUIRED on a gotify create, so a gotify target cannot be
|
|
31
|
+
* created from here at all. Create one out of band with its secret and then declare it — the
|
|
32
|
+
* read finds it, reconcile takes the PUT path, and PVE leaves any field the body does not
|
|
33
|
+
* mention alone. The secret half stays a human's; the rest is declared. Same for an
|
|
34
|
+
* authenticated smtp target and a signed webhook.
|
|
35
|
+
*
|
|
36
|
+
* ★ WHAT THIS NEEDS, READ OFF THE CLUSTER'S OWN SCHEMA:
|
|
37
|
+
* read/diff GET endpoints/{type}/{name} Mapping.Audit (or Mapping.Modify) on
|
|
38
|
+
* /mapping/notifications
|
|
39
|
+
* reconcile POST + PUT Mapping.Modify on /mapping/notifications AND one of
|
|
40
|
+
* Sys.Audit / Sys.Modify / Sys.AccessNetwork on /
|
|
41
|
+
* delete DELETE Mapping.Modify on /mapping/notifications
|
|
42
|
+
* `LXCProvisioner` already carries Sys.Audit on `/` (see lxc.ts), so the delta is the two Mapping
|
|
43
|
+
* privileges — notably NOT the `Sys.Modify` a backup job costs.
|
|
44
|
+
* ⚠️ PREFER A SECOND ROLE GRANTED AT `/mapping/notifications` OVER WIDENING THE PROVISIONING ROLE
|
|
45
|
+
* ON `/`. `/mapping` also holds the PCI and USB passthrough maps, so Mapping.Modify at the root
|
|
46
|
+
* lets a credential scoped to make containers rewire somebody's hardware.
|
|
47
|
+
* ⛔ A MISSING READ PRIVILEGE DOES NOT LOOK LIKE A 403. `pveOperations.read` ends in
|
|
48
|
+
* `orElseSucceed(() => undefined)`, so a 403 on the GET is indistinguishable from "absent": the
|
|
49
|
+
* plan says create, PVE answers "already exists", and the fault was an ACL all along. That pair
|
|
50
|
+
* is the signature — grant Mapping.Audit to the READ role before changing anything else.
|
|
51
|
+
*/
|
|
52
|
+
import { Resource } from 'alchemy';
|
|
53
|
+
import * as Provider from 'alchemy/Provider';
|
|
54
|
+
import { type PveRequirements, type WithTarget } from './resource.ts';
|
|
55
|
+
/** The four families PVE ships. `GET /cluster/notifications/endpoints` lists exactly these. */
|
|
56
|
+
export type NotificationTargetType = 'gotify' | 'sendmail' | 'smtp' | 'webhook';
|
|
57
|
+
/**
|
|
58
|
+
* ⚠️ PVE'S OWN KEY NAMES, DASHES INCLUDED, for the reason backup-job.ts gives: the form is then a
|
|
59
|
+
* copy rather than a translation table, and a translation table is one more place for a key to be
|
|
60
|
+
* renamed and silently never sent.
|
|
61
|
+
*
|
|
62
|
+
* ⚠️ A FIELD THAT DOES NOT BELONG TO `type` IS STILL SENT, and PVE refuses it by name. That 400
|
|
63
|
+
* beats this file quietly dropping a `mailto` somebody wrote on a webhook.
|
|
64
|
+
*/
|
|
65
|
+
export interface NotificationTargetProps extends WithTarget {
|
|
66
|
+
/** ⛔ IDENTITY: it picks the family AND the URL, so changing it is a replace. */
|
|
67
|
+
type: NotificationTargetType;
|
|
68
|
+
/**
|
|
69
|
+
* PVE's primary key, unique ACROSS ALL FOUR FAMILIES: a webhook cannot be called `mail-to-root`,
|
|
70
|
+
* because a sendmail target already is. Format `pve-configid`.
|
|
71
|
+
*/
|
|
72
|
+
name: string;
|
|
73
|
+
/** Free text in the UI. */
|
|
74
|
+
comment?: string;
|
|
75
|
+
/** Configured but not delivering. Always sent and always compared — see `shape`. */
|
|
76
|
+
disable?: boolean;
|
|
77
|
+
/** sendmail, smtp: recipients. ⚠️ Order is not meaning — see `addressList`. */
|
|
78
|
+
mailto?: readonly string[];
|
|
79
|
+
/** sendmail, smtp: PVE users, whose own configured address receives the mail. */
|
|
80
|
+
'mailto-user'?: readonly string[];
|
|
81
|
+
/** smtp: the SMTP host. gotify: the server URL. PVE requires it on create for both. */
|
|
82
|
+
server?: string;
|
|
83
|
+
/** smtp only. Unset lets PVE pick from `mode`: 465 tls, 587 starttls, 25 insecure. */
|
|
84
|
+
port?: number;
|
|
85
|
+
/** smtp only. PVE's schema default is `tls`. */
|
|
86
|
+
mode?: 'insecure' | 'starttls' | 'tls';
|
|
87
|
+
/** smtp only. ⛔ The matching `password` is not declarable — see the header. */
|
|
88
|
+
username?: string;
|
|
89
|
+
/** smtp: PVE requires it on create. sendmail: optional. */
|
|
90
|
+
'from-address'?: string;
|
|
91
|
+
/** Display name on the mail. PVE's smtp schema defaults it to `Proxmox VE`. */
|
|
92
|
+
author?: string;
|
|
93
|
+
/** webhook: PVE requires it on create. */
|
|
94
|
+
url?: string;
|
|
95
|
+
/** webhook: PVE requires it on create. Lower case — PVE's enum is `post|put|get`. */
|
|
96
|
+
method?: 'get' | 'post' | 'put';
|
|
97
|
+
/**
|
|
98
|
+
* webhook: `name=<name>,value=<base64 of value>` property strings, as PVE stores them.
|
|
99
|
+
* ⚠️ MORE THAN ONE HEADER IS THE ONE THING HERE NOT PROVEN ON A WIRE — see `REPEATED_KEY`.
|
|
100
|
+
*/
|
|
101
|
+
header?: readonly string[];
|
|
102
|
+
/**
|
|
103
|
+
* webhook: the request body, base64, as PVE stores it.
|
|
104
|
+
* ⚠️ THE UI SHOWS THE DECODED BODY AND THIS DOES NOT. Declaring the base64 is what keeps
|
|
105
|
+
* `matches` symmetric with the read; decoding for display would mean encoding on write, and a
|
|
106
|
+
* round trip PVE normalises differently is a forever-update. The cost is an unreadable diff.
|
|
107
|
+
*/
|
|
108
|
+
body?: string;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* ⚠️ EVERY FIELD IS PRESENT FOR EVERY FAMILY, with `''` / `0` meaning "PVE holds nothing". A
|
|
112
|
+
* webhook reads back an empty `mailto`, which is also what a webhook declares, so the
|
|
113
|
+
* cross-family fields cost one comparison each and never a false update.
|
|
114
|
+
*/
|
|
115
|
+
export interface NotificationTargetAttributes {
|
|
116
|
+
name: string;
|
|
117
|
+
type: NotificationTargetType;
|
|
118
|
+
comment: string;
|
|
119
|
+
disable: boolean;
|
|
120
|
+
/** Sorted and comma-joined, so re-ordering a declaration is not a diff. See `addressList`. */
|
|
121
|
+
mailto: string;
|
|
122
|
+
'mailto-user': string;
|
|
123
|
+
server: string;
|
|
124
|
+
/** 0 means PVE holds no port and will pick one from `mode`. */
|
|
125
|
+
port: number;
|
|
126
|
+
mode: string;
|
|
127
|
+
username: string;
|
|
128
|
+
'from-address': string;
|
|
129
|
+
author: string;
|
|
130
|
+
url: string;
|
|
131
|
+
method: string;
|
|
132
|
+
/** Sorted and comma-joined for COMPARISON only; the wire form differs. See `headerList`. */
|
|
133
|
+
header: string;
|
|
134
|
+
body: string;
|
|
135
|
+
}
|
|
136
|
+
export interface ProxmoxNotificationTarget extends Resource<'Proxmox.NotificationTarget', NotificationTargetProps, NotificationTargetAttributes, never, PveRequirements> {
|
|
137
|
+
}
|
|
138
|
+
export declare const ProxmoxNotificationTarget: import("alchemy").ResourceClass<ProxmoxNotificationTarget>;
|
|
139
|
+
/**
|
|
140
|
+
* ⚠️ ON A BUILT-IN TARGET A DELETE REVERTS RATHER THAN REMOVES, and PVE reports success either way.
|
|
141
|
+
* It is passed through unguarded because everywhere else in this package the cluster's answer is
|
|
142
|
+
* the answer — but this is the one case where a green plan and the cluster disagree, and the
|
|
143
|
+
* object left standing is the one carrying the estate's failure mail.
|
|
144
|
+
*/
|
|
145
|
+
export declare const ProxmoxNotificationTargetProvider: () => import("effect/Layer").Layer<Provider.Provider<ProxmoxNotificationTarget>, never, import("effect/unstable/http/HttpClient").HttpClient>;
|
|
146
|
+
//# sourceMappingURL=notification-target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-target.d.ts","sourceRoot":"","sources":["../../src/proxmox/notification-target.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAe,MAAM,eAAe,CAAC;AAGnF,+FAA+F;AAC/F,MAAM,MAAM,sBAAsB,GAAG,QAAQ,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;AAEhF;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAwB,SAAQ,UAAU;IACzD,gFAAgF;IAChF,IAAI,EAAE,sBAAsB,CAAC;IAC7B;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,iFAAiF;IACjF,aAAa,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sFAAsF;IACtF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,KAAK,CAAC;IACvC,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,qFAAqF;IACrF,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAC;IAChC;;;OAGG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,sBAAsB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,8FAA8F;IAC9F,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,yBAA0B,SAAQ,QAAQ,CACzD,4BAA4B,EAC5B,uBAAuB,EACvB,4BAA4B,EAC5B,KAAK,EACL,eAAe,CAChB;CAAG;AAEJ,eAAO,MAAM,yBAAyB,4DAErC,CAAC;AA2DF;;;;;GAKG;AACH,eAAO,MAAM,iCAAiC,+IAI3C,CAAC"}
|