@homeflare/alchemy 0.39.0 → 0.40.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 (207) hide show
  1. package/README.md +10 -8
  2. package/dist/ceph/auth-entity.d.ts +34 -0
  3. package/dist/ceph/auth-entity.d.ts.map +1 -0
  4. package/dist/ceph/ceph-argv-config.d.ts +10 -0
  5. package/dist/ceph/ceph-argv-config.d.ts.map +1 -0
  6. package/dist/ceph/ceph-argv.d.ts +19 -0
  7. package/dist/ceph/ceph-argv.d.ts.map +1 -0
  8. package/dist/ceph/ceph-auth-form.d.ts +54 -0
  9. package/dist/ceph/ceph-auth-form.d.ts.map +1 -0
  10. package/dist/ceph/ceph-auth-parse.d.ts +56 -0
  11. package/dist/ceph/ceph-auth-parse.d.ts.map +1 -0
  12. package/dist/ceph/ceph-auth-reconcile.d.ts +35 -0
  13. package/dist/ceph/ceph-auth-reconcile.d.ts.map +1 -0
  14. package/dist/ceph/ceph-config-options.d.ts +23 -0
  15. package/dist/ceph/ceph-config-options.d.ts.map +1 -0
  16. package/dist/ceph/ceph-key-leak-assert.d.ts +25 -0
  17. package/dist/ceph/ceph-key-leak-assert.d.ts.map +1 -0
  18. package/dist/ceph/ceph-transport.d.ts +37 -0
  19. package/dist/ceph/ceph-transport.d.ts.map +1 -0
  20. package/dist/ceph/fake-ceph-dial.d.ts +35 -0
  21. package/dist/ceph/fake-ceph-dial.d.ts.map +1 -0
  22. package/dist/ceph/index.d.ts +13 -0
  23. package/dist/ceph/index.d.ts.map +1 -0
  24. package/dist/ceph/index.js +1609 -0
  25. package/dist/ceph/index.js.map +38 -0
  26. package/dist/index.d.ts +1 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/proxmox/index.js +15 -6
  29. package/dist/proxmox/index.js.map +3 -3
  30. package/dist/proxmox/qemu-volume.d.ts +12 -2
  31. package/dist/proxmox/qemu-volume.d.ts.map +1 -1
  32. package/dist/talos/credentials-write.d.ts +46 -0
  33. package/dist/talos/credentials-write.d.ts.map +1 -0
  34. package/dist/talos/credentials.d.ts +51 -9
  35. package/dist/talos/credentials.d.ts.map +1 -1
  36. package/dist/talos/fake-process.d.ts +56 -0
  37. package/dist/talos/fake-process.d.ts.map +1 -0
  38. package/dist/talos/index.d.ts +19 -4
  39. package/dist/talos/index.d.ts.map +1 -1
  40. package/dist/talos/index.js +357 -173
  41. package/dist/talos/index.js.map +16 -10
  42. package/dist/talos/kubeconfig-attrs.d.ts +4 -0
  43. package/dist/talos/kubeconfig-attrs.d.ts.map +1 -0
  44. package/dist/talos/kubeconfig.d.ts +41 -23
  45. package/dist/talos/kubeconfig.d.ts.map +1 -1
  46. package/dist/talos/machine-config-fixtures.d.ts +60 -0
  47. package/dist/talos/machine-config-fixtures.d.ts.map +1 -0
  48. package/dist/talos/machine-config-poll.d.ts +43 -0
  49. package/dist/talos/machine-config-poll.d.ts.map +1 -0
  50. package/dist/talos/machine-config-read.d.ts +49 -0
  51. package/dist/talos/machine-config-read.d.ts.map +1 -0
  52. package/dist/talos/resource.d.ts +18 -0
  53. package/dist/talos/resource.d.ts.map +1 -1
  54. package/dist/talos/talos-bootstrap-etcd.d.ts +38 -0
  55. package/dist/talos/talos-bootstrap-etcd.d.ts.map +1 -0
  56. package/dist/talos/talos-bootstrap.d.ts +87 -0
  57. package/dist/talos/talos-bootstrap.d.ts.map +1 -1
  58. package/dist/talos/talos-cluster-health.d.ts +43 -1
  59. package/dist/talos/talos-cluster-health.d.ts.map +1 -1
  60. package/dist/talos/talos-errors.d.ts +68 -0
  61. package/dist/talos/talos-errors.d.ts.map +1 -0
  62. package/dist/talos/talos-machine-config.d.ts +51 -15
  63. package/dist/talos/talos-machine-config.d.ts.map +1 -1
  64. package/dist/talos/talosctl.d.ts +7 -0
  65. package/dist/talos/talosctl.d.ts.map +1 -1
  66. package/dist/talos/values.d.ts +23 -3
  67. package/dist/talos/values.d.ts.map +1 -1
  68. package/dist/unifi/acl-rule-drift.d.ts +22 -0
  69. package/dist/unifi/acl-rule-drift.d.ts.map +1 -0
  70. package/dist/unifi/acl-rule-form.d.ts +90 -0
  71. package/dist/unifi/acl-rule-form.d.ts.map +1 -0
  72. package/dist/unifi/acl-rule-ordering-form.d.ts +49 -0
  73. package/dist/unifi/acl-rule-ordering-form.d.ts.map +1 -0
  74. package/dist/unifi/acl-rule-ordering.d.ts +54 -0
  75. package/dist/unifi/acl-rule-ordering.d.ts.map +1 -0
  76. package/dist/unifi/acl-rule.d.ts +49 -0
  77. package/dist/unifi/acl-rule.d.ts.map +1 -0
  78. package/dist/unifi/dns-policy-drift.d.ts +21 -0
  79. package/dist/unifi/dns-policy-drift.d.ts.map +1 -0
  80. package/dist/unifi/dns-policy-form.d.ts +97 -0
  81. package/dist/unifi/dns-policy-form.d.ts.map +1 -0
  82. package/dist/unifi/dns-policy.d.ts +55 -0
  83. package/dist/unifi/dns-policy.d.ts.map +1 -0
  84. package/dist/unifi/firewall-policy-drift.d.ts +26 -0
  85. package/dist/unifi/firewall-policy-drift.d.ts.map +1 -0
  86. package/dist/unifi/firewall-policy-form.d.ts +102 -0
  87. package/dist/unifi/firewall-policy-form.d.ts.map +1 -0
  88. package/dist/unifi/firewall-policy-ordering-form.d.ts +66 -0
  89. package/dist/unifi/firewall-policy-ordering-form.d.ts.map +1 -0
  90. package/dist/unifi/firewall-policy-ordering.d.ts +57 -0
  91. package/dist/unifi/firewall-policy-ordering.d.ts.map +1 -0
  92. package/dist/unifi/firewall-policy.d.ts +57 -0
  93. package/dist/unifi/firewall-policy.d.ts.map +1 -0
  94. package/dist/unifi/index.d.ts +7 -0
  95. package/dist/unifi/index.d.ts.map +1 -1
  96. package/dist/unifi/index.js +588 -3
  97. package/dist/unifi/index.js.map +24 -4
  98. package/dist/unifi/paginate.d.ts +0 -12
  99. package/dist/unifi/paginate.d.ts.map +1 -1
  100. package/dist/unifi/providers.d.ts +1 -1
  101. package/dist/unifi/providers.d.ts.map +1 -1
  102. package/dist/unifi/scan-source.d.ts +11 -0
  103. package/dist/unifi/scan-source.d.ts.map +1 -0
  104. package/dist/unifi/traffic-matching-list-drift.d.ts +22 -0
  105. package/dist/unifi/traffic-matching-list-drift.d.ts.map +1 -0
  106. package/dist/unifi/traffic-matching-list-form.d.ts +56 -0
  107. package/dist/unifi/traffic-matching-list-form.d.ts.map +1 -0
  108. package/dist/unifi/traffic-matching-list.d.ts +51 -0
  109. package/dist/unifi/traffic-matching-list.d.ts.map +1 -0
  110. package/dist/unifi/wifi-broadcast-drift.d.ts +22 -0
  111. package/dist/unifi/wifi-broadcast-drift.d.ts.map +1 -0
  112. package/dist/unifi/wifi-broadcast-form.d.ts +127 -0
  113. package/dist/unifi/wifi-broadcast-form.d.ts.map +1 -0
  114. package/dist/unifi/wifi-broadcast.d.ts +73 -0
  115. package/dist/unifi/wifi-broadcast.d.ts.map +1 -0
  116. package/docs/unifi-acl-rule.md +122 -0
  117. package/docs/unifi-dns-policy.md +75 -0
  118. package/docs/unifi-firewall-policy.md +109 -0
  119. package/docs/unifi-traffic-matching-list.md +82 -0
  120. package/docs/unifi-wifi.md +120 -0
  121. package/docs/unifi.md +10 -5
  122. package/package.json +6 -2
  123. package/src/ceph/auth-entity.ts +74 -0
  124. package/src/ceph/ceph-argv-config.ts +78 -0
  125. package/src/ceph/ceph-argv.test.ts +226 -0
  126. package/src/ceph/ceph-argv.ts +189 -0
  127. package/src/ceph/ceph-auth-form.test.ts +103 -0
  128. package/src/ceph/ceph-auth-form.ts +107 -0
  129. package/src/ceph/ceph-auth-parse.test.ts +155 -0
  130. package/src/ceph/ceph-auth-parse.ts +121 -0
  131. package/src/ceph/ceph-auth-reconcile-secrecy.test.ts +82 -0
  132. package/src/ceph/ceph-auth-reconcile.test.ts +233 -0
  133. package/src/ceph/ceph-auth-reconcile.ts +119 -0
  134. package/src/ceph/ceph-config-options.test.ts +44 -0
  135. package/src/ceph/ceph-config-options.ts +36 -0
  136. package/src/ceph/ceph-key-leak-assert.ts +35 -0
  137. package/src/ceph/ceph-no-output-logging.test.ts +52 -0
  138. package/src/ceph/ceph-transport.test.ts +125 -0
  139. package/src/ceph/ceph-transport.ts +117 -0
  140. package/src/ceph/fake-ceph-dial.ts +55 -0
  141. package/src/ceph/index.ts +12 -0
  142. package/src/index.ts +1 -0
  143. package/src/proxmox/qemu-volume.test.ts +153 -4
  144. package/src/proxmox/qemu-volume.ts +71 -14
  145. package/src/talos/credentials-write.test.ts +81 -0
  146. package/src/talos/credentials-write.ts +95 -0
  147. package/src/talos/credentials.test.ts +157 -0
  148. package/src/talos/credentials.ts +160 -90
  149. package/src/talos/fake-process.ts +94 -0
  150. package/src/talos/index.ts +39 -4
  151. package/src/talos/kubeconfig-attrs.ts +55 -0
  152. package/src/talos/kubeconfig.test.ts +256 -0
  153. package/src/talos/kubeconfig.ts +170 -90
  154. package/src/talos/machine-config-fixtures.ts +86 -0
  155. package/src/talos/machine-config-poll.test.ts +57 -0
  156. package/src/talos/machine-config-poll.ts +82 -0
  157. package/src/talos/machine-config-read.ts +118 -0
  158. package/src/talos/resource.ts +19 -0
  159. package/src/talos/secrets-gate.test.ts +63 -0
  160. package/src/talos/talos-bootstrap-etcd.ts +71 -0
  161. package/src/talos/talos-bootstrap.test.ts +218 -0
  162. package/src/talos/talos-bootstrap.ts +151 -45
  163. package/src/talos/talos-cluster-health.test.ts +143 -0
  164. package/src/talos/talos-cluster-health.ts +91 -28
  165. package/src/talos/talos-errors.ts +85 -0
  166. package/src/talos/talos-machine-config.test.ts +189 -0
  167. package/src/talos/talos-machine-config.ts +102 -88
  168. package/src/talos/talosctl.ts +8 -0
  169. package/src/talos/values.test.ts +34 -6
  170. package/src/talos/values.ts +41 -7
  171. package/src/unifi/acl-rule-drift.ts +57 -0
  172. package/src/unifi/acl-rule-form.ts +135 -0
  173. package/src/unifi/acl-rule-ordering-form.ts +79 -0
  174. package/src/unifi/acl-rule-ordering.test.ts +141 -0
  175. package/src/unifi/acl-rule-ordering.ts +70 -0
  176. package/src/unifi/acl-rule.test.ts +183 -0
  177. package/src/unifi/acl-rule.ts +61 -0
  178. package/src/unifi/discriminated-filter-decode.test.ts +134 -0
  179. package/src/unifi/dns-policy-drift.ts +48 -0
  180. package/src/unifi/dns-policy-form.ts +142 -0
  181. package/src/unifi/dns-policy.test.ts +154 -0
  182. package/src/unifi/dns-policy.ts +67 -0
  183. package/src/unifi/firewall-policy-drift.test.ts +151 -0
  184. package/src/unifi/firewall-policy-drift.ts +55 -0
  185. package/src/unifi/firewall-policy-filter-roundtrip.test.ts +143 -0
  186. package/src/unifi/firewall-policy-form.ts +146 -0
  187. package/src/unifi/firewall-policy-ordering-form.ts +111 -0
  188. package/src/unifi/firewall-policy-ordering.test.ts +185 -0
  189. package/src/unifi/firewall-policy-ordering.ts +87 -0
  190. package/src/unifi/firewall-policy.test.ts +183 -0
  191. package/src/unifi/firewall-policy.ts +73 -0
  192. package/src/unifi/index.ts +63 -0
  193. package/src/unifi/paginate.test.ts +24 -0
  194. package/src/unifi/paginate.ts +15 -3
  195. package/src/unifi/providers.ts +19 -1
  196. package/src/unifi/scan-source.ts +64 -0
  197. package/src/unifi/traffic-matching-list-drift.ts +40 -0
  198. package/src/unifi/traffic-matching-list-form.ts +77 -0
  199. package/src/unifi/traffic-matching-list.test.ts +202 -0
  200. package/src/unifi/traffic-matching-list.ts +78 -0
  201. package/src/unifi/wifi-broadcast-details-call.test.ts +92 -0
  202. package/src/unifi/wifi-broadcast-drift.ts +69 -0
  203. package/src/unifi/wifi-broadcast-form.ts +211 -0
  204. package/src/unifi/wifi-broadcast-secrets.test.ts +145 -0
  205. package/src/unifi/wifi-broadcast.test.ts +155 -0
  206. package/src/unifi/wifi-broadcast.ts +90 -0
  207. package/src/unifi/write-op-reference.test.ts +8 -54
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Post-apply convergence for `Talos.MachineConfig` — split out of talos-machine-config.ts
3
+ * (2026-09-26, K-A3) once the bounded-poll logic needed its own header. See
4
+ * docs/plans/2026-09-26-talos-stack-first-boot.md, "Converged" and "Bootstrap" sections.
5
+ *
6
+ * ⛔ NO SILENT `converged: false` HERE. A single attempt's own transport failure is swallowed
7
+ * ONLY inside the bounded loop below — a rebooting node's API is legitimately unreachable for a
8
+ * while — but the loop AS A WHOLE never swallows: running out of attempts always raises
9
+ * `TalosConvergenceTimeout`, never a quiet `false`. That distinction is read()'s job, not
10
+ * reconcile's — see talos-machine-config.ts.
11
+ * ⚠️ SHORT INTERVALS, A LOW CAP, NEVER ONE LONG SLEEP — the house timeout rule, and the specific
12
+ * reason talosctl's own `health` gate has a 20-minute `--wait-timeout`: a single blocking wait
13
+ * that long would hide whether THIS process is still alive from anything watching it.
14
+ */
15
+ import * as Effect from 'effect/Effect';
16
+ import type { ApplyMode } from './resource.ts';
17
+ import { TalosConvergenceTimeout } from './talos-errors.ts';
18
+ /**
19
+ * ★ TWO POLICIES, ONE PER MODE CLASS — REASONED, no live cluster to time this against yet.
20
+ * `no-reboot` never drops the API, so a handful of quick retries covers only ordinary
21
+ * config-propagation lag. `reboot`/`auto` can drop the API for a full boot cycle, so the cap is
22
+ * longer and the interval coarser. Both stay well inside the house rule above.
23
+ */
24
+ export type PollPolicy = {
25
+ readonly intervalMs: number;
26
+ readonly maxAttempts: number;
27
+ };
28
+ /**
29
+ * Confirm convergence per `mode`'s policy, or fail with `TalosConvergenceTimeout`.
30
+ *
31
+ * Returns which CLAIM was proven, never a boolean — `'read-back'` for the immediate policy
32
+ * (no-reboot), `'accepted'` for the reboot-tolerant one (reboot/auto): state records which kind of
33
+ * confirmation actually happened, per the design doc's `converged` field.
34
+ *
35
+ * ⚠️ `policies` IS A TEST SEAM, NOT A PROP. `talos-machine-config.ts` never passes it, so
36
+ * production always runs the REASONED defaults above; tests pass tiny intervals so a bounded
37
+ * poll test finishes in milliseconds instead of the real ~10s/~2m caps.
38
+ */
39
+ export declare const confirmConverged: <E, R>(node: string, mode: ApplyMode, check: Effect.Effect<boolean, E, R>, policies?: {
40
+ readonly immediate: PollPolicy;
41
+ readonly rebootish: PollPolicy;
42
+ }) => Effect.Effect<'read-back' | 'accepted', TalosConvergenceTimeout, R>;
43
+ //# sourceMappingURL=machine-config-poll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-config-poll.d.ts","sourceRoot":"","sources":["../../src/talos/machine-config-poll.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAQ5D;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAwBvF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,EAAE,CAAC,QAC7B,MAAM,QACN,SAAS,SACR,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,aACzB;IAAE,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE,KAC3E,MAAM,CAAC,MAAM,CAAC,WAAW,GAAG,UAAU,EAAE,uBAAuB,EAAE,CAAC,CAWjE,CAAC"}
@@ -0,0 +1,49 @@
1
+ import type { Input } from 'alchemy/Input';
2
+ import * as Effect from 'effect/Effect';
3
+ import type { MachineConfigAttributes, MachineConfigProps } from './talos-machine-config.ts';
4
+ /**
5
+ * Single live read: `talosctl get machineconfig v1alpha1`, extract `spec`, hash it. No retry, no
6
+ * swallow.
7
+ *
8
+ * ⛔ THE ID IS NEVER OMITTED (fix-first #2, PR 307 red team). An unfiltered `get machineconfig`
9
+ * lists `persistent` AND `v1alpha1`, sorted by id — `persistent` first — so a caller that does not
10
+ * name `v1alpha1` gets whichever the SDK's `doc[0]` happens to be, never provably the applied
11
+ * config (values.ts's own multi-document guard is the second half of this fix).
12
+ * ★ `endpoints: [props.node]` (fix-first #5, same review) — without it talosctl falls back to the
13
+ * talosconfig context's own endpoints, which may be a control-plane VIP that only comes up AFTER
14
+ * bootstrap; every pre-bootstrap call in this file targets the node directly instead.
15
+ */
16
+ export declare const liveSpecDigest: (props: MachineConfigProps, talosconfigPath: string) => Effect.Effect<string, Error | import("effect/PlatformError").PlatformError | import("./talosctl.ts").TalosError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
17
+ /**
18
+ * ⛔ NO SWALLOWING. A transport failure here propagates as its own error (TalosError or the
19
+ * shape error above) — never as `converged: false`. A false only means "read succeeded, and
20
+ * genuinely differs" (docs/plans/2026-09-26-talos-stack-first-boot.md, acceptance test 3).
21
+ * ⛔ THREE-WAY COLD-START ANSWER, NOT TWO (fix-first #1). Alchemy's engine calls this exact
22
+ * function as its adoption probe with no prior state (alchemy beta.79 Plan.ts:1302-1318) and
23
+ * again to recover an interrupted create (`:1413-1450`) — both hand it nothing to distinguish
24
+ * "not created" from "already configured". Answering only "attrs or error" (the shipped shape)
25
+ * meant a maintenance-mode node's expected auth failure aborted the plan instead of reaching
26
+ * CREATE, and a genuinely different node's matching-cluster-CA-but-wrong-content read landed as a
27
+ * plain, unbranded attrs object — a silent adopt with no `adopt()`, breaking first-boot acceptance
28
+ * test 1 ("zero adopts"). Now: authenticated read fails but the maintenance probe succeeds →
29
+ * `undefined` (not created); authenticated read succeeds and the digest matches → plain attrs
30
+ * (ours); succeeds and differs → `Unowned` (exists, not proven ours — the digest match IS this
31
+ * family's ownership proof, not a state-row lookup like `ownership/probe.ts`'s generic
32
+ * `ownedRead`, which answers a different question: "did OUR state row create this name", not
33
+ * "does this node hold OUR pinned content"); both reads fail → the authenticated error propagates.
34
+ *
35
+ * ★ EXPORTED (like proxmox/ceph-daemon.ts's `reconcileDaemon`) so tests call it directly with a
36
+ * fake `ChildProcessSpawner` instead of driving it through the full Alchemy engine.
37
+ */
38
+ export declare const readMachineConfig: (props: MachineConfigProps) => Effect.Effect<{
39
+ configDigest: string;
40
+ converged: "read-back" | false;
41
+ mode: import("./resource.ts").ApplyMode;
42
+ node: string;
43
+ } | undefined, Error | import("effect/PlatformError").PlatformError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
44
+ export declare const diffMachineConfig: (news: Input<MachineConfigProps>, output: MachineConfigAttributes | undefined) => Effect.Effect<{
45
+ readonly action: 'noop';
46
+ } | {
47
+ readonly action: 'update';
48
+ } | undefined, Error | import("effect/PlatformError").PlatformError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
49
+ //# sourceMappingURL=machine-config-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-config-read.d.ts","sourceRoot":"","sources":["../../src/talos/machine-config-read.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAI7F;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,UAAW,kBAAkB,mBAAmB,MAAM,gMAiB9E,CAAC;AAuBJ;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,iBAAiB,UAAW,kBAAkB;;;;;+IAiBxD,CAAC;AAEJ,eAAO,MAAM,iBAAiB,SACtB,KAAK,CAAC,kBAAkB,CAAC,UACvB,uBAAuB,GAAG,SAAS;qBAK4B,MAAM;;qBAC1D,QAAQ;+IACzB,CAAC"}
@@ -29,4 +29,22 @@ export type TalosRequirements = ChildProcessSpawner.ChildProcessSpawner;
29
29
  export type WithTarget = {
30
30
  target: TalosTarget;
31
31
  };
