@homeflare/alchemy 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/README.md +3 -3
  2. package/dist/caddy/admin-guard.d.ts +1 -1
  3. package/dist/caddy/index.js.map +2 -2
  4. package/dist/cloudflare/client.d.ts +1 -1
  5. package/dist/cloudflare/index.js.map +2 -2
  6. package/dist/cloudflare/lock-rules.d.ts +1 -1
  7. package/dist/forgejo/branch-protection-form.d.ts +1 -1
  8. package/dist/forgejo/branch-protection.d.ts +1 -1
  9. package/dist/forgejo/index.js.map +10 -10
  10. package/dist/forgejo/org-actions-secrets.d.ts +1 -1
  11. package/dist/forgejo/org-label.d.ts +1 -1
  12. package/dist/forgejo/org-team.d.ts +1 -1
  13. package/dist/forgejo/repo-webhook.d.ts +1 -1
  14. package/dist/forgejo/repository.d.ts +2 -2
  15. package/dist/forgejo/repository.d.ts.map +1 -1
  16. package/dist/forgejo/resource.d.ts +2 -2
  17. package/dist/forgejo/team-member.d.ts +1 -1
  18. package/dist/openbao/auth-role.d.ts +1 -1
  19. package/dist/openbao/bao-http.d.ts +4 -4
  20. package/dist/openbao/cloudflare-permission-groups.d.ts +1 -1
  21. package/dist/openbao/cloudflare-role-form.d.ts +2 -2
  22. package/dist/openbao/cloudflare-role-form.d.ts.map +1 -1
  23. package/dist/openbao/cloudflare-roles-expand.d.ts +1 -1
  24. package/dist/openbao/digest.d.ts +1 -1
  25. package/dist/openbao/forgejo-bootstrap-parse.d.ts +2 -2
  26. package/dist/openbao/forgejo-bootstrap-parse.d.ts.map +1 -1
  27. package/dist/openbao/forgejo-bootstrap.d.ts +1 -1
  28. package/dist/openbao/index.js.map +14 -14
  29. package/dist/openbao/mount.d.ts +1 -1
  30. package/dist/openbao/pki-role.d.ts +1 -1
  31. package/dist/openbao/policy.d.ts +2 -2
  32. package/dist/openbao/proxmox-role-form.d.ts +2 -2
  33. package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
  34. package/dist/openbao/proxmox-role.d.ts +10 -10
  35. package/dist/openbao/ssh-role.d.ts +3 -3
  36. package/dist/proxmox/acl.d.ts +2 -2
  37. package/dist/proxmox/acl.d.ts.map +1 -1
  38. package/dist/proxmox/alertmanager-body.d.ts +54 -0
  39. package/dist/proxmox/alertmanager-body.d.ts.map +1 -0
  40. package/dist/proxmox/api-token-form.d.ts +2 -2
  41. package/dist/proxmox/api-token.d.ts +11 -11
  42. package/dist/proxmox/ceph-daemon-form.d.ts +3 -3
  43. package/dist/proxmox/ceph-daemon.d.ts +19 -19
  44. package/dist/proxmox/ceph-daemon.d.ts.map +1 -1
  45. package/dist/proxmox/ceph-flag.d.ts +5 -5
  46. package/dist/proxmox/ceph-fs-wire.d.ts +5 -5
  47. package/dist/proxmox/ceph-fs.d.ts +16 -16
  48. package/dist/proxmox/ceph-osd-tree.d.ts +16 -16
  49. package/dist/proxmox/ceph-osd.d.ts +5 -5
  50. package/dist/proxmox/ceph-pool-form.d.ts +13 -13
  51. package/dist/proxmox/ceph-pool-form.d.ts.map +1 -1
  52. package/dist/proxmox/ceph-pool.d.ts +3 -3
  53. package/dist/proxmox/client.d.ts +2 -2
  54. package/dist/proxmox/credential-literals.d.ts +36 -0
  55. package/dist/proxmox/credential-literals.d.ts.map +1 -0
  56. package/dist/proxmox/credentials.d.ts +8 -8
  57. package/dist/proxmox/credentials.d.ts.map +1 -1
  58. package/dist/proxmox/fake-pbs-notify.d.ts +23 -0
  59. package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -0
  60. package/dist/proxmox/fake-pve.d.ts +5 -0
  61. package/dist/proxmox/fake-pve.d.ts.map +1 -1
  62. package/dist/proxmox/firewall-alias-form.d.ts +1 -1
  63. package/dist/proxmox/firewall-alias.d.ts +1 -1
  64. package/dist/proxmox/generated/pbs.d.ts +279 -1
  65. package/dist/proxmox/generated/pbs.d.ts.map +1 -1
  66. package/dist/proxmox/generated/pve.d.ts +58 -1
  67. package/dist/proxmox/generated/pve.d.ts.map +1 -1
  68. package/dist/proxmox/group.d.ts +8 -8
  69. package/dist/proxmox/ha-resource.d.ts +3 -2
  70. package/dist/proxmox/ha-resource.d.ts.map +1 -1
  71. package/dist/proxmox/ha-rule-form.d.ts +5 -5
  72. package/dist/proxmox/ha-rule.d.ts +3 -3
  73. package/dist/proxmox/index.d.ts +14 -0
  74. package/dist/proxmox/index.d.ts.map +1 -1
  75. package/dist/proxmox/index.js +935 -223
  76. package/dist/proxmox/index.js.map +69 -56
  77. package/dist/proxmox/lease-cache.d.ts +4 -4
  78. package/dist/proxmox/lxc-harness.d.ts +1 -1
  79. package/dist/proxmox/lxc-props.d.ts +2 -2
  80. package/dist/proxmox/lxc.d.ts +3 -3
  81. package/dist/proxmox/members.d.ts +3 -3
  82. package/dist/proxmox/network-apply-read.d.ts +5 -5
  83. package/dist/proxmox/network-apply.d.ts +9 -9
  84. package/dist/proxmox/node-network-form.d.ts +6 -6
  85. package/dist/proxmox/node-network.d.ts +17 -17
  86. package/dist/proxmox/node-network.d.ts.map +1 -1
  87. package/dist/proxmox/notification-matcher-form.d.ts +100 -0
  88. package/dist/proxmox/notification-matcher-form.d.ts.map +1 -0
  89. package/dist/proxmox/notification-matcher.d.ts +46 -0
  90. package/dist/proxmox/notification-matcher.d.ts.map +1 -0
  91. package/dist/proxmox/notification-target.d.ts +3 -2
  92. package/dist/proxmox/notification-target.d.ts.map +1 -1
  93. package/dist/proxmox/pbs-datastore-guard.d.ts +1 -1
  94. package/dist/proxmox/pbs-datastore.d.ts +10 -10
  95. package/dist/proxmox/pbs-notification-matcher.d.ts +53 -0
  96. package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -0
  97. package/dist/proxmox/pbs-notification-target-form.d.ts +63 -0
  98. package/dist/proxmox/pbs-notification-target-form.d.ts.map +1 -0
  99. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +100 -0
  100. package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -0
  101. package/dist/proxmox/pbs-notification-target-wire.d.ts +30 -0
  102. package/dist/proxmox/pbs-notification-target-wire.d.ts.map +1 -0
  103. package/dist/proxmox/pbs-notification-target.d.ts +123 -0
  104. package/dist/proxmox/pbs-notification-target.d.ts.map +1 -0
  105. package/dist/proxmox/pbs-prune-job.d.ts +2 -2
  106. package/dist/proxmox/pbs-sync-job.d.ts +4 -4
  107. package/dist/proxmox/pbs-verify-job.d.ts +1 -1
  108. package/dist/proxmox/pool.d.ts +4 -4
  109. package/dist/proxmox/provision-baseline.d.ts +74 -0
  110. package/dist/proxmox/provision-baseline.d.ts.map +1 -0
  111. package/dist/proxmox/provision-bootstrap.d.ts +33 -0
  112. package/dist/proxmox/provision-bootstrap.d.ts.map +1 -0
  113. package/dist/proxmox/provision-cli-fake.d.ts +38 -0
  114. package/dist/proxmox/provision-cli-fake.d.ts.map +1 -0
  115. package/dist/proxmox/provision-declare.d.ts +25 -0
  116. package/dist/proxmox/provision-declare.d.ts.map +1 -0
  117. package/dist/proxmox/replication-job.d.ts +9 -8
  118. package/dist/proxmox/replication-job.d.ts.map +1 -1
  119. package/dist/proxmox/resource.d.ts +6 -5
  120. package/dist/proxmox/resource.d.ts.map +1 -1
  121. package/dist/proxmox/sdn-apply-read.d.ts +7 -7
  122. package/dist/proxmox/sdn-apply.d.ts +1 -1
  123. package/dist/proxmox/sdn-subnet-form.d.ts +1 -1
  124. package/dist/proxmox/sdn-subnet.d.ts +5 -5
  125. package/dist/proxmox/sdn-vnet.d.ts +5 -6
  126. package/dist/proxmox/sdn-vnet.d.ts.map +1 -1
  127. package/dist/proxmox/storage.d.ts +4 -3
  128. package/dist/proxmox/storage.d.ts.map +1 -1
  129. package/dist/proxmox/user.d.ts +3 -2
  130. package/dist/proxmox/user.d.ts.map +1 -1
  131. package/dist/proxmox/values.d.ts +1 -1
  132. package/dist/proxmox/write-only.d.ts +35 -0
  133. package/dist/proxmox/write-only.d.ts.map +1 -0
  134. package/dist/proxmox/zfs-pool-write.d.ts +1 -1
  135. package/dist/proxmox/zfs-pool.d.ts +5 -5
  136. package/dist/talos/credentials.d.ts +1 -1
  137. package/dist/talos/credentials.d.ts.map +1 -1
  138. package/dist/talos/index.js.map +1 -1
  139. package/dist/verify/fake-engine.d.ts +5 -0
  140. package/dist/verify/fake-engine.d.ts.map +1 -1
  141. package/docs/adopt-verify.md +21 -5
  142. package/docs/adopted-deploys.md +9 -1
  143. package/docs/ownership.md +26 -1
  144. package/docs/pbs-alertmanager-body.md +73 -0
  145. package/docs/pbs-notifications.md +163 -0
  146. package/docs/provision-baseline.md +106 -0
  147. package/docs/proxmox-lxc-adopt.md +2 -2
  148. package/docs/proxmox-lxc.md +5 -4
  149. package/docs/proxmox.md +9 -0
  150. package/package.json +1 -1
  151. package/src/caddy/admin-guard.ts +1 -1
  152. package/src/caddy/local-admin.ts +1 -1
  153. package/src/cloudflare/client.ts +1 -1
  154. package/src/cloudflare/lock-rules.ts +1 -1
  155. package/src/forgejo/branch-protection-form.ts +1 -1
  156. package/src/forgejo/branch-protection.ts +1 -1
  157. package/src/forgejo/client.ts +1 -1
  158. package/src/forgejo/org-actions-secrets.ts +1 -1
  159. package/src/forgejo/org-label.ts +1 -1
  160. package/src/forgejo/org-team.ts +1 -1
  161. package/src/forgejo/repo-webhook.ts +1 -1
  162. package/src/forgejo/repository.ts +2 -2
  163. package/src/forgejo/resource.ts +2 -2
  164. package/src/forgejo/team-member.ts +1 -1
  165. package/src/openbao/README.md +4 -4
  166. package/src/openbao/auth-role.ts +1 -1
  167. package/src/openbao/bao-http.test.ts +2 -2
  168. package/src/openbao/bao-http.ts +6 -6
  169. package/src/openbao/bao-status.test.ts +1 -1
  170. package/src/openbao/bao-status.ts +1 -1
  171. package/src/openbao/cloudflare-group-scope.test.ts +1 -1
  172. package/src/openbao/cloudflare-permission-groups.test.ts +1 -1
  173. package/src/openbao/cloudflare-permission-groups.ts +3 -3
  174. package/src/openbao/cloudflare-policy.ts +1 -1
  175. package/src/openbao/cloudflare-role-form.ts +2 -2
  176. package/src/openbao/cloudflare-roles-expand.ts +1 -1
  177. package/src/openbao/digest.ts +1 -1
  178. package/src/openbao/forgejo-bootstrap-parse.test.ts +3 -3
  179. package/src/openbao/forgejo-bootstrap-parse.ts +2 -2
  180. package/src/openbao/forgejo-bootstrap.ts +4 -4
  181. package/src/openbao/kubernetes-role-form.test.ts +5 -5
  182. package/src/openbao/mount.ts +1 -1
  183. package/src/openbao/pki-role.ts +1 -1
  184. package/src/openbao/policy-wire.test.ts +3 -3
  185. package/src/openbao/policy.ts +2 -2
  186. package/src/openbao/proxmox-role-form.ts +5 -5
  187. package/src/openbao/proxmox-role.ts +11 -11
  188. package/src/openbao/ssh-role.ts +3 -3
  189. package/src/proxmox/acl.ts +4 -4
  190. package/src/proxmox/adopt-noop.test.ts +6 -6
  191. package/src/proxmox/alertmanager-body.ts +125 -0
  192. package/src/proxmox/api-token-form.ts +4 -4
  193. package/src/proxmox/api-token.ts +13 -13
  194. package/src/proxmox/ceph-daemon-form.ts +3 -3
  195. package/src/proxmox/ceph-daemon.ts +20 -20
  196. package/src/proxmox/ceph-flag.ts +7 -7
  197. package/src/proxmox/ceph-fs-wire.ts +7 -7
  198. package/src/proxmox/ceph-fs.ts +18 -18
  199. package/src/proxmox/ceph-osd-tree.ts +16 -16
  200. package/src/proxmox/ceph-osd.ts +6 -6
  201. package/src/proxmox/ceph-pool-adopt.test.ts +14 -14
  202. package/src/proxmox/ceph-pool-form.ts +13 -13
  203. package/src/proxmox/ceph-pool.ts +6 -6
  204. package/src/proxmox/client.test.ts +3 -3
  205. package/src/proxmox/client.ts +3 -3
  206. package/src/proxmox/credential-literals.test.ts +148 -0
  207. package/src/proxmox/credential-literals.ts +74 -0
  208. package/src/proxmox/credentials.test.ts +6 -8
  209. package/src/proxmox/credentials.ts +8 -8
  210. package/src/proxmox/fake-pbs-notify.ts +108 -0
  211. package/src/proxmox/fake-pve.ts +12 -2
  212. package/src/proxmox/firewall-alias-form.ts +1 -1
  213. package/src/proxmox/firewall-alias.ts +1 -1
  214. package/src/proxmox/generated/pbs.ts +330 -1
  215. package/src/proxmox/generated/pve.ts +63 -1
  216. package/src/proxmox/group.ts +10 -10
  217. package/src/proxmox/ha-resource.ts +3 -2
  218. package/src/proxmox/ha-rule-form.ts +5 -5
  219. package/src/proxmox/ha-rule.ts +3 -3
  220. package/src/proxmox/index.test.ts +10 -0
  221. package/src/proxmox/index.ts +32 -0
  222. package/src/proxmox/lease-cache.test.ts +19 -19
  223. package/src/proxmox/lease-cache.ts +4 -4
  224. package/src/proxmox/lxc-adopt.test.ts +3 -3
  225. package/src/proxmox/lxc-create-form.ts +1 -1
  226. package/src/proxmox/lxc-guards.test.ts +4 -4
  227. package/src/proxmox/lxc-harness.ts +3 -3
  228. package/src/proxmox/lxc-judge.test.ts +5 -5
  229. package/src/proxmox/lxc-lifecycle.ts +1 -1
  230. package/src/proxmox/lxc-props.ts +2 -2
  231. package/src/proxmox/lxc-read.test.ts +3 -3
  232. package/src/proxmox/lxc-strict-adopt.test.ts +1 -1
  233. package/src/proxmox/lxc-volume.test.ts +9 -9
  234. package/src/proxmox/lxc-volume.ts +1 -1
  235. package/src/proxmox/lxc-wire.ts +1 -1
  236. package/src/proxmox/lxc.ts +3 -3
  237. package/src/proxmox/members.test.ts +1 -1
  238. package/src/proxmox/members.ts +3 -3
  239. package/src/proxmox/metric-server-otel.ts +2 -2
  240. package/src/proxmox/network-apply-read.ts +10 -10
  241. package/src/proxmox/network-apply.ts +10 -10
  242. package/src/proxmox/node-network-form.ts +7 -7
  243. package/src/proxmox/node-network.ts +18 -18
  244. package/src/proxmox/notification-matcher-form.ts +221 -0
  245. package/src/proxmox/notification-matcher.test.ts +234 -0
  246. package/src/proxmox/notification-matcher.ts +76 -0
  247. package/src/proxmox/notification-target.ts +3 -2
  248. package/src/proxmox/pbs-datastore-guard.ts +1 -1
  249. package/src/proxmox/pbs-datastore.ts +10 -10
  250. package/src/proxmox/pbs-notification-matcher.ts +81 -0
  251. package/src/proxmox/pbs-notification-target-drift.test.ts +83 -0
  252. package/src/proxmox/pbs-notification-target-form.ts +229 -0
  253. package/src/proxmox/pbs-notification-target-lifecycle.ts +177 -0
  254. package/src/proxmox/pbs-notification-target-state.test.ts +191 -0
  255. package/src/proxmox/pbs-notification-target-wire.ts +112 -0
  256. package/src/proxmox/pbs-notification-target.test.ts +226 -0
  257. package/src/proxmox/pbs-notification-target.ts +139 -0
  258. package/src/proxmox/pbs-notifications-stack.test.ts +94 -0
  259. package/src/proxmox/pbs-prune-job.ts +2 -2
  260. package/src/proxmox/pbs-sync-job.ts +4 -4
  261. package/src/proxmox/pbs-verify-job.ts +1 -1
  262. package/src/proxmox/pool.test.ts +2 -2
  263. package/src/proxmox/pool.ts +4 -4
  264. package/src/proxmox/provision-baseline.test.ts +87 -0
  265. package/src/proxmox/provision-baseline.ts +162 -0
  266. package/src/proxmox/provision-bootstrap.test.ts +132 -0
  267. package/src/proxmox/provision-bootstrap.ts +135 -0
  268. package/src/proxmox/provision-cli-fake.ts +217 -0
  269. package/src/proxmox/provision-declare.test.ts +111 -0
  270. package/src/proxmox/provision-declare.ts +103 -0
  271. package/src/proxmox/replication-job.ts +9 -8
  272. package/src/proxmox/resource.ts +9 -9
  273. package/src/proxmox/sdn-apply-read.test.ts +16 -16
  274. package/src/proxmox/sdn-apply-read.ts +7 -7
  275. package/src/proxmox/sdn-apply.ts +3 -3
  276. package/src/proxmox/sdn-subnet-form.ts +1 -1
  277. package/src/proxmox/sdn-subnet.ts +5 -5
  278. package/src/proxmox/sdn-vnet.ts +5 -6
  279. package/src/proxmox/storage.ts +4 -3
  280. package/src/proxmox/user.ts +3 -2
  281. package/src/proxmox/values.test.ts +2 -2
  282. package/src/proxmox/values.ts +1 -1
  283. package/src/proxmox/write-only.test.ts +75 -0
  284. package/src/proxmox/write-only.ts +106 -0
  285. package/src/proxmox/zfs-pool-write.ts +1 -1
  286. package/src/proxmox/zfs-pool.ts +6 -6
  287. package/src/talos/credentials.ts +3 -3
  288. package/src/talos/values.test.ts +9 -9
  289. package/src/verify/cli.test.ts +1 -1
  290. package/src/verify/fake-engine.ts +8 -1
