@homeflare/alchemy 0.21.0 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +3 -1
  2. package/dist/launchd/index.d.ts +2 -0
  3. package/dist/launchd/index.d.ts.map +1 -1
  4. package/dist/launchd/index.js +53 -1
  5. package/dist/launchd/index.js.map +4 -3
  6. package/dist/launchd/port-claims.d.ts +31 -0
  7. package/dist/launchd/port-claims.d.ts.map +1 -0
  8. package/dist/proxmox/api-token-form.d.ts +12 -1
  9. package/dist/proxmox/api-token-form.d.ts.map +1 -1
  10. package/dist/proxmox/api-token.d.ts +8 -3
  11. package/dist/proxmox/api-token.d.ts.map +1 -1
  12. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts +13 -13
  13. package/dist/proxmox/generated/pve/cluster-sdn-fabrics.d.ts.map +1 -1
  14. package/dist/proxmox/index.d.ts +9 -2
  15. package/dist/proxmox/index.d.ts.map +1 -1
  16. package/dist/proxmox/index.js +17 -9
  17. package/dist/proxmox/index.js.map +8 -7
  18. package/dist/proxmox/zfs-pool-spec.d.ts +27 -0
  19. package/dist/proxmox/zfs-pool-spec.d.ts.map +1 -0
  20. package/dist/proxmox/zfs-pool-write.d.ts +48 -7
  21. package/dist/proxmox/zfs-pool-write.d.ts.map +1 -1
  22. package/dist/proxmox/zfs-pool.d.ts +42 -20
  23. package/dist/proxmox/zfs-pool.d.ts.map +1 -1
  24. package/dist/release/catalog.d.ts +37 -0
  25. package/dist/release/catalog.d.ts.map +1 -1
  26. package/dist/release/index.d.ts +2 -1
  27. package/dist/release/index.d.ts.map +1 -1
  28. package/dist/release/index.js +75 -15
  29. package/dist/release/index.js.map +5 -4
  30. package/dist/release/openbao.d.ts +35 -0
  31. package/dist/release/openbao.d.ts.map +1 -0
  32. package/docs/adopted-deploys.md +14 -0
  33. package/docs/launchd-ports.md +109 -0
  34. package/docs/launchd.md +5 -0
  35. package/docs/release-binary-catalogs.md +32 -7
  36. package/docs/release-binary-openbao.md +151 -0
  37. package/docs/release-binary.md +13 -2
  38. package/package.json +1 -1
  39. package/src/launchd/index.ts +2 -0
  40. package/src/launchd/port-claims.test.ts +124 -0
  41. package/src/launchd/port-claims.ts +102 -0
  42. package/src/launchd/providers.test.ts +3 -0
  43. package/src/proxmox/api-token-adopt.test.ts +138 -0
  44. package/src/proxmox/api-token-form.ts +19 -2
  45. package/src/proxmox/api-token.ts +8 -3
  46. package/src/proxmox/generated/pve/cluster-sdn-fabrics.ts +23 -13
  47. package/src/proxmox/index.test.ts +5 -2
  48. package/src/proxmox/index.ts +9 -2
  49. package/src/proxmox/zfs-pool-adopt.test.ts +141 -0
  50. package/src/proxmox/zfs-pool-spec.ts +76 -0
  51. package/src/proxmox/zfs-pool-write.ts +63 -9
  52. package/src/proxmox/zfs-pool.ts +45 -95
  53. package/src/release/catalog.test.ts +71 -0
  54. package/src/release/catalog.ts +68 -5
  55. package/src/release/fixtures/openbao/checksums.txt +83 -0
  56. package/src/release/fixtures/openbao/checksums.txt.gpgsig +0 -0
  57. package/src/release/fixtures/openbao/darwin-assets.txt +12 -0
  58. package/src/release/fixtures/openbao/openbao-gpg-pub-20240618.asc +109 -0
  59. package/src/release/index.ts +3 -0
  60. package/src/release/openbao.test.ts +120 -0
  61. package/src/release/openbao.ts +86 -0
  62. package/src/release/providers.test.ts +1 -0
@@ -6,20 +6,22 @@
6
6
  * moment the worker runs. Every other decision in this file follows from that one sentence, and
7
7
  * each of them is written out below rather than left to be inferred.
8
8
  *