32
+ /**
33
+ * `talosctl apply-config -m/--mode`. Lives here, not in talos-machine-config.ts, so
34
+ * talos-errors.ts can name it in `TalosConvergenceTimeout` without importing that file back
35
+ * (machine-config-poll.ts -> talos-errors.ts -> talos-machine-config.ts would cycle).
36
+ *
37
+ * ★ K-A3 DROPPED THE `insecure` PROP THAT USED TO SIT BESIDE THIS TYPE. A fixed `insecure` prop
38
+ * fails in both directions (`true` breaks every later update, `false` breaks the very first
39
+ * apply against a node with no talosconfig auth yet), so `Talos.MachineConfig` now derives it
40
+ * itself: CREATE (`output === undefined`) applies `--insecure`, UPDATE never does.
41
+ * ⛔ `'staged'` AND `'try'` ARE DROPPED (fix-first #2, PR 307 red team). `try` reverts itself after
42
+ * its timeout, so a bounded poll that later reads the reverted config would see it never match
43
+ * the pin and re-run `apply-config` on every deploy — the poll would be "confirming" a change
44
+ * `try` had already undone. `staged` defers the write to the NEXT reboot rather than applying it,
45
+ * which `isRebootish` (machine-config-poll.ts) cannot honestly claim `'accepted'` for without a
46
+ * reboot ever happening — the reboot for a staged config is a separate, later operator action
47
+ * this package does not drive. Both need their own design work, not a poll-policy guess.
48
+ */
49
+ export type ApplyMode = 'auto' | 'no-reboot' | 'reboot';
32
50
  //# sourceMappingURL=resource.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/talos/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,KAAK,mBAAmB,MAAM,6CAA6C,CAAC;AACxF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,mBAAmB,CAAC;AAExE,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../../src/talos/resource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,KAAK,mBAAmB,MAAM,6CAA6C,CAAC;AACxF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,mBAAmB,CAAC,mBAAmB,CAAC;AAExE,kEAAkE;AAClE,MAAM,MAAM,UAAU,GAAG;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,CAAC;AAEjD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,CAAC"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * `Talos.Bootstrap`'s etcd-members probe and split-brain guard — split out of talos-bootstrap.ts
