@homeflare/alchemy 0.7.0 → 0.8.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 (137) hide show
  1. package/README.md +26 -37
  2. package/dist/caddy/config-lifecycle.d.ts +49 -3
  3. package/dist/caddy/config-lifecycle.d.ts.map +1 -1
  4. package/dist/caddy/config.d.ts +6 -2
  5. package/dist/caddy/config.d.ts.map +1 -1
  6. package/dist/caddy/fake-caddy.d.ts +11 -0
  7. package/dist/caddy/fake-caddy.d.ts.map +1 -1
  8. package/dist/caddy/index.d.ts +2 -1
  9. package/dist/caddy/index.d.ts.map +1 -1
  10. package/dist/caddy/index.js +71 -13
  11. package/dist/caddy/index.js.map +9 -9
  12. package/dist/caddy/with-file.d.ts +7 -3
  13. package/dist/caddy/with-file.d.ts.map +1 -1
  14. package/dist/launchd/fake-sudo.d.ts +50 -0
  15. package/dist/launchd/fake-sudo.d.ts.map +1 -0
  16. package/dist/launchd/host-effect.d.ts +18 -9
  17. package/dist/launchd/host-effect.d.ts.map +1 -1
  18. package/dist/launchd/host-file-form.d.ts +5 -0
  19. package/dist/launchd/host-file-form.d.ts.map +1 -1
  20. package/dist/launchd/host-file-lifecycle.d.ts.map +1 -1
  21. package/dist/launchd/index.d.ts +2 -0
  22. package/dist/launchd/index.d.ts.map +1 -1
  23. package/dist/launchd/index.js +342 -8
  24. package/dist/launchd/index.js.map +15 -11
  25. package/dist/launchd/job-preflight.d.ts.map +1 -1
  26. package/dist/launchd/job-validate.d.ts +2 -0
  27. package/dist/launchd/job-validate.d.ts.map +1 -1
  28. package/dist/launchd/local-runner.d.ts +2 -0
  29. package/dist/launchd/local-runner.d.ts.map +1 -1
  30. package/dist/launchd/providers.d.ts +2 -1
  31. package/dist/launchd/providers.d.ts.map +1 -1
  32. package/dist/launchd/runner.d.ts +7 -5
  33. package/dist/launchd/runner.d.ts.map +1 -1
  34. package/dist/launchd/sudo-allowlist.d.ts +47 -0
  35. package/dist/launchd/sudo-allowlist.d.ts.map +1 -0
  36. package/dist/launchd/sudo-guard.d.ts +56 -0
  37. package/dist/launchd/sudo-guard.d.ts.map +1 -0
  38. package/dist/launchd/sudo-runner.d.ts +60 -0
  39. package/dist/launchd/sudo-runner.d.ts.map +1 -0
  40. package/dist/launchd/sudo-stage.d.ts +7 -0
  41. package/dist/launchd/sudo-stage.d.ts.map +1 -0
  42. package/dist/openbao/auth-role-form.d.ts +0 -9
  43. package/dist/openbao/auth-role-form.d.ts.map +1 -1
  44. package/dist/openbao/auth-role.d.ts +3 -2
  45. package/dist/openbao/auth-role.d.ts.map +1 -1
  46. package/dist/openbao/cloudflare-role.d.ts.map +1 -1
  47. package/dist/openbao/fake-engines-roles.d.ts +32 -0
  48. package/dist/openbao/fake-engines-roles.d.ts.map +1 -0
  49. package/dist/openbao/fake-engines.d.ts +8 -2
  50. package/dist/openbao/fake-engines.d.ts.map +1 -1
  51. package/dist/openbao/fake-families.d.ts +49 -0
  52. package/dist/openbao/fake-families.d.ts.map +1 -0
  53. package/dist/openbao/host-approles.d.ts.map +1 -1
  54. package/dist/openbao/index.js +359 -280
  55. package/dist/openbao/index.js.map +21 -19
  56. package/dist/openbao/jwt-config.d.ts +5 -0
  57. package/dist/openbao/jwt-config.d.ts.map +1 -1
  58. package/dist/openbao/jwt-role.d.ts +2 -1
  59. package/dist/openbao/jwt-role.d.ts.map +1 -1
  60. package/dist/openbao/kubernetes-role.d.ts +2 -1
  61. package/dist/openbao/kubernetes-role.d.ts.map +1 -1
  62. package/dist/openbao/mfa-enforcement.d.ts +6 -3
  63. package/dist/openbao/mfa-enforcement.d.ts.map +1 -1
  64. package/dist/openbao/mfa-totp.d.ts.map +1 -1
  65. package/dist/openbao/pki-role.d.ts.map +1 -1
  66. package/dist/openbao/plugin.d.ts.map +1 -1
  67. package/dist/openbao/policy.d.ts.map +1 -1
  68. package/dist/openbao/proxmox-role.d.ts.map +1 -1
  69. package/dist/openbao/rename-identity.d.ts +69 -0
  70. package/dist/openbao/rename-identity.d.ts.map +1 -0
  71. package/dist/openbao/rename.d.ts +13 -12
  72. package/dist/openbao/rename.d.ts.map +1 -1
  73. package/dist/openbao/ssh-role-erase.d.ts +11 -0
  74. package/dist/openbao/ssh-role-erase.d.ts.map +1 -0
  75. package/dist/openbao/ssh-role.d.ts.map +1 -1
  76. package/docs/caddy-admin.md +24 -0
  77. package/docs/caddy.md +73 -54
  78. package/docs/launchd-sudo.md +122 -0
  79. package/docs/launchd.md +7 -4
  80. package/docs/peers.md +39 -0
  81. package/package.json +1 -1
  82. package/src/caddy/adopt-scope.test.ts +82 -0
  83. package/src/caddy/config-adopt.test.ts +235 -0
  84. package/src/caddy/config-lifecycle.test.ts +31 -53
  85. package/src/caddy/config-lifecycle.ts +98 -7
  86. package/src/caddy/config.ts +48 -11
  87. package/src/caddy/fake-caddy.ts +19 -0
  88. package/src/caddy/index.ts +2 -1
  89. package/src/caddy/providers.test.ts +8 -23
  90. package/src/caddy/with-file.ts +7 -3
  91. package/src/launchd/fake-sudo.ts +136 -0
  92. package/src/launchd/host-effect.ts +33 -1
  93. package/src/launchd/host-file-form.ts +10 -2
  94. package/src/launchd/host-file-lifecycle.ts +2 -1
  95. package/src/launchd/index.ts +2 -0
  96. package/src/launchd/job-preflight.ts +3 -2
  97. package/src/launchd/job-validate.ts +1 -1
  98. package/src/launchd/local-runner.ts +6 -3
  99. package/src/launchd/providers.test.ts +3 -0
  100. package/src/launchd/providers.ts +2 -1
  101. package/src/launchd/runner.ts +7 -5
  102. package/src/launchd/sudo-allowlist.test.ts +191 -0
  103. package/src/launchd/sudo-allowlist.ts +213 -0
  104. package/src/launchd/sudo-guard.test.ts +100 -0
  105. package/src/launchd/sudo-guard.ts +151 -0
  106. package/src/launchd/sudo-lifecycle.test.ts +172 -0
  107. package/src/launchd/sudo-runner-files.test.ts +240 -0
  108. package/src/launchd/sudo-runner.test.ts +126 -0
  109. package/src/launchd/sudo-runner.ts +235 -0
  110. package/src/launchd/sudo-stage.ts +28 -0
  111. package/src/openbao/README.md +8 -2
  112. package/src/openbao/REPLACE.md +65 -37
  113. package/src/openbao/auth-role-form.test.ts +2 -16
  114. package/src/openbao/auth-role-form.ts +0 -11
  115. package/src/openbao/auth-role.ts +35 -14
  116. package/src/openbao/cloudflare-role.ts +6 -14
  117. package/src/openbao/fake-engines-roles.ts +114 -0
  118. package/src/openbao/fake-engines.ts +18 -11
  119. package/src/openbao/fake-families.ts +182 -0
  120. package/src/openbao/host-approles.test.ts +15 -2
  121. package/src/openbao/host-approles.ts +19 -4
  122. package/src/openbao/jwt-config.ts +26 -5
  123. package/src/openbao/jwt-role.ts +24 -8
  124. package/src/openbao/kubernetes-role.ts +20 -8
  125. package/src/openbao/mfa-enforcement.ts +23 -8
  126. package/src/openbao/mfa-totp.ts +21 -8
  127. package/src/openbao/pki-role.ts +19 -7
  128. package/src/openbao/plugin.ts +41 -15
  129. package/src/openbao/policy.ts +7 -7
  130. package/src/openbao/proxmox-role.ts +7 -16
  131. package/src/openbao/rename-adoption.test.ts +82 -0
  132. package/src/openbao/rename-families.test.ts +122 -0
  133. package/src/openbao/rename-identity.test.ts +129 -0
  134. package/src/openbao/rename-identity.ts +117 -0
  135. package/src/openbao/rename.ts +19 -15
  136. package/src/openbao/ssh-role-erase.ts +50 -0
  137. package/src/openbao/ssh-role.ts +16 -54
@@ -7,22 +7,22 @@
7
7
  "/**\n * An XML property list serializer — the subset launchd.plist(5) reads, and nothing else.\n *\n * ★ WHY NOT AN npm LIBRARY. Nothing in this workspace depends on one (checked 2026-09-21: no\n * `plist`, `xmlbuilder` or `@xmldom/xmldom` anywhere under node_modules), and the common one pulls\n * an XML DOM in for PARSING, which a provider that only RENDERS never does. The subset a launchd\n * job needs is five node types, so this is ~100 lines plus exhaustive tests (plist.test.ts), which\n * also round-trip every case through Apple's own `plutil` when the tests run on macOS.\n *\n * ⛔ THE OUTPUT MUST BE DETERMINISTIC. LaunchdJob diffs by the SHA-256 of these bytes, so two\n * renders of one declaration must be byte-identical: dict keys are sorted, indentation is fixed,\n * and nothing depends on insertion order. A non-deterministic render is a forever-`update`.\n * ⚠️ AND A CHANGE TO THIS FILE'S OUTPUT RESTARTS EVERY JOB. A new header, indent or escape changes\n * every digest, so the next deploy after the kit upgrade updates — restarts — every declared\n * job at once. Treat any byte change here as a breaking change and say so in its changeset.\n *\n * ⛔ REFUSE, NEVER COERCE. `undefined`, `null`, a fraction, NaN, a class instance or a character XML\n * 1.0 cannot carry is a thrown PlistError naming the path, not a silently dropped key. A dropped\n * `KeepAlive` is a daemon that never restarts, and nothing downstream would say why.\n */\n\n/** Every value launchd reads. ⚠️ No `<real>`, `<date>` or `<data>`: no launchd key needs one. */\nexport type PlistValue =\n | string\n | number\n | boolean\n | readonly PlistValue[]\n | { readonly [key: string]: PlistValue };\n\nexport type PlistDict = { readonly [key: string]: PlistValue };\n\nexport class PlistError extends Error {\n constructor(\n readonly path: string,\n detail: string,\n ) {\n super(`plist ${path === '' ? '(root)' : path}: ${detail}`);\n this.name = 'PlistError';\n }\n}\n\nconst HEADER =\n '<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n' +\n '<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" ' +\n '\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\\n' +\n '<plist version=\"1.0\">\\n';\n\n/** ⚠️ A cycle, or a runaway generator, would otherwise recurse until the stack dies. */\nconst MAX_DEPTH = 32;\n\n/**\n * ⛔ CHARACTERS XML 1.0 CANNOT CARRY AT ALL — not even escaped (XML 1.0 §2.2 `Char`). C0 controls\n * other than tab, LF and CR; U+FFFE / U+FFFF; and an unpaired surrogate, which has no UTF-8 form.\n */\n// ★ The `u` flag makes a VALID surrogate pair one code point above U+FFFF, outside this class, so\n// only an unpaired half matches the \\uD800-\\uDFFF range.\n// oxlint-disable-next-line no-control-regex -- matching control characters is the point\nconst FORBIDDEN = /[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F\\uD800-\\uDFFF\\uFFFE\\uFFFF]/u;\n\n/**\n * Escape text content.\n * ⚠️ CR IS A CHARACTER REFERENCE, NOT A LITERAL. An XML parser normalises a literal CR (and CRLF)\n * to LF before the application sees it (XML 1.0 §2.11), so `a\\r\\nb` would come back as `a\\nb` and\n * the argument launchd passes would differ from the one declared. `&#13;` survives normalisation.\n */\nexport const escapeText = (text: string, path: string): string => {\n const bad = FORBIDDEN.exec(text);\n if (bad !== null) {\n const code = bad[0].codePointAt(0) ?? 0;\n throw new PlistError(\n path,\n `U+${code.toString(16).toUpperCase().padStart(4, '0')} cannot appear in XML 1.0`,\n );\n }\n return text\n .replaceAll('&', '&amp;')\n .replaceAll('<', '&lt;')\n .replaceAll('>', '&gt;')\n .replaceAll('\\r', '&#13;');\n};\n\nconst isPlainObject = (value: object): boolean => {\n const proto = Object.getPrototypeOf(value) as unknown;\n return proto === Object.prototype || proto === null;\n};\n\nconst renderValue = (value: unknown, path: string, depth: number, out: string[]): void => {\n const pad = '\\t'.repeat(depth);\n if (depth > MAX_DEPTH) throw new PlistError(path, `nested deeper than ${String(MAX_DEPTH)}`);\n if (typeof value === 'string') {\n out.push(`${pad}<string>${escapeText(value, path)}</string>`);\n return;\n }\n if (typeof value === 'boolean') {\n out.push(`${pad}<${value ? 'true' : 'false'}/>`);\n return;\n }\n if (typeof value === 'number') {\n // ⛔ launchd reads every numeric key as an integer; 1.5 would be a silently different job.\n if (!Number.isSafeInteger(value)) {\n throw new PlistError(path, `${String(value)} is not a safe integer (no <real> support)`);\n }\n out.push(`${pad}<integer>${String(value === 0 ? 0 : value)}</integer>`);\n return;\n }\n if (Array.isArray(value)) {\n if (value.length === 0) {\n out.push(`${pad}<array/>`);\n return;\n }\n out.push(`${pad}<array>`);\n for (const [index, item] of (value as readonly unknown[]).entries()) {\n renderValue(item, `${path}[${String(index)}]`, depth + 1, out);\n }\n out.push(`${pad}</array>`);\n return;\n }\n if (typeof value === 'object' && value !== null && isPlainObject(value)) {\n renderDict(value as Record<string, unknown>, path, depth, out);\n return;\n }\n const kind = value === null ? 'null' : typeof value;\n throw new PlistError(path, `${kind} has no plist form — omit the key instead`);\n};\n\nconst renderDict = (\n dict: Record<string, unknown>,\n path: string,\n depth: number,\n out: string[],\n): void => {\n const pad = '\\t'.repeat(depth);\n // ★ Code-unit order, not localeCompare: the same bytes on every machine and every locale.\n const keys = Object.keys(dict).sort();\n if (keys.length === 0) {\n out.push(`${pad}<dict/>`);\n return;\n }\n out.push(`${pad}<dict>`);\n for (const key of keys) {\n const child = path === '' ? key : `${path}.${key}`;\n out.push(`${pad}\\t<key>${escapeText(key, child)}</key>`);\n renderValue(dict[key], child, depth + 1, out);\n }\n out.push(`${pad}</dict>`);\n};\n\n/** Render a top-level dict as a complete XML plist document, ending in a newline. */\nexport const renderPlist = (dict: PlistDict): string => {\n if (typeof dict !== 'object' || dict === null || Array.isArray(dict) || !isPlainObject(dict)) {\n throw new PlistError('', 'the top level of a launchd plist must be a dict');\n }\n const out: string[] = [];\n renderDict(dict as Record<string, unknown>, '', 0, out);\n return `${HEADER}${out.join('\\n')}\\n</plist>\\n`;\n};\n",
8
8
  "/**\n * The one form an adapted Caddy config is hashed in — so what `POST /adapt` returns for the\n * declared Caddyfile and what `GET /config/` reports after the load hash the same.\n *\n * ⛔ THE TWO ARE NOT THE SAME BYTES, AND COMPARING BYTES WOULD BE A FOREVER-`update`. Read in\n * caddyserver/caddy v2.11.4:\n * · `/adapt` returns the adapter's `json.Marshal(cfg)` of Go STRUCTS — keys in field order\n * (caddyconfig/caddyfile/adapter.go Adapt; load.go handleAdapt).\n * · `/load` decodes that JSON into a generic `any` and re-marshals it (caddy.go changeConfig):\n * Go sorts MAP keys, so the stored config has every object's keys sorted.\n * · `GET /config/` streams that stored value through `json.Encoder`, which appends a newline\n * (admin.go unsyncedConfigAccess), and escapes `<`, `>` and `&` as `\\u003c`, `\\u003e`, `\\u0026`.\n * Parsing both and re-serialising with sorted keys erases all three differences. MEASURED\n * 2026-09-21 on a throwaway Caddy 2.11.4: the digest of `GET /config/` after `POST /load` equals\n * the digest of `/adapt` for the same Caddyfile — and a Caddy started with `--config <file>`\n * hashes the same as `/adapt` of that file's text, so adopting it is a noop.\n * ★ NUMBERS AGREE BY CONSTRUCTION. Go decodes every JSON number in that generic `any` as float64\n * (caddy.go indexConfigObjects says so), and JSON.parse does too — the same double on both sides\n * serialises the same way here.\n */\nimport { sha256Hex } from '../launchd/job-form.ts';\n\ntype Json = null | boolean | number | string | readonly Json[] | { readonly [key: string]: Json };\n\nconst sorted = (value: Json): Json => {\n if (Array.isArray(value)) return (value as readonly Json[]).map(sorted);\n if (typeof value === 'object' && value !== null) {\n const object = value as { readonly [key: string]: Json };\n const out: Record<string, Json> = {};\n for (const key of Object.keys(object).sort()) out[key] = sorted(object[key] ?? null);\n return out;\n }\n return value;\n};\n\n/** Parse a config body Caddy returned. ⚠️ An empty config is the JSON `null`, not an error. */\nexport const parseConfig = (text: string): unknown => JSON.parse(text) as unknown;\n\n/** The canonical text of a parsed config: sorted keys, no whitespace. */\nexport const canonicalConfig = (config: unknown): string =>\n JSON.stringify(sorted((config ?? null) as Json));\n\n/** SHA-256 of the canonical form — what the resource stores and compares, never the config. */\nexport const configDigest = (config: unknown): string => sha256Hex(canonicalConfig(config));\n",
