@homeflare/alchemy 0.37.1 → 0.37.2
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/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/group-wire.d.ts +2 -2
- package/dist/proxmox/group-wire.d.ts.map +1 -1
- package/dist/proxmox/index.js +295 -313
- package/dist/proxmox/index.js.map +5 -5
- package/dist/proxmox/node-network-wire.d.ts +1 -1
- package/dist/proxmox/node-network-wire.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/proxmox.md +9 -3
- package/package.json +3 -3
- 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
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Alchemy changelog archive 9
|
|
2
|
+
|
|
3
|
+
[Current changelog](../../CHANGELOG.md) · [Archive index](./README.md)
|
|
4
|
+
|
|
5
|
+
Not published upstream yet, so aliased onto `@homeflare/distilled-litellm@0.2.0` as a plain
|
|
6
|
+
`dependencies` entry, not a peer — see `docs/distilled-interim.md`. Credentials still resolve
|
|
7
|
+
from `LITELLM_PROXY_URL` / `LITELLM_PROXY_API_KEY` at call time, now through the package's own
|
|
8
|
+
`CredentialsFromEnv` layer. Props and attributes are unchanged — an adopted pass-through endpoint
|
|
9
|
+
still plans noop.
|
|
10
|
+
|
|
11
|
+
- [#212](https://github.com/taslabs-net/homeflare-kit/pull/212) [`e419a44`](https://github.com/taslabs-net/homeflare-kit/commit/e419a44f45e3332eb9ae7c3b196d203309668f26) Thanks [@taslabs-net](https://github.com/taslabs-net)! - The `paperless/*` family (`Tag`, `DocumentType`, `StoragePath`, `CustomField`) now calls
|
|
12
|
+
`@distilled.cloud/paperless-ngx`'s typed operations instead of a hand-rolled `Effect HttpClient`
|
|
13
|
+
client. `client.ts`, `errors.ts` and `credentials.ts` are gone; every status check
|
|
14
|
+
(`statusToError`, the status-carrying `PaperlessError` union) is replaced by
|
|
15
|
+
`Effect.catchTag('NotFound', …)` at each resource file's own `getById` call, the same rule
|
|
16
|
+
`forgejo/*` and `netbox/*` follow — a LIST call is never folded to absent at all, since an empty
|
|
17
|
+
page is a normal 200 (measured live, kit PR 194). The shared `matching.ts`/`matching-locate.ts`
|
|
18
|
+
engine (locate-by-name before there is state, by the stored `output.id` after — PR 163) is
|
|
19
|
+
unchanged in shape, generalized only over the SDK's typed `Live` row and each family's own
|
|
20
|
+
distilled error union instead of an untyped `PaperlessRow`.
|
|
21
|
+
|
|
22
|
+
`@distilled.cloud/paperless-ngx` is not published upstream yet, so this package was already
|
|
23
|
+
aliased onto `@homeflare/distilled-paperless-ngx@0.3.0` (kit PR 188/194/206 — built the distilled
|
|
24
|
+
way and shipped from this monorepo, see `docs/distilled-interim.md`); this PR is the first thing
|
|
25
|
+
that actually imports it. `packages/alchemy`'s own `build:interim-deps` now builds it too, and
|
|
26
|
+
picks up a pre-existing bug in the same line while doing so: `bun --cwd <dir> run <script>`
|
|
27
|
+
(space-separated) silently no-ops instead of building — `bun --cwd=<dir> run <script>` is the form
|
|
28
|
+
that actually works. Fixed for all three interim deps this script already named (netbox, proxmox,
|
|
29
|
+
paperless-ngx); the root-level `scripts/build-interim-packages.ts` was never affected, since it
|
|
30
|
+
spawns `bun run build` with an explicit `cwd` option rather than a shell string.
|
|
31
|
+
|
|
32
|
+
Credentials still resolve from `PAPERLESS_URL` / `PAPERLESS_TOKEN` at call time, now through the
|
|
33
|
+
SDK's own `CredentialsFromEnv`, baked directly into each of the four `xxxProvider()`s the same way
|
|
34
|
+
`netboxHandlers`/`forgejoHandlers` do — `providers.ts`'s `paperlessProviders()` no longer takes a
|
|
35
|
+
credentials-layer override (nothing in this estate called it with one: measured 2026-09-24, no
|
|
36
|
+
tray repo under `homeflare-landscape` declares a Paperless resource at all). Props and attributes
|
|
37
|
+
are unchanged — an adopted tag, document type, storage path or custom field still plans noop.
|
|
38
|
+
|
|
39
|
+
- [#220](https://github.com/taslabs-net/homeflare-kit/pull/220) [`4da7ecc`](https://github.com/taslabs-net/homeflare-kit/commit/4da7ecc7d5dd59e0c9aab3a4a13dee148f9a14a9) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `client.ts`'s `executeOnCluster` and `distilled-pve.ts`'s `runPveWith`
|
|
40
|
+
(`members.ts`, shared by both) now bound a single cluster-member attempt with
|
|
41
|
+
a new `MEMBER_TIMEOUT` (20 seconds). Previously nothing did: a PVE member
|
|
42
|
+
that accepted the TCP connection but never answered hung the whole call
|
|
43
|
+
forever, because the cluster-failover loop never got a failure to classify
|
|
44
|
+
and so never reached a healthy member — this affected every PVE resource,
|
|
45
|
+
migrated or not, since both codepaths funnel through `executeOnCluster`. A
|
|
46
|
+
bounded timeout now fails a read over to the next member (no side effect to
|
|
47
|
+
duplicate) and fails a write outright without resending it, matching the
|
|
48
|
+
existing rule that a post-connect failure is never pre-send. Found on review
|
|
49
|
+
of `Proxmox.Acl`'s migration (PR 209); fixed once, shared by every PVE
|
|
50
|
+
family.
|
|
51
|
+
|
|
52
|
+
- [#216](https://github.com/taslabs-net/homeflare-kit/pull/216) [`eaa12e5`](https://github.com/taslabs-net/homeflare-kit/commit/eaa12e532fc420a74efd8df717e1fe080f1e3840) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `repoBaselineSettings` (repo-baseline-data.ts) no longer turns `allowAutoMerge` on
|
|
53
|
+
unconditionally — it now follows `checks.length > 0`, the same as the sibling `repoPolicy` path
|
|
54
|
+
already refuses to do (`repo-policy-guards.ts`'s `assertAutoMergeWaits`). With zero required
|
|
55
|
+
status checks, `gh pr merge --auto` (and the ruleset's own auto-merge) has nothing to wait for:
|
|
56
|
+
GitHub merges a CLEAN pull request on the spot, with no review and no green run required. A repo
|
|
57
|
+
declared through `declareRepoBaseline` with an empty `checks` list (homeflare-anyauth today, for
|
|
58
|
+
example — no workflows, so no checks to name) would otherwise get auto-merge turned on with
|
|
59
|
+
nothing gating it.
|
|
60
|
+
|
|
61
|
+
This baseline has no `autoMerge` opt-out prop the way `repoPolicy` does, so the fix is
|
|
62
|
+
unconditional rather than a thrown refusal: a repo with checks keeps `allowAutoMerge: true`
|
|
63
|
+
exactly as before; one without simply never gets it turned on by this baseline.
|
|
64
|
+
|
|
65
|
+
- [#217](https://github.com/taslabs-net/homeflare-kit/pull/217) [`ad397ea`](https://github.com/taslabs-net/homeflare-kit/commit/ad397eadbff8c20fef5c9634980b99ce65203758) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `GitHub.RepositoryRuleset`'s `Resource<>` declaration put `GitHubCredentials` in its 5th
|
|
66
|
+
(`Providers`) type parameter, which puts that credential requirement on every `yield*
|
|
67
|
+
RepositoryRuleset(...)` call site — including inside `declareRepoPolicy`/`declareRepoBaseline`.
|
|
68
|
+
A stack body cannot supply `GitHubCredentials` itself (`Alchemy.Stack`'s own `ProviderServices`
|
|
69
|
+
type is a closed union `GitHubCredentials` does not structurally match), so any consumer whose
|
|
70
|
+
stack body calls `declareRepoPolicy`/`declareRepoBaseline` — or declares `RepositoryRuleset`
|
|
71
|
+
directly — failed `tsc` (measured 2026-09-24: homeflare-builds bumping to 0.27.4, TS2345).
|
|
72
|
+
|
|
73
|
+
Verified against upstream `alchemy@2.0.0-beta.79`'s own `GitHub.Ruleset`
|
|
74
|
+
(`node_modules/alchemy/src/GitHub/Ruleset.ts`): its 5th slot is `GitHub.Providers`, the
|
|
75
|
+
`ProviderCollection` tag `GitHub.providers()` outputs — never the raw `GitHubCredentials`
|
|
76
|
+
service its own provider handlers pull in via `octokitFor`. `RepositoryRuleset` now omits the
|
|
77
|
+
5th parameter, defaulting its declaration requirement to `Provider<RepositoryRuleset>` instead —
|
|
78
|
+
a real `ProviderServices` member, matching the pattern a standalone (non-collection) resource
|
|
79
|
+
needs.
|
|
80
|
+
|
|
81
|
+
Fixing only the declaration was not enough on its own: `GitHub.providers()` and
|
|
82
|
+
`RepositoryRulesetProvider()` written side by side as a stack's `providers` still fails to
|
|
83
|
+
typecheck, because `RepositoryRulesetProvider()`'s handlers need `GitHubCredentials` fed in, and
|
|
84
|
+
merging two layers does not thread one's output into the other's requirement. New export
|
|
85
|
+
`repoPolicyProviders()` (`repository-ruleset-providers.ts`) composes the two correctly —
|
|
86
|
+
`RepositoryRulesetProvider().pipe(Layer.provideMerge(GitHub.providers()))` merged with
|
|
87
|
+
`GitHub.providers()` itself — and is now the documented wiring in `docs/repo-policy.md`,
|
|
88
|
+
`docs/repository-ruleset.md`, `repo-policy.ts` and `declare-repo-baseline.ts`.
|
|
89
|
+
|
|
90
|
+
Added a type-level test (`repo-policy-stack.test.ts`): a real `Alchemy.Stack(...)` whose body
|
|
91
|
+
calls `declareRepoPolicy` and `declareRepoBaseline` with `repoPolicyProviders()`, built (never
|
|
92
|
+
run) so `tsc` checks it on every `bun run check` — the test that would have caught this before
|
|
93
|
+
it reached a consumer's bump PR.
|
|
94
|
+
|
|
95
|
+
No runtime behavior changes: every CRUD handler (`read`/`diff`/`reconcile`/`delete`) and its own
|
|
96
|
+
`GitHubCredentials` requirement is unchanged — only the type surface a stack body sees when
|
|
97
|
+
declaring the resource, and how the two providers compose, is fixed.
|
|
98
|
+
|
|
99
|
+
## 0.28.0
|
|
100
|
+
|
|
101
|
+
### Minor Changes
|
|
102
|
+
|
|
103
|
+
- [#206](https://github.com/taslabs-net/homeflare-kit/pull/206) [`06591c9`](https://github.com/taslabs-net/homeflare-kit/commit/06591c91d622178fdf1b09d8ac2f455bafa70107) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Aliases four more distilled interim SDKs onto `@homeflare/alchemy`'s
|
|
104
|
+
`dependencies`, following `docs/distilled-interim.md`'s step 5:
|
|
105
|
+
`@distilled.cloud/proxmox-backup`, `@distilled.cloud/paperless-ngx`,
|
|
106
|
+
`@distilled.cloud/litellm` and `@distilled.cloud/caddy`, each aliased onto
|
|
107
|
+
its published `@homeflare/distilled-<vendor>` interim copy (kit PRs [#200](https://github.com/taslabs-net/homeflare-kit/issues/200),
|
|
108
|
+
[#194](https://github.com/taslabs-net/homeflare-kit/issues/194)/[#195](https://github.com/taslabs-net/homeflare-kit/issues/195), [#201](https://github.com/taslabs-net/homeflare-kit/issues/201), [#202](https://github.com/taslabs-net/homeflare-kit/issues/202)) at its current workspace version — not opnsense or
|
|
109
|
+
unifi-network, neither of which is aliased anywhere yet. No resource in this
|
|
110
|
+
package imports any of the four yet; that migration is each family's own
|
|
111
|
+
later PR, per `distilled-interim.md`'s "what NOT to do".
|
|
112
|
+
|
|
113
|
+
The root `build:interim-packages` script (kept root-level and non-nested,
|
|
114
|
+
the CI-race fix from kit PR [#193](https://github.com/taslabs-net/homeflare-kit/issues/193)) is now generic: it discovers which
|
|
115
|
+
`packages/distilled-*` copies to build by scanning every workspace
|
|
116
|
+
manifest's `dependencies` for a `"@distilled.cloud/<vendor>":
|
|
117
|
+
"npm:@homeflare/distilled-<vendor>@<version>"` alias, instead of a
|
|
118
|
+
hard-coded netbox/proxmox list — a new alias needs no edit to this script.
|
|
119
|
+
`tests/catalog.test.ts`'s `EXACT_PEERS` table is now derived the same way
|
|
120
|
+
for every `distilled-*` interim copy (`effect` alone, one reasoning comment
|
|
121
|
+
kept in one place) instead of one hand-added entry and comment per vendor,
|
|
122
|
+
which had become a recurring merge-conflict hot spot across concurrent
|
|
123
|
+
interim-package PRs landing the same night.
|
|
124
|
+
|
|
125
|
+
This is a `minor`, not a `patch`: `@homeflare/alchemy`'s own `dependencies`
|
|
126
|
+
gained four new runtime entries, even though no exported code changed.
|
|
127
|
+
|
|
128
|
+
- [#205](https://github.com/taslabs-net/homeflare-kit/pull/205) [`3ab881e`](https://github.com/taslabs-net/homeflare-kit/commit/3ab881ecd53108050b1d23db39d955e82a07c322) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `GitHub.RepositoryRuleset` now expresses a live ruleset's exact shape when that shape is
|
|
129
|
+
narrower than the house baseline: a `rules.pullRequest: false` declares its deliberate absence
|
|
130
|
+
(matching the `requiredStatusChecks: false` pattern that already existed), and `bypassActors`
|
|
131
|
+
already carried an arbitrary `actor_id`/`actor_type`/`bypass_mode` list — the gap was never the
|
|
132
|
+
prop shape, it was that nothing stopped a declaration from silently narrowing what is live.
|
|
133
|
+
|
|
134
|
+
Two new guards close that: `bypassNarrowingRefusal` refuses a declared `bypassActors` that drops
|
|
135
|
+
a live actor, and `ruleNarrowingRefusal` refuses a declared `false` (any modeled rule) that drops
|
|
136
|
+
a rule the live ruleset still has — both unless the declaration also carries the matching new
|
|
137
|
+
prop, `acknowledgeBypassNarrowing: { reason: string }` or `acknowledgeRuleNarrowing: { reason:
|
|
138
|
+
string }`, a reasoned, explicit sign-off. The two acknowledgements are deliberately separate
|
|
139
|
+
props, not one shared flag: an adversarial review found that a single shared
|
|
140
|
+
`acknowledgeNarrowing` let a reason worded for one kind of drop silently also excuse the other
|
|
141
|
+
kind in the same declaration, so each guard now reads only the prop scoped to what it checks.
|
|
142
|
+
Widening bypass stays refused unconditionally, as before — this only ever loosens the NARROWING
|
|
143
|
+
side, and only with a recorded, correctly-scoped reason.
|
|
144
|
+
|
|
145
|
+
Prompted by a red-team finding against a design for declaring ~88 `taslabs-net` repos' GitHub
|
|
146
|
+
settings in Alchemy: 5 live repos (`taslabs-net`, `aop`, `magictransit`, `loggarr`,
|
|
147
|
+
`doesthishelp-workeropen`) carry a non-empty live `bypass_actors`, and `taslabs-net` itself has
|
|
148
|
+
no live `pull_request` rule — the exact combination `repoBaselineRuleset()`'s hardcoded
|
|
149
|
+
`bypassActors: []` and always-on `pullRequest` rule could not adopt without silently stripping
|
|
150
|
+
protection. `repoBaselineRuleset()` itself is unchanged — it keeps its baseline defaults, for
|
|
151
|
+
repos the baseline shape actually fits; a caller with a narrower live ruleset now declares
|
|
152
|
+
`RepositoryRuleset` directly with the live shape instead.
|
|
153
|
+
|
|
154
|
+
Tested against all 5 live shapes, re-read via `gh api repos/taslabs-net/<repo>/rulesets/<id>`
|
|
155
|
+
2026-09-23 (not copied from an earlier paraphrase — `doesthishelp-workeropen`'s two bypass
|
|
156
|
+
actors' `bypass_mode`s differ from how an earlier design doc described them).
|
|
157
|
+
|
|
158
|
+
## 0.27.4
|
|
159
|
+
|
|
160
|
+
### Patch Changes
|
|
161
|
+
|
|
162
|
+
- [#193](https://github.com/taslabs-net/homeflare-kit/pull/193) [`41bdc5d`](https://github.com/taslabs-net/homeflare-kit/commit/41bdc5d8f32fca92d60ee667b85306c77cea2442) Thanks [@taslabs-net](https://github.com/taslabs-net)! - The `netbox/*` family (`Netbox.Prefix`) now calls `@distilled.cloud/netbox`'s
|
|
163
|
+
typed `ipam` operations instead of a hand-rolled `Effect HttpClient` client.
|
|
164
|
+
The old status-carrying `NetboxError` and its `cause.status === 404` check
|
|
165
|
+
are gone: `Netbox.Prefix` locates by a server-side list filter, which
|
|
166
|
+
`@distilled.cloud/netbox` never answers with a 404 (an empty page is a
|
|
167
|
+
normal 200), so nothing here checks a status code at all — a stronger
|
|
168
|
+
version of the same rule `catchTag('NotFound', …)` enforces for a
|
|
169
|
+
distilled-backed family that reads by direct key, like `forgejo/*`.
|
|
170
|
+
`client.ts` is gone; nothing else in this package imported it. The real
|
|
171
|
+
`@distilled.cloud/netbox` is not published upstream yet, so this package
|
|
172
|
+
aliases it onto `@homeflare/distilled-netbox@0.2.0` (built the distilled way
|
|
173
|
+
and shipped from this monorepo — see `docs/distilled-interim.md`) as a plain
|
|
174
|
+
`dependencies` entry, not a peer — nothing changes for a consumer's install.
|
|
175
|
+
Credentials still resolve from `NETBOX_URL` / `NETBOX_TOKEN` at call time,
|
|
176
|
+
now through the package's own `CredentialsFromEnv` layer. Props and
|
|
177
|
+
attributes are unchanged — an adopted prefix still plans noop.
|
|
178
|
+
|
|
179
|
+
## 0.27.3
|
|
180
|
+
|
|
181
|
+
### Patch Changes
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Alchemy changelog archive 10
|
|
2
|
+
|
|
3
|
+
[Current changelog](../../CHANGELOG.md) · [Archive index](./README.md)
|
|
4
|
+
|
|
5
|
+
- [#191](https://github.com/taslabs-net/homeflare-kit/pull/191) [`e991225`](https://github.com/taslabs-net/homeflare-kit/commit/e9912254041b1f15195f8a4cf2917e00a1194d43) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `packages/alchemy` now depends on `@distilled.cloud/proxmox`, aliased per
|
|
6
|
+
`packages/alchemy/docs/distilled-interim.md`'s interim-package route onto
|
|
7
|
+
the published `@homeflare/distilled-proxmox@0.2.0`
|
|
8
|
+
(`"npm:@homeflare/distilled-proxmox@0.2.0"` in `dependencies`, not
|
|
9
|
+
`peerDependencies` — the package needs it resolved, not left to whoever
|
|
10
|
+
installs it). This is the follow-up PR `distilled-proxmox-interim.md`
|
|
11
|
+
called out: the alias, plus `src/proxmox/distilled-task-await.test.ts`
|
|
12
|
+
(ported off draft PR 182's dev-only `link:` dependency onto the real alias,
|
|
13
|
+
assertions unchanged) proving `awaitTask`'s poll-until-exitstatus loop
|
|
14
|
+
against a fake PVE — success only on `exitstatus` exactly `"OK"`,
|
|
15
|
+
`"OK (warnings)"` fails, and a bare 400 is the non-retryable `BadRequest`
|
|
16
|
+
(that last case already lives in `packages/distilled-proxmox/src/protocol.test.ts`,
|
|
17
|
+
merged with the package itself).
|
|
18
|
+
|
|
19
|
+
No existing `packages/alchemy/src/proxmox/*` resource is touched or moved
|
|
20
|
+
onto the distilled package — that migration is still a separate, later PR.
|
|
21
|
+
Because `@homeflare/distilled-proxmox` is also a sibling workspace package,
|
|
22
|
+
bun resolves the alias to the local workspace copy rather than fetching the
|
|
23
|
+
npm tarball (same unmodified code either way); that local copy has no
|
|
24
|
+
prebuilt `dist` by default, and `tsc`'s `moduleResolution: "bundler"`
|
|
25
|
+
follows the alias's `exports["."].types` straight to `dist/index.d.ts` when
|
|
26
|
+
typechecking the new test file — `bun test` itself needs no such build
|
|
27
|
+
(it resolves the package's own `bun` export condition straight to `src`).
|
|
28
|
+
`packages/alchemy/package.json` gets a `pretypes` script
|
|
29
|
+
(`bun run --filter '@homeflare/distilled-proxmox' build`) so `bun run
|
|
30
|
+
types` — and therefore `bun run check` — builds that one dependency first;
|
|
31
|
+
nothing else changes.
|
|
32
|
+
|
|
33
|
+
## 0.27.2
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- [#180](https://github.com/taslabs-net/homeflare-kit/pull/180) [`1a85198`](https://github.com/taslabs-net/homeflare-kit/commit/1a85198349981e04f475c0e08b0aa2a995719ef4) Thanks [@taslabs-net](https://github.com/taslabs-net)! - The `forgejo/*` family (`Forgejo.Repository`, `Forgejo.BranchProtection`,
|
|
38
|
+
`Forgejo.OrgLabel`, `Forgejo.OrgTeam`, `Forgejo.RepoWebhook`,
|
|
39
|
+
`Forgejo.OrgSecret`, `Forgejo.TeamMember`) now calls
|
|
40
|
+
`@distilled.cloud/forgejo@1.0.0-rc.12`'s typed operations instead of a
|
|
41
|
+
hand-rolled `Effect HttpClient` client — `catchTag('NotFound', …)` in place
|
|
42
|
+
of a status-carrying `ForgejoError`. `client.ts` is gone; nothing else in
|
|
43
|
+
this package imported it. Credentials still resolve from `FORGEJO_URL` /
|
|
44
|
+
`FORGEJO_TOKEN` at call time, now through the package's own
|
|
45
|
+
`CredentialsFromEnv` layer. Every operation this family calls exists in the
|
|
46
|
+
package and every error it handles carries a tag, so no distilled patch was
|
|
47
|
+
needed. Props and attributes are unchanged — an adopted repository, label,
|
|
48
|
+
team, webhook, branch protection rule, org secret or team membership still
|
|
49
|
+
plans noop.
|
|
50
|
+
|
|
51
|
+
- [#179](https://github.com/taslabs-net/homeflare-kit/pull/179) [`0419d30`](https://github.com/taslabs-net/homeflare-kit/commit/0419d30860e4f6ffbf42e0e1257bd5845a00a418) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `Cloudflare.R2BucketLock` now calls `@distilled.cloud/cloudflare/r2`
|
|
52
|
+
(`getBucketLock`/`putBucketLock`) instead of the `cloudflare` npm SDK, the same
|
|
53
|
+
distilled package `MeshNode` already used — `catchTag('NoSuchBucket', …)` in
|
|
54
|
+
place of an `instanceof NotFoundError` status check. The `cloudflare` peer
|
|
55
|
+
dependency and `client.ts` are gone; nothing else in this package imported
|
|
56
|
+
them. Props, attributes and the wire body are unchanged — an adopted lock
|
|
57
|
+
still plans noop.
|
|
58
|
+
|
|
59
|
+
## 0.27.1
|
|
60
|
+
|
|
61
|
+
### Patch Changes
|
|
62
|
+
|
|
63
|
+
- [#177](https://github.com/taslabs-net/homeflare-kit/pull/177) [`615d7f0`](https://github.com/taslabs-net/homeflare-kit/commit/615d7f0f081828a64f2c334ce423324f10e13921) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `launchd-ports.md` and `port-claims.ts`'s header comment no longer cite the
|
|
64
|
+
house monorepo's path and a pinned commit as the source of the port-collision
|
|
65
|
+
rule; both now state it as the kit's own rule, matching the estate's existing
|
|
66
|
+
port-collision strictness on its own terms. No behavior change.
|
|
67
|
+
|
|
68
|
+
## 0.27.0
|
|
69
|
+
|
|
70
|
+
### Minor Changes
|
|
71
|
+
|
|
72
|
+
- [#175](https://github.com/taslabs-net/homeflare-kit/pull/175) [`7767e82`](https://github.com/taslabs-net/homeflare-kit/commit/7767e82d18e74af203ccae108b8e4f3de99be724) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add `GitHub.RepositoryRuleset`, a bridge resource under `@homeflare/alchemy/github` that
|
|
73
|
+
probes for an existing same-named ruleset by name before creating one (closing the
|
|
74
|
+
duplicate-ruleset hazard in upstream `alchemy@2.0.0-beta.79`'s `GitHub.Ruleset`) and
|
|
75
|
+
normalizes before comparing so a matching live ruleset is a true noop. Also adds
|
|
76
|
+
`declareRepoBaseline`, and rewires `declareRepoPolicy`'s ruleset half onto the new
|
|
77
|
+
resource — `builds` is the only caller, and its ruleset has never been created, so this
|
|
78
|
+
changes no live resource's identity.
|
|
79
|
+
|
|
80
|
+
Versions this was walked against: `alchemy@2.0.0-beta.79`, `@octokit/rest@22.0.1`,
|
|
81
|
+
`@octokit/openapi-types@27.0.0` (the version the REST method parameter types actually
|
|
82
|
+
resolve through — see `repository-ruleset-constraints.ts` for the version-chain note).
|
|
83
|
+
|
|
84
|
+
## 0.26.0
|
|
85
|
+
|
|
86
|
+
### Minor Changes
|
|
87
|
+
|
|
88
|
+
- [#171](https://github.com/taslabs-net/homeflare-kit/pull/171) [`66e6e03`](https://github.com/taslabs-net/homeflare-kit/commit/66e6e03060b0cebbf0e326e6f2880f196735fba7) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add `sshSudoRunner()` to `@homeflare/alchemy/linux` — the Linux twin of the launchd subpath's
|
|
89
|
+
`sudoRunner()`, for a host stack whose ssh user is not root but has passwordless sudo
|
|
90
|
+
(`(ALL) NOPASSWD: ALL`). The deploy runs as the operator, and only a fixed allowlist of absolute
|
|
91
|
+
`sudo -n` calls elevates: `install`→`mv` (GNU `install` writes through its destination, so this
|
|
92
|
+
runner stages, installs into a derived temp file, then `mv`s it into place — a rename(2), atomic
|
|
93
|
+
by construction), `rm`, `mkdir`/`chmod`/`chown`/`rmdir` for `Host.Directory`, and
|
|
94
|
+
`daemon-reload`/`enable`/`disable`/`start`/`stop`/`restart` for `Systemd.Unit` and
|
|
95
|
+
`Systemd.Timer` (gated by each unit's own `FragmentPath`, so a vendor unit like
|
|
96
|
+
`pveproxy.service` stays unreachable). Every privileged argv is logged before it runs; a plan
|
|
97
|
+
never elevates (`checkWrite` and every read stay on the operator); the host guard reads the whole
|
|
98
|
+
directory chain from `/` down to the target in one `ls -ldn` call, refusing anything not
|
|
99
|
+
root-owned, group/other-writable, ACL-flagged, or the wrong kind. See
|
|
100
|
+
`packages/alchemy/docs/linux-sudo.md` for the full argv table and what is measured versus
|
|
101
|
+
reasoned — write paths are reasoned, not run live; construction and `checkWrite` were exercised
|
|
102
|
+
read-only against a real Debian 13 / trixie host (systemd 257.13-1~deb13u1, coreutils 9.7-3),
|
|
103
|
+
confirming nothing is elevated by a plan.
|
|
104
|
+
|
|
105
|
+
homeflare-proxmox's first consumer: a script, systemd service and timer declared per Proxmox
|
|
106
|
+
node with `sshSudoRunner({ host, prefixes: ['/usr/local/bin', '/etc/systemd/system'] })`.
|
|
107
|
+
Groundwork from PR 132 (`src/linux/sudo-listing.ts`, the `ls -ldn` chain reader) is now used
|
|
108
|
+
directly by the new guard rather than left unreferenced.
|
|
109
|
+
|
|
110
|
+
## 0.25.1
|
|
111
|
+
|
|
112
|
+
### Patch Changes
|
|
113
|
+
|
|
114
|
+
- [#168](https://github.com/taslabs-net/homeflare-kit/pull/168) [`f5b8dcf`](https://github.com/taslabs-net/homeflare-kit/commit/f5b8dcf7e5875f1f5f589a73139fa58d8d886a43) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Fix `Systemd.Unit`: for a unit declared `started: false`, `ActiveState=activating` no longer
|
|
115
|
+
reads as drift. A timer-driven `Type=oneshot` service (no `[Install]`; its `.timer` starts it)
|
|
116
|
+
reports `activating` for the whole duration of its run, not an instant — so a plan taken
|
|
117
|
+
mid-run used to show `update`, and a deploy would `systemctl stop` a check that was already
|
|
118
|
+
running. Now only `ActiveState=active` counts as drift for `started: false`; `started: true`
|
|
119
|
+
(the default) is unchanged. `activating` with `SubState=auto-restart`/`auto-restart-queued` —
|
|
120
|
+
systemd's crash-restart backoff, not a fresh start — is excluded from that exemption and still
|
|
121
|
+
counts as drift, so a crash-looping unit declared `started: false` is still stopped. Both
|
|
122
|
+
`diffUnit` (unit-lifecycle.ts) and `settle` (unit-settle.ts) now share one predicate,
|
|
123
|
+
`isUnitRunning` (unit-form.ts), so they can never disagree about it.
|
|
124
|
+
|
|
125
|
+
## 0.25.0
|
|
126
|
+
|
|
127
|
+
### Minor Changes
|
|
128
|
+
|
|
129
|
+
- [#163](https://github.com/taslabs-net/homeflare-kit/pull/163) [`cf301fb`](https://github.com/taslabs-net/homeflare-kit/commit/cf301fba5196056938419140e80754dc7bf34991) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add `@homeflare/alchemy/paperless`: `Tag`, `DocumentType`, `StoragePath` and `CustomField` —
|
|
130
|
+
Paperless-ngx's taxonomy, create-or-update and never deleted by default — generated from
|
|
131
|
+
Paperless-ngx **3.1.1**'s own served OpenAPI document (API version **10**, sha256 `d0fe550d…`).
|
|
132
|
+
Types (`generated/types/*.ts`) and constraint tables (`generated/constraints/*.ts`) are both
|
|
133
|
+
generated by the new `bun codegen/paperless.ts`, which also introduces
|
|
134
|
+
`codegen/openapi-types.ts` (a component-to-TypeScript emitter over `openapi.ts`'s `JsonSchema`)
|
|
135
|
+
and `codegen/dialects.ts` (the regex-dialect-plus-anchoring table `codegen/netbox.ts` now reads
|
|
136
|
+
too, unchanged).
|
|
137
|
+
|
|
138
|
+
Every create sends `owner` (null when undeclared) — Paperless-ngx's `OwnedObjectSerializer`
|
|
139
|
+
defaults an absent `owner` to the token user, so omitting it the way an undeclared NetBox
|
|
140
|
+
foreign key is omitted would create a second, invisible row on every deploy. `read` answers
|
|
141
|
+
`Unowned` on every match rather than adopting silently (H1). A `CustomField.dataType` change is
|
|
142
|
+
refused at plan time — never a PATCH, never a replace — because either one deletes that field's
|
|
143
|
+
value on every document.
|
|
144
|
+
|
|
145
|
+
## 0.24.0
|
|
146
|
+
|
|
147
|
+
### Minor Changes
|
|
148
|
+
|
|
149
|
+
- [#157](https://github.com/taslabs-net/homeflare-kit/pull/157) [`2a69523`](https://github.com/taslabs-net/homeflare-kit/commit/2a69523410521f8373d2c216070f788df0ca2a64) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Add `LiteLLM.PassThroughEndpoint` to `@homeflare/alchemy/litellm`, the kit's first LiteLLM
|
|
150
|
+
resource: one row of LiteLLM's `/config/pass_through_endpoint` family, a route on the proxy that
|
|
151
|
+
forwards requests to an upstream target. Types are generated from LiteLLM **1.100.0**'s own
|
|
152
|
+
OpenAPI document (tag `v1.100.0`, commit `e4f25265704e2b2c6cf6e81be2e4c5cffff896f4`), dumped the
|
|
153
|
+
way the vendor's own CI dumps it — `prisma generate` against `litellm/proxy/schema.prisma`, then
|
|
154
|
+
the `dumpSpec` program embedded in `ui/litellm-dashboard/scripts/gen-api-types.mjs`, run with
|
|
155
|
+
`app.routes`' `include_in_schema` forced `True` the way the dashboard's generator does — because
|
|
156
|
+
the reference proxy's `/openapi.json` could not be reached when this was walked (jetsam restart
|
|
157
|
+
loop). Cross-checked byte-identically: regenerating with `openapi-typescript@7.13.0` reproduces
|
|
158
|
+
the tag's committed `ui/litellm-dashboard/src/lib/http/schema.d.ts` exactly, sha256
|
|
159
|
+
`8bc5d9c9…40b83f9`, 2,334,248 bytes on both sides. The dump itself is sha256
|
|
160
|
+
`1b3e4d23…4b006399f` (`codegen/manifest.json`'s `litellm-openapi` entry).
|
|
161
|
+
|
|
162
|
+
Every pass-through endpoint lives in one `general_settings.pass_through_endpoints` field — every
|
|
163
|
+
create, update and delete is a read-modify-write of the whole list — so every mutating call is
|
|
164
|
+
wrapped in a per-base-URL `Effect` semaphore, and `reconcile` reads back after writing rather
|
|
165
|
+
than trusting the call that just returned. A path already held by a `config.yaml` entry
|
|
166
|
+
(`is_from_config: true`) is refused at plan rather than silently overridden; a foreign DB row on
|
|
167
|
+
the same path is `Unowned` and needs `--adopt`; a literal secret in a forwarded `Authorization`,
|
|
168
|
+
`x-api-key` or `cf-aig-authorization` header is refused unless it carries LiteLLM's own
|
|
169
|
+
`os.environ/NAME` reference form. Clearing `timeout`, `methods` or `guardrails` is planned as a
|
|
170
|
+
replace (delete then create), because LiteLLM's update route merges with `exclude_none` and can
|
|
171
|
+
never clear an already-set field. Credentials (`LITELLM_PROXY_URL`, `LITELLM_PROXY_API_KEY` —
|
|
172
|
+
LiteLLM's own variable names) are read fresh from the environment on every call, never a prop.
|
|
173
|
+
|
|
174
|
+
Deferred: the Claude OAuth model, key and team slice, which needs estate answers only Tim can
|
|
175
|
+
give. See `docs/litellm.md`.
|
|
176
|
+
|
|
177
|
+
- [#156](https://github.com/taslabs-net/homeflare-kit/pull/156) [`6b4b58c`](https://github.com/taslabs-net/homeflare-kit/commit/6b4b58c9a207dd4351d80e514ee4b7d09cc74ea0) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Create-and-assert a database on a self-hosted cluster: `Postgres.Database` in the new
|
|
178
|
+
`@homeflare/alchemy/postgres` subpath. Walked against PostgreSQL 18.6 (`REL_18_6`, commit
|
|
179
|
+
`724edf9b`) — upstream `alchemy@2.0.0-beta.79` has vendor-API Postgres resources (Planetscale,
|
|
180
|
+
Neon, Prisma, Fly, Railway) and a runtime binding over `@effect/sql-pg`, but nothing for a
|
|
181
|
+
database you run yourself, so this builds on that same `@effect/sql-pg` `PgClient` upstream's
|
|
182
|
+
own `alchemy/SQL/Postgres` uses (new optional peer, `@effect/sql-pg@4.0.0-rc.115`).
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Alchemy changelog archive 11
|
|
2
|
+
|
|
3
|
+
[Current changelog](../../CHANGELOG.md) · [Archive index](./README.md)
|
|
4
|
+
|
|
5
|
+
Create-and-assert only: every optional prop (`encoding`, `localeProvider`, `lcCollate`,
|
|
6
|
+
`lcCtype`, `allowConnections`, `connectionLimit`, `isTemplate`, `tablespace`) is asserted once
|
|
7
|
+
at create and compared against the live row on every later plan — a mismatch is a typed
|
|
8
|
+
`PostgresDatabaseDrift` refusal, never an `ALTER DATABASE`. `name` is refused at plan past 63
|
|
9
|
+
UTF-8 bytes (`NAMEDATALEN`), because the server would otherwise silently truncate it with only
|
|
10
|
+
a `NOTICE`. A rename is refused at plan; `diff` never answers `replace` (a replace here is DROP
|
|
11
|
+
then CREATE, on data). `delete` always refuses with a typed tag and `defaultRemovalPolicy` is
|
|
12
|
+
`retain` — dropping a database stays a human act on the host. `CREATE DATABASE` takes no bind
|
|
13
|
+
parameters at all (measured at `gram.y`), so every value is quoted by hand: a single-token
|
|
14
|
+
identifier quoter for the name (deliberately NOT `alchemy`'s own `sql(value)`, which
|
|
15
|
+
dot-splits a qualified name and would break on a name containing `.`), and a string-literal
|
|
16
|
+
quoter for the rest. `read` always answers `Unowned` for a match — a database carries no
|
|
17
|
+
ownership mark, so an adopting stack needs `adopt(true)`.
|
|
18
|
+
|
|
19
|
+
Measured path (2026-09-23): only a Unix socket reaches the maintenance database on the mini —
|
|
20
|
+
no `pg_hba` rule opens it over TCP. A JS client reaches that socket: `@effect/sql-pg` under
|
|
21
|
+
Bun, live-checked from scratch space, connected and read all 24 live databases.
|
|
22
|
+
|
|
23
|
+
- [#160](https://github.com/taslabs-net/homeflare-kit/pull/160) [`26f45bd`](https://github.com/taslabs-net/homeflare-kit/commit/26f45bda0108744994c63b611ca27492aef715fe) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `Release.Binary` can now install from a directory-wrapped vendor archive. `tarReader(wanted, root)`
|
|
24
|
+
(`packages/alchemy/src/release/tar.ts`) accepts an optional `root`: exactly one declared leading
|
|
25
|
+
directory (typeflag `5`, size 0) is stripped from every entry name before it is matched, listed or
|
|
26
|
+
checked for a duplicate. Every entry outside that declared root, a second directory entry, a
|
|
27
|
+
`<root>-evil/x` sibling (a segment match, not a string prefix), and a `<root>/` entry that is not
|
|
28
|
+
an empty directory are refused whole, same as every existing refusal (PAX, GNU long-name, `..`,
|
|
29
|
+
links, devices). Without `root`, behaviour is unchanged: a directory entry — the wrapper included —
|
|
30
|
+
is still refused exactly as it always was.
|
|
31
|
+
|
|
32
|
+
`ReleaseArchive.root?: string` (`binary-form.ts`) carries the pin; `pinProblems` (split out to the
|
|
33
|
+
new `binary-pins.ts` to stay under the file's 250-line cap, re-exported so no importer moves)
|
|
34
|
+
refuses a root that is not one safe path segment. `catalogBinary` (`catalog.ts`) carries
|
|
35
|
+
`archive.root` through when a catalog entry has one. A state row from before this change has no
|
|
36
|
+
`root`, and `undefined === undefined`, so it is not treated as a moved pin; declaring or changing a
|
|
37
|
+
root is.
|
|
38
|
+
|
|
39
|
+
Measured 2026-09-23 by downloading each vendor's own GitHub release asset into a scratch directory
|
|
40
|
+
(never executed) and re-hashing: all four Prometheus-family darwin-arm64 archives —
|
|
41
|
+
`alertmanager` v0.33.1 (37,247,168 B), `blackbox_exporter` v0.28.0 (15,705,022 B), `node_exporter`
|
|
42
|
+
v1.12.1 (5,368,643 B), `prometheus-community/postgres_exporter` v0.20.1 (10,072,235 B) — recompute
|
|
43
|
+
to GitHub's own asset `digest`, wrap every entry in exactly one directory named
|
|
44
|
+
`<binary>-<version>.darwin-arm64/`, and carry no PAX or GNU long-name entries. The worktree's own
|
|
45
|
+
`tarReader(wanted, root)` was re-run against those same downloaded bytes and now parses each to
|
|
46
|
+
completion, returning the named member at its full pinned size (`docs/release-binary-catalogs.md`
|
|
47
|
+
has the full table and commands; `tar-root.test.ts` and `binary-root.test.ts` hold the same proof
|
|
48
|
+
as committed fixtures).
|
|
49
|
+
|
|
50
|
+
This unit adds the reader capability and its tests only. `VICTORIA_RELEASES` and
|
|
51
|
+
`OPENBAO_RELEASES` are unchanged — no catalog entry for alertmanager, blackbox_exporter,
|
|
52
|
+
node_exporter or postgres_exporter exists yet; that is its own data-set walk-down and PR.
|
|
53
|
+
|
|
54
|
+
### Patch Changes
|
|
55
|
+
|
|
56
|
+
- [#161](https://github.com/taslabs-net/homeflare-kit/pull/161) [`5500f3e`](https://github.com/taslabs-net/homeflare-kit/commit/5500f3e83e90b9e7a0bbad457527f994a741d0d2) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Correct the last stale claim in `Proxmox.ZfsPool`'s docs, per Tim's decision 28 (2026-09-23).
|
|
57
|
+
|
|
58
|
+
PR 154 already corrected `zfs-pool.ts`'s header, which previously said destroy was refused and
|
|
59
|
+
`delete` made no API call — false, since `destroyPool` sends a real `DELETE` under
|
|
60
|
+
`.pipe(RemovalPolicy.destroy())`, guarded only by the resource's default `retain` removal policy.
|
|
61
|
+
That fix missed one line: the file's "PRIVILEGES, FROM THE SCHEMA" paragraph still said `delete`
|
|
62
|
+
"needs nothing at all, since it calls nothing" — the same mistake, left uncorrected in a second
|
|
63
|
+
place. It now says `delete` needs `Sys.Modify` on `/`, the same as `reconcile`'s POST, per
|
|
64
|
+
`ceph-osd.ts`'s own privilege comparison (measured against the same 2026-09-13 apidoc read),
|
|
65
|
+
which names `disks/zfs` as one of the sibling families whose write verbs — not only the create —
|
|
66
|
+
carry a `Sys.Modify` check.
|
|
67
|
+
|
|
68
|
+
No behaviour change. `RemovalPolicy.destroy()` reaching a single `DELETE`, and `retain` sending
|
|
69
|
+
none, are already pinned in `zfs-pool-adopt.test.ts` (added by PR 154); this PR touches docs only.
|
|
70
|
+
|
|
71
|
+
## 0.23.0
|
|
72
|
+
|
|
73
|
+
### Minor Changes
|
|
74
|
+
|
|
75
|
+
- [#154](https://github.com/taslabs-net/homeflare-kit/pull/154) [`2044487`](https://github.com/taslabs-net/homeflare-kit/commit/2044487b81fecb635da785b0b2637daf2c5b20fe) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Export `ProxmoxApiToken` and `ProxmoxZfsPool` from `@homeflare/alchemy/proxmox`, per Tim's
|
|
76
|
+
decision 9 (2026-09-23): the kit builds an ApiToken export that is metadata-only and a ZfsPool
|
|
77
|
+
export that is adopt-only. Neither can create what it does not already have to adopt.
|
|
78
|
+
|
|
79
|
+
`ProxmoxApiToken` adopts and manages `comment`, `expire` and `privsep` — the whole of a token's
|
|
80
|
+
policy that PVE will report back — and refuses to mint a new one: the secret exists for one HTTP
|
|
81
|
+
response and then nowhere (measured from the vendor schema), and this estate's state store
|
|
82
|
+
persists attributes unencrypted, so a token created here would be a live credential nobody holds.
|
|
83
|
+
`reconcile` dies by name when the token is absent, naming the two ways to get a usable one
|
|
84
|
+
(`pveum user token add`, or OpenBao's `proxmox-c1` mount) instead. `shape()` is now typed against
|
|
85
|
+
the generated `AccessUsersUseridTokenTokenidPutParams` and `PostParams`, so a schema drift fails
|
|
86
|
+
`tsc` here rather than surfacing as a 400 on a live cluster.
|
|
87
|
+
|
|
88
|
+
`ProxmoxZfsPool`'s `devices` and `raidlevel` are now optional: omitting both declares an
|
|
89
|
+
adopt-only pool, for one PVE's own POST schema cannot fully describe — a stripe layout has no
|
|
90
|
+
`raidlevel` to declare. `ZfsRaidLevel` and `ZfsCompression` are now generated aliases of
|
|
91
|
+
`NodesNodeDisksZfsPostParams`'s fields rather than hand-typed, so PVE adding or removing a
|
|
92
|
+
`raidlevel` value is caught by `tsc`, not discovered on a live cluster. `createPool` refuses
|
|
93
|
+
before any POST when an adopt-only declaration's pool is not already there. `zfs-pool.ts`'s
|
|
94
|
+
header previously said destroy was refused and `delete` made no API call — that was false
|
|
95
|
+
(`destroyPool` sends a real `DELETE`, guarded only by the resource's default `retain` removal
|
|
96
|
+
policy); the header is corrected, and `delete` is unchanged.
|
|
97
|
+
|
|
98
|
+
Walked down against the manifest-verified `pve-apidoc` cache, pve-manager 9.2.11/f6997e698c7933ea,
|
|
99
|
+
sha256 `9def8f13611184ee1c7d0399713130dfc4a065701d0d91a69b9c03df929344e9` — re-sliced for this
|
|
100
|
+
change with `codegen/apidoc.ts`'s own parser (2026-09-23), rather than assumed from the header's
|
|
101
|
+
2026-09-13 measurement. The four endpoint definitions this change touches (`POST`/`GET`/`DELETE`
|
|
102
|
+
`/nodes/{node}/disks/zfs[/{name}]`, `GET`/`POST`/`PUT`/`DELETE`
|
|
103
|
+
`/access/users/{userid}/token/{tokenid}`) are byte-identical against the cached 9.2.4 apidoc, so
|
|
104
|
+
nothing here is pinned to a version drift between them.
|
|
105
|
+
|
|
106
|
+
### Patch Changes
|
|
107
|
+
|
|
108
|
+
- [#152](https://github.com/taslabs-net/homeflare-kit/pull/152) [`0a19365`](https://github.com/taslabs-net/homeflare-kit/commit/0a19365c9370f315e0fa0dba6700934e4a7022fc) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Fix the Proxmox type generator (`codegen/`) to read a property's own `oneOf`, not just its outer `optional`. Measured 2026-09-23 against pve-manager 9.2.11: six PVE SDN fabric request parameters and seven return fields under `/cluster/sdn/fabrics/*` (`delete`, `redistribute`, `interfaces`) are spelled `{oneOf: […], type: 'array'}` with no outer `optional`, one branch per routing protocol, every branch `optional: 1` — a property that is optional in every case the vendor states, which the generator previously read as required. `isOptional` (exported from `codegen/tsmap.ts`) now treats a property as optional when it says so itself or when every `oneOf` branch does (one branch without `optional` still keeps it required), and an array property with no `items` of its own but a `oneOf` gets its element type from the deduplicated union of each branch's own mapped `items`. `codegen/emit.ts`'s constraint-table required flag routes through the same helper, so a future fabric Resource is not refused at plan time for omitting a key the vendor never requires. Regeneration touches only `packages/alchemy/src/proxmox/generated/pve/cluster-sdn-fabrics.ts` (+23/−13); nothing in `packages/` imports its types yet. `codegen/TYPES.md` records what stays unclaimed: the branches' own per-protocol `instance-types` are not read, so the element type is a superset across protocols rather than a discriminated union — that decision belongs to the `SdnFabric`/`SdnFabricNode` family this unblocks.
|
|
109
|
+
|
|
110
|
+
## 0.22.0
|
|
111
|
+
|
|
112
|
+
### Minor Changes
|
|
113
|
+
|
|
114
|
+
- [#148](https://github.com/taslabs-net/homeflare-kit/pull/148) [`2a8fc6e`](https://github.com/taslabs-net/homeflare-kit/commit/2a8fc6ef5f634c52a7dfa59f3f0e3464d83aea7c) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Refuse two of a stack's own launchd jobs sharing one port, before either is declared: `@homeflare/alchemy/launchd` now exports `claimPorts(claims)` and the pure `portClaimProblems(claims)` it runs on. Like `catalogBinary()`, this is a plain function the stack program calls itself (a provider never sees its sibling resources, and never diffs a first create whose props still hold an unresolved `Output` — every mini job's does), so a colliding declaration fails the plan before anything is fetched, written or `launchctl`'d. It keys on the port number alone, mirroring `lib-ports.nix`'s `assertNoCollision` in the house repo, the check it replaces for a job once that job moves off Nix — address and protocol are not part of the key, matched against that registry's own strictness rather than a new, looser rule. Walked against Darwin 27.2.0 (macOS 27.2): a wildcard bind and a specific-address bind on the same port both succeed at the OS level (measured with SO_REUSEADDR, which Go sets on every darwin listener unconditionally), so `claimPorts` refuses that pairing deliberately rather than relying on the OS to catch it. It sees only the claims a stack passes it — a forgotten job, or a daemon still on Nix, stays invisible until its port is added as a claim.
|
|
115
|
+
|
|
116
|
+
- [#149](https://github.com/taslabs-net/homeflare-kit/pull/149) [`6b8d88f`](https://github.com/taslabs-net/homeflare-kit/commit/6b8d88f9d3b68286f2d83a64358ecb1e84909629) Thanks [@taslabs-net](https://github.com/taslabs-net)! - `@homeflare/alchemy/release` gets its second vendor data set, `OPENBAO_RELEASES`: OpenBao 2.6.2 for darwin_arm64, walked down 2026-09-23 (`docs/release-binary-openbao.md` has every command). OpenBao's `checksums.txt` lists archives and SBOMs only, never the `bao` binary inside the archive — the first vendor the kit has pinned where the member digest is not a vendor fact. `catalog.ts` gains an optional `computed` record per archive for exactly that case (hashed from an archive whose own SHA-256 already matched the pin, filed separately from `members` and never both at once — a member pinned in both is refused) and an optional `checksums.signature` record for a vendor that publishes a detached signature over its checksum file; `catalogProblems`, `catalogBinary` and `identifyBinary` all read vendor digests first, then computed ones. Both fields are optional and `VICTORIA_RELEASES` is unchanged, so nothing that already builds against this package needs to change. The `checksums.txt` signature was checked once with `gpgv` against the key published at openbao.org; the accompanying Sigstore bundle was read but not verified (`cosign` is not installed where this was walked down), and is recorded as an unverified identity rather than a passing check. `docs/release-binary.md`'s "Declaring one" example also gets a real bug fixed: `ReleaseBinary('vmalert')` followed by `LaunchdJob('vmalert')` shares one alchemy@2.0.0-beta.79 FQN, so the second declaration silently returns the first resource instead of registering a job — the example now uses distinct ids and says why.
|
|
117
|
+
|
|
118
|
+
## 0.21.0
|
|
119
|
+
|
|
120
|
+
### Minor Changes
|
|
121
|
+
|
|
122
|
+
- [#138](https://github.com/taslabs-net/homeflare-kit/pull/138) [`ef4108a`](https://github.com/taslabs-net/homeflare-kit/commit/ef4108a54c3700485cedb102833f16da54f02d3e) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Install vendor binaries from pinned release archives: `@homeflare/alchemy/release`. `ReleaseBinary` puts one binary out of a GitHub release archive into a directory the stack declares, and installs nothing else. The pinned archive is its props — repository, tag, exact asset name, size, the archive's SHA-256, the archive member and the member's own SHA-256 — so the resource knows no vendor. Each vendor's pinned versions are data kept beside it; `VICTORIA_RELEASES` is the first set, covering `victoria-metrics`, `victoria-logs`, `victoria-traces` and the `vmutils` tools (`vmagent`, `vmalert`, …) at the versions the Mac host runs, each digest copied from the vendor's checksum file with its URL and date. `catalogBinary(VICTORIA_RELEASES, { package, version, platform, binary })` turns an entry into props, and refuses a version the data set does not pin — in the stack program, so the plan fails before anything is fetched. Every pin must be a plain value in the stack program: one wired from another resource's Output (a checksum file read during the deploy) is refused before any request, on a first deploy too. Every download is checked twice: the archive before anything is unpacked, then the binary before it is written. Asset names are matched exactly, so the `-enterprise` and `-cluster` archives next to them are never picked. Only the declared member is extracted, and an archive holding any link, `..` or absolute entry is refused whole. The bytes reach the host only through the existing `HostRunner` write. A new pin at the same path is refused rather than overwriting a running binary; give each version its own directory (`catalogDirectory`). A binary someone already placed is recognised by its SHA-256, never by running it, and is taken over only under `--adopt`, without a download — at apply too, where the plan could not ask. A file with other bytes at the path is never taken over, `--adopt` or not, so a plan never prints `adopted` for a binary it would overwrite. A renamed declaration keeps its binary: declare the rename with `renamedFrom()`, and under `--adopt` the old name's delete leaves a path the new name installed in the same deploy. It never starts anything: the stack's job puts the binary's `path` in its argv. `HostFile` now shares its whole-file convergence with it, with no change in behaviour.
|
|
123
|
+
|
|
124
|
+
### Patch Changes
|
|
125
|
+
|
|
126
|
+
- [#138](https://github.com/taslabs-net/homeflare-kit/pull/138) [`ef4108a`](https://github.com/taslabs-net/homeflare-kit/commit/ef4108a54c3700485cedb102833f16da54f02d3e) Thanks [@taslabs-net](https://github.com/taslabs-net)! - A `HostFile` or `ReleaseBinary` whose path is respelled to the same file — through a symlinked parent such as `/etc` and `/private/etc`, or by case alone on case-insensitive APFS — is now planned as an `update` that keeps the file. Before, it was planned as a `replace` whose cleanup deleted the old path, which was the same file: the deploy succeeded and the file was gone until the next one. `HostRunner.stat` may now report `dev` and `ino` (the local runner does); a runner that does not gets a check after the move, so a lost file fails the deploy instead. A create whose read-back throws (not only one that mismatches) is now removed. `ReleaseBinary` also refuses a mode its owner cannot read, and a directory that group or other may write. It refuses, too, a second resource installing the same path in one deploy: two owners of one file meant that dropping either one deleted the file the other still declared.
|
|
127
|
+
|
|
128
|
+
## 0.20.0
|
|
129
|
+
|
|
130
|
+
### Minor Changes
|
|
131
|
+
|
|
132
|
+
- [#139](https://github.com/taslabs-net/homeflare-kit/pull/139) [`70e8899`](https://github.com/taslabs-net/homeflare-kit/commit/70e8899fe70cf5426bcbf11b498a658b60f9488b) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Export `ProxmoxCephDaemon`, `ProxmoxCephFs` and `ProxmoxCephOsd` from `@homeflare/alchemy/proxmox`, so a stack can adopt a live cluster's Ceph monitors, managers, metadata servers, CephFS and OSDs. Each is adopt-only by shape: none has an update path, the daemon and filesystem compare nothing so they can never plan a replace, and an OSD is created only when `dev` is declared. All three retain on destroy, so removing a declaration drops its state row and never sends a DELETE. `ProxmoxCephFlag` stays Provider-only on purpose: a declared flag reasserts a maintenance toggle such as `noout` on every deploy.
|
|
133
|
+
|
|
134
|
+
### Patch Changes
|
|
135
|
+
|
|
136
|
+
- [#135](https://github.com/taslabs-net/homeflare-kit/pull/135) [`925454b`](https://github.com/taslabs-net/homeflare-kit/commit/925454b5d9fbefea061a3a204e4b0093a79c0bdc) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Two ways a `Remote.File` managed region could break its own promise, and a systemd rename that
|
|
137
|
+
refused too late. All three found by reviewing the merged diff and reproduced against the fake
|
|
138
|
+
Linux host before anything was changed.
|
|
139
|
+
|
|
140
|
+
🔴 **A REGION RENAME LEFT THE OLD BLOCK IN THE FILE FOREVER.** Only `path` was identity, so changing
|
|
141
|
+
`region.name` — or its `comment` token, which is part of the marker line — planned a routine
|
|
142
|
+
`update`: the new markers were spliced in, the old ones were never touched, and `delete` could only
|
|
143
|
+
ever look for the name in state, which was now the new one. Reproduced: a vendor file ended up
|
|
144
|
+
carrying two `BEGIN` blocks and destroying the resource removed one of them. For the named
|
|
145
|
+
consumers that is two `anchor` lines in a packet filter and a duplicate entry in a host table, with
|
|
146
|
+
nothing in the stack able to take either back. A rename is now a MOVE: the new block is written and
|
|
147
|
+
verified, then the old one is removed, and the stored digest is re-read afterwards so it describes
|
|
148
|
+
the file that is actually there.
|
|
149
|
+
|
|
150
|
+
🔴 **DROPPING `region` TOOK OVER A FILE THIS RESOURCE DID NOT OWN.** Same cause, worse effect: the
|
|
151
|
+
plan said `update` and the apply replaced every byte of the other owner's file with this resource's
|
|
152
|
+
few lines. That is the one thing the managed-region design exists to make impossible. A flip between
|
|
153
|
+
owning the whole file and owning a block — in either direction, at the same path — is now a
|
|
154
|
+
PLAN-TIME REFUSAL, because neither order is safe: writing the whole file first destroys the other
|
|
155
|
+
owner's bytes before anything can be undone, and removing the block first destroys our own claim and
|
|
156
|
+
then refuses. Destroy the resource and declare a new one.
|
|
157
|
+
|
|
158
|
+
🔴 **A `Systemd.Unit` RENAME WHOSE `content` WAS STILL AN OUTPUT WAS NOT CHECKED AT ALL.** The
|
|
159
|
+
resolved rename is checked in the plan since the systemd preflight; the branch `diffHandler` takes
|
|
160
|
+
while `content` is unresolved — exactly the deploy that templates a rendered config's digest into
|
|
161
|
+
the unit — still returned `{ action: 'replace', deleteFirst: true }` with no check, and Alchemy
|
|
162
|
+
deletes the old unit BEFORE reconciling the new one. A rename onto a masked name therefore took the
|
|
163
|
+
service down and only then refused. The half of the check that needs only the new name — the old
|
|
164
|
+
unit deletable, the new one writable and not masked — now runs there too. ⛔ This forbids nothing
|
|
165
|
+
that used to work: the identical refusal was always going to fire in reconcile, just later and with
|
|
166
|
+
nothing running.
|
|
167
|
+
|
|
168
|
+
- [#142](https://github.com/taslabs-net/homeflare-kit/pull/142) [`1905f18`](https://github.com/taslabs-net/homeflare-kit/commit/1905f18c30f7ae2a6d7294d588bf18396b64f4ed) Thanks [@taslabs-net](https://github.com/taslabs-net)! - Two new docs, and no code change.
|
|
169
|
+
|
|
170
|
+
`docs/provider-standard.md` is the kit-side statement of the house standard for a custom
|
|
171
|
+
Alchemy provider. It keeps the standard's rule numbers, and each rule is cited upstream at
|
|
172
|
+
`alchemy@2.0.0-beta.79`. It covers four things. First, use upstream's resource when one
|
|
173
|
+
exists. Second, route every vendor call through `@distilled.cloud/<vendor>` when that
|
|
174
|
+
package exists. Third, use Alchemy's own helpers (`alchemy/Util/sha256`, `Util/poll`,
|
|
175
|
+
`Util/AtomicFile`, `Diff`, `Tags`, `PhysicalName`, `AdoptPolicy`, `Auth` and `Test/Bun`)
|
|
176
|
+
rather than house copies. Fourth, keep `src/**` provider code runtime-portable, because
|
|
177
|
+
this package builds with `--target node`, while tests, fakes, scripts and codegen stay
|
|
178
|
+
Bun-native. The page also records, per family, the vendor version each one was walked
|
|
179
|
+
against and where that record lives. Six families record it only in prose.
|
|
180
|
+
|
|
181
|
+
`docs/upstream-conformance.md` is the audit of every family against that standard, as a
|
|
182
|
+
ranked ledger. It was measured read-only on `925454b`. The findings, in rank order:
|