@homeflare/alchemy 0.20.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/dist/caddy/access-forward-auth.d.ts +56 -0
- package/dist/caddy/access-forward-auth.d.ts.map +1 -0
- package/dist/caddy/index.d.ts +6 -0
- package/dist/caddy/index.d.ts.map +1 -1
- package/dist/caddy/index.js +169 -75
- package/dist/caddy/index.js.map +10 -7
- package/dist/cloudflare/mcp-server-api.d.ts +135 -0
- package/dist/cloudflare/mcp-server-api.d.ts.map +1 -0
- package/dist/cloudflare/mcp-server-form.d.ts +116 -0
- package/dist/cloudflare/mcp-server-form.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/launchd/file-converge.d.ts +70 -0
- package/dist/launchd/file-converge.d.ts.map +1 -0
- package/dist/launchd/file-identity.d.ts +32 -0
- package/dist/launchd/file-identity.d.ts.map +1 -0
- package/dist/launchd/host-file-form.d.ts +2 -0
- package/dist/launchd/host-file-form.d.ts.map +1 -1
- package/dist/launchd/host-file-lifecycle.d.ts +11 -24
- package/dist/launchd/host-file-lifecycle.d.ts.map +1 -1
- package/dist/launchd/host-file.d.ts.map +1 -1
- package/dist/launchd/index.js +173 -120
- package/dist/launchd/index.js.map +12 -10
- package/dist/launchd/local-runner.d.ts.map +1 -1
- package/dist/launchd/runner.d.ts +17 -2
- package/dist/launchd/runner.d.ts.map +1 -1
- package/dist/launchd/sudo-runner.d.ts +11 -0
- package/dist/launchd/sudo-runner.d.ts.map +1 -1
- package/dist/linux/index.js.map +3 -3
- package/dist/linux/sudo-listing.d.ts +47 -0
- package/dist/linux/sudo-listing.d.ts.map +1 -0
- package/dist/proxmox/credential-literals.d.ts +1 -1
- package/dist/proxmox/credential-literals.d.ts.map +1 -1
- package/dist/proxmox/generated/pbs/nodes-node.d.ts +1 -1
- package/dist/proxmox/generated/pbs/nodes-node.d.ts.map +1 -1
- package/dist/proxmox/generated/pbs.d.ts +7 -0
- package/dist/proxmox/generated/pbs.d.ts.map +1 -1
- package/dist/proxmox/generated/pve.d.ts +7 -0
- package/dist/proxmox/generated/pve.d.ts.map +1 -1
- package/dist/proxmox/index.d.ts +1 -1
- package/dist/proxmox/index.d.ts.map +1 -1
- package/dist/proxmox/index.js +2 -2
- package/dist/proxmox/index.js.map +9 -9
- package/dist/proxmox/pbs-notification-target-form.d.ts +1 -1
- package/dist/proxmox/pbs-notification-target-form.d.ts.map +1 -1
- package/dist/proxmox/pbs-notification-target.d.ts +1 -1
- package/dist/proxmox/pbs-notification-target.d.ts.map +1 -1
- package/dist/release/archive.d.ts +11 -0
- package/dist/release/archive.d.ts.map +1 -0
- package/dist/release/binary-claim.d.ts +53 -0
- package/dist/release/binary-claim.d.ts.map +1 -0
- package/dist/release/binary-diff.d.ts +38 -0
- package/dist/release/binary-diff.d.ts.map +1 -0
- package/dist/release/binary-form.d.ts +118 -0
- package/dist/release/binary-form.d.ts.map +1 -0
- package/dist/release/binary-lifecycle.d.ts +55 -0
- package/dist/release/binary-lifecycle.d.ts.map +1 -0
- package/dist/release/binary-read.d.ts +11 -0
- package/dist/release/binary-read.d.ts.map +1 -0
- package/dist/release/binary.d.ts +68 -0
- package/dist/release/binary.d.ts.map +1 -0
- package/dist/release/catalog.d.ts +76 -0
- package/dist/release/catalog.d.ts.map +1 -0
- package/dist/release/checksums.d.ts +22 -0
- package/dist/release/checksums.d.ts.map +1 -0
- package/dist/release/declared-pins.d.ts +4 -0
- package/dist/release/declared-pins.d.ts.map +1 -0
- package/dist/release/download.d.ts +44 -0
- package/dist/release/download.d.ts.map +1 -0
- package/dist/release/fake-deploy.d.ts +48 -0
- package/dist/release/fake-deploy.d.ts.map +1 -0
- package/dist/release/fake-engine.d.ts +8 -0
- package/dist/release/fake-engine.d.ts.map +1 -0
- package/dist/release/fake-install.d.ts +39 -0
- package/dist/release/fake-install.d.ts.map +1 -0
- package/dist/release/fake-provider.d.ts +58 -0
- package/dist/release/fake-provider.d.ts.map +1 -0
- package/dist/release/fake-release.d.ts +95 -0
- package/dist/release/fake-release.d.ts.map +1 -0
- package/dist/release/index.d.ts +24 -0
- package/dist/release/index.d.ts.map +1 -0
- package/dist/release/index.js +1365 -0
- package/dist/release/index.js.map +35 -0
- package/dist/release/providers.d.ts +19 -0
- package/dist/release/providers.d.ts.map +1 -0
- package/dist/release/refused.d.ts +39 -0
- package/dist/release/refused.d.ts.map +1 -0
- package/dist/release/tar.d.ts +14 -0
- package/dist/release/tar.d.ts.map +1 -0
- package/dist/release/victoria.d.ts +36 -0
- package/dist/release/victoria.d.ts.map +1 -0
- package/dist/{proxmox → secrets}/write-only.d.ts.map +1 -1
- package/docs/release-binary-catalogs.md +113 -0
- package/docs/release-binary-measured.md +163 -0
- package/docs/release-binary-refusals.md +57 -0
- package/docs/release-binary-upstream.md +163 -0
- package/docs/release-binary.md +154 -0
- package/package.json +5 -1
- package/src/caddy/access-forward-auth.test.ts +63 -0
- package/src/caddy/access-forward-auth.ts +120 -0
- package/src/caddy/index.ts +10 -0
- package/src/caddy/providers.test.ts +4 -0
- package/src/cloudflare/mcp-server-api.ts +294 -0
- package/src/cloudflare/mcp-server-form.ts +219 -0
- package/src/index.ts +1 -0
- package/src/launchd/file-converge.ts +233 -0
- package/src/launchd/file-identity.ts +69 -0
- package/src/launchd/host-file-alias.test.ts +65 -0
- package/src/launchd/host-file-form.ts +2 -1
- package/src/launchd/host-file-lifecycle.ts +25 -144
- package/src/launchd/host-file.ts +8 -2
- package/src/launchd/local-runner.test.ts +12 -1
- package/src/launchd/local-runner.ts +2 -0
- package/src/launchd/runner.ts +17 -2
- package/src/launchd/sudo-runner.ts +11 -0
- package/src/linux/sudo-listing.ts +122 -0
- package/src/proxmox/credential-literals.ts +1 -1
- package/src/proxmox/generated/pbs/nodes-node.ts +1 -1
- package/src/proxmox/generated/pbs.ts +7 -0
- package/src/proxmox/generated/pve.ts +7 -0
- package/src/proxmox/index.ts +1 -1
- package/src/proxmox/pbs-notification-target-form.ts +1 -1
- package/src/proxmox/pbs-notification-target-lifecycle.ts +1 -1
- package/src/proxmox/pbs-notification-target-wire.ts +1 -1
- package/src/proxmox/pbs-notification-target.ts +1 -1
- package/src/release/adopt-parity.test.ts +106 -0
- package/src/release/apply-pins.test.ts +128 -0
- package/src/release/archive.ts +94 -0
- package/src/release/binary-alias.test.ts +109 -0
- package/src/release/binary-claim.ts +80 -0
- package/src/release/binary-claims.test.ts +47 -0
- package/src/release/binary-diff.ts +73 -0
- package/src/release/binary-form.ts +229 -0
- package/src/release/binary-lifecycle.test.ts +90 -0
- package/src/release/binary-lifecycle.ts +202 -0
- package/src/release/binary-read.ts +55 -0
- package/src/release/binary-refusals.test.ts +192 -0
- package/src/release/binary-sudo.test.ts +93 -0
- package/src/release/binary.ts +181 -0
- package/src/release/catalog.test.ts +96 -0
- package/src/release/catalog.ts +175 -0
- package/src/release/checksums.test.ts +35 -0
- package/src/release/checksums.ts +43 -0
- package/src/release/declared-pins.ts +39 -0
- package/src/release/download.test.ts +138 -0
- package/src/release/download.ts +110 -0
- package/src/release/error-channel.test.ts +69 -0
- package/src/release/fake-deploy.ts +87 -0
- package/src/release/fake-engine.ts +50 -0
- package/src/release/fake-install.ts +59 -0
- package/src/release/fake-provider.ts +68 -0
- package/src/release/fake-release.ts +185 -0
- package/src/release/fixtures/victoria/darwin-arm64-assets.txt +22 -0
- package/src/release/fixtures/victoria/victoria-logs-darwin-arm64-v1.52.0_checksums.txt +2 -0
- package/src/release/fixtures/victoria/victoria-metrics-darwin-arm64-v1.151.0_checksums.txt +2 -0
- package/src/release/fixtures/victoria/victoria-traces-darwin-arm64-v0.10.0_checksums.txt +2 -0
- package/src/release/fixtures/victoria/vmutils-darwin-arm64-v1.151.0_checksums.txt +8 -0
- package/src/release/index.ts +41 -0
- package/src/release/plan.test.ts +189 -0
- package/src/release/provider-refusals.test.ts +48 -0
- package/src/release/providers.test.ts +174 -0
- package/src/release/providers.ts +24 -0
- package/src/release/recovery-read.test.ts +44 -0
- package/src/release/refused.ts +40 -0
- package/src/release/tar.test.ts +142 -0
- package/src/release/tar.ts +195 -0
- package/src/release/victoria.test.ts +108 -0
- package/src/release/victoria.ts +154 -0
- package/src/{proxmox → secrets}/write-only.ts +6 -0
- /package/dist/{proxmox → secrets}/write-only.d.ts +0 -0
- /package/src/{proxmox → secrets}/write-only.test.ts +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VictoriaMetrics' release archives, pinned — the first data set for Release.Binary, and vendor
|
|
3
|
+
* facts only: every digest copied from the vendor's own checksum file for that archive and
|
|
4
|
+
* cross-checked against GitHub's asset digest ON THE RECORDED DATE. Nothing here names a host, a
|
|
5
|
+
* path, a user or an estate. victoria.test.ts holds every pin to the committed checksum files
|
|
6
|
+
* (fixtures/victoria/) — ⚠️ consistency, not provenance: it cannot re-read GitHub, so a fixture
|
|
7
|
+
* edited together with its pins passes, and review is the gate for that.
|
|
8
|
+
*
|
|
9
|
+
* ★ PINNED IN CODE, NOT FETCHED AT APPLY. The checksum file is published by the same account, on
|
|
10
|
+
* the same release, over the same channel as the archive it describes — and these releases are
|
|
11
|
+
* MUTABLE (`"immutable": false` on v1.151.0, v1.52.0 and v0.10.0, measured 2026-09-22; the
|
|
12
|
+
* vendor's own release Makefile says to delete an asset in the UI to re-upload it). Whoever can
|
|
13
|
+
* swap an archive can swap its checksum file in the same minute, and a provider that trusts
|
|
14
|
+
* whatever checksum file it finds would install the new bytes and call them verified. A pin
|
|
15
|
+
* committed here changes only through a reviewed kit release, so a swapped asset is a REFUSAL
|
|
16
|
+
* that names the digest it expected. It also means an apply makes one request, not two, and a
|
|
17
|
+
* plan knows exactly which bytes it will install without touching the network.
|
|
18
|
+
* ⚠️ TRUST ROOT. At apply, these pins alone: the transport (TLS, the redirect, the CDN) can fail
|
|
19
|
+
* a download, never pass other bytes. The pins' own root is the GitHub release over TLS as read
|
|
20
|
+
* on RECORDED (trust on first use), uploaded from a maintainer's personal account.
|
|
21
|
+
* VictoriaMetrics publishes no signature, cosign bundle, SLSA provenance or GitHub artifact
|
|
22
|
+
* attestation for these archives (measured 2026-09-22: no .sig/.asc/.pem/.bundle assets; the
|
|
23
|
+
* attestations API answers 404 for all four digests, next to a positive control that answers
|
|
24
|
+
* 200). A checksum file proves the download is not corrupt — integrity — not who built it.
|
|
25
|
+
* ⛔ A NEW VERSION IS A NEW ENTRY, WALKED DOWN THE SAME WAY — docs/release-binary-catalogs.md.
|
|
26
|
+
* Never `latest`, never sorted by publish date: VictoriaLogs v1.51.1 was published AFTER v1.52.0.
|
|
27
|
+
* ⛔ EXACT NAMES. `-enterprise`, `-cluster` and `-enterprise-cluster` archives sit beside each of
|
|
28
|
+
* these with every prefix in common, so each asset name below is written out whole — never
|
|
29
|
+
* composed from a package and a version, which is how a template drifts onto a sibling.
|
|
30
|
+
* ★ THE VENDOR'S `-prod` SUFFIX IS DROPPED ON PURPOSE: every member is `<binary>-prod`, and
|
|
31
|
+
* installing it as `<binary>` keeps a job's argv[0], `ps` and `pgrep` names identical to the
|
|
32
|
+
* builds it replaces. The map is written out per package, never derived.
|
|
33
|
+
*/
|
|
34
|
+
import type { ReleaseCatalog } from './catalog.ts';
|
|
35
|
+
export declare const VICTORIA_RELEASES: ReleaseCatalog;
|
|
36
|
+
//# sourceMappingURL=victoria.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"victoria.d.ts","sourceRoot":"","sources":["../../src/release/victoria.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAKnD,eAAO,MAAM,iBAAiB,EAAE,cAmH/B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-only.d.ts","sourceRoot":"","sources":["../../src/
|
|
1
|
+
{"version":3,"file":"write-only.d.ts","sourceRoot":"","sources":["../../src/secrets/write-only.ts"],"names":[],"mappings":"AAiCA,+FAA+F;AAC/F,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,+FAA+F;AAC/F,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC;AAEvE;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,SACf,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,QAClC,WAAW,KACf;IAAE,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAS1F,CAAC;AAqBF;;;;;;GAMG;AACH,eAAO,MAAM,IAAI,WAAY,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,SAAS,MAAM,KAAG,MAI9E,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,WAAY,MAAM,UAAU,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,KAAG,OAStF,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Release binaries — the data sets, and adding one
|
|
2
|
+
|
|
3
|
+
Status: active
|
|
4
|
+
Verified: 2026-09-22
|
|
5
|
+
|
|
6
|
+
`Release.Binary` takes one pinned archive as props and knows no vendor. What
|
|
7
|
+
each vendor published lives in a **data set** beside it (`src/release/<vendor>.ts`),
|
|
8
|
+
typed `ReleaseCatalog`, and `catalogBinary(dataSet, request)` turns one entry
|
|
9
|
+
into props. This page is what is in the data sets, and the rule for adding to
|
|
10
|
+
them. The guide is [release-binary.md](./release-binary.md).
|
|
11
|
+
|
|
12
|
+
## `VICTORIA_RELEASES` — the only data set today
|
|
13
|
+
|
|
14
|
+
| package | version | asset (darwin-arm64) | binaries (installed ← member) |
|
|
15
|
+
| ------------------ | ------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
|
|
16
|
+
| `victoria-metrics` | 1.151.0 | `victoria-metrics-darwin-arm64-v1.151.0.tar.gz` | `victoria-metrics` ← `victoria-metrics-prod` |
|
|
17
|
+
| `victoria-logs` | 1.52.0 | `victoria-logs-darwin-arm64-v1.52.0.tar.gz` | `victoria-logs` ← `victoria-logs-prod` |
|
|
18
|
+
| `victoria-traces` | 0.10.0 | `victoria-traces-darwin-arm64-v0.10.0.tar.gz` | `victoria-traces` ← `victoria-traces-prod` |
|
|
19
|
+
| `vmutils` | 1.151.0 | `vmutils-darwin-arm64-v1.151.0.tar.gz` | `vmagent`, `vmalert`, `vmalert-tool`, `vmauth`, `vmbackup`, `vmctl`, `vmrestore`, each ← `<name>-prod` |
|
|
20
|
+
|
|
21
|
+
These are the versions the Mac host runs today, pinned for parity first. An
|
|
22
|
+
upgrade (VictoriaMetrics 1.152.0, VictoriaTraces 0.11.1 exist) is its own
|
|
23
|
+
change that re-pins both digest layers.
|
|
24
|
+
|
|
25
|
+
- ⛔ **The single-node `victoria-metrics` archive**, not `-cluster`, which holds
|
|
26
|
+
vminsert, vmselect and vmstorage instead.
|
|
27
|
+
- ★ **The `-prod` suffix is dropped on purpose.** The installed names keep a
|
|
28
|
+
job's argv[0], `ps` and `pgrep` names identical to the builds they replace.
|
|
29
|
+
The map is written out per package, never derived.
|
|
30
|
+
- **vmalert-logs is not a binary.** It is `vmalert` run with
|
|
31
|
+
`--rule.defaultRuleType=vlogs`: one `ReleaseBinary`, two jobs.
|
|
32
|
+
- **A consumer's path.** `catalogDirectory(root, request)` plus the name gives
|
|
33
|
+
`<root>/<package>-<version>/<binary>`, e.g.
|
|
34
|
+
`<root>/vmutils-1.151.0/vmalert`. That is the layout homeflare-mini's
|
|
35
|
+
`binaryPath()` already renders, so its argv and goldens need no path change.
|
|
36
|
+
A stack that keeps its own digest slots (the mini's `PINS`) can compare them
|
|
37
|
+
with `catalogBinary(...).sha256` in its own tests: two witnesses, one source.
|
|
38
|
+
|
|
39
|
+
## Adding a version of a vendor already here
|
|
40
|
+
|
|
41
|
+
Walk it down from the vendor, never from memory:
|
|
42
|
+
|
|
43
|
+
1. `gh api repos/<owner>/<repo>/releases/tags/<tag>`: the exact asset names,
|
|
44
|
+
sizes and `digest`s, and `immutable`.
|
|
45
|
+
2. Fetch the checksum file. Its SHA-256 must equal GitHub's digest of it, and
|
|
46
|
+
its archive line must equal the archive's digest.
|
|
47
|
+
3. Stream the archive through `tar -tvzf -`, saving nothing: root-level
|
|
48
|
+
regular files with the expected member names, nothing else.
|
|
49
|
+
4. Add the entry with `recorded` set to that day. Commit the checksum file
|
|
50
|
+
under `src/release/fixtures/<vendor>/` and add the asset names to the
|
|
51
|
+
listing there. `<vendor>.test.ts` then holds the pins to it.
|
|
52
|
+
5. Re-check attestations with a positive control, so a 404 means something:
|
|
53
|
+
`gh api repos/<owner>/<repo>/attestations/sha256:<digest>` next to a repo
|
|
54
|
+
known to publish them (`cli/cli` answered 2 on 2026-09-22).
|
|
55
|
+
|
|
56
|
+
⛔ Never `latest`, and never sort by publish date. VictoriaLogs v1.51.1 was
|
|
57
|
+
published after v1.52.0, and VictoriaMetrics interleaves LTS lines.
|
|
58
|
+
|
|
59
|
+
## Adding a vendor
|
|
60
|
+
|
|
61
|
+
⛔ **Only after that vendor's own walk-down measures two things**, recorded in
|
|
62
|
+
its data module's header with the date:
|
|
63
|
+
|
|
64
|
+
- **Its checksum format, byte-exact.** `checksums.ts` parses one measured shape,
|
|
65
|
+
`sha256sum` text mode listing the archive and every member. Most vendors the
|
|
66
|
+
estate census found (landscape PR 88) list **archives only**: Prometheus's
|
|
67
|
+
`sha256sums.txt`, vector's `-SHA256SUMS`, pyroscope's and OpenBao's
|
|
68
|
+
`checksums.txt`. For those the member digest is not a vendor fact. It is
|
|
69
|
+
computed from a verified archive, and the data set must say so rather than
|
|
70
|
+
file it beside the vendor's lines. That field does not exist yet; it arrives
|
|
71
|
+
with its first consumer.
|
|
72
|
+
- **Its binary's linkage** (`otool -L` on the extracted member, or `ldd`).
|
|
73
|
+
Vendor binaries were measured self-contained **only for the five Victoria
|
|
74
|
+
ones** (system libraries and `/usr/lib/libresolv.9.dylib` only). That is not
|
|
75
|
+
a property of "a Go binary". The Nix vector links jemalloc, librdkafka,
|
|
76
|
+
oniguruma and libiconv from `/nix/store`, and whether the vendor tarball
|
|
77
|
+
bundles them is unknown until someone inspects the extracted member.
|
|
78
|
+
|
|
79
|
+
Also carried over from the Victoria walk-down, and checked again per vendor:
|
|
80
|
+
exact asset names against near-identical siblings; the tar layout; the
|
|
81
|
+
`immutable` flag; signatures and attestations with a positive control; and
|
|
82
|
+
where `-version` prints, if anything ever runs it (Victoria's goes to stderr).
|
|
83
|
+
|
|
84
|
+
The queue, from the census, each **not yet walked for this resource**:
|
|
85
|
+
alertmanager, blackbox_exporter, node_exporter and postgres_exporter
|
|
86
|
+
(Prometheus `sha256sums.txt`), unpoller (detached `.sig`; 5.2.7 renames
|
|
87
|
+
`darwin_arm64` to `darwin_all`), pyroscope, vector, and `bao` (OpenBao, with
|
|
88
|
+
GPG and Sigstore to check).
|
|
89
|
+
|
|
90
|
+
⚠️ REASONED NOT MEASURED: the Prometheus-family archives wrap their files in a
|
|
91
|
+
directory, and `tar.ts` refuses a directory entry (and any PAX header) for the
|
|
92
|
+
whole archive. Walking those down includes a reader change, not only a data
|
|
93
|
+
set ([release-binary-upstream.md](./release-binary-upstream.md), gap 11).
|
|
94
|
+
|
|
95
|
+
## What this resource is not for
|
|
96
|
+
|
|
97
|
+
- **A whole tree.** Grafana needs its homepath and plugins, and an interpreter
|
|
98
|
+
like python-build-standalone needs its `lib/`. That is a **tree mode**:
|
|
99
|
+
unpack an archive into a directory. It is a separate, later capability with
|
|
100
|
+
its own walk-down, not a flag bent onto `Release.Binary`. Grafana's darwin
|
|
101
|
+
builds also come from grafana.com's downloads API, not a GitHub release. (The
|
|
102
|
+
house's stdlib-only Python jobs are tracked for a Bun/TS rewrite instead of a
|
|
103
|
+
vendored interpreter, decided 2026-09-23.)
|
|
104
|
+
- **Another source kind.** Only GitHub release downloads, whose URL is
|
|
105
|
+
`https://github.com/<repo>/releases/download/<tag>/<asset>`. A second kind
|
|
106
|
+
(grafana.com JSON, Codeberg) arrives with its first consumer.
|
|
107
|
+
- **Another archive format.** Only `.tar.gz`/`.tgz`. A `.zip` or a bare
|
|
108
|
+
binary is refused at plan rather than downloaded and then not unpacked.
|
|
109
|
+
- **Builds from source.** Postgres, pgBackRest and Valkey ship no darwin
|
|
110
|
+
binary. Caddy with its plugins and cloudflare-exporter are to be built by the
|
|
111
|
+
public `taslabs-net/homeflare-builds` repo, which publishes GitHub releases
|
|
112
|
+
with a `SHA256SUMS` file (decided 2026-09-23). Each then becomes an ordinary
|
|
113
|
+
data set, after its own walk-down like any vendor's.
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Release binaries — measured, and the limits
|
|
2
|
+
|
|
3
|
+
Status: active
|
|
4
|
+
Verified: 2026-09-22
|
|
5
|
+
|
|
6
|
+
What [release-binary.md](./release-binary.md) rests on, how each fact was read,
|
|
7
|
+
and what is still reasoned rather than measured. Every vendor fact here is
|
|
8
|
+
VictoriaMetrics': the only data set so far. Host class: the Mac mini the stack is for
|
|
9
|
+
(macOS 27.2, arm64), bun 1.4.0. No binary was executed for any of this.
|
|
10
|
+
|
|
11
|
+
## The vendor side (read-only, `gh api` and `curl`)
|
|
12
|
+
|
|
13
|
+
| archive (darwin-arm64) | bytes | members (all regular, root-level) |
|
|
14
|
+
| ----------------------------- | ----------- | ------------------------------------ |
|
|
15
|
+
| `victoria-metrics-…-v1.151.0` | 12,739,105 | `victoria-metrics-prod` |
|
|
16
|
+
| `victoria-logs-…-v1.52.0` | 10,943,697 | `victoria-logs-prod` |
|
|
17
|
+
| `victoria-traces-…-v0.10.0` | 11,126,077 | `victoria-traces-prod` |
|
|
18
|
+
| `vmutils-…-v1.151.0` | 123,584,800 | seven `-prod` tools, 264 MB unpacked |
|
|
19
|
+
|
|
20
|
+
- **Asset names are exact**, and `-cluster`, `-enterprise`,
|
|
21
|
+
`-enterprise-cluster` (and `vlutils`) archives sit beside them with every
|
|
22
|
+
prefix in common — twelve siblings in the darwin-arm64 listing alone. The
|
|
23
|
+
listing is committed as `src/release/fixtures/victoria/darwin-arm64-assets.txt`.
|
|
24
|
+
- **Each checksum file hashes both layers**: line 1 the archive, then one line
|
|
25
|
+
per `-prod` member, in `sha256sum` text mode (`<hex> <name>`, LF, trailing
|
|
26
|
+
newline). For all four, GitHub's asset `digest` equals line 1, and GitHub's
|
|
27
|
+
digest of the checksum file equals the SHA-256 of the committed fixture.
|
|
28
|
+
- **Re-read the same day by the supply-chain review** (`gh api …/releases/tags/<tag>`):
|
|
29
|
+
all four archives' and checksum files' sizes and `digest`s still equal the pins
|
|
30
|
+
and the recorded checksum-file digests. Nothing had been swapped since recording.
|
|
31
|
+
- **Releases are mutable** (`"immutable": false`), uploaded from maintainers'
|
|
32
|
+
personal accounts. **No signatures or attestations** exist for the archives:
|
|
33
|
+
no `.sig`/`.asc`/`.pem`/`.bundle` assets, and the attestations API answers 404
|
|
34
|
+
for all four digests next to a positive control (`cli/cli`) that answers 200.
|
|
35
|
+
- **Tar format**: `tar -tvzf -` on each streamed archive (nothing saved) shows
|
|
36
|
+
only root-level regular files with short names — no directories, links, PAX
|
|
37
|
+
or GNU long-name headers. Owners differ by release (`builder`, `valyala`,
|
|
38
|
+
`ubuntu`), which is why the archive's mode and owner are never used.
|
|
39
|
+
|
|
40
|
+
## The provider against the real archives
|
|
41
|
+
|
|
42
|
+
The provider, as `Victoria.Binary` through `victoriaProviders(localRunner())`,
|
|
43
|
+
installed all five binaries the stack needs into a scratch directory (never
|
|
44
|
+
`/opt`), 2026-09-22. ⚠️ **That was the code before it was generalised into
|
|
45
|
+
`Release.Binary`, and the generalised resource has NOT been re-run against the
|
|
46
|
+
real archives** (a download needs the operator's go-ahead). What carried over
|
|
47
|
+
unchanged: `download.ts`, `tar.ts`, the two checks in `archive.ts` and the write
|
|
48
|
+
through `file-converge.ts`. What changed: where the pins come from (props,
|
|
49
|
+
filled by `catalogBinary()`) and the URL, now composed from repo, tag and asset
|
|
50
|
+
and held equal to the old whole URLs by `victoria.test.ts`.
|
|
51
|
+
|
|
52
|
+
- **Every digest verified**, both layers, and every installed file's SHA-256 is
|
|
53
|
+
its pin. One run took 1.7–5.9 s on the mini's link.
|
|
54
|
+
- **One download for vmagent + vmalert** installed concurrently: `fetch` was
|
|
55
|
+
called once for the shared vmutils archive (counted by wrapping `fetch`).
|
|
56
|
+
- **`codesign -v` is valid** on all five as written: the vendor binaries are
|
|
57
|
+
ad-hoc, linker-signed (`flags=0x20002`, no TeamIdentifier) and the write is
|
|
58
|
+
byte-identical, so the embedded signature survives. The running Nix builds are
|
|
59
|
+
the same signature class, and launchd runs them.
|
|
60
|
+
- **No `com.apple.quarantine`**, only `com.apple.provenance` — the same as the
|
|
61
|
+
running Nix binaries.
|
|
62
|
+
- **No `/nix/store` library** (`otool -L`, all five): `libSystem`,
|
|
63
|
+
`/usr/lib/libresolv.9.dylib`, CoreFoundation, Security. ⚠️ `libresolv.9.dylib`
|
|
64
|
+
is not on disk; it is in the dyld shared cache — a preflight that checks the
|
|
65
|
+
file exists would refuse a working binary.
|
|
66
|
+
- **Memory (peak RSS, bun 1.4.0)**: victoria-traces alone ~200 MB; vmalert
|
|
67
|
+
alone ~520 MB; vmagent + vmalert together ~590–720 MB; all five together
|
|
68
|
+
~750–930 MB. 🔴 Before the gzip input was fed in slices, vmalert alone peaked
|
|
69
|
+
at 1,116 MB: the whole archive inflated in one step and 264 MB of output queued
|
|
70
|
+
ahead of the reader (archive.ts). Before the download filled one pinned-size
|
|
71
|
+
buffer, the chunk list plus its concatenated copy held the archive twice.
|
|
72
|
+
- **Under node** (v26.7.0), the bundled extractor reads a gzipped tar the same
|
|
73
|
+
way (`DecompressionStream` is the web standard in both runtimes).
|
|
74
|
+
|
|
75
|
+
## Through Alchemy's own engine (fakes, not a host)
|
|
76
|
+
|
|
77
|
+
`src/release/plan.test.ts` runs alchemy@2.0.0-beta.79's Plan and Apply over
|
|
78
|
+
in-memory state, with `HostDirectory` declared in front of the binary, a fake
|
|
79
|
+
host that models `mkdir`/`rmdir`, and a fake HTTP server:
|
|
80
|
+
|
|
81
|
+
- **A first deploy** runs `mkdir`, one `GET`, one write, in that order: the
|
|
82
|
+
`directory: dir.path` Output orders them.
|
|
83
|
+
- **A version bump** (`HostDirectory('vmutils-<version>')`, `ReleaseBinary('vmalert')`)
|
|
84
|
+
plans `replace` / `create` / `delete` and runs: mkdir new, GET new, write new,
|
|
85
|
+
**then** remove old, **then** rmdir old. That was reasoned from `Apply.ts`
|
|
86
|
+
before; it is measured now, on the engine, not on a host.
|
|
87
|
+
- **A version outside the data set** fails in the stack program: zero requests,
|
|
88
|
+
zero host calls.
|
|
89
|
+
- **A new pin in the same directory** fails at plan: the directory already
|
|
90
|
+
exists, so its path is resolved when `diff` runs.
|
|
91
|
+
- 🔴 **The same bytes re-pinned while the directory is being updated** (its
|
|
92
|
+
path unresolved at plan): with `diff` answering `replace`, the deploy
|
|
93
|
+
SUCCEEDED and the binary was gone. The new generation accepted the identical
|
|
94
|
+
file as its own, and Phase 2 deleted the old generation at the same path.
|
|
95
|
+
`diff` now answers `update` there, and reconcile's in-place guard refuses it
|
|
96
|
+
with the binary kept (`plan.test.ts` pins the refusal).
|
|
97
|
+
|
|
98
|
+
## The install path on a real filesystem (red team, 2026-09-22)
|
|
99
|
+
|
|
100
|
+
Measured on macOS 27.2 in temp directories only: no live host, no sudo.
|
|
101
|
+
|
|
102
|
+
- 🔴 **One file under two spellings was lost, silently** (fixed;
|
|
103
|
+
`binary-alias.test.ts`). Respelling the directory through a symlinked parent,
|
|
104
|
+
or by case alone on case-insensitive APFS, planned a `replace`: the new
|
|
105
|
+
generation took the identical file as its resumed install, then Phase 2
|
|
106
|
+
deleted the old generation's path, which was the same file. The deploy
|
|
107
|
+
succeeded with no binary on disk. Now `diff` compares device and inode
|
|
108
|
+
(`launchd/file-identity.ts`) and answers `update`, and a move never removes
|
|
109
|
+
an old path that is the file it just verified.
|
|
110
|
+
- 🔴 **An execute-only mode (`0o111`) was written and then unreadable** (fixed).
|
|
111
|
+
The read-back threw EACCES, the file stayed with no state, and every later
|
|
112
|
+
probe threw the same. Now the mode must be owner-readable, and a read-back
|
|
113
|
+
that throws rolls the create back like one that mismatches.
|
|
114
|
+
- 🔴 **Two resources at one path both owned it** (fixed; `binary-claims.test.ts`).
|
|
115
|
+
`vmalert` and `vmalert-logs` declared at one path both created it; the deploy
|
|
116
|
+
that dropped `vmalert-logs` deleted the file `vmalert` still declared, and
|
|
117
|
+
reported `vmalert` as `noop`. A second claim on a path in one run is refused.
|
|
118
|
+
- ⚠️ **`/usr/bin/install -S` chmods after the rename.** An lstat poller racing a
|
|
119
|
+
300 MB install saw the new inode at the path as `0600`, then `0755` 0.2–0.4 ms
|
|
120
|
+
later, six runs out of six. Never torn and never wider than declared. localRunner
|
|
121
|
+
sets the mode before its rename. Closing the window under `sudoRunner` changes
|
|
122
|
+
the sudo allowlist, so it has not been done.
|
|
123
|
+
- ⚠️ **`install` copies extended attributes**, `com.apple.quarantine` included.
|
|
124
|
+
Files this process writes through `node:fs` carried only
|
|
125
|
+
`com.apple.provenance`, so no staged file is quarantined today.
|
|
126
|
+
- ★ **Tar-slip cannot reach the disk by construction.** The installed path is
|
|
127
|
+
`<directory>/<name>`, with `name` one `[A-Za-z0-9._-]` segment; an archive
|
|
128
|
+
entry's name only selects bytes and never becomes a path.
|
|
129
|
+
|
|
130
|
+
## Reasoned, not measured
|
|
131
|
+
|
|
132
|
+
- ⚠️ **REASONED NOT MEASURED: a quarantined binary would not start under
|
|
133
|
+
launchd.** Adoption (`--adopt`) and the resume rule accept a file by digest,
|
|
134
|
+
mode and owner, and never read xattrs. A binary that was downloaded by a
|
|
135
|
+
browser and placed by hand would be adopted with its quarantine. Remove it
|
|
136
|
+
(`xattr -d com.apple.quarantine`) before adopting.
|
|
137
|
+
|
|
138
|
+
- ⚠️ **REASONED NOT MEASURED: a vendor binary runs under launchd from the
|
|
139
|
+
installed path.** Strongly suggested — same signature class as the running
|
|
140
|
+
Nix builds, valid signature, no quarantine — but nothing was executed.
|
|
141
|
+
- ⚠️ **REASONED NOT MEASURED: `sudoRunner` installs under the real
|
|
142
|
+
`/opt/<estate>/obs`.** Exercised only over the kit's fake sudo host
|
|
143
|
+
(`binary-sudo.test.ts`: one staged file, one `install -S -m 0755 -o 0 -g 0`,
|
|
144
|
+
the staged file removed on success and on failure).
|
|
145
|
+
- ⚠️ **`HostDirectory` under `sudoRunner` does not work today** (measured with
|
|
146
|
+
the kit's fakes during the walk-down): `mkdir` is not on the sudo allowlist,
|
|
147
|
+
and `checkWrite` refuses a directory. A stack that deploys as itself cannot
|
|
148
|
+
create `<root>/<package>-<version>` through sudo yet; deploy as root, or
|
|
149
|
+
pre-create the directory. Fixing it is a sudo-seam change with its own review.
|
|
150
|
+
- ⚠️ **REASONED NOT MEASURED: `HostDirectory`'s argv (`mkdir -m`, `chmod`,
|
|
151
|
+
`chown`, `rmdir`) behaves the same on macOS** — its header says so.
|
|
152
|
+
- ⚠️ **REASONED NOT MEASURED: the process-memory panels.** The vendor darwin
|
|
153
|
+
builds are `CGO_ENABLED=0` (their Makefile), and VictoriaMetrics' metrics
|
|
154
|
+
library reads darwin RSS only with cgo — the reason the house Nix module
|
|
155
|
+
builds with cgo. Expect `process_resident_memory_bytes` to go missing after
|
|
156
|
+
the swap until someone decides what replaces it. A consumer decision, not
|
|
157
|
+
this provider's.
|
|
158
|
+
- ⚠️ **The platform is not checked against the host.** A `darwin-arm64` pin on
|
|
159
|
+
a Linux host installs a binary that will not start; the job's first launch is
|
|
160
|
+
where that shows. The platform is the data set's key, not a prop.
|
|
161
|
+
- ⚠️ **A stamped vendor build prints `-version` to stderr**, not stdout
|
|
162
|
+
(`lib/buildinfo`, read). Identity here is the SHA-256 alone; if a check ever
|
|
163
|
+
runs `-version`, read stderr, and say which build (Nix or vendor) it was.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Release binaries — every refusal
|
|
2
|
+
|
|
3
|
+
Status: active
|
|
4
|
+
Verified: 2026-09-22
|
|
5
|
+
|
|
6
|
+
Everything `Release.Binary` refuses, by where it is refused. The guide is
|
|
7
|
+
[release-binary.md](./release-binary.md). ★ One page per subject: the guide says
|
|
8
|
+
what a declaration does, this says what stops it, and a new refusal lands here.
|
|
9
|
+
|
|
10
|
+
In the stack program (`catalogBinary`): a package, version, platform or
|
|
11
|
+
binary the data set does not pin, including `v1.151.0`, `1.151.0-enterprise`,
|
|
12
|
+
`victoria-metrics-cluster`, `vmalert-prod` and inherited keys like
|
|
13
|
+
`constructor`.
|
|
14
|
+
|
|
15
|
+
At plan (the probe and `diff`), before anything is touched: a pin that is
|
|
16
|
+
missing or not a plain value (an Output pin would be a fetch; a first deploy
|
|
17
|
+
is never diffed, so reconcile refuses it there, before any host call, from the
|
|
18
|
+
props as declared); a repo that is not `owner/name`, or whose name is `.` or
|
|
19
|
+
`..`; a tag, asset or name that is not one safe path segment; an asset that is
|
|
20
|
+
not a `.tar.gz` or `.tgz` (a glob is not a name); a size that is not an
|
|
21
|
+
integer from 1 byte to 1 GiB (it is allocated up front); a digest that is not
|
|
22
|
+
64 lower-case hex; a member that is absolute or climbs. Also a relative or
|
|
23
|
+
unnormalised directory; a mode that is setuid, setgid, sticky, group- or
|
|
24
|
+
world-writable, or not owner-readable and owner-executable; an owner or group
|
|
25
|
+
that is not a valid name or id; a new pin at the same path.
|
|
26
|
+
|
|
27
|
+
⚠️ **Not on a first deploy with `directory: dir.path`.** Alchemy neither
|
|
28
|
+
probes nor diffs a create whose props hold an Output, so there every check
|
|
29
|
+
above runs at apply, after `HostDirectory` has made the directory (measured: a
|
|
30
|
+
group-writable mode planned `create`, then the apply ran `mkdir` and refused).
|
|
31
|
+
Nothing is written to the binary's path. A failed first install never bricks
|
|
32
|
+
the next plan: the recovery read of its row answers "nothing recovered"
|
|
33
|
+
(`binary-read.ts`).
|
|
34
|
+
|
|
35
|
+
At plan, only under `--adopt` (the probe then hands `diff` what it read): a
|
|
36
|
+
file at the path that is not the pinned binary, or not a regular file.
|
|
37
|
+
|
|
38
|
+
At apply, before the download: a second resource installing the same path in
|
|
39
|
+
one deploy (two jobs that run one binary share one `ReleaseBinary`; measured,
|
|
40
|
+
two owners meant dropping either deleted the other's file); a missing
|
|
41
|
+
directory, one that is a symlink, or one that group or other may write; a
|
|
42
|
+
symlink or directory at the path; a file the resource does not own (without
|
|
43
|
+
`--adopt`, even the pinned bytes; with it, anything but the pinned bytes); a
|
|
44
|
+
chown without root.
|
|
45
|
+
|
|
46
|
+
From the vendor: a status other than 200; a body past or short of the pinned
|
|
47
|
+
size; an archive whose SHA-256 is not the pin; an archive entry that is
|
|
48
|
+
absolute, has a `..` segment, is a hard link, symlink, directory, device, FIFO
|
|
49
|
+
or contiguous file, is a GNU long-name or PAX header, is named twice, fails its
|
|
50
|
+
header checksum, uses base-256, is neither ustar nor GNU, a lone zero block,
|
|
51
|
+
data after the end, a truncated archive; the declared member missing; the
|
|
52
|
+
member's SHA-256 not its pin. Every one of these ends `Nothing was written.`
|
|
53
|
+
|
|
54
|
+
After the write: a file that does not read back as declared (a create is
|
|
55
|
+
removed). Errors are tagged (`BinaryRefused`, `DownloadFailed`,
|
|
56
|
+
`ChecksumMismatch`, `ArchiveRefused`) and carry the house refusal sentence.
|
|
57
|
+
⚠️ Tagged, not typed: no caller can `catchTag` one yet (`error-channel.test.ts`).
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Release binaries — what upstream would need
|
|
2
|
+
|
|
3
|
+
Status: active
|
|
4
|
+
Verified: 2026-09-22 (against `alchemy-run/alchemy` at `v2.0.0-beta.79`, read-only:
|
|
5
|
+
AGENTS.md and `scripts/generate-api-reference.ts`, plus the installed
|
|
6
|
+
`src/Command/Exec.ts`, `src/GitHub/Release.ts`, `src/GitHub/BaseUrl.ts`,
|
|
7
|
+
`src/Util/AtomicFile.ts`, `src/Hetzner/Ssh.ts` and `src/Hetzner/Providers.ts`)
|
|
8
|
+
|
|
9
|
+
`Release.Binary` is built so it **could** be contributed to Alchemy. This page
|
|
10
|
+
is the honest gap list: what already fits, and what a pull request to
|
|
11
|
+
`alchemy-run/alchemy` would have to change first. Nothing here is a promise
|
|
12
|
+
that upstream wants it.
|
|
13
|
+
|
|
14
|
+
## Already shaped for it
|
|
15
|
+
|
|
16
|
+
- **Generic before its first release.** It began as `Victoria.Binary` with the
|
|
17
|
+
catalog inside the resource. It was generalised before any state row carried
|
|
18
|
+
that type name, because a rename after release would be a state migration.
|
|
19
|
+
The pinned archive (repo, tag, exact asset name, size, archive digest,
|
|
20
|
+
member, member digest, installed name) is props. Vendor data sets sit beside
|
|
21
|
+
it as plain modules. Alchemy could ship the resource without shipping anyone's
|
|
22
|
+
pins.
|
|
23
|
+
- **Named the upstream way.** The type is `Release.Binary` (`Namespace.Kind`).
|
|
24
|
+
Under upstream's layout it would be `src/Release/Binary.ts`, imported as
|
|
25
|
+
`import * as Release from "alchemy/Release"`, used as `Release.Binary`. The
|
|
26
|
+
house exports the prefixed `ReleaseBinary`, as it does `HostFile` and
|
|
27
|
+
`LaunchdJob`. Upstream's `GitHub.Release` already means _publishing_ a
|
|
28
|
+
release, so the GitHub namespace would be the confusing home.
|
|
29
|
+
- **The lifecycle is beta.79's**: `read`, `diff`, `reconcile`, `delete`,
|
|
30
|
+
`list` returning `[]`. No `create`/`update`. `reconcile` is one flow —
|
|
31
|
+
observe the path, ensure the bytes, sync mode and owner, read back — not a
|
|
32
|
+
create body and an update body. ⚠️ It does branch on `output` and `olds` for
|
|
33
|
+
**ownership**: only a create may take over a file under `--adopt`
|
|
34
|
+
(`file-converge.ts`), and the in-place guard compares `olds`' pins (gap 3).
|
|
35
|
+
- **Props are plain types**, never `Input<T>`, and every prop has JSDoc, with
|
|
36
|
+
`@default` where one applies. ⚠️ Not every attribute: `path`, `mode`, `uid`,
|
|
37
|
+
`gid` and `size` come from `HostFileAttributes` with no JSDoc, so the
|
|
38
|
+
generator would print them bare.
|
|
39
|
+
- **The resource JSDoc is the generator's shape**: `### Installing a binary`,
|
|
40
|
+
`**Example:**` with fenced TypeScript, and `@resource` last. (An untitled
|
|
41
|
+
example is titled "Example" by the generator.) ⚠️ The example needs
|
|
42
|
+
`HostDirectory`, `LaunchdJob` and `VICTORIA_RELEASES`, which upstream lacks.
|
|
43
|
+
- **Errors are tagged** (`Data.TaggedError`: `BinaryRefused`, `DownloadFailed`,
|
|
44
|
+
`ChecksumMismatch`, `ArchiveRefused`), and nothing casts on `_tag`.
|
|
45
|
+
⚠️ **They are not typed end to end, so no caller can `catchTag` one**
|
|
46
|
+
(measured, `error-channel.test.ts`). `catalogBinary()` throws, which in a
|
|
47
|
+
stack program is a defect that `catchTag` never sees. The provider's `lift()`
|
|
48
|
+
keeps the instance at runtime but types the channel as `Error` (gap 10).
|
|
49
|
+
- **HTTP is Effect's `HttpClient`**, and `reconcile` reports progress through
|
|
50
|
+
`session.note`. A provider layer that brings `FetchHttpClient.layer` itself
|
|
51
|
+
has upstream precedent (`Hetzner/Providers.ts`).
|
|
52
|
+
- **The props carry nothing estate-specific.** Paths, owners and modes are the
|
|
53
|
+
stack's. ⚠️ The wiring does carry the estate's name: the host service is
|
|
54
|
+
keyed `homeflare/launchd/HostRunner`, and the ownership notes are
|
|
55
|
+
`Artifacts` keys `homeflare/ownership/*` (gaps 1 and 2).
|
|
56
|
+
|
|
57
|
+
## What would have to change
|
|
58
|
+
|
|
59
|
+
1. **The host seam.** Every host touch goes through the kit's promise-based
|
|
60
|
+
`HostRunner`, a Context service under `launchd/`. Upstream would expect an
|
|
61
|
+
Effect service with a neutral key. ⚠️ `Util/AtomicFile.ts` is not that
|
|
62
|
+
service as it stands: it writes a **string** (`writeFileString`) and a mode,
|
|
63
|
+
never bytes, a uid or a gid. `FileSystem` reaches only the deploying
|
|
64
|
+
machine. Upstream's one remote-host seam is `Hetzner.Ssh` (Effect `exec` and
|
|
65
|
+
`scp`). A contribution needs a host service with a local implementation over
|
|
66
|
+
`FileSystem` and a remote one in that shape. The house seam is one file wide
|
|
67
|
+
on purpose, so the swap is local.
|
|
68
|
+
2. **Ownership semantics.** The house refuses to adopt without `--adopt`:
|
|
69
|
+
`read` returns `Unowned` even for a byte-identical file, and `reconcile`
|
|
70
|
+
resolves adoption at apply through `ownership/adopt.ts`. That reads
|
|
71
|
+
`Stack.resources[fqn].Adopt`, which upstream marks `@internal`, and state
|
|
72
|
+
rows. Upstream's default for a resource without ownership metadata is
|
|
73
|
+
**plain attributes, which means silent adoption**. A contribution would drop
|
|
74
|
+
`adoptsAtApply` / `noteUnfinished`, ask the engine to hand `adopt` to
|
|
75
|
+
`reconcile`, and settle whether a file on disk is ever adopted silently.
|
|
76
|
+
Worth keeping in any form: a file whose digest is not the pin is never
|
|
77
|
+
adopted, because adopting it is overwriting it (`binary-claim.ts`).
|
|
78
|
+
⚠️ `declared-pins.ts` reads the same `@internal` record (`.Props`) to refuse
|
|
79
|
+
a pin that was an Output on a first deploy. Upstream applies `Input<T>` to
|
|
80
|
+
every prop automatically, so "this prop must be a literal" is an engine
|
|
81
|
+
feature to propose (the declared props handed to `reconcile`, or a
|
|
82
|
+
literal-only marker), not something a provider can reach for.
|
|
83
|
+
3. **`diff` does live I/O and returns `noop`.** Upstream's guidance is that a
|
|
84
|
+
diff should almost never return `noop`. Live I/O has precedent, though:
|
|
85
|
+
`Command.Exec`'s diff hashes a directory and answers `noop` on a match, the
|
|
86
|
+
same shape as this one. Two rules are worth keeping in any form: a new pin
|
|
87
|
+
or path → `replace`, even while `directory` is an Output; and a new pin at
|
|
88
|
+
the same path is refused. The apply-time half of that refusal reads `olds`,
|
|
89
|
+
which upstream's reconciler doctrine calls "at most a hint". Restated as
|
|
90
|
+
observation it reads: the recorded path's live file is not these bytes.
|
|
91
|
+
4. **Layout and exports.** Upstream co-locates contract and provider in
|
|
92
|
+
`src/{Cloud}/{Service}/{Resource}.ts`, re-exports with `export *`, and
|
|
93
|
+
registers providers in a `Providers.ts` collection that `alchemy unsafe nuke`
|
|
94
|
+
enumerates. The house splits files under a 250-line cap, curates its barrel,
|
|
95
|
+
and exports `releaseProviders(runner)` with no collection.
|
|
96
|
+
5. **Where the data sets live.** Upstream ships no estate's pins. The resource,
|
|
97
|
+
`catalogBinary()` and the tar reader would go upstream; `victoria.ts` and its
|
|
98
|
+
fixtures would stay in the kit, or become a documented example.
|
|
99
|
+
6. **Tests.** The house tests run on fakes (`bun:test`, no network, no host),
|
|
100
|
+
plus Alchemy's real Plan and Apply over in-memory state (`plan.test.ts`).
|
|
101
|
+
Upstream tests run `pnpm test` against the real target, start and end with
|
|
102
|
+
`stack.destroy()`, and verify out of band. For a host resource the honest
|
|
103
|
+
equivalent is a real temporary directory and a real download. That was done
|
|
104
|
+
once, by hand, before the generalisation (release-binary-measured.md), and
|
|
105
|
+
CI here deliberately never does it.
|
|
106
|
+
7. **Toolchain and style.** pnpm, the alchemy-test runner, `tsc -b`, double
|
|
107
|
+
quotes; generated docs via `pnpm docs:gen`, never hand-edited.
|
|
108
|
+
8. **The pull request itself.** A conventional-commit title; no `#` or `##`
|
|
109
|
+
headings in the body (the smallest is `###`), the summary first with no
|
|
110
|
+
heading, no test plan, and code over prose.
|
|
111
|
+
9. **The directory must already exist.** `reconcile` refuses a missing one, and
|
|
112
|
+
the kit's `HostDirectory` (linux subpath) is what creates it. Upstream has no
|
|
113
|
+
such resource, so a contribution brings one, or a prop that creates the
|
|
114
|
+
directory with a declared mode and owner.
|
|
115
|
+
10. **A typed error channel.** `catalogBinary()` would return an `Effect` that
|
|
116
|
+
fails with `BinaryRefused`, and the provider would fail with the tag union
|
|
117
|
+
instead of `lift()`'s `Error`. `error-channel.test.ts` fails the moment
|
|
118
|
+
either changes, so this page moves with it.
|
|
119
|
+
11. **The tar reader accepts one measured layout.** Measured 2026-09-22 against
|
|
120
|
+
`extractMembers`: macOS's default `tar czf` (bsdtar 3.5.3) writes a PAX `x`
|
|
121
|
+
header before every entry (a sub-second `mtime` and the
|
|
122
|
+
`com.apple.provenance` xattr), so even a one-file archive is refused whole.
|
|
123
|
+
`--no-mac-metadata` still writes them; `--format ustar` passes. A wrapping
|
|
124
|
+
directory entry is refused whole. Go's `archive/tar` writing root-level
|
|
125
|
+
files passes. The house accepts this on purpose: the Victoria archives are
|
|
126
|
+
flat, and homeflare-builds writes plain ustar. ⚠️ REASONED NOT MEASURED:
|
|
127
|
+
the Prometheus family's archives (`alertmanager-0.34.1.darwin-arm64.tar.gz`)
|
|
128
|
+
wrap their files in a directory, so the queue in
|
|
129
|
+
release-binary-catalogs.md needs a reader change, not only a data set.
|
|
130
|
+
Generic replacement: skip directory entries, and links that are not the
|
|
131
|
+
declared member, because nothing is ever written from an entry name; read
|
|
132
|
+
PAX records and refuse only a `path`, `linkpath` or `size` record. The
|
|
133
|
+
member digest still guards the bytes.
|
|
134
|
+
12. **One source, one tag alphabet, other prop names.** `releaseUrl` fixes
|
|
135
|
+
`https://github.com`. Upstream's GitHub family also reaches GitHub
|
|
136
|
+
Enterprise (`normalizeGitHubBaseUrl`, `GitHub/BaseUrl.ts`). A tag must be
|
|
137
|
+
one `[A-Za-z0-9._-]` segment, so kustomize's `kustomize/v5.8.1` (measured:
|
|
138
|
+
GitHub's own download URL carries the raw `/`) and a changesets-style
|
|
139
|
+
`pkg@1.2.3` are refused at plan. Generic: validate git's ref-name rules,
|
|
140
|
+
segment by segment. Upstream's GitHub props split `owner` and `repository`
|
|
141
|
+
and call a tag `tagName`; this takes `repo: "owner/name"` and `tag`.
|
|
142
|
+
13. **No platform check.** `platform` is a catalog key, not a prop, and nothing
|
|
143
|
+
compares the archive's platform with the host. On a Linux runner, a stack
|
|
144
|
+
that asks for `darwin-arm64` gets a Mach-O written, read back and reported
|
|
145
|
+
installed; its unit then fails with "exec format error". Generic: a
|
|
146
|
+
`platform` prop, checked against the host (`uname -sm` through the host
|
|
147
|
+
service) or against the member's executable header.
|
|
148
|
+
|
|
149
|
+
## What should NOT change on the way
|
|
150
|
+
|
|
151
|
+
- ⛔ **Pins in code, never a checksum file fetched at apply.** Mutable releases
|
|
152
|
+
make a fetched checksum file a claim by the same party whose bytes it vouches
|
|
153
|
+
for. A pin that is an Output is refused for the same reason.
|
|
154
|
+
- ⛔ **Two layers.** Verify the archive before unpacking and the member before
|
|
155
|
+
writing. The member check is what still holds if a reader is ever wrong about
|
|
156
|
+
a boundary.
|
|
157
|
+
- ⛔ **Exact names.** An asset and a member are matched whole, never by a
|
|
158
|
+
prefix, a glob or "contains darwin-arm64", because the enterprise and cluster
|
|
159
|
+
siblings share every prefix.
|
|
160
|
+
- ⛔ **Refuse the whole archive** for any unsafe entry, extract only what was
|
|
161
|
+
declared, and stage nothing on disk before the one atomic write. ⚠️ Gap 11 is
|
|
162
|
+
where upstream is likeliest to push back: which entries count as unsafe when
|
|
163
|
+
nothing is written from an entry name.
|