@homeflare/alchemy 0.37.1 → 0.37.3
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/dist/proxmox/backup-job-config.d.ts +95 -0
- package/dist/proxmox/backup-job-config.d.ts.map +1 -0
- package/dist/proxmox/backup-job-distilled.d.ts +46 -0
- package/dist/proxmox/backup-job-distilled.d.ts.map +1 -0
- package/dist/proxmox/backup-job.d.ts +9 -26
- package/dist/proxmox/backup-job.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-distilled.d.ts +17 -10
- package/dist/proxmox/ceph-fs-distilled.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs-wire.d.ts +5 -22
- package/dist/proxmox/ceph-fs-wire.d.ts.map +1 -1
- package/dist/proxmox/ceph-fs.d.ts +3 -3
- package/dist/proxmox/distilled-pbs.d.ts +12 -0
- package/dist/proxmox/distilled-pbs.d.ts.map +1 -0
- package/dist/proxmox/fake-pbs-jobs.d.ts +23 -0
- package/dist/proxmox/fake-pbs-jobs.d.ts.map +1 -0
- package/dist/proxmox/fake-pbs-notify.d.ts.map +1 -1
- package/dist/proxmox/fake-pve.d.ts +1 -1
- package/dist/proxmox/fake-pve.d.ts.map +1 -1
- package/dist/proxmox/group-wire.d.ts +2 -2
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/index.js +1382 -506
- package/dist/proxmox/index.js.map +51 -27
- package/dist/proxmox/live-core-fixtures.d.ts +40 -0
- package/dist/proxmox/live-core-fixtures.d.ts.map +1 -0
- package/dist/proxmox/metric-server-config.d.ts +49 -0
- package/dist/proxmox/metric-server-config.d.ts.map +1 -0
- package/dist/proxmox/metric-server-distilled.d.ts +43 -0
- package/dist/proxmox/metric-server-distilled.d.ts.map +1 -0
- package/dist/proxmox/metric-server.d.ts +2 -10
- package/dist/proxmox/metric-server.d.ts.map +1 -1
- package/dist/proxmox/node-network-wire.d.ts +1 -1
- package/dist/proxmox/node-network-wire.d.ts.map +1 -1
- package/dist/proxmox/notification-matcher-distilled.d.ts +9 -0
- package/dist/proxmox/notification-matcher-distilled.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher-lifecycle.d.ts +27 -0
- package/dist/proxmox/notification-matcher-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/notification-matcher.d.ts +5 -5
- package/dist/proxmox/notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/notification-sdk-input.d.ts +22 -0
- package/dist/proxmox/notification-sdk-input.d.ts.map +1 -0
- package/dist/proxmox/notification-target-distilled.d.ts +12 -0
- package/dist/proxmox/notification-target-distilled.d.ts.map +1 -0
- package/dist/proxmox/notification-target-form.d.ts +2 -0
- package/dist/proxmox/notification-target-form.d.ts.map +1 -1
- package/dist/proxmox/notification-target-lifecycle.d.ts +29 -0
- package/dist/proxmox/notification-target-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/notification-target-spec.d.ts +5 -0
- package/dist/proxmox/notification-target-spec.d.ts.map +1 -0
- package/dist/proxmox/notification-target.d.ts +4 -5
- package/dist/proxmox/notification-target.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-endpoint.d.ts +1 -1
- package/dist/proxmox/pbs-datastore-form.d.ts +18 -42
- package/dist/proxmox/pbs-datastore-form.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-guard.d.ts +7 -4
- package/dist/proxmox/pbs-datastore-guard.d.ts.map +1 -1
- package/dist/proxmox/pbs-datastore-lifecycle.d.ts +56 -0
- package/dist/proxmox/pbs-datastore-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore-wire.d.ts +21 -0
- package/dist/proxmox/pbs-datastore-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-datastore.d.ts +5 -6
- package/dist/proxmox/pbs-datastore.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-input.d.ts +18 -0
- package/dist/proxmox/pbs-notification-input.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts +11 -0
- package/dist/proxmox/pbs-notification-matcher-distilled.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-matcher.d.ts +4 -3
- package/dist/proxmox/pbs-notification-matcher.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target-distilled.d.ts +20 -0
- package/dist/proxmox/pbs-notification-target-distilled.d.ts.map +1 -0
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts +4 -4
- package/dist/proxmox/pbs-notification-target-lifecycle.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target.d.ts +1 -1
- package/dist/proxmox/pbs-prune-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-prune-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-spec.d.ts +5 -0
- package/dist/proxmox/pbs-prune-job-spec.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-prune-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-prune-job.d.ts +9 -8
- package/dist/proxmox/pbs-prune-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job-attributes.d.ts +34 -0
- package/dist/proxmox/pbs-sync-job-attributes.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-form.d.ts +21 -22
- package/dist/proxmox/pbs-sync-job-form.d.ts.map +1 -1
- package/dist/proxmox/pbs-sync-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-sync-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-sync-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-sync-job.d.ts +9 -39
- package/dist/proxmox/pbs-sync-job.d.ts.map +1 -1
- package/dist/proxmox/pbs-verify-job-lifecycle.d.ts +22 -0
- package/dist/proxmox/pbs-verify-job-lifecycle.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-spec.d.ts +16 -0
- package/dist/proxmox/pbs-verify-job-spec.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job-wire.d.ts +11 -0
- package/dist/proxmox/pbs-verify-job-wire.d.ts.map +1 -0
- package/dist/proxmox/pbs-verify-job.d.ts +12 -12
- package/dist/proxmox/pbs-verify-job.d.ts.map +1 -1
- package/dist/proxmox/pool.d.ts +10 -1
- package/dist/proxmox/pool.d.ts.map +1 -1
- package/dist/proxmox/storage-wire.d.ts +2 -2
- package/dist/proxmox/storage-wire.d.ts.map +1 -1
- package/dist/proxmox/user-wire.d.ts +2 -2
- package/dist/proxmox/user-wire.d.ts.map +1 -1
- package/dist/proxmox/zfs-pool-wire.d.ts +1 -1
- package/dist/proxmox/zfs-pool-wire.d.ts.map +1 -1
- package/docs/changelog/README.md +22 -0
- package/docs/changelog/archive-01.md +176 -0
- package/docs/changelog/archive-02.md +180 -0
- package/docs/changelog/archive-03.md +179 -0
- package/docs/changelog/archive-04.md +178 -0
- package/docs/changelog/archive-05.md +177 -0
- package/docs/changelog/archive-06.md +180 -0
- package/docs/changelog/archive-07.md +180 -0
- package/docs/changelog/archive-08.md +178 -0
- package/docs/changelog/archive-09.md +181 -0
- package/docs/changelog/archive-10.md +182 -0
- package/docs/changelog/archive-11.md +182 -0
- package/docs/changelog/archive-12.md +183 -0
- package/docs/changelog/archive-13.md +180 -0
- package/docs/changelog/archive-14.md +178 -0
- package/docs/changelog/archive-15.md +181 -0
- package/docs/changelog/archive-16.md +182 -0
- package/docs/changelog/archive-17.md +183 -0
- package/docs/changelog/archive-18.md +55 -0
- package/docs/pbs-distilled-jobs.md +31 -0
- package/docs/pbs-notifications.md +8 -2
- package/docs/proxmox-notification-sdk.md +28 -0
- package/docs/proxmox-sdk-migration.md +60 -0
- package/docs/proxmox.md +9 -3
- package/docs/upstream-conformance-families.md +6 -2
- package/package.json +3 -3
- package/src/proxmox/adopt-noop.test.ts +4 -5
- package/src/proxmox/backup-job-config.ts +95 -0
- package/src/proxmox/backup-job-distilled.ts +86 -0
- package/src/proxmox/backup-job.ts +78 -116
- package/src/proxmox/ceph-fs-delete.test.ts +5 -8
- package/src/proxmox/ceph-fs-destroy-sdk.test.ts +98 -0
- package/src/proxmox/ceph-fs-distilled.ts +41 -13
- package/src/proxmox/ceph-fs-wire.ts +5 -93
- package/src/proxmox/ceph-fs.ts +7 -7
- package/src/proxmox/constraints.test.ts +9 -8
- package/src/proxmox/distilled-pbs.test.ts +87 -0
- package/src/proxmox/distilled-pbs.ts +50 -0
- package/src/proxmox/fake-pbs-jobs.ts +99 -0
- package/src/proxmox/fake-pbs-notify.ts +4 -1
- package/src/proxmox/fake-pve.ts +3 -2
- package/src/proxmox/live-core-fixtures.ts +126 -0
- package/src/proxmox/live-core-lifecycle.test.ts +115 -0
- package/src/proxmox/live-core-wire.test.ts +190 -0
- package/src/proxmox/metric-server-config.ts +87 -0
- package/src/proxmox/metric-server-distilled.ts +81 -0
- package/src/proxmox/metric-server.ts +77 -88
- package/src/proxmox/notification-distilled.test.ts +162 -0
- package/src/proxmox/notification-matcher-distilled.ts +60 -0
- package/src/proxmox/notification-matcher-lifecycle.ts +71 -0
- package/src/proxmox/notification-matcher.ts +9 -26
- package/src/proxmox/notification-replacement.test.ts +190 -0
- package/src/proxmox/notification-sdk-input.ts +39 -0
- package/src/proxmox/notification-target-distilled.ts +160 -0
- package/src/proxmox/notification-target-form.ts +2 -0
- package/src/proxmox/notification-target-lifecycle.ts +75 -0
- package/src/proxmox/notification-target-spec.ts +68 -0
- package/src/proxmox/notification-target.ts +6 -68
- package/src/proxmox/pbs-datastore-endpoint.ts +1 -1
- package/src/proxmox/pbs-datastore-form.ts +18 -49
- package/src/proxmox/pbs-datastore-guard.ts +7 -4
- package/src/proxmox/pbs-datastore-lifecycle.ts +114 -0
- package/src/proxmox/pbs-datastore-wire.ts +94 -0
- package/src/proxmox/pbs-datastore.ts +7 -133
- package/src/proxmox/pbs-jobs-delete.test.ts +40 -0
- package/src/proxmox/pbs-jobs-sdk.test.ts +215 -0
- package/src/proxmox/pbs-notification-errors.test.ts +93 -0
- package/src/proxmox/pbs-notification-input.ts +25 -0
- package/src/proxmox/pbs-notification-mail.test.ts +64 -0
- package/src/proxmox/pbs-notification-matcher-distilled.ts +65 -0
- package/src/proxmox/pbs-notification-matcher.ts +70 -17
- package/src/proxmox/pbs-notification-target-distilled.ts +124 -0
- package/src/proxmox/pbs-notification-target-lifecycle.ts +26 -17
- package/src/proxmox/pbs-notification-target.ts +1 -1
- package/src/proxmox/pbs-notifications-stack.test.ts +25 -0
- package/src/proxmox/pbs-prune-job-lifecycle.ts +52 -0
- package/src/proxmox/pbs-prune-job-spec.ts +53 -0
- package/src/proxmox/pbs-prune-job-wire.ts +68 -0
- package/src/proxmox/pbs-prune-job.ts +10 -58
- package/src/proxmox/pbs-sync-job-attributes.ts +33 -0
- package/src/proxmox/pbs-sync-job-form.ts +7 -6
- package/src/proxmox/pbs-sync-job-lifecycle.ts +46 -0
- package/src/proxmox/pbs-sync-job-wire.ts +71 -0
- package/src/proxmox/pbs-sync-job.ts +10 -41
- package/src/proxmox/pbs-verify-job-lifecycle.ts +52 -0
- package/src/proxmox/pbs-verify-job-spec.ts +76 -0
- package/src/proxmox/pbs-verify-job-wire.ts +58 -0
- package/src/proxmox/pbs-verify-job.ts +13 -85
- package/src/proxmox/pool.ts +92 -10
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Alchemy changelog archive 17
|
|
2
|
+
|
|
3
|
+
[Current changelog](../../CHANGELOG.md) · [Archive index](./README.md)
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#67](https://github.com/taslabs-net/homeflare-kit/pull/67) [`48163cf`](https://github.com/taslabs-net/homeflare-kit/commit/48163cfc83ad7deac8720be2e8fb7fe964ade7a3) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add to `@homeflare/alchemy/openbao`:
|
|
8
|
+
|
|
9
|
+
- **`BaoJwtRole`**: roles on `jwt` and `oidc` mounts.
|
|
10
|
+
- **`BaoKubernetesRole`**: roles on `kubernetes` mounts. `aliasNameSource` is required, because changing it on a live role moves every pod to a new entity.
|
|
11
|
+
- **`BaoJwtAuthConfig`**: the non-secret config of a JWT-validating mount. It deliberately has no OIDC client secret, and it refuses a mount that has one, because its full-replace write would erase it.
|
|
12
|
+
- **`BaoMfaTotpMethod`** and **`BaoMfaLoginEnforcement`**: login MFA. A TOTP method is found by name. Renaming one is refused, because a rename would strand every enrolled secret. A name already held by another MFA method type is refused too, because the write would convert that method. Deleting an enforcement is refused, because in OpenBao 2.6.2 the delete comes back after a restart (openbao/openbao#4030).
|
|
13
|
+
- **`assertBaoIdentity`** (with `assertBaoIdentityEffect` and `BaoIdentityError`): refuses to proceed unless unauthenticated `sys/health` reports the expected `cluster_name` and the namespace is the expected one.
|
|
14
|
+
- **`hostAppRoles`**: a pure generator that makes one AppRole per host, named `<class>--<host>`. It refuses a secret_id TTL of 0.
|
|
15
|
+
|
|
16
|
+
Behaviour changes:
|
|
17
|
+
|
|
18
|
+
- A changed `path` on `BaoMount` or `BaoAuthMethod` now **fails the plan** unless the new `remountFrom` prop names the old path. With `remountFrom`, the mount is moved with `sys/remount`, keeping its data (leases under it are revoked). Before, the plan answered `update` and enabled an empty mount at the new path.
|
|
19
|
+
- A changed `name` on `BaoAuthRole`, or a changed `name` on `BaoPkiRole`, now plans `replace`. Before, it answered `update` and left the old role live with no state record.
|
|
20
|
+
- `BaoPkiRole` now manages `requireCn`, `enforceHostnames`, `keyUsage`, `allowedDomainsTemplate`, `noStore` and `generateLease`, defaulting to OpenBao's own values. Its full-replace write already reset these fields silently, so a role whose live values differ from those defaults now plans `update` instead of being reset unseen. `noStore` together with `generateLease` is refused.
|
|
21
|
+
|
|
22
|
+
See `src/openbao/REPLACE.md` for what every family does on a rename.
|
|
23
|
+
|
|
24
|
+
- [#68](https://github.com/taslabs-net/homeflare-kit/pull/68) [`b023bae`](https://github.com/taslabs-net/homeflare-kit/commit/b023bae8f20d6aa33239107dc0538750efc466ca) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add the `@homeflare/alchemy/launchd` subpath, so a Mac host can be declared with Alchemy.
|
|
25
|
+
|
|
26
|
+
- `LaunchdJob` / `LaunchdJobProvider` (`Launchd.Job`): one launchd job in the `system` domain or a `gui/<uid>` domain. The provider renders the plist itself, writes it atomically to the derived path (`/Library/LaunchDaemons` or the user's `LaunchAgents`) and drives `launchctl`. Create bootstraps; update writes, boots out and bootstraps (a restart); read uses `launchctl print`; diff compares the rendered plist's SHA-256 with the stored and on-disk digests. Replace happens only on a `label` or `domain` change, delete-first — so everything the new job would be refused for is refused at plan time, while the old job still runs, and a rename is seen even while other props are unresolved. A label another job already holds is never booted out or overwritten. A failed first bootstrap removes the plist it wrote. A disabled label is refused, not re-enabled. Labels under `org.nixos.`, `com.apple.` and `homebrew.mxcl.` are refused; `docs/launchd.md` describes the nix-darwin cutover.
|
|
27
|
+
- `HostFile` / `HostFileProvider` (`Host.File`): a text file with mode, owner and group. It is written atomically (temp file, then rename), diffed by SHA-256, mode and owner, and refused over a symlink, a directory, or a different file at a new path.
|
|
28
|
+
- `HostRunner`, `localRunner()`, `hostRunnerLayer()` and `launchdProviders()`: every filesystem and `launchctl` call goes through one injectable runner. The local runner never elevates. System-domain writes are refused unless the deploy runs as root or the runner is explicitly `privileged`.
|
|
29
|
+
- `renderPlist`: a small, deterministic XML plist serializer, round-tripped through `plutil` in the tests.
|
|
30
|
+
|
|
31
|
+
Props are stored unencrypted in Alchemy state, so `environment`, `programArguments` and file `content` must not hold secrets. A tripwire refuses the obvious cases; secret files stay rendered by openbao-agent, and the stack declares only their path. Anything already on the host is read as `Unowned`, so it is never adopted without `--adopt`.
|
|
32
|
+
|
|
33
|
+
## 0.5.0
|
|
34
|
+
|
|
35
|
+
### Minor Changes
|
|
36
|
+
|
|
37
|
+
- [#64](https://github.com/taslabs-net/homeflare-kit/pull/64) [`3ed9771`](https://github.com/taslabs-net/homeflare-kit/commit/3ed977142ffe2fe6dd02359a3a87b1a0a84c9952) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add `appRoleLogin` / `revokeSelf` (with `appRoleLoginEffect`, `revokeSelfEffect` and `BaoLoginError`) to `@homeflare/alchemy/openbao`, so wrapper scripts can log in with an AppRole and revoke on exit. They use the same address resolution and transport as the `Bao.*` families. The login never sends `BAO_TOKEN`, and error strings are redacted, because OpenBao 2.6.2 can echo a secret_id back in an error. `clientToken` is a getter over a private field, so printing or serialising the result does not show the token under Bun or Node.
|
|
38
|
+
|
|
39
|
+
Add `BaoPlugin` / `BaoPluginProvider` for the plugin catalog (`sys/plugins/catalog/<type>/<name>`). It has no `env` prop and retains on destroy. It refuses to shadow a builtin or overwrite a declarative entry, and it names the fix when an unversioned registration is filed under the binary's self-reported version.
|
|
40
|
+
|
|
41
|
+
Fix `BaoSshRole` writes. `default_extensions` and `default_critical_options` were sent as JSON strings. OpenBao's field validation rejects that with a 400, so every role write failed. They are now sent as objects.
|
|
42
|
+
|
|
43
|
+
## 0.4.1
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- [#62](https://github.com/taslabs-net/homeflare-kit/pull/62) [`93b7107`](https://github.com/taslabs-net/homeflare-kit/commit/93b71070b1a64701cadc1a547044a3ec4da26a50) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Export TB4 control-plane Resource constructors from `@homeflare/alchemy/proxmox` (Storage, SDN, access, HA, backup, metrics, PBS). Guests and NIC apply stay Provider-only.
|
|
48
|
+
|
|
49
|
+
## 0.4.0
|
|
50
|
+
|
|
51
|
+
### Minor Changes
|
|
52
|
+
|
|
53
|
+
- [#60](https://github.com/taslabs-net/homeflare-kit/pull/60) [`df34d27`](https://github.com/taslabs-net/homeflare-kit/commit/df34d2750fca4a2b6b86490ddb0a819c12f9ea39) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add `BaoAuthMethod` so a stack can enable `approle` (or jwt/oidc) instead of running `configure-engines`. Metadata only — no role ids or OIDC secrets. File audit stays out: OpenBao 2.6 file audit is config-only.
|
|
54
|
+
|
|
55
|
+
## 0.3.2
|
|
56
|
+
|
|
57
|
+
### Patch Changes
|
|
58
|
+
|
|
59
|
+
- [#58](https://github.com/taslabs-net/homeflare-kit/pull/58) [`e7c101f`](https://github.com/taslabs-net/homeflare-kit/commit/e7c101faada2b14466a1f0bbf1f93da46e5e06cb) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Bump Alchemy to 2.0.0-beta.79. Effect stays rc.115. 79 starts Bun with production JSX (the `jsxDEV` CLI crash on 78) and declares `mime` on cloudflare-runtime; the `mime` peer stays so existing consumer installs do not drop a required line.
|
|
60
|
+
|
|
61
|
+
## 0.3.1
|
|
62
|
+
|
|
63
|
+
### Patch Changes
|
|
64
|
+
|
|
65
|
+
- [#56](https://github.com/taslabs-net/homeflare-kit/pull/56) [`5f41ad7`](https://github.com/taslabs-net/homeflare-kit/commit/5f41ad7ab59375e42bb4757dd5b4b81f4a7b6cd9) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Export `BaoPkiRole` and `BaoSshRole` from `@homeflare/alchemy/openbao`. The barrel already shipped both providers; stacks constructing either role had to import the resource from internals.
|
|
66
|
+
|
|
67
|
+
## 0.3.0
|
|
68
|
+
|
|
69
|
+
### Minor Changes
|
|
70
|
+
|
|
71
|
+
- [#54](https://github.com/taslabs-net/homeflare-kit/pull/54) [`d526365`](https://github.com/taslabs-net/homeflare-kit/commit/d526365a51deac968e5b0076e88b2e68b866534f) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Bump Alchemy to 2.0.0-beta.78 and Effect to 4.0.0-rc.115. Consumers must move the override set with it — Alchemy 78's peer is `effect >= rc.115`. Effect rc.115 renamed `Config.redacted` to `Config.Redacted`. `mime@4.1.0` is now a required peer: Alchemy's cloudflare-runtime imports it and does not declare it.
|
|
72
|
+
|
|
73
|
+
## 0.2.2
|
|
74
|
+
|
|
75
|
+
### Patch Changes
|
|
76
|
+
|
|
77
|
+
- [#46](https://github.com/taslabs-net/homeflare-kit/pull/46) [`9bc37f8`](https://github.com/taslabs-net/homeflare-kit/commit/9bc37f8bd7b9af1fd5c62cc4bc48597251ef61ab) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Export `BaoMount` and `BaoAuthRole` from `@homeflare/alchemy/openbao`.
|
|
78
|
+
|
|
79
|
+
The docs already showed `import { BaoMount } from '@homeflare/alchemy/openbao'`, but the barrel only shipped the providers. Stacks constructing those resources had to import from internals.
|
|
80
|
+
|
|
81
|
+
- [#48](https://github.com/taslabs-net/homeflare-kit/pull/48) [`4967118`](https://github.com/taslabs-net/homeflare-kit/commit/4967118cc887faba34b17cd48588102736be49bb) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Export `BaoCloudflareRole` and the Cloudflare role catalog helpers from `@homeflare/alchemy/openbao`, and retry dropped OpenBao transports.
|
|
82
|
+
|
|
83
|
+
Stacks declaring mint roles need the resource constructor plus `parseRolesConfig` / `expandAll` / `permissionGroupsFromEngine`. The barrel previously shipped only `BaoCloudflareRoleProvider`. A 585-role plan against a Mesh-fronted vault died mid-diff with an empty transport error; status-0 calls now retry twice.
|
|
84
|
+
|
|
85
|
+
## 0.2.1
|
|
86
|
+
|
|
87
|
+
### Patch Changes
|
|
88
|
+
|
|
89
|
+
- [#40](https://github.com/taslabs-net/homeflare-kit/pull/40) [`60c90eb`](https://github.com/taslabs-net/homeflare-kit/commit/60c90eb4682d0ded2597a79033159ab71cc7a649) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Pin `rolldown` in the consumer overrides so an unlocked install cannot float a missing tarball.
|
|
90
|
+
|
|
91
|
+
Alchemy's optional peer `vite@^8` depends on `rolldown: ~1.2.6`. A consumer `bun add`
|
|
92
|
+
(no lockfile) resolved that to 1.2.9; npm listed the version and 404'd
|
|
93
|
+
`rolldown-1.2.9.tgz`. Main CI failed on that fetch after [#39](https://github.com/taslabs-net/homeflare-kit/issues/39). The override holds 1.2.8 —
|
|
94
|
+
the last tarball a green smoke actually installed.
|
|
95
|
+
|
|
96
|
+
## 0.2.0
|
|
97
|
+
|
|
98
|
+
### Minor Changes
|
|
99
|
+
|
|
100
|
+
- [#36](https://github.com/taslabs-net/homeflare-kit/pull/36) [`cff4111`](https://github.com/taslabs-net/homeflare-kit/commit/cff4111c2f4461a45be5811547125b1f5d98c6d2) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Ship the HTTP and Website SDK surfaces apps were hand-rolling.
|
|
101
|
+
|
|
102
|
+
**`@homeflare/kit/openapi`** — `createOpenApiApp()` is OpenAPIHono with one
|
|
103
|
+
readable validation hook. The document and the request share a Zod schema.
|
|
104
|
+
Subpath, not the main entry: a Node script that only wants `parseEnv` must not
|
|
105
|
+
resolve Hono. Peers: `hono`, `@hono/zod-openapi`, `zod` (optional). Import `z`
|
|
106
|
+
from `@hono/zod-openapi`.
|
|
107
|
+
|
|
108
|
+
**`astroWebsite` / `viteWebsite`** on `@homeflare/alchemy/cloudflare` — house
|
|
109
|
+
flags on Alchemy's own stacks. Astro gets `disable_nodejs_process_v2` (workerd
|
|
110
|
+
process-v2 returns `[object Object]`). Vite is TanStack Start / static Vite.
|
|
111
|
+
Not Nextjs: that helper hashes source and plans as create against a live Worker.
|
|
112
|
+
|
|
113
|
+
Catalog also pins `@tanstack/react-router` 1.170.35, `@tanstack/react-start`
|
|
114
|
+
1.168.52, `@tanstack/react-query` 5.102.8 — match these, do not wrap them.
|
|
115
|
+
|
|
116
|
+
## 0.1.3
|
|
117
|
+
|
|
118
|
+
### Patch Changes
|
|
119
|
+
|
|
120
|
+
- [#32](https://github.com/taslabs-net/homeflare-kit/pull/32) [`5cc2cd6`](https://github.com/taslabs-net/homeflare-kit/commit/5cc2cd62241f926aace1646fd3a1e0057e96cddd) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add `accessIdentity` (ctx.access) and RFC 9728 MCP discovery; fix three doc defects.
|
|
121
|
+
|
|
122
|
+
An app team reviewed the published packages before adopting and was right on every point.
|
|
123
|
+
|
|
124
|
+
**`accessIdentity(ctx)` — Access identity without parsing a JWT.** Cloudflare attaches the
|
|
125
|
+
authenticated identity to the execution context (shipped 2026-08-14), so a Worker behind
|
|
126
|
+
Access reads `ctx.access.getIdentity()` with no token handling. The kit only offered
|
|
127
|
+
`verifyAccessJwt`, which is the older path.
|
|
128
|
+
|
|
129
|
+
⛔ Both stay, because they are not alternatives: `accessIdentity` for a Worker behind
|
|
130
|
+
Access, `verifyAccessJwt` for an origin that has no `ctx.access` — service-to-service, a
|
|
131
|
+
non-Worker origin, or a Worker reached by service binding, since **`ctx.access` does not
|
|
132
|
+
propagate through bindings**.
|
|
133
|
+
|
|
134
|
+
⚠️ Read groups from `accessIdentity`, not from a token: Cloudflare trims the JWT's
|
|
135
|
+
`custom` claim at roughly 1 KB _silently_, so token-read group membership can be
|
|
136
|
+
incomplete — an authorization bug that only appears for users in many groups.
|
|
137
|
+
|
|
138
|
+
**`serveMcpMetadata` / `unauthorizedResponse` — RFC 9728.** The MCP spec requires a server
|
|
139
|
+
to publish Protected Resource Metadata _and_ a 401 naming it in `WWW-Authenticate`.
|
|
140
|
+
Publishing the document while answering a bare 401 leaves clients that follow the header
|
|
141
|
+
with nowhere to go.
|
|
142
|
+
|
|
143
|
+
**Three documentation defects, all reported and all real:**
|
|
144
|
+
|
|
145
|
+
- `@homeflare/alchemy`'s README documented `@homeflare/alchemy/providers`, which does not
|
|
146
|
+
exist. The real path is `/cloudflare`.
|
|
147
|
+
- `@homeflare/cloudflare`'s npm description advertised "typed bindings" — it exports none.
|
|
148
|
+
- `@homeflare/kit`'s advertised "logging" — `log` lives in `@homeflare/cloudflare`.
|
|
149
|
+
|
|
150
|
+
**Packing no longer edits a manifest on disk.** `packForPublish` stripped `scripts` and
|
|
151
|
+
`devDependencies` from the real `package.json`, packed, then restored it — which is a race
|
|
152
|
+
when two smoke tests pack the same workspace dependency in parallel. It destroyed
|
|
153
|
+
`@homeflare/kit`'s `scripts` block during this branch, _after_ `verify` had passed. The
|
|
154
|
+
strip now happens inside the packed tarball, so nothing in the repository is written to.
|
|
155
|
+
|
|
156
|
+
## 0.1.2
|
|
157
|
+
|
|
158
|
+
### Patch Changes
|
|
159
|
+
|
|
160
|
+
- [#29](https://github.com/taslabs-net/homeflare-kit/pull/29) [`72f0787`](https://github.com/taslabs-net/homeflare-kit/commit/72f0787952b3a71bb6573476918e79117250409e) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `cloudflare` is a required peer, not an optional one.
|
|
161
|
+
|
|
162
|
+
Measured 2026-09-16 against the published 0.1.1 in a clean consumer install: importing
|
|
163
|
+
`@homeflare/alchemy/cloudflare` without it throws `Cannot find package 'cloudflare'`.
|
|
164
|
+
Marking it optional claimed the subpath would degrade gracefully; it does not load at all.
|
|
165
|
+
An optional peer should mean a _feature_ is absent, not that an import fails.
|
|
166
|
+
|
|
167
|
+
⛔ **Why this got through, and what now stops it.** The README's pinned install command and
|
|
168
|
+
the smoke test's install command disagreed — the smoke test installed `cloudflare`, the
|
|
169
|
+
README never mentioned it, and nothing compared the two. So the gate proved an install no
|
|
170
|
+
consumer would ever perform.
|
|
171
|
+
|
|
172
|
+
`tests/peers.test.ts` now asserts the manifest, the README and the smoke script agree:
|
|
173
|
+
every declared peer appears in all three, peers are pinned rather than ranged, and none is
|
|
174
|
+
marked optional.
|
|
175
|
+
|
|
176
|
+
## 0.1.1
|
|
177
|
+
|
|
178
|
+
### Patch Changes
|
|
179
|
+
|
|
180
|
+
- [#27](https://github.com/taslabs-net/homeflare-kit/pull/27) [`5b73400`](https://github.com/taslabs-net/homeflare-kit/commit/5b73400e7fc2dd8e28a0368db3e5aa105ebdde9a) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Fix the peer contract: 0.1.0 installed cleanly and threw at import.
|
|
181
|
+
|
|
182
|
+
Measured 2026-09-16 against the published 0.1.0 in a clean consumer install — three
|
|
183
|
+
defects, none of which failed at install time:
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Alchemy changelog archive 18
|
|
2
|
+
|
|
3
|
+
[Current changelog](../../CHANGELOG.md) · [Archive index](./README.md)
|
|
4
|
+
|
|
5
|
+
1. **`@effect/platform-node` was a devDependency**, so a consumer got
|
|
6
|
+
`Cannot find module '@effect/platform-node/NodeServices'`. Alchemy's module graph
|
|
7
|
+
reaches `Cloudflare/Workers/WorkerBridge` even when you only import `/proxmox`, so it
|
|
8
|
+
is a required peer, not an optional one.
|
|
9
|
+
2. **The `effect` peer was ranged** `>=4.0.0-rc.112`, which resolves to rc.115 —
|
|
10
|
+
`TypeError: Config.string is not a function`. Effect's rc line is not
|
|
11
|
+
semver-compatible with itself, so a range is a promise this package cannot keep. Peers
|
|
12
|
+
are pinned exactly now.
|
|
13
|
+
3. **`@effect/platform-node-shared` still resolves up** to rc.115 against effect rc.112
|
|
14
|
+
(`Cannot find module 'effect/ByteSize'`), because `platform-bun@rc.112` asks for
|
|
15
|
+
`^4.0.0-rc.112`. Only a consumer-side `overrides` block holds the set together, and the
|
|
16
|
+
README now says so with the exact block to paste.
|
|
17
|
+
|
|
18
|
+
⛔ The smoke script was `echo '…exercised by the consuming stack'` — a check that cannot
|
|
19
|
+
fail, which is how all three shipped. It now packs the tarball, installs it the way the
|
|
20
|
+
README says, and **imports every subpath**, because each of these threw at import rather
|
|
21
|
+
than at install.
|
|
22
|
+
|
|
23
|
+
## 0.1.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- [#25](https://github.com/taslabs-net/homeflare-kit/pull/25) [`309c644`](https://github.com/taslabs-net/homeflare-kit/commit/309c644d83c7149571f4caada135d1b8d141a484) Thanks [@taslabs-net](https://github.com/taslabs-net)! - New package: custom Alchemy providers for five systems the vendor has none for.
|
|
28
|
+
|
|
29
|
+
**`R2BucketLock`** — an R2 bucket's lock rules, declared rather than applied by hand. A
|
|
30
|
+
lock rule is a retention floor: while a rule covers an object, no API call, no lifecycle
|
|
31
|
+
rule and no credential can delete it.
|
|
32
|
+
|
|
33
|
+
★ Alchemy 2.0.0-beta.77 has no lock property anywhere in its R2 namespace, so the
|
|
34
|
+
alternative was a runbook step a human runs once — and a plan can never show a missing
|
|
35
|
+
runbook step. Covering the gap with a resource makes the drift visible in `plan`.
|
|
36
|
+
|
|
37
|
+
⛔ Deletion is refused by design: removing a lock removes a retention floor, which is the
|
|
38
|
+
one operation this resource exists to make hard.
|
|
39
|
+
|
|
40
|
+
⚠️ `alchemy`, `cloudflare` and `effect` are **peers**, not dependencies — Alchemy's
|
|
41
|
+
resource registry and Effect's context both break if two copies load in one process.
|
|
42
|
+
|
|
43
|
+
**Also in this release** — the same treatment for four more systems, 140 files in all:
|
|
44
|
+
|
|
45
|
+
- **`/proxmox`** (61 source files) — ACLs, API tokens, backup jobs, Ceph, SDN, storage.
|
|
46
|
+
- **`/openbao`** (33) — mounts, policies, PKI/SSH/auth roles, Cloudflare role expansion.
|
|
47
|
+
- **`/forgejo`** (12) — branch protection, org secrets, labels, webhooks.
|
|
48
|
+
- **`/talos`** (8) — cluster bootstrap, machine config, health.
|
|
49
|
+
|
|
50
|
+
⛔ **Import a subpath, never the root.** Each system carries its own client, so a root
|
|
51
|
+
barrel would pull Proxmox into a stack that only wanted Forgejo.
|
|
52
|
+
|
|
53
|
+
★ **The barrels are deliberately smaller than the directories** — 70 exported symbols out
|
|
54
|
+
of 606 defined, chosen from what a real stack actually consumes. An `export *` would
|
|
55
|
+
publish every helper as API and make the next refactor a breaking change.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# PBS jobs on distilled
|
|
2
|
+
|
|
3
|
+
The datastore, prune, sync and verification providers use the typed operations in
|
|
4
|
+
`@distilled.cloud/proxmox-backup`, generated from **proxmox-backup-server 4.2.6-1**.
|
|
5
|
+
`distilled-pbs.ts` supplies the existing leased read/provision credentials to the SDK's
|
|
6
|
+
Credentials service. The SDK owns the `PBSAPIToken` header and request serialization.
|
|
7
|
+
|
|
8
|
+
Existing props and state remain unchanged: retention windows omitted from a declaration
|
|
9
|
+
stay unmanaged, `remove-vanished` still defaults to false, a verification schedule may be
|
|
10
|
+
explicitly null, and a datastore path or backend change is refused. No automatic clearing,
|
|
11
|
+
new deletion flag or live job execution is introduced.
|
|
12
|
+
|
|
13
|
+
Read-only probes on 2026-09-24 found that missing configuration sections answer plain-text
|
|
14
|
+
HTTP 400: `no such datastore`, `no such prune`, `no such sync`, or `no such verification`.
|
|
15
|
+
The SDK patches those specific GET errors as `DatastoreNotFound`, `PruneJobNotFound`,
|
|
16
|
+
`SyncJobNotFound` and `VerifyJobNotFound`. Providers catch those tags only. Permission
|
|
17
|
+
errors, unrelated 400/500 responses, malformed replies and failed transports propagate.
|
|
18
|
+
A failed read can no longer become a speculative create or a successful deletion.
|
|
19
|
+
|
|
20
|
+
Delete first reads the declared item. A typed absent item needs no DELETE. When it exists,
|
|
21
|
+
the provider calls the SDK delete operation; any unclassified error during a concurrent
|
|
22
|
+
removal propagates. The datastore retains its bounded readback and default retain policy;
|
|
23
|
+
`destroy-data` is never sent.
|
|
24
|
+
|
|
25
|
+
The pinned schema declares a null result for datastore create and a UPID for delete. The
|
|
26
|
+
provider verifies the resulting configuration rather than inventing a create task id.
|
|
27
|
+
|
|
28
|
+
The offline lifecycle suite uses `alchemy/Test/Bun` and a strict in-memory PBS HTTP
|
|
29
|
+
fixture. It covers matching adoption without writes, genuine create, drift repair followed
|
|
30
|
+
by a no-op plan, and refused reads. Live adoption remains a separate consumer gate against
|
|
31
|
+
the released package; an offline fixture is not proof of the running estate.
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
|
|
13
13
|
A target that no matcher names receives nothing. Declare both.
|
|
14
14
|
|
|
15
|
+
PBS lifecycle calls use `@distilled.cloud/proxmox-backup` (vendor schema PBS 4.2.6-1).
|
|
16
|
+
The runner reuses short-lived OpenBao leases and bounds each request to 20 seconds, with SDK
|
|
17
|
+
retries disabled. A typed `NotFound` alone means absence or an already completed delete.
|
|
18
|
+
Other read failures stop the plan, so a denied read cannot silently plan a create.
|
|
19
|
+
|
|
15
20
|
## Paging on a failed verify, sync, prune or garbage collection
|
|
16
21
|
|
|
17
22
|
```ts
|
|
@@ -157,7 +162,8 @@ built-in reverts it to the shipped rule**, and the server reports success. The a
|
|
|
157
162
|
| PBS | `Sys.Audit` on `/system/notifications` | `Sys.Modify` on `/system/notifications` |
|
|
158
163
|
| PVE | `Mapping.Audit` or `Mapping.Modify` on `/mapping/notifications` | `Mapping.Modify` on `/mapping/notifications` |
|
|
159
164
|
|
|
160
|
-
⚠️
|
|
161
|
-
|
|
165
|
+
⚠️ The old shared client hid missing read privileges as "absent", planning a `create` that the
|
|
166
|
+
server rejected as already existing. PBS now preserves the SDK refusal and fails the plan.
|
|
167
|
+
Grant the required read privilege before changing infrastructure. On PVE,
|
|
162
168
|
`Mapping.Use` is not enough: it opens the matcher list, not the per-matcher read this package
|
|
163
169
|
makes (`get_matcher` in pve-manager's `Notifications.pm`).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# PVE notification SDK migration
|
|
2
|
+
|
|
3
|
+
PVE 9.2.11 notification targets and matchers use named distilled SDK operations
|
|
4
|
+
for every lifecycle call. Their existing declaration forms, comparison rules,
|
|
5
|
+
secret omission and vendor constraint guards remain in place.
|
|
6
|
+
|
|
7
|
+
A missing matcher or target returns HTTP 404, confirmed by read-only probes on
|
|
8
|
+
2026-09-24. The provider catches the SDK's typed `NotFound` for absent reads and
|
|
9
|
+
idempotent deletes. Authentication, permissions, transport failures and unrelated
|
|
10
|
+
server errors propagate. A malformed successful read fails against the SDK's
|
|
11
|
+
response schema; diagnostics do not retain response contents.
|
|
12
|
+
|
|
13
|
+
SDK request schemas validate the form-name translation before sending a write.
|
|
14
|
+
Arrays use repeated form keys. Commas within matcher rules and webhook property
|
|
15
|
+
strings remain part of each item. An explicitly empty recipient/header list uses
|
|
16
|
+
the vendor's update `delete` field, because an empty SDK array emits no keys.
|
|
17
|
+
Undeclared fields remain unmanaged.
|
|
18
|
+
|
|
19
|
+
A matching adoption sends no write. A changed name replaces the resource; an
|
|
20
|
+
endpoint changing type under the same name replaces delete-first because the
|
|
21
|
+
name is unique across types. Built-in deletion still reverts the vendor default,
|
|
22
|
+
and a gotify create still refuses its missing write-only token: this migration
|
|
23
|
+
does not introduce secrets into persisted props.
|
|
24
|
+
|
|
25
|
+
`notification-distilled.test.ts` exercises create, matching adoption, list clearing,
|
|
26
|
+
identity replacement, malformed/refused reads and subsequent no-op plans through
|
|
27
|
+
the upstream Alchemy harness and real SDK protocol. Live verification follows the
|
|
28
|
+
published release in the consuming stack; fixtures do not prove live writes.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# PVE/PBS SDK migration
|
|
2
|
+
|
|
3
|
+
2026-09-24. Transport completion and live adoption are separate gates. The interim
|
|
4
|
+
SDKs follow [the distilled route](./distilled-interim.md): generated vendor schemas,
|
|
5
|
+
local distilled fixes first, verbatim source copies, and published npm aliases.
|
|
6
|
+
|
|
7
|
+
## Implemented transport
|
|
8
|
+
|
|
9
|
+
Seventeen existing PVE resource families use named distilled operations throughout:
|
|
10
|
+
ACL, ApiToken, BackupJob, CephDaemon, CephFs, CephOsd, CephPool, Group, MetricServer,
|
|
11
|
+
NodeNetwork, NotificationMatcher, NotificationTarget, Pool, Role, Storage, User and
|
|
12
|
+
ZfsPool. All six existing PBS families now do too: Datastore, NotificationMatcher,
|
|
13
|
+
NotificationTarget, PruneJob, SyncJob and VerifyJob.
|
|
14
|
+
|
|
15
|
+
The latest migration preserves credential leases, safe member failover, bounded
|
|
16
|
+
polling, vendor constraints, write-only secret handling and existing state shapes.
|
|
17
|
+
The newly migrated pool, metric, job and notification providers fold only typed
|
|
18
|
+
missing-resource errors to absence; their other SDK failures propagate.
|
|
19
|
+
Protocol fixtures exercise the generated operations; Alchemy engine tests prove
|
|
20
|
+
matching adoption does not write and a real update settles to a no-op.
|
|
21
|
+
|
|
22
|
+
The SDK patches record PVE 9.2.11 source and PBS 4.2.6-1 schema provenance. Read-only
|
|
23
|
+
probes against PVE 9.2.11 and PBS 4.2.3 distinguish notification HTTP404 responses
|
|
24
|
+
from PBS configuration HTTP400 responses. Resource providers do not inspect status
|
|
25
|
+
codes or message text to make that distinction.
|
|
26
|
+
|
|
27
|
+
## Consumer gate
|
|
28
|
+
|
|
29
|
+
These families cover the default TB4/HF-Ops/PBS adoption declarations in
|
|
30
|
+
`homeflare-proxmox` at commit `91b31b0`. Its opt-in vault-door Lxc is still a later
|
|
31
|
+
slice. This source migration alone does not establish live adoption.
|
|
32
|
+
After both SDKs and the provider release publish, the consumer pins those exact
|
|
33
|
+
versions, runs its full check and all three adoption verifiers, then compares the
|
|
34
|
+
read and admin plans at stage `live`. A write requires a fresh matching proof.
|
|
35
|
+
|
|
36
|
+
## Remaining PVE families
|
|
37
|
+
|
|
38
|
+
Twelve existing families still retain some hand-client transport: Lxc, Vm (QEMU),
|
|
39
|
+
NetworkApply, SdnApply, SdnZone, SdnVnet, SdnSubnet, HaResource, HaRule,
|
|
40
|
+
ReplicationJob, FirewallAlias and CephFlag. Their previous measured-none records
|
|
41
|
+
are historical inventories, not a current census or permission to skip migration.
|
|
42
|
+
|
|
43
|
+
- NetworkApply needs the `changes` sibling beside the response's `data`; generic
|
|
44
|
+
unwrapping discards it. Fix the distilled protocol/schema boundary before moving
|
|
45
|
+
this read. The network diff can contain secrets and must not enter state or logs.
|
|
46
|
+
- Lxc task polling must keep the credential that started the task, preserve bounded
|
|
47
|
+
uncertainty after connection loss, and retain its cluster-wide identity checks.
|
|
48
|
+
- SdnApply distinguishes an unavailable subsystem from failed reads and refuses
|
|
49
|
+
undiffable settings. Those semantics need vendor-backed typed SDK errors.
|
|
50
|
+
- Qemu's guest DELETE route differs from its configuration GET/PUT route; copying
|
|
51
|
+
the old generic factory path would keep an existing bug.
|
|
52
|
+
|
|
53
|
+
Complete vendor coverage additionally requires a census of families not yet modeled
|
|
54
|
+
as resources. The 23 migrated families above are a transport milestone, not that
|
|
55
|
+
larger completion claim. No upstream push or pull request was made.
|
|
56
|
+
|
|
57
|
+
Existing NodeNetwork and ZfsPool adoption/reconcile reads still contain broad failure
|
|
58
|
+
folds. Their strict diff reads do not remove that separate conformance gap. CephFs
|
|
59
|
+
task polling also tolerates temporary read failures, but uses a bounded uncertainty
|
|
60
|
+
policy rather than treating a resource as absent.
|
package/docs/proxmox.md
CHANGED
|
@@ -37,7 +37,13 @@ CephFS index cannot produce a successful delete. Existing adoption fixtures prov
|
|
|
37
37
|
matching objects remain no-ops. Credential-denied warnings are still not live no-op
|
|
38
38
|
evidence; use the consumer's authorized read lane and `hf-adopt-verify --all`.
|
|
39
39
|
|
|
40
|
-
This is a scoped follow-up to the transport migration. CephFS delete
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
This is a scoped follow-up to the transport migration. CephFS delete now uses SDK 0.3.0's fixed
|
|
41
|
+
DELETE query binding and folds only CephFsNotFound. Its existing destructive flags,
|
|
42
|
+
bounded task polling and final index read remain in place. Other provider families
|
|
43
|
+
retain their own policies. No live writes are part
|
|
43
44
|
of this validation. Released-consumer plans remain a separate gate from these fixtures.
|
|
45
|
+
|
|
46
|
+
`ceph-fs-destroy-sdk.test.ts` verifies that remove-pools/remove-storages are query
|
|
47
|
+
parameters with an empty DELETE body, omitted flags retain the safe defaults, an
|
|
48
|
+
already-missing filesystem succeeds, and unrelated 401/403/500 responses fail.
|
|
49
|
+
The engine destroy fixture still proves the complete read/delete/poll/read-back flow.
|
|
@@ -10,6 +10,9 @@ measurements and open findings are preserved below.
|
|
|
10
10
|
- 27 `Effect.die` sites in non-test source (defect risk, not a separate S20 ban);
|
|
11
11
|
- 7 test files on `node:test`;
|
|
12
12
|
- `Pbs.*` type strings (H14).
|
|
13
|
+
**SDK progress, 2026-09-24:** 17 PVE and all six existing PBS resource families now
|
|
14
|
+
call named distilled operations. The [migration record](./proxmox-sdk-migration.md)
|
|
15
|
+
separates that transport milestone, the published-consumer gate and remaining families.
|
|
13
16
|
8. **`talos/*`.** Its `talosctl` calls go through `ChildProcessSpawner`, as upstream's Docker
|
|
14
17
|
provider does, which conforms. Its file handling uses `Bun.file`, `Bun.write`,
|
|
15
18
|
`Bun.YAML` and `node:fs` (S42), and it uses `Effect.promise` in 3 files (S19). The
|
|
@@ -75,8 +78,9 @@ HttpClient` client. The old status-carrying `NetboxError` and its `cause.status
|
|
|
75
78
|
12. **Resource JSDoc is not in upstream's generator format.** Zero files use `@resource`,
|
|
76
79
|
`**Example:**` or `### Section` (S31, H10). **Decision:** maintainer, because it moves
|
|
77
80
|
the house's glyph rationale into `//` comments.
|
|
78
|
-
13. **
|
|
79
|
-
|
|
81
|
+
13. **Original finding: tests did not use `alchemy/Test/Bun`.** The PVE/PBS SDK migration
|
|
82
|
+
now exercises provider lifecycles through that upstream engine harness with fake HTTP
|
|
83
|
+
responses. It does not claim live write coverage; live suites still need a place to run.
|
|
80
84
|
14. **`discord/*` is new (2026-09-24, task-authorized, kit PR TBD) and built directly on
|
|
81
85
|
`@distilled.cloud/discord` — no hand-rolled `client.ts` ever existed to retire.**
|
|
82
86
|
`Discord.ApplicationCommand` and `Discord.GuildApplicationCommand` call the SDK's
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@homeflare/alchemy",
|
|
3
|
-
"version": "0.37.
|
|
3
|
+
"version": "0.37.3",
|
|
4
4
|
"description": "Custom Alchemy providers for Cloudflare, Proxmox, OpenBao, Forgejo, Talos, launchd and Caddy, and a verifier that proves adoptions are no-ops.",
|
|
5
5
|
"homepage": "https://github.com/taslabs-net/homeflare-kit/tree/main/packages/alchemy#readme",
|
|
6
6
|
"bugs": "https://github.com/taslabs-net/homeflare-kit/issues",
|
|
@@ -124,8 +124,8 @@
|
|
|
124
124
|
"@distilled.cloud/netbox": "npm:@homeflare/distilled-netbox@0.3.0",
|
|
125
125
|
"@distilled.cloud/opnsense": "npm:@homeflare/distilled-opnsense@0.2.0",
|
|
126
126
|
"@distilled.cloud/paperless-ngx": "npm:@homeflare/distilled-paperless-ngx@0.3.0",
|
|
127
|
-
"@distilled.cloud/proxmox": "npm:@homeflare/distilled-proxmox@0.3.
|
|
128
|
-
"@distilled.cloud/proxmox-backup": "npm:@homeflare/distilled-proxmox-backup@0.3.
|
|
127
|
+
"@distilled.cloud/proxmox": "npm:@homeflare/distilled-proxmox@0.3.1",
|
|
128
|
+
"@distilled.cloud/proxmox-backup": "npm:@homeflare/distilled-proxmox-backup@0.3.1",
|
|
129
129
|
"@distilled.cloud/unifi-network": "npm:@homeflare/distilled-unifi-network@0.2.0"
|
|
130
130
|
},
|
|
131
131
|
"peerDependencies": {
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
* Adopting an object that already matches must be read-only — measured per reconcile SHAPE, through
|
|
3
3
|
* Alchemy's own Plan and Apply over a fake cluster (fake-pve.ts, ../verify/fake-engine.ts).
|
|
4
4
|
*
|
|
5
|
-
* ★ ONE ROW PER WAY A FAMILY REACHES ITS WRITE, not one per family.
|
|
6
|
-
* `
|
|
7
|
-
*
|
|
8
|
-
* it wrong, has its own file. docs/adopt-verify.md carries the full per-family table.
|
|
5
|
+
* ★ ONE ROW PER WAY A FAMILY REACHES ITS WRITE, not one per family. Pool now has its own distilled
|
|
6
|
+
* lifecycle; `Acl` wraps resource.ts's shared reconcile; `PbsDatastore` and `SdnApply` write their
|
|
7
|
+
* own. CephPool, the one that got it wrong, has its own file. docs/adopt-verify.md carries the full per-family table.
|
|
9
8
|
* ⚠️ A FIXTURE THAT DOES NOT MATCH TURNS A ROW INTO A DRIFT TEST, and the assertion on the
|
|
10
9
|
* verifier's `noop` is what catches that: a wrong fixture fails loudly instead of passing for
|
|
11
10
|
* the wrong reason. The Pool drift row proves the harness can see a write at all.
|
|
@@ -36,7 +35,7 @@ const cases: readonly Case[] = [
|
|
|
36
35
|
{
|
|
37
36
|
declare: () => ProxmoxPool('lab', { comment: 'x', poolid: 'lab', target: FAKE_TARGET }),
|
|
38
37
|
live: { 'pools/lab': { comment: 'x', members: [] } },
|
|
39
|
-
name: 'Proxmox.Pool (
|
|
38
|
+
name: 'Proxmox.Pool (direct distilled lifecycle)',
|
|
40
39
|
provider: ProxmoxPoolProvider as never,
|
|
41
40
|
},
|
|
42
41
|
{
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/** Pure existing forms, normalization and equality, shared by plan and SDK lifecycle. */
|
|
2
|
+
import type { BackupJobAttributes, BackupJobProps } from './backup-job.ts';
|
|
3
|
+
import type { PveSpec } from './resource-spec.ts';
|
|
4
|
+
import { bool, guestList, propertyString, text } from './values.ts';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The form for both create and update.
|
|
8
|
+
*
|
|
9
|
+
* ⛔ A FIELD THE DECLARATION LEAVES OUT IS NOT SENT, AND THAT IS LOAD BEARING. PVE's PUT can only
|
|
10
|
+
* SET a key; clearing one needs `delete=<key>`, which this resource deliberately never sends.
|
|
11
|
+
* `updateForm` is handed the props and nothing else, so a `delete` list could only be "every
|
|
12
|
+
* field you did not declare" — and on a job adopted out of the UI that would silently strip
|
|
13
|
+
* settings this resource does not even model, `compress`, `mailto` and `bwlimit` among them, on
|
|
14
|
+
* the first deploy. Leaving undeclared fields alone is the smaller lie, and `matches` tells the
|
|
15
|
+
* same lie consistently: it does not compare what it cannot change.
|
|
16
|
+
*
|
|
17
|
+
* ⚠️ THE FIVE FIELDS PVE DOCUMENTS A DEFAULT FOR ARE ALWAYS SENT AND ALWAYS COMPARED — `enabled`
|
|
18
|
+
* (1), `all` (0), `mode` (snapshot), `notification-mode` (auto), `repeat-missed` (0). That is
|
|
19
|
+
* what makes a hand-flipped `enabled 0` show up as drift rather than as silence, and it is also
|
|
20
|
+
* what stops those five looping: a field compared against a default must be sent as that
|
|
21
|
+
* default, or every plan reports an update the PUT never performs. The cost is worth reading
|
|
22
|
+
* before the first deploy against an adopted job — an omitted `all` is a declaration that this
|
|
23
|
+
* job backs up NOTHING, and PVE accepts a job that selects nothing without a word. Read that
|
|
24
|
+
* first plan; do not deploy it unseen.
|
|
25
|
+
*/
|
|
26
|
+
const shape = (props: BackupJobProps) => ({
|
|
27
|
+
all: props.all === true ? '1' : '0',
|
|
28
|
+
enabled: props.enabled === false ? '0' : '1',
|
|
29
|
+
mode: props.mode ?? 'snapshot',
|
|
30
|
+
'notification-mode': props['notification-mode'] ?? 'auto',
|
|
31
|
+
'repeat-missed': props['repeat-missed'] === true ? '1' : '0',
|
|
32
|
+
schedule: props.schedule,
|
|
33
|
+
...(props.comment === undefined ? {} : { comment: props.comment }),
|
|
34
|
+
...(props.exclude === undefined ? {} : { exclude: guestList(props.exclude) }),
|
|
35
|
+
...(props.fleecing === undefined ? {} : { fleecing: props.fleecing }),
|
|
36
|
+
...(props['notes-template'] === undefined ? {} : { 'notes-template': props['notes-template'] }),
|
|
37
|
+
...(props.pool === undefined ? {} : { pool: props.pool }),
|
|
38
|
+
...(props['prune-backups'] === undefined ? {} : { 'prune-backups': props['prune-backups'] }),
|
|
39
|
+
...(props.storage === undefined ? {} : { storage: props.storage }),
|
|
40
|
+
...(props.vmid === undefined ? {} : { vmid: guestList(props.vmid) }),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const backupJobSpec = {
|
|
44
|
+
/**
|
|
45
|
+
* ⛔ NO "IS IT REALLY THERE" GUARD, ON PURPOSE. Returning undefined for a job whose JSON is
|
|
46
|
+
* missing some key would be a guess about which keys PVE echoes back, and a wrong guess here
|
|
47
|
+
* does not read as absent-and-harmless: `reconcile` would POST, and POST creates ANOTHER job.
|
|
48
|
+
* Absence is decided by the API declining to answer, which `pveOperations.read` already
|
|
49
|
+
* handles — not by a key being missing from an answer that did arrive.
|
|
50
|
+
*/
|
|
51
|
+
attributes: (live, props) => ({
|
|
52
|
+
all: bool(live['all'], false),
|
|
53
|
+
comment: text(live['comment'], ''),
|
|
54
|
+
enabled: bool(live['enabled'], true),
|
|
55
|
+
exclude: guestList(live['exclude']),
|
|
56
|
+
fleecing: propertyString(live['fleecing'], 'enabled'),
|
|
57
|
+
id: props.id,
|
|
58
|
+
mode: text(live['mode'], 'snapshot'),
|
|
59
|
+
'next-run': typeof live['next-run'] === 'number' ? live['next-run'] : 0,
|
|
60
|
+
'notes-template': text(live['notes-template'], ''),
|
|
61
|
+
'notification-mode': text(live['notification-mode'], 'auto'),
|
|
62
|
+
pool: text(live['pool'], ''),
|
|
63
|
+
'prune-backups': propertyString(live['prune-backups']),
|
|
64
|
+
'repeat-missed': bool(live['repeat-missed'], false),
|
|
65
|
+
schedule: text(live['schedule'], ''),
|
|
66
|
+
storage: text(live['storage'], ''),
|
|
67
|
+
vmid: guestList(live['vmid']),
|
|
68
|
+
}),
|
|
69
|
+
collection: () => 'cluster/backup',
|
|
70
|
+
/** ⛔ `id` IS SENT AND IS NOT OPTIONAL. Everything above depends on PVE not inventing one. */
|
|
71
|
+
createForm: (props) => ({ ...shape(props), id: props.id }),
|
|
72
|
+
/** Each line reads "not declared, or equal"; the five defaulted fields have no undeclared case. */
|
|
73
|
+
/** The vendor rules these forms are checked against at plan time — resource-spec.ts. */
|
|
74
|
+
endpoint: { create: 'pve:POST /cluster/backup', update: 'pve:PUT /cluster/backup/{id}' },
|
|
75
|
+
matches: (attributes, props) =>
|
|
76
|
+
attributes.schedule === props.schedule &&
|
|
77
|
+
attributes.enabled === (props.enabled !== false) &&
|
|
78
|
+
attributes.all === (props.all === true) &&
|
|
79
|
+
attributes.mode === (props.mode ?? 'snapshot') &&
|
|
80
|
+
attributes['notification-mode'] === (props['notification-mode'] ?? 'auto') &&
|
|
81
|
+
attributes['repeat-missed'] === (props['repeat-missed'] === true) &&
|
|
82
|
+
(props.storage === undefined || attributes.storage === props.storage) &&
|
|
83
|
+
(props.pool === undefined || attributes.pool === props.pool) &&
|
|
84
|
+
(props.comment === undefined || attributes.comment === props.comment) &&
|
|
85
|
+
(props.vmid === undefined || attributes.vmid === guestList(props.vmid)) &&
|
|
86
|
+
(props.exclude === undefined || attributes.exclude === guestList(props.exclude)) &&
|
|
87
|
+
(props['notes-template'] === undefined ||
|
|
88
|
+
attributes['notes-template'] === props['notes-template']) &&
|
|
89
|
+
(props['prune-backups'] === undefined ||
|
|
90
|
+
attributes['prune-backups'] === propertyString(props['prune-backups'])) &&
|
|
91
|
+
(props.fleecing === undefined ||
|
|
92
|
+
attributes.fleecing === propertyString(props.fleecing, 'enabled')),
|
|
93
|
+
path: (props) => `cluster/backup/${props.id}`,
|
|
94
|
+
updateForm: shape,
|
|
95
|
+
} satisfies PveSpec<BackupJobProps, BackupJobAttributes>;
|