3
+ * (LAND, 2026-09-26, I4 fix) once adding the guard pushed that file over the 250-line cap, the same
4
+ * reason machine-config-read.ts was split out of talos-machine-config.ts before it. Only
5
+ * `import type` comes back from talos-bootstrap.ts, so there is no runtime cycle between the files.
6
+ */
7
+ import * as Effect from 'effect/Effect';
8
+ import type { BootstrapProps } from './talos-bootstrap.ts';
9
+ import { TalosReBootstrapRefused } from './talos-errors.ts';
10
+ /**
11
+ * REASONED: `talosctl get etcdmembers -o json` should list members after bootstrap.
12
+ * ⚠️ NOT MEASURED — first live deploy confirms the resource type name.
13
+ * ⛔ NO SWALLOWING (K-talos-first-boot) — a transport failure propagates as its own error; only a
14
+ * SUCCESSFUL read may conclude presence or absence. Every caller decides for itself what a
15
+ * failure here means (talos-bootstrap.ts's `read`/`reconcile`, and `assertNoLivePeers` below), so
16
+ * it is not decided here.
17
+ */
18
+ export declare const isBootstrapped: (props: BootstrapProps, talosconfigPath: string) => Effect.Effect<boolean, import("effect/PlatformError").PlatformError | import("./talosctl.ts").TalosError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
19
+ /**
20
+ * ⛔ I4 FIX (LAND red team, 2026-09-26) — THE CREATE PATH ONLY CHECKED THIS NODE. If the state row
21
+ * is lost (wrong `--stage`, an FQN rename, a wiped state store) while the cluster already lives on
22
+ * OTHER control-plane nodes, the cold-start adoption read on THIS node comes back empty, plan
23
+ * calls `create`, and `talosctl bootstrap` runs here too — a SECOND, isolated etcd cluster: split
24
+ * brain. Before bootstrapping, every OTHER declared control-plane node (`props.peers`) must show a
25
+ * successful, EMPTY etcd-members read — proof no cluster already exists anywhere this row knows
26
+ * about. A peer that already has members, or a peer whose read fails, refuses bootstrap exactly
27
+ * like an already-bootstrapped confirmation failure does (same typed error, same "hand it to a
28
+ * human" posture) — a genuinely fresh cluster has no peers with members yet, so this never blocks
29
+ * a real first boot.
30
+ * ⚠️ `peers` IS OPTIONAL, SO THIS GUARD IS OPT-IN — an existing single-control-plane declaration
31
+ * keeps working unguarded; a consuming stack with more than one control-plane node (decision 66:
32
+ * 3, one per TB4 node) SHOULD list the other two here to get the protection.
33
+ * ★ `Effect.all` over a mapped array, not `Effect.forEach` — oxlint's unicorn/no-array-for-each
34
+ * matches the method NAME and cannot tell Effect's from Array's (same pattern as
35
+ * openbao/cloudflare-permission-groups.ts).
36
+ */
37
+ export declare const assertNoLivePeers: (props: BootstrapProps, talosconfigPath: string) => Effect.Effect<void, TalosReBootstrapRefused, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
38
+ //# sourceMappingURL=talos-bootstrap-etcd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"talos-bootstrap-etcd.d.ts","sourceRoot":"","sources":["../../src/talos/talos-bootstrap-etcd.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAG5D;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,UAAW,cAAc,mBAAmB,MAAM,yLAIM,CAAC;AAEpF;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,iBAAiB,UAAW,cAAc,mBAAmB,MAAM,4HA2B7E,CAAC"}
@@ -6,13 +6,41 @@
6
6
  * the bootstrap node. This command must run exactly once per cluster.