9
- * ⛔ DESTROY IS REFUSED. `delete` MAKES NO API CALL — removing this resource from a stack leaves
10
- * the zpool and its data exactly where they are, and Alchemy simply forgets about them. The
11
- * asymmetry is the whole argument: an ORPHANED pool is recoverable (declare it again, or destroy
12
- * it by hand once a human has looked at which disks are in it), a DESTROYED pool is not. Alchemy
13
- * deletes for reasons that have nothing to do with anybody intending it — a line removed from a
14
- * stack file, a resource renamed so the old id is dropped, a whole stack torn down by the wrong
15
- * command, a replace triggered by a prop nobody meant to touch. Handing `zpool destroy` to any
16
- * of those is not a feature. MEASURED in the node's own source: the DELETE handler forks
17
- * `zfsremove`, which runs `zpool destroy <name>` unconditionally, and with `cleanup-disks` then
18
- * calls `wipe_blockdev` on every member. Neither flag is offered as a prop, because there is no
19
- * call here for them to reach.
20
- * ★ THE OPERATOR'S PATH IS `pvesh delete /nodes/<node>/disks/zfs/<name>`, typed by a person who
21
- * has just looked at the pool. Do not "complete the CRUD" by wiring `delete` to `ops.destroy`:
22
- * that one line turns a text edit into an unrecoverable one.
9
+ * ⛔ ★ CORRECTED 2026-09-23 — THIS PARAGRAPH PREVIOUSLY SAID "DESTROY IS REFUSED" AND "`delete`
10
+ * MAKES NO API CALL". THAT WAS FALSE: MEASURED against this file's own `handlers.delete` below,
11
+ * which calls `destroyPool`, which sends `DELETE /nodes/{node}/disks/zfs/{name}` — the node's
12
+ * own source forks `zfsremove`, which runs `zpool destroy <name>` unconditionally, and with
13
+ * `cleanup-disks` then calls `wipe_blockdev` on every member (neither flag is offered as a prop,
14
+ * because there is no call here for them to reach). `delete` is FULLY IMPLEMENTED, per S14
15
+ * (alchemy-provider-standard): a `delete` that silently does nothing lies to whoever reads the
16
+ * plan. What actually keeps an ordinary removed declaration from reaching it is
17
+ * `defaultRemovalPolicy: 'retain'` below — Alchemy SKIPS `provider.delete` entirely for an
18
+ * orphaned or destroyed resource under `retain`, so the state row drops and the zpool and its
19
+ * data are left exactly where they are. The asymmetry is still the whole argument: an ORPHANED
20
+ * pool is recoverable (declare it again, or destroy it by hand once a human has looked at which
21
+ * disks are in it), a DESTROYED pool is not — which is why `retain` is the default rather than a
22
+ * documented caution, and why `delete` runs only for a caller who opts in by name with
23
+ * `.pipe(RemovalPolicy.destroy())`. See the ★ on `defaultRemovalPolicy` in resource-spec.ts, and
24
+ * zfs-pool-adopt.test.ts's mutation row, which pins exactly one DELETE reaching the fake cluster.
23
25
  *
24
26
  * ⛔ THERE IS NO PUT ON THIS FAMILY AT ALL. MEASURED from the cluster's own published schema
25
27
  * (`/usr/share/pve-docs/api-viewer/apidoc.js`, read on node-b 2026-09-13): `/nodes/{node}/disks/zfs`
@@ -47,7 +49,8 @@
47
49
  * ⚠️ TWO HANDLERS ARE OVERRIDDEN AND THE OTHER THREE COME FROM THE FACTORY — see the ★ above
48
50
  * `handlers`. `delete` is the ⛔ above. `reconcile` is because the create is a FORKED WORKER, so
49
51
  * the factory's immediate read-back would report a successful create as a failure; the measured
50
- * detail is in the ⛔ on `createPool` in zfs-pool-create.ts.
52
+ * detail, and the adopt-only refusal decision 9 adds, are both in `createPool` in
53
+ * zfs-pool-write.ts.
51
54
  *
52
55
  * ⚠️ PRIVILEGES, FROM THE SCHEMA. Read and diff need `Sys.Audit` on `/` — both GETs check it.
53
56
  * Reconcile needs `Sys.Modify` on `/` for the POST. `Datastore.Allocate` on `/storage` is NOT
@@ -57,29 +60,23 @@
57
60
  import { Resource } from 'alchemy';
58
61
  import * as Provider from 'alchemy/Provider';
59
62
  import * as Effect from 'effect/Effect';
60
- import {
61
- type PveRequirements,
62
- type PveSpec,
63
- type WithTarget,
64
- pveHandlers,
65
- pveOperations,
66
- } from './resource.ts';
67
- import { text } from './values.ts';
68
- import { ZFS_POOL_ENDPOINT, createForm, createPool, destroyPool } from './zfs-pool-write.ts';
63
+ import type { NodesNodeDisksZfsPostParams } from './generated/pve.ts';
64
+ import { type PveRequirements, type WithTarget, pveHandlers, pveOperations } from './resource.ts';
65
+ import { spec } from './zfs-pool-spec.ts';
66
+ import { createPool, destroyPool } from './zfs-pool-write.ts';
69
67
 
70
- /** PVE's layouts. ⚠️ Each has a minimum disk count PVE enforces, and `raid10` needs an even one. */
71
- export type ZfsRaidLevel =
72
- | 'single'
73
- | 'mirror'
74
- | 'raid10'
75
- | 'raidz'
76
- | 'raidz2'
77
- | 'raidz3'
78
- | 'draid'
79
- | 'draid2'
80
- | 'draid3';
68
+ /**
69
+ * PVE's layouts, generated (2026-09-23) from `NodesNodeDisksZfsPostParams['raidlevel']`
70
+ * (`generated/pve.ts`, manifest `pve-apidoc` pve-manager 9.2.11 sha256 9def8f13…) rather than
71
+ * hand-typed, per decision 9. ⚠️ Each has a minimum disk count PVE enforces, and `raid10` needs an
72
+ * even one. ⛔ THERE IS NO `stripe` IN THIS ENUM. n1's `speed` pool is one (decision 14 accepts it
73
+ * as-is): `ZfsPoolProps` below is why a stripe pool is declarable at all — as an ADOPT-ONLY
74
+ * declaration with no `raidlevel`, never as a full one.
75
+ */
76
+ export type ZfsRaidLevel = NonNullable<NodesNodeDisksZfsPostParams['raidlevel']>;
81
77
 
82
- export type ZfsCompression = 'on' | 'off' | 'gzip' | 'lz4' | 'lzjb' | 'zle' | 'zstd';
78
+ /** Generated the same way, from `NodesNodeDisksZfsPostParams['compression']`. */
79
+ export type ZfsCompression = NonNullable<NodesNodeDisksZfsPostParams['compression']>;
83
80
 
