@homeflare/alchemy 0.5.0 → 0.7.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 (288) hide show
  1. package/README.md +68 -55
  2. package/dist/caddy/admin-calls.d.ts +48 -0
  3. package/dist/caddy/admin-calls.d.ts.map +1 -0
  4. package/dist/caddy/admin-guard.d.ts +40 -0
  5. package/dist/caddy/admin-guard.d.ts.map +1 -0
  6. package/dist/caddy/admin.d.ts +73 -0
  7. package/dist/caddy/admin.d.ts.map +1 -0
  8. package/dist/caddy/caddyfile-tripwire.d.ts +5 -0
  9. package/dist/caddy/caddyfile-tripwire.d.ts.map +1 -0
  10. package/dist/caddy/config-form.d.ts +27 -0
  11. package/dist/caddy/config-form.d.ts.map +1 -0
  12. package/dist/caddy/config-lifecycle.d.ts +45 -0
  13. package/dist/caddy/config-lifecycle.d.ts.map +1 -0
  14. package/dist/caddy/config.d.ts +30 -0
  15. package/dist/caddy/config.d.ts.map +1 -0
  16. package/dist/caddy/digest.d.ts +7 -0
  17. package/dist/caddy/digest.d.ts.map +1 -0
  18. package/dist/caddy/fake-caddy.d.ts +43 -0
  19. package/dist/caddy/fake-caddy.d.ts.map +1 -0
  20. package/dist/caddy/index.d.ts +21 -0
  21. package/dist/caddy/index.d.ts.map +1 -0
  22. package/dist/caddy/index.js +912 -0
  23. package/dist/caddy/index.js.map +28 -0
  24. package/dist/caddy/local-admin.d.ts +45 -0
  25. package/dist/caddy/local-admin.d.ts.map +1 -0
  26. package/dist/caddy/providers.d.ts +14 -0
  27. package/dist/caddy/providers.d.ts.map +1 -0
  28. package/dist/caddy/with-file.d.ts +47 -0
  29. package/dist/caddy/with-file.d.ts.map +1 -0
  30. package/dist/cloudflare/fake-mesh.d.ts +58 -0
  31. package/dist/cloudflare/fake-mesh.d.ts.map +1 -0
  32. package/dist/cloudflare/index.d.ts +9 -3
  33. package/dist/cloudflare/index.d.ts.map +1 -1
  34. package/dist/cloudflare/index.js +218 -7
  35. package/dist/cloudflare/index.js.map +9 -4
  36. package/dist/cloudflare/mesh-node-api.d.ts +75 -0
  37. package/dist/cloudflare/mesh-node-api.d.ts.map +1 -0
  38. package/dist/cloudflare/mesh-node-form.d.ts +82 -0
  39. package/dist/cloudflare/mesh-node-form.d.ts.map +1 -0
  40. package/dist/cloudflare/mesh-node-harness.d.ts +46 -0
  41. package/dist/cloudflare/mesh-node-harness.d.ts.map +1 -0
  42. package/dist/cloudflare/mesh-node-lifecycle.d.ts +22 -0
  43. package/dist/cloudflare/mesh-node-lifecycle.d.ts.map +1 -0
  44. package/dist/cloudflare/mesh-node-token.d.ts +23 -0
  45. package/dist/cloudflare/mesh-node-token.d.ts.map +1 -0
  46. package/dist/cloudflare/mesh-node.d.ts +48 -0
  47. package/dist/cloudflare/mesh-node.d.ts.map +1 -0
  48. package/dist/cloudflare/providers.d.ts +1 -12
  49. package/dist/cloudflare/providers.d.ts.map +1 -1
  50. package/dist/index.d.ts +2 -0
  51. package/dist/index.d.ts.map +1 -1
  52. package/dist/launchd/fake-runner.d.ts +36 -0
  53. package/dist/launchd/fake-runner.d.ts.map +1 -0
  54. package/dist/launchd/host-effect.d.ts +19 -0
  55. package/dist/launchd/host-effect.d.ts.map +1 -0
  56. package/dist/launchd/host-file-form.d.ts +24 -0
  57. package/dist/launchd/host-file-form.d.ts.map +1 -0
  58. package/dist/launchd/host-file-lifecycle.d.ts +29 -0
  59. package/dist/launchd/host-file-lifecycle.d.ts.map +1 -0
  60. package/dist/launchd/host-file.d.ts +25 -0
  61. package/dist/launchd/host-file.d.ts.map +1 -0
  62. package/dist/launchd/host-lookup.d.ts +27 -0
  63. package/dist/launchd/host-lookup.d.ts.map +1 -0
  64. package/dist/launchd/index.d.ts +21 -0
  65. package/dist/launchd/index.d.ts.map +1 -0
  66. package/dist/launchd/index.js +969 -0
  67. package/dist/launchd/index.js.map +25 -0
  68. package/dist/launchd/job-form.d.ts +90 -0
  69. package/dist/launchd/job-form.d.ts.map +1 -0
  70. package/dist/launchd/job-lifecycle.d.ts +34 -0
  71. package/dist/launchd/job-lifecycle.d.ts.map +1 -0
  72. package/dist/launchd/job-preflight.d.ts +50 -0
  73. package/dist/launchd/job-preflight.d.ts.map +1 -0
  74. package/dist/launchd/job-validate.d.ts +25 -0
  75. package/dist/launchd/job-validate.d.ts.map +1 -0
  76. package/dist/launchd/job.d.ts +34 -0
  77. package/dist/launchd/job.d.ts.map +1 -0
  78. package/dist/launchd/launchctl.d.ts +62 -0
  79. package/dist/launchd/launchctl.d.ts.map +1 -0
  80. package/dist/launchd/local-runner.d.ts +7 -0
  81. package/dist/launchd/local-runner.d.ts.map +1 -0
  82. package/dist/launchd/plist.d.ts +41 -0
  83. package/dist/launchd/plist.d.ts.map +1 -0
  84. package/dist/launchd/providers.d.ts +15 -0
  85. package/dist/launchd/providers.d.ts.map +1 -0
  86. package/dist/launchd/runner.d.ts +91 -0
  87. package/dist/launchd/runner.d.ts.map +1 -0
  88. package/dist/launchd/secret-tripwire.d.ts +24 -0
  89. package/dist/launchd/secret-tripwire.d.ts.map +1 -0
  90. package/dist/openbao/auth-method-form.d.ts +7 -0
  91. package/dist/openbao/auth-method-form.d.ts.map +1 -1
  92. package/dist/openbao/auth-method-reconcile.d.ts +15 -0
  93. package/dist/openbao/auth-method-reconcile.d.ts.map +1 -0
  94. package/dist/openbao/auth-method.d.ts +6 -0
  95. package/dist/openbao/auth-method.d.ts.map +1 -1
  96. package/dist/openbao/auth-role-form.d.ts +9 -0
  97. package/dist/openbao/auth-role-form.d.ts.map +1 -1
  98. package/dist/openbao/auth-role.d.ts.map +1 -1
  99. package/dist/openbao/auth-token-form.d.ts +48 -0
  100. package/dist/openbao/auth-token-form.d.ts.map +1 -0
  101. package/dist/openbao/bao-identity.d.ts +81 -0
  102. package/dist/openbao/bao-identity.d.ts.map +1 -0
  103. package/dist/openbao/bao-namespace.d.ts +20 -0
  104. package/dist/openbao/bao-namespace.d.ts.map +1 -0
  105. package/dist/openbao/cloudflare-role.d.ts.map +1 -1
  106. package/dist/openbao/cloudflare-roles-expand.d.ts.map +1 -1
  107. package/dist/openbao/fake-engines.d.ts +30 -0
  108. package/dist/openbao/fake-engines.d.ts.map +1 -0
  109. package/dist/openbao/fake-stack.d.ts +29 -0
  110. package/dist/openbao/fake-stack.d.ts.map +1 -0
  111. package/dist/openbao/host-approles.d.ts +62 -0
  112. package/dist/openbao/host-approles.d.ts.map +1 -0
  113. package/dist/openbao/index.d.ts +19 -0
  114. package/dist/openbao/index.d.ts.map +1 -1
  115. package/dist/openbao/index.js +1518 -310
  116. package/dist/openbao/index.js.map +40 -17
  117. package/dist/openbao/jwt-config-form.d.ts +51 -0
  118. package/dist/openbao/jwt-config-form.d.ts.map +1 -0
  119. package/dist/openbao/jwt-config.d.ts +25 -0
  120. package/dist/openbao/jwt-config.d.ts.map +1 -0
  121. package/dist/openbao/jwt-role-form.d.ts +91 -0
  122. package/dist/openbao/jwt-role-form.d.ts.map +1 -0
  123. package/dist/openbao/jwt-role.d.ts +31 -0
  124. package/dist/openbao/jwt-role.d.ts.map +1 -0
  125. package/dist/openbao/kubernetes-role-form.d.ts +68 -0
  126. package/dist/openbao/kubernetes-role-form.d.ts.map +1 -0
  127. package/dist/openbao/kubernetes-role.d.ts +25 -0
  128. package/dist/openbao/kubernetes-role.d.ts.map +1 -0
  129. package/dist/openbao/mfa-enforcement-form.d.ts +46 -0
  130. package/dist/openbao/mfa-enforcement-form.d.ts.map +1 -0
  131. package/dist/openbao/mfa-enforcement.d.ts +41 -0
  132. package/dist/openbao/mfa-enforcement.d.ts.map +1 -0
  133. package/dist/openbao/mfa-totp-form.d.ts +52 -0
  134. package/dist/openbao/mfa-totp-form.d.ts.map +1 -0
  135. package/dist/openbao/mfa-totp.d.ts +33 -0
  136. package/dist/openbao/mfa-totp.d.ts.map +1 -0
  137. package/dist/openbao/mfa-wire.d.ts +63 -0
  138. package/dist/openbao/mfa-wire.d.ts.map +1 -0
  139. package/dist/openbao/mount-form.d.ts +7 -0
  140. package/dist/openbao/mount-form.d.ts.map +1 -1
  141. package/dist/openbao/mount-move.d.ts +49 -0
  142. package/dist/openbao/mount-move.d.ts.map +1 -0
  143. package/dist/openbao/mount-path.d.ts +8 -4
  144. package/dist/openbao/mount-path.d.ts.map +1 -1
  145. package/dist/openbao/mount-reconcile.d.ts +16 -0
  146. package/dist/openbao/mount-reconcile.d.ts.map +1 -0
  147. package/dist/openbao/mount.d.ts +9 -0
  148. package/dist/openbao/mount.d.ts.map +1 -1
  149. package/dist/openbao/pki-role-canonical.d.ts +46 -0
  150. package/dist/openbao/pki-role-canonical.d.ts.map +1 -0
  151. package/dist/openbao/pki-role-form.d.ts +38 -30
  152. package/dist/openbao/pki-role-form.d.ts.map +1 -1
  153. package/dist/openbao/pki-role.d.ts.map +1 -1
  154. package/dist/openbao/policy.d.ts +2 -0
  155. package/dist/openbao/policy.d.ts.map +1 -1
  156. package/dist/openbao/proxmox-role-form.d.ts +18 -8
  157. package/dist/openbao/proxmox-role-form.d.ts.map +1 -1
  158. package/dist/openbao/proxmox-role-wire.d.ts +18 -0
  159. package/dist/openbao/proxmox-role-wire.d.ts.map +1 -0
  160. package/dist/openbao/proxmox-role.d.ts.map +1 -1
  161. package/dist/openbao/remount-wire.d.ts +44 -0
  162. package/dist/openbao/remount-wire.d.ts.map +1 -0
  163. package/dist/openbao/rename.d.ts +106 -0
  164. package/dist/openbao/rename.d.ts.map +1 -0
  165. package/dist/openbao/role-reconcile.d.ts +40 -0
  166. package/dist/openbao/role-reconcile.d.ts.map +1 -0
  167. package/docs/caddy.md +180 -0
  168. package/docs/launchd.md +167 -0
  169. package/docs/mesh-node.md +188 -0
  170. package/package.json +12 -2
  171. package/src/caddy/admin-calls.ts +128 -0
  172. package/src/caddy/admin-guard.ts +170 -0
  173. package/src/caddy/admin.ts +74 -0
  174. package/src/caddy/caddyfile-tripwire.ts +133 -0
  175. package/src/caddy/config-form.ts +64 -0
  176. package/src/caddy/config-lifecycle.test.ts +218 -0
  177. package/src/caddy/config-lifecycle.ts +148 -0
  178. package/src/caddy/config.ts +116 -0
  179. package/src/caddy/digest.ts +44 -0
  180. package/src/caddy/fake-caddy.ts +160 -0
  181. package/src/caddy/guards.test.ts +156 -0
  182. package/src/caddy/index.ts +25 -0
  183. package/src/caddy/local-admin.test.ts +160 -0
  184. package/src/caddy/local-admin.ts +189 -0
  185. package/src/caddy/providers.test.ts +244 -0
  186. package/src/caddy/providers.ts +17 -0
  187. package/src/caddy/with-file.ts +62 -0
  188. package/src/cloudflare/fake-mesh.ts +181 -0
  189. package/src/cloudflare/index.ts +9 -3
  190. package/src/cloudflare/mesh-node-api.ts +127 -0
  191. package/src/cloudflare/mesh-node-edges.test.ts +103 -0
  192. package/src/cloudflare/mesh-node-form.test.ts +80 -0
  193. package/src/cloudflare/mesh-node-form.ts +115 -0
  194. package/src/cloudflare/mesh-node-harness.ts +124 -0
  195. package/src/cloudflare/mesh-node-lifecycle.ts +187 -0
  196. package/src/cloudflare/mesh-node-policy.test.ts +151 -0
  197. package/src/cloudflare/mesh-node-token.test.ts +117 -0
  198. package/src/cloudflare/mesh-node-token.ts +125 -0
  199. package/src/cloudflare/mesh-node.test.ts +172 -0
  200. package/src/cloudflare/mesh-node.ts +120 -0
  201. package/src/cloudflare/providers.ts +20 -2
  202. package/src/index.ts +2 -0
  203. package/src/launchd/fake-runner.ts +176 -0
  204. package/src/launchd/host-effect.ts +28 -0
  205. package/src/launchd/host-file-form.ts +71 -0
  206. package/src/launchd/host-file-lifecycle.test.ts +198 -0
  207. package/src/launchd/host-file-lifecycle.ts +164 -0
  208. package/src/launchd/host-file.ts +64 -0
  209. package/src/launchd/host-lookup.test.ts +92 -0
  210. package/src/launchd/host-lookup.ts +94 -0
  211. package/src/launchd/index.ts +26 -0
  212. package/src/launchd/job-form.test.ts +171 -0
  213. package/src/launchd/job-form.ts +180 -0
  214. package/src/launchd/job-identity.test.ts +147 -0
  215. package/src/launchd/job-lifecycle.test.ts +220 -0
  216. package/src/launchd/job-lifecycle.ts +194 -0
  217. package/src/launchd/job-preflight.ts +152 -0
  218. package/src/launchd/job-validate.ts +134 -0
  219. package/src/launchd/job.ts +96 -0
  220. package/src/launchd/launchctl.test.ts +181 -0
  221. package/src/launchd/launchctl.ts +188 -0
  222. package/src/launchd/local-runner.test.ts +125 -0
  223. package/src/launchd/local-runner.ts +143 -0
  224. package/src/launchd/plist.test.ts +186 -0
  225. package/src/launchd/plist.ts +156 -0
  226. package/src/launchd/providers.test.ts +222 -0
  227. package/src/launchd/providers.ts +21 -0
  228. package/src/launchd/runner.ts +95 -0
  229. package/src/launchd/secret-tripwire.test.ts +110 -0
  230. package/src/launchd/secret-tripwire.ts +81 -0
  231. package/src/openbao/README.md +181 -0
  232. package/src/openbao/REPLACE.md +163 -0
  233. package/src/openbao/auth-method-form.ts +7 -0
  234. package/src/openbao/auth-method-reconcile.ts +74 -0
  235. package/src/openbao/auth-method.ts +20 -41
  236. package/src/openbao/auth-role-form.test.ts +16 -1
  237. package/src/openbao/auth-role-form.ts +11 -0
  238. package/src/openbao/auth-role.ts +12 -0
  239. package/src/openbao/auth-token-form.ts +161 -0
  240. package/src/openbao/bao-identity.test.ts +131 -0
  241. package/src/openbao/bao-identity.ts +188 -0
  242. package/src/openbao/bao-namespace.ts +32 -0
  243. package/src/openbao/cloudflare-role-wire.test.ts +8 -34
  244. package/src/openbao/cloudflare-role.ts +30 -8
  245. package/src/openbao/cloudflare-roles-expand.ts +2 -5
  246. package/src/openbao/fake-engines.ts +119 -0
  247. package/src/openbao/fake-stack.ts +112 -0
  248. package/src/openbao/host-approles.test.ts +121 -0
  249. package/src/openbao/host-approles.ts +126 -0
  250. package/src/openbao/index.ts +19 -0
  251. package/src/openbao/jwt-config-form.ts +161 -0
  252. package/src/openbao/jwt-config.ts +85 -0
  253. package/src/openbao/jwt-role-form.test.ts +130 -0
  254. package/src/openbao/jwt-role-form.ts +231 -0
  255. package/src/openbao/jwt-role.ts +102 -0
  256. package/src/openbao/kubernetes-role-form.test.ts +82 -0
  257. package/src/openbao/kubernetes-role-form.ts +149 -0
  258. package/src/openbao/kubernetes-role.ts +94 -0
  259. package/src/openbao/mfa-enforcement-form.ts +147 -0
  260. package/src/openbao/mfa-enforcement.ts +143 -0
  261. package/src/openbao/mfa-totp-form.ts +155 -0
  262. package/src/openbao/mfa-totp.ts +118 -0
  263. package/src/openbao/mfa-wire.ts +117 -0
  264. package/src/openbao/mfa.test.ts +200 -0
  265. package/src/openbao/mount-form.ts +9 -1
  266. package/src/openbao/mount-move.test.ts +164 -0
  267. package/src/openbao/mount-move.ts +113 -0
  268. package/src/openbao/mount-path.test.ts +11 -1
  269. package/src/openbao/mount-path.ts +17 -11
  270. package/src/openbao/mount-reconcile.ts +75 -0
  271. package/src/openbao/mount.ts +26 -43
  272. package/src/openbao/pki-role-canonical.ts +163 -0
  273. package/src/openbao/pki-role-form.test.ts +112 -0
  274. package/src/openbao/pki-role-form.ts +76 -110
  275. package/src/openbao/pki-role.ts +12 -27
  276. package/src/openbao/policy-rename.test.ts +112 -0
  277. package/src/openbao/policy-wire.test.ts +7 -26
  278. package/src/openbao/policy.ts +27 -4
  279. package/src/openbao/proxmox-role-form.ts +27 -8
  280. package/src/openbao/proxmox-role-wire.ts +24 -0
  281. package/src/openbao/proxmox-role.ts +62 -37
  282. package/src/openbao/remount-wire.test.ts +87 -0
  283. package/src/openbao/remount-wire.ts +112 -0
  284. package/src/openbao/rename-occupied.test.ts +166 -0
  285. package/src/openbao/rename.ts +162 -0
  286. package/src/openbao/role-reconcile.test.ts +104 -0
  287. package/src/openbao/role-reconcile.ts +84 -0
  288. package/src/openbao/role-rename.test.ts +205 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The four `launchctl` subcommands LaunchdJob uses — print, print-disabled, bootstrap, bootout —