7
7
  *
8
8
  * ⛔ NOT A FACTORY RESOURCE — bootstrap cannot be deleted or updated in place; `delete` is a no-op.
9
+ *
10
+ * ⛔ ONCE MEANS ONCE (K-talos-first-boot, 2026-09-26) — THE SHIPPED SHAPE COULD BOOTSTRAP A SECOND
11
+ * etcd CLUSTER. The original `isBootstrapped` turned every read failure into `false`
12
+ * (`Effect.orElseSucceed`), `diff` then planned `update`, and `reconcile` re-ran `talosctl
13
+ * bootstrap` — Talos's only server-side guard is a non-empty etcd data directory (REASONED,
14
+ * `v1alpha1_server.go:441`), so a reinstalled or reset bootstrap node re-bootstrapped a fresh,
15
+ * isolated single-member cluster: split brain. Fixed per
16
+ * docs/plans/2026-09-26-talos-stack-first-boot.md's "Bootstrap — once means once" section:
17
+ * - `read` now answers presence/absence correctly instead of always returning a defined
18
+ * object — `undefined` only from a SUCCESSFUL read that finds no members (cold-start
19
+ * absence); a failing read propagates its error instead of being reinterpreted as absence.
20
+ * - `diff` trusts `output.bootstrapped` once it is `true` and reports `noop` without touching
21
+ * the live cluster at all — confirmation is `reconcile`'s job now, not plan's.
22
+ * - `reconcile` checks `output?.bootstrapped` FIRST: once true, it NEVER calls `talosctl
23
+ * bootstrap` again, no matter what a live read says. A live read that fails OR comes back
24
+ * empty both raise the same `TalosReBootstrapRefused` — re-bootstrap is a human decision.
9
25
  */
10
26
  import { Resource } from 'alchemy';
27
+ import type { Input } from 'alchemy/Input';
11
28
  import * as Provider from 'alchemy/Provider';
29
+ import * as Effect from 'effect/Effect';
12
30
  import type { TalosRequirements, WithTarget } from './resource.ts';
31
+ import { TalosReBootstrapRefused } from './talos-errors.ts';
13
32
  export interface BootstrapProps extends WithTarget {
14
33
  /** The ONE control-plane node that receives `talosctl bootstrap`. */
15
34
  node: string;
35
+ /**
36
+ * The OTHER declared control-plane nodes — never `node` itself. Optional, but a multi-control-
37
+ * plane cluster (decision 66: 3, one per TB4 node) SHOULD set it: `reconcileBootstrap`'s CREATE
38
+ * path (I4 fix, LAND red team, 2026-09-26) requires every one of these to show a successful,
39
+ * EMPTY etcd-members read before it will bootstrap `node` — the guard against re-bootstrapping a
40
+ * split-brain second cluster when this row's own state was lost. See talos-bootstrap-etcd.ts's
41
+ * `assertNoLivePeers`.
42
+ */
43
+ peers?: readonly string[];
16
44
  /** Machine configs for this node (and peers) must be applied first. */
17
45
  after?: readonly unknown[];
18
46
  }