84
81
  /**
85
82
  * ⛔ THERE IS NO `add_storage`, `cleanup-config` OR `cleanup-disks` PROP, AND THAT IS NOT AN
@@ -112,9 +109,19 @@ export interface ZfsPoolProps extends WithTarget {
112
109
  * `csv` sorts, and sorting this list silently re-pairs the mirrors into a different pool.
113
110
  * ⚠️ PVE's only guard is `assert_disk_unused`, i.e. its own `disk_is_used` over the same `used`
114
111
  * column the Disks view shows. A blank disk it considers free is wiped with no confirmation.
112
+ *
113
+ * ⛔ OPTIONAL SINCE DECISION 9 (2026-09-23), AND OMITTING IT MEANS SOMETHING SPECIFIC: leaving
114
+ * both this and `raidlevel` undeclared declares an ADOPT-ONLY pool — one this resource may
115
+ * read and report but never build. That is the only shape available for a pool PVE's own POST
116
+ * schema cannot fully describe, such as n1's `speed`, whose layout is a stripe and whose
117
+ * `raidlevel` enum (above) has no stripe to declare. `createForm` (zfs-pool-write.ts) omits
118
+ * both keys when they are absent, and `createPool` refuses before any POST if the pool it
119
+ * would need to build is not already there — an adopt-only declaration never creates. Declare
120
+ * both fields together for a pool this resource should build from scratch; declaring only one
121
+ * of the two is refused the same way, since PVE's POST requires both.
115
122
  */
116
- devices: readonly string[];
117
- raidlevel: ZfsRaidLevel;
123
+ devices?: readonly string[];
124
+ raidlevel?: ZfsRaidLevel;
118
125
  /**
119
126
  * Sector size exponent, 9–16, PVE default 12.
120
127
  *
@@ -174,63 +181,6 @@ export const ProxmoxZfsPool = Resource<ProxmoxZfsPool>('Proxmox.ZfsPool', {
174
181
  defaultRemovalPolicy: 'retain',
175
182
  });
176
183
 
177
- /**
178
- * The device paths at the bottom of PVE's vdev tree, in the order ZFS reports them.
179
- *
180
- * ⚠️ "NO CHILDREN" IS THE LEAF TEST, AND IT IS NOT A GUESS — MEASURED in the node's `preparetree`,
181
- * which sets `leaf` to 0 when there are children and 1 otherwise. Reading the flag would work
182
- * equally well; recursing on `children` needs no second field to be present and is what makes
183
- * nested sections (mirrors inside a raid10, `spares`, `cache`) flatten correctly.
184
- */
185
- const leafDevices = (children: unknown): string[] =>
186
- Array.isArray(children)
187
- ? children.flatMap((entry: unknown) => {
188
- const vdev = entry as { children?: unknown; name?: unknown };
189
- const nested = leafDevices(vdev.children);
190
- return nested.length > 0 ? nested : [text(vdev.name)].filter((name) => name !== '');
191
- })
192
- : [];
193
-
194
- const spec: PveSpec<ZfsPoolProps, ZfsPoolAttributes> = {
195
- /**
196
- * ⚠️ `scan`, `status` AND `action` ARE DELIBERATELY NOT ATTRIBUTES. All three are advisory
197
- * strings ZFS rewrites on its own — `scan` on every scrub, the other two as feature flags and
198
- * faults come and go — so keeping them would rewrite this resource's state row for reasons no
199
- * declaration caused. Same reasoning as `digest` in storage.ts.
200
- */
201
- attributes: (live, props) => {
202
- // ⚠️ A detail answer with no `name` is not a pool. `name` is non-optional in PVE's schema, so
203
- // this is a shape check rather than a default — and "absent" is the honest reading of a
204
- // reply that does not describe the object that was asked for.
205
- if (text(live['name']) === '') return undefined;
206
- return {
207
- devices: leafDevices(live['children']).join(','),
208
- errors: text(live['errors'], 'unknown'),
209
- name: props.name,
210
- node: props.node,
211
- state: text(live['state'], 'unknown'),
212
- };
213
- },
214
- collection: (props) => `nodes/${props.node}/disks/zfs`,
215
- createForm,
216
- endpoint: ZFS_POOL_ENDPOINT,
217
- /**
218
- * ⛔ IT ALWAYS ANSWERS TRUE, AND THAT IS THE POINT OF THIS FILE. A pool that is there under the
219
- * declared name on the declared node IS the declaration, because there is nothing else the two
220
- * can be compared on: every create parameter is write-only (see the ⛔ in the header) and
221
- * everything the read does return is telemetry that moves by itself. So declaring what is live
222
- * plans as `noop` — the only honest answer available, and the one the live cluster needs:
223
- * `rpool` exists on node-b, node-c and node-d with nothing about its construction readable.
224
- * ⛔ ANYTHING ADDED HERE IS A REPLACE, NOT AN UPDATE. `updateForm` is omitted because PVE has
225
- * no PUT, so `resource.ts` turns a false into `{action:'replace'}` — delete then create — on
226
- * a family whose delete is `zpool destroy`. A comparison added here would be one plan away
227
- * from destroying a pool over a field it cannot even read back. If a future PVE grows a PUT,
228
- * add `updateForm` FIRST and only then consider comparing what that PUT accepts.
229
- */
230
- matches: () => true,
231
- path: (props) => `nodes/${props.node}/disks/zfs/${props.name}`,
232
- };
233
-
234
184
  const ops = pveOperations(spec);
235
185
 
236
186
  /**
@@ -5,7 +5,9 @@
5
5
  */
6
6
  import { describe, expect, test } from 'bun:test';
7
7
  import { binaryProblems } from './binary-form.ts';
8
+ import type { ReleaseCatalog } from './catalog.ts';
8
9
  import { catalogBinary, catalogDirectory, catalogProblems, identifyBinary } from './catalog.ts';
