@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,1158 @@
|
|
|
1
|
+
// src/openbao/auth-role.ts
|
|
2
|
+
import { Resource } from "alchemy";
|
|
3
|
+
import { isResolved } from "alchemy/Diff";
|
|
4
|
+
import * as Provider from "alchemy/Provider";
|
|
5
|
+
import * as Effect2 from "effect/Effect";
|
|
6
|
+
|
|
7
|
+
// src/openbao/digest.ts
|
|
8
|
+
var canonical = (text) => text.trimEnd();
|
|
9
|
+
var sha256 = (text) => new Bun.CryptoHasher("sha256").update(canonical(text)).digest("hex");
|
|
10
|
+
|
|
11
|
+
// src/openbao/mount-form.ts
|
|
12
|
+
var mountPath = (path) => path.replace(/\/+$/, "");
|
|
13
|
+
var DURATION = /^(\d+)([smhd])$/;
|
|
14
|
+
var parseDuration = (text) => {
|
|
15
|
+
const trimmed = text.trim();
|
|
16
|
+
if (trimmed === "0")
|
|
17
|
+
return 0;
|
|
18
|
+
const match = DURATION.exec(trimmed);
|
|
19
|
+
if (match === null)
|
|
20
|
+
return;
|
|
21
|
+
const amount = Number(match[1]);
|
|
22
|
+
switch (match[2]) {
|
|
23
|
+
case "s":
|
|
24
|
+
return amount;
|
|
25
|
+
case "m":
|
|
26
|
+
return amount * 60;
|
|
27
|
+
case "h":
|
|
28
|
+
return amount * 3600;
|
|
29
|
+
case "d":
|
|
30
|
+
return amount * 86400;
|
|
31
|
+
default:
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var ttlSeconds = (value) => {
|
|
36
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
37
|
+
return value;
|
|
38
|
+
if (typeof value === "string")
|
|
39
|
+
return parseDuration(value);
|
|
40
|
+
return;
|
|
41
|
+
};
|
|
42
|
+
var configOf = (live) => typeof live["config"] === "object" && live["config"] !== null ? live["config"] : {};
|
|
43
|
+
var text = (value) => typeof value === "string" ? value : "";
|
|
44
|
+
var ttlProp = (seconds, fallback = "") => {
|
|
45
|
+
if (seconds === undefined)
|
|
46
|
+
return fallback;
|
|
47
|
+
if (seconds % 86400 === 0)
|
|
48
|
+
return `${String(seconds / 86400)}d`;
|
|
49
|
+
if (seconds % 3600 === 0)
|
|
50
|
+
return `${String(seconds / 3600)}h`;
|
|
51
|
+
if (seconds % 60 === 0)
|
|
52
|
+
return `${String(seconds / 60)}m`;
|
|
53
|
+
return `${String(seconds)}s`;
|
|
54
|
+
};
|
|
55
|
+
var attributesOf = (props, live) => {
|
|
56
|
+
const config = configOf(live);
|
|
57
|
+
const defaultLeaseTtl = ttlProp(ttlSeconds(config["default_lease_ttl"]));
|
|
58
|
+
const maxLeaseTtl = ttlProp(ttlSeconds(config["max_lease_ttl"]));
|
|
59
|
+
const attrs = {
|
|
60
|
+
path: mountPath(props.path),
|
|
61
|
+
type: text(live["type"]) || props.type,
|
|
62
|
+
description: text(live["description"]),
|
|
63
|
+
defaultLeaseTtl,
|
|
64
|
+
maxLeaseTtl
|
|
65
|
+
};
|
|
66
|
+
return { ...attrs, digest: sha256(JSON.stringify(attrs)) };
|
|
67
|
+
};
|
|
68
|
+
var readPath = (path) => `sys/mounts/${mountPath(path)}`;
|
|
69
|
+
var tunePath = (path) => `${readPath(path)}/tune`;
|
|
70
|
+
var enableBody = (props) => props.version === 2 ? { options: { version: "2" }, type: props.type } : { type: props.type };
|
|
71
|
+
var tuneBody = (props) => {
|
|
72
|
+
const body = {};
|
|
73
|
+
if (props.description !== undefined)
|
|
74
|
+
body["description"] = props.description;
|
|
75
|
+
if (props.defaultLeaseTtl !== undefined)
|
|
76
|
+
body["default_lease_ttl"] = props.defaultLeaseTtl;
|
|
77
|
+
if (props.maxLeaseTtl !== undefined)
|
|
78
|
+
body["max_lease_ttl"] = props.maxLeaseTtl;
|
|
79
|
+
return body;
|
|
80
|
+
};
|
|
81
|
+
var sameTtl = (want, have) => {
|
|
82
|
+
if (want === undefined)
|
|
83
|
+
return true;
|
|
84
|
+
const wantSeconds = ttlSeconds(want);
|
|
85
|
+
const haveSeconds = ttlSeconds(have);
|
|
86
|
+
return wantSeconds !== undefined && wantSeconds === haveSeconds;
|
|
87
|
+
};
|
|
88
|
+
var matches = (attributes, props) => attributes.type === props.type && (props.description === undefined || attributes.description === props.description) && sameTtl(props.defaultLeaseTtl, attributes.defaultLeaseTtl) && sameTtl(props.maxLeaseTtl, attributes.maxLeaseTtl);
|
|
89
|
+
var wantsTune = (props) => props.description !== undefined || props.defaultLeaseTtl !== undefined || props.maxLeaseTtl !== undefined;
|
|
90
|
+
|
|
91
|
+
// src/openbao/auth-role-form.ts
|
|
92
|
+
var policiesOf = (live) => {
|
|
93
|
+
const raw = live["token_policies"];
|
|
94
|
+
if (!Array.isArray(raw))
|
|
95
|
+
return [];
|
|
96
|
+
return raw.filter((entry) => typeof entry === "string").slice().sort();
|
|
97
|
+
};
|
|
98
|
+
var bool = (value, fallback) => typeof value === "boolean" ? value : fallback;
|
|
99
|
+
var int = (value, fallback) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
100
|
+
var ttlText = (seconds, fallback = "") => {
|
|
101
|
+
if (seconds === undefined)
|
|
102
|
+
return fallback;
|
|
103
|
+
if (seconds % 86400 === 0)
|
|
104
|
+
return `${String(seconds / 86400)}d`;
|
|
105
|
+
if (seconds % 3600 === 0)
|
|
106
|
+
return `${String(seconds / 3600)}h`;
|
|
107
|
+
if (seconds % 60 === 0)
|
|
108
|
+
return `${String(seconds / 60)}m`;
|
|
109
|
+
return `${String(seconds)}s`;
|
|
110
|
+
};
|
|
111
|
+
var attributesOf2 = (props, live) => {
|
|
112
|
+
const attrs = {
|
|
113
|
+
name: props.name,
|
|
114
|
+
tokenPolicies: policiesOf(live),
|
|
115
|
+
tokenTtl: ttlText(ttlSeconds(live["token_ttl"])),
|
|
116
|
+
tokenMaxTtl: ttlText(ttlSeconds(live["token_max_ttl"])),
|
|
117
|
+
secretIdTtl: ttlText(ttlSeconds(live["secret_id_ttl"]), "0"),
|
|
118
|
+
bindSecretId: bool(live["bind_secret_id"], true),
|
|
119
|
+
secretIdNumUses: int(live["secret_id_num_uses"], 0)
|
|
120
|
+
};
|
|
121
|
+
return { ...attrs, digest: sha256(JSON.stringify(attrs)) };
|
|
122
|
+
};
|
|
123
|
+
var readPath2 = (name) => `auth/approle/role/${name}`;
|
|
124
|
+
var writeBody = (props) => {
|
|
125
|
+
const body = {
|
|
126
|
+
token_policies: props.tokenPolicies.join(","),
|
|
127
|
+
token_ttl: props.tokenTtl,
|
|
128
|
+
token_max_ttl: props.tokenMaxTtl,
|
|
129
|
+
secret_id_ttl: props.secretIdTtl
|
|
130
|
+
};
|
|
131
|
+
if (props.bindSecretId !== undefined)
|
|
132
|
+
body["bind_secret_id"] = String(props.bindSecretId);
|
|
133
|
+
if (props.secretIdNumUses !== undefined) {
|
|
134
|
+
body["secret_id_num_uses"] = String(props.secretIdNumUses);
|
|
135
|
+
}
|
|
136
|
+
return body;
|
|
137
|
+
};
|
|
138
|
+
var samePolicies = (want, have) => want.length === have.length && want.every((policy, index) => policy === have[index]);
|
|
139
|
+
var sameTtl2 = (want, have) => {
|
|
140
|
+
const wantSeconds = parseDuration(want);
|
|
141
|
+
const haveSeconds = ttlSeconds(have);
|
|
142
|
+
return wantSeconds !== undefined && wantSeconds === haveSeconds;
|
|
143
|
+
};
|
|
144
|
+
var matches2 = (attributes, props) => {
|
|
145
|
+
const wantPolicies = props.tokenPolicies.slice().sort();
|
|
146
|
+
return samePolicies(wantPolicies, attributes.tokenPolicies) && sameTtl2(props.tokenTtl, attributes.tokenTtl) && sameTtl2(props.tokenMaxTtl, attributes.tokenMaxTtl) && sameTtl2(props.secretIdTtl, attributes.secretIdTtl) && (props.bindSecretId === undefined || attributes.bindSecretId === props.bindSecretId) && (props.secretIdNumUses === undefined || attributes.secretIdNumUses === props.secretIdNumUses);
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// src/openbao/bao-http.ts
|
|
150
|
+
import * as Context from "effect/Context";
|
|
151
|
+
import * as Effect from "effect/Effect";
|
|
152
|
+
import * as Option from "effect/Option";
|
|
153
|
+
import * as Semaphore from "effect/Semaphore";
|
|
154
|
+
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
155
|
+
import * as Headers from "effect/unstable/http/Headers";
|
|
156
|
+
import * as HttpClient from "effect/unstable/http/HttpClient";
|
|
157
|
+
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
|
|
158
|
+
|
|
159
|
+
// src/openbao/bao-address.ts
|
|
160
|
+
var DEFAULT_ADDRESS = "https://127.0.0.1:8200";
|
|
161
|
+
var UNIX = "unix://";
|
|
162
|
+
var readBaoVariable = (env, name) => env[name] ?? env[`VAULT_${name.slice("BAO_".length)}`] ?? "";
|
|
163
|
+
var resolveAddress = (env) => {
|
|
164
|
+
const agent = readBaoVariable(env, "BAO_AGENT_ADDR");
|
|
165
|
+
const direct = readBaoVariable(env, "BAO_ADDR");
|
|
166
|
+
const raw = agent !== "" ? agent : direct !== "" ? direct : DEFAULT_ADDRESS;
|
|
167
|
+
const namespace = readBaoVariable(env, "BAO_NAMESPACE");
|
|
168
|
+
const socket = raw.startsWith(UNIX) ? raw.slice(UNIX.length) : undefined;
|
|
169
|
+
return {
|
|
170
|
+
base: socket === undefined ? raw.replace(/\/+$/, "") : "http://localhost",
|
|
171
|
+
namespace: namespace === "" ? undefined : namespace,
|
|
172
|
+
socket
|
|
173
|
+
};
|
|
174
|
+
};
|
|
175
|
+
var headersFor = (address, env) => {
|
|
176
|
+
const headers = { "X-Vault-Request": "true" };
|
|
177
|
+
if (address.namespace !== undefined)
|
|
178
|
+
headers["X-Vault-Namespace"] = address.namespace;
|
|
179
|
+
const token = readBaoVariable(env, "BAO_TOKEN");
|
|
180
|
+
if (token !== "")
|
|
181
|
+
headers["X-Vault-Token"] = token;
|
|
182
|
+
return headers;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
// src/openbao/bao-status.ts
|
|
186
|
+
class BaoError extends Error {
|
|
187
|
+
status;
|
|
188
|
+
method;
|
|
189
|
+
path;
|
|
190
|
+
errors;
|
|
191
|
+
constructor(status, method, path, errors) {
|
|
192
|
+
const code = status === 0 ? "no response" : String(status);
|
|
193
|
+
super(`OpenBao ${method} /v1/${path} -> ${code}: ${errors.join("; ") || "(no errors given)"}`);
|
|
194
|
+
this.status = status;
|
|
195
|
+
this.method = method;
|
|
196
|
+
this.path = path;
|
|
197
|
+
this.errors = errors;
|
|
198
|
+
this.name = "BaoError";
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
var objectOf = (text2) => {
|
|
202
|
+
try {
|
|
203
|
+
const parsed = JSON.parse(text2);
|
|
204
|
+
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : undefined;
|
|
205
|
+
} catch {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
var errorsOf = (text2) => {
|
|
210
|
+
const raw = objectOf(text2)?.["errors"];
|
|
211
|
+
return Array.isArray(raw) && raw.every((entry) => typeof entry === "string") ? raw : undefined;
|
|
212
|
+
};
|
|
213
|
+
var settle = (intent, method, path, status, text2) => {
|
|
214
|
+
const fail = (errors) => ({
|
|
215
|
+
error: new BaoError(status, method, path, errors)
|
|
216
|
+
});
|
|
217
|
+
if (status < 200 || status >= 300) {
|
|
218
|
+
const errors = errorsOf(text2);
|
|
219
|
+
if (errors === undefined) {
|
|
220
|
+
return fail([`not an OpenBao error body (${String(text2.length)} bytes)`]);
|
|
221
|
+
}
|
|
222
|
+
if (status === 404 && intent !== "write")
|
|
223
|
+
return { body: undefined };
|
|
224
|
+
return fail(errors);
|
|
225
|
+
}
|
|
226
|
+
if (text2.trim() === "") {
|
|
227
|
+
return intent === "read" ? fail(["a successful read with an empty body"]) : { body: undefined };
|
|
228
|
+
}
|
|
229
|
+
const body = objectOf(text2);
|
|
230
|
+
return body === undefined ? fail(["a success body that is not a JSON object"]) : { body };
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// src/openbao/bao-http.ts
|
|
234
|
+
var BaoEnv = Context.Reference("homeflare/openbao/BaoEnv", {
|
|
235
|
+
defaultValue: () => process.env
|
|
236
|
+
});
|
|
237
|
+
var MAX_IN_FLIGHT = 8;
|
|
238
|
+
var BaoGate = Context.Reference("homeflare/openbao/BaoGate", {
|
|
239
|
+
defaultValue: () => Semaphore.makeUnsafe(MAX_IN_FLIGHT)
|
|
240
|
+
});
|
|
241
|
+
var TIMEOUT = "60 seconds";
|
|
242
|
+
var redactingToken = (effect) => Effect.gen(function* () {
|
|
243
|
+
const names = yield* Headers.CurrentRedactedNames;
|
|
244
|
+
return yield* Effect.provideService(effect, Headers.CurrentRedactedNames, [
|
|
245
|
+
...names,
|
|
246
|
+
"x-vault-token"
|
|
247
|
+
]);
|
|
248
|
+
});
|
|
249
|
+
var overSocket = (socket, method, path) => (effect) => {
|
|
250
|
+
if (socket === undefined)
|
|
251
|
+
return effect;
|
|
252
|
+
if (typeof Bun === "undefined") {
|
|
253
|
+
return Effect.fail(new BaoError(0, method, path, ["a unix:// address needs Bun fetch; this is not Bun"]));
|
|
254
|
+
}
|
|
255
|
+
return Effect.flatMap(Effect.serviceOption(FetchHttpClient.RequestInit), (defaults) => {
|
|
256
|
+
const init = { ...Option.getOrElse(defaults, () => ({})), unix: socket };
|
|
257
|
+
return Effect.provideService(effect, FetchHttpClient.RequestInit, init);
|
|
258
|
+
});
|
|
259
|
+
};
|
|
260
|
+
var baoCall = (intent, method, path, body) => Effect.gen(function* () {
|
|
261
|
+
const env = yield* BaoEnv;
|
|
262
|
+
const address = resolveAddress(env);
|
|
263
|
+
const client = yield* HttpClient.HttpClient;
|
|
264
|
+
const request = HttpClientRequest.make(method)(`${address.base}/v1/${path}`).pipe(HttpClientRequest.setHeaders(headersFor(address, env)), body === undefined ? (self) => self : HttpClientRequest.bodyText(JSON.stringify(body), "application/json"));
|
|
265
|
+
const exchange = client.execute(request).pipe(Effect.flatMap((response) => Effect.map(response.text, (text3) => ({ status: response.status, text: text3 }))), Effect.timeout(TIMEOUT), Effect.mapError((cause) => new BaoError(0, method, path, [cause.message])));
|
|
266
|
+
const gate = yield* BaoGate;
|
|
267
|
+
const { status, text: text2 } = yield* exchange.pipe(overSocket(address.socket, method, path), redactingToken, Semaphore.withPermits(gate, 1));
|
|
268
|
+
const outcome = settle(intent, method, path, status, text2);
|
|
269
|
+
return "error" in outcome ? yield* Effect.fail(outcome.error) : outcome.body;
|
|
270
|
+
});
|
|
271
|
+
var baoRead = (path) => Effect.map(baoCall("read", "GET", path), (body) => {
|
|
272
|
+
if (body === undefined)
|
|
273
|
+
return;
|
|
274
|
+
const data = body["data"];
|
|
275
|
+
return typeof data === "object" && data !== null ? data : body;
|
|
276
|
+
});
|
|
277
|
+
var baoWrite = (method, path, body) => Effect.asVoid(baoCall("write", method, path, body));
|
|
278
|
+
var baoDelete = (path) => Effect.asVoid(baoCall("delete", "DELETE", path));
|
|
279
|
+
|
|
280
|
+
// src/openbao/auth-role.ts
|
|
281
|
+
var BaoAuthRole = Resource("Bao.AuthRole", {
|
|
282
|
+
defaultRemovalPolicy: "retain"
|
|
283
|
+
});
|
|
284
|
+
var readRole = (props) => Effect2.gen(function* () {
|
|
285
|
+
const live = yield* baoRead(readPath2(props.name));
|
|
286
|
+
if (live === undefined)
|
|
287
|
+
return;
|
|
288
|
+
return attributesOf2(props, live);
|
|
289
|
+
});
|
|
290
|
+
var BaoAuthRoleProvider = () => Provider.effect(BaoAuthRole, Effect2.succeed(BaoAuthRole.Provider.of({
|
|
291
|
+
list: () => Effect2.succeed([]),
|
|
292
|
+
read: Effect2.fn(function* ({ olds }) {
|
|
293
|
+
return yield* readRole(olds);
|
|
294
|
+
}),
|
|
295
|
+
diff: Effect2.fn(function* ({ news, output }) {
|
|
296
|
+
if (output === undefined || !isResolved(news))
|
|
297
|
+
return;
|
|
298
|
+
const live = yield* readRole(news);
|
|
299
|
+
if (live === undefined)
|
|
300
|
+
return { action: "update" };
|
|
301
|
+
if (matches2(live, news))
|
|
302
|
+
return { action: "noop" };
|
|
303
|
+
return { action: "update" };
|
|
304
|
+
}),
|
|
305
|
+
reconcile: Effect2.fn(function* ({ news }) {
|
|
306
|
+
const live = yield* readRole(news);
|
|
307
|
+
if (live === undefined || !matches2(live, news)) {
|
|
308
|
+
yield* baoWrite("PUT", readPath2(news.name), writeBody(news));
|
|
309
|
+
}
|
|
310
|
+
const after = yield* readRole(news);
|
|
311
|
+
if (after === undefined) {
|
|
312
|
+
return yield* Effect2.die(new Error(`Bao.AuthRole ${news.name}: write returned no error but the role is still absent.`));
|
|
313
|
+
}
|
|
314
|
+
return after;
|
|
315
|
+
}),
|
|
316
|
+
delete: Effect2.fn(function* ({ output }) {
|
|
317
|
+
yield* baoDelete(readPath2(output.name));
|
|
318
|
+
return;
|
|
319
|
+
})
|
|
320
|
+
})));
|
|
321
|
+
// src/openbao/cloudflare-role.ts
|
|
322
|
+
import { Resource as Resource2 } from "alchemy";
|
|
323
|
+
import { isResolved as isResolved2 } from "alchemy/Diff";
|
|
324
|
+
import * as Provider2 from "alchemy/Provider";
|
|
325
|
+
import * as Effect5 from "effect/Effect";
|
|
326
|
+
|
|
327
|
+
// src/openbao/cloudflare-group-scope.ts
|
|
328
|
+
var GROUP_SCOPES = {
|
|
329
|
+
account: "com.cloudflare.api.account",
|
|
330
|
+
bucket: "com.cloudflare.edge.r2.bucket",
|
|
331
|
+
zone: "com.cloudflare.api.account.zone"
|
|
332
|
+
};
|
|
333
|
+
var scopeOfResource = (resource) => {
|
|
334
|
+
const scopes = [GROUP_SCOPES.zone, GROUP_SCOPES.bucket, GROUP_SCOPES.account];
|
|
335
|
+
return scopes.find((scope) => resource.startsWith(`${scope}.`)) ?? resource;
|
|
336
|
+
};
|
|
337
|
+
var groupKey = (name, scope) => `${scope} ${name}`;
|
|
338
|
+
|
|
339
|
+
// src/openbao/cloudflare-permission-groups.ts
|
|
340
|
+
import * as Context2 from "effect/Context";
|
|
341
|
+
import * as Effect4 from "effect/Effect";
|
|
342
|
+
|
|
343
|
+
// src/openbao/cloudflare-policy.ts
|
|
344
|
+
var resolvePolicies = (declared, ids) => {
|
|
345
|
+
const missing = new Set;
|
|
346
|
+
const policies = declared.map((policy) => {
|
|
347
|
+
const scope = policy.scope ?? scopeOfResource(policy.resource);
|
|
348
|
+
const groupIds = policy.groups.flatMap((name) => {
|
|
349
|
+
const id = ids.get(groupKey(name, scope));
|
|
350
|
+
if (id === undefined)
|
|
351
|
+
missing.add(name);
|
|
352
|
+
return id === undefined ? [] : [id];
|
|
353
|
+
});
|
|
354
|
+
return {
|
|
355
|
+
effect: policy.effect,
|
|
356
|
+
groupIds: policy.groupOrder === "by-id" ? [...groupIds].sort() : groupIds,
|
|
357
|
+
groupOrder: policy.groupOrder,
|
|
358
|
+
resources: { [policy.resource]: "*" }
|
|
359
|
+
};
|
|
360
|
+
});
|
|
361
|
+
return { missing: [...missing].sort(), policies };
|
|
362
|
+
};
|
|
363
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
364
|
+
var stringMap = (value) => {
|
|
365
|
+
if (!isRecord(value))
|
|
366
|
+
return;
|
|
367
|
+
const out = {};
|
|
368
|
+
for (const [key, item] of Object.entries(value)) {
|
|
369
|
+
if (typeof item !== "string")
|
|
370
|
+
return;
|
|
371
|
+
out[key] = item;
|
|
372
|
+
}
|
|
373
|
+
return out;
|
|
374
|
+
};
|
|
375
|
+
var entryOf = (entry) => {
|
|
376
|
+
if (!isRecord(entry) || typeof entry["effect"] !== "string")
|
|
377
|
+
return;
|
|
378
|
+
const resources = stringMap(entry["resources"]);
|
|
379
|
+
const groups = entry["permission_groups"];
|
|
380
|
+
if (resources === undefined || !Array.isArray(groups))
|
|
381
|
+
return;
|
|
382
|
+
const groupIds = groups.flatMap((group) => isRecord(group) && typeof group["id"] === "string" ? [group["id"]] : []);
|
|
383
|
+
return groupIds.length === groups.length ? { effect: entry["effect"], groupIds, resources } : undefined;
|
|
384
|
+
};
|
|
385
|
+
var parseLivePolicies = (raw) => {
|
|
386
|
+
if (typeof raw !== "string")
|
|
387
|
+
return;
|
|
388
|
+
let parsed;
|
|
389
|
+
try {
|
|
390
|
+
parsed = JSON.parse(raw);
|
|
391
|
+
} catch {
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
394
|
+
if (!Array.isArray(parsed))
|
|
395
|
+
return;
|
|
396
|
+
const policies = parsed.map(entryOf);
|
|
397
|
+
return policies.every((policy) => policy !== undefined) ? policies : undefined;
|
|
398
|
+
};
|
|
399
|
+
var policiesJson = (policies) => JSON.stringify(policies.map((policy) => ({
|
|
400
|
+
effect: policy.effect,
|
|
401
|
+
resources: policy.resources,
|
|
402
|
+
permission_groups: policy.groupIds.map((id) => ({ id }))
|
|
403
|
+
})));
|
|
404
|
+
var sameList = (a, b) => a.length === b.length && a.every((item, i) => item === b[i]);
|
|
405
|
+
var sameResources = (a, b) => {
|
|
406
|
+
const keys = Object.keys(a);
|
|
407
|
+
return keys.length === Object.keys(b).length && keys.every((key) => a[key] === b[key]);
|
|
408
|
+
};
|
|
409
|
+
var samePolicies2 = (want, have) => want.length === have.length && want.every((w, i) => {
|
|
410
|
+
const h = have[i];
|
|
411
|
+
if (h === undefined || w.effect !== h.effect || !sameResources(w.resources, h.resources)) {
|
|
412
|
+
return false;
|
|
413
|
+
}
|
|
414
|
+
return w.groupOrder === "by-id" ? sameList([...w.groupIds].sort(), [...h.groupIds].sort()) : sameList(w.groupIds, h.groupIds);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// src/openbao/cloudflare-role-wire.ts
|
|
418
|
+
import * as Effect3 from "effect/Effect";
|
|
419
|
+
|
|
420
|
+
// src/openbao/cloudflare-role-form.ts
|
|
421
|
+
var rolePath = (mount, name) => `${mountPath(mount)}/roles/${name}`;
|
|
422
|
+
var seconds = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
423
|
+
var liveRoleOf = (data) => ({
|
|
424
|
+
description: typeof data["description"] === "string" ? data["description"] : "",
|
|
425
|
+
maxTtl: seconds(data["max_ttl"]),
|
|
426
|
+
name: typeof data["name"] === "string" ? data["name"] : "",
|
|
427
|
+
policies: parseLivePolicies(data["policies"]),
|
|
428
|
+
policiesText: typeof data["policies"] === "string" ? data["policies"] : "",
|
|
429
|
+
ttl: seconds(data["ttl"])
|
|
430
|
+
});
|
|
431
|
+
var attributesOf3 = (props, live) => {
|
|
432
|
+
const attrs = {
|
|
433
|
+
description: live.description,
|
|
434
|
+
maxTtl: live.maxTtl ?? 0,
|
|
435
|
+
mount: mountPath(props.mount),
|
|
436
|
+
name: live.name || props.name,
|
|
437
|
+
policies: live.policiesText,
|
|
438
|
+
ttl: live.ttl ?? 0
|
|
439
|
+
};
|
|
440
|
+
return { ...attrs, digest: sha256(JSON.stringify(attrs)) };
|
|
441
|
+
};
|
|
442
|
+
var refusalOf = (props) => {
|
|
443
|
+
const bad = [
|
|
444
|
+
...parseDuration(props.ttl) === undefined ? [`ttl=${props.ttl}`] : [],
|
|
445
|
+
...parseDuration(props.maxTtl) === undefined ? [`maxTtl=${props.maxTtl}`] : []
|
|
446
|
+
];
|
|
447
|
+
if (bad.length > 0)
|
|
448
|
+
return `unparseable duration ${bad.join(", ")} (use 30s / 5m / 1h / 1d)`;
|
|
449
|
+
if (props.policies.length === 0)
|
|
450
|
+
return "no policy entries";
|
|
451
|
+
if (props.policies.some((policy) => policy.groups.length === 0))
|
|
452
|
+
return "a policy with no groups";
|
|
453
|
+
return;
|
|
454
|
+
};
|
|
455
|
+
var writeBody2 = (props, policies) => ({
|
|
456
|
+
description: props.description,
|
|
457
|
+
max_ttl: props.maxTtl,
|
|
458
|
+
policies: policiesJson(policies),
|
|
459
|
+
ttl: props.ttl
|
|
460
|
+
});
|
|
461
|
+
var sameTtl3 = (want, have) => {
|
|
462
|
+
const wantSeconds = parseDuration(want);
|
|
463
|
+
return wantSeconds !== undefined && wantSeconds === have;
|
|
464
|
+
};
|
|
465
|
+
var differences = (props, policies, live) => {
|
|
466
|
+
const out = [];
|
|
467
|
+
if (props.description !== live.description) {
|
|
468
|
+
out.push({ field: "description", have: live.description, want: props.description });
|
|
469
|
+
}
|
|
470
|
+
if (!sameTtl3(props.ttl, live.ttl)) {
|
|
471
|
+
out.push({ field: "ttl", have: String(live.ttl), want: props.ttl });
|
|
472
|
+
}
|
|
473
|
+
if (!sameTtl3(props.maxTtl, live.maxTtl)) {
|
|
474
|
+
out.push({ field: "max_ttl", have: String(live.maxTtl), want: props.maxTtl });
|
|
475
|
+
}
|
|
476
|
+
if (live.policies === undefined || !samePolicies2(policies, live.policies)) {
|
|
477
|
+
out.push({ field: "policies", have: live.policiesText, want: policiesJson(policies) });
|
|
478
|
+
}
|
|
479
|
+
return out;
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
// src/openbao/cloudflare-role-wire.ts
|
|
483
|
+
var readCloudflareRole = (mount, name) => Effect3.map(baoRead(rolePath(mount, name)), (data) => data === undefined ? undefined : liveRoleOf(data));
|
|
484
|
+
var writeCloudflareRole = (mount, name, body) => baoWrite("PUT", rolePath(mount, name), body);
|
|
485
|
+
var deleteCloudflareRole = (mount, name) => baoDelete(rolePath(mount, name));
|
|
486
|
+
|
|
487
|
+
// src/openbao/cloudflare-permission-groups.ts
|
|
488
|
+
class CloudflarePermissionGroups extends Context2.Service()("homeflare/openbao/CloudflarePermissionGroups") {
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/openbao/cloudflare-role.ts
|
|
492
|
+
var BaoCloudflareRole = Resource2("Bao.CloudflareRole", {
|
|
493
|
+
defaultRemovalPolicy: "retain"
|
|
494
|
+
});
|
|
495
|
+
var BaoCloudflareRoleProvider = () => Provider2.effect(BaoCloudflareRole, Effect5.gen(function* () {
|
|
496
|
+
const groups = yield* CloudflarePermissionGroups;
|
|
497
|
+
const resolved = (props) => Effect5.gen(function* () {
|
|
498
|
+
const refs = new Map;
|
|
499
|
+
for (const policy of props.policies) {
|
|
500
|
+
const scope = policy.scope ?? scopeOfResource(policy.resource);
|
|
501
|
+
for (const name of policy.groups)
|
|
502
|
+
refs.set(groupKey(name, scope), { name, scope });
|
|
503
|
+
}
|
|
504
|
+
const ids = yield* groups.resolve(props.mount, [...refs.values()]);
|
|
505
|
+
const { missing, policies } = resolvePolicies(props.policies, ids);
|
|
506
|
+
if (missing.length > 0) {
|
|
507
|
+
return yield* Effect5.die(new Error(`Bao.CloudflareRole ${rolePath(props.mount, props.name)}: groups ` + `${missing.join(", ")} came back unresolved from a lookup that reported success.`));
|
|
508
|
+
}
|
|
509
|
+
return policies;
|
|
510
|
+
});
|
|
511
|
+
return BaoCloudflareRole.Provider.of({
|
|
512
|
+
list: () => Effect5.succeed([]),
|
|
513
|
+
read: Effect5.fn(function* ({ olds }) {
|
|
514
|
+
const live = yield* readCloudflareRole(olds.mount, olds.name);
|
|
515
|
+
return live === undefined ? undefined : attributesOf3(olds, live);
|
|
516
|
+
}),
|
|
517
|
+
diff: Effect5.fn(function* ({ news, output }) {
|
|
518
|
+
if (output === undefined || !isResolved2(news))
|
|
519
|
+
return;
|
|
520
|
+
if (refusalOf(news) !== undefined)
|
|
521
|
+
return { action: "update" };
|
|
522
|
+
const policies = yield* resolved(news);
|
|
523
|
+
const live = yield* readCloudflareRole(news.mount, news.name);
|
|
524
|
+
if (live === undefined)
|
|
525
|
+
return { action: "update" };
|
|
526
|
+
return differences(news, policies, live).length === 0 ? { action: "noop" } : { action: "update" };
|
|
527
|
+
}),
|
|
528
|
+
reconcile: Effect5.fn(function* ({ news }) {
|
|
529
|
+
const path = rolePath(news.mount, news.name);
|
|
530
|
+
const refusal = refusalOf(news);
|
|
531
|
+
if (refusal !== undefined) {
|
|
532
|
+
return yield* Effect5.die(new Error(`Bao.CloudflareRole ${path}: ${refusal}.`));
|
|
533
|
+
}
|
|
534
|
+
const policies = yield* resolved(news);
|
|
535
|
+
const live = yield* readCloudflareRole(news.mount, news.name);
|
|
536
|
+
if (live === undefined || differences(news, policies, live).length > 0) {
|
|
537
|
+
yield* writeCloudflareRole(news.mount, news.name, writeBody2(news, policies));
|
|
538
|
+
}
|
|
539
|
+
const after = yield* readCloudflareRole(news.mount, news.name);
|
|
540
|
+
if (after === undefined) {
|
|
541
|
+
return yield* Effect5.die(new Error(`Bao.CloudflareRole ${path}: the write succeeded but the role is absent.`));
|
|
542
|
+
}
|
|
543
|
+
const left = differences(news, policies, after);
|
|
544
|
+
if (left.length > 0) {
|
|
545
|
+
return yield* Effect5.die(new Error(`Bao.CloudflareRole ${path}: wrote cleanly but reads back different in ` + `${left.map((d) => d.field).join(", ")}. Inspect the role before retrying.`));
|
|
546
|
+
}
|
|
547
|
+
return attributesOf3(news, after);
|
|
548
|
+
}),
|
|
549
|
+
delete: Effect5.fn(function* ({ output }) {
|
|
550
|
+
yield* deleteCloudflareRole(output.mount, output.name);
|
|
551
|
+
return;
|
|
552
|
+
})
|
|
553
|
+
});
|
|
554
|
+
}));
|
|
555
|
+
// src/openbao/mount.ts
|
|
556
|
+
import { Resource as Resource3 } from "alchemy";
|
|
557
|
+
import { isResolved as isResolved3 } from "alchemy/Diff";
|
|
558
|
+
import * as Provider3 from "alchemy/Provider";
|
|
559
|
+
import * as Effect7 from "effect/Effect";
|
|
560
|
+
|
|
561
|
+
// src/openbao/mount-wire.ts
|
|
562
|
+
import * as Effect6 from "effect/Effect";
|
|
563
|
+
var absentFromTable = (path, asked) => baoRead("sys/mounts").pipe(Effect6.mapError((listing) => new BaoError(asked.status, asked.method, asked.path, [
|
|
564
|
+
...asked.errors,
|
|
565
|
+
`sys/mounts could not confirm the mount is absent: ${listing.message}`
|
|
566
|
+
])), Effect6.flatMap((table) => table !== undefined && !Object.hasOwn(table, `${mountPath(path)}/`) ? Effect6.succeed(undefined) : Effect6.fail(asked)));
|
|
567
|
+
var readMountData = (path) => baoRead(readPath(path)).pipe(Effect6.catchIf((error) => error.status === 400, (error) => absentFromTable(path, error)));
|
|
568
|
+
var enableMount = (props) => baoWrite("POST", readPath(props.path), enableBody(props));
|
|
569
|
+
var tuneMount = (props) => baoWrite("POST", tunePath(props.path), tuneBody(props));
|
|
570
|
+
var disableMount = (path) => baoDelete(readPath(path));
|
|
571
|
+
|
|
572
|
+
// src/openbao/mount.ts
|
|
573
|
+
var BaoMount = Resource3("Bao.Mount", {
|
|
574
|
+
defaultRemovalPolicy: "retain"
|
|
575
|
+
});
|
|
576
|
+
var readMount = (props) => Effect7.gen(function* () {
|
|
577
|
+
const live = yield* readMountData(props.path);
|
|
578
|
+
if (live === undefined)
|
|
579
|
+
return;
|
|
580
|
+
return attributesOf(props, live);
|
|
581
|
+
});
|
|
582
|
+
var BaoMountProvider = () => Provider3.effect(BaoMount, Effect7.succeed(BaoMount.Provider.of({
|
|
583
|
+
list: () => Effect7.succeed([]),
|
|
584
|
+
read: Effect7.fn(function* ({ olds }) {
|
|
585
|
+
return yield* readMount(olds);
|
|
586
|
+
}),
|
|
587
|
+
diff: Effect7.fn(function* ({ news, output }) {
|
|
588
|
+
if (output === undefined || !isResolved3(news))
|
|
589
|
+
return;
|
|
590
|
+
const live = yield* readMount(news);
|
|
591
|
+
if (live === undefined)
|
|
592
|
+
return { action: "update" };
|
|
593
|
+
if (live.type !== news.type)
|
|
594
|
+
return { action: "replace" };
|
|
595
|
+
if (matches(live, news))
|
|
596
|
+
return { action: "noop" };
|
|
597
|
+
return { action: "update" };
|
|
598
|
+
}),
|
|
599
|
+
reconcile: Effect7.fn(function* ({ news }) {
|
|
600
|
+
let live = yield* readMount(news);
|
|
601
|
+
if (live === undefined) {
|
|
602
|
+
yield* enableMount(news);
|
|
603
|
+
if (wantsTune(news))
|
|
604
|
+
yield* tuneMount(news);
|
|
605
|
+
} else if (!matches(live, news)) {
|
|
606
|
+
if (live.type !== news.type) {
|
|
607
|
+
return yield* Effect7.die(new Error(`Bao.Mount ${mountPath(news.path)}: live type ${live.type} != ${news.type}. ` + "Secrets engine type is immutable — replace manually."));
|
|
608
|
+
}
|
|
609
|
+
yield* tuneMount(news);
|
|
610
|
+
}
|
|
611
|
+
live = yield* readMount(news);
|
|
612
|
+
if (live === undefined) {
|
|
613
|
+
return yield* Effect7.die(new Error(`Bao.Mount ${mountPath(news.path)}: write returned no error but the mount is still absent.`));
|
|
614
|
+
}
|
|
615
|
+
return live;
|
|
616
|
+
}),
|
|
617
|
+
delete: Effect7.fn(function* ({ output }) {
|
|
618
|
+
yield* disableMount(output.path);
|
|
619
|
+
return;
|
|
620
|
+
})
|
|
621
|
+
})));
|
|
622
|
+
// src/openbao/pki-role.ts
|
|
623
|
+
import { Resource as Resource4 } from "alchemy";
|
|
624
|
+
import { isResolved as isResolved4 } from "alchemy/Diff";
|
|
625
|
+
import * as Provider4 from "alchemy/Provider";
|
|
626
|
+
import * as Effect8 from "effect/Effect";
|
|
627
|
+
|
|
628
|
+
// src/openbao/pki-role-form.ts
|
|
629
|
+
var sorted = (values) => [...values].sort();
|
|
630
|
+
var foldedUsages = (values) => sorted(values.map((v) => v.toLowerCase()));
|
|
631
|
+
var canonical2 = (input) => ({
|
|
632
|
+
allowBareDomains: input.allowBareDomains,
|
|
633
|
+
allowGlobDomains: input.allowGlobDomains,
|
|
634
|
+
allowIpSans: input.allowIpSans,
|
|
635
|
+
allowLocalhost: input.allowLocalhost,
|
|
636
|
+
allowSubdomains: input.allowSubdomains,
|
|
637
|
+
allowWildcardCertificates: input.allowWildcardCertificates,
|
|
638
|
+
allowedDomains: sorted(input.allowedDomains),
|
|
639
|
+
clientFlag: input.clientFlag,
|
|
640
|
+
cnValidations: sorted(input.cnValidations),
|
|
641
|
+
extKeyUsage: foldedUsages(input.extKeyUsage),
|
|
642
|
+
keyBits: input.keyBits,
|
|
643
|
+
keyType: input.keyType,
|
|
644
|
+
maxTtlSeconds: input.maxTtlSeconds,
|
|
645
|
+
serverFlag: input.serverFlag,
|
|
646
|
+
signatureBits: input.signatureBits,
|
|
647
|
+
ttlSeconds: input.ttlSeconds
|
|
648
|
+
});
|
|
649
|
+
var seconds2 = (text2) => parseDuration(text2) ?? -1;
|
|
650
|
+
var canonicalFromProps = (props) => canonical2({
|
|
651
|
+
allowBareDomains: props.allowBareDomains ?? false,
|
|
652
|
+
allowGlobDomains: props.allowGlobDomains ?? false,
|
|
653
|
+
allowIpSans: props.allowIpSans ?? false,
|
|
654
|
+
allowLocalhost: props.allowLocalhost ?? false,
|
|
655
|
+
allowSubdomains: props.allowSubdomains ?? false,
|
|
656
|
+
allowWildcardCertificates: props.allowWildcardCertificates ?? false,
|
|
657
|
+
allowedDomains: props.allowedDomains,
|
|
658
|
+
clientFlag: props.clientFlag ?? true,
|
|
659
|
+
cnValidations: props.cnValidations ?? ["hostname"],
|
|
660
|
+
extKeyUsage: props.extKeyUsage ?? [],
|
|
661
|
+
keyBits: props.keyBits ?? 0,
|
|
662
|
+
keyType: props.keyType ?? "rsa",
|
|
663
|
+
maxTtlSeconds: seconds2(props.maxTtl),
|
|
664
|
+
serverFlag: props.serverFlag ?? true,
|
|
665
|
+
signatureBits: props.signatureBits ?? 0,
|
|
666
|
+
ttlSeconds: seconds2(props.ttl)
|
|
667
|
+
});
|
|
668
|
+
var bool2 = (value, fallback) => typeof value === "boolean" ? value : fallback;
|
|
669
|
+
var int2 = (value, fallback) => typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
670
|
+
var list = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
|
|
671
|
+
var str = (value, fallback) => typeof value === "string" ? value : fallback;
|
|
672
|
+
var canonicalFromLive = (live) => canonical2({
|
|
673
|
+
allowBareDomains: bool2(live["allow_bare_domains"], false),
|
|
674
|
+
allowGlobDomains: bool2(live["allow_glob_domains"], false),
|
|
675
|
+
allowIpSans: bool2(live["allow_ip_sans"], false),
|
|
676
|
+
allowLocalhost: bool2(live["allow_localhost"], false),
|
|
677
|
+
allowSubdomains: bool2(live["allow_subdomains"], false),
|
|
678
|
+
allowWildcardCertificates: bool2(live["allow_wildcard_certificates"], false),
|
|
679
|
+
allowedDomains: list(live["allowed_domains"]),
|
|
680
|
+
clientFlag: bool2(live["client_flag"], true),
|
|
681
|
+
cnValidations: list(live["cn_validations"]),
|
|
682
|
+
extKeyUsage: list(live["ext_key_usage"]),
|
|
683
|
+
keyBits: int2(live["key_bits"], 0),
|
|
684
|
+
keyType: str(live["key_type"], "rsa"),
|
|
685
|
+
maxTtlSeconds: ttlSeconds(live["max_ttl"]) ?? -1,
|
|
686
|
+
serverFlag: bool2(live["server_flag"], true),
|
|
687
|
+
signatureBits: int2(live["signature_bits"], 0),
|
|
688
|
+
ttlSeconds: ttlSeconds(live["ttl"]) ?? -1
|
|
689
|
+
});
|
|
690
|
+
var ttlText2 = (value) => {
|
|
691
|
+
if (value < 0)
|
|
692
|
+
return "";
|
|
693
|
+
if (value !== 0 && value % 86400 === 0)
|
|
694
|
+
return `${String(value / 86400)}d`;
|
|
695
|
+
if (value !== 0 && value % 3600 === 0)
|
|
696
|
+
return `${String(value / 3600)}h`;
|
|
697
|
+
if (value !== 0 && value % 60 === 0)
|
|
698
|
+
return `${String(value / 60)}m`;
|
|
699
|
+
return `${String(value)}s`;
|
|
700
|
+
};
|
|
701
|
+
var digestOf = (form) => sha256(JSON.stringify(form));
|
|
702
|
+
var rolePath2 = (props) => `${mountPath(props.mount ?? "pki")}/roles/${props.name}`;
|
|
703
|
+
var attributesOf4 = (props, live) => {
|
|
704
|
+
const form = canonicalFromLive(live);
|
|
705
|
+
return {
|
|
706
|
+
...form,
|
|
707
|
+
name: props.name,
|
|
708
|
+
mount: mountPath(props.mount ?? "pki"),
|
|
709
|
+
ttl: ttlText2(form.ttlSeconds),
|
|
710
|
+
maxTtl: ttlText2(form.maxTtlSeconds),
|
|
711
|
+
digest: digestOf(form)
|
|
712
|
+
};
|
|
713
|
+
};
|
|
714
|
+
var writeBody3 = (props) => {
|
|
715
|
+
const form = canonicalFromProps(props);
|
|
716
|
+
const body = {
|
|
717
|
+
allowed_domains: form.allowedDomains.join(","),
|
|
718
|
+
allow_bare_domains: String(form.allowBareDomains),
|
|
719
|
+
allow_glob_domains: String(form.allowGlobDomains),
|
|
720
|
+
allow_subdomains: String(form.allowSubdomains),
|
|
721
|
+
allow_ip_sans: String(form.allowIpSans),
|
|
722
|
+
allow_localhost: String(form.allowLocalhost),
|
|
723
|
+
allow_wildcard_certificates: String(form.allowWildcardCertificates),
|
|
724
|
+
cn_validations: form.cnValidations.join(","),
|
|
725
|
+
client_flag: String(form.clientFlag),
|
|
726
|
+
server_flag: String(form.serverFlag),
|
|
727
|
+
key_type: form.keyType,
|
|
728
|
+
key_bits: String(form.keyBits),
|
|
729
|
+
signature_bits: String(form.signatureBits),
|
|
730
|
+
ttl: props.ttl,
|
|
731
|
+
max_ttl: props.maxTtl
|
|
732
|
+
};
|
|
733
|
+
const usages = props.extKeyUsage ?? [];
|
|
734
|
+
if (usages.length > 0)
|
|
735
|
+
body["ext_key_usage"] = usages.join(",");
|
|
736
|
+
return body;
|
|
737
|
+
};
|
|
738
|
+
var matches3 = (attributes, props) => attributes.digest === digestOf(canonicalFromProps(props));
|
|
739
|
+
|
|
740
|
+
// src/openbao/pki-role.ts
|
|
741
|
+
var BaoPkiRole = Resource4("Bao.PkiRole", {
|
|
742
|
+
defaultRemovalPolicy: "retain"
|
|
743
|
+
});
|
|
744
|
+
var readRole2 = (props) => Effect8.gen(function* () {
|
|
745
|
+
const live = yield* baoRead(rolePath2(props));
|
|
746
|
+
if (live === undefined)
|
|
747
|
+
return;
|
|
748
|
+
return attributesOf4(props, live);
|
|
749
|
+
});
|
|
750
|
+
var badDurations = (props) => [["ttl", props.ttl], ["maxTtl", props.maxTtl]].filter(([, text2]) => parseDuration(text2) === undefined);
|
|
751
|
+
var BaoPkiRoleProvider = () => Provider4.effect(BaoPkiRole, Effect8.succeed(BaoPkiRole.Provider.of({
|
|
752
|
+
list: () => Effect8.succeed([]),
|
|
753
|
+
read: Effect8.fn(function* ({ olds }) {
|
|
754
|
+
return yield* readRole2(olds);
|
|
755
|
+
}),
|
|
756
|
+
diff: Effect8.fn(function* ({ news, output }) {
|
|
757
|
+
if (output === undefined || !isResolved4(news))
|
|
758
|
+
return;
|
|
759
|
+
if (mountPath(news.mount ?? "pki") !== output.mount)
|
|
760
|
+
return { action: "replace" };
|
|
761
|
+
if (news.allowedDomains.length === 0 || badDurations(news).length > 0) {
|
|
762
|
+
return { action: "update" };
|
|
763
|
+
}
|
|
764
|
+
const live = yield* readRole2(news);
|
|
765
|
+
if (live === undefined)
|
|
766
|
+
return { action: "update" };
|
|
767
|
+
return matches3(live, news) ? { action: "noop" } : { action: "update" };
|
|
768
|
+
}),
|
|
769
|
+
reconcile: Effect8.fn(function* ({ news }) {
|
|
770
|
+
const path = rolePath2(news);
|
|
771
|
+
if (news.allowedDomains.length === 0) {
|
|
772
|
+
return yield* Effect8.die(new Error(`Bao.PkiRole ${path}: allowedDomains is empty. With allow_any_name false ` + "that role can issue no certificate at all."));
|
|
773
|
+
}
|
|
774
|
+
const bad = badDurations(news);
|
|
775
|
+
if (bad.length > 0) {
|
|
776
|
+
return yield* Effect8.die(new Error(`Bao.PkiRole ${path}: ${bad.map(([k, v]) => `${k}=${v}`).join(", ")} ` + "is not a duration OpenBao parses (expect 30m, 720h, 8760h or 0)."));
|
|
777
|
+
}
|
|
778
|
+
const live = yield* readRole2(news);
|
|
779
|
+
if (live === undefined || !matches3(live, news)) {
|
|
780
|
+
yield* baoWrite("PUT", path, writeBody3(news));
|
|
781
|
+
}
|
|
782
|
+
const after = yield* readRole2(news);
|
|
783
|
+
if (after === undefined) {
|
|
784
|
+
return yield* Effect8.die(new Error(`Bao.PkiRole ${path}: write reported success but the role is absent.`));
|
|
785
|
+
}
|
|
786
|
+
if (!matches3(after, news)) {
|
|
787
|
+
return yield* Effect8.die(new Error(`Bao.PkiRole ${path}: wrote cleanly but the role read back different. ` + "OpenBao stored something other than what was declared — inspect with " + `\`bao read ${path}\` before retrying.`));
|
|
788
|
+
}
|
|
789
|
+
return after;
|
|
790
|
+
}),
|
|
791
|
+
delete: Effect8.fn(function* ({ output }) {
|
|
792
|
+
yield* baoDelete(rolePath2(output));
|
|
793
|
+
return;
|
|
794
|
+
})
|
|
795
|
+
})));
|
|
796
|
+
// src/openbao/policy.ts
|
|
797
|
+
import { Resource as Resource5 } from "alchemy";
|
|
798
|
+
import { isResolved as isResolved5 } from "alchemy/Diff";
|
|
799
|
+
import * as Provider5 from "alchemy/Provider";
|
|
800
|
+
import * as Effect10 from "effect/Effect";
|
|
801
|
+
import * as FileSystem from "effect/FileSystem";
|
|
802
|
+
import * as Path from "effect/Path";
|
|
803
|
+
|
|
804
|
+
// src/openbao/policy-assembly.ts
|
|
805
|
+
var isEmptyAssembly = (parts, joined) => parts === 0 || !/^\s*path\s+"/m.test(joined);
|
|
806
|
+
|
|
807
|
+
// src/openbao/policy-wire.ts
|
|
808
|
+
import * as Effect9 from "effect/Effect";
|
|
809
|
+
var policyPath = (name) => `sys/policies/acl/${name}`;
|
|
810
|
+
var readPolicy = (name) => Effect9.flatMap(baoRead(policyPath(name)), (data) => {
|
|
811
|
+
if (data === undefined)
|
|
812
|
+
return Effect9.succeed("");
|
|
813
|
+
const text2 = data["policy"];
|
|
814
|
+
return typeof text2 === "string" ? Effect9.succeed(text2) : Effect9.fail(new BaoError(200, "GET", policyPath(name), ["no data.policy in the response"]));
|
|
815
|
+
});
|
|
816
|
+
var writePolicy = (name, hcl) => baoWrite("PUT", policyPath(name), { policy: hcl });
|
|
817
|
+
var deletePolicy = (name) => baoDelete(policyPath(name));
|
|
818
|
+
|
|
819
|
+
// src/openbao/policy.ts
|
|
820
|
+
var BaoPolicy = Resource5("Bao.Policy", {
|
|
821
|
+
defaultRemovalPolicy: "retain"
|
|
822
|
+
});
|
|
823
|
+
var grantsOf = (hcl) => (hcl.match(/^path /gm) ?? []).length;
|
|
824
|
+
var attributesOf5 = (name, hcl, parts) => ({
|
|
825
|
+
name,
|
|
826
|
+
digest: sha256(hcl),
|
|
827
|
+
grants: grantsOf(hcl),
|
|
828
|
+
parts
|
|
829
|
+
});
|
|
830
|
+
var BaoPolicyProvider = () => Provider5.effect(BaoPolicy, Effect10.gen(function* () {
|
|
831
|
+
const fs = yield* FileSystem.FileSystem;
|
|
832
|
+
const path = yield* Path.Path;
|
|
833
|
+
const assemble = (dir) => Effect10.gen(function* () {
|
|
834
|
+
const entries = (yield* fs.readDirectory(dir)).filter((f) => f.endsWith(".hcl")).sort();
|
|
835
|
+
let joined = "";
|
|
836
|
+
for (const f of entries)
|
|
837
|
+
joined += `${yield* fs.readFileString(path.join(dir, f))}
|
|
838
|
+
`;
|
|
839
|
+
return { joined, parts: entries.length };
|
|
840
|
+
});
|
|
841
|
+
return BaoPolicy.Provider.of({
|
|
842
|
+
list: () => Effect10.succeed([]),
|
|
843
|
+
read: Effect10.fn(function* ({ olds }) {
|
|
844
|
+
const live = yield* readPolicy(olds.name);
|
|
845
|
+
return live.length === 0 ? undefined : attributesOf5(olds.name, live, 0);
|
|
846
|
+
}),
|
|
847
|
+
diff: Effect10.fn(function* ({ news, output }) {
|
|
848
|
+
if (output === undefined || !isResolved5(news))
|
|
849
|
+
return;
|
|
850
|
+
const { joined } = yield* assemble(news.fragments);
|
|
851
|
+
const live = yield* readPolicy(news.name);
|
|
852
|
+
return live.length > 0 && sha256(live) === sha256(joined) ? { action: "noop" } : { action: "update" };
|
|
853
|
+
}),
|
|
854
|
+
reconcile: Effect10.fn(function* ({ news }) {
|
|
855
|
+
const name = news.name;
|
|
856
|
+
const { joined, parts } = yield* assemble(news.fragments);
|
|
857
|
+
if (isEmptyAssembly(parts, joined))
|
|
858
|
+
return yield* Effect10.die(new Error(`Bao.Policy ${name}: the fragments under ${news.fragments} carry no path grant ` + `(${String(parts)} file(s)). Writing it would revoke every grant the policy carries.`));
|
|
859
|
+
const current = yield* readPolicy(name);
|
|
860
|
+
if (current.length > 0 && sha256(current) === sha256(joined)) {
|
|
861
|
+
return attributesOf5(name, joined, parts);
|
|
862
|
+
}
|
|
863
|
+
yield* writePolicy(name, joined);
|
|
864
|
+
return attributesOf5(name, joined, parts);
|
|
865
|
+
}),
|
|
866
|
+
delete: Effect10.fn(function* ({ output }) {
|
|
867
|
+
yield* deletePolicy(output.name);
|
|
868
|
+
return;
|
|
869
|
+
})
|
|
870
|
+
});
|
|
871
|
+
}));
|
|
872
|
+
// src/openbao/proxmox-role.ts
|
|
873
|
+
import { Resource as Resource6 } from "alchemy";
|
|
874
|
+
import { isResolved as isResolved6 } from "alchemy/Diff";
|
|
875
|
+
import * as Provider6 from "alchemy/Provider";
|
|
876
|
+
import * as Effect11 from "effect/Effect";
|
|
877
|
+
|
|
878
|
+
// src/openbao/proxmox-role-form.ts
|
|
879
|
+
var text2 = (value) => typeof value === "string" ? value : "";
|
|
880
|
+
var ttlText3 = (seconds3, fallback = "0") => {
|
|
881
|
+
if (seconds3 === undefined)
|
|
882
|
+
return fallback;
|
|
883
|
+
if (seconds3 % 86400 === 0)
|
|
884
|
+
return `${String(seconds3 / 86400)}d`;
|
|
885
|
+
if (seconds3 % 3600 === 0)
|
|
886
|
+
return `${String(seconds3 / 3600)}h`;
|
|
887
|
+
if (seconds3 % 60 === 0)
|
|
888
|
+
return `${String(seconds3 / 60)}m`;
|
|
889
|
+
return `${String(seconds3)}s`;
|
|
890
|
+
};
|
|
891
|
+
var rolePath3 = (mount, name) => `${mountPath(mount)}/roles/${name}`;
|
|
892
|
+
var attributesOf6 = (props, live) => {
|
|
893
|
+
const attrs = {
|
|
894
|
+
mount: mountPath(props.mount),
|
|
895
|
+
name: text2(live["name"]) || props.name,
|
|
896
|
+
mintUser: text2(live["mint_user"]),
|
|
897
|
+
ttl: ttlText3(ttlSeconds(live["ttl"])),
|
|
898
|
+
maxTtl: ttlText3(ttlSeconds(live["max_ttl"]))
|
|
899
|
+
};
|
|
900
|
+
return { ...attrs, digest: sha256(JSON.stringify(attrs)) };
|
|
901
|
+
};
|
|
902
|
+
var writeBody4 = (props) => {
|
|
903
|
+
const ttl = parseDuration(props.ttl);
|
|
904
|
+
const maxTtl = parseDuration(props.maxTtl);
|
|
905
|
+
const bad = [];
|
|
906
|
+
if (ttl === undefined)
|
|
907
|
+
bad.push(`ttl=${props.ttl}`);
|
|
908
|
+
if (maxTtl === undefined)
|
|
909
|
+
bad.push(`maxTtl=${props.maxTtl}`);
|
|
910
|
+
if (ttl === undefined || maxTtl === undefined)
|
|
911
|
+
return { ok: false, bad };
|
|
912
|
+
return {
|
|
913
|
+
ok: true,
|
|
914
|
+
body: { mint_user: props.mintUser, ttl: String(ttl), max_ttl: String(maxTtl) }
|
|
915
|
+
};
|
|
916
|
+
};
|
|
917
|
+
var sameTtl4 = (want, have) => {
|
|
918
|
+
const wantSeconds = parseDuration(want);
|
|
919
|
+
const haveSeconds = ttlSeconds(have);
|
|
920
|
+
return wantSeconds !== undefined && wantSeconds === haveSeconds;
|
|
921
|
+
};
|
|
922
|
+
var matches4 = (attributes, props) => attributes.mintUser === props.mintUser && sameTtl4(props.ttl, attributes.ttl) && sameTtl4(props.maxTtl, attributes.maxTtl);
|
|
923
|
+
|
|
924
|
+
// src/openbao/proxmox-role.ts
|
|
925
|
+
var BaoProxmoxRole = Resource6("Bao.ProxmoxRole", {
|
|
926
|
+
defaultRemovalPolicy: "retain"
|
|
927
|
+
});
|
|
928
|
+
var readRole3 = (props) => Effect11.gen(function* () {
|
|
929
|
+
const live = yield* baoRead(rolePath3(props.mount, props.name));
|
|
930
|
+
if (live === undefined)
|
|
931
|
+
return;
|
|
932
|
+
return attributesOf6(props, live);
|
|
933
|
+
});
|
|
934
|
+
var BaoProxmoxRoleProvider = () => Provider6.effect(BaoProxmoxRole, Effect11.succeed(BaoProxmoxRole.Provider.of({
|
|
935
|
+
list: () => Effect11.succeed([]),
|
|
936
|
+
read: Effect11.fn(function* ({ olds }) {
|
|
937
|
+
return yield* readRole3(olds);
|
|
938
|
+
}),
|
|
939
|
+
diff: Effect11.fn(function* ({ news, output }) {
|
|
940
|
+
if (output === undefined || !isResolved6(news))
|
|
941
|
+
return;
|
|
942
|
+
const live = yield* readRole3(news);
|
|
943
|
+
if (live === undefined)
|
|
944
|
+
return { action: "update" };
|
|
945
|
+
if (matches4(live, news))
|
|
946
|
+
return { action: "noop" };
|
|
947
|
+
return { action: "update" };
|
|
948
|
+
}),
|
|
949
|
+
reconcile: Effect11.fn(function* ({ news }) {
|
|
950
|
+
const live = yield* readRole3(news);
|
|
951
|
+
if (live !== undefined && live.mintUser !== news.mintUser && news.allowMintUserChange !== live.mintUser) {
|
|
952
|
+
return yield* Effect11.die(new Error(`Bao.ProxmoxRole ${rolePath3(news.mount, news.name)}: live mint_user ` + `${live.mintUser} != declared ${news.mintUser}. Every token this role mints ` + "inherits the ACL of that PVE user. To re-scope deliberately, declare " + `allowMintUserChange: '${live.mintUser}'.`));
|
|
953
|
+
}
|
|
954
|
+
if (live === undefined || !matches4(live, news)) {
|
|
955
|
+
const write = writeBody4(news);
|
|
956
|
+
if (!write.ok) {
|
|
957
|
+
return yield* Effect11.die(new Error(`Bao.ProxmoxRole ${rolePath3(news.mount, news.name)}: unparseable duration ` + `${write.bad.join(", ")}. Use 30s / 5m / 1h / 1d, or 0.`));
|
|
958
|
+
}
|
|
959
|
+
yield* baoWrite("PUT", rolePath3(news.mount, news.name), write.body);
|
|
960
|
+
}
|
|
961
|
+
const after = yield* readRole3(news);
|
|
962
|
+
if (after === undefined) {
|
|
963
|
+
return yield* Effect11.die(new Error(`Bao.ProxmoxRole ${rolePath3(news.mount, news.name)}: write returned no error ` + "but the role is still absent."));
|
|
964
|
+
}
|
|
965
|
+
return after;
|
|
966
|
+
}),
|
|
967
|
+
delete: Effect11.fn(function* ({ output }) {
|
|
968
|
+
yield* baoDelete(rolePath3(output.mount, output.name));
|
|
969
|
+
return;
|
|
970
|
+
})
|
|
971
|
+
})));
|
|
972
|
+
// src/openbao/ssh-role.ts
|
|
973
|
+
import { Resource as Resource7 } from "alchemy";
|
|
974
|
+
import { isResolved as isResolved7 } from "alchemy/Diff";
|
|
975
|
+
import * as Provider7 from "alchemy/Provider";
|
|
976
|
+
import * as Effect12 from "effect/Effect";
|
|
977
|
+
|
|
978
|
+
// src/openbao/ssh-role-form.ts
|
|
979
|
+
var mountOf = (mount) => (mount ?? "ssh").replace(/\/+$/, "");
|
|
980
|
+
var readPath3 = (mount, name) => `${mountOf(mount)}/roles/${name}`;
|
|
981
|
+
var text3 = (value) => typeof value === "string" ? value : "";
|
|
982
|
+
var bool3 = (value, fallback) => typeof value === "boolean" ? value : fallback;
|
|
983
|
+
var csvList = (value) => {
|
|
984
|
+
const parts = Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : text3(value).split(",");
|
|
985
|
+
return [
|
|
986
|
+
...new Set(parts.map((entry) => entry.trim()).filter((entry) => entry.length > 0))
|
|
987
|
+
].sort();
|
|
988
|
+
};
|
|
989
|
+
var extMap = (value) => {
|
|
990
|
+
const out = {};
|
|
991
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
992
|
+
return out;
|
|
993
|
+
const source = value;
|
|
994
|
+
for (const key of Object.keys(source).sort())
|
|
995
|
+
out[key] = text3(source[key]);
|
|
996
|
+
return out;
|
|
997
|
+
};
|
|
998
|
+
var resolve = (props) => ({
|
|
999
|
+
name: props.name,
|
|
1000
|
+
mount: mountOf(props.mount),
|
|
1001
|
+
allowedUsers: csvList(props.allowedUsers),
|
|
1002
|
+
defaultUser: props.defaultUser ?? "",
|
|
1003
|
+
allowUserCertificates: props.allowUserCertificates ?? false,
|
|
1004
|
+
allowHostCertificates: props.allowHostCertificates ?? false,
|
|
1005
|
+
allowedDomains: csvList(props.allowedDomains),
|
|
1006
|
+
allowSubdomains: props.allowSubdomains ?? false,
|
|
1007
|
+
allowBareDomains: props.allowBareDomains ?? false,
|
|
1008
|
+
allowedExtensions: csvList(props.allowedExtensions),
|
|
1009
|
+
defaultExtensions: extMap(props.defaultExtensions ?? {}),
|
|
1010
|
+
allowedCriticalOptions: csvList(props.allowedCriticalOptions),
|
|
1011
|
+
defaultCriticalOptions: extMap(props.defaultCriticalOptions ?? {}),
|
|
1012
|
+
allowEmptyPrincipals: props.allowEmptyPrincipals ?? false,
|
|
1013
|
+
ttl: props.ttl,
|
|
1014
|
+
maxTtl: props.maxTtl
|
|
1015
|
+
});
|
|
1016
|
+
var attributesOf7 = (form, live) => {
|
|
1017
|
+
const attrs = {
|
|
1018
|
+
name: form.name,
|
|
1019
|
+
mount: form.mount,
|
|
1020
|
+
allowedUsers: csvList(live["allowed_users"]),
|
|
1021
|
+
defaultUser: text3(live["default_user"]),
|
|
1022
|
+
allowUserCertificates: bool3(live["allow_user_certificates"], false),
|
|
1023
|
+
allowHostCertificates: bool3(live["allow_host_certificates"], false),
|
|
1024
|
+
allowedDomains: csvList(live["allowed_domains"]),
|
|
1025
|
+
allowSubdomains: bool3(live["allow_subdomains"], false),
|
|
1026
|
+
allowBareDomains: bool3(live["allow_bare_domains"], false),
|
|
1027
|
+
allowedExtensions: csvList(live["allowed_extensions"]),
|
|
1028
|
+
defaultExtensions: extMap(live["default_extensions"]),
|
|
1029
|
+
allowedCriticalOptions: csvList(live["allowed_critical_options"]),
|
|
1030
|
+
defaultCriticalOptions: extMap(live["default_critical_options"]),
|
|
1031
|
+
allowEmptyPrincipals: bool3(live["allow_empty_principals"], false),
|
|
1032
|
+
keyType: text3(live["key_type"]) || "ca",
|
|
1033
|
+
ttlSeconds: ttlSeconds(live["ttl"]) ?? 0,
|
|
1034
|
+
maxTtlSeconds: ttlSeconds(live["max_ttl"]) ?? 0
|
|
1035
|
+
};
|
|
1036
|
+
return { ...attrs, digest: sha256(JSON.stringify(attrs)) };
|
|
1037
|
+
};
|
|
1038
|
+
var mapValue = (map4) => JSON.stringify(extMap(map4));
|
|
1039
|
+
var writeBody5 = (form) => ({
|
|
1040
|
+
key_type: "ca",
|
|
1041
|
+
allowed_users: form.allowedUsers.join(","),
|
|
1042
|
+
default_user: form.defaultUser,
|
|
1043
|
+
allow_user_certificates: String(form.allowUserCertificates),
|
|
1044
|
+
allow_host_certificates: String(form.allowHostCertificates),
|
|
1045
|
+
allowed_domains: form.allowedDomains.join(","),
|
|
1046
|
+
allow_subdomains: String(form.allowSubdomains),
|
|
1047
|
+
allow_bare_domains: String(form.allowBareDomains),
|
|
1048
|
+
allowed_extensions: form.allowedExtensions.join(","),
|
|
1049
|
+
default_extensions: mapValue(form.defaultExtensions),
|
|
1050
|
+
allowed_critical_options: form.allowedCriticalOptions.join(","),
|
|
1051
|
+
default_critical_options: mapValue(form.defaultCriticalOptions),
|
|
1052
|
+
allow_empty_principals: String(form.allowEmptyPrincipals),
|
|
1053
|
+
ttl: form.ttl,
|
|
1054
|
+
max_ttl: form.maxTtl
|
|
1055
|
+
});
|
|
1056
|
+
var sameList2 = (want, have) => want.length === have.length && want.every((entry, index) => entry === have[index]);
|
|
1057
|
+
var sameMap = (want, have) => {
|
|
1058
|
+
const keys = Object.keys(want);
|
|
1059
|
+
return keys.length === Object.keys(have).length && keys.every((key) => have[key] === want[key]);
|
|
1060
|
+
};
|
|
1061
|
+
var sameTtl5 = (want, have) => parseDuration(want) === have;
|
|
1062
|
+
var badDurations2 = (form) => ["ttl", "maxTtl"].filter((field) => parseDuration(form[field]) === undefined);
|
|
1063
|
+
var matches5 = (attributes, form) => attributes.keyType === "ca" && sameList2(form.allowedUsers, attributes.allowedUsers) && attributes.defaultUser === form.defaultUser && attributes.allowUserCertificates === form.allowUserCertificates && attributes.allowHostCertificates === form.allowHostCertificates && sameList2(form.allowedDomains, attributes.allowedDomains) && attributes.allowSubdomains === form.allowSubdomains && attributes.allowBareDomains === form.allowBareDomains && sameList2(form.allowedExtensions, attributes.allowedExtensions) && sameMap(form.defaultExtensions, attributes.defaultExtensions) && sameList2(form.allowedCriticalOptions, attributes.allowedCriticalOptions) && sameMap(form.defaultCriticalOptions, attributes.defaultCriticalOptions) && attributes.allowEmptyPrincipals === form.allowEmptyPrincipals && sameTtl5(form.ttl, attributes.ttlSeconds) && sameTtl5(form.maxTtl, attributes.maxTtlSeconds);
|
|
1064
|
+
|
|
1065
|
+
// src/openbao/ssh-role.ts
|
|
1066
|
+
var BaoSshRole = Resource7("Bao.SshRole", {
|
|
1067
|
+
defaultRemovalPolicy: "retain"
|
|
1068
|
+
});
|
|
1069
|
+
var readRole4 = (form) => Effect12.gen(function* () {
|
|
1070
|
+
const live = yield* baoRead(readPath3(form.mount, form.name));
|
|
1071
|
+
if (live === undefined)
|
|
1072
|
+
return;
|
|
1073
|
+
return { attributes: attributesOf7(form, live), live };
|
|
1074
|
+
});
|
|
1075
|
+
var UNMANAGED_DEFAULTS = {
|
|
1076
|
+
algorithm_signer: "default",
|
|
1077
|
+
allow_commas_in_identity_templates: false,
|
|
1078
|
+
allow_user_key_ids: false,
|
|
1079
|
+
allowed_domains_template: false,
|
|
1080
|
+
allowed_users_template: false,
|
|
1081
|
+
default_extensions_template: false,
|
|
1082
|
+
default_user_template: false,
|
|
1083
|
+
issuer_ref: "default",
|
|
1084
|
+
key_id_format: "",
|
|
1085
|
+
not_before_duration: 30
|
|
1086
|
+
};
|
|
1087
|
+
var isDefault = (live, field) => live[field] === undefined || live[field] === "" || live[field] === UNMANAGED_DEFAULTS[field];
|
|
1088
|
+
var wouldErase = (live) => {
|
|
1089
|
+
const lost = Object.keys(UNMANAGED_DEFAULTS).filter((field) => !isDefault(live, field));
|
|
1090
|
+
if (Object.keys(extMap(live["allowed_user_key_lengths"])).length > 0) {
|
|
1091
|
+
lost.push("allowed_user_key_lengths");
|
|
1092
|
+
}
|
|
1093
|
+
return lost.sort();
|
|
1094
|
+
};
|
|
1095
|
+
var refuse = (message) => Effect12.die(new Error(message));
|
|
1096
|
+
var BaoSshRoleProvider = () => Provider7.effect(BaoSshRole, Effect12.succeed(BaoSshRole.Provider.of({
|
|
1097
|
+
list: () => Effect12.succeed([]),
|
|
1098
|
+
read: Effect12.fn(function* ({ olds }) {
|
|
1099
|
+
const found = yield* readRole4(resolve(olds));
|
|
1100
|
+
return found?.attributes;
|
|
1101
|
+
}),
|
|
1102
|
+
diff: Effect12.fn(function* ({ news, output }) {
|
|
1103
|
+
if (output === undefined || !isResolved7(news))
|
|
1104
|
+
return;
|
|
1105
|
+
const form = resolve(news);
|
|
1106
|
+
if (output.mount !== form.mount || output.name !== form.name) {
|
|
1107
|
+
return { action: "replace" };
|
|
1108
|
+
}
|
|
1109
|
+
const found = yield* readRole4(form);
|
|
1110
|
+
if (found === undefined)
|
|
1111
|
+
return { action: "update" };
|
|
1112
|
+
return matches5(found.attributes, form) ? { action: "noop" } : { action: "update" };
|
|
1113
|
+
}),
|
|
1114
|
+
reconcile: Effect12.fn(function* ({ news }) {
|
|
1115
|
+
const form = resolve(news);
|
|
1116
|
+
const path = readPath3(form.mount, form.name);
|
|
1117
|
+
const bad = badDurations2(form);
|
|
1118
|
+
if (bad.length > 0) {
|
|
1119
|
+
return yield* refuse(`Bao.SshRole ${path}: ${bad.join(", ")} is not a duration parseDuration understands ` + "(`0` or `<n>s|m|h|d`). It would write, then report update on every plan forever.");
|
|
1120
|
+
}
|
|
1121
|
+
if (form.allowUserCertificates && form.allowedUsers.length === 0 && !form.allowEmptyPrincipals) {
|
|
1122
|
+
return yield* refuse(`Bao.SshRole ${path}: allowUserCertificates with an empty allowedUsers and ` + "allowEmptyPrincipals false. Every sign request against it would be rejected.");
|
|
1123
|
+
}
|
|
1124
|
+
const found = yield* readRole4(form);
|
|
1125
|
+
if (found === undefined || !matches5(found.attributes, form)) {
|
|
1126
|
+
if (found !== undefined) {
|
|
1127
|
+
const lost = wouldErase(found.live);
|
|
1128
|
+
if (lost.length > 0) {
|
|
1129
|
+
return yield* refuse(`Bao.SshRole ${path}: live role carries ${lost.join(", ")}, which this ` + "resource does not declare and the full-replace write would ERASE. " + "Fold those settings into the declaration, or clear them by hand first.");
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
yield* baoWrite("PUT", path, writeBody5(form));
|
|
1133
|
+
}
|
|
1134
|
+
const after = yield* readRole4(form);
|
|
1135
|
+
if (after === undefined) {
|
|
1136
|
+
return yield* refuse(`Bao.SshRole ${path}: write returned no error but the role is still absent.`);
|
|
1137
|
+
}
|
|
1138
|
+
return after.attributes;
|
|
1139
|
+
}),
|
|
1140
|
+
delete: Effect12.fn(function* ({ output }) {
|
|
1141
|
+
yield* baoDelete(readPath3(output.mount, output.name));
|
|
1142
|
+
return;
|
|
1143
|
+
})
|
|
1144
|
+
})));
|
|
1145
|
+
export {
|
|
1146
|
+
BaoAuthRoleProvider,
|
|
1147
|
+
BaoCloudflareRoleProvider,
|
|
1148
|
+
BaoMountProvider,
|
|
1149
|
+
BaoPkiRoleProvider,
|
|
1150
|
+
BaoPolicy,
|
|
1151
|
+
BaoPolicyProvider,
|
|
1152
|
+
BaoProxmoxRole,
|
|
1153
|
+
BaoProxmoxRoleProvider,
|
|
1154
|
+
BaoSshRoleProvider
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
//# debugId=72D46173613CE4CB64756E2164756E21
|
|
1158
|
+
//# sourceMappingURL=index.js.map
|