9
9
  "/**\n * The three admin API calls the provider makes, each classified into a value or a readable Error\n * that carries Caddy's own message.\n *\n * Read in caddyserver/caddy v2.11.4 (and the API docs, caddyserver.com/docs/api):\n * · `POST /adapt` — adapts without running (load.go handleAdapt): 200 `{warnings?, result}`,\n * or 400 `{\"error\": …}`. Side-effect free, so it is what plan-time validation uses.\n * · `POST /load` — Content-Type `text/caddyfile` picks the adapter (load.go adaptByContentType);\n * \"if the new config fails for any reason, the old config is rolled back into place without\n * downtime\" (docs; caddy.go changeConfig restores the previous raw config). An identical\n * config is a no-op without `Cache-Control: must-revalidate`.\n * · `GET /config/` — the running config as JSON, `null` when empty (admin.go handleConfig).\n * ⛔ A FAILED LOAD CAN ANSWER 200. load.go writes the adapter's WARNINGS to the body before it\n * runs the config; if the run then fails, the error JSON is appended to a response whose status\n * is already 200 (Go's first Write sends 200; the later WriteHeader(400) is ignored). The\n * Caddyfile adapter warns on every unformatted input (adapter.go FormattingDifference), so this\n * is the COMMON failure shape, not an edge. Hence: look for the error in the body whatever the\n * status — and the lifecycle reads the config back after every load anyway.\n * MEASURED 2026-09-21 on a throwaway Caddy 2.11.4 (loopback ports, isolated XDG dirs): an\n * unformatted Caddyfile whose port was taken answered `200` with body\n * `[{\"file\":\"Caddyfile\",\"line\":8,\"message\":\"Caddyfile input is not formatted; …\"}]{\"error\":\"loading\n * config: … bind: address already in use\"}`, and Caddy kept serving the previous config.\n */\nimport type { CaddyAdmin, CaddyAdminResponse } from './admin.ts';\nimport { parseConfig } from './digest.ts';\n\nexport class CaddyAdminError extends Error {\n constructor(\n readonly status: number,\n readonly method: string,\n readonly path: string,\n reason: string,\n ) {\n super(`Caddy ${method} ${path} → ${String(status)}: ${reason}`);\n this.name = 'CaddyAdminError';\n }\n}\n\nconst CADDYFILE = 'text/caddyfile';\n/** APIError marshals as exactly this key (admin.go APIError, `json:\"error\"`). */\nconst ERROR_OPEN = '{\"error\":';\n\ntype Warning = { file?: string; line?: number; message?: string };\n\nconst describe = (warning: Warning): string =>\n `${warning.file ?? 'Caddyfile'}:${String(warning.line ?? 0)}: ${warning.message ?? '(no message)'}`;\n\n/** Caddy's `error` string from a body that holds one, or `undefined`. */\nconst errorIn = (body: string): string | undefined => {\n const at = body.lastIndexOf(ERROR_OPEN);\n if (at === -1) return undefined;\n try {\n const parsed = JSON.parse(body.slice(at)) as { error?: unknown };\n return typeof parsed.error === 'string' ? parsed.error : undefined;\n } catch {\n return undefined;\n }\n};\n\nconst warningsIn = (text: string): string[] => {\n if (text.trim() === '') return [];\n try {\n const parsed = JSON.parse(text) as unknown;\n return Array.isArray(parsed) ? (parsed as Warning[]).map(describe) : [];\n } catch {\n return [];\n }\n};\n\nconst fail = (response: CaddyAdminResponse, method: string, path: string): never => {\n const reason = errorIn(response.body) ?? (response.body.trim().slice(0, 500) || '(empty body)');\n // ⚠️ Say so when the status lied (the ⛔ above), or \"→ 200\" reads like success.\n const shown = response.status === 200 ? `${reason} (error in a 200 body)` : reason;\n throw new CaddyAdminError(response.status, method, path, shown);\n};\n\nexport type Adapted = { readonly config: unknown; readonly warnings: readonly string[] };\n\n/** Adapt a Caddyfile to JSON on the running Caddy, without loading it. */\nexport const adaptCaddyfile = async (admin: CaddyAdmin, caddyfile: string): Promise<Adapted> => {\n const response = await admin.request({\n body: caddyfile,\n headers: { 'Content-Type': CADDYFILE },\n method: 'POST',\n path: '/adapt',\n });\n // ★ Status alone here: /adapt writes its body once, and a successful result is config JSON in\n // which an `error` handler's key could look like an error object to a substring search.\n if (response.status !== 200) fail(response, 'POST', '/adapt');\n const parsed = JSON.parse(response.body) as { result?: unknown; warnings?: Warning[] };\n return { config: parsed.result ?? null, warnings: (parsed.warnings ?? []).map(describe) };\n};\n\n/** The running config, parsed; `null` when Caddy runs with none. */\nexport const readRunningConfig = async (admin: CaddyAdmin): Promise<unknown> => {\n const response = await admin.request({ method: 'GET', path: '/config/' });\n if (response.status !== 200) fail(response, 'GET', '/config/');\n return parseConfig(response.body);\n};\n\n/**\n * Apply a Caddyfile with `POST /load`; resolves with the adapter's warnings.\n *\n * ★ `sourceFile` RIDES AS `Caddy-Config-Source-File` (+ `-Adapter: caddyfile`), the headers\n * `caddy reload` sends (cmd/commandfuncs.go). A load WITHOUT them makes Caddy forget the file it\n * was started with (caddy.go ClearLastConfigIfDifferent), and SIGUSR1 then no longer reloads\n * from that file. With them, and the same path, it keeps it.\n */\nexport const loadCaddyfile = async (\n admin: CaddyAdmin,\n caddyfile: string,\n sourceFile?: string,\n): Promise<readonly string[]> => {\n const response = await admin.request({\n body: caddyfile,\n headers: {\n 'Content-Type': CADDYFILE,\n ...(sourceFile === undefined\n ? {}\n : { 'Caddy-Config-Source-Adapter': 'caddyfile', 'Caddy-Config-Source-File': sourceFile }),\n },\n method: 'POST',\n path: '/load',\n });\n const refused = errorIn(response.body);\n if (response.status !== 200 || refused !== undefined) fail(response, 'POST', '/load');\n return warningsIn(response.body);\n};\n",