10
+ import { OPENBAO_RELEASES } from './openbao.ts';
9
11
  import { BinaryRefused } from './refused.ts';
10
12
  import { VICTORIA_RELEASES } from './victoria.ts';
11
13
 
@@ -93,4 +95,73 @@ describe('identifying an installed file', () => {
93
95
  const archive = 'cbe83f1d409cbb85fbf1c890c4a9b7fd34c1d74acec4dce2e0ebe46fcb260e38';
94
96
  expect(identifyBinary([VICTORIA_RELEASES], archive)).toBeUndefined();
95
97
  });
98
+
99
+ test('by a COMPUTED digest, same as a vendor one — OpenBao pins bao only there', () => {
100
+ const computed = 'd476d17e81a35e6d70dd7e86a8ab2a3664313525118f1f1cfe0130e7a2b95f3a';
101
+ expect(identifyBinary([VICTORIA_RELEASES, OPENBAO_RELEASES], computed)).toEqual({
102
+ binary: 'bao',
103
+ package: 'openbao',
104
+ platform: 'darwin_arm64',
105
+ vendor: 'OpenBao',
106
+ version: '2.6.2',
107
+ });
108
+ });
109
+ });
110
+
111
+ /** A fabricated one-entry catalog, only to exercise the `computed`/`members` overlap refusal. */
112
+ const doubled = (extra: {
113
+ members?: Record<string, string>;
114
+ computed?: Record<string, { sha256: string; recorded: string }>;
115
+ }): ReleaseCatalog => ({
116
+ packages: {
117
+ thing: {
118
+ binaries: { thing: 'thing-bin' },
119
+ versions: {
120
+ '1.0.0': {
121
+ any: {
122
+ asset: 'thing-1.0.0.tar.gz',
123
+ checksums: {
124
+ recorded: '2026-09-23',
125
+ sha256: 'a'.repeat(64),
126
+ url: 'https://example.invalid/checksums.txt',
127
+ },
128
+ // ⚠️ Spread, not a plain key: `exactOptionalPropertyTypes` treats an explicit
129
+ // `computed: undefined` as different from the key being absent.
130
+ ...(extra.computed !== undefined ? { computed: extra.computed } : {}),
131
+ members: extra.members ?? {},
132
+ repo: 'example/thing',
133
+ sha256: 'b'.repeat(64),
134
+ size: 1,
135
+ tag: 'v1.0.0',
136
+ },
137
+ },
138
+ },
139
+ },
140
+ },
141
+ vendor: 'Example',
142
+ });
143
+
144
+ const THING = { binary: 'thing', package: 'thing', platform: 'any', version: '1.0.0' };
145
+
146
+ describe('a member pinned twice, once vendor and once computed', () => {
147
+ test('catalogProblems names it, rather than silently preferring one', () => {
148
+ const catalog = doubled({
149
+ computed: { 'thing-bin': { recorded: '2026-09-23', sha256: 'c'.repeat(64) } },
150
+ members: { 'thing-bin': 'c'.repeat(64) },
151
+ });
152
+ const problems = catalogProblems(catalog, THING);
153
+ expect(problems.join('; ')).toContain('pinned twice');
154
+ expect(() => catalogBinary(catalog, THING)).toThrow('pinned twice');
155
+ });
156
+
157
+ test('computed alone resolves; vendor alone resolves; neither refuses "no digest"', () => {
158
+ const computedOnly = doubled({
159
+ computed: { 'thing-bin': { recorded: '2026-09-23', sha256: 'c'.repeat(64) } },
160
+ });
161
+ expect(catalogBinary(computedOnly, THING).sha256).toBe('c'.repeat(64));
162
+ const vendorOnly = doubled({ members: { 'thing-bin': 'd'.repeat(64) } });
163
+ expect(catalogBinary(vendorOnly, THING).sha256).toBe('d'.repeat(64));
164
+ const neither = doubled({});
165
+ expect(() => catalogBinary(neither, THING)).toThrow('pins no digest');
166
+ });
96
167
  });
@@ -13,16 +13,55 @@
13
13
  * ⛔ ADD A VENDOR ONLY AFTER ITS OWN WALK-DOWN (docs/release-binary-catalogs.md): its checksum
14
14
  * format read byte-exact, and its binary's linkage measured (`otool -L`). Vendor binaries were
15
15
  * measured self-contained ONLY for the five Victoria ones; that is not a property of "Go".
16
+ * ★ `computed` IS A SECOND, LESSER OWNER OF A MEMBER'S DIGEST — never the vendor's, always ours,
17
+ * for a checksum file that never lists the member at all (OpenBao: archives and SBOMs only,
18
+ * never `bao`; docs/release-binary-openbao.md is the walk-down). `checksums.signature` records
19
+ * how the checksum file's OWN authorship was checked, if a vendor publishes one to check.
16
20
  */
17
21
  import type { ReleaseArchive, ReleaseBinaryProps } from './binary-form.ts';
18
22
  import { BinaryRefused } from './refused.ts';
19
23
 
24
+ /**
25
+ * A member digest WE computed, because the vendor's checksum file never lists it — read
26
+ * checksums.ts's header before adding one: most vendors list archives only.
27
+ */
28
+ export interface ComputedDigest {
29
+ readonly sha256: string;
30
+ /** The date it was computed, from the archive whose pinned SHA-256 matched that day. */
31
+ readonly recorded: string;
32
+ }
33
+
34
+ /** How the checksum file itself was checked, beyond its own SHA-256 — openpgp today. */
35
+ export interface ChecksumSignature {
36
+ readonly kind: 'openpgp';
37
+ /** The detached signature asset's URL. */
38
+ readonly url: string;
39
+ readonly sha256: string;
40
+ /** The public key it was checked against. */
41
+ readonly key: { readonly url: string; readonly sha256: string; readonly fingerprint: string };
42
+ /** The date `gpgv` was run, recorded once — never re-run at apply or by CI. */
43
+ readonly verified: string;
44
+ }
45
+
20
46
  /** One release archive as a catalog records it: the pin, plus where every digest came from. */