@@ -29,9 +29,9 @@ const { Bun } = globalThis as unknown as {
29
29
 
30
30
  const TOKEN = 'hvs.FAKE-sentinel-never-a-real-token';
31
31
  const SECRET = 'fake-pve-secret-0000';
32
- const TB4: PveTarget = {
32
+ const C1: PveTarget = {
33
33
  members: ['pve.example'],
34
- mount: 'proxmox-tb4',
34
+ mount: 'proxmox-c1',
35
35
  scheme: 'pve',
36
36
  };
37
37
 
@@ -59,11 +59,9 @@ const withFake = async (
59
59
  };
60
60
 
61
61
  const minted = (env: BaoEnvironment) =>
62
- Effect.runPromise(mint(TB4, 'read', env).pipe(Effect.provide(FetchHttpClient.layer)));
62
+ Effect.runPromise(mint(C1, 'read', env).pipe(Effect.provide(FetchHttpClient.layer)));
63
63
  const refused = (env: BaoEnvironment) =>
64
- Effect.runPromise(
65
- mint(TB4, 'read', env).pipe(Effect.flip, Effect.provide(FetchHttpClient.layer)),
66
- );
64
+ Effect.runPromise(mint(C1, 'read', env).pipe(Effect.flip, Effect.provide(FetchHttpClient.layer)));
67
65
 
68
66
  const CREDENTIAL = {
69
67
  json: { data: { secret: SECRET, token_id: 'hf-read@pve!hf-read-1' }, lease_duration: 3600 },
@@ -79,7 +77,7 @@ describe('mint', () => {
79
77
  secret: SECRET,
80
78
  tokenId: 'hf-read@pve!hf-read-1',
81
79
  });
82
- assert.equal(seen[0]?.path, '/v1/proxmox-tb4/creds/read');
80
+ assert.equal(seen[0]?.path, '/v1/proxmox-c1/creds/read');
83
81
  assert.equal(seen[0]?.headers.get('x-vault-token'), TOKEN);
84
82
  assert.equal(seen[0]?.headers.get('x-vault-namespace'), 'homeflare');
85
83
  assert.equal(seen[0]?.headers.get('x-vault-request'), 'true');
@@ -135,7 +133,7 @@ describe('mint', () => {
135
133
  CREDENTIAL,
136
134
  async (address, seen) => {
137
135
  assert.equal((await minted({ BAO_ADDR: address })).leaseSeconds, 3600);
138
- assert.equal(seen[0]?.path, '/v1/proxmox-tb4/creds/read');
136
+ assert.equal(seen[0]?.path, '/v1/proxmox-c1/creds/read');
139
137
  },
140
138
  socket,
141
139
  );
@@ -2,13 +2,13 @@
2
2
  * The PVE credential a reconcile runs with: minted from OpenBao, alive for five minutes.
3
3
  *
4
4
  * ⛔ THERE IS NO STATIC WRITE TOKEN IN THIS ESTATE, AND THAT IS DELIBERATE. Every PVE token stored
5
- * in `kv/infra/proxmox/*` is read-only — PVEAuditor on media/ops/tb4, Audit on pbs, all as
6
- * `monitoring@pve`. The MCP proxmox client says why in its own header: the auditor token bounds
5
+ * on the estate's static KV shelf is read-only — PVEAuditor on each cluster, Audit on PBS, all
6
+ * as one metrics user. The MCP proxmox client says why in its own header: the auditor token bounds
7
7
  * what a BUG can do, independently of what the code bounds. Adding a long-lived write token to
8
8
  * that shelf would delete the outer lock for every reader of the shelf, not just for this
9
9
  * provider.
10
10
  *
11
- * ★ SO WRITES COME FROM A DYNAMIC MOUNT INSTEAD, which the estate already built: `proxmox-tb4/`
11
+ * ★ SO WRITES COME FROM A DYNAMIC MOUNT INSTEAD, which the estate already built: `proxmox-c1/`
12
12
  * exposes two roles — `read` (ttl 3600s) and `provision` (ttl 300s, max 1800s, NOT renewable).
13
13
  * A mint returns a PVE API token of its own, `hf-provision@pve!hf-provision-…-<timestamp>-<id>`,
14
14
  * which expires on its own whether or not anything cleans up. Same shape as
@@ -24,7 +24,7 @@
24
24
  *
25
25
  * ⛔ THE SECRET NEVER TOUCHES DISK, A LOG, OR ALCHEMY STATE. Alchemy persists resource attributes
26
26
  * WITHOUT encryption — StateEncoding.ts writes `Redacted` as `{"@redacted": <plaintext>}` — and
27
- * this estate's state store is the `alchemy` Postgres, which pg-backup.sh dumps nightly. A PVE
27
+ * this estate's state store is the `alchemy` Postgres, which a nightly job dumps. A PVE
28
28
  * secret that reached an attribute would outlive its 300s lease by months, in four places.
29
29
  * Nothing here returns it to a resource; it is used to build a header and then dropped.
30
30
  */
@@ -60,10 +60,10 @@ export type PveRole = 'read' | 'provision';
60
60
  * use this file unchanged; that is the whole difference between a provider and a script.
61
61
  */
62
62
  export type PveTarget = {
63
- /** OpenBao mount that vends API tokens for this host, e.g. `proxmox-tb4`. */
63
+ /** OpenBao mount that vends API tokens for this host, e.g. `proxmox-c1`. */
64
64
  readonly mount: string;
65
65
  /**
66
- * mgmt hostnames for cluster members, e.g. `n2.mgmt.example.com`. Any member's :8006 API
66
+ * mgmt hostnames for cluster members, e.g. `node-b.mgmt.example.com`. Any member's :8006 API
67
67
  * manages the whole cluster; `client.ts` failovers across them.
68
68
  */
69
69
  readonly members: readonly string[];
@@ -133,14 +133,14 @@ const baoVariable = (env: BaoEnvironment, name: string) =>
133
133
  * Mint one credential for `role`: `GET /v1/<mount>/creds/<role>` on OpenBao's HTTP API.
134
134
  *
135
135
  * 🔴 IT SHELLED OUT TO `bao read -format=json` UNTIL 2026-09-14. The case for the CLI was that one
136
- * client could not disagree with `bao kv get` about what this machine may do. house/openbao then
136
+ * client could not disagree with `bao kv get` about what this machine may do. <estate>/openbao then
137
137
  * learned twice what deciding by stderr costs — every failure read as absent, and two commands
138
138
  * said "absent" in different words — so this reads STATUS CODES: 2xx is a credential, and 403
139
139
  * (denied), 404 (no such role or mount — a mint has no "absent"), 503 (sealed) all fail.
140
140
  * ★ THE ENVIRONMENT CONTRACT IS STILL THE CLI's, READ THE CLI's WAY, which keeps the case above
141
141
  * true (openbao v2.6.2 api/client.go:34-66, 370, 503-507, 761-762, 776-785): BAO_AGENT_ADDR over
142
142
  * BAO_ADDR over https://127.0.0.1:8200; BAO_NAMESPACE as X-Vault-Namespace; BAO_TOKEN as
143
- * X-Vault-Token; X-Vault-Request always. house/openbao/src/bao-address.ts has the long form.
143
+ * X-Vault-Token; X-Vault-Request always. <estate>/openbao/src/bao-address.ts has the long form.
144
144
  * ★ AGENT MODE IS NO BAO_TOKEN AND NO TOKEN HEADER. An Agent or Proxy listener with
145
145
  * `api_proxy { use_auto_auth_token = true }` (or the older `cache { … }`) substitutes its
146
146
  * auto-auth token exactly when a request carries none — command/agentproxyshared/cache/handler.go
@@ -0,0 +1,108 @@
1
+ /**
2
+ * A PBS (or PVE) notification config that remembers writes — enough of proxmox-notify's CRUD for
3
+ * a family's REAL provider to create, drift, rotate and adopt through Alchemy's engine.
4
+ *
5
+ * ★ STATEFUL, UNLIKE `fakePve`'s bare answer function, because the questions here are about the
6
+ * SECOND deploy: whether a rotated secret is written, whether an unchanged one is not. That needs
7
+ * a server that holds what the first deploy wrote.
8
+ * ★ IT KEEPS proxmox-notify's TWO RULES THAT MATTER HERE (api/webhook.rs, read at HEAD 2026-09-22):
9
+ * 1. a read returns secret NAMES only; values live in a separate private store;
10
+ * 2. a PUT that carries `secret` REPLACES the list, a name without a value keeping the old
11
+ * one; a PUT without `secret` leaves the list alone. `delete` entries clear a field.
12
+ * Repeated form keys arrive as arrays, as the real urlencoded decoder builds them.
13
+ * ⛔ TEST-ONLY. No provider imports this file.
14
+ */
15
+ import { type FakePve, type PveCall, fakePve } from './fake-pve.ts';
16
+ import { keyAndValue } from './pbs-notification-target-wire.ts';
17
+
18
+ /** Keys the servers take as `type: array`. Everything else is a scalar. */
19
+ const LISTS = new Set([
20
+ 'header',
21
+ 'mailto',
22
+ 'mailto-user',
23
+ 'match-calendar',
24
+ 'match-field',
25
+ 'match-severity',
26
+ 'secret',
27
+ 'target',
28
+ ]);
29
+
30
+ export interface FakeNotify extends FakePve {
31
+ /** Public config by path, as a GET would see it apart from secrets. Mutable, for drift. */
32
+ readonly objects: Map<string, Record<string, unknown>>;
33
+ /** Secret items with values (`name=…,value=<base64>`), by path — the root-only file. */
34
+ readonly secrets: Map<string, string[]>;
35
+ }
36
+
37
+ const body = (call: PveCall) => {
38
+ const fields: Record<string, unknown> = {};
39
+ const deletes: string[] = [];
40
+ for (const [key, value] of call.pairs) {
41
+ if (key === 'delete') deletes.push(value);
42
+ else if (LISTS.has(key))
43
+ fields[key] = [...((fields[key] as string[] | undefined) ?? []), value];
44
+ // ⚠️ Scalars as the server's JSON reads them back: a flag as a boolean, a port as a number.
45
+ else if (key === 'disable' || key === 'invert-match') fields[key] = value === '1';
46
+ else if (key === 'port') fields[key] = Number(value);
47
+ else fields[key] = value;
48
+ }
49
+ return { deletes, fields };
50
+ };
51
+
52
+ const withSecretsBlanked = (
53
+ path: string,
54
+ object: Record<string, unknown>,
55
+ secrets: FakeNotify['secrets'],
56
+ ) => {
57
+ const held = secrets.get(path);
58
+ return held === undefined || held.length === 0
59
+ ? object
60
+ : { ...object, secret: held.map((item) => `name=${keyAndValue(item).name}`) };
61
+ };
62
+
63
+ export const fakeNotify = (
64
+ seed: Readonly<Record<string, Record<string, unknown>>> = {},
65
+ ): FakeNotify => {
66
+ const objects = new Map(Object.entries(seed).map(([path, object]) => [path, { ...object }]));
67
+ const secrets = new Map<string, string[]>();
68
+
69
+ const putSecrets = (path: string, items: readonly string[]) => {
70
+ const old = secrets.get(path) ?? [];
71
+ const next = items.map((item) => {
72
+ const { name, value } = keyAndValue(item);
73
+ if (value !== undefined) return item;
74
+ const kept = old.find((existing) => keyAndValue(existing).name === name);
75
+ if (kept === undefined) throw new Error(`secret '${name}' not known`);
76
+ return kept;
77
+ });
78
+ secrets.set(path, next);
79
+ };
80
+
81
+ const fake = fakePve((call) => {
82
+ const path = call.path.split('?')[0] ?? call.path;
83
+ const { deletes, fields } = body(call);
84
+ if (call.method === 'GET') {
85
+ const object = objects.get(path);
86
+ return object === undefined ? undefined : withSecretsBlanked(path, object, secrets);
87
+ }
88
+ if (call.method === 'DELETE') {
89
+ objects.delete(path);
90
+ secrets.delete(path);
91
+ return undefined;
92
+ }
93
+ const target = call.method === 'POST' ? `${path}/${String(fields['name'])}` : path;
94
+ const { secret, ...rest } = fields as { secret?: string[] } & Record<string, unknown>;
95
+ const next: Record<string, unknown> = {
96
+ ...(call.method === 'PUT' ? objects.get(target) : {}),
97
+ ...rest,
98
+ };
99
+ for (const key of deletes) {
100
+ if (key === 'secret') secrets.delete(target);
101
+ else delete next[key];
102
+ }
103
+ objects.set(target, next);
104
+ if (secret !== undefined) putSecrets(target, secret);
105
+ return undefined;
106
+ });
107
+ return { ...fake, objects, secrets };
108
+ };
@@ -28,6 +28,11 @@ export interface PveCall {
28
28
  /** Relative to `/api2/json/`, query included — the string a family passes to `pve()`. */
29
29
  readonly path: string;
30
30
  readonly form: Readonly<Record<string, string>>;
31
+ /**
32
+ * Every key/value pair in wire order. ⚠️ `form` keeps only the LAST value of a repeated key, and
33
+ * a PBS list IS a repeated key (client.ts `encode`) — assert a list on `pairs`, never `form`.
34
+ */
35
+ readonly pairs: readonly (readonly [string, string])[];
31
36
  }
32
37
 
33
38
  /** Answers one call with its `data`; `undefined` is PVE's `{"data": null}`. */
@@ -56,8 +61,13 @@ export const fakePve = (answer: PveAnswer): FakePve => {
56
61
  return Response.json({ data, lease_duration: 0 });
57
62
  }
58
63
  const path = `${url.pathname.replace(/^\/api2\/json\//, '')}${url.search}`;
59
- const form = Object.fromEntries(new URLSearchParams(await request.text()));
60
- const call = { form, method: request.method, path };
64
+ const params = new URLSearchParams(await request.text());
65
+ const call = {
66
+ form: Object.fromEntries(params),
67
+ method: request.method,
68
+ pairs: [...params],
69
+ path,
70
+ };
61
71
  calls.push(call);
62
72
  return Response.json({ data: answer(call) ?? null });
63
73
  };
@@ -36,7 +36,7 @@ export const fold = (name: string) => name.trim().toLowerCase();
36
36
  /**
37
37
  * A CIDR reduced to the one form PVE will hand back.
38
38
  *
39
- * ⛔ MEASURED by running the cluster's own `PVE::Firewall::parse_alias` on n2 (pve-manager 9.2.11,
39
+ * ⛔ MEASURED by running the cluster's own `PVE::Firewall::parse_alias` on node-b (pve-manager 9.2.11,
40
40
  * 2026-09-13) — a pure function over a string, no config read and none written:
41
41
  * `10.1.1.5/32` -> `10.1.1.5` a /32 host suffix is STRIPPED
42
42
  * `2001:0DB8::1/128`-> `2001:0DB8::1` a /128 is stripped, and the case is NOT folded
@@ -61,7 +61,7 @@
61
61
  * ⚠️ PRIVILEGES, AND FOR ONCE NOTHING NEEDS WIDENING. MEASURED 2026-09-13 from the cluster's own
62
62
  * schema and its live ACL: the item GET checks `["perm","/",["Sys.Audit"]]`, POST/PUT/DELETE
63
63
  * check `["perm","/",["Sys.Modify"]]`, `hf-read@pve` holds `PVEAuditor` at `/` with propagate and
64
- * `hf-provision@pve` holds `LXCProvisioner` at `/` with propagate. Both privileges are already
64
+ * `hf-provision@pve` holds the provision role (provision-baseline.ts) at `/` with propagate. Both privileges are already
65
65
  * held, so `readRole` stays the default `read` lane — unlike storage/sdn-zone/sdn-vnet, whose
66
66
  * ITEM reads are gated on an allocate privilege the auditor cannot have.
67
67
  * ⚠️ `Sys.Modify` ON `/` IS STILL A BIG HAMMER — it is the same grant `metric-server.ts` warns
@@ -2,7 +2,7 @@
2
2
  * Generated PBS API types — DO NOT EDIT BY HAND.
3
3
  *
4
4
  * Run: bun codegen/generate.ts
5
- * Source: house/proxmox/apidoc/ — see README there for provenance.
5
+ * Source: the cluster's own `apidoc.js` (pve-docs), extracted outside this repo.
6
6
  */
7
7
 
8
8
  /** GET /config/datastore — `data` payload after client unwrap. */
@@ -141,6 +141,335 @@ export type ConfigDatastoreNameDeleteParams = {
141
141
  /** DELETE /config/datastore/{name} — `data` payload after client unwrap. */
142
142
  export type ConfigDatastoreNameDeleteReturn = string;
143
143
 
144
+ /** GET /config/notifications/endpoints — `data` payload after client unwrap. */
145
+ export type ConfigNotificationsEndpointsGetReturn = null;
146
+
147
+ /** GET /config/notifications/endpoints/gotify — `data` payload after client unwrap. */
148
+ export type ConfigNotificationsEndpointsGotifyGetReturn = readonly {
149
+ comment?: string;
150
+ disable?: boolean | 0 | 1;
151
+ filter?: string;
152
+ name: string;
153
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
154
+ server: string;
155
+ }[];
156
+
157
+ /** POST /config/notifications/endpoints/gotify — form/query parameters (path segments omitted). */
158
+ export type ConfigNotificationsEndpointsGotifyPostParams = {
159
+ comment?: string;
160
+ disable?: '0' | '1';
161
+ filter?: string;
162
+ name: string;
163
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
164
+ server: string;
165
+ token: string;
166
+ };
167
+ /** POST /config/notifications/endpoints/gotify — `data` payload after client unwrap. */
168
+ export type ConfigNotificationsEndpointsGotifyPostReturn = null;
169
+
170
+ /** GET /config/notifications/endpoints/gotify/{name} — `data` payload after client unwrap. */
171
+ export type ConfigNotificationsEndpointsGotifyNameGetReturn = {
172
+ comment?: string;
173
+ disable?: boolean | 0 | 1;
174
+ filter?: string;
175
+ name: string;
176
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
177
+ server: string;
178
+ };
179
+
180
+ /** PUT /config/notifications/endpoints/gotify/{name} — form/query parameters (path segments omitted). */
181
+ export type ConfigNotificationsEndpointsGotifyNamePutParams = {
182
+ comment?: string;
183
+ delete?: readonly ('comment' | 'disable')[];
184
+ digest?: string;
185
+ disable?: '0' | '1';
186
+ server?: string;
187
+ token?: string;
188
+ };
189
+ /** PUT /config/notifications/endpoints/gotify/{name} — `data` payload after client unwrap. */
190
+ export type ConfigNotificationsEndpointsGotifyNamePutReturn = null;
191
+
192
+ /** DELETE /config/notifications/endpoints/gotify/{name} — `data` payload after client unwrap. */
193
+ export type ConfigNotificationsEndpointsGotifyNameDeleteReturn = null;
194
+
195
+ /** GET /config/notifications/endpoints/sendmail — `data` payload after client unwrap. */
196
+ export type ConfigNotificationsEndpointsSendmailGetReturn = readonly {
197
+ author?: string;
198
+ comment?: string;
199
+ disable?: boolean | 0 | 1;
200
+ filter?: string;
201
+ 'from-address'?: string;
202
+ mailto?: readonly string[];
203
+ 'mailto-user'?: readonly string[];
204
+ name: string;
205
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
206
+ }[];
207
+
208
+ /** POST /config/notifications/endpoints/sendmail — form/query parameters (path segments omitted). */
209
+ export type ConfigNotificationsEndpointsSendmailPostParams = {
210
+ author?: string;
211
+ comment?: string;
212
+ disable?: '0' | '1';
213
+ filter?: string;
214
+ 'from-address'?: string;
215
+ mailto?: readonly string[];
216
+ 'mailto-user'?: readonly string[];
217
+ name: string;
218
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
219
+ };
220
+ /** POST /config/notifications/endpoints/sendmail — `data` payload after client unwrap. */
221
+ export type ConfigNotificationsEndpointsSendmailPostReturn = null;
222
+
223
+ /** GET /config/notifications/endpoints/sendmail/{name} — `data` payload after client unwrap. */
224
+ export type ConfigNotificationsEndpointsSendmailNameGetReturn = {
225
+ author?: string;
226
+ comment?: string;
227
+ disable?: boolean | 0 | 1;
228
+ filter?: string;
229
+ 'from-address'?: string;
230
+ mailto?: readonly string[];
231
+ 'mailto-user'?: readonly string[];
232
+ name: string;
233
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
234
+ };
235
+
236
+ /** PUT /config/notifications/endpoints/sendmail/{name} — form/query parameters (path segments omitted). */
237
+ export type ConfigNotificationsEndpointsSendmailNamePutParams = {
238
+ author?: string;
239
+ comment?: string;
240
+ delete?: readonly (
241
+ | 'author'
242
+ | 'comment'
243
+ | 'disable'
244
+ | 'from-address'
245
+ | 'mailto'
246
+ | 'mailto-user'
247
+ )[];
248
+ digest?: string;
249
+ disable?: '0' | '1';
250
+ 'from-address'?: string;
251
+ mailto?: readonly string[];
252
+ 'mailto-user'?: readonly string[];
253
+ };
254
+ /** PUT /config/notifications/endpoints/sendmail/{name} — `data` payload after client unwrap. */
255
+ export type ConfigNotificationsEndpointsSendmailNamePutReturn = null;
256
+
257
+ /** DELETE /config/notifications/endpoints/sendmail/{name} — `data` payload after client unwrap. */
258
+ export type ConfigNotificationsEndpointsSendmailNameDeleteReturn = null;
259
+
260
+ /** GET /config/notifications/endpoints/smtp — `data` payload after client unwrap. */
261
+ export type ConfigNotificationsEndpointsSmtpGetReturn = readonly {
262
+ author?: string;
263
+ comment?: string;
264
+ disable?: boolean | 0 | 1;
265
+ 'from-address': string;
266
+ mailto?: readonly string[];
267
+ 'mailto-user'?: readonly string[];
268
+ mode?: 'insecure' | 'starttls' | 'tls';
269
+ name: string;
270
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
271
+ port?: number;
272
+ server: string;
273
+ username?: string;
274
+ }[];
275
+
276
+ /** POST /config/notifications/endpoints/smtp — form/query parameters (path segments omitted). */
277
+ export type ConfigNotificationsEndpointsSmtpPostParams = {
278
+ author?: string;
279
+ comment?: string;
280
+ disable?: '0' | '1';
281
+ 'from-address': string;
282
+ mailto?: readonly string[];
283
+ 'mailto-user'?: readonly string[];
284
+ mode?: 'insecure' | 'starttls' | 'tls';
285
+ name: string;
286
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
287
+ password?: string;
288
+ port?: string;
289
+ server: string;
290
+ username?: string;
291
+ };
292
+ /** POST /config/notifications/endpoints/smtp — `data` payload after client unwrap. */
293
+ export type ConfigNotificationsEndpointsSmtpPostReturn = null;
294
+
295
+ /** GET /config/notifications/endpoints/smtp/{name} — `data` payload after client unwrap. */
296
+ export type ConfigNotificationsEndpointsSmtpNameGetReturn = {
297
+ author?: string;
298
+ comment?: string;
299
+ disable?: boolean | 0 | 1;
300
+ 'from-address': string;
301
+ mailto?: readonly string[];
302
+ 'mailto-user'?: readonly string[];
303
+ mode?: 'insecure' | 'starttls' | 'tls';
304
+ name: string;
305
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
306
+ port?: number;
307
+ server: string;
308
+ username?: string;
309
+ };
310
+
311
+ /** PUT /config/notifications/endpoints/smtp/{name} — form/query parameters (path segments omitted). */
312
+ export type ConfigNotificationsEndpointsSmtpNamePutParams = {
313
+ author?: string;
314
+ comment?: string;
315
+ delete?: readonly (
316
+ | 'author'
317
+ | 'comment'
318
+ | 'disable'
319
+ | 'mailto'
320
+ | 'mailto-user'
321
+ | 'password'
322
+ | 'port'
323
+ | 'username'
324
+ )[];
325
+ digest?: string;
326
+ disable?: '0' | '1';
327
+ 'from-address'?: string;
328
+ mailto?: readonly string[];
329
+ 'mailto-user'?: readonly string[];
330
+ mode?: 'insecure' | 'starttls' | 'tls';
331
+ password?: string;
332
+ port?: string;
333
+ server?: string;
334
+ username?: string;
335
+ };
336
+ /** PUT /config/notifications/endpoints/smtp/{name} — `data` payload after client unwrap. */
337
+ export type ConfigNotificationsEndpointsSmtpNamePutReturn = null;
338
+
339
+ /** DELETE /config/notifications/endpoints/smtp/{name} — `data` payload after client unwrap. */
340
+ export type ConfigNotificationsEndpointsSmtpNameDeleteReturn = null;
341
+
342
+ /** GET /config/notifications/endpoints/webhook — `data` payload after client unwrap. */
343
+ export type ConfigNotificationsEndpointsWebhookGetReturn = readonly {
344
+ body?: string;
345
+ comment?: string;
346
+ disable?: boolean | 0 | 1;
347
+ header?: readonly string[];
348
+ method: 'post' | 'put' | 'get';
349
+ name: string;
350
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
351
+ secret?: readonly string[];
352
+ url: string;
353
+ }[];
354
+
355
+ /** POST /config/notifications/endpoints/webhook — form/query parameters (path segments omitted). */
356
+ export type ConfigNotificationsEndpointsWebhookPostParams = {
357
+ body?: string;
358
+ comment?: string;
359
+ disable?: '0' | '1';
360
+ header?: readonly string[];
361
+ method: 'post' | 'put' | 'get';
362
+ name: string;
363
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
364
+ secret?: readonly string[];
365
+ url: string;
366
+ };
367
+ /** POST /config/notifications/endpoints/webhook — `data` payload after client unwrap. */
368
+ export type ConfigNotificationsEndpointsWebhookPostReturn = null;
369
+
370
+ /** GET /config/notifications/endpoints/webhook/{name} — `data` payload after client unwrap. */
371
+ export type ConfigNotificationsEndpointsWebhookNameGetReturn = {
372
+ body?: string;
373
+ comment?: string;
374
+ disable?: boolean | 0 | 1;
375
+ header?: readonly string[];
376
+ method: 'post' | 'put' | 'get';
377
+ name: string;
378
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
379
+ secret?: readonly string[];
380
+ url: string;
381
+ };
382
+
383
+ /** PUT /config/notifications/endpoints/webhook/{name} — form/query parameters (path segments omitted). */
384
+ export type ConfigNotificationsEndpointsWebhookNamePutParams = {
385
+ body?: string;
386
+ comment?: string;
387
+ delete?: readonly ('comment' | 'disable' | 'header' | 'body' | 'secret')[];
388
+ digest?: string;
389
+ disable?: '0' | '1';
390
+ header?: readonly string[];
391
+ method?: 'post' | 'put' | 'get';
392
+ secret?: readonly string[];
393
+ url?: string;
394
+ };
395
+ /** PUT /config/notifications/endpoints/webhook/{name} — `data` payload after client unwrap. */
396
+ export type ConfigNotificationsEndpointsWebhookNamePutReturn = null;
397
+
398
+ /** DELETE /config/notifications/endpoints/webhook/{name} — `data` payload after client unwrap. */
399
+ export type ConfigNotificationsEndpointsWebhookNameDeleteReturn = null;
400
+
401
+ /** GET /config/notifications/matchers — `data` payload after client unwrap. */
402
+ export type ConfigNotificationsMatchersGetReturn = readonly {
403
+ comment?: string;
404
+ disable?: boolean | 0 | 1;
405
+ 'invert-match'?: boolean | 0 | 1;
406
+ 'match-calendar'?: readonly string[];
407
+ 'match-field'?: readonly string[];
408
+ 'match-severity'?: readonly string[];
409
+ mode?: 'all' | 'any';
410
+ name: string;
411
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
412
+ target?: readonly string[];
413
+ }[];
414
+
415
+ /** POST /config/notifications/matchers — form/query parameters (path segments omitted). */
416
+ export type ConfigNotificationsMatchersPostParams = {
417
+ comment?: string;
418
+ disable?: '0' | '1';
419
+ 'invert-match'?: '0' | '1';
420
+ 'match-calendar'?: readonly string[];
421
+ 'match-field'?: readonly string[];
422
+ 'match-severity'?: readonly string[];
423
+ mode?: 'all' | 'any';
424
+ name: string;
425
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
426
+ target?: readonly string[];
427
+ };
428
+ /** POST /config/notifications/matchers — `data` payload after client unwrap. */
429
+ export type ConfigNotificationsMatchersPostReturn = null;
430
+
431
+ /** GET /config/notifications/matchers/{name} — `data` payload after client unwrap. */
432
+ export type ConfigNotificationsMatchersNameGetReturn = {
433
+ comment?: string;
434
+ disable?: boolean | 0 | 1;
435
+ 'invert-match'?: boolean | 0 | 1;
436
+ 'match-calendar'?: readonly string[];
437
+ 'match-field'?: readonly string[];
438
+ 'match-severity'?: readonly string[];
439
+ mode?: 'all' | 'any';
440
+ name: string;
441
+ origin?: 'user-created' | 'builtin' | 'modified-builtin';
442
+ target?: readonly string[];
443
+ };
444
+
445
+ /** PUT /config/notifications/matchers/{name} — form/query parameters (path segments omitted). */
446
+ export type ConfigNotificationsMatchersNamePutParams = {
447
+ comment?: string;
448
+ delete?: readonly (
449
+ | 'comment'
450
+ | 'disable'
451
+ | 'invert-match'
452
+ | 'match-calendar'
453
+ | 'match-field'
454
+ | 'match-severity'
455
+ | 'mode'
456
+ | 'target'
457
+ )[];
458
+ digest?: string;
459
+ disable?: '0' | '1';
460
+ 'invert-match'?: '0' | '1';
461
+ 'match-calendar'?: readonly string[];
462
+ 'match-field'?: readonly string[];
463
+ 'match-severity'?: readonly string[];
464
+ mode?: 'all' | 'any';
465
+ target?: readonly string[];
466
+ };
467
+ /** PUT /config/notifications/matchers/{name} — `data` payload after client unwrap. */
468
+ export type ConfigNotificationsMatchersNamePutReturn = null;
469
+
470
+ /** DELETE /config/notifications/matchers/{name} — `data` payload after client unwrap. */
471
+ export type ConfigNotificationsMatchersNameDeleteReturn = null;
472
+
144
473
  /** GET /config/prune — `data` payload after client unwrap. */
145
474
  export type ConfigPruneGetReturn = readonly {
146
475
  comment?: string;