3
+ * over a HostRunner, plus the parsers for the two read-only ones.
4
+ *
5
+ * ★ MEASURED 2026-09-21 ON macOS 27.2 (read-only `print` / `print-disabled` only; nothing was
6
+ * bootstrapped or booted out to write this):
7
+ * - `print system/<unknown>` exits **113** with "Could not find service … in domain for system"
8
+ * on stderr. 113 is therefore "not loaded", not an error.
9
+ * - `print` of a loaded job is a `<target> = {` block. Its own fields sit one tab deep
10
+ * (`\tstate = running`, `\tpid = 1219`, `\tlast exit code = 0` or `(never exited)`); nested
11
+ * blocks (`resource coalition`, `semaphores`) repeat names like `state` two tabs deep.
12
+ * - `print-disabled <domain>` lists `"label" => enabled|disabled`.
13
+ * - reading needs no root: a uid-501 shell printed system-domain jobs.
14
+ * - `print gui/<uid>/<label>` for a uid with NO LOGIN SESSION exits **112** with "Could not find
15
+ * domain for user gui: <uid>" (and so does `print-disabled gui/<uid>`); a malformed target is
16
+ * 64, a missing service in a live gui domain is 113. So 112 + that text is "the domain does
17
+ * not exist", which means the job cannot be loaded — not an error for a read or a delete.
18
+ * ⚠️ REASONED, NOT MEASURED: bootstrap/bootout exit codes. nix-darwin's own activation script on the
19
+ * same host notes "`bootout` on an already-absent job is a no-op that returns non-zero", which is
20
+ * why bootout here is always preceded by a print and followed by a poll, never trusted alone.
21
+ * ⛔ NEVER PUT `print` STDOUT IN AN ERROR. It contains the job's whole environment.
22
+ */
23
+ import type { HostRunner } from './runner.ts';
24
+ export declare const LAUNCHCTL = "/bin/launchctl";
25
+ /** `launchctl print` of an unknown service — measured, see above. */
26
+ export declare const NOT_FOUND = 113;
27
+ /** `launchctl print` into a gui domain with no login session — measured, see above. */
28
+ export declare const NO_DOMAIN = 112;
29
+ export declare class LaunchctlError extends Error {
30
+ constructor(command: string, exitCode: number, stderr: string);
31
+ }
32
+ export type ServiceStatus = {
33
+ readonly loaded: boolean;
34
+ /** The domain itself is absent (a gui/<uid> with no login session). Implies `loaded: false`. */
35
+ readonly domainMissing?: true;
36
+ readonly state?: string;
37
+ readonly pid?: number;
38
+ readonly lastExitCode?: number;
39
+ };
40
+ /**
41
+ * The job block's own `key = value` lines, ignoring nested blocks.
42
+ * ⚠️ A multi-line ProgramArguments entry is printed raw inside `arguments = {`, so a close is only
43
+ * accepted when its indentation matches the open. An argument that itself contains a line of
44
+ * exactly one tab and `}` would still desynchronise this; no real job has one.
45
+ */
46
+ export declare const printFields: (stdout: string) => Map<string, string>;
47
+ export declare const parsePrint: (stdout: string) => ServiceStatus;
48
+ /** Labels `print-disabled` reports as disabled. Older releases printed `=> true` for disabled. */
49
+ export declare const parseDisabled: (stdout: string) => Set<string>;
50
+ export declare const printService: (runner: HostRunner, target: string) => Promise<ServiceStatus>;
51
+ export declare const isDisabled: (runner: HostRunner, domain: string, label: string) => Promise<boolean>;
52
+ export declare const bootstrap: (runner: HostRunner, domain: string, plistPath: string) => Promise<void>;
53
+ /**
54
+ * Poll `print` until the service is gone. ⚠️ bootout can return before launchd lets go of it.
55
+ * ★ 30 s, because a job gets SIGTERM and then its ExitTimeOut before SIGKILL: launchd.plist(5)
56
+ * documents a 20 s default (a macOS 27.2 `print` showed `exit timeout = 5` for a job that sets
57
+ * none). Giving up sooner would fail a deploy over a job that was, correctly, still exiting.
58
+ */
59
+ export declare const waitUnloaded: (runner: HostRunner, target: string, attempts?: number, intervalMs?: number) => Promise<void>;
60
+ /** Boot a service out if it is loaded, and wait until launchd has let go. Idempotent. */
61
+ export declare const bootoutIfLoaded: (runner: HostRunner, target: string) => Promise<void>;
62
+ //# sourceMappingURL=launchctl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"launchctl.d.ts","sourceRoot":"","sources":["../../src/launchd/launchctl.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAC1C,qEAAqE;AACrE,eAAO,MAAM,SAAS,MAAM,CAAC;AAC7B,uFAAuF;AACvF,eAAO,MAAM,SAAS,MAAM,CAAC;AAG7B,qBAAa,cAAe,SAAQ,KAAK;IACvC,YAAY,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAG5D;CACF;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,gGAAgG;IAChG,QAAQ,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,WAAY,MAAM,KAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAuB9D,CAAC;AAOF,eAAO,MAAM,UAAU,WAAY,MAAM,KAAG,aAY3C,CAAC;AAEF,kGAAkG;AAClG,eAAO,MAAM,aAAa,WAAY,MAAM,KAAG,GAAG,CAAC,MAAM,CAQxD,CAAC;AAEF,eAAO,MAAM,YAAY,WAAkB,UAAU,UAAU,MAAM,KAAG,OAAO,CAAC,aAAa,CAW5F,CAAC;AAEF,eAAO,MAAM,UAAU,WACb,UAAU,UACV,MAAM,SACP,MAAM,KACZ,OAAO,CAAC,OAAO,CAMjB,CAAC;AAEF,eAAO,MAAM,SAAS,WACZ,UAAU,UACV,MAAM,aACH,MAAM,KAChB,OAAO,CAAC,IAAI,CAKd,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,WACf,UAAU,UACV,MAAM,6CAGb,OAAO,CAAC,IAAI,CAQd,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,eAAe,WAAkB,UAAU,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAqBtF,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { HostRunner } from './runner.ts';
2
+ export type LocalRunnerOptions = {
3
+ /** Kill a spawned program after this long. @default 120_000 */
4
+ readonly execTimeoutMs?: number;
5
+ };
6
+ export declare const localRunner: (options?: LocalRunnerOptions) => HostRunner;
7
+ //# sourceMappingURL=local-runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-runner.d.ts","sourceRoot":"","sources":["../../src/launchd/local-runner.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAwB,UAAU,EAAgB,MAAM,aAAa,CAAC;AAElF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC,CAAC;AAyEF,eAAO,MAAM,WAAW,aAAa,kBAAkB,KAAQ,UAgD9D,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * An XML property list serializer — the subset launchd.plist(5) reads, and nothing else.
3
+ *
4
+ * ★ WHY NOT AN npm LIBRARY. Nothing in this workspace depends on one (checked 2026-09-21: no
5
+ * `plist`, `xmlbuilder` or `@xmldom/xmldom` anywhere under node_modules), and the common one pulls
6
+ * an XML DOM in for PARSING, which a provider that only RENDERS never does. The subset a launchd
7
+ * job needs is five node types, so this is ~100 lines plus exhaustive tests (plist.test.ts), which
8
+ * also round-trip every case through Apple's own `plutil` when the tests run on macOS.
9
+ *
10
+ * ⛔ THE OUTPUT MUST BE DETERMINISTIC. LaunchdJob diffs by the SHA-256 of these bytes, so two
11
+ * renders of one declaration must be byte-identical: dict keys are sorted, indentation is fixed,
12
+ * and nothing depends on insertion order. A non-deterministic render is a forever-`update`.
13
+ * ⚠️ AND A CHANGE TO THIS FILE'S OUTPUT RESTARTS EVERY JOB. A new header, indent or escape changes
14
+ * every digest, so the next deploy after the kit upgrade updates — restarts — every declared
15
+ * job at once. Treat any byte change here as a breaking change and say so in its changeset.
16
+ *
17
+ * ⛔ REFUSE, NEVER COERCE. `undefined`, `null`, a fraction, NaN, a class instance or a character XML
18
+ * 1.0 cannot carry is a thrown PlistError naming the path, not a silently dropped key. A dropped
19
+ * `KeepAlive` is a daemon that never restarts, and nothing downstream would say why.
20
+ */
21
+ /** Every value launchd reads. ⚠️ No `<real>`, `<date>` or `<data>`: no launchd key needs one. */
22
+ export type PlistValue = string | number | boolean | readonly PlistValue[] | {
23
+ readonly [key: string]: PlistValue;
24
+ };
25
+ export type PlistDict = {
26
+ readonly [key: string]: PlistValue;
27
+ };
28
+ export declare class PlistError extends Error {
29
+ readonly path: string;
30
+ constructor(path: string, detail: string);
31
+ }
32
+ /**
33
+ * Escape text content.
34
+ * ⚠️ CR IS A CHARACTER REFERENCE, NOT A LITERAL. An XML parser normalises a literal CR (and CRLF)
35
+ * to LF before the application sees it (XML 1.0 §2.11), so `a\r\nb` would come back as `a\nb` and
36
+ * the argument launchd passes would differ from the one declared. `&#13;` survives normalisation.
37
+ */
38
+ export declare const escapeText: (text: string, path: string) => string;
39
+ /** Render a top-level dict as a complete XML plist document, ending in a newline. */
40
+ export declare const renderPlist: (dict: PlistDict) => string;
41
+ //# sourceMappingURL=plist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plist.d.ts","sourceRoot":"","sources":["../../src/launchd/plist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,iGAAiG;AACjG,MAAM,MAAM,UAAU,GAClB,MAAM,GACN,MAAM,GACN,OAAO,GACP,SAAS,UAAU,EAAE,GACrB;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AAE/D,qBAAa,UAAW,SAAQ,KAAK;IAEjC,QAAQ,CAAC,IAAI,EAAE,MAAM;IADvB,YACW,IAAI,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EAIf;CACF;AAoBD;;;;;GAKG;AACH,eAAO,MAAM,UAAU,SAAU,MAAM,QAAQ,MAAM,KAAG,MAcvD,CAAC;AAoEF,qFAAqF;AACrF,eAAO,MAAM,WAAW,SAAU,SAAS,KAAG,MAO7C,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Both launchd-subpath providers with one HostRunner, as a single layer for a host stack.
3
+ *
4
+ * Layer.mergeAll(launchdProviders(), …the stack's other providers)
5
+ *
6
+ * ★ ONE RUNNER FOR BOTH, so a daemon's config file (HostFile) and the daemon (LaunchdJob) are
7
+ * written by the same identity on the same host — a stack cannot write the file as one user and
8
+ * bootstrap the job as another by accident.
9
+ * ⚠️ The default is localRunner(): this process, never elevated. A system-domain job therefore
10
+ * needs the deploy itself started as root, or a runner passed here with `privileged: true`.
11
+ */
12
+ import * as Layer from 'effect/Layer';
13
+ import { type HostRunner } from './runner.ts';
14
+ export declare const launchdProviders: (runner?: HostRunner) => Layer.Layer<import("alchemy").Provider<import("./host-file.ts").HostFile> | import("alchemy").Provider<import("./job.ts").LaunchdJob>, never, never>;
15
+ //# sourceMappingURL=providers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../../src/launchd/providers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAItC,OAAO,EAAE,KAAK,UAAU,EAAmB,MAAM,aAAa,CAAC;AAE/D,eAAO,MAAM,gBAAgB,YAAY,UAAU,yJAGhD,CAAC"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * The one seam between the launchd providers and the host: every file read, file write and
3
+ * `launchctl` call goes through a HostRunner, and nothing in this directory touches the host any
4
+ * other way.
5
+ *
6
+ * ★ WHY A SEAM AND NOT DIRECT node:fs CALLS. launchd has no SDK — the provider renders a plist and
7
+ * drives `launchctl` — so the only way to test the lifecycle without a real launchd (and without
8
+ * writing /Library) is to swap the host out. fake-runner.ts is an in-memory host with a scripted
9
+ * launchd; local-runner.ts is the real one. The same seam is where a consumer plugs in a runner
10
+ * that reaches a host some other way.
11
+ *
12
+ * ★ PROMISES, NOT EFFECTS, ON PURPOSE. A consumer writing their own runner implements plain async
13
+ * functions; the providers lift them into Effect at the call site (host-effect.ts).
14
+ */
15
+ import * as Context from 'effect/Context';
16
+ import * as Layer from 'effect/Layer';
17
+ /** One finished process. ⛔ `stdout` may carry anything the job's environment holds — never log it. */
18
+ export type ExecResult = {
19
+ readonly exitCode: number;
20
+ readonly stdout: string;
21
+ readonly stderr: string;
22
+ };
23
+ /**
24
+ * What `lstat` says about a path. ⚠️ lstat, not stat: a symlink is reported as a symlink, because
25
+ * writing through one would change a file this resource never named.
26
+ */
27
+ export type FileStat = {
28
+ readonly kind: 'file' | 'directory' | 'symlink' | 'other';
29
+ /** Permission bits only (`& 0o7777`), never the file-type bits. */
30
+ readonly mode: number;
31
+ readonly uid: number;
32
+ readonly gid: number;
33
+ readonly size: number;
34
+ };
35
+ export type WriteOptions = {
36
+ /** Permission bits for the final file, applied BEFORE it becomes visible at `path`. */
37
+ readonly mode: number;
38
+ /** Numeric owner; omitted means whoever the runner writes as. */
39
+ readonly uid?: number;
40
+ /** Numeric group; omitted means the directory's default. */
41
+ readonly gid?: number;
42
+ };
43
+ export type HostUser = {
44
+ readonly uid: number;
45
+ readonly gid: number;
46
+ readonly home: string;
47
+ };
48
+ export interface HostRunner {
49
+ /**
50
+ * ⛔ DECLARED, NEVER INFERRED. `true` only when every call this runner makes already runs with
51
+ * root's authority — a runner that talks to a root helper, say. The providers refuse
52
+ * system-domain writes unless `effectiveUid()` is 0 or this is `true`, so claiming it falsely
53
+ * turns that refusal into an EACCES halfway through a deploy.
54
+ * ⛔ THE KIT NEVER ELEVATES. No runner here calls `sudo`: a deploy that prompts for a password
55
+ * mid-plan, or silently uses a cached sudo ticket, is exactly the "no silent sudo" failure. A
56
+ * consumer who wants elevation writes that runner deliberately and sets this flag.
57
+ */
58
+ readonly privileged: boolean;
59
+ /** The uid file writes and launchctl calls run as. */
60
+ effectiveUid(): number;
61
+ /** Run a program with an argv — ⛔ never through a shell. */
62
+ exec(argv: readonly string[]): Promise<ExecResult>;
63
+ /** File bytes, or `undefined` when nothing is at `path`. */
64
+ readFile(path: string): Promise<Uint8Array | undefined>;
65
+ /** lstat, or `undefined` when nothing is at `path`. */
66
+ stat(path: string): Promise<FileStat | undefined>;
67
+ /**
68
+ * Write `bytes` so that `path` holds either the old file or the new one, never a torn mix:
69
+ * a temporary file in the same directory, mode and owner set on it, then rename(2) over `path`.
70
+ * ⚠️ The parent directory must exist; creating it would invent an owner and mode nobody declared.
71
+ */
72
+ writeFileAtomic(path: string, bytes: Uint8Array, options: WriteOptions): Promise<void>;
73
+ /** Remove a file. Idempotent: nothing at `path` is success. */
74
+ removeFile(path: string): Promise<void>;
75
+ /** A user by name or numeric id, or `undefined` when the host has none. */
76
+ lookupUser(nameOrId: string): Promise<HostUser | undefined>;
77
+ /** A group's gid by name or numeric id, or `undefined` when the host has none. */
78
+ lookupGroup(nameOrId: string): Promise<number | undefined>;
79
+ /** Wait between launchctl polls. ★ Injected so a test never sleeps for real. */
80
+ sleep(ms: number): Promise<void>;
81
+ }
82
+ declare const HostRunnerService_base: Context.ServiceClass<HostRunnerService, "homeflare/launchd/HostRunner", HostRunner>;
83
+ /** The Effect service every launchd provider reads its runner from. */
84
+ export declare class HostRunnerService extends HostRunnerService_base {
85
+ }
86
+ /** Provide a runner to the providers: `Layer.provide(hostRunnerLayer(localRunner()))`. */
87
+ export declare const hostRunnerLayer: (runner: HostRunner) => Layer.Layer<HostRunnerService>;
88
+ /** True when this runner may write root-owned paths and drive the system domain. */
89
+ export declare const canActAsRoot: (runner: HostRunner) => boolean;
90
+ export {};
91
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/launchd/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAC;AAC1C,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAEtC,sGAAsG;AACtG,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;IAC1D,mEAAmE;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,MAAM,WAAW,UAAU;IACzB;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,sDAAsD;IACtD,YAAY,IAAI,MAAM,CAAC;IACvB,4DAA4D;IAC5D,IAAI,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IACnD,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAAC,CAAC;IACxD,uDAAuD;IACvD,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAClD;;;;OAIG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,+DAA+D;IAC/D,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,2EAA2E;IAC3E,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAC5D,kFAAkF;IAClF,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC3D,gFAAgF;IAChF,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAClC;;AAED,uEAAuE;AACvE,qBAAa,iBAAkB,SAAQ,sBAEtC;CAAG;AAEJ,0FAA0F;AAC1F,eAAO,MAAM,eAAe,WAAY,UAAU,KAAG,KAAK,CAAC,KAAK,CAAC,iBAAiB,CACxC,CAAC;AAE3C,oFAAoF;AACpF,eAAO,MAAM,YAAY,WAAY,UAAU,KAAG,OACA,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A tripwire for the obvious ways a secret ends up in Alchemy state through these providers.
3
+ *
4
+ * ⛔ WHY IT EXISTS. Alchemy persists every prop UNENCRYPTED in its state store — the same reason
5
+ * the OpenBao families are metadata-only (openbao/policy.ts). A LaunchdJob's `environment` and
6
+ * `programArguments`, and a HostFile's `content`, are props. A token declared there is a token
7
+ * copied into the state store, into every plan diff, and into the plist or file on disk.
8
+ * ★ THE ALTERNATIVE, NAMED IN EVERY REFUSAL: let a secret renderer (openbao-agent, or any other)
9
+ * write the secret to a root-owned file, and declare only its PATH — `FOO_TOKEN_FILE=/path`,
10
+ * `--token-file /path`. The job reads the file at start; the state holds a path.
11
+ *
12
+ * ⚠️ A TRIPWIRE, NOT A SCANNER. It catches the names and shapes people actually type. A token in
13
+ * an innocently named variable passes; nothing here can make a secret safe to declare.
14
+ */
15
+ import type { PlistDict } from './plist.ts';
16
+ export declare const PRIVATE_KEY: RegExp;
17
+ /**
18
+ * Refusals for a job's environment, argv and extraKeys. Empty means nothing tripped.
19
+ * ★ extraKeys too: it is a prop like the others, so it lands in state just the same.
20
+ */
21
+ export declare const jobSecretProblems: (environment: Readonly<Record<string, string>> | undefined, argv: readonly string[], extraKeys?: PlistDict) => string[];
22
+ /** Refusals for a HostFile's content. */
23
+ export declare const fileSecretProblems: (content: string) => string[];
24
+ //# sourceMappingURL=secret-tripwire.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secret-tripwire.d.ts","sourceRoot":"","sources":["../../src/launchd/secret-tripwire.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,SAAS,EAAc,MAAM,YAAY,CAAC;AAgBxD,eAAO,MAAM,WAAW,QAA0C,CAAC;AAgBnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,gBACf,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,SAAS,QACnD,SAAS,MAAM,EAAE,cACX,SAAS,KACpB,MAAM,EAsBR,CAAC;AAEF,yCAAyC;AACzC,eAAO,MAAM,kBAAkB,YAAa,MAAM,KAAG,MAAM,EACuB,CAAC"}
@@ -9,6 +9,13 @@ export interface BaoAuthMethodProps {
9
9
  defaultLeaseTtl?: string;
10
10
  /** Max lease TTL, e.g. `8760h`. */
11
11
  maxLeaseTtl?: string;
12
+ /**
13
+ * The path this method lives at NOW, when `path` is where it should move to — `sys/remount`
14
+ * from `auth/<remountFrom>`, keeping every role. Without it a changed `path` fails the plan
15
+ * (mount-move.ts). ⚠️ Every token issued through the method is revoked by the move, and login
16
+ * URLs, policies and MFA enforcements by accessor are not rewritten (the accessor is kept).
17
+ */
18
+ remountFrom?: string;
12
19
  }
13
20
  export interface BaoAuthMethodAttributes {
14
21
  path: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth-method-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method-form.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAoB,CAAC;AAiB1D,eAAO,MAAM,YAAY,UAChB,kBAAkB,QACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,uBAUF,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAiC,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,WAA6B,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,UAAU,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAI3E,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,QAAQ,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAMzE,CAAC;AASF,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,uBAAuB,SAAS,kBAAkB,YAIlC,CAAC;AAErD,eAAO,MAAM,SAAS,UAAW,kBAAkB,YAGlB,CAAC"}
1
+ {"version":3,"file":"auth-method-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method-form.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,kBAAkB;IACjC,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mCAAmC;IACnC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wEAAwE;AACxE,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAoB,CAAC;AAiB1D,eAAO,MAAM,YAAY,UAChB,kBAAkB,QACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,uBAUF,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAiC,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,WAA6B,CAAC;AAEnE;;;;GAIG;AACH,eAAO,MAAM,UAAU,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAI3E,CAAC;AAEF,yFAAyF;AACzF,eAAO,MAAM,QAAQ,UAAW,kBAAkB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAMzE,CAAC;AASF,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,uBAAuB,SAAS,kBAAkB,YAIlC,CAAC;AAErD,eAAO,MAAM,SAAS,UAAW,kBAAkB,YAGlB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Bao.AuthMethod's read and reconcile as plain effects — split from auth-method.ts so the move runs
3
+ * against a fake server. Same shape as mount-reconcile.ts; the one difference that matters is the
4
+ * `auth/` prefix sys/remount needs (vault/logical_system.go:1295-1310 refuses to move an auth mount
5
+ * to a non-auth path, and `token/` cannot move at all).
6
+ */
7
+ import * as Effect from 'effect/Effect';
8
+ import type * as HttpClient from 'effect/unstable/http/HttpClient';
9
+ import { type BaoAuthMethodAttributes, type BaoAuthMethodProps } from './auth-method-form.ts';
10
+ import type { BaoError } from './bao-status.ts';
11
+ import type { RemountError, RemountOptions } from './remount-wire.ts';
12
+ export declare const readMethod: (props: BaoAuthMethodProps) => Effect.Effect<BaoAuthMethodAttributes | undefined, BaoError, HttpClient.HttpClient>;
13
+ /** Converge one auth method. `stated` is the path in Alchemy's state, if any. */
14
+ export declare const reconcileAuthMethod: (news: BaoAuthMethodProps, stated: string | undefined, options?: RemountOptions) => Effect.Effect<BaoAuthMethodAttributes, BaoError | RemountError, HttpClient.HttpClient>;
15
+ //# sourceMappingURL=auth-method-reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-method-reconcile.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method-reconcile.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAKxB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEtE,eAAO,MAAM,UAAU,UACd,kBAAkB,KACxB,MAAM,CAAC,MAAM,CAAC,uBAAuB,GAAG,SAAS,EAAE,QAAQ,EAAE,UAAU,CAAC,UAAU,CAKjF,CAAC;AAUL,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,SACxB,kBAAkB,UAChB,MAAM,GAAG,SAAS,YAChB,cAAc,KACvB,MAAM,CAAC,MAAM,CAAC,uBAAuB,EAAE,QAAQ,GAAG,YAAY,EAAE,UAAU,CAAC,UAAU,CA8BpF,CAAC"}
@@ -7,6 +7,12 @@
7
7
  * ★ `defaultRemovalPolicy: 'retain'` — disabling a method destroys every role under it
8
8
  * and revokes tokens minted through those roles. Opt in with
9
9
  * `.pipe(RemovalPolicy.destroy())`.
10
+ *
11
+ * ★ REPLACE SEMANTICS (audited 2026-09-21, see REPLACE.md): `type` changed → plans `replace`, whose
12
+ * create half dies on the still-occupied path before any write, so the apply fails and nothing is
13
+ * disabled (auth-method-reconcile.ts); `path` changed → FAILS unless `remountFrom` names the old
14
+ * path (then an in-place move). It used to plan `update` and enable an empty method at the new
15
+ * path, exactly as Bao.Mount did.
10
16
  */
11
17
  import { Resource } from 'alchemy';
12
18
  import * as Provider from 'alchemy/Provider';
@@ -1 +1 @@
1
- {"version":3,"file":"auth-method.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAKxB,MAAM,uBAAuB,CAAC;AAQ/B,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,CAAC;AAE5D,MAAM,WAAW,aAAc,SAAQ,QAAQ,CAC7C,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,aAAa,gDAExB,CAAC;AASH,eAAO,MAAM,qBAAqB,oGAmE/B,CAAC"}
1
+ {"version":3,"file":"auth-method.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-method.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EAExB,MAAM,uBAAuB,CAAC;AAK/B,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,CAAC;AAE5D,MAAM,WAAW,aAAc,SAAQ,QAAQ,CAC7C,gBAAgB,EAChB,kBAAkB,EAClB,uBAAuB,EACvB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,aAAa,gDAExB,CAAC;AAEH,eAAO,MAAM,qBAAqB,oGAqD/B,CAAC"}
@@ -33,6 +33,15 @@ export declare const readPath: (name: string) => string;
33
33
  * ★ ALL STRINGS, EXACTLY THE `k=v` PAIRS `bao write` USED TO SEND — see `baoWrite` in bao-http.ts.
34
34
  */
35
35
  export declare const writeBody: (props: BaoAuthRoleProps) => Record<string, string>;
36
+ /**
37
+ * Whether a changed `name` names a DIFFERENT role — the diff's `replace` question.
38
+ *
39
+ * ⚠️ CASE-INSENSITIVELY, BECAUSE THE SERVER IS. AppRole stores `role/<lowercased name>` (openbao
40
+ * v2.6.2 builtin/credential/approle/path_role.go:1485, and roleEntry reads the same key, :1537),
41
+ * so `Host` → `host` is the SAME role: a `replace` there would write it, then — under `destroy` —
42
+ * delete the old generation, which is that same role.
43
+ */
44
+ export declare const isRename: (from: string, to: string) => boolean;
36
45
  /** True when live already matches the declaration on every managed field. */
37
46
  export declare const matches: (attributes: BaoAuthRoleAttributes, props: BaoAuthRoleProps) => boolean;
38
47
  //# sourceMappingURL=auth-role-form.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role-form.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,eAAO,MAAM,YAAY,UAChB,gBAAgB,QACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,qBAWF,CAAC;AAEF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAgC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAYxE,CAAC;AAWF,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,qBAAqB,SAAS,gBAAgB,YAUjF,CAAC"}
1
+ {"version":3,"file":"auth-role-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role-form.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,gBAAgB;IAC/B,4DAA4D;IAC5D,IAAI,EAAE,MAAM,CAAC;IACb,sFAAsF;IACtF,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,0FAA0F;IAC1F,WAAW,EAAE,MAAM,CAAC;IACpB,kFAAkF;IAClF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,MAAM,EAAE,MAAM,CAAC;CAChB;AAwBD,eAAO,MAAM,YAAY,UAChB,gBAAgB,QACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,qBAWF,CAAC;AAEF,eAAO,MAAM,QAAQ,SAAU,MAAM,WAAgC,CAAC;AAEtE;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAW,gBAAgB,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAYxE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,SAAU,MAAM,MAAM,MAAM,KAAG,OACX,CAAC;AAW1C,6EAA6E;AAC7E,eAAO,MAAM,OAAO,eAAgB,qBAAqB,SAAS,gBAAgB,YAUjF,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"auth-role.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAKtB,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;AAExD,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AASH,eAAO,MAAM,mBAAmB,kGAsD7B,CAAC"}
1
+ {"version":3,"file":"auth-role.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EAMtB,MAAM,qBAAqB,CAAC;AAG7B,YAAY,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,CAAC;AAExD,MAAM,WAAW,WAAY,SAAQ,QAAQ,CAC3C,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,WAAW,8CAEtB,CAAC;AASH,eAAO,MAAM,mBAAmB,kGAiE7B,CAAC"}
@@ -0,0 +1,48 @@
1
+ export type BaoTokenType = 'batch' | 'default' | 'service';
2
+ export interface BaoTokenProps {
3
+ /** Policies on tokens this role issues. Names only. */
4
+ tokenPolicies: readonly string[];
5
+ /** Token TTL (`15m`). Default `0` — the mount's default. */
6
+ tokenTtl?: string;
7
+ /** Token max TTL (`1h`). Default `0` — the mount's max. */
8
+ tokenMaxTtl?: string;
9
+ /** Hard ceiling that renewal cannot pass. Default `0` (none). */
10
+ tokenExplicitMaxTtl?: string;
11
+ /** Periodic tokens renew forever within this period. Default `0` (not periodic). */
12
+ tokenPeriod?: string;
13
+ /** Leave the `default` policy off. Default false. */
14
+ tokenNoDefaultPolicy?: boolean;
15
+ /** CIDRs a token may be USED from. Default none. */
16
+ tokenBoundCidrs?: readonly string[];
17
+ /** Uses before the token dies; `0` is unlimited. Default 0. */
18
+ tokenNumUses?: number;
19
+ /** Default `default` — the mount decides, which means service tokens unless it says otherwise. */
20
+ tokenType?: BaoTokenType;
21
+ }
22
+ /** The token fields as seconds and sorted sets — both sides of a diff are built by `tokenForm`. */
23
+ export interface BaoTokenForm {
24
+ tokenBoundCidrs: readonly string[];
25
+ tokenExplicitMaxTtlSeconds: number;
26
+ tokenMaxTtlSeconds: number;
27
+ tokenNoDefaultPolicy: boolean;
28
+ tokenNumUses: number;
29
+ tokenPeriodSeconds: number;
30
+ tokenPolicies: readonly string[];
31
+ tokenTtlSeconds: number;
32
+ tokenType: string;
33
+ }
34
+ /**
35
+ * The token fields of an already-normalised form, in one fixed key order — what a role family's own
36
+ * canonical literal spreads, so its digest cannot depend on how the object was assembled.
37
+ */
38
+ export declare const tokenFields: (input: BaoTokenForm) => BaoTokenForm;
39
+ export declare const tokenFormOfProps: (props: BaoTokenProps) => BaoTokenForm;
40
+ export declare const tokenFormOfLive: (live: Record<string, unknown>) => BaoTokenForm;
41
+ /**
42
+ * The token half of a role write — every field, defaults included (the merge ⚠️ above). Durations
43
+ * go over as the author's own strings; lists as JSON arrays, which a comma-string-slice accepts.
44
+ */
45
+ export declare const tokenBody: (props: BaoTokenProps) => Record<string, unknown>;
46
+ /** Refusals the token half can know without a server. */
47
+ export declare const tokenProblems: (props: BaoTokenProps) => readonly string[];
48
+ //# sourceMappingURL=auth-token-form.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-token-form.d.ts","sourceRoot":"","sources":["../../src/openbao/auth-token-form.ts"],"names":[],"mappings":"AAiBA,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,oDAAoD;IACpD,eAAe,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kGAAkG;IAClG,SAAS,CAAC,EAAE,YAAY,CAAC;CAC1B;AAED,mGAAmG;AACnG,MAAM,WAAW,YAAY;IAC3B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,0BAA0B,EAAE,MAAM,CAAC;IACnC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AAgBD;;;GAGG;AACH,eAAO,MAAM,WAAW,UAAW,YAAY,KAAG,YAUhD,CAAC;AAeH,eAAO,MAAM,gBAAgB,UAAW,aAAa,KAAG,YAWpD,CAAC;AAEL,eAAO,MAAM,eAAe,SAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,YAW5D,CAAC;AAEL;;;GAGG;AACH,eAAO,MAAM,SAAS,UAAW,aAAa,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAUrE,CAAC;AAEH,yDAAyD;AACzD,eAAO,MAAM,aAAa,UAAW,aAAa,KAAG,SAAS,MAAM,EAqBnE,CAAC"}
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Refuse to plan against the wrong vault: assert the `cluster_name` and namespace a stack expects
3
+ * BEFORE any Bao.* resource reads or writes.
4
+ *
5
+ * ⛔ WHY IT EXISTS. A `BAO_ADDR` left exported for one vault (or a `BAO_AGENT_ADDR`, which wins)
6
+ * would otherwise apply a stack written for another vault to it, and every family here would
7
+ * plan CREATEs against the wrong server with a clean conscience (the estate's site-config plan,
8
+ * "Identity checks on every live plan").
9
+ * A missing `BAO_NAMESPACE` does the same thing one level down: the stack lands in root.
10
+ *
11
+ * ★ `sys/health`, UNAUTHENTICATED, WITH NO TOKEN AND NO NAMESPACE HEADER. READ FROM openbao v2.6.2:
12
+ * · http/sys_health.go:143-165 — `cluster_name` and `cluster_id` are filled ONLY while unsealed,
13
+ * and the body is top-level JSON, not wrapped in `data`.
14
+ * · http/handler.go:108-124, :405-411 — `health` is a root-only API: a request carrying
15
+ * `X-Vault-Namespace` is answered 400 "operation unavailable in namespaces". So the header is
16
+ * stripped for this one call, whatever BAO_NAMESPACE says.
17
+ * · The query asks for 200 when sealed, uninitialised or standby (`sealedcode`, `uninitcode`,
18
+ * `standbyok`, :78-110), so the body is always there to read and the refusal can say WHICH.
19
+ * ⛔ NO TOKEN IS SENT. Until this check passes, the server has not even claimed to be ours, and a
20
+ * token sent to the wrong server is a token handed over.
21
+ * ⚠️ IT CATCHES A MISTARGET, NOT AN IMPOSTOR (review 2026-09-21). `cluster_name` is answered to
22
+ * anyone, unauthenticated, so a hostile server can echo the expected one; passing proves the
23
+ * address is configured right, never who answers. TLS to a trusted certificate is what
24
+ * authenticates the server — keep an https BAO_ADDR on anything that is not loopback or a socket.
25
+ * ★ THE NAMESPACE IS CHECKED TWICE. Locally first: the namespace every later call will send (the
26
+ * same bao-address.ts resolution) must BE the expected one. Then remotely: unauthenticated
27
+ * `sys/internal/ui/mounts` with that namespace header answers 200 when the namespace exists and
28
+ * 404 "namespace not found" when it does not (vault/request_handling.go:607-612; the path is in
29
+ * the system backend's Unauthenticated list, vault/logical_system.go:95). Only the status is read.
30
+ * REASONED FROM SOURCE, not measured against a live server.
31
+ * ⚠️ IT PROVES THE CLUSTER, NOT THE NODE. A standby answers with the cluster's name, which is the
32
+ * point: a standby forwards to the same vault.
33
+ */
34
+ import * as Effect from 'effect/Effect';
35
+ import type * as HttpClient from 'effect/unstable/http/HttpClient';
36
+ import { type BaoEnvironment } from './bao-address.ts';
37
+ /** `input` a blank expectation · `mismatch` the wrong vault · `unconfirmed` could not tell · `unreachable` no answer. */
38
+ export type BaoIdentityFailure = 'input' | 'mismatch' | 'unconfirmed' | 'unreachable';
39
+ /** ⛔ Every message starts `REFUSING:`, and none carries a token — only the address and names. */
40
+ export declare class BaoIdentityError extends Error {
41
+ readonly reason: BaoIdentityFailure;
42
+ constructor(reason: BaoIdentityFailure, detail: string);
43
+ }
44
+ export interface BaoIdentityExpected {
45
+ /** The vault's `cluster_name` (server config `cluster_name`, or the one generated at init). */
46
+ readonly clusterName: string;
47
+ /** The namespace the stack targets: `team-a`, or `''` / `root` for the root namespace. */
48
+ readonly namespace: string;
49
+ }
50
+ /** What the vault said about itself once every check passed. */
51
+ export interface BaoIdentity {
52
+ /** The resolved address, without any token. */
53
+ readonly address: string;
54
+ readonly clusterName: string;
55
+ readonly clusterId: string;
56
+ readonly version: string;
57
+ /** Canonical: `''` is root. */
58
+ readonly namespace: string;
59
+ }
60
+ export declare const HEALTH_PATH = "sys/health?standbyok=true&sealedcode=200&uninitcode=200";
61
+ export declare const NAMESPACE_PROBE_PATH = "sys/internal/ui/mounts";
62
+ /** Read a health body into the one question asked of it. Pure, for the tests. */
63
+ export declare const checkHealth: (address: string, expectedCluster: string, body: Record<string, unknown> | undefined) => {
64
+ readonly clusterId: string;
65
+ readonly version: string;
66
+ } | BaoIdentityError;
67
+ /**
68
+ * The check, as an Effect under BaoEnv (process.env unless provided). Fails with BaoIdentityError
69
+ * and nothing else.
70
+ */
71
+ export declare const assertBaoIdentityEffect: (expected: BaoIdentityExpected) => Effect.Effect<BaoIdentity, BaoIdentityError, HttpClient.HttpClient>;
72
+ /**
73
+ * `assertBaoIdentityEffect` for a plain script. `env` defaults to `process.env`, read at call time.
74
+ * ★ Rejects with the BaoIdentityError itself (Effect.runPromise, as in approle-login.ts).
75
+ *
76
+ * await assertBaoIdentity({ clusterName: 'vault-example', namespace: 'team-a' });
77
+ */
78
+ export declare const assertBaoIdentity: (input: BaoIdentityExpected & {
79
+ readonly env?: BaoEnvironment;
80
+ }) => Promise<BaoIdentity>;
81
+ //# sourceMappingURL=bao-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bao-identity.d.ts","sourceRoot":"","sources":["../../src/openbao/bao-identity.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,kBAAkB,CAAC;AAKvE,yHAAyH;AACzH,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,UAAU,GAAG,aAAa,GAAG,aAAa,CAAC;AAEtF,iGAAiG;AACjG,qBAAa,gBAAiB,SAAQ,KAAK;IAEvC,QAAQ,CAAC,MAAM,EAAE,kBAAkB;IADrC,YACW,MAAM,EAAE,kBAAkB,EACnC,MAAM,EAAE,MAAM,EAIf;CACF;AAED,MAAM,WAAW,mBAAmB;IAClC,+FAA+F;IAC/F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,gEAAgE;AAChE,MAAM,WAAW,WAAW;IAC1B,+CAA+C;IAC/C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,WAAW,4DAA4D,CAAC;AACrF,eAAO,MAAM,oBAAoB,2BAA2B,CAAC;AAc7D,iFAAiF;AACjF,eAAO,MAAM,WAAW,YACb,MAAM,mBACE,MAAM,QACjB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,KACxC;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GAAG,gBAyB7D,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,uBAAuB,aACxB,mBAAmB,KAC5B,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,gBAAgB,EAAE,UAAU,CAAC,UAAU,CA8CjE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,UACrB,mBAAmB,GAAG;IAAE,QAAQ,CAAC,GAAG,CAAC,EAAE,cAAc,CAAA;CAAE,KAC7D,OAAO,CAAC,WAAW,CAKrB,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Namespace spellings, made comparable. `homeflare`, `/homeflare/` and `homeflare/` are one
3
+ * namespace to OpenBao, and `root` (or nothing) is the root namespace.
4
+ *
5
+ * ★ READ FROM openbao v2.6.2: namespace.Canonicalize trims the leading slash and appends a trailing
6
+ * one, and ResolveNamespaceFromRequest (vault/namespace_store.go:1573-1598) re-routes a header
7
+ * that is literally `root/` to the root namespace. A namespace's own `namespace_path` in an API
8
+ * answer is that canonical form: `''` for root, `a/b/` below it (vault/login_mfa.go
9
+ * mfaConfigToMap and mfaLoginEnforcementConfigToMap).
10
+ */
11
+ import { type BaoEnvironment } from './bao-address.ts';
12
+ /** The bare name: no leading or trailing slash, and `''` for the root namespace. */
13
+ export declare const canonicalNamespace: (namespace: string | undefined) => string;
14
+ /** OpenBao's `namespace_path` rendering of the same namespace: `''` or `a/b/`. */
15
+ export declare const namespacePath: (namespace: string | undefined) => string;
16
+ /** The namespace every call under `env` targets, canonical. */
17
+ export declare const envNamespace: (env: BaoEnvironment) => string;
18
+ /** How a namespace reads in a message: `root` rather than an empty pair of backticks. */
19
+ export declare const namespaceLabel: (namespace: string) => string;
20
+ //# sourceMappingURL=bao-namespace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bao-namespace.d.ts","sourceRoot":"","sources":["../../src/openbao/bao-namespace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,kBAAkB,CAAC;AAGvE,oFAAoF;AACpF,eAAO,MAAM,kBAAkB,cAAe,MAAM,GAAG,SAAS,KAAG,MAGlE,CAAC;AAEF,kFAAkF;AAClF,eAAO,MAAM,aAAa,cAAe,MAAM,GAAG,SAAS,KAAG,MAG7D,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,YAAY,QAAS,cAAc,KAAG,MACA,CAAC;AAEpD,yFAAyF;AACzF,eAAO,MAAM,cAAc,cAAe,MAAM,KAAG,MACZ,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-role.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAE,0BAA0B,EAAiB,MAAM,mCAAmC,CAAC;AAE9F,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAM5B,MAAM,2BAA2B,CAAC;AAOnC,YAAY,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AAEH,eAAO,MAAM,yBAAyB,qIA+HnC,CAAC"}
1
+ {"version":3,"file":"cloudflare-role.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-role.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAE7C,OAAO,KAAK,KAAK,UAAU,MAAM,iCAAiC,CAAC;AAEnE,OAAO,EAAE,0BAA0B,EAAiB,MAAM,mCAAmC,CAAC;AAE9F,OAAO,EACL,KAAK,2BAA2B,EAChC,KAAK,sBAAsB,EAM5B,MAAM,2BAA2B,CAAC;AAcnC,YAAY,EAAE,2BAA2B,EAAE,sBAAsB,EAAE,CAAC;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,sBAAsB,EACtB,2BAA2B,EAC3B,KAAK,EACL,UAAU,CAAC,UAAU,CACtB;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAE5B,CAAC;AAEH,eAAO,MAAM,yBAAyB,qIA8InC,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"cloudflare-roles-expand.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-roles-expand.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAgB,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAE3F,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC;IAC1C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B;AAED,0DAA0D;AAC1D,eAAO,MAAM,IAAI,SAAU,MAAM,WAIL,CAAC;AAE7B;;;;GAIG;AACH,eAAO,MAAM,QAAQ,SAAU,cAAc,WACY,CAAC;AAE1D,qFAAqF;AACrF,eAAO,MAAM,SAAS,YAAa,MAAM,WAAW,MAAM,WAAuC,CAAC;AAkFlG,+FAA+F;AAC/F,eAAO,MAAM,aAAa,WAChB,WAAW,WACV,WAAW,SACb,SAAS,cAAc,EAAE,KAC/B,YAAY,EAwBd,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,WACZ,WAAW,kBACH,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC,CAAC,KAClE,WAAW,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAgB7C,CAAC"}
1
+ {"version":3,"file":"cloudflare-roles-expand.d.ts","sourceRoot":"","sources":["../../src/openbao/cloudflare-roles-expand.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAgB,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG3F,6FAA6F;AAC7F,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,+FAA+F;IAC/F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC;IAC1C;;;;;;;;;;OAUG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,uFAAuF;AACvF,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,CAAC;IAC7C,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;CAC/B;AAED,0DAA0D;AAC1D,eAAO,MAAM,IAAI,SAAU,MAAM,WAAkE,CAAC;AAEpG;;;;GAIG;AACH,eAAO,MAAM,QAAQ,SAAU,cAAc,WACY,CAAC;AAE1D,qFAAqF;AACrF,eAAO,MAAM,SAAS,YAAa,MAAM,WAAW,MAAM,WAAuC,CAAC;AAkFlG,+FAA+F;AAC/F,eAAO,MAAM,aAAa,WAChB,WAAW,WACV,WAAW,SACb,SAAS,cAAc,EAAE,KAC/B,YAAY,EAwBd,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,SAAS,WACZ,WAAW,kBACH,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,cAAc,EAAE,CAAC,CAAC,KAClE,WAAW,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,CAgB7C,CAAC"}
@@ -0,0 +1,30 @@
1
+ import * as Layer from 'effect/Layer';
2
+ import type { Reply, Seen } from './fake-bao.ts';
3
+ type Stored = Record<string, unknown>;
4
+ /** An answer for fake-bao that also exposes what it holds, keyed by API path (no `/v1/`). */
5
+ export type Store = ((seen: Seen) => Reply) & {
6
+ readonly live: Map<string, Stored>;
7
+ };
8
+ /**
9
+ * `sys/policies/acl/<name>`. It strips the policy's trailing newline on write (measured on the live
10
+ * server — digest.ts), and keys the name as OpenBao does: trimmed and lowercased (`policyKey`).
11
+ */
12
+ export declare const aclPolicies: () => Store;
13
+ /**
14
+ * `<mount>/roles/<name>` on the Cloudflare engine. A write parses the duration text into seconds and
15
+ * re-marshals `policies` (path_roles.go:117-146, :199-210). Names are stored verbatim.
16
+ */
17
+ export declare const cloudflareRoles: () => Store;
18
+ /**
19
+ * `<mount>/roles/<name>` on the proxmox engine. A write MERGES into the stored role, and TTLs go over
20
+ * as seconds (homeflare-openbao-plugins secrets/proxmox/path_roles.go, pathRolesWrite).
21
+ */
22
+ export declare const proxmoxRoles: () => Store;
23
+ /**
24
+ * Bao.Policy's provider over fragment directories that exist only here: `…/empty` holds no file,
25
+ * `…/two` holds two, and any other directory one. Each file is one grant named after its own path,
26
+ * so a directory's content is its name.
27
+ */
28
+ export declare const fakePolicyProviders: Layer.Layer<import("effect/unstable/http/HttpClient").HttpClient | import("alchemy").Provider<import("./policy.ts").BaoPolicy>, never, import("effect/unstable/http/HttpClient").HttpClient>;
29
+ export {};
30
+ //# sourceMappingURL=fake-engines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-engines.d.ts","sourceRoot":"","sources":["../../src/openbao/fake-engines.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AAGtC,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAMjD,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEtC,6FAA6F;AAC7F,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AA6BrF;;;GAGG;AACH,eAAO,MAAM,WAAW,QAAO,KAU5B,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,eAAe,QAAO,KAQ9B,CAAC;AAEN;;;GAGG;AACH,eAAO,MAAM,YAAY,QAAO,KAS3B,CAAC;AAEN;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,8LAiB/B,CAAC"}