21
47
  export interface PinnedArchive extends ReleaseArchive {
22
48
  /** Archive member → its SHA-256. Every member the vendor's checksum file lists, not just ours. */
23
49
  readonly members: Readonly<Record<string, string>>;
50
+ /**
51
+ * Archive member → a digest WE computed, for a checksum file that never lists it (OpenBao's
52
+ * lists archives and SBOMs, never `bao`). ⛔ NOT A VENDOR FACT: computed from the bytes of an
53
+ * archive whose own SHA-256 already matched the pin, never filed beside `members`. A member in
54
+ * both is refused (catalogProblems) — two owners of one pin is a data-set bug, not a choice.
55
+ */
56
+ readonly computed?: Readonly<Record<string, ComputedDigest>>;
24
57
  /** The checksum file the digests were copied from, its own SHA-256, and when. Never fetched. */
25
- readonly checksums: { readonly url: string; readonly sha256: string; readonly recorded: string };
58
+ readonly checksums: {
59
+ readonly url: string;
60
+ readonly sha256: string;
61
+ readonly recorded: string;
62
+ /** How the checksum file's own authorship was checked, if at all. Optional: not every vendor publishes one. */
63
+ readonly signature?: ChecksumSignature;
64
+ };
26
65
  }
27
66
 
28
67
  /** One package (one archive per version and platform) of a vendor. */
@@ -60,6 +99,20 @@ const quoted = (names: Iterable<string>): string => [...names].map((n) => `"${n}
60
99
  // ★ A plain boolean, not a type guard: on a `Record<string, …>` a guard narrows the miss to `never`.
61
100
  const has = (record: object, key: string): boolean => Object.hasOwn(record, key);
62
101
 
102
+ /**
103
+ * A member's SHA-256, vendor digest first, then computed — or `'both'` when it is pinned in both,
104
+ * which is refused rather than silently preferring one owner over the other.
105
+ */
106
+ const memberDigest = (archive: PinnedArchive, member: string): string | 'both' | undefined => {
107
+ const vendor = has(archive.members, member) ? archive.members[member] : undefined;
108
+ const computed =
109
+ archive.computed !== undefined && has(archive.computed, member)
110
+ ? archive.computed[member]?.sha256
111
+ : undefined;
112
+ if (vendor !== undefined && computed !== undefined) return 'both';
113
+ return vendor ?? computed;
114
+ };
115
+
63
116
  /** Everything wrong with a request against a catalog; empty when it resolves. */
64
117
  export const catalogProblems = (catalog: ReleaseCatalog, request: CatalogRequest): string[] => {
65
118
  const entry = has(catalog.packages, request.package)
@@ -97,9 +150,16 @@ export const catalogProblems = (catalog: ReleaseCatalog, request: CatalogRequest
97
150
  );
98
151
  } else if (found.length === 0) {
99
152
  const member = entry.binaries[request.binary] ?? '';
100
- if (!has(archive.members, member)) {
153
+ const digest = memberDigest(archive, member);
154
+ if (digest === 'both') {
155
+ found.push(
156
+ `${request.package} ${request.version}'s member "${member}" is pinned twice — once as a ` +
157
+ 'vendor digest and once as a computed one; a member has exactly one owner',
158
+ );
159
+ } else if (digest === undefined) {
101
160
  found.push(
102
- `${request.package} ${request.version}'s checksum file lists no member "${member}"`,
161
+ `${request.package} ${request.version} pins no digest for member "${member}" (checked ` +
162
+ 'the vendor checksum file and any computed record)',
103
163
  );
104
164
  }
105
165
  }
@@ -118,7 +178,9 @@ export const catalogBinary = (catalog: ReleaseCatalog, request: CatalogRequest):
118
178
  const entry = own(catalog.packages, request.package);
119
179
  const archive = own(own(entry?.versions, request.version), request.platform);
120
180
  const member = own(entry?.binaries, request.binary);