10
- "/**\n * `Caddy.Config` — a running Caddy's whole config, declared as Caddyfile text and applied through\n * Caddy's own admin API (decision 23).\n *\n * - create / update — `POST /adapt` (validate), `POST /load` with `text/caddyfile` (a graceful\n * reload; Caddy keeps the old config if it refuses the new one, and the error names why), then\n * `GET /config/` and insist it matches. Skipped when Caddy already runs this config.\n * - read — the digest of `GET /config/`. ★ WITH NO STATE, A RUNNING CADDY IS ADOPTED: its live\n * config becomes the starting point, and the deploy's forced update loads the declared one.\n * - diff — declared, live and stored digests (config-lifecycle.ts). Never `replace`.\n * - delete — ⛔ NOTHING. See below.\n *\n * ⛔ DELETE NEVER TOUCHES CADDY, UNDER EITHER REMOVAL POLICY. Removing this declaration must not take\n * every site down: there is no \"less config\" to fall back to, and an empty config stops all\n * servers. `retain` is the default (the engine never calls delete); with `destroy` the handler\n * still only forgets. It never calls `/stop` (which exits the process) or `DELETE /config/`.\n * To empty a Caddy, do it by hand at its admin API.\n * ⛔ `caddyfile` IS NEVER A SECRET — see config-form.ts; refused before anything is sent.\n * ⚠️ WHICH CADDY is the transport's (providers.ts), not a prop: one CaddyAdmin per stack.\n */\nimport { Resource } from 'alchemy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { lift, resolvedString } from '../launchd/host-effect.ts';\nimport { CaddyAdminService, CaddyUnreachableError } from './admin.ts';\nimport type { CaddyConfigAttributes, CaddyConfigProps } from './config-form.ts';\nimport { diffConfig, readLive, reconcileConfig } from './config-lifecycle.ts';\n\nexport type { CaddyConfigAttributes, CaddyConfigProps } from './config-form.ts';\n\nexport interface CaddyConfig extends Resource<\n 'Caddy.Config',\n CaddyConfigProps,\n CaddyConfigAttributes\n> {}\n\nexport const CaddyConfig = Resource<CaddyConfig>('Caddy.Config', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst unreachable = (error: Error): error is CaddyUnreachableError =>\n error instanceof CaddyUnreachableError;\n\n/**\n * ⚠️ A STOPPED CADDY MUST NOT FAIL THE PLAN THAT BRINGS IT BACK. Its launchd job usually sits in the\n * same stack; if every plan died on `ECONNREFUSED`, the fix to that job could never be deployed.\n * So at PLAN time only (read, diff) an unreachable Caddy plans an update — with a warning, because\n * the plan-time `/adapt` check did not run — and reconcile, which needs Caddy, still fails loudly.\n */\nconst planWithoutCaddy =\n <A>(admin: { endpoint: string }, fallback: A) =>\n (error: Error) =>\n Effect.as(\n Effect.logWarning(\n `Caddy.Config: no Caddy at ${admin.endpoint} (${error.message}); planning without it — ` +\n 'the Caddyfile is validated when the deploy reaches Caddy',\n ),\n fallback,\n );\n\nexport const CaddyConfigProvider = () =>\n Provider.effect(\n CaddyConfig,\n Effect.gen(function* () {\n const admin = yield* CaddyAdminService;\n return CaddyConfig.Provider.of({\n /** ⛔ A Caddy has one config; there is nothing to enumerate, and nothing for nuke to delete. */\n list: () => Effect.succeed([]),\n\n /**\n * ★ Plain attributes, never `Unowned`: a Caddy on this host's loopback admin API is the one\n * the stack means (the transport refuses anything else), and adopting reads — it changes\n * nothing until reconcile loads the declared Caddyfile.\n */\n read: ({ olds }) =>\n lift(() => readLive(admin, resolvedString(olds, 'sourceFile'))).pipe(\n // ★ Nothing to adopt from a Caddy that is down: plan a create, which is the same load.\n Effect.catchIf(unreachable, planWithoutCaddy(admin, undefined)),\n ),\n\n diff: ({ news, output }) => {\n if (output === undefined) return Effect.succeed(undefined);\n const update = planWithoutCaddy(admin, { action: 'update' as const });\n if (isResolved(news)) {\n return lift(() => diffConfig(admin, news, output)).pipe(\n Effect.catchIf(unreachable, update),\n );\n }\n /**\n * ⚠️ `sourceFile` is an Output while its HostFile changes in the same deploy\n * (caddyWithFile). The Caddyfile itself is usually plain text, so validate and compare\n * it now — a bad Caddyfile must fail the PLAN, before the HostFile writes it to disk —\n * and plan an update, which reloads only if the config really differs.\n */\n const caddyfile = resolvedString(news, 'caddyfile');\n if (caddyfile === undefined) return Effect.succeed(undefined);\n return lift(async () => {\n await diffConfig(admin, { caddyfile }, output);\n return { action: 'update' as const };\n }).pipe(Effect.catchIf(unreachable, update));\n },\n\n reconcile: ({ news }) =>\n Effect.gen(function* () {\n const applied = yield* lift(() => reconcileConfig(admin, news));\n for (const warning of applied.warnings) {\n yield* Effect.logWarning(`Caddy.Config at ${admin.endpoint}: ${warning}`);\n }\n return applied.attributes;\n }),\n\n delete: () => Effect.void,\n });\n }),\n );\n",
11
- "/**\n * The one place a HostRunner's promises become Effects — plus the one probe of an unresolved\n * `news` both providers' diffs share.\n *\n * ★ The lifecycle files are plain async functions over a HostRunner (see runner.ts for why the\n * runner is promise-based), so each provider handler is a single `lift(() => step(runner, …))`.\n * ⚠️ A thrown Error stays that Error — its message is the refusal a deploy prints. Anything else\n * thrown is wrapped, so the failure channel is always an Error with a readable message.\n */\nimport * as Effect from 'effect/Effect';\n\nexport const lift = <A>(step: () => Promise<A>): Effect.Effect<A, Error> =>\n Effect.tryPromise({\n catch: (cause) => (cause instanceof Error ? cause : new Error(String(cause))),\n try: step,\n });\n\n/**\n * A string prop of a diff's `news`, when that one prop is already resolved; `undefined` while it\n * is still an Output (a function-typed proxy), an Effect or a Config.\n * ★ For the identity props a diff must see even when the rest of `news` is unresolved — a plain\n * string is resolved by definition, so `typeof` is the whole test.\n */\nexport const resolvedString = (news: unknown, key: string): string | undefined => {\n if (typeof news !== 'object' || news === null) return undefined;\n const value = (news as Record<string, unknown>)[key];\n return typeof value === 'string' ? value : undefined;\n};\n",
10
+ "/**\n * `Caddy.Config` — a running Caddy's whole config, declared as Caddyfile text and applied through\n * Caddy's own admin API (decision 23).\n *\n * - create / update — `POST /adapt` (validate), `POST /load` with `text/caddyfile` (a graceful\n * reload; Caddy keeps the old config if it refuses the new one, and the error names why), then\n * `GET /config/` and insist it matches. Skipped when Caddy already runs this config.\n * - read — the digest of `GET /config/`. With no state it is the adoption probe (below).\n * - diff — declared, live and stored digests (config-lifecycle.ts). Never `replace`.\n * - delete — ⛔ NOTHING. See below.\n *\n * ⛔ DELETE NEVER TOUCHES CADDY, UNDER EITHER REMOVAL POLICY. Removing this declaration must not take\n * every site down: there is no \"less config\" to fall back to, and an empty config stops all\n * servers. `retain` is the default (the engine never calls delete); with `destroy` the handler\n * still only forgets. It never calls `/stop` (which exits the process) or `DELETE /config/`.\n * To empty a Caddy, do it by hand at its admin API.\n * ⛔ NOTHING IS ADOPTED SILENTLY (decision, 2026-09-21 — the house rule HostFile and LaunchdJob\n * keep). With no state, a running Caddy whose live config IS the declared one reads as ours (the\n * adoption changes nothing); any other config reads as `Unowned`, and the plan refuses it unless\n * the deploy runs with `--adopt`. A Caddy serving nothing reads as absent: a create. Where the\n * engine skips that probe, reconcile refuses the same takeover (config-lifecycle.ts).\n * ⛔ `caddyfile` IS NEVER A SECRET — see config-form.ts; refused before anything is sent.\n * ⚠️ WHICH CADDY is the transport's (providers.ts), not a prop: one CaddyAdmin per stack.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { adoptEnabled, lift, resolvedString } from '../launchd/host-effect.ts';\nimport { CaddyAdminService, CaddyUnreachableError } from './admin.ts';\nimport type { CaddyConfigAttributes, CaddyConfigProps } from './config-form.ts';\nimport { diffConfig, probeLive, readLive, reconcileConfig } from './config-lifecycle.ts';\n\nexport type { CaddyConfigAttributes, CaddyConfigProps } from './config-form.ts';\n\nexport interface CaddyConfig extends Resource<\n 'Caddy.Config',\n CaddyConfigProps,\n CaddyConfigAttributes\n> {}\n\nexport const CaddyConfig = Resource<CaddyConfig>('Caddy.Config', {\n defaultRemovalPolicy: 'retain',\n});\n\nconst unreachable = (error: Error): error is CaddyUnreachableError =>\n error instanceof CaddyUnreachableError;\n\n/**\n * ⚠️ A STOPPED CADDY MUST NOT FAIL THE PLAN THAT BRINGS IT BACK. Its launchd job usually sits in the\n * same stack; if every plan died on `ECONNREFUSED`, the fix to that job could never be deployed.\n * So at PLAN time only (read, diff) an unreachable Caddy plans an update — with a warning, because\n * the plan-time `/adapt` check did not run — and reconcile, which needs Caddy, still fails loudly.\n */\nconst planWithoutCaddy =\n <A>(admin: { endpoint: string }, fallback: A) =>\n (error: Error) =>\n Effect.as(\n Effect.logWarning(\n `Caddy.Config: no Caddy at ${admin.endpoint} (${error.message}); planning without it — ` +\n 'the Caddyfile is validated when the deploy reaches Caddy',\n ),\n fallback,\n );\n\nexport const CaddyConfigProvider = () =>\n Provider.effect(\n CaddyConfig,\n Effect.gen(function* () {\n const admin = yield* CaddyAdminService;\n /** The probe, branded for the engine: plain when ours, `Unowned` otherwise. */\n const probe = (caddyfile: string | undefined, sourceFile: string | undefined) =>\n Effect.flatMap(\n lift(() => probeLive(admin, caddyfile, sourceFile)),\n (found) => {\n if (found === undefined) return Effect.succeed(undefined);\n if (found.ours) return Effect.succeed(found.attributes);\n const unowned = Unowned(found.attributes);\n return found.unchecked === undefined\n ? Effect.succeed(unowned)\n : Effect.as(\n Effect.logWarning(\n `Caddy.Config at ${admin.endpoint}: the running config could not be compared ` +\n `with the declared Caddyfile (${found.unchecked}), so it is not treated as ours`,\n ),\n unowned,\n );\n },\n );\n return CaddyConfig.Provider.of({\n /** ⛔ A Caddy has one config; there is nothing to enumerate, and nothing for nuke to delete. */\n list: () => Effect.succeed([]),\n\n /**\n * With state: the live attributes, which diff compares. With none, the ADOPTION PROBE —\n * `olds` is the declaration (Plan.ts passes `news`), and only a live config equal to it is\n * ours. ⚠️ A running Caddy is still the one the stack means (the transport reaches only\n * loopback), but WHOSE CONFIG it runs is not: a person, another tool or another stack.\n */\n read: ({ olds, output }) =>\n (output === undefined\n ? probe(resolvedString(olds, 'caddyfile'), resolvedString(olds, 'sourceFile'))\n : lift(() => readLive(admin, resolvedString(olds, 'sourceFile')))\n ).pipe(\n // ★ Nothing to adopt from a Caddy that is down: plan a create, which is the same load.\n Effect.catchIf(unreachable, planWithoutCaddy(admin, undefined)),\n ),\n\n diff: ({ news, output }) => {\n if (output === undefined) return Effect.succeed(undefined);\n const update = planWithoutCaddy(admin, { action: 'update' as const });\n if (isResolved(news)) {\n return lift(() => diffConfig(admin, news, output)).pipe(\n Effect.catchIf(unreachable, update),\n );\n }\n /**\n * ⚠️ `sourceFile` is an Output while its HostFile changes in the same deploy\n * (caddyWithFile). The Caddyfile itself is usually plain text, so validate and compare\n * it now — a bad Caddyfile must fail the PLAN, before the HostFile writes it to disk —\n * and plan an update, which reloads only if the config really differs.\n */\n const caddyfile = resolvedString(news, 'caddyfile');\n if (caddyfile === undefined) return Effect.succeed(undefined);\n return lift(async () => {\n await diffConfig(admin, { caddyfile }, output);\n return { action: 'update' as const };\n }).pipe(Effect.catchIf(unreachable, update));\n },\n\n reconcile: ({ fqn, news, output }) =>\n Effect.gen(function* () {\n // ⛔ State is authority over the Caddy it was applied to, and no other. With none (the\n // engine may not have probed: see reconcileConfig), or with the transport now at\n // another endpoint, only adoption — `--adopt`, or this resource's own `adopt(…)`,\n // as the planner resolves it — loads over a config the stack cannot claim.\n const sameCaddy = output !== undefined && output.endpoint === admin.endpoint;\n const takeOver = sameCaddy || (yield* adoptEnabled(fqn));\n const stored = output === undefined ? {} : { stored: output.configSha256 };\n const applied = yield* lift(() =>\n reconcileConfig(admin, news, { takeOver, ...stored }),\n );\n for (const warning of applied.warnings) {\n yield* Effect.logWarning(`Caddy.Config at ${admin.endpoint}: ${warning}`);\n }\n return applied.attributes;\n }),\n\n delete: () => Effect.void,\n });\n }),\n );\n",
11
+ "/**\n * The one place a HostRunner's promises become Effects — plus the one probe of an unresolved\n * `news` both providers' diffs share, and the engine's adopt setting for a check at apply.\n *\n * ★ The lifecycle files are plain async functions over a HostRunner (see runner.ts for why the\n * runner is promise-based), so each provider handler is a single `lift(() => step(runner, …))`.\n * ⚠️ A thrown Error stays that Error — its message is the refusal a deploy prints. Anything else\n * thrown is wrapped, so the failure channel is always an Error with a readable message.\n */\nimport { AdoptPolicy } from 'alchemy/AdoptPolicy';\nimport { AlchemyContext } from 'alchemy/AlchemyContext';\nimport { Stack } from 'alchemy/Stack';\nimport * as Effect from 'effect/Effect';\nimport * as Option from 'effect/Option';\n\nexport const lift = <A>(step: () => Promise<A>): Effect.Effect<A, Error> =>\n Effect.tryPromise({\n catch: (cause) => (cause instanceof Error ? cause : new Error(String(cause))),\n try: step,\n });\n\n/**\n * A string prop of a diff's `news`, when that one prop is already resolved; `undefined` while it\n * is still an Output (a function-typed proxy), an Effect or a Config.\n * ★ For the identity props a diff must see even when the rest of `news` is unresolved — a plain\n * string is resolved by definition, so `typeof` is the whole test.\n */\nexport const resolvedString = (news: unknown, key: string): string | undefined => {\n if (typeof news !== 'object' || news === null) return undefined;\n const value = (news as Record<string, unknown>)[key];\n return typeof value === 'string' ? value : undefined;\n};\n\n/**\n * Whether the resource at `fqn` may be adopted, resolved exactly as the planner resolves it (alchemy\n * beta.79 Plan.ts: `resource.Adopt ?? shouldAdopt`): a resource-scoped `adopt(…)`, else the\n * `AdoptPolicy` service, else `AlchemyContext.adopt`, else off.\n * ★ FOR A CREATE THE ENGINE NEVER PROBED. Alchemy skips the adoption probe while `news` holds an\n * Output, so an ownership check that must still honour `--adopt` can only run at apply. All three\n * reach the apply: Alchemist runs plan AND apply under the session context, which provides the\n * services from the CLI flag and the Stack the resources registered on (Alchemist/Session.ts,\n * routes/stack.ts; Apply.ts reads the same Stack).\n * ⛔ THE RESOURCE-SCOPED SETTING WINS, BOTH WAYS. The engine captures `adopt(…)` on the resource at\n * registration (Resource.ts `Adopt`, into `Stack.resources[fqn]`) and never hands it to a\n * provider. Reading only the deploy-wide flag let `--adopt` take over a resource declared\n * `.pipe(adopt(false))` — which the planner refuses — and refused one declared `adopt(true)`.\n * ⚠️ `resources` carries an `@internal` comment in Alchemy's source, but it is in the published types\n * and the planner reads `Adopt` from it. caddy/adopt-scope.test.ts registers through the real\n * `Resource`, so an upgrade that stops recording it there fails the suite.\n */\nexport const adoptEnabled = (fqn: string): Effect.Effect<boolean> =>\n Effect.gen(function* () {\n const stack = yield* Effect.serviceOption(Stack);\n const scoped = Option.isSome(stack) ? stack.value.resources[fqn]?.Adopt : undefined;\n if (scoped !== undefined) return scoped;\n const policy = yield* Effect.serviceOption(AdoptPolicy);\n if (Option.isSome(policy)) return policy.value;\n const context = yield* Effect.serviceOption(AlchemyContext);\n return Option.isSome(context) ? context.value.adopt : false;\n });\n",
12
12
  "/**\n * Refuse a config whose `admin` block would cut the provider off from Caddy, or expose the admin\n * API — checked on the ADAPTED JSON (`POST /adapt`), before anything is loaded.\n *\n * ⛔ A Caddyfile can move its own admin endpoint. After `POST /load`, Caddy swaps the admin server\n * to the new config's (admin.go replaceLocalAdminServer), so each of these would succeed ONCE and\n * then strand the resource — no read-back, no drift check, no next deploy — or worse:\n * · `admin off` (`disabled`): nothing can manage this Caddy through its API again.\n * · a `listen` off loopback (`:2019`, `0.0.0.0:2019`, a LAN address): the unauthenticated API is\n * on the network. Decision 23: never expose :2019.\n * · a `listen` on another port, socket or loopback ADDRESS than the transport's: the provider\n * loses Caddy. Go binds exactly the literal address given (`[::1]:2019` is not reachable at\n * `127.0.0.1:2019`), and a hostname binds its first IPv4 address (net ListenConfig.Listen,\n * `addrs.first(isIPv4)`), so `localhost:2019` is `127.0.0.1:2019`. MEASURED 2026-09-21 on a\n * throwaway Caddy 2.11.4: after loading `admin [::1]:<p>`, `127.0.0.1:<p>` refused the\n * connection; after `admin localhost:<p>`, `[::1]:<p>` did.\n * · NO `admin` address at all: Caddy falls back to DefaultAdminListen — `localhost:2019`, or\n * `$CADDY_ADMIN` in ITS environment (admin.go replaceLocalAdminServer), which this side cannot\n * see. A Caddy reached any other way (a socket, another port) moves there on the first load.\n * Refused unless the transport is at that default; declaring `admin <address>` is the fix.\n * · a Host the new config does not allow: `origins` without it, or — with no `origins` — a Host\n * outside Caddy's loopback defaults (`localhost`, `[::1]`, `127.0.0.1` at the listen port).\n * Every later call answers 403 (admin.go allowedOrigins / checkHost; MEASURED on the mini's\n * Caddy 2.11.4 — `host not allowed`). The transport also sends `Origin: http://<Host>`, which\n * Caddy checks against the same list (originAllowed), so one check covers both.\n * · `enforce_origin` over a unix socket: this transport, like Caddy's CLI (cmd/commandfuncs.go\n * AdminAPIRequest), sends no Origin there, and a unix listener has no default origins — so\n * checkOrigin answers 403 to every call. MEASURED 2026-09-21 on a throwaway Caddy 2.11.4:\n * `client is not allowed to access from origin ''`, and that Caddy could not be managed again.\n * · `remote`: a second, network-facing admin listener (mTLS on :2021) — declare that deliberately,\n * not through this resource.\n * · `config.load`: Caddy pulls its config from elsewhere, replacing this one on a timer.\n * ★ Read in caddyserver/caddy v2.11.4: admin.go AdminConfig / allowedOrigins / originAllowed, and\n * httpcaddyfile/options.go parseOptAdmin (an `admin { … }` block with no address still emits\n * `listen` as the default, so a present `listen` is the norm, not a change).\n */\nimport type { CaddyAdminListener } from './admin.ts';\n\ntype AdminBlock = {\n disabled?: boolean;\n listen?: string;\n origins?: string[];\n enforce_origin?: boolean;\n remote?: unknown;\n config?: { load?: unknown };\n};\n\n/** Caddy's compiled-in admin port (admin.go DefaultAdminListen `localhost:2019`). */\nconst DEFAULT_ADMIN_PORT = 2019;\n\nconst LOOPBACK_HOST = /^(?:localhost|127(?:\\.\\d{1,3}){3}|\\[?::1\\]?)$/;\n\ntype Listen = { kind: 'tcp'; host: string; port: number } | { kind: 'unix'; path: string };\n\n/** Caddy network address → a listener, or a description of why it is not one we can check. */\nconst parseListen = (listen: string): Listen | string => {\n if (listen.includes('{')) return 'a placeholder, which cannot be checked before it runs';\n const unix = /^unix(?:gram|packet)?\\/(.+?)(?:\\|[0-7]{3,4})?$/.exec(listen);\n if (unix !== null) return { kind: 'unix', path: unix[1] ?? '' };\n if (/^[a-z0-9]+\\//.test(listen) && !/^tcp[46]?\\//.test(listen)) {\n return `network ${JSON.stringify(listen.split('/')[0])}, not tcp or unix`;\n }\n const address = listen.replace(/^tcp[46]?\\//, '');\n const colon = address.lastIndexOf(':');\n const port = Number(address.slice(colon + 1));\n if (colon === -1 || !Number.isInteger(port) || port <= 0) return 'no single port';\n return { host: address.slice(0, colon), kind: 'tcp', port };\n};\n\nconst unbracket = (host: string): string => host.replace(/^\\[(.*)\\]$/, '$1');\n\n/**\n * Whether a transport sending `hostHeader` reaches a listener bound to `listenHost`.\n * ★ The Host stands in for the dial address: they are the same unless `hostHeader` was set for a\n * forward, where it should name the far side's address anyway. `localhost` on the transport's\n * side may resolve to either family (node tries both), so it reaches any loopback listener; a\n * literal address reaches only itself.\n */\nconst reaches = (hostHeader: string, listenHost: string): boolean => {\n const dialled = unbracket(hostHeader.slice(0, hostHeader.lastIndexOf(':')));\n const listening = unbracket(listenHost);\n return (\n dialled === 'localhost' || dialled === (listening === 'localhost' ? '127.0.0.1' : listening)\n );\n};\n\nconst sameListener = (listen: Listen, transport: CaddyAdminListener): boolean =>\n listen.kind === 'tcp'\n ? transport.kind === 'tcp' &&\n listen.port === transport.port &&\n reaches(transport.hostHeader, listen.host)\n : transport.kind === 'unix' && listen.path === transport.path;\n\n/** Caddy's allowed Hosts for a loopback listener with no `origins` (admin.go allowedOrigins). */\nconst defaultOrigins = (port: number): string[] =>\n ['localhost', '[::1]', '127.0.0.1'].map((host) => `${host}:${String(port)}`);\n\nconst originHost = (origin: string): string | undefined => {\n if (!origin.includes('://')) return origin;\n try {\n const url = new URL(origin);\n // ⚠️ originAllowed compares schemes when an entry has one; the transport sends `http://`.\n return url.protocol === 'http:' ? url.host : undefined;\n } catch {\n return undefined;\n }\n};\n\nconst listenProblem = (admin: AdminBlock, transport: CaddyAdminListener): string | undefined => {\n if (admin.listen === undefined) {\n const atDefault =\n transport.kind === 'tcp' &&\n transport.port === DEFAULT_ADMIN_PORT &&\n reaches(transport.hostHeader, 'localhost');\n return atDefault\n ? undefined\n : 'no `admin` address: Caddy would fall back to its default listener (localhost:2019, or ' +\n '$CADDY_ADMIN), away from where this provider reaches it — declare `admin <address>`';\n }\n const listen = parseListen(admin.listen);\n const shown = JSON.stringify(admin.listen);\n if (typeof listen === 'string') {\n return `admin listen ${shown} is ${listen}; declare a literal loopback address`;\n }\n if (listen.kind === 'tcp' && !LOOPBACK_HOST.test(listen.host)) {\n return `admin listen ${shown} is not loopback — it would put the admin API on the network`;\n }\n return sameListener(listen, transport)\n ? undefined\n : `admin listen ${shown} moves the admin API away from where this provider reaches it`;\n};\n\n/** Refusals for the adapted config's `admin` block, given where the transport reaches Caddy. */\nexport const adminProblems = (adapted: unknown, transport: CaddyAdminListener): string[] => {\n const admin =\n typeof adapted === 'object' && adapted !== null\n ? ((adapted as { admin?: AdminBlock }).admin ?? {})\n : {};\n const found: string[] = [];\n if (admin.disabled === true) {\n return ['`admin off` would disable the admin API this resource manages Caddy through'];\n }\n if (admin.remote !== undefined) found.push('`admin.remote` opens a network-facing admin API');\n if (admin.config?.load !== undefined) {\n found.push(\n '`admin.config.load` makes Caddy pull its config from elsewhere, replacing this one',\n );\n }\n const moved = listenProblem(admin, transport);\n if (moved !== undefined) found.push(moved);\n if (transport.kind === 'tcp') {\n const allowed = admin.origins?.map(originHost) ?? defaultOrigins(transport.port);\n if (!allowed.includes(transport.hostHeader)) {\n const which =\n admin.origins === undefined\n ? `Caddy's default origins (no \\`origins\\` declared) ${JSON.stringify(allowed)}`\n : `admin origins ${JSON.stringify(admin.origins)}`;\n found.push(\n `${which} do not allow Host ${transport.hostHeader}, which this provider sends — ` +\n 'Caddy would refuse it every call after this one',\n );\n }\n } else if (admin.enforce_origin === true) {\n found.push(\n '`enforce_origin` over a unix socket: this transport (like the Caddy CLI) sends no Origin ' +\n 'there, so Caddy would answer 403 to every call',\n );\n }\n return found;\n};\n",
13
13
  "/**\n * A tripwire for the obvious ways a secret ends up in Alchemy state through these providers.\n *\n * ⛔ WHY IT EXISTS. Alchemy persists every prop UNENCRYPTED in its state store — the same reason\n * the OpenBao families are metadata-only (openbao/policy.ts). A LaunchdJob's `environment` and\n * `programArguments`, and a HostFile's `content`, are props. A token declared there is a token\n * copied into the state store, into every plan diff, and into the plist or file on disk.\n * ★ THE ALTERNATIVE, NAMED IN EVERY REFUSAL: let a secret renderer (openbao-agent, or any other)\n * write the secret to a root-owned file, and declare only its PATH — `FOO_TOKEN_FILE=/path`,\n * `--token-file /path`. The job reads the file at start; the state holds a path.\n *\n * ⚠️ A TRIPWIRE, NOT A SCANNER. It catches the names and shapes people actually type. A token in\n * an innocently named variable passes; nothing here can make a secret safe to declare.\n */\nimport type { PlistDict, PlistValue } from './plist.ts';\n\nconst ALTERNATIVE =\n 'props are stored unencrypted in Alchemy state. Have a secret renderer (e.g. openbao-agent) ' +\n 'write it to a root-owned file and declare the file path instead (FOO_FILE=/path, --foo-file /path).';\n\n/** Names that end in a word meaning \"this is the secret itself\". `*_FILE`, `*_PATH` never match. */\nconst SECRET_NAME =\n /(?:^|_)(?:TOKEN|SECRET|PASSWORD|PASSWD|PASSPHRASE|API_?KEY|ACCESS_KEY|PRIVATE_KEY|SECRET_KEY|CREDENTIALS?)$/i;\n\n/** `--token=…` / `-password=…` style flags carrying their value inline. */\nconst INLINE_FLAG = /^--?[a-z0-9-]*(?:token|password|passwd|passphrase|secret|api-?key)=./i;\n\n/** A flag whose NEXT argv element is the secret: `--token abc`. */\nconst BARE_FLAG = /^--?(?:[a-z0-9]+-)*(?:token|password|passwd|passphrase|secret|api-?key)$/i;\n\nexport const PRIVATE_KEY = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/;\n\n/** Paths of every string leaf in a plist value holding a private key. */\nconst keyLeaves = (value: PlistValue, path: string): string[] => {\n if (typeof value === 'string') return PRIVATE_KEY.test(value) ? [path] : [];\n if (Array.isArray(value)) {\n return (value as readonly PlistValue[]).flatMap((item, index) =>\n keyLeaves(item, `${path}[${String(index)}]`),\n );\n }\n if (typeof value === 'object' && value !== null) {\n return Object.entries(value).flatMap(([key, item]) => keyLeaves(item, `${path}.${key}`));\n }\n return [];\n};\n\n/**\n * Refusals for a job's environment, argv and extraKeys. Empty means nothing tripped.\n * ★ extraKeys too: it is a prop like the others, so it lands in state just the same.\n */\nexport const jobSecretProblems = (\n environment: Readonly<Record<string, string>> | undefined,\n argv: readonly string[],\n extraKeys?: PlistDict,\n): string[] => {\n const found: string[] = [];\n for (const [name, value] of Object.entries(environment ?? {})) {\n if (SECRET_NAME.test(name))\n found.push(`environment ${name} looks like a secret: ${ALTERNATIVE}`);\n else if (PRIVATE_KEY.test(value))\n found.push(`environment ${name} holds a private key: ${ALTERNATIVE}`);\n }\n for (const [index, arg] of argv.entries()) {\n // ⛔ Never echo the argument itself: if it IS a secret, the refusal would print it.\n if (INLINE_FLAG.test(arg)) {\n found.push(`programArguments[${String(index)}] passes a secret inline: ${ALTERNATIVE}`);\n } else if (BARE_FLAG.test(arg) && index + 1 < argv.length) {\n found.push(`programArguments[${String(index + 1)}] follows a secret flag: ${ALTERNATIVE}`);\n } else if (PRIVATE_KEY.test(arg)) {\n found.push(`programArguments[${String(index)}] holds a private key: ${ALTERNATIVE}`);\n }\n }\n for (const path of keyLeaves(extraKeys ?? {}, 'extraKeys')) {\n found.push(`${path} holds a private key: ${ALTERNATIVE}`);\n }\n return found;\n};\n\n/** Refusals for a HostFile's content. */\nexport const fileSecretProblems = (content: string): string[] =>\n PRIVATE_KEY.test(content) ? [`content holds a private key: ${ALTERNATIVE}`] : [];\n",