@@ -23,5 +51,64 @@ export interface BootstrapAttributes {
23
51
  export interface TalosBootstrap extends Resource<'Talos.Bootstrap', BootstrapProps, BootstrapAttributes, never, TalosRequirements> {
24
52
  }
25
53
  export declare const TalosBootstrap: import("alchemy").ResourceClass<TalosBootstrap>;
54
+ /**
55
+ * ⛔ WRAPPED IN `Effect.scoped` (K-A3, 2026-09-26) — `mintTalosconfig` contributes `Scope.Scope`
56
+ * to its own return type rather than closing its scope internally (the C1 fix, credentials.ts's
57
+ * own header); this is the caller that owns the temp talosconfig's lifetime for the read.
58
+ *
59
+ * ⛔ ANSWERS PRESENCE/ABSENCE, NOT "attrs-or-error" (K-talos-first-boot, fixes the shipped shape).
60
+ * `undefined` only when a read SUCCEEDS and finds no members — the honest "not created yet"
61
+ * Alchemy's engine expects from a cold-start adoption probe. A failing read propagates instead
62
+ * of being reinterpreted as absence — this is the fix `isBootstrapped` no longer hides.
63
+ *
64
+ * ★ EXPORTED (like proxmox/ceph-daemon.ts's `reconcileDaemon`, machine-config-read.ts's
65
+ * `readMachineConfig`) so talos-bootstrap.test.ts calls it directly with a fake spawner instead
66
+ * of driving it through the full Alchemy engine.
67
+ */
68
+ export declare const readBootstrap: (props: BootstrapProps) => Effect.Effect<{
69
+ readonly bootstrapped: true;
70
+ readonly node: string;
71
+ } | undefined, Error | import("effect/PlatformError").PlatformError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
72
+ /**
73
+ * ⛔ TRUSTS STATE, NEVER TOUCHES THE LIVE CLUSTER (K-talos-first-boot) — once `output.bootstrapped`
74
+ * is `true` this always reports `noop`, unconditionally, with zero live calls.
75
+ *
76
+ * ⛔ I3 CORRECTION (LAND red team, 2026-09-26) — THE OLD COMMENT HERE CLAIMED "confirming the
77
+ * invariant against etcd is reconcile's job (every deploy calls it)". THAT IS FALSE, VERIFIED
78
+ * against the pinned alchemy beta.79 source: Apply never reconciles a node whose diff came back
79
+ * `noop` (`Apply.ts`'s `noop` branch just refreshes row metadata and signals ready — it does not
80
+ * call `provider.reconcile` at all). This file's own `noop` above IS what skips it. So
81
+ * `reconcileBootstrap`'s re-confirm branch (the one that raises `TalosReBootstrapRefused` on a
82
+ * failed or empty read) never runs on an ordinary `plan`/`deploy` once `bootstrapped: true` is
83
+ * recorded — only `--force` reaches it, by forcing a reconcile regardless of the plan action.
84
+ * Safety still holds — `talosctl bootstrap` genuinely never re-runs while state exists — but a
85
+ * reset control-plane node (etcd wiped, VM replaced) stays silently invisible to `plan` and
86
+ * `deploy` alike until an operator runs `--force` or a verify pass.
87
+ * ⚠️ NOT FIXED BY HAVING `diff` READ HERE TOO (the finding's other offered fix) — that would touch
88
+ * the live cluster (and mint a talosconfig) on EVERY plan for an already-bootstrapped node, for a
89
+ * scenario (a reset node) that a `configDigest`-style pinned check can't distinguish from a merely
90
+ * slow/unreachable read; a single transient failure would then abort every future plan for a
91
+ * healthy node, for no benefit — the exact cost the original (correct) half of this comment
92
+ * already named. `MachineConfig`/`ClusterHealth`'s diffs DO mint and read live on every plan
93
+ * (D2 puts that on the admin/operator lane, not agent), so this isn't a lane-access limit; it is
94
+ * this resource's own choice to trust a `true` boolean forever rather than re-derive it from a
95
+ * live signal that cannot yet tell "reset" apart from "briefly unreachable". Recorded as an open
96
+ * gap for whoever builds the operator verify pass, not silently fixed here.
97
+ */
98
+ export declare const diffBootstrap: (news: Input<BootstrapProps>, output: BootstrapAttributes | undefined) => Effect.Effect<{
99
+ readonly action: 'noop';
100
+ } | {
101
+ readonly action: 'update';
102
+ } | undefined, Error | import("effect/PlatformError").PlatformError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
103
+ /**
104
+ * ⛔ ONCE MEANS ONCE — see this file's own header. `output?.bootstrapped === true` is checked
105
+ * BEFORE anything else, and that branch never builds a `talosctl bootstrap` argv at all — the
106
+ * confirmation read below can fail, or succeed and say "absent", and EITHER way this raises
107
+ * `TalosReBootstrapRefused` rather than treating "absent" as licence to bootstrap again.
108
+ */
109
+ export declare const reconcileBootstrap: (props: BootstrapProps, output: BootstrapAttributes | undefined) => Effect.Effect<{
110
+ bootstrapped: boolean;
111
+ node: string;
112
+ }, Error | import("effect/PlatformError").PlatformError | TalosReBootstrapRefused, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
26
113
  export declare const TalosBootstrapProvider: () => import("effect/Layer").Layer<Provider.Provider<TalosBootstrap>, never, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
27
114
  //# sourceMappingURL=talos-bootstrap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"talos-bootstrap.d.ts","sourceRoot":"","sources":["../../src/talos/talos-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGnE,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,cAAc,iDAGzB,CAAC;AAkEH,eAAO,MAAM,sBAAsB,yJACoD,CAAC"}
1
+ {"version":3,"file":"talos-bootstrap.d.ts","sourceRoot":"","sources":["../../src/talos/talos-bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEnE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAG5D,MAAM,WAAW,cAAe,SAAQ,UAAU;IAChD,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC1B,uEAAuE;IACvE,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAe,SAAQ,QAAQ,CAC9C,iBAAiB,EACjB,cAAc,EACd,mBAAmB,EACnB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,cAAc,iDAGzB,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,aAAa,UAAW,cAAc;;;+IAOhD,CAAC;AAEJ;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,aAAa,SAClB,KAAK,CAAC,cAAc,CAAC,UACnB,mBAAmB,GAAG,SAAS;qBAIK,MAAM;;qBAIjC,QAAQ;+IACvB,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,UACtB,cAAc,UACb,mBAAmB,GAAG,SAAS;;;6JAmDtC,CAAC;AAsBJ,eAAO,MAAM,sBAAsB,yJACoD,CAAC"}
@@ -6,9 +6,27 @@
6
6
  *
7
7
  * ⚠️ PLAN READS USE A SHORT TIMEOUT SO `plan` DOES NOT BLOCK TWENTY MINUTES. Reconcile uses the
8
8
  * declared timeout — that is the deploy gate, not the plan gate.
9
+ *
10
+ * ⛔ THIS RESOURCE ASSUMES A CNI IS ALREADY RUNNING (K-talos-first-boot, 2026-09-26). The default
11
+ * `talosctl health` suite checks nodes Ready, kube-proxy and CoreDNS, and Talos's own source
12
+ * comment says these wait on the CNI (REASONED, `pkg/cluster/check/default.go:28-60,89-91`;
13
+ * docs/plans/2026-09-26-talos-stack-first-boot.md's "CNI ordering" section). A consuming stack's
14
+ * `after` MUST include the Cilium install step, not just `Talos.Bootstrap`/`Talos.Kubeconfig` —
15
+ * declaring this row directly after bootstrap deadlocks into the `--wait-timeout` on every first
16
+ * deploy, because Argo CD itself needs pod networking before it can install Cilium. Row order:
17
+ * Bootstrap → Kubeconfig → Cilium (inline manifests in the seeded machine config, D-S2) →
18
+ * `Talos.ClusterHealth`.
19
+ * ⛔ NO SWALLOWED TRANSPORT ERRORS (K-talos-first-boot) — the shipped `read` caught EVERY error
20
+ * from `check()`, including a `mintTalosconfig`/`bao` failure, into a plain `healthy: false`. A
21
+ * vault outage would then read as "cluster not healthy yet" — an operator chasing a Cilium
22
+ * rollout would never learn the real problem was vault. Both `read` and `reconcile` now only
23
+ * catch `TalosError` (`talosctl health` itself ran and exited non-zero — the one case this
24
+ * family may honestly call "not healthy"); anything else propagates untouched.
9
25
  */
10
26
  import { Resource } from 'alchemy';
27
+ import type { Input } from 'alchemy/Input';
11
28
  import * as Provider from 'alchemy/Provider';
29
+ import * as Effect from 'effect/Effect';
12
30
  import type { TalosRequirements, WithTarget } from './resource.ts';
13
31
  export interface ClusterHealthProps extends WithTarget {
14
32
  /** Control-plane node IPs passed to `--control-plane-nodes`. */
@@ -21,7 +39,11 @@ export interface ClusterHealthProps extends WithTarget {
21
39
  * ⚠️ REASONED default — not measured here.
22
40
  */
23
41
  waitTimeout?: string;
24
- /** Bootstrap and kubeconfig should complete before health is enforced. */
42
+ /**
43
+ * Ordering edge. ⛔ MUST reach past `Talos.Bootstrap`/`Talos.Kubeconfig` all the way through the
44
+ * Cilium CNI install (this file's own header, "CNI ordering") — the default health checks wait
45
+ * on kube-proxy and CoreDNS, both of which wait on a CNI that does not exist yet at bootstrap.
46
+ */
25
47
  after?: readonly unknown[];
26
48
  }
27
49
  export interface ClusterHealthAttributes {
@@ -32,5 +54,25 @@ export interface ClusterHealthAttributes {
32
54
  export interface TalosClusterHealth extends Resource<'Talos.ClusterHealth', ClusterHealthProps, ClusterHealthAttributes, never, TalosRequirements> {
33
55
  }
34
56
  export declare const TalosClusterHealth: import("alchemy").ResourceClass<TalosClusterHealth>;
57
+ /** ★ EXPORTED for talos-cluster-health.test.ts — see talos-bootstrap.ts's own note on the pattern. */
58
+ export declare const readClusterHealth: (props: ClusterHealthProps) => Effect.Effect<{
59
+ controlPlaneNodes: string;
60
+ healthy: boolean;
61
+ workerNodes: string;
62
+ } | {
63
+ controlPlaneNodes: string;
64
+ healthy: boolean;
65
+ workerNodes: string;
66
+ }, Error | import("effect/PlatformError").PlatformError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
67
+ export declare const diffClusterHealth: (news: Input<ClusterHealthProps>, output: ClusterHealthAttributes | undefined) => Effect.Effect<{
68
+ readonly action: 'noop';
69
+ } | {
70
+ readonly action: 'update';
71
+ } | undefined, Error | import("effect/PlatformError").PlatformError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
72
+ export declare const reconcileClusterHealth: (props: ClusterHealthProps) => Effect.Effect<{
73
+ controlPlaneNodes: string;
74
+ healthy: boolean;
75
+ workerNodes: string;
76
+ }, Error | import("effect/PlatformError").PlatformError, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
35
77
  export declare const TalosClusterHealthProvider: () => import("effect/Layer").Layer<Provider.Provider<TalosClusterHealth>, never, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
36
78
  //# sourceMappingURL=talos-cluster-health.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"talos-cluster-health.d.ts","sourceRoot":"","sources":["../../src/talos/talos-cluster-health.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGnE,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,gEAAgE;IAChE,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,kDAAkD;IAClD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0EAA0E;IAC1E,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAAsD,CAAC;AAsEtF,eAAO,MAAM,0BAA0B,6JACwD,CAAC"}
1
+ {"version":3,"file":"talos-cluster-health.d.ts","sourceRoot":"","sources":["../../src/talos/talos-cluster-health.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGnE,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,gEAAgE;IAChE,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,kDAAkD;IAClD,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAAsD,CAAC;AAgEtF,sGAAsG;AACtG,eAAO,MAAM,iBAAiB,UAAW,kBAAkB;;;;;;;;mIASxD,CAAC;AAEJ,eAAO,MAAM,iBAAiB,SACtB,KAAK,CAAC,kBAAkB,CAAC,UACvB,uBAAuB,GAAG,SAAS;qBAKN,MAAM;;qBACxB,QAAQ;+IACzB,CAAC;AAEL,eAAO,MAAM,sBAAsB,UAAW,kBAAkB;;;;mIAU7D,CAAC;AAgBJ,eAAO,MAAM,0BAA0B,6JACwD,CAAC"}
@@ -0,0 +1,68 @@
1
+ import type { ApplyMode } from './resource.ts';
2
+ declare const TalosConfigDigestMismatch_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
3
+ readonly _tag: "TalosConfigDigestMismatch";
4
+ } & Readonly<A>;
5
+ /**
6
+ * The OpenBao KV content at `mount/key` does not hash to the pinned `configDigest`.
7
+ *
8
+ * ★ FAIL CLOSED, NO SPAWN — docs/plans/2026-09-26-talos-secrets-flow.md's O-A: "a poisoned or
9
+ * fat-fingered KV write cannot silently reach a node". Raised BEFORE any talosctl process
10
+ * starts, so a mismatch never sends an unpinned config anywhere — see reconcile() in
11
+ * talos-machine-config.ts, which checks this ahead of minting the talosconfig credential too.
12
+ * ⛔ NEITHER DIGEST IN THIS MESSAGE IS THE CONFIG. Both are sha256 hex digests — safe to log,
13
+ * safe to persist — never the KV content itself.
14
+ */
15
+ export declare class TalosConfigDigestMismatch extends TalosConfigDigestMismatch_base<{
16
+ readonly mount: string;
17
+ readonly key: string;
18
+ readonly expected: string;
19
+ readonly got: string;
20
+ }> {
21
+ get message(): string;
22
+ }
23
+ declare const TalosConvergenceTimeout_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
24
+ readonly _tag: "TalosConvergenceTimeout";
25
+ } & Readonly<A>;
26
+ /**
27
+ * `apply-config` returned success but the live machineconfig digest never matched the pin within
28
+ * the bounded poll (machine-config-poll.ts). See
29
+ * docs/plans/2026-09-26-talos-stack-first-boot.md's "Converged" section.
30
+ *
31
+ * ⛔ THIS IS THE REPLACEMENT FOR A SILENT `converged: false`. The shipped code let a read
32
+ * failure during convergence checking disguise itself as "not converged yet" forever
33
+ * (`orElseSucceed`); this type makes cap expiry a refusal an operator sees, not a plan that
34
+ * quietly retries the same apply on every future run.
35
+ */
36
+ export declare class TalosConvergenceTimeout extends TalosConvergenceTimeout_base<{
37
+ readonly node: string;
38
+ readonly mode: ApplyMode;
39
+ readonly attempts: number;
40
+ }> {
41
+ get message(): string;
42
+ }
43
+ declare const TalosReBootstrapRefused_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
44
+ readonly _tag: "TalosReBootstrapRefused";
45
+ } & Readonly<A>;
46
+ /**
47
+ * `Talos.Bootstrap` reconcile found state already recording `bootstrapped: true`, and a live
48
+ * check against etcd could not reconfirm it — either the read itself failed (`detail` carries the
49
+ * propagated cause), or it succeeded and came back with no members.
50
+ *
51
+ * ★ K-talos-first-boot's "once means once" — docs/plans/2026-09-26-talos-stack-first-boot.md's own
52
+ * section of that name. EITHER cause raises this SAME typed error, and both ways `talosctl
53
+ * bootstrap` is never spawned a second time: re-running it against a node that lost its etcd
54
+ * data (a VM replace, a reset) forms a second, isolated single-member etcd cluster instead of
55
+ * rejoining the existing one (REASONED, `v1alpha1_server.go:441` — Talos's only server-side
56
+ * guard is a non-empty data directory, not "did this cluster already bootstrap"). That is split
57
+ * brain, and nothing here can tell a genuinely reset node (needs its own operator-run recovery)
58
+ * apart from a merely slow or unreachable read (needs a retry) — so both refuse alike and hand
59
+ * the node to a human instead of guessing.
60
+ */
61
+ export declare class TalosReBootstrapRefused extends TalosReBootstrapRefused_base<{
62
+ readonly node: string;
63
+ readonly detail: string;
64
+ }> {
65
+ get message(): string;
66
+ }
67
+ export {};
68
+ //# sourceMappingURL=talos-errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"talos-errors.d.ts","sourceRoot":"","sources":["../../src/talos/talos-errors.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;;;;AAE/C;;;;;;;;;GASG;AACH,qBAAa,yBAA0B,SAAQ,+BAA8C;IAC3F,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAO7B;CACF;;;;AAED;;;;;;;;;GASG;AACH,qBAAa,uBAAwB,SAAQ,6BAA4C;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAM7B;CACF;;;;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAwB,SAAQ,6BAA4C;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;IACA,IAAa,OAAO,IAAI,MAAM,CAM7B;CACF"}
@@ -2,28 +2,44 @@
2
2
  * `Talos.MachineConfig` — apply one machine configuration document to one node.
3
3
  *
4
4
  * ★ REASONED FROM talosctl apply-config (Talos v1.13 CLI reference): `-f/--file`, `-n/--nodes`,
5
- * `-m/--mode`, `-i/--insecure` for maintenance mode, `--dry-run` for planning without a write.
5
+ * `-m/--mode`, `-i/--insecure` for maintenance mode.
6
6
  *
7
- * ⛔ THE CONFIG BODY IS NEVER AN ATTRIBUTE. Machine configs can embed cluster tokens; only a digest
8
- * of the repo file is persisted. Read `configFile` from disk at reconcile time.
7
+ * ⛔ THE CONFIG BODY IS NEVER AN ATTRIBUTE, A PROP, OR ARGV — ONLY ITS DIGEST. K-A3
8
+ * (docs/plans/2026-09-26-talos-secrets-flow.md, option O-A) moved the source of truth from a
9
+ * repo file (the shipped `configFile` + `STACK_DIR`, gone with this rewrite) to OpenBao: props
10
+ * carry a `configKey` (a vault path) and a `configDigest` PINNED IN GIT, never the bytes. The
11
+ * source is write-only from Alchemy's point of view — content is read once per reconcile,
12
+ * verified against the pin, written to a session-temp file, and never returned from any
13
+ * handler.
14
+ * ⛔ NEVER `--dry-run`. Talos's dry-run prints a full, unredacted old/new config diff — with an
15
+ * empty old config (the first apply) that diff IS the whole config, CA private key and
16
+ * bootstrap token included (REASONED, `internal/app/machined/.../v1alpha1_server.go:258-272` +
17
+ * `configdiff.go`; docs/plans/2026-09-26-talos-stack-first-boot.md). This file never builds
18
+ * that flag.
19
+ * ⛔ `insecure` IS NOT A PROP. A fixed value fails in both directions — `true` breaks every later
20
+ * update, `false` breaks the very first apply against a node with no talosconfig auth yet — so
21
+ * CREATE (`output === undefined`) applies `--insecure` and UPDATE never does (resource.ts).
9
22
  */
10
23
  import { Resource } from 'alchemy';
11
24
  import * as Provider from 'alchemy/Provider';
12
- import type { TalosRequirements, WithTarget } from './resource.ts';
13
- export type ApplyMode = 'auto' | 'no-reboot' | 'reboot' | 'staged' | 'try';
25
+ import * as Effect from 'effect/Effect';
26
+ import { type PollPolicy } from './machine-config-poll.ts';
27
+ import type { ApplyMode, TalosRequirements, WithTarget } from './resource.ts';
28
+ import { TalosConfigDigestMismatch } from './talos-errors.ts';
29
+ export type { ApplyMode } from './resource.ts';
14
30
  export interface MachineConfigProps extends WithTarget {
15
31
  /** Node IP or hostname — the `-n` target. */
16
32
  node: string;
17
- /** Repo-relative or absolute path to the YAML file passed to `-f`. */
18
- configFile: string;
19
- /** talosctl `-m` mode. Default `auto` per published CLI. */
20
- mode?: ApplyMode;
33
+ /** OpenBao KV path under `target.mount` holding this node's rendered config, e.g. `nodes/10001`. */
34
+ configKey: string;
21
35
  /**
22
- * Maintenance-mode apply only (`-i`). Default false — post-bootstrap applies use talosconfig auth.
23
- *
24
- * ⚠️ REASONED: docs describe `--insecure` for the first apply before talosconfig exists.
36
+ * sha256(canonicalText(content)) of the seeded KV value (values.ts `configDigest`), pinned in
37
+ * git by the operator. ⛔ Verified against the live KV content before ANY talosctl spawn — a
38
+ * mismatch fails closed (`TalosConfigDigestMismatch`), nothing is applied.
25
39
  */
26
- insecure?: boolean;
40
+ configDigest: string;
41
+ /** talosctl `-m` mode. Default `auto` per published CLI. */
42
+ mode?: ApplyMode;
27
43
  /** Ordering edge — e.g. wait for Proxmox.Vm. */
28
44
  after?: readonly unknown[];
29
45
  }
@@ -31,11 +47,31 @@ export interface MachineConfigAttributes {
31
47
  node: string;
32
48
  configDigest: string;
33
49
  mode: ApplyMode;
34
- /** True when the node's reported config digest matches the declared file. */
35
- converged: boolean;
50
+ /**
51
+ * Which convergence claim was made. `'read-back'`/`'accepted'` only come from `reconcile` (see
52
+ * machine-config-poll.ts); `false` only comes from `read`, meaning a live digest was
53
+ * successfully fetched and genuinely differs — never a stand-in for a failed read, which
54
+ * propagates as its own error instead (docs/plans/2026-09-26-talos-stack-first-boot.md).
55
+ */
56
+ converged: 'read-back' | 'accepted' | false;
36
57
  }
37
58
  export interface TalosMachineConfig extends Resource<'Talos.MachineConfig', MachineConfigProps, MachineConfigAttributes, never, TalosRequirements> {
38
59
  }
39
60
  export declare const TalosMachineConfig: import("alchemy").ResourceClass<TalosMachineConfig>;
61
+ /**
62
+ * The read path (`readMachineConfig`, `diffMachineConfig`, `liveSpecDigest`) lives in
63
+ * machine-config-read.ts — see that file's own header for the ownership-aware three-way answer
64
+ * `read` now gives (fix-first #1/#2, PR 307 red team).
65
+ */
66
+ /** `pollPolicies` is a test seam only — see machine-config-poll.ts's `confirmConverged`. */
67
+ export declare const reconcileMachineConfig: (props: MachineConfigProps, output: MachineConfigAttributes | undefined, pollPolicies?: {
68
+ readonly immediate: PollPolicy;
69
+ readonly rebootish: PollPolicy;
70
+ }) => Effect.Effect<{
71
+ configDigest: string;
72
+ converged: "accepted" | "read-back";
73
+ mode: ApplyMode;
74
+ node: string;
75
+ }, Error | import("effect/PlatformError").PlatformError | TalosConfigDigestMismatch | import("./talos-errors.ts").TalosConvergenceTimeout, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
40
76
  export declare const TalosMachineConfigProvider: () => import("effect/Layer").Layer<Provider.Provider<TalosMachineConfig>, never, import("effect/unstable/process/ChildProcessSpawner").ChildProcessSpawner>;
41
77
  //# sourceMappingURL=talos-machine-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"talos-machine-config.d.ts","sourceRoot":"","sources":["../../src/talos/talos-machine-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAG7C,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAMnE,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,QAAQ,GAAG,KAAK,CAAC;AAE3E,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gDAAgD;IAChD,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAAsD,CAAC;AAwFtF,eAAO,MAAM,0BAA0B,6JACwD,CAAC"}
1
+ {"version":3,"file":"talos-machine-config.d.ts","sourceRoot":"","sources":["../../src/talos/talos-machine-config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,EAAE,KAAK,UAAU,EAAoB,MAAM,0BAA0B,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,EAAE,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAI9D,YAAY,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,WAAW,kBAAmB,SAAQ,UAAU;IACpD,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,oGAAoG;IACpG,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,gDAAgD;IAChD,KAAK,CAAC,EAAE,SAAS,OAAO,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB;;;;;OAKG;IACH,SAAS,EAAE,WAAW,GAAG,UAAU,GAAG,KAAK,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAClD,qBAAqB,EACrB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,iBAAiB,CAClB;CAAG;AAEJ,eAAO,MAAM,kBAAkB,qDAAsD,CAAC;AAEtF;;;;GAIG;AAEH,4FAA4F;AAC5F,eAAO,MAAM,sBAAsB,UAC1B,kBAAkB,UACjB,uBAAuB,GAAG,SAAS,iBAC5B;IAAE,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAA;CAAE;;;;;qNAkDhF,CAAC;AAsBJ,eAAO,MAAM,0BAA0B,6JACwD,CAAC"}
@@ -21,6 +21,13 @@ export type TalosRunOptions = {
21
21
  readonly talosconfigPath: string;
22
22
  readonly nodes?: readonly string[];
23
23
  readonly endpoints?: readonly string[];
24
+ /**
25
+ * `-i/--insecure` — a maintenance-mode node has no established PKI yet. Used by the CREATE apply
26
+ * (resource.ts's own header) and by talos-machine-config.ts's maintenance-mode probe (fix-first
27
+ * #1, PR 307 red team): `--talosconfig` is still passed alongside it, matching the CREATE path,
28
+ * since insecure mode ignores the file's certs rather than requiring their absence.
29
+ */
30
+ readonly insecure?: boolean;
24
31
  };
25
32
  /** Run `talosctl <args…>`. Returns trimmed stdout, or '' when empty. */
26
33
  export declare const talosctl: (args: readonly string[], options: TalosRunOptions) => Effect.Effect<string, import("effect/PlatformError").PlatformError | TalosError, ChildProcessSpawner.ChildProcessSpawner>;
@@ -1 +1 @@
1
- {"version":3,"file":"talosctl.d.ts","sourceRoot":"","sources":["../../src/talos/talosctl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,mBAAmB,MAAM,6CAA6C,CAAC;AAEnF,qBAAa,UAAW,SAAQ,KAAK;IAEjC,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAF3B,YACW,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EACzB,MAAM,EAAE,MAAM,EAIf;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,QAAQ,SAAU,SAAS,MAAM,EAAE,WAAW,eAAe,8HA8CtE,CAAC;AAEL,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,SAAU,SAAS,MAAM,EAAE,WAAW,eAAe,iHAShF,CAAC"}
1
+ {"version":3,"file":"talosctl.d.ts","sourceRoot":"","sources":["../../src/talos/talosctl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,OAAO,KAAK,mBAAmB,MAAM,6CAA6C,CAAC;AAEnF,qBAAa,UAAW,SAAQ,KAAK;IAEjC,QAAQ,CAAC,OAAO,EAAE,MAAM;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM;IAF3B,YACW,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EACzB,MAAM,EAAE,MAAM,EAIf;CACF;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,QAAQ,SAAU,SAAS,MAAM,EAAE,WAAW,eAAe,8HA+CtE,CAAC;AAEL,uFAAuF;AACvF,eAAO,MAAM,iBAAiB,SAAU,SAAS,MAAM,EAAE,WAAW,eAAe,iHAShF,CAAC"}
@@ -2,8 +2,30 @@
2
2
  export declare const canonicalText: (text: string) => string;
3
3
  /** SHA-256 hex digest of canonical text. Safe to persist — it is not reversible to the input. */
4
4
  export declare const sha256: (text: string) => string;
5
- /** Digest of a machine config file read from the repo. ⛔ Never persist the file body itself. */
5
+ /** Digest of a machine config's canonical text. ⛔ Never persist the text itself. */
6
6
  export declare const configDigest: (text: string) => string;
7
+ /**
8
+ * Extract the raw config text under `spec` from `talosctl get machineconfig -o yaml`'s wrapper.
9
+ *
10
+ * ⛔ THE WHOLE WRAPPER CAN NEVER MATCH THE PINNED DIGEST, AND HASHING IT WAS THE SHIPPED BUG.
11
+ * REASONED from talosctl source (`cmd/talosctl/.../output/yaml.go:63`,
12
+ * `resources/config/machine_config.go:40-50`): the resource envelope nests a `node:` line and
13
+ * resource metadata (version, timestamps) around the config, which is itself a raw YAML STRING
14
+ * under `spec`. Metadata changes on every observation, so `configDigest(wrapper)` never equals
15
+ * `configDigest(seededConfigText)` even when nothing changed — every reconcile would die, every
16
+ * diff would plan `update`, verify would never go quiet (all three MEASURED against the shipped
17
+ * code's shape, docs/plans/2026-09-26-talos-stack-first-boot.md).
18
+ * ⚠️ Returns `undefined` for any shape without a string `spec`, so a caller cannot mistake
19
+ * "unparsable wrapper" for "empty config" — those must fail differently (the caller's job).
20
+ * ⛔ MORE THAN ONE DOCUMENT IS REFUSED, NEVER `doc[0]` (fix-first #2, PR 307 red team). An unfiltered
21
+ * `get machineconfig` lists BOTH the `persistent` and `v1alpha1` resources, sorted by id
22
+ * (REASONED, cosi-project/runtime `inmem/collection.go:122-124`), so a bare `doc[0]` is always
23
+ * `persistent` — never the config this package applies. The caller (machine-config-read.ts) now
24
+ * always requests the single named resource (`get machineconfig v1alpha1`); a second document
25
+ * showing up here means that request shape changed without this guard changing with it, and
26
+ * silently picking one would repeat the exact shipped bug.
27
+ */
28
+ export declare const extractMachineConfigSpec: (wrapperYaml: string) => string | undefined;
7
29
  /**
8
30
  * Public metadata extracted from a kubeconfig file on disk.
9
31
  *
@@ -15,6 +37,4 @@ export declare const kubeconfigMetadata: (yamlText: string, contextName: string)
15
37
  caFingerprint: string;
16
38
  clientFingerprint: string;
17
39
  } | undefined;
18
- /** Resolve a config file path relative to the declaring stack file. */
19
- export declare const resolveConfigPath: (stackDir: string, configFile: string) => string;
20
40
  //# sourceMappingURL=values.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../src/talos/values.ts"],"names":[],"mappings":"AAQA,2EAA2E;AAC3E,eAAO,MAAM,aAAa,SAAU,MAAM,WAAmB,CAAC;AAE9D,iGAAiG;AACjG,eAAO,MAAM,MAAM,SAAU,MAAM,WAC6B,CAAC;AAEjE,gGAAgG;AAChG,eAAO,MAAM,YAAY,SAAU,MAAM,WAAiB,CAAC;AAe3D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,aACnB,MAAM,eACH,MAAM,KAClB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GAAG,SAyB3E,CAAC;AAEF,uEAAuE;AACvE,eAAO,MAAM,iBAAiB,aAAc,MAAM,cAAc,MAAM,WACC,CAAC"}
1
+ {"version":3,"file":"values.d.ts","sourceRoot":"","sources":["../../src/talos/values.ts"],"names":[],"mappings":"AASA,2EAA2E;AAC3E,eAAO,MAAM,aAAa,SAAU,MAAM,WAAmB,CAAC;AAE9D,iGAAiG;AACjG,eAAO,MAAM,MAAM,SAAU,MAAM,WAC6B,CAAC;AAEjE,oFAAoF;AACpF,eAAO,MAAM,YAAY,SAAU,MAAM,WAAiB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,wBAAwB,gBAAiB,MAAM,KAAG,MAAM,GAAG,SAcvE,CAAC;AAeF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,aACnB,MAAM,eACH,MAAM,KAClB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE,GAAG,SAyB3E,CAAC"}