121
- const sha256 = member === undefined ? undefined : own(archive?.members, member);
181
+ const digest =
182
+ member === undefined || archive === undefined ? undefined : memberDigest(archive, member);
183
+ const sha256 = digest === 'both' ? undefined : digest;
122
184
  if (
123
185
  problems.length > 0 ||
124
186
  archive === undefined ||
@@ -163,7 +225,8 @@ export const identifyBinary = (
163
225
  for (const [version, platforms] of Object.entries(entry.versions)) {
164
226
  for (const [platform, archive] of Object.entries(platforms)) {
165
227
  for (const [binary, member] of Object.entries(entry.binaries)) {
166
- if (archive.members[member] === sha256) {
228
+ const digest = memberDigest(archive, member);
229
+ if (digest !== 'both' && digest === sha256) {
167
230
  return { binary, package: pkg, platform, vendor: catalog.vendor, version };
168
231
  }
169
232
  }
@@ -0,0 +1,83 @@
1
+ a7784550a9db16f24e99d65a18c9b12a433707c79ef4c1f34262d3f48171c7a9 openbao-dist-v2.6.2.tar.xz
2
+ f19784c4c865ee66b6dda164e0535fa6ca6ac2d45e5edb7ed0e0a5dc08bd9c3c openbao-hsm_2.6.2_linux_amd64.deb
3
+ 2c4ec5c393d7b8c69f560fd8f92ba7c9a12d70044b88f94b8b90ec27dcee9e2d openbao-hsm_2.6.2_linux_amd64.deb.sbom.json
4
+ 8aebfef4f6537777c68c4ff6d84741aaf50a2b7c40e0ac1f9cef36e00f29fb84 openbao-hsm_2.6.2_linux_amd64.pkg.tar.zst
5
+ 2b1c9dc60858cb08bcb30f931861b6b7af7ae17135dc8f4c253b32823616f9b9 openbao-hsm_2.6.2_linux_amd64.pkg.tar.zst.sbom.json
6
+ 7242e327c66c85c3d720395a94de06b9a08b8394db18b656f3313fa2cc654f0e openbao-hsm_2.6.2_linux_amd64.rpm
7
+ 8bd3ee543abd527031a4bc4ddcfff3d5a02a089ab89d34a18f067ff5c13539b1 openbao-hsm_2.6.2_linux_amd64.rpm.sbom.json
8
+ 340511b6f87662b80252c202a7c5aa90dbe32341ea741458d49ce6839c2d7721 openbao-hsm_2.6.2_linux_amd64.tar.gz
9
+ dfc89e52387e4e91065b1678e7612268161e578c0e987ae745b85e0db897e6b4 openbao-hsm_2.6.2_linux_amd64.tar.gz.sbom.json
10
+ ecbbe80b120ea108a31e8a8157f613901b7441761c882a9bc042314ac71824e4 openbao-hsm_2.6.2_linux_arm64.deb
11
+ ee141346e8ee1c52725b08811caa92845243411a695d14bf6366fd2ef839cfb1 openbao-hsm_2.6.2_linux_arm64.deb.sbom.json
12
+ f0444501b81f731a9c9c8f41fd41dea359a3a2caacafc2f54bc5170ccc3f5fe6 openbao-hsm_2.6.2_linux_arm64.pkg.tar.zst
13
+ 925609394ea12da34b2f94d79d8d01d8b8a2174788e4a7211ae69a3600d1d3ab openbao-hsm_2.6.2_linux_arm64.pkg.tar.zst.sbom.json
14
+ fff1b905c697bc5ef28e905e1ed4c670c101017f81a23bed859e7eed29faf325 openbao-hsm_2.6.2_linux_arm64.rpm
15
+ bf13d04c98d5d7cdc7cbc608ebd20519f3879b149c60eff6b8da7af0466bb3f8 openbao-hsm_2.6.2_linux_arm64.rpm.sbom.json
16
+ b796dae3269323a06ae198732f0c3727f8f041df9e45893a3d7ec5eeac627730 openbao-hsm_2.6.2_linux_arm64.tar.gz
17
+ 05a6e5fb308a7b5b16555d7599f99bae0d4ad9f034e7d005162f8d927d4428bc openbao-hsm_2.6.2_linux_arm64.tar.gz.sbom.json
18
+ 64fdf1ce8f410bbc1531d2f0ea142d21b4e755b986542025a00294999a8cfaa5 openbao_2.6.2_darwin_amd64.tar.gz
19
+ c9b5cd775a297705b46d562b5902e96071796b47f6792768eca1c38d17a4ba21 openbao_2.6.2_darwin_amd64.tar.gz.sbom.json
20
+ 4e495376174accc0e014d31e9901f518a974f966850c839f626347eaac05fd52 openbao_2.6.2_darwin_arm64.tar.gz
21
+ d349558495345d314284fd7a554467c0e28e154a71982fdea17f1cd813101de7 openbao_2.6.2_darwin_arm64.tar.gz.sbom.json
22
+ 2f6b1f77c9ce7cb83d87a2c85d546c2bb6761cef013efe99654cef9ca5177b5d openbao_2.6.2_freebsd_amd64.tar.gz
23
+ 30b8cc4ca539371917bd170e3f4e1ee6fb9874f8e538ba561f2811eb39b09e94 openbao_2.6.2_freebsd_amd64.tar.gz.sbom.json
24
+ 572a4995663dcb7affe6ba30dcbeed0180612f81e641db1e8c64ebe9c246c7ea openbao_2.6.2_freebsd_arm64.tar.gz
25
+ 486da7fcc18cf266168b30e6eb88463624fbc01a195fc15a0499989b172658fe openbao_2.6.2_freebsd_arm64.tar.gz.sbom.json
26
+ 3fc3b24dbfbb48d6b89d919b546954f376e18f90d5a6998574815d89a805a299 openbao_2.6.2_freebsd_armv6.tar.gz
27
+ 1eaaac21642f9eb018b4cdb090943c9088d8446822a464898cf69d474f0d9601 openbao_2.6.2_freebsd_armv6.tar.gz.sbom.json
28
+ f22b703e626d03d1595c21cf1c57c8737675b721daa3b8e3972531ceafcc3993 openbao_2.6.2_linux_amd64.deb
29
+ c176ef194d77706c09432326f8e0f029fb8d2550e66ed275854fa400d23a9d92 openbao_2.6.2_linux_amd64.deb.sbom.json
30
+ 3c1bd831cca649bd47add1d7204b64cf66266b52b0631c772cfb23af44c7980e openbao_2.6.2_linux_amd64.pkg.tar.zst
31
+ 3dab2410454f0bb6dd4289ad4f8df14a2342a2c7b203454c34b2d6b90cf941ad openbao_2.6.2_linux_amd64.pkg.tar.zst.sbom.json
32
+ 586262fc6e49324efeee7969798a75f72402f30ba9edc2966f73c2dcc60aa48c openbao_2.6.2_linux_amd64.rpm
33
+ b760636d3ff65c04fd7364211e5dec44ca53340c916ae0245af2c377255aba3f openbao_2.6.2_linux_amd64.rpm.sbom.json
34
+ 8dc11cc5fca0b539a9e352727dacb4e2d304daffcf9a66e0718ac325a20d05aa openbao_2.6.2_linux_amd64.tar.gz
35
+ d29dd86fed21a16a1c5b4d0b8c785299274a4fde3976041cf545f36dc17d7df6 openbao_2.6.2_linux_amd64.tar.gz.sbom.json
36
+ 554e0f196394edc5b35267dc8888e28eaf0f3a7de01271f232c5d93e709b6cf8 openbao_2.6.2_linux_arm64.deb
37
+ 486a83df4a5cf3837cbede91b147f402a8ce264073886eafdd95189015092ed7 openbao_2.6.2_linux_arm64.deb.sbom.json
38
+ a7ff5fb158597a241d3cac77770034b77366d04048ea8d6bf51ffed16355fec7 openbao_2.6.2_linux_arm64.pkg.tar.zst
39
+ 02711ec8581b8a26819f038c043bdd874919d48dbec47ea8dc60a4e60ffe288a openbao_2.6.2_linux_arm64.pkg.tar.zst.sbom.json
40
+ 1bedfbe3a24925c0e037564ea4504a8890aff7924fb72d6d699118d3cf8d61b5 openbao_2.6.2_linux_arm64.rpm
41
+ e8a7e5d81e07c772489f573965a3a8fba03b52dbe57ed7e8afde2186a6c896bb openbao_2.6.2_linux_arm64.rpm.sbom.json
42
+ 1b408e01f3565ac0cbcb88d637dca271d0515148fb72efdeff4473a34fa50c4e openbao_2.6.2_linux_arm64.tar.gz
43
+ f3b4b4757a685d922244b738edf0d23143f4ce66155758cc280e6131dd225d60 openbao_2.6.2_linux_arm64.tar.gz.sbom.json
44
+ d2a5e2d0d66bd64ee1669902905010747dbc13f1662871b95f9fc53b2c8207d7 openbao_2.6.2_linux_armv6.deb
45
+ 407c5b21216eb8422813d822f0b6a524bb7b0600a2acc4fd880a71396676de8a openbao_2.6.2_linux_armv6.deb.sbom.json
46
+ f40ad101126827474857f21d0b43bb481209e1c728bf3a0d5afd786df773999e openbao_2.6.2_linux_armv6.rpm
47
+ 40bd8f614ad2951d06b9dc60fc12bd2fb6ccc917ab7f2154fe004a7df64fcd5a openbao_2.6.2_linux_armv6.rpm.sbom.json
48
+ 7c7d37409d9898d0203a3000fe9e51dece44ef57bd92daffeedd9cc75ee5a35e openbao_2.6.2_linux_armv6.tar.gz
49
+ 95cd1d1e268e3d5f410f0b085b2b2038e1bf7a682057521ebf68e3a87e89a99f openbao_2.6.2_linux_armv6.tar.gz.sbom.json
50
+ b0b385406d52491b53c5d7d550b88fedd8ccb85ec159c6093a536b101bb6ea24 openbao_2.6.2_linux_ppc64le.deb
51
+ f3c5e4309ec51e02c2c8e65178aa55af80dd3f0c077a544ad275300757b09de5 openbao_2.6.2_linux_ppc64le.deb.sbom.json
52
+ 166ae4749775f63d1396a6be584f1828b9412efc1a50861ed6815286a4baeae9 openbao_2.6.2_linux_ppc64le.rpm
53
+ aa489019a315045c37c046340c0d1f16a0dd694432f901551325af6f334b147f openbao_2.6.2_linux_ppc64le.rpm.sbom.json
54
+ 5288e7183dc7844cd3b025f33f140856797b6a93674d9637dcf070b9c081da3d openbao_2.6.2_linux_ppc64le.tar.gz
55
+ 4571e8e4315d20a5760d90d340e2dd4e8dff763b540416e1a17ee52e7b39f158 openbao_2.6.2_linux_ppc64le.tar.gz.sbom.json
56
+ f0d06b2efb2a0717cbcc33efa2ec6416a76bb1842d22a4e84deff016a5ae9981 openbao_2.6.2_linux_riscv64.deb
57
+ c3350ebc21aa1815d8bd6f22cb1eeb0239f49cfe66566178b91605627828c362 openbao_2.6.2_linux_riscv64.deb.sbom.json
58
+ 5d4c85e8dade07e58bedfab1e56823a5a9e68fa2b9830b88480c0083f305d6c1 openbao_2.6.2_linux_riscv64.rpm
59
+ b78a5a4f448cf4d4339543cef969b6680172d0c3b30ec18aacd8ce17ddd048fe openbao_2.6.2_linux_riscv64.rpm.sbom.json
60
+ 177614c6371194c2318a5cf415a3a141337a0bf5e3092de3fa291185dfe9f695 openbao_2.6.2_linux_riscv64.tar.gz
61
+ aef41cf1b5b1e10fb0b679c6e119b4e90123fe7bff3243f859df3bca206427e1 openbao_2.6.2_linux_riscv64.tar.gz.sbom.json
62
+ 2b2c2474e69728361263a636798408f6744ad9bba891db60b0e8a8bfdf4a2c81 openbao_2.6.2_linux_s390x.deb
63
+ d4d6253c90ebf5ce4d295011062ea1eb9d4c02d805ec965b02650f38802e723e openbao_2.6.2_linux_s390x.deb.sbom.json
64
+ 33f30660dfbfcfe0d5804939329128d0025d37a9b887f3e1b2450fd4591a10c6 openbao_2.6.2_linux_s390x.rpm
65
+ a555bdec0e39ced1400199f9c66a59c6215d747b32cf2672078c321d20119302 openbao_2.6.2_linux_s390x.rpm.sbom.json
66
+ dd987c5d69dfe93bf918c7f4fd7aee438fc60d5f292c84ed670aec95b6943daf openbao_2.6.2_linux_s390x.tar.gz
67
+ 663c48d0dfcec4835ba26a36b4bea4ed1795cd7c917eb511ed6c93ca39d09d73 openbao_2.6.2_linux_s390x.tar.gz.sbom.json
68
+ 8dc6d8f19148b87d41b4f7eb87944b27efd93f624c4e4838fc66380d0601a3a8 openbao_2.6.2_netbsd_amd64.tar.gz
69
+ 88533203c066d62a24fd3c7e4b0c8f8166d3bab0c3d2881da6318a224099b208 openbao_2.6.2_netbsd_amd64.tar.gz.sbom.json
70
+ 7639564e055a8523d73e6efe0ed950bf25ead0c1e8661db4b5a32a6c8e35b75c openbao_2.6.2_netbsd_arm64.tar.gz
71
+ 3c0c3d2bddee7a4be2e1393ffdf7b5f8ab439747d649340b747eedb4be1f0b76 openbao_2.6.2_netbsd_arm64.tar.gz.sbom.json
72
+ 0e39bcbbbae69a4def27922080cc47ba1b42ea67308f101c47584ebd24dfe544 openbao_2.6.2_netbsd_armv6.tar.gz
73
+ c25e9f9065f2d5484091a40dc5c797ff40a19fad0382bb48f60b3e0a469bb294 openbao_2.6.2_netbsd_armv6.tar.gz.sbom.json
74
+ 289d17cb38f0007beaa71ced90b6c7e34d2e3a190d997d7865b6c277d8a703b2 openbao_2.6.2_openbsd_amd64.tar.gz
75
+ 15c6f1e5fc54d89a2ba455b76c16ec5bff81f4c48a299117dc58d5aaf383588b openbao_2.6.2_openbsd_amd64.tar.gz.sbom.json
76
+ faa52e6ae74544192ed6fbd40ca631d808c1ab480067adc61450eb3a550a649c openbao_2.6.2_openbsd_arm64.tar.gz
77
+ a11cd6d9269c10f386f34d6051fd3089d94808226f4e2113ceb914c9e215ad2a openbao_2.6.2_openbsd_arm64.tar.gz.sbom.json
78
+ 4002255fe061cec3f8e4494b4bb2464a79abfd67ea683ce16b3ae73ab004e151 openbao_2.6.2_openbsd_armv6.tar.gz
79
+ dc8fe407c9917f3b4e8c9593782c591f70b23e6e8e05642b67f852527dbd6830 openbao_2.6.2_openbsd_armv6.tar.gz.sbom.json
80
+ 12e8d40c71fe5a0aa23062c6c36a77a96717005980e30d0d42acdea3268d3fa2 openbao_2.6.2_windows_amd64.zip
81
+ b19219d3795e772b53a81cee1deaf1139364411e2c57359d0745cda9afdf097e openbao_2.6.2_windows_amd64.zip.sbom.json
82
+ e4fa1edb95129c36a36cfc5ad1b96198208344df42f66371b14fe579328ec662 openbao_2.6.2_windows_arm64.zip
83
+ 179c8de71459d2d6059939b23ab9b5012ace519340820c1beabcd882364fae7c openbao_2.6.2_windows_arm64.zip.sbom.json
@@ -0,0 +1,12 @@
1
+ openbao_2.6.2_darwin_amd64.tar.gz
2
+ openbao_2.6.2_darwin_amd64.tar.gz.gpgsig
3
+ openbao_2.6.2_darwin_amd64.tar.gz.sbom.json
4
+ openbao_2.6.2_darwin_amd64.tar.gz.sbom.json.gpgsig
5
+ openbao_2.6.2_darwin_amd64.tar.gz.sbom.json.sigstore.json
6
+ openbao_2.6.2_darwin_amd64.tar.gz.sigstore.json
7
+ openbao_2.6.2_darwin_arm64.tar.gz
8
+ openbao_2.6.2_darwin_arm64.tar.gz.gpgsig
9
+ openbao_2.6.2_darwin_arm64.tar.gz.sbom.json
10
+ openbao_2.6.2_darwin_arm64.tar.gz.sbom.json.gpgsig
11
+ openbao_2.6.2_darwin_arm64.tar.gz.sbom.json.sigstore.json
12
+ openbao_2.6.2_darwin_arm64.tar.gz.sigstore.json
@@ -0,0 +1,109 @@
1
+ -----BEGIN PGP PUBLIC KEY BLOCK-----
2
+
3
+ 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
108
+ =coUG
109
+ -----END PGP PUBLIC KEY BLOCK-----
@@ -22,12 +22,15 @@ export { ReleaseBinary, ReleaseBinaryProvider } from './binary.ts';
22
22
  export type {
23
23
  CatalogPackage,
24
24
  CatalogRequest,
25
+ ChecksumSignature,
26
+ ComputedDigest,
25
27
  IdentifiedBinary,
26
28
  PinnedArchive,
27
29
  PinnedBinary,
28
30
  ReleaseCatalog,
29
31
  } from './catalog.ts';
30
32
  export { catalogBinary, catalogDirectory, catalogProblems, identifyBinary } from './catalog.ts';
33
+ export { OPENBAO_RELEASES } from './openbao.ts';
31
34
  export { releaseProviders } from './providers.ts';
32
35
  export { ArchiveRefused, BinaryRefused, ChecksumMismatch, DownloadFailed } from './refused.ts';
33
36
  export { VICTORIA_RELEASES } from './victoria.ts';