14
14
  "/**\n * A tripwire for the obvious ways a secret ends up in a CaddyConfig's `caddyfile` prop.\n *\n * ⛔ WHY IT EXISTS. Alchemy stores every prop UNENCRYPTED in its state store and prints it in plan\n * diffs (the reason launchd's secret-tripwire.ts and the metadata-only OpenBao families exist).\n * The whole Caddyfile is one prop, so a token typed into it is a token in state.\n * ★ THE ALTERNATIVE, NAMED IN EVERY REFUSAL: a RUNTIME placeholder, which Caddy resolves in its own\n * process and which therefore never enters the Caddyfile, the state, the adapted JSON or\n * `autosave.json` (caddyserver/caddy v2.11.4 replacer.go):\n * · `{env.NAME}` — the Caddy process's environment; start Caddy with `--envfile <path>` where a\n * secret renderer (openbao-agent) writes that file. A rotated value needs a Caddy restart.\n * · `{file./path}` — the file's contents, trailing newline trimmed, read when the placeholder is\n * evaluated; the renderer writes the file.\n * ⚠️ `{$NAME}` IS NOT ONE OF THEM. It is substituted at ADAPT time (caddyfile/parse.go\n * replaceEnvVars), so the value lands in the adapted JSON — readable at `GET /config/` and\n * written to `autosave.json`. It is not refused (the prop holds only the name), but it is worse.\n * `{$NAME:default}` is different: the default IS in the prop, and is checked as a literal.\n * ⚠️ A TRIPWIRE, NOT A SCANNER. It catches the names and shapes people actually type; a secret in\n * an innocent-looking argument passes. Nothing here makes a secret safe to declare.\n */\nimport { PRIVATE_KEY } from '../launchd/secret-tripwire.ts';\n\nconst ALTERNATIVE =\n 'props are stored unencrypted in Alchemy state. Use a runtime placeholder instead: {env.NAME} ' +\n '(Caddy started with --envfile <file a secret renderer writes>) or {file./path/to/secret}.';\n\n/** A subdirective whose whole name says \"my argument is the secret\". `*_file` never matches. */\nconst SECRET_WORD =\n /^(?:token|api_token|auth_token|access_token|secret|client_secret|shared_secret|secret_key|password|passwd|passphrase|api_key|apikey|private_key|mac_key|hmac_key)$/i;\n\n/** Directives whose second argument is a DNS provider's credential: `dns cloudflare <token>`. */\nconst DNS_DIRECTIVE = /^(?:dns|acme_dns)$/;\n\nconst HEADER_DIRECTIVE = /^(?:header|header_up|header_down|request_header)$/;\nconst CREDENTIAL_HEADER =\n /^[+-]?(?:authorization|proxy-authorization|cookie|x-api-key|api-key|x-auth-token|x-vault-token)$/i;\n\n/** Shapes that are a credential wherever they appear. The label is what the refusal says. */\nconst SHAPES: readonly (readonly [RegExp, string])[] = [\n [/^(?:gh[pousr]_[A-Za-z0-9]{30,}|github_pat_[A-Za-z0-9_]{20,})$/, 'a GitHub token'],\n [/^(?:AKIA|ASIA)[0-9A-Z]{16}$/, 'an AWS access key id'],\n [/^xox[abposr]-[A-Za-z0-9-]{10,}$/, 'a Slack token'],\n [/^sk-(?:ant-|proj-)?[A-Za-z0-9_-]{20,}$/, 'an API secret key'],\n [/^eyJ[A-Za-z0-9_-]{8,}\\.eyJ[A-Za-z0-9_-]{8,}\\.[A-Za-z0-9_-]{8,}$/, 'a JWT'],\n // ★ basic_auth replaces placeholders in the hash at provision time (caddyauth/basicauth.go\n // Provision), so `{env.ADMIN_HASH}` works and a literal hash never needs to be declared.\n [/^\\$2[abxy]?\\$\\d{2}\\$[./A-Za-z0-9]{53}$/, 'a bcrypt password hash'],\n [/^\\$argon2(?:id|i|d)\\$/, 'an argon2 password hash'],\n];\n\nconst PLACEHOLDER = /\\{[^{}\\s]+\\}/g;\nconst AUTH_SCHEME = /^(?:bearer|basic|token|digest)$/i;\n\n/**\n * ⛔ `{$NAME:default}` IS A LITERAL. Caddy splices the default in at ADAPT time whenever NAME is\n * unset (caddyfile/parse.go replaceEnvVars: SplitN on `:`), so a secret typed as the default is\n * in the prop, the state and the adapted JSON. MEASURED 2026-09-21 on a throwaway Caddy 2.11.4:\n * `respond \"{$UNSET:literal-default}\"` adapted to the body `literal-default`. So each line is\n * checked as Caddy sees it with every such variable unset. The RUNTIME replacer has no default\n * syntax (replacer.go looks the whole key up), so `{env.X}` stays the safe form.\n */\nconst ENV_DEFAULT = /\\{\\$[^{}\\s:]*:([^}]*)\\}/g;\nconst withDefaults = (line: string): string => line.replace(ENV_DEFAULT, '$1');\n\n/** One line's tokens: whitespace-separated, \"double-quoted\" and `backquoted` kept whole, # comments dropped. */\nexport const tokensOf = (line: string): string[] => {\n const tokens: string[] = [];\n let current = '';\n let quote: string | undefined;\n for (let index = 0; index < line.length; index += 1) {\n const char = line[index] ?? '';\n if (quote !== undefined) {\n if (char === '\\\\' && quote === '\"' && index + 1 < line.length) {\n index += 1;\n current += line[index] ?? '';\n } else if (char === quote) quote = undefined;\n else current += char;\n } else if (char === '\"' || char === '`') {\n quote = char;\n } else if (/\\s/.test(char)) {\n if (current !== '') tokens.push(current);\n current = '';\n } else if (char === '#' && current === '') {\n break; // a comment starts only at the beginning of a token (caddyfile lexer)\n } else current += char;\n }\n if (current !== '') tokens.push(current);\n return tokens;\n};\n\n/**\n * True when nothing but placeholders (and an auth scheme word) is left of these tokens.\n * ⚠️ A bare `{` / `}` is a block brace, not a value: `dns cloudflare {` opens a block whose\n * `api_token` line is checked on its own.\n */\nconst placeholderOnly = (tokens: readonly string[]): boolean =>\n tokens\n .flatMap((token) => token.replace(PLACEHOLDER, ' ').split(/\\s+/))\n .filter((part) => part !== '' && part !== '{' && part !== '}' && !AUTH_SCHEME.test(part))\n .length === 0;\n\nconst lineProblems = (tokens: readonly string[], where: string): string[] => {\n const [first = '', second] = tokens;\n const found: string[] = [];\n if (SECRET_WORD.test(first) && tokens.length > 1 && !placeholderOnly(tokens.slice(1))) {\n found.push(`${where}: \\`${first}\\` has a literal value`);\n }\n if (DNS_DIRECTIVE.test(first) && tokens.length > 2 && !placeholderOnly(tokens.slice(2))) {\n found.push(`${where}: \\`${first} ${second ?? ''}\\` has a literal credential`);\n }\n if (HEADER_DIRECTIVE.test(first)) {\n const at = tokens.findIndex((token, index) => index > 0 && CREDENTIAL_HEADER.test(token));\n if (at !== -1 && tokens.length > at + 1 && !placeholderOnly(tokens.slice(at + 1))) {\n found.push(`${where}: \\`${first} ${tokens[at] ?? ''}\\` sets a literal credential`);\n }\n }\n for (const token of tokens) {\n const shape = SHAPES.find(([pattern]) => pattern.test(token));\n // ⛔ Never echo the token: if it IS a secret, the refusal would print it.\n if (shape !== undefined) found.push(`${where}: holds what looks like ${shape[1]}`);\n }\n return found;\n};\n\n/** Refusals for a Caddyfile's text. Empty means nothing tripped. */\nexport const caddyfileSecretProblems = (caddyfile: string): string[] => {\n const found: string[] = [];\n if (PRIVATE_KEY.test(caddyfile)) found.push('the Caddyfile holds a PEM private key');\n for (const [index, line] of caddyfile.split('\\n').entries()) {\n found.push(...lineProblems(tokensOf(withDefaults(line)), `line ${String(index + 1)}`));\n }\n return found.map((problem) => `${problem}: ${ALTERNATIVE}`);\n};\n",
15
15
  "/**\n * CaddyConfig's props, attributes and validation — the pure half of the provider.\n *\n * ⛔ `caddyfile` IS NEVER A SECRET. Alchemy stores props unencrypted, so the whole Caddyfile sits in\n * the state store and in every plan diff. Secrets reach Caddy as runtime placeholders\n * (`{env.NAME}`, `{file./path}`) resolved from files a secret renderer writes — see\n * caddyfile-tripwire.ts, which refuses the obvious literals.\n */\nimport { caddyfileSecretProblems } from './caddyfile-tripwire.ts';\n\nexport interface CaddyConfigProps {\n /**\n * The whole Caddyfile. Applied with `POST /load` (Content-Type `text/caddyfile`); it REPLACES the\n * running config. ⛔ Never a secret. ⚠️ `import` paths resolve against Caddy's working directory\n * (the API adapts with no file name), so keep them absolute.\n */\n caddyfile: string;\n /**\n * The file Caddy was started with (`caddy run --config <this>`), when the same Caddyfile is also\n * written there — `caddyWithFile()` sets it from its HostFile. Sent as\n * `Caddy-Config-Source-File` so a load keeps Caddy's SIGUSR1 reload-from-file working.\n */\n sourceFile?: string;\n}\n\nexport interface CaddyConfigAttributes {\n /**\n * SHA-256 of the ADAPTED JSON Caddy reported after the last apply (digest.ts canonical form) —\n * never the config itself. Drift is the live config hashing to anything else.\n */\n configSha256: string;\n /** The admin endpoint the config was applied through (no credential; the API has none). */\n endpoint: string;\n /** The `sourceFile` prop as applied. */\n sourceFile?: string;\n}\n\nconst absoluteNormalised = (path: string): boolean =>\n path.startsWith('/') &&\n !path.endsWith('/') &&\n !path.includes('\\x00') &&\n !path\n .split('/')\n .slice(1)\n .some((part) => part === '' || part === '.' || part === '..');\n\nexport const configProblems = (props: CaddyConfigProps): string[] => {\n const found: string[] = [];\n /**\n * ⛔ AN EMPTY CADDYFILE IS NOT \"NOTHING TO DO\". It adapts to a config with no apps, and loading\n * it stops every server Caddy runs — every site down, on a templating bug that rendered ''.\n * Emptying Caddy is a deliberate act for a person at the admin API, not a declaration.\n * Text that is not empty but serves nothing (only comments, only global options) is caught\n * after `/adapt`, where it shows as no apps (config-lifecycle.ts servesNothing).\n */\n if (props.caddyfile.trim() === '') {\n found.push('caddyfile is empty — loading it would stop every site Caddy serves');\n }\n if (props.sourceFile !== undefined && !absoluteNormalised(props.sourceFile)) {\n found.push('sourceFile must be an absolute, normalised path');\n }\n found.push(...caddyfileSecretProblems(props.caddyfile));\n return found;\n};\n",
16
- "/**\n * CaddyConfig's read / diff / reconcile as plain async functions over a CaddyAdmin, so the\n * lifecycle runs against a fake admin server in tests.\n *\n * The comparison is always between DIGESTS OF ADAPTED JSON (digest.ts): what `POST /adapt` makes\n * of the declared Caddyfile, what `GET /config/` reports, and what was stored after the last\n * apply. Three digests, because they answer different questions:\n * · declared ≠ stored — the declaration changed.\n * · live ≠ stored — DRIFT: a hand `curl` to the API, or a restart that loaded a different file\n * (or an `autosave.json` holding someone else's change, under `--resume`).\n * · declared = live — nothing to load, whatever the state says.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport type { CaddyAdmin } from './admin.ts';\nimport {\n CaddyAdminError,\n adaptCaddyfile,\n loadCaddyfile,\n readRunningConfig,\n} from './admin-calls.ts';\nimport { adminProblems } from './admin-guard.ts';\nimport {\n type CaddyConfigAttributes,\n type CaddyConfigProps,\n configProblems,\n} from './config-form.ts';\nimport { configDigest } from './digest.ts';\n\nconst refuse = (admin: CaddyAdmin, message: string): Error =>\n new Error(`Caddy.Config at ${admin.endpoint}: ${message}`);\n\nconst short = (digest: string): string => digest.slice(0, 12);\n\ntype Desired = { readonly digest: string; readonly warnings: readonly string[] };\n\n/**\n * ⛔ A CONFIG WITH NO APPS SERVES NOTHING. A Caddyfile of only comments or only global options\n * passes the empty-text check (config-form.ts) yet adapts to `{}` or `{\"admin\":…}` — MEASURED\n * 2026-09-21 on a throwaway Caddy 2.11.4 — and loading it stops every server: the same outage,\n * from the same templating bug, that the empty check exists to prevent.\n */\nconst servesNothing = (config: unknown): boolean => {\n const apps =\n typeof config === 'object' && config !== null ? (config as { apps?: unknown }).apps : undefined;\n return typeof apps !== 'object' || apps === null || Object.keys(apps).length === 0;\n};\n\n/**\n * Validate the props, adapt the Caddyfile on the running Caddy, and check its `admin` block.\n * ★ Every refusal happens HERE, before anything is loaded — and diff calls this at plan time, so a\n * Caddyfile that does not adapt, carries a literal secret, or would strand the admin API fails the\n * PLAN, before any resource (a HostFile holding the same text included) is applied.\n */\nexport const desiredConfig = async (\n admin: CaddyAdmin,\n props: CaddyConfigProps,\n): Promise<Desired> => {\n const found = configProblems(props);\n if (found.length > 0) throw refuse(admin, found.join('; '));\n const adapted = await adaptCaddyfile(admin, props.caddyfile);\n if (servesNothing(adapted.config)) {\n throw refuse(admin, 'the Caddyfile adapts to no apps — loading it would stop every site');\n }\n const guard = adminProblems(adapted.config, admin.listener);\n if (guard.length > 0) throw refuse(admin, guard.join('; '));\n return { digest: configDigest(adapted.config), warnings: adapted.warnings };\n};\n\n/** What is running now, as attributes. `sourceFile` is carried over, never discovered. */\nexport const readLive = async (\n admin: CaddyAdmin,\n sourceFile?: string,\n): Promise<CaddyConfigAttributes> => ({\n configSha256: configDigest(await readRunningConfig(admin)),\n endpoint: admin.endpoint,\n ...(sourceFile === undefined ? {} : { sourceFile }),\n});\n\n/**\n * ★ NEVER `replace`. Nothing in the props names a different object: a new Caddyfile is a reload of\n * the same Caddy, and `sourceFile` is a header. (Which Caddy is the transport's business — see\n * the ⚠️ on `endpoint` in the diff.)\n */\nexport const diffConfig = async (\n admin: CaddyAdmin,\n news: CaddyConfigProps,\n output: CaddyConfigAttributes,\n): Promise<Diff> => {\n const want = await desiredConfig(admin, news);\n const live = await readLive(admin);\n const converged =\n want.digest === live.configSha256 &&\n live.configSha256 === output.configSha256 &&\n news.sourceFile === output.sourceFile &&\n // ⚠️ A stack whose transport now reaches ANOTHER Caddy (a new address) must load there; the\n // old one keeps what it has — the same as a delete, which never unloads (see config.ts).\n admin.endpoint === output.endpoint;\n return converged ? { action: 'noop' } : { action: 'update' };\n};\n\nexport type Applied = {\n readonly attributes: CaddyConfigAttributes;\n /** The adapter's warnings (e.g. \"Caddyfile input is not formatted\") — log them, never refuse. */\n readonly warnings: readonly string[];\n /** Whether a `POST /load` was sent (false when Caddy already ran this config). */\n readonly loaded: boolean;\n};\n\n/** Load the Caddyfile unless Caddy already runs it, then read it back and insist it matches. */\nexport const reconcileConfig = async (\n admin: CaddyAdmin,\n props: CaddyConfigProps,\n): Promise<Applied> => {\n const want = await desiredConfig(admin, props);\n const before = configDigest(await readRunningConfig(admin));\n let warnings = want.warnings;\n const loaded = before !== want.digest;\n if (loaded) {\n try {\n warnings = await loadCaddyfile(admin, props.caddyfile, props.sourceFile);\n } catch (cause) {\n if (!(cause instanceof CaddyAdminError)) throw cause;\n // ★ SAY WHAT IS RUNNING NOW. Caddy restores the previous config on a failed load (caddy.go\n // changeConfig); confirm it rather than promise it, because the deploy log is where the\n // person decides whether sites are down.\n const after = await readLive(admin).catch(() => undefined);\n const state =\n after === undefined\n ? 'and the running config could not be read back'\n : after.configSha256 === before\n ? 'Caddy kept the previous config (still serving)'\n : `and the running config is now ${short(after.configSha256)}, not the previous ${short(before)}`;\n throw refuse(admin, `Caddy refused the Caddyfile — ${cause.message}; ${state}`);\n }\n }\n // ⚠️ READ BACK, never echo the declaration: a concurrent writer, or a load Caddy answered 200 but\n // did not apply (admin-calls.ts), shows up here as a refusal instead of as stored state that is\n // already wrong.\n const attributes = await readLive(admin, props.sourceFile);\n if (attributes.configSha256 !== want.digest) {\n throw refuse(\n admin,\n `after the load Caddy runs config ${short(attributes.configSha256)}, not the ` +\n `${short(want.digest)} this Caddyfile adapts to — another writer changed it, or it was not applied`,\n );\n }\n return { attributes, loaded, warnings };\n};\n",
16
+ "/**\n * CaddyConfig's read / diff / reconcile as plain async functions over a CaddyAdmin, so the\n * lifecycle runs against a fake admin server in tests.\n *\n * The comparison is always between DIGESTS OF ADAPTED JSON (digest.ts): what `POST /adapt` makes\n * of the declared Caddyfile, what `GET /config/` reports, and what was stored after the last\n * apply. Three digests, because they answer different questions:\n * · declared ≠ stored — the declaration changed.\n * · live ≠ stored — DRIFT: a hand `curl` to the API, or a restart that loaded a different file\n * (or an `autosave.json` holding someone else's change, under `--resume`).\n * · declared = live — nothing to load, whatever the state says.\n * With NO state there is no stored digest, and the question is whose config is running: see\n * `claimable` below, which both the adoption probe and the apply ask.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport { type CaddyAdmin, CaddyUnreachableError } from './admin.ts';\nimport {\n CaddyAdminError,\n adaptCaddyfile,\n loadCaddyfile,\n readRunningConfig,\n} from './admin-calls.ts';\nimport { adminProblems } from './admin-guard.ts';\nimport {\n type CaddyConfigAttributes,\n type CaddyConfigProps,\n configProblems,\n} from './config-form.ts';\nimport { configDigest } from './digest.ts';\n\nconst refuse = (admin: CaddyAdmin, message: string): Error =>\n new Error(`Caddy.Config at ${admin.endpoint}: ${message}`);\n\nconst short = (digest: string): string => digest.slice(0, 12);\n\ntype Desired = { readonly digest: string; readonly warnings: readonly string[] };\n\n/**\n * ⛔ A CONFIG WITH NO APPS SERVES NOTHING. A Caddyfile of only comments or only global options\n * passes the empty-text check (config-form.ts) yet adapts to `{}` or `{\"admin\":…}` — MEASURED\n * 2026-09-21 on a throwaway Caddy 2.11.4 — and loading it stops every server: the same outage,\n * from the same templating bug, that the empty check exists to prevent.\n */\nconst servesNothing = (config: unknown): boolean => {\n const apps =\n typeof config === 'object' && config !== null ? (config as { apps?: unknown }).apps : undefined;\n return typeof apps !== 'object' || apps === null || Object.keys(apps).length === 0;\n};\n\n/**\n * Validate the props, adapt the Caddyfile on the running Caddy, and check its `admin` block.\n * ★ Every refusal happens HERE, before anything is loaded — and diff calls this at plan time, so a\n * Caddyfile that does not adapt, carries a literal secret, or would strand the admin API fails the\n * PLAN, before any resource (a HostFile holding the same text included) is applied.\n */\nexport const desiredConfig = async (\n admin: CaddyAdmin,\n props: CaddyConfigProps,\n): Promise<Desired> => {\n const found = configProblems(props);\n if (found.length > 0) throw refuse(admin, found.join('; '));\n const adapted = await adaptCaddyfile(admin, props.caddyfile);\n if (servesNothing(adapted.config)) {\n throw refuse(admin, 'the Caddyfile adapts to no apps — loading it would stop every site');\n }\n const guard = adminProblems(adapted.config, admin.listener);\n if (guard.length > 0) throw refuse(admin, guard.join('; '));\n return { digest: configDigest(adapted.config), warnings: adapted.warnings };\n};\n\nconst attributesOf = (\n admin: CaddyAdmin,\n running: unknown,\n sourceFile?: string,\n): CaddyConfigAttributes => ({\n configSha256: configDigest(running),\n endpoint: admin.endpoint,\n ...(sourceFile === undefined ? {} : { sourceFile }),\n});\n\n/** What is running now, as attributes. `sourceFile` is carried over, never discovered. */\nexport const readLive = async (\n admin: CaddyAdmin,\n sourceFile?: string,\n): Promise<CaddyConfigAttributes> =>\n attributesOf(admin, await readRunningConfig(admin), sourceFile);\n\n/**\n * ⛔ WITH NO STATE, A RUNNING CONFIG IS THIS STACK'S ONLY IF IT IS THE DECLARED ONE (decision,\n * 2026-09-21: CaddyConfig must not adopt silently — the house rule HostFile and LaunchdJob keep).\n * Anything else was put there by a person, another tool or another stack, and loading over it\n * replaces every site it serves. ★ A config that serves nothing (`null`, or no apps) is claimable\n * too: there is nothing to take over, as an empty R2 lock rule set reads as no lock.\n */\nexport const claimable = (running: unknown, declaredDigest: string): boolean =>\n servesNothing(running) || configDigest(running) === declaredDigest;\n\nexport type Probe = {\n readonly attributes: CaddyConfigAttributes;\n /** The live config IS the declared one: adopting it changes nothing. */\n readonly ours: boolean;\n /** Why the declaration could not be compared, when it could not — the Caddy is then not ours. */\n readonly unchecked?: string;\n};\n\n/**\n * The adoption probe — `read` with no state: `undefined` when Caddy serves nothing (plan a create),\n * else the live attributes and whether they are provably ours (the provider brands the rest\n * `Unowned`, and the plan refuses them without `--adopt`).\n * ⛔ IT NEVER THROWS OVER THE DECLARATION. The engine also runs this read to recover an interrupted\n * create, with THAT deploy's props (Plan.ts, `status: \"creating\"`) — so a Caddyfile that failed to\n * adapt then would fail every later plan, the one carrying the fix included. What cannot be\n * compared is \"not proven ours\", with the reason; the next diff or apply validates the Caddyfile.\n */\nexport const probeLive = async (\n admin: CaddyAdmin,\n caddyfile: string | undefined,\n sourceFile?: string,\n): Promise<Probe | undefined> => {\n const running = await readRunningConfig(admin);\n if (servesNothing(running)) return undefined;\n const attributes = attributesOf(admin, running, sourceFile);\n if (caddyfile === undefined) {\n return { attributes, ours: false, unchecked: 'the stored Caddyfile is not plain text' };\n }\n try {\n const want = await desiredConfig(admin, { caddyfile });\n return { attributes, ours: claimable(running, want.digest) };\n } catch (cause) {\n // ⚠️ Unreachable mid-probe is still \"no Caddy\" (config.ts plans without it), not \"not ours\".\n if (cause instanceof CaddyUnreachableError) throw cause;\n return {\n attributes,\n ours: false,\n unchecked: cause instanceof Error ? cause.message : String(cause),\n };\n }\n};\n\n/**\n * ★ NEVER `replace`. Nothing in the props names a different object: a new Caddyfile is a reload of\n * the same Caddy, and `sourceFile` is a header. (Which Caddy is the transport's business — see\n * the ⚠️ on `endpoint` in the diff.)\n */\nexport const diffConfig = async (\n admin: CaddyAdmin,\n news: CaddyConfigProps,\n output: CaddyConfigAttributes,\n): Promise<Diff> => {\n const want = await desiredConfig(admin, news);\n const live = await readLive(admin);\n const converged =\n want.digest === live.configSha256 &&\n live.configSha256 === output.configSha256 &&\n news.sourceFile === output.sourceFile &&\n // ⚠️ A stack whose transport now reaches ANOTHER Caddy (a new address) must load there; the\n // old one keeps what it has — the same as a delete, which never unloads (see config.ts).\n // Planned as an update, but the state does not vouch for that Caddy: see Authority.\n admin.endpoint === output.endpoint;\n return converged ? { action: 'noop' } : { action: 'update' };\n};\n\nexport type Applied = {\n readonly attributes: CaddyConfigAttributes;\n /** The adapter's warnings (e.g. \"Caddyfile input is not formatted\") — log them, never refuse. */\n readonly warnings: readonly string[];\n /** Whether a `POST /load` was sent (false when Caddy already ran this config). */\n readonly loaded: boolean;\n};\n\n/**\n * What this apply may load over — config.ts decides from the state and the adopt setting:\n * · `takeOver` — any running config: the state was applied to THIS Caddy (an update, drift\n * correction, or a create the engine adopted), or adoption is on (`--adopt`, or the\n * resource's own `adopt(true)` — host-effect.ts adoptEnabled).\n * · otherwise only a claimable one — plus, as `stored`, the digest the state last recorded: a\n * Caddy reached at a NEW endpoint that runs exactly that is the same config under another name.\n */\nexport type Authority = { readonly takeOver: boolean; readonly stored?: string };\n\n/**\n * Load the Caddyfile unless Caddy already runs it, then read it back and insist it matches.\n * ⛔ WITHOUT `takeOver`, A CONFIG THE STACK CANNOT CLAIM IS NEVER LOADED OVER. The engine's adoption\n * probe cannot guard every create: it is skipped while `news` holds an Output — ALWAYS on\n * caddyWithFile's first deploy (`sourceFile` is its HostFile's path) — and a Caddy that was down at\n * plan time read as nothing. Nor does state vouch for a Caddy the transport NOW reaches at another\n * endpoint (diff plans that as an update). Refused here, before any `/load`, like HostFile's.\n */\nexport const reconcileConfig = async (\n admin: CaddyAdmin,\n props: CaddyConfigProps,\n authority: Authority,\n): Promise<Applied> => {\n const want = await desiredConfig(admin, props);\n const running = await readRunningConfig(admin);\n const before = configDigest(running);\n const known = before === authority.stored || claimable(running, want.digest);\n if (!authority.takeOver && !known) {\n throw refuse(\n admin,\n `Caddy runs config ${short(before)}, which this stack did not load, and the Caddyfile adapts ` +\n `to ${short(want.digest)} — loading it would replace every site that config serves. ` +\n 'Deploy with --adopt (or wrap the resource in adopt(true)) to take this Caddy over, or ' +\n 'point caddyProviders() at the Caddy you meant.',\n );\n }\n let warnings = want.warnings;\n const loaded = before !== want.digest;\n if (loaded) {\n try {\n warnings = await loadCaddyfile(admin, props.caddyfile, props.sourceFile);\n } catch (cause) {\n if (!(cause instanceof CaddyAdminError)) throw cause;\n // ★ SAY WHAT IS RUNNING NOW. Caddy restores the previous config on a failed load (caddy.go\n // changeConfig); confirm it rather than promise it, because the deploy log is where the\n // person decides whether sites are down.\n const after = await readLive(admin).catch(() => undefined);\n const state =\n after === undefined\n ? 'and the running config could not be read back'\n : after.configSha256 === before\n ? 'Caddy kept the previous config (still serving)'\n : `and the running config is now ${short(after.configSha256)}, not the previous ${short(before)}`;\n throw refuse(admin, `Caddy refused the Caddyfile — ${cause.message}; ${state}`);\n }\n }\n // ⚠️ READ BACK, never echo the declaration: a concurrent writer, or a load Caddy answered 200 but\n // did not apply (admin-calls.ts), shows up here as a refusal instead of as stored state that is\n // already wrong.\n const attributes = await readLive(admin, props.sourceFile);\n if (attributes.configSha256 !== want.digest) {\n throw refuse(\n admin,\n `after the load Caddy runs config ${short(attributes.configSha256)}, not the ` +\n `${short(want.digest)} this Caddyfile adapts to — another writer changed it, or it was not applied`,\n );\n }\n return { attributes, loaded, warnings };\n};\n",
17
17
  "/**\n * The real CaddyAdmin: this machine's Caddy, over loopback TCP or a unix socket.\n *\n * ★ node:http, NOT fetch. MEASURED 2026-09-21 on bun 1.4.0 and node 26.7.0: node:http sends a Host\n * override and dials a unix socket on BOTH runtimes, while node's fetch silently drops a `Host`\n * header (it sent the URL's host) and only Bun's fetch has a `unix` option. The published dist\n * loads under node too (launchd's local-runner.ts makes the same call for the same reason).\n * ⛔ LOOPBACK OR A UNIX SOCKET, NOTHING ELSE. The admin API is unauthenticated plaintext; an\n * address off this machine means it is reachable from the network, which decision 23 forbids.\n * Reaching another host's Caddy means forwarding its socket or port to here (SSH), never\n * exposing :2019.\n * ★ HOST AND ORIGIN, AS CADDY's OWN CLI SENDS THEM (cmd/commandfuncs.go AdminAPIRequest): over\n * TCP, `Host` and `Origin: http://<host>`; over a socket, `Host: 127.0.0.1` and no Origin, because\n * Caddy skips the Host check on unix listeners (admin.go allowedOrigins). MEASURED against the\n * mini's Caddy 2.11.4: any other Host gets 403 `host not allowed`, a forwarded port included —\n * hence `hostHeader`.\n */\nimport { request as httpRequest } from 'node:http';\nimport {\n type CaddyAdmin,\n type CaddyAdminListener,\n type CaddyAdminResponse,\n CaddyUnreachableError,\n} from './admin.ts';\n\nexport type LocalCaddyAdminOptions = {\n /**\n * `http://<loopback>:<port>` or `unix:///absolute/path.sock`.\n * @default 'http://127.0.0.1:2019' — Caddy's default admin listener (`localhost:2019`).\n */\n readonly address?: string;\n /**\n * The Host (and Origin) to send over TCP, when it is not the address's own: the Caddyfile\n * narrowed `admin { origins … }`, or the port is an SSH forward of the real one (Caddy checks\n * the Host against ITS port, not yours). Form `host:port`.\n */\n readonly hostHeader?: string;\n /**\n * Abort an exchange after this long. @default 60_000\n * ⚠️ A `shutdown_delay` in the Caddyfile makes `/load` block that long (caddyhttp app.go Stop);\n * a timeout then fails the deploy while Caddy goes on to apply — the next plan converges.\n */\n readonly timeoutMs?: number;\n /**\n * Retries when the connection is REFUSED (nothing listening yet — a Caddy launchd job that was\n * bootstrapped a moment ago). Only refusals: those never reached Caddy, so re-sending a\n * `POST /load` cannot apply it twice. @default 2\n */\n readonly retries?: number;\n /** @default 500 */\n readonly retryDelayMs?: number;\n};\n\nexport const DEFAULT_ADMIN_ADDRESS = 'http://127.0.0.1:2019';\n\ntype Target =\n | { readonly kind: 'tcp'; readonly host: string; readonly port: number }\n | { readonly kind: 'unix'; readonly socketPath: string };\n\nconst UNIX = 'unix://';\nconst HOST_PORT = /^(?:[A-Za-z0-9.-]+|\\[[0-9A-Fa-f:.]+\\]):\\d{1,5}$/;\n\nconst refuse = (address: string, why: string): Error =>\n new Error(`Caddy admin address ${JSON.stringify(address)}: ${why}`);\n\nconst isLoopback = (hostname: string): boolean =>\n hostname === 'localhost' || hostname === '[::1]' || /^127(?:\\.\\d{1,3}){3}$/.test(hostname);\n\n/** The dial target and listener for an address, or a refusal naming what is wrong with it. */\nexport const parseAdminAddress = (\n address: string,\n hostHeader?: string,\n): { target: Target; listener: CaddyAdminListener } => {\n if (hostHeader !== undefined && !HOST_PORT.test(hostHeader)) {\n throw refuse(address, `hostHeader ${JSON.stringify(hostHeader)} must be host:port`);\n }\n if (address.startsWith(UNIX)) {\n const socketPath = address.slice(UNIX.length);\n if (!socketPath.startsWith('/')) throw refuse(address, 'the socket path must be absolute');\n return { listener: { kind: 'unix', path: socketPath }, target: { kind: 'unix', socketPath } };\n }\n let url: URL;\n try {\n url = new URL(address);\n } catch {\n throw refuse(address, 'not a URL (http://127.0.0.1:2019 or unix:///path)');\n }\n // ⛔ Caddy's local admin endpoint is plaintext by design; its TLS one (`admin.remote`, mTLS on\n // :2021) is a different, network-facing API this transport deliberately does not speak.\n if (url.protocol !== 'http:') throw refuse(address, 'only http:// (loopback) or unix://');\n if (!isLoopback(url.hostname)) {\n throw refuse(address, 'not loopback — the admin API is unauthenticated; forward it to here');\n }\n if (url.pathname !== '/' || url.search !== '' || url.username !== '') {\n throw refuse(address, 'no path, query or credentials — the API lives at the root');\n }\n const port = url.port === '' ? 80 : Number(url.port);\n // ⚠️ NOT `url.host`: WHATWG URL drops a default port (`http://127.0.0.1:80` has host\n // `127.0.0.1`), and Caddy compares the Host against `host:port` (admin.go allowedOrigins), so\n // a portless Host is a 403 on every call. The Caddy CLI always sends the port (JoinHostPort).\n const host = hostHeader ?? `${url.hostname}:${String(port)}`;\n return {\n listener: {\n hostHeader: host,\n kind: 'tcp',\n port: Number(host.slice(host.lastIndexOf(':') + 1)),\n },\n // ★ `hostname` keeps IPv6 brackets; node:http wants them off.\n target: { host: url.hostname.replace(/^\\[(.*)\\]$/, '$1'), kind: 'tcp', port },\n };\n};\n\nconst code = (cause: unknown): string | undefined =>\n typeof cause === 'object' && cause !== null && 'code' in cause\n ? String((cause as { code: unknown }).code)\n : undefined;\n\n/** Nothing accepted the connection: refused TCP, or no socket file yet. The request never left. */\nconst neverConnected = (cause: unknown): boolean =>\n code(cause) === 'ECONNREFUSED' || code(cause) === 'ENOENT';\n\nconst exchange = (\n target: Target,\n method: string,\n path: string,\n headers: Record<string, string>,\n body: string | undefined,\n timeoutMs: number,\n): Promise<CaddyAdminResponse> =>\n new Promise((resolve, reject) => {\n const bytes = body === undefined ? undefined : Buffer.from(body, 'utf8');\n const request = httpRequest(\n {\n ...(target.kind === 'tcp'\n ? { host: target.host, port: target.port }\n : { socketPath: target.socketPath }),\n headers: bytes === undefined ? headers : { ...headers, 'Content-Length': bytes.length },\n method,\n path,\n timeout: timeoutMs,\n },\n (response) => {\n const chunks: Buffer[] = [];\n response.on('data', (chunk: Buffer) => chunks.push(chunk));\n response.on('error', reject);\n response.on('end', () =>\n resolve({\n body: Buffer.concat(chunks).toString('utf8'),\n status: response.statusCode ?? 0,\n }),\n );\n },\n );\n request.on('timeout', () => request.destroy(new Error(`no answer in ${String(timeoutMs)} ms`)));\n request.on('error', reject);\n request.end(bytes);\n });\n\nexport const localCaddyAdmin = (options: LocalCaddyAdminOptions = {}): CaddyAdmin => {\n const address = options.address ?? DEFAULT_ADMIN_ADDRESS;\n const { listener, target } = parseAdminAddress(address, options.hostHeader);\n const timeoutMs = options.timeoutMs ?? 60_000;\n const retries = options.retries ?? 2;\n const delay = options.retryDelayMs ?? 500;\n const fixed: Record<string, string> =\n listener.kind === 'tcp'\n ? { Host: listener.hostHeader, Origin: `http://${listener.hostHeader}` }\n : { Host: '127.0.0.1' };\n return {\n endpoint: address,\n listener,\n request: async ({ body, headers, method, path }) => {\n for (let attempt = 0; ; attempt += 1) {\n try {\n // oxlint-disable-next-line no-await-in-loop -- sequential by design: retry after a wait\n return await exchange(target, method, path, { ...headers, ...fixed }, body, timeoutMs);\n } catch (cause) {\n if (!neverConnected(cause) || attempt >= retries) {\n const reason = cause instanceof Error ? cause.message : String(cause);\n const message = `Caddy admin ${method} ${path} at ${address}: ${reason}`;\n throw neverConnected(cause) ? new CaddyUnreachableError(message) : new Error(message);\n }\n // oxlint-disable-next-line no-await-in-loop -- the wait between attempts\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n },\n };\n};\n",
18
18
  "/**\n * The Caddy provider with its admin transport, as a single layer for a host stack.\n *\n * Layer.mergeAll(caddyProviders(), launchdProviders(), …the stack's other providers)\n *\n * ★ launchdProviders() TOO when the stack uses caddyWithFile(): the file is a launchd-subpath\n * HostFile, written through that subpath's HostRunner.\n * ⚠️ The default is localCaddyAdmin(): `http://127.0.0.1:2019`, Caddy's default admin listener.\n * Pass another CaddyAdmin for a unix socket, a narrowed `admin { origins }`, or an SSH forward.\n */\nimport * as Layer from 'effect/Layer';\nimport { type CaddyAdmin, caddyAdminLayer } from './admin.ts';\nimport { CaddyConfigProvider } from './config.ts';\nimport { localCaddyAdmin } from './local-admin.ts';\n\nexport const caddyProviders = (admin: CaddyAdmin = localCaddyAdmin()) =>\n CaddyConfigProvider().pipe(Layer.provide(caddyAdminLayer(admin)));\n",
19
- "/**\n * `caddyWithFile` — one Caddyfile, applied to the running Caddy AND written to the file Caddy\n * starts from, so a restart keeps it (decision 23: \"the file also lands on disk\").\n *\n * ★ REUSES launchd's HostFile, NOT A SECOND FILE WRITER. The file is an ordinary `Host.File`\n * (atomic write, digest diff, symlink refusal, `--adopt` for a file already there); the config is\n * a `Caddy.Config` whose `sourceFile` is that HostFile's `path` Output — which is what orders them.\n *\n * ⛔ THE ORDER IS FILE, THEN `/load`. The dependency makes Alchemy write the file first, and it is the\n * only order that bootstraps: a new host's Caddy launchd job starts from the file, before any\n * admin API exists to load into. Its cost, and what bounds it:\n * · A Caddyfile that does not ADAPT never reaches disk once the config has state: CaddyConfig's\n * diff adapts it at PLAN time and fails the plan. (On the very first deploy the file's path is\n * still an Output, Alchemy skips the adoption probe, and the check runs at apply instead.)\n * · A Caddyfile that adapts but that Caddy then REFUSES (a port in use, a missing cert file) is\n * on disk when `/load` fails. Caddy keeps serving the old config, the deploy fails with Caddy's\n * reason — and a RESTART before the fix would load the refused file. Fix and redeploy, or run\n * Caddy with `--resume` (docs/caddy.md): it restarts from `autosave.json`, the last config Caddy\n * ACCEPTED, and the file is only the first-boot fallback.\n * ⛔ SECRETS ARE REFUSED HERE, AT DECLARATION, before the HostFile exists: its `content` is a prop\n * too, and would carry a literal into state even if the CaddyConfig then refused it.\n * ★ BOTH RETAIN. Removing this from a stack must not delete the file a restart needs.\n */\nimport * as Effect from 'effect/Effect';\nimport * as RemovalPolicy from 'alchemy/RemovalPolicy';\nimport { HostFile } from '../launchd/host-file.ts';\nimport { CaddyConfig } from './config.ts';\nimport { configProblems } from './config-form.ts';\n\nexport interface CaddyWithFileProps {\n /** The whole Caddyfile. ⛔ Never a secret — use `{env.NAME}` / `{file./path}` placeholders. */\n readonly caddyfile: string;\n /** Absolute path Caddy is started with: `caddy run --config <path> --adapter caddyfile`. */\n readonly path: string;\n /** @default 0o644 */\n readonly mode?: number;\n readonly owner?: string | number;\n readonly group?: string | number;\n}\n\n/**\n * Declare the file (`<id>-file`) and the config (`<id>`). Provide `launchdProviders()` for the\n * file and `caddyProviders()` for the config.\n *\n * const { config, file } = yield* caddyWithFile('caddy', { caddyfile, path: '/usr/local/etc/Caddyfile' });\n */\nexport const caddyWithFile = (id: string, props: CaddyWithFileProps) =>\n Effect.gen(function* () {\n const found = configProblems({ caddyfile: props.caddyfile, sourceFile: props.path });\n if (found.length > 0) {\n return yield* Effect.fail(new Error(`caddyWithFile ${id}: ${found.join('; ')}`));\n }\n const file = yield* HostFile(`${id}-file`, {\n content: props.caddyfile,\n mode: props.mode ?? 0o644,\n path: props.path,\n ...(props.owner === undefined ? {} : { owner: props.owner }),\n ...(props.group === undefined ? {} : { group: props.group }),\n }).pipe(RemovalPolicy.retain());\n const config = yield* CaddyConfig(id, { caddyfile: props.caddyfile, sourceFile: file.path });\n return { config, file };\n });\n",
19
+ "/**\n * `caddyWithFile` — one Caddyfile, applied to the running Caddy AND written to the file Caddy\n * starts from, so a restart keeps it (decision 23: \"the file also lands on disk\").\n *\n * ★ REUSES launchd's HostFile, NOT A SECOND FILE WRITER. The file is an ordinary `Host.File`\n * (atomic write, digest diff, symlink refusal, `--adopt` for a file already there); the config is\n * a `Caddy.Config` whose `sourceFile` is that HostFile's `path` Output — which is what orders them.\n *\n * ⛔ THE ORDER IS FILE, THEN `/load`. The dependency makes Alchemy write the file first, and it is the\n * only order that bootstraps: a new host's Caddy launchd job starts from the file, before any\n * admin API exists to load into. Its cost, and what bounds it:\n * · A Caddyfile that does not ADAPT never reaches disk once the config has state: CaddyConfig's\n * diff adapts it at PLAN time and fails the plan. (On the very first deploy the file's path is\n * still an Output, Alchemy skips the adoption probe, and the check runs at apply instead.)\n * · A Caddyfile that adapts but that Caddy then REFUSES (a port in use, a missing cert file) is\n * on disk when `/load` fails. Caddy keeps serving the old config, the deploy fails with Caddy's\n * reason — and a Caddy started without `--resume` would load the refused file on its next\n * restart. Managed Caddies run with `--resume` (decision, 2026-09-21; docs/caddy.md): they\n * restart from `autosave.json`, the last config Caddy ACCEPTED, and the file is only the\n * first-boot fallback.\n * · The skipped probe is also why the ADOPTION check of a first deploy happens at apply: a Caddy\n * running a config this stack did not load is refused before `/load` unless the deploy runs\n * with `--adopt` (config-lifecycle.ts reconcileConfig) — after this file is written.\n * ⛔ SECRETS ARE REFUSED HERE, AT DECLARATION, before the HostFile exists: its `content` is a prop\n * too, and would carry a literal into state even if the CaddyConfig then refused it.\n * ★ BOTH RETAIN. Removing this from a stack must not delete the file a restart needs.\n */\nimport * as Effect from 'effect/Effect';\nimport * as RemovalPolicy from 'alchemy/RemovalPolicy';\nimport { HostFile } from '../launchd/host-file.ts';\nimport { CaddyConfig } from './config.ts';\nimport { configProblems } from './config-form.ts';\n\nexport interface CaddyWithFileProps {\n /** The whole Caddyfile. ⛔ Never a secret — use `{env.NAME}` / `{file./path}` placeholders. */\n readonly caddyfile: string;\n /** Absolute path Caddy is started with: `caddy run --config <path> --adapter caddyfile`. */\n readonly path: string;\n /** @default 0o644 */\n readonly mode?: number;\n readonly owner?: string | number;\n readonly group?: string | number;\n}\n\n/**\n * Declare the file (`<id>-file`) and the config (`<id>`). Provide `launchdProviders()` for the\n * file and `caddyProviders()` for the config.\n *\n * const { config, file } = yield* caddyWithFile('caddy', { caddyfile, path: '/usr/local/etc/Caddyfile' });\n */\nexport const caddyWithFile = (id: string, props: CaddyWithFileProps) =>\n Effect.gen(function* () {\n const found = configProblems({ caddyfile: props.caddyfile, sourceFile: props.path });\n if (found.length > 0) {\n return yield* Effect.fail(new Error(`caddyWithFile ${id}: ${found.join('; ')}`));\n }\n const file = yield* HostFile(`${id}-file`, {\n content: props.caddyfile,\n mode: props.mode ?? 0o644,\n path: props.path,\n ...(props.owner === undefined ? {} : { owner: props.owner }),\n ...(props.group === undefined ? {} : { group: props.group }),\n }).pipe(RemovalPolicy.retain());\n const config = yield* CaddyConfig(id, { caddyfile: props.caddyfile, sourceFile: file.path });\n return { config, file };\n });\n",
20
20
  "/**\n * `Host.File` — one text file on the host, declared: content, mode, owner, group.\n *\n * - create / update — atomic write (temp file in the same directory, mode and owner set, rename),\n * skipped when the file already matches; then read back and compared.\n * - read — lstat plus the SHA-256 of the bytes.\n * - diff — declared SHA-256 against the stored and on-disk digest, plus mode and owner.\n * - replace — only when `path` changes; create-before-delete.\n * - delete — removes the file.\n *\n * ⛔ NEVER A SECRET — see host-file-form.ts. Secret files stay rendered by openbao-agent.\n * ⚠️ NOTHING IS ADOPTED WITHOUT `--adopt`: a file already at the path reads as `Unowned`.\n * ★ IT LIVES IN THE launchd SUBPATH because the two travel together: a Mac host stack declares a\n * daemon's config file and the daemon that reads it, through the same HostRunner.\n */\nimport { Resource } from 'alchemy';\nimport { Unowned } from 'alchemy/AdoptPolicy';\nimport { isResolved } from 'alchemy/Diff';\nimport * as Provider from 'alchemy/Provider';\nimport * as Effect from 'effect/Effect';\nimport { lift, resolvedString } from './host-effect.ts';\nimport type { HostFileAttributes, HostFileProps } from './host-file-form.ts';\nimport { deleteFile, diffFile, readFileAttributes, reconcileFile } from './host-file-lifecycle.ts';\nimport { HostRunnerService } from './runner.ts';\n\nexport type { HostFileAttributes, HostFileProps } from './host-file-form.ts';\n\nexport interface HostFile extends Resource<'Host.File', HostFileProps, HostFileAttributes> {}\n\nexport const HostFile = Resource<HostFile>('Host.File');\n\nexport const HostFileProvider = () =>\n Provider.effect(\n HostFile,\n Effect.gen(function* () {\n const runner = yield* HostRunnerService;\n return HostFile.Provider.of({\n list: () => Effect.succeed([]),\n\n read: ({ olds, output }) =>\n lift(async () => {\n const found = await readFileAttributes(runner, olds.path);\n if (found === undefined) return undefined;\n return output === undefined ? Unowned(found) : found;\n }),\n\n diff: ({ news, output }) => {\n if (output === undefined) return Effect.succeed(undefined);\n if (isResolved(news)) return lift(() => diffFile(runner, news, output));\n // ⛔ A new path is a replace even while `content` is unresolved (an Output templated in):\n // the engine's default would be an update, which writes the new path and never\n // removes the old one. reconcileFile also defends, for a path that is an Output.\n const path = resolvedString(news, 'path');\n return Effect.succeed(\n path !== undefined && path !== output.path ? { action: 'replace' as const } : undefined,\n );\n },\n\n reconcile: ({ news, output }) => lift(() => reconcileFile(runner, news, output)),\n\n delete: ({ output }) => lift(() => deleteFile(runner, output)),\n });\n }),\n );\n",
21
- "/**\n * HostFile's props, attributes and validation — the pure half of the provider.\n *\n * ⛔ `content` IS NEVER A SECRET. Alchemy stores props unencrypted, so the content of every HostFile\n * sits in the state store and in every plan diff. A secret file stays RENDERED BY A SECRET\n * RENDERER (openbao-agent templates, on this estate) that fetches it at run time; the stack\n * declares only non-secret config, and at most the PATH the renderer writes to.\n * secret-tripwire.ts refuses the obvious case (a PEM private key); nothing can catch all of them.\n */\nimport { fileSecretProblems } from './secret-tripwire.ts';\n\nexport interface HostFileProps {\n /** Absolute path. The parent directory must already exist. */\n path: string;\n /** UTF-8 text. ⛔ Never a secret — see the file header. */\n content: string;\n /** Permission bits. @default 0o644 */\n mode?: number;\n /** User name or numeric uid. Omitted: whoever the runner writes as (a rewrite keeps no owner). */\n owner?: string | number;\n /** Group name or numeric gid. Omitted: the directory's default group. */\n group?: string | number;\n}\n\nexport interface HostFileAttributes {\n path: string;\n /** SHA-256 of the bytes on disk. */\n sha256: string;\n mode: number;\n uid: number;\n gid: number;\n size: number;\n}\n\nexport const DEFAULT_MODE = 0o644;\n\nconst identityProblems = (name: string, value: string | number | undefined): string[] => {\n if (value === undefined) return [];\n if (typeof value === 'number') {\n return Number.isSafeInteger(value) && value >= 0\n ? []\n : [`${name} must be a non-negative integer id`];\n }\n return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) || /^\\d+$/.test(value)\n ? []\n : [`${name} ${JSON.stringify(value)} is not a valid user or group name`];\n};\n\nexport const fileProblems = (props: HostFileProps): string[] => {\n const found: string[] = [];\n const { path } = props;\n if (!path.startsWith('/')) found.push('path must be absolute');\n // ★ One spelling per file: `a/./b`, `a//b` and `a/../b` would each be a second resource for the\n // same inode, and `..` is how a derived path walks out of the directory it was meant for.\n if (\n path.endsWith('/') ||\n path\n .split('/')\n .slice(1)\n .some((part) => part === '' || part === '.' || part === '..')\n ) {\n found.push('path must be normalised (no trailing slash, no empty, \".\" or \"..\" segments)');\n }\n if (path.includes('\\x00')) found.push('path contains NUL');\n const mode = props.mode ?? DEFAULT_MODE;\n if (!Number.isSafeInteger(mode) || mode < 0 || mode > 0o7777) found.push('mode must be 0–0o7777');\n found.push(...identityProblems('owner', props.owner));\n found.push(...identityProblems('group', props.group));\n found.push(...fileSecretProblems(props.content));\n return found;\n};\n",
22
- "/**\n * The one seam between the launchd providers and the host: every file read, file write and\n * `launchctl` call goes through a HostRunner, and nothing in this directory touches the host any\n * other way.\n *\n * ★ WHY A SEAM AND NOT DIRECT node:fs CALLS. launchd has no SDK — the provider renders a plist and\n * drives `launchctl` — so the only way to test the lifecycle without a real launchd (and without\n * writing /Library) is to swap the host out. fake-runner.ts is an in-memory host with a scripted\n * launchd; local-runner.ts is the real one. The same seam is where a consumer plugs in a runner\n * that reaches a host some other way.\n *\n * ★ PROMISES, NOT EFFECTS, ON PURPOSE. A consumer writing their own runner implements plain async\n * functions; the providers lift them into Effect at the call site (host-effect.ts).\n */\nimport * as Context from 'effect/Context';\nimport * as Layer from 'effect/Layer';\n\n/** One finished process. ⛔ `stdout` may carry anything the job's environment holds — never log it. */\nexport type ExecResult = {\n readonly exitCode: number;\n readonly stdout: string;\n readonly stderr: string;\n};\n\n/**\n * What `lstat` says about a path. ⚠️ lstat, not stat: a symlink is reported as a symlink, because\n * writing through one would change a file this resource never named.\n */\nexport type FileStat = {\n readonly kind: 'file' | 'directory' | 'symlink' | 'other';\n /** Permission bits only (`& 0o7777`), never the file-type bits. */\n readonly mode: number;\n readonly uid: number;\n readonly gid: number;\n readonly size: number;\n};\n\nexport type WriteOptions = {\n /** Permission bits for the final file, applied BEFORE it becomes visible at `path`. */\n readonly mode: number;\n /** Numeric owner; omitted means whoever the runner writes as. */\n readonly uid?: number;\n /** Numeric group; omitted means the directory's default. */\n readonly gid?: number;\n};\n\nexport type HostUser = { readonly uid: number; readonly gid: number; readonly home: string };\n\nexport interface HostRunner {\n /**\n * ⛔ DECLARED, NEVER INFERRED. `true` only when every call this runner makes already runs with\n * root's authority — a runner that talks to a root helper, say. The providers refuse\n * system-domain writes unless `effectiveUid()` is 0 or this is `true`, so claiming it falsely\n * turns that refusal into an EACCES halfway through a deploy.\n * ⛔ THE KIT NEVER ELEVATES. No runner here calls `sudo`: a deploy that prompts for a password\n * mid-plan, or silently uses a cached sudo ticket, is exactly the \"no silent sudo\" failure. A\n * consumer who wants elevation writes that runner deliberately and sets this flag.\n */\n readonly privileged: boolean;\n /** The uid file writes and launchctl calls run as. */\n effectiveUid(): number;\n /** Run a program with an argv — ⛔ never through a shell. */\n exec(argv: readonly string[]): Promise<ExecResult>;\n /** File bytes, or `undefined` when nothing is at `path`. */\n readFile(path: string): Promise<Uint8Array | undefined>;\n /** lstat, or `undefined` when nothing is at `path`. */\n stat(path: string): Promise<FileStat | undefined>;\n /**\n * Write `bytes` so that `path` holds either the old file or the new one, never a torn mix:\n * a temporary file in the same directory, mode and owner set on it, then rename(2) over `path`.\n * ⚠️ The parent directory must exist; creating it would invent an owner and mode nobody declared.\n */\n writeFileAtomic(path: string, bytes: Uint8Array, options: WriteOptions): Promise<void>;\n /** Remove a file. Idempotent: nothing at `path` is success. */\n removeFile(path: string): Promise<void>;\n /** A user by name or numeric id, or `undefined` when the host has none. */\n lookupUser(nameOrId: string): Promise<HostUser | undefined>;\n /** A group's gid by name or numeric id, or `undefined` when the host has none. */\n lookupGroup(nameOrId: string): Promise<number | undefined>;\n /** Wait between launchctl polls. ★ Injected so a test never sleeps for real. */\n sleep(ms: number): Promise<void>;\n}\n\n/** The Effect service every launchd provider reads its runner from. */\nexport class HostRunnerService extends Context.Service<HostRunnerService, HostRunner>()(\n 'homeflare/launchd/HostRunner',\n) {}\n\n/** Provide a runner to the providers: `Layer.provide(hostRunnerLayer(localRunner()))`. */\nexport const hostRunnerLayer = (runner: HostRunner): Layer.Layer<HostRunnerService> =>\n Layer.succeed(HostRunnerService, runner);\n\n/** True when this runner may write root-owned paths and drive the system domain. */\nexport const canActAsRoot = (runner: HostRunner): boolean =>\n runner.privileged || runner.effectiveUid() === 0;\n",
23
- "/**\n * HostFile's read / diff / reconcile / delete as plain async functions over a HostRunner, so the\n * lifecycle runs against fake-runner.ts in tests.\n *\n * ⛔ SYMLINKS AND DIRECTORIES ARE REFUSED, NOT REPLACED. rename(2) over a symlink replaces the LINK,\n * so a path that is a symlink (nix-darwin's /etc entries point into /nix/store) would be quietly\n * taken from the tool that owns it — which puts it back, or refuses its next activation. Remove\n * the path from its owner first, then declare it here.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport {\n DEFAULT_MODE,\n type HostFileAttributes,\n type HostFileProps,\n fileProblems,\n} from './host-file-form.ts';\nimport { sha256Hex } from './job-form.ts';\nimport { type HostRunner, canActAsRoot } from './runner.ts';\n\ntype Desired = {\n readonly bytes: Uint8Array;\n readonly sha256: string;\n readonly mode: number;\n readonly uid?: number;\n readonly gid?: number;\n};\n\nconst refuse = (path: string, message: string): Error => new Error(`Host.File ${path}: ${message}`);\n\nconst numericId = (value: string | number): number | undefined =>\n typeof value === 'number' ? value : /^\\d+$/.test(value) ? Number(value) : undefined;\n\n/** Validate, then resolve owner/group names to ids through the runner. */\nexport const desiredFile = async (runner: HostRunner, props: HostFileProps): Promise<Desired> => {\n const found = fileProblems(props);\n if (found.length > 0) throw refuse(props.path, found.join('; '));\n const bytes = new TextEncoder().encode(props.content);\n let uid: number | undefined;\n let gid: number | undefined;\n if (props.owner !== undefined) {\n uid = numericId(props.owner) ?? (await runner.lookupUser(String(props.owner)))?.uid;\n if (uid === undefined) throw refuse(props.path, `no user ${String(props.owner)} on this host`);\n }\n if (props.group !== undefined) {\n gid = numericId(props.group) ?? (await runner.lookupGroup(String(props.group)));\n if (gid === undefined) throw refuse(props.path, `no group ${String(props.group)} on this host`);\n }\n return {\n bytes,\n mode: props.mode ?? DEFAULT_MODE,\n sha256: sha256Hex(bytes),\n ...(uid === undefined ? {} : { uid }),\n ...(gid === undefined ? {} : { gid }),\n };\n};\n\n/** The file as it is now, or `undefined` when nothing is at the path. Non-files read with no digest. */\nexport const readFileAttributes = async (\n runner: HostRunner,\n path: string,\n): Promise<HostFileAttributes | undefined> => {\n const stat = await runner.stat(path);\n if (stat === undefined) return undefined;\n const bytes = stat.kind === 'file' ? await runner.readFile(path) : undefined;\n return {\n gid: stat.gid,\n mode: stat.mode,\n path,\n sha256: bytes === undefined ? '' : sha256Hex(bytes),\n size: stat.size,\n uid: stat.uid,\n };\n};\n\nconst matches = (live: HostFileAttributes, want: Desired): boolean =>\n live.sha256 === want.sha256 &&\n live.mode === want.mode &&\n (want.uid === undefined || live.uid === want.uid) &&\n (want.gid === undefined || live.gid === want.gid);\n\nexport const diffFile = async (\n runner: HostRunner,\n news: HostFileProps,\n output: HostFileAttributes,\n): Promise<Diff> => {\n // ★ Create-before-delete: two paths can hold two files at once, so nothing forces deleteFirst.\n if (news.path !== output.path) return { action: 'replace' };\n const want = await desiredFile(runner, news);\n if (want.sha256 !== output.sha256) return { action: 'update' };\n const live = await readFileAttributes(runner, news.path);\n return live !== undefined && matches(live, want) ? { action: 'noop' } : { action: 'update' };\n};\n\nexport const reconcileFile = async (\n runner: HostRunner,\n props: HostFileProps,\n output?: HostFileAttributes,\n): Promise<HostFileAttributes> => {\n const want = await desiredFile(runner, props);\n // ⛔ NO SILENT SUDO: handing a file to another user is root's call (chown(2)), so say so up front.\n if (want.uid !== undefined && want.uid !== runner.effectiveUid() && !canActAsRoot(runner)) {\n throw refuse(\n props.path,\n `owner ${String(props.owner)} is not the deploying user; only root may chown. Deploy as root ` +\n 'or through a privileged HostRunner (privileged: true). This provider never calls sudo.',\n );\n }\n const stat = await runner.stat(props.path);\n if (stat !== undefined && stat.kind !== 'file') {\n throw refuse(\n props.path,\n `is a ${stat.kind}; remove it from whatever owns it before declaring it here`,\n );\n }\n const before = stat === undefined ? undefined : await readFileAttributes(runner, props.path);\n // ⚠️ An `output` at another path: the engine planned an UPDATE across a move, which it does\n // when diff could not see the new path (host-file.ts). Finish it the way a replace would.\n const moved = output !== undefined && output.path !== props.path;\n const prior = moved ? undefined : output;\n /**\n * ⛔ A FILE THIS RESOURCE DOES NOT OWN IS NEVER OVERWRITTEN. With no prior state for this path,\n * the engine's adoption probe has already refused anything it found — except at the new path of\n * a replace, where it reads nothing. A path typo there would otherwise overwrite, say, a system\n * file that a fresh declaration of the same path would have been refused as `Unowned`.\n */\n if (prior === undefined && before !== undefined && !matches(before, want)) {\n throw refuse(\n props.path,\n 'already exists and is not this resource. Remove it, or declare it as a new resource and ' +\n 'deploy with --adopt.',\n );\n }\n if (before === undefined || !matches(before, want)) {\n await runner.writeFileAtomic(props.path, want.bytes, {\n mode: want.mode,\n ...(want.uid === undefined ? {} : { uid: want.uid }),\n ...(want.gid === undefined ? {} : { gid: want.gid }),\n });\n }\n // ⚠️ READ BACK, never echo the declaration: a umask, an ACL or a runner that ignored `uid` shows\n // up here as a refusal instead of as a forever-`update`.\n const after = await readFileAttributes(runner, props.path);\n if (after === undefined || !matches(after, want)) {\n // ⚠️ Roll back a CREATE: left behind, the next plan's recovery `read` finds a file with no\n // state, reports it `Unowned`, and every later deploy demands --adopt for our own file.\n if (before === undefined) await runner.removeFile(props.path).catch(() => undefined);\n throw refuse(\n props.path,\n 'the write returned but the file on disk does not match the declaration',\n );\n }\n // ★ Create-before-delete, as the replace would have been: the old path goes only once the new\n // one is written and verified.\n if (moved) await deleteFile(runner, output);\n return after;\n};\n\n/** Remove the file. Idempotent. ⛔ Refuses a path that has since become a symlink or directory. */\nexport const deleteFile = async (runner: HostRunner, output: HostFileAttributes): Promise<void> => {\n const stat = await runner.stat(output.path);\n if (stat === undefined) return;\n if (stat.kind !== 'file') throw refuse(output.path, `is now a ${stat.kind}; not removing it`);\n await runner.removeFile(output.path);\n};\n"
21
+ "/**\n * HostFile's props, attributes and validation — the pure half of the provider.\n *\n * ⛔ `content` IS NEVER A SECRET. Alchemy stores props unencrypted, so the content of every HostFile\n * sits in the state store and in every plan diff. A secret file stays RENDERED BY A SECRET\n * RENDERER (openbao-agent templates, on this estate) that fetches it at run time; the stack\n * declares only non-secret config, and at most the PATH the renderer writes to.\n * secret-tripwire.ts refuses the obvious case (a PEM private key); nothing can catch all of them.\n */\nimport { fileSecretProblems } from './secret-tripwire.ts';\n\nexport interface HostFileProps {\n /** Absolute path. The parent directory must already exist. */\n path: string;\n /** UTF-8 text. ⛔ Never a secret — see the file header. */\n content: string;\n /** Permission bits. @default 0o644 */\n mode?: number;\n /** User name or numeric uid. Omitted: whoever the runner writes as (a rewrite keeps no owner). */\n owner?: string | number;\n /** Group name or numeric gid. Omitted: the directory's default group. */\n group?: string | number;\n}\n\nexport interface HostFileAttributes {\n path: string;\n /** SHA-256 of the bytes on disk. */\n sha256: string;\n mode: number;\n uid: number;\n gid: number;\n size: number;\n}\n\nexport const DEFAULT_MODE = 0o644;\n\nconst identityProblems = (name: string, value: string | number | undefined): string[] => {\n if (value === undefined) return [];\n if (typeof value === 'number') {\n return Number.isSafeInteger(value) && value >= 0\n ? []\n : [`${name} must be a non-negative integer id`];\n }\n return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value) || /^\\d+$/.test(value)\n ? []\n : [`${name} ${JSON.stringify(value)} is not a valid user or group name`];\n};\n\n/**\n * An absolute, normalised path, or the reasons it is not. ★ Shared with sudo-allowlist.ts, so a\n * path the sudo runner may touch as root obeys exactly the rule a declared file does.\n */\nexport const pathProblems = (path: string): string[] => {\n const found: string[] = [];\n if (!path.startsWith('/')) found.push('path must be absolute');\n // ★ One spelling per file: `a/./b`, `a//b` and `a/../b` would each be a second resource for the\n // same inode, and `..` is how a derived path walks out of the directory it was meant for.\n if (\n path.endsWith('/') ||\n path\n .split('/')\n .slice(1)\n .some((part) => part === '' || part === '.' || part === '..')\n ) {\n found.push('path must be normalised (no trailing slash, no empty, \".\" or \"..\" segments)');\n }\n if (path.includes('\\x00')) found.push('path contains NUL');\n return found;\n};\n\nexport const fileProblems = (props: HostFileProps): string[] => {\n const found = pathProblems(props.path);\n const mode = props.mode ?? DEFAULT_MODE;\n if (!Number.isSafeInteger(mode) || mode < 0 || mode > 0o7777) found.push('mode must be 0–0o7777');\n found.push(...identityProblems('owner', props.owner));\n found.push(...identityProblems('group', props.group));\n found.push(...fileSecretProblems(props.content));\n return found;\n};\n",
22
+ "/**\n * The one seam between the launchd providers and the host: every file read, file write and\n * `launchctl` call goes through a HostRunner, and nothing in this directory touches the host any\n * other way.\n *\n * ★ WHY A SEAM AND NOT DIRECT node:fs CALLS. launchd has no SDK — the provider renders a plist and\n * drives `launchctl` — so the only way to test the lifecycle without a real launchd (and without\n * writing /Library) is to swap the host out. fake-runner.ts is an in-memory host with a scripted\n * launchd; local-runner.ts is the real one. The same seam is where a consumer plugs in a runner\n * that reaches a host some other way.\n *\n * ★ PROMISES, NOT EFFECTS, ON PURPOSE. A consumer writing their own runner implements plain async\n * functions; the providers lift them into Effect at the call site (host-effect.ts).\n */\nimport * as Context from 'effect/Context';\nimport * as Layer from 'effect/Layer';\n\n/** One finished process. ⛔ `stdout` may carry anything the job's environment holds — never log it. */\nexport type ExecResult = {\n readonly exitCode: number;\n readonly stdout: string;\n readonly stderr: string;\n};\n\n/**\n * What `lstat` says about a path. ⚠️ lstat, not stat: a symlink is reported as a symlink, because\n * writing through one would change a file this resource never named.\n */\nexport type FileStat = {\n readonly kind: 'file' | 'directory' | 'symlink' | 'other';\n /** Permission bits only (`& 0o7777`), never the file-type bits. */\n readonly mode: number;\n readonly uid: number;\n readonly gid: number;\n readonly size: number;\n};\n\nexport type WriteOptions = {\n /** Permission bits for the final file, applied BEFORE it becomes visible at `path`. */\n readonly mode: number;\n /** Numeric owner; omitted means whoever the runner writes as. */\n readonly uid?: number;\n /** Numeric group; omitted means the directory's default. */\n readonly gid?: number;\n};\n\nexport type HostUser = { readonly uid: number; readonly gid: number; readonly home: string };\n\nexport interface HostRunner {\n /**\n * ⛔ DECLARED, NEVER INFERRED. `true` only when this runner can do every root-needing call the\n * providers make of it: every call already runs as root (a runner that talks to a root helper,\n * say), or the runner elevates exactly those calls (sudoRunner). The providers refuse\n * system-domain writes unless `effectiveUid()` is 0 or this is `true`, so claiming it falsely\n * turns that refusal into an EACCES halfway through a deploy.\n * ⛔ THE KIT NEVER ELEVATES SILENTLY. localRunner() never calls `sudo`: a deploy that prompts for\n * a password mid-plan, or quietly borrows a cached sudo ticket, is exactly the \"no silent sudo\"\n * failure. sudoRunner() does call `sudo -n`, but only when a stack passes it explicitly, only\n * for the argv shapes in sudo-allowlist.ts, and logging each one (decided 2026-09-21).\n */\n readonly privileged: boolean;\n /** The uid file writes and launchctl calls run as. */\n effectiveUid(): number;\n /** Run a program with an argv — ⛔ never through a shell. */\n exec(argv: readonly string[]): Promise<ExecResult>;\n /** File bytes, or `undefined` when nothing is at `path`. */\n readFile(path: string): Promise<Uint8Array | undefined>;\n /** lstat, or `undefined` when nothing is at `path`. */\n stat(path: string): Promise<FileStat | undefined>;\n /**\n * Write `bytes` so that `path` holds either the old file or the new one, never a torn mix:\n * a temporary file in the same directory, mode and owner set on it, then rename(2) over `path`.\n * ⚠️ The parent directory must exist; creating it would invent an owner and mode nobody declared.\n */\n writeFileAtomic(path: string, bytes: Uint8Array, options: WriteOptions): Promise<void>;\n /** Remove a file. Idempotent: nothing at `path` is success. */\n removeFile(path: string): Promise<void>;\n /** A user by name or numeric id, or `undefined` when the host has none. */\n lookupUser(nameOrId: string): Promise<HostUser | undefined>;\n /** A group's gid by name or numeric id, or `undefined` when the host has none. */\n lookupGroup(nameOrId: string): Promise<number | undefined>;\n /** Wait between launchctl polls. ★ Injected so a test never sleeps for real. */\n sleep(ms: number): Promise<void>;\n}\n\n/** The Effect service every launchd provider reads its runner from. */\nexport class HostRunnerService extends Context.Service<HostRunnerService, HostRunner>()(\n 'homeflare/launchd/HostRunner',\n) {}\n\n/** Provide a runner to the providers: `Layer.provide(hostRunnerLayer(localRunner()))`. */\nexport const hostRunnerLayer = (runner: HostRunner): Layer.Layer<HostRunnerService> =>\n Layer.succeed(HostRunnerService, runner);\n\n/** True when this runner may write root-owned paths and drive the system domain. */\nexport const canActAsRoot = (runner: HostRunner): boolean =>\n runner.privileged || runner.effectiveUid() === 0;\n",
23
+ "/**\n * HostFile's read / diff / reconcile / delete as plain async functions over a HostRunner, so the\n * lifecycle runs against fake-runner.ts in tests.\n *\n * ⛔ SYMLINKS AND DIRECTORIES ARE REFUSED, NOT REPLACED. rename(2) over a symlink replaces the LINK,\n * so a path that is a symlink (nix-darwin's /etc entries point into /nix/store) would be quietly\n * taken from the tool that owns it — which puts it back, or refuses its next activation. Remove\n * the path from its owner first, then declare it here.\n */\nimport type { Diff } from 'alchemy/Diff';\nimport {\n DEFAULT_MODE,\n type HostFileAttributes,\n type HostFileProps,\n fileProblems,\n} from './host-file-form.ts';\nimport { sha256Hex } from './job-form.ts';\nimport { type HostRunner, canActAsRoot } from './runner.ts';\n\ntype Desired = {\n readonly bytes: Uint8Array;\n readonly sha256: string;\n readonly mode: number;\n readonly uid?: number;\n readonly gid?: number;\n};\n\nconst refuse = (path: string, message: string): Error => new Error(`Host.File ${path}: ${message}`);\n\nconst numericId = (value: string | number): number | undefined =>\n typeof value === 'number' ? value : /^\\d+$/.test(value) ? Number(value) : undefined;\n\n/** Validate, then resolve owner/group names to ids through the runner. */\nexport const desiredFile = async (runner: HostRunner, props: HostFileProps): Promise<Desired> => {\n const found = fileProblems(props);\n if (found.length > 0) throw refuse(props.path, found.join('; '));\n const bytes = new TextEncoder().encode(props.content);\n let uid: number | undefined;\n let gid: number | undefined;\n if (props.owner !== undefined) {\n uid = numericId(props.owner) ?? (await runner.lookupUser(String(props.owner)))?.uid;\n if (uid === undefined) throw refuse(props.path, `no user ${String(props.owner)} on this host`);\n }\n if (props.group !== undefined) {\n gid = numericId(props.group) ?? (await runner.lookupGroup(String(props.group)));\n if (gid === undefined) throw refuse(props.path, `no group ${String(props.group)} on this host`);\n }\n return {\n bytes,\n mode: props.mode ?? DEFAULT_MODE,\n sha256: sha256Hex(bytes),\n ...(uid === undefined ? {} : { uid }),\n ...(gid === undefined ? {} : { gid }),\n };\n};\n\n/** The file as it is now, or `undefined` when nothing is at the path. Non-files read with no digest. */\nexport const readFileAttributes = async (\n runner: HostRunner,\n path: string,\n): Promise<HostFileAttributes | undefined> => {\n const stat = await runner.stat(path);\n if (stat === undefined) return undefined;\n const bytes = stat.kind === 'file' ? await runner.readFile(path) : undefined;\n return {\n gid: stat.gid,\n mode: stat.mode,\n path,\n sha256: bytes === undefined ? '' : sha256Hex(bytes),\n size: stat.size,\n uid: stat.uid,\n };\n};\n\nconst matches = (live: HostFileAttributes, want: Desired): boolean =>\n live.sha256 === want.sha256 &&\n live.mode === want.mode &&\n (want.uid === undefined || live.uid === want.uid) &&\n (want.gid === undefined || live.gid === want.gid);\n\nexport const diffFile = async (\n runner: HostRunner,\n news: HostFileProps,\n output: HostFileAttributes,\n): Promise<Diff> => {\n // ★ Create-before-delete: two paths can hold two files at once, so nothing forces deleteFirst.\n if (news.path !== output.path) return { action: 'replace' };\n const want = await desiredFile(runner, news);\n if (want.sha256 !== output.sha256) return { action: 'update' };\n const live = await readFileAttributes(runner, news.path);\n return live !== undefined && matches(live, want) ? { action: 'noop' } : { action: 'update' };\n};\n\nexport const reconcileFile = async (\n runner: HostRunner,\n props: HostFileProps,\n output?: HostFileAttributes,\n): Promise<HostFileAttributes> => {\n const want = await desiredFile(runner, props);\n // ⛔ NO SILENT SUDO: handing a file to another user is root's call (chown(2)), so say so up front.\n if (want.uid !== undefined && want.uid !== runner.effectiveUid() && !canActAsRoot(runner)) {\n throw refuse(\n props.path,\n `owner ${String(props.owner)} is not the deploying user; only root may chown. Deploy as root ` +\n 'or through a privileged HostRunner (sudoRunner() with this path under a prefix). This ' +\n 'provider never calls sudo itself.',\n );\n }\n const stat = await runner.stat(props.path);\n if (stat !== undefined && stat.kind !== 'file') {\n throw refuse(\n props.path,\n `is a ${stat.kind}; remove it from whatever owns it before declaring it here`,\n );\n }\n const before = stat === undefined ? undefined : await readFileAttributes(runner, props.path);\n // ⚠️ An `output` at another path: the engine planned an UPDATE across a move, which it does\n // when diff could not see the new path (host-file.ts). Finish it the way a replace would.\n const moved = output !== undefined && output.path !== props.path;\n const prior = moved ? undefined : output;\n /**\n * ⛔ A FILE THIS RESOURCE DOES NOT OWN IS NEVER OVERWRITTEN. With no prior state for this path,\n * the engine's adoption probe has already refused anything it found — except at the new path of\n * a replace, where it reads nothing. A path typo there would otherwise overwrite, say, a system\n * file that a fresh declaration of the same path would have been refused as `Unowned`.\n */\n if (prior === undefined && before !== undefined && !matches(before, want)) {\n throw refuse(\n props.path,\n 'already exists and is not this resource. Remove it, or declare it as a new resource and ' +\n 'deploy with --adopt.',\n );\n }\n if (before === undefined || !matches(before, want)) {\n await runner.writeFileAtomic(props.path, want.bytes, {\n mode: want.mode,\n ...(want.uid === undefined ? {} : { uid: want.uid }),\n ...(want.gid === undefined ? {} : { gid: want.gid }),\n });\n }\n // ⚠️ READ BACK, never echo the declaration: a umask, an ACL or a runner that ignored `uid` shows\n // up here as a refusal instead of as a forever-`update`.\n const after = await readFileAttributes(runner, props.path);\n if (after === undefined || !matches(after, want)) {\n // ⚠️ Roll back a CREATE: left behind, the next plan's recovery `read` finds a file with no\n // state, reports it `Unowned`, and every later deploy demands --adopt for our own file.\n if (before === undefined) await runner.removeFile(props.path).catch(() => undefined);\n throw refuse(\n props.path,\n 'the write returned but the file on disk does not match the declaration',\n );\n }\n // ★ Create-before-delete, as the replace would have been: the old path goes only once the new\n // one is written and verified.\n if (moved) await deleteFile(runner, output);\n return after;\n};\n\n/** Remove the file. Idempotent. ⛔ Refuses a path that has since become a symlink or directory. */\nexport const deleteFile = async (runner: HostRunner, output: HostFileAttributes): Promise<void> => {\n const stat = await runner.stat(output.path);\n if (stat === undefined) return;\n if (stat.kind !== 'file') throw refuse(output.path, `is now a ${stat.kind}; not removing it`);\n await runner.removeFile(output.path);\n};\n"
24
24
  ],
25
- "mappings": "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",
26
- "debugId": "205F5FF536DDDFA164756E2164756E21",
25
+ "mappings": "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",
26
+ "debugId": "7B177B132A41844D64756E2164756E21",
27
27
  "names": []
28
28
  }
@@ -14,9 +14,13 @@
14
14
  * still an Output, Alchemy skips the adoption probe, and the check runs at apply instead.)
15
15
  * · A Caddyfile that adapts but that Caddy then REFUSES (a port in use, a missing cert file) is
16
16
  * on disk when `/load` fails. Caddy keeps serving the old config, the deploy fails with Caddy's
17
- * reason — and a RESTART before the fix would load the refused file. Fix and redeploy, or run
18
- * Caddy with `--resume` (docs/caddy.md): it restarts from `autosave.json`, the last config Caddy
19
- * ACCEPTED, and the file is only the first-boot fallback.
17
+ * reason — and a Caddy started without `--resume` would load the refused file on its next
18
+ * restart. Managed Caddies run with `--resume` (decision, 2026-09-21; docs/caddy.md): they
19
+ * restart from `autosave.json`, the last config Caddy ACCEPTED, and the file is only the
20
+ * first-boot fallback.
21
+ * · The skipped probe is also why the ADOPTION check of a first deploy happens at apply: a Caddy
22
+ * running a config this stack did not load is refused before `/load` unless the deploy runs
23
+ * with `--adopt` (config-lifecycle.ts reconcileConfig) — after this file is written.
20
24
  * ⛔ SECRETS ARE REFUSED HERE, AT DECLARATION, before the HostFile exists: its `content` is a prop
21
25
  * too, and would carry a literal into state even if the CaddyConfig then refused it.
22
26
  * ★ BOTH RETAIN. Removing this from a stack must not delete the file a restart needs.
@@ -1 +1 @@
1
- {"version":3,"file":"with-file.d.ts","sourceRoot":"","sources":["../../src/caddy/with-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,MAAM,WAAW,kBAAkB;IACjC,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,OAAQ,MAAM,SAAS,kBAAkB;;;yFAe/D,CAAC"}
1
+ {"version":3,"file":"with-file.d.ts","sourceRoot":"","sources":["../../src/caddy/with-file.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,MAAM,WAAW,kBAAkB;IACjC,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,qBAAqB;IACrB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAClC;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,OAAQ,MAAM,SAAS,kBAAkB;;;yFAe/D,CAAC"}
@@ -0,0 +1,50 @@
1
+ import type { ExecResult, HostRunner } from './runner.ts';
2
+ export declare const OPERATOR = 501;
3
+ export declare const PREFIXES: string[];
4
+ /** What `sudo -n` prints (exit 1) when it will not run the command. Reasoned; see sudo-runner.ts. */
5
+ export declare const SUDO_SAYS: {
6
+ readonly denied: "Sorry, user someone is not allowed to execute '/bin/launchctl bootout system/x' as root on example.\n";
7
+ readonly 'not-in-sudoers': 'someone is not in the sudoers file.\n';
8
+ readonly 'not-on-host': 'someone is not allowed to run sudo on example.\n';
9
+ readonly password: 'sudo: a password is required\n';
10
+ };
11
+ /** `groups` is the operator's `id -G`; `'unknown'` models that lookup failing. */
12
+ export declare const fakeSudoHost: (groups?: readonly number[] | 'unknown', prefixes?: readonly string[]) => {
13
+ fake: {
14
+ calls: string[][];
15
+ disabled: Set<string>;
16
+ dirs: Map<string, number>;
17
+ files: Map<string, {
18
+ bytes: Uint8Array;
19
+ mode: number;
20
+ uid: number;
21
+ gid: number;
22
+ kind: import("./runner.ts").FileStat['kind'];
23
+ }>;
24
+ loaded: Map<string, number | undefined>;
25
+ runner: HostRunner;
26
+ state: {
27
+ lingerPrints: number;
28
+ nextPid: number;
29
+ bootstrapFailure: ExecResult | undefined;
30
+ bootoutFailure: ExecResult | undefined;
31
+ bootoutKeepsJob: boolean;
32
+ loggedOut: Set<number>;
33
+ };
34
+ };
35
+ logs: string[];
36
+ privileged: () => string[][];
37
+ runner: HostRunner;
38
+ staged: Map<string, Uint8Array<ArrayBufferLike>>;
39
+ state: {
40
+ /** What sudo itself answers: runs the command, or refuses it with one of SUDO_SAYS. */
41
+ sudo: 'ok' | keyof typeof SUDO_SAYS;
42
+ /** Returned by `install` in place of success; nothing is written. */
43
+ installFailure: ExecResult | undefined;
44
+ stagedCount: number;
45
+ /** The staged bytes as `install` saw them, by destination. */
46
+ installedFrom: Map<string, string>;
47
+ };
48
+ sudoCalls: string[][];
49
+ };
50
+ //# sourceMappingURL=fake-sudo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-sudo.d.ts","sourceRoot":"","sources":["../../src/launchd/fake-sudo.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAI1D,eAAO,MAAM,QAAQ,MAAM,CAAC;AAC5B,eAAO,MAAM,QAAQ,UAA6C,CAAC;AAInE,qGAAqG;AACrG,eAAO,MAAM,SAAS;aACpB,MAAM,EACJ,uGAAuG;aACzG,gBAAgB,EAAE,uCAAuC;aACzD,aAAa,EAAE,kDAAkD;aACjE,QAAQ,EAAE,gCAAgC;CAClC,CAAC;AAoBX,kFAAkF;AAClF,eAAO,MAAM,YAAY,YACf,SAAS,MAAM,EAAE,GAAG,SAAS,aAC3B,SAAS,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAsBzB,uFAAuF;cACzE,IAAI,GAAG,MAAM,OAAO,SAAS;QAC3C,qEAAqE;wBACxC,UAAU,GAAG,SAAS;;QAEnD,8DAA8D;;;;CAqDjE,CAAC"}
@@ -1,12 +1,3 @@
1
- /**
2
- * The one place a HostRunner's promises become Effects — plus the one probe of an unresolved
3
- * `news` both providers' diffs share.
4
- *
5
- * ★ The lifecycle files are plain async functions over a HostRunner (see runner.ts for why the
6
- * runner is promise-based), so each provider handler is a single `lift(() => step(runner, …))`.
7
- * ⚠️ A thrown Error stays that Error — its message is the refusal a deploy prints. Anything else
8
- * thrown is wrapped, so the failure channel is always an Error with a readable message.
9
- */
10
1
  import * as Effect from 'effect/Effect';
11
2
  export declare const lift: <A>(step: () => Promise<A>) => Effect.Effect<A, Error>;
12
3
  /**
@@ -16,4 +7,22 @@ export declare const lift: <A>(step: () => Promise<A>) => Effect.Effect<A, Error
16
7
  * string is resolved by definition, so `typeof` is the whole test.
17
8
  */
18
9
  export declare const resolvedString: (news: unknown, key: string) => string | undefined;
10
+ /**
11
+ * Whether the resource at `fqn` may be adopted, resolved exactly as the planner resolves it (alchemy
12
+ * beta.79 Plan.ts: `resource.Adopt ?? shouldAdopt`): a resource-scoped `adopt(…)`, else the
13
+ * `AdoptPolicy` service, else `AlchemyContext.adopt`, else off.
14
+ * ★ FOR A CREATE THE ENGINE NEVER PROBED. Alchemy skips the adoption probe while `news` holds an
15
+ * Output, so an ownership check that must still honour `--adopt` can only run at apply. All three
16
+ * reach the apply: Alchemist runs plan AND apply under the session context, which provides the
17
+ * services from the CLI flag and the Stack the resources registered on (Alchemist/Session.ts,
18
+ * routes/stack.ts; Apply.ts reads the same Stack).
19
+ * ⛔ THE RESOURCE-SCOPED SETTING WINS, BOTH WAYS. The engine captures `adopt(…)` on the resource at
20
+ * registration (Resource.ts `Adopt`, into `Stack.resources[fqn]`) and never hands it to a
21
+ * provider. Reading only the deploy-wide flag let `--adopt` take over a resource declared
22
+ * `.pipe(adopt(false))` — which the planner refuses — and refused one declared `adopt(true)`.
23
+ * ⚠️ `resources` carries an `@internal` comment in Alchemy's source, but it is in the published types
24
+ * and the planner reads `Adopt` from it. caddy/adopt-scope.test.ts registers through the real
25
+ * `Resource`, so an upgrade that stops recording it there fails the suite.
26
+ */
27
+ export declare const adoptEnabled: (fqn: string) => Effect.Effect<boolean>;
19
28
  //# sourceMappingURL=host-effect.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"host-effect.d.ts","sourceRoot":"","sources":["../../src/launchd/host-effect.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,eAAO,MAAM,IAAI,GAAI,CAAC,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAInE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,cAAc,SAAU,OAAO,OAAO,MAAM,KAAG,MAAM,GAAG,SAIpE,CAAC"}
1
+ {"version":3,"file":"host-effect.d.ts","sourceRoot":"","sources":["../../src/launchd/host-effect.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAGxC,eAAO,MAAM,IAAI,GAAI,CAAC,QAAQ,MAAM,OAAO,CAAC,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAInE,CAAC;AAEL;;;;;GAKG;AACH,eAAO,MAAM,cAAc,SAAU,OAAO,OAAO,MAAM,KAAG,MAAM,GAAG,SAIpE,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,QAAS,MAAM,KAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAS5D,CAAC"}