@geekmidas/cli 10.0.0-alpha.87 → 10.0.0-alpha.89

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 (180) hide show
  1. package/dist/GoDaddyProvider--36rw8cP.mjs +387 -0
  2. package/dist/GoDaddyProvider--36rw8cP.mjs.map +1 -0
  3. package/dist/GoDaddyProvider-DnRor066.cjs +387 -0
  4. package/dist/GoDaddyProvider-DnRor066.cjs.map +1 -0
  5. package/dist/{HostingerProvider-CVc3rLPB.mjs → HostingerProvider-BI6J7oJr.mjs} +2 -2
  6. package/dist/{HostingerProvider-CVc3rLPB.mjs.map → HostingerProvider-BI6J7oJr.mjs.map} +1 -1
  7. package/dist/{HostingerProvider-BCB1QJj1.cjs → HostingerProvider-CyTpG_jy.cjs} +2 -2
  8. package/dist/{HostingerProvider-BCB1QJj1.cjs.map → HostingerProvider-CyTpG_jy.cjs.map} +1 -1
  9. package/dist/{LegacyStateStore-CWG_HVC0.cjs → LegacyStateStore-BaBoYl59.cjs} +2 -2
  10. package/dist/{LegacyStateStore-CWG_HVC0.cjs.map → LegacyStateStore-BaBoYl59.cjs.map} +1 -1
  11. package/dist/{LegacyStateStore-B0R4LlX_.mjs → LegacyStateStore-CO7bzJCU.mjs} +2 -2
  12. package/dist/{LegacyStateStore-B0R4LlX_.mjs.map → LegacyStateStore-CO7bzJCU.mjs.map} +1 -1
  13. package/dist/{LocalStateStore-CLwa2DpK.cjs → LocalStateStore-CUWh2BWU.cjs} +2 -2
  14. package/dist/{LocalStateStore-CLwa2DpK.cjs.map → LocalStateStore-CUWh2BWU.cjs.map} +1 -1
  15. package/dist/{LocalStateStore-Q4vXg2ua.mjs → LocalStateStore-C_hYDv6I.mjs} +2 -2
  16. package/dist/{LocalStateStore-Q4vXg2ua.mjs.map → LocalStateStore-C_hYDv6I.mjs.map} +1 -1
  17. package/dist/{S3StateStore-Cea9_5gF.cjs → S3StateStore-O8EYfjfG.cjs} +2 -2
  18. package/dist/{S3StateStore-Cea9_5gF.cjs.map → S3StateStore-O8EYfjfG.cjs.map} +1 -1
  19. package/dist/{S3StateStore-CksKYKRa.mjs → S3StateStore-hIm8Rrmu.mjs} +2 -2
  20. package/dist/{S3StateStore-CksKYKRa.mjs.map → S3StateStore-hIm8Rrmu.mjs.map} +1 -1
  21. package/dist/{SSMStateStore-aBOTF018.cjs → SSMStateStore-DIzbiqCz.cjs} +2 -2
  22. package/dist/{SSMStateStore-aBOTF018.cjs.map → SSMStateStore-DIzbiqCz.cjs.map} +1 -1
  23. package/dist/{SSMStateStore-BszSCAS2.mjs → SSMStateStore-IvcjTk8h.mjs} +2 -2
  24. package/dist/{SSMStateStore-BszSCAS2.mjs.map → SSMStateStore-IvcjTk8h.mjs.map} +1 -1
  25. package/dist/SubscriberGenerator-BPqQf8fB.mjs.map +1 -1
  26. package/dist/SubscriberGenerator-BVn7cQ-2.cjs.map +1 -1
  27. package/dist/{add-5MZAXybv.cjs → add-C9qX4Juc.cjs} +30 -7
  28. package/dist/add-C9qX4Juc.cjs.map +1 -0
  29. package/dist/{add-D_jNqnfZ.mjs → add-v3tCbrpE.mjs} +30 -7
  30. package/dist/add-v3tCbrpE.mjs.map +1 -0
  31. package/dist/{bundler-1REMhKps.cjs → bundler-C4NmpRTD.cjs} +5 -5
  32. package/dist/{bundler-1REMhKps.cjs.map → bundler-C4NmpRTD.cjs.map} +1 -1
  33. package/dist/{bundler-BMo-JP5s.mjs → bundler-C_fCgcVO.mjs} +5 -5
  34. package/dist/{bundler-BMo-JP5s.mjs.map → bundler-C_fCgcVO.mjs.map} +1 -1
  35. package/dist/{cli-BL8ZHsLQ.cjs → cli-C85Dut8K.cjs} +6 -5
  36. package/dist/cli-C85Dut8K.cjs.map +1 -0
  37. package/dist/{cli-7edNFwGk.mjs → cli-REdO9X5b.mjs} +7 -6
  38. package/dist/cli-REdO9X5b.mjs.map +1 -0
  39. package/dist/{compose-xw86kVYi.cjs → compose-BvrhDwJ6.cjs} +556 -30
  40. package/dist/compose-BvrhDwJ6.cjs.map +1 -0
  41. package/dist/{compose-DbJhRQlh.mjs → compose-Dn0oAgQ0.mjs} +509 -31
  42. package/dist/compose-Dn0oAgQ0.mjs.map +1 -0
  43. package/dist/{config-DrCAyoH5.d.mts → config-CdEcTrjG.d.mts} +3 -3
  44. package/dist/{config-CpjsVxru.d.cts.map → config-CdEcTrjG.d.mts.map} +1 -1
  45. package/dist/{config-CpjsVxru.d.cts → config-pST7E0CS.d.cts} +3 -3
  46. package/dist/{config-DrCAyoH5.d.mts.map → config-pST7E0CS.d.cts.map} +1 -1
  47. package/dist/config.cjs +1 -1
  48. package/dist/config.d.cts +3 -3
  49. package/dist/config.d.mts +3 -3
  50. package/dist/config.mjs +1 -1
  51. package/dist/{credentials-tr26-KML.cjs → credentials-BKfIugu2.cjs} +47 -2
  52. package/dist/credentials-BKfIugu2.cjs.map +1 -0
  53. package/dist/{credentials-BgJ5mS_d.mjs → credentials-CnVqoXMb.mjs} +30 -3
  54. package/dist/credentials-CnVqoXMb.mjs.map +1 -0
  55. package/dist/databases-BY-pEImp.cjs.map +1 -1
  56. package/dist/databases-CzszQV0N.mjs.map +1 -1
  57. package/dist/deploy/public.cjs +10 -10
  58. package/dist/deploy/public.d.cts +9 -3
  59. package/dist/deploy/public.d.cts.map +1 -1
  60. package/dist/deploy/public.d.mts +9 -3
  61. package/dist/deploy/public.d.mts.map +1 -1
  62. package/dist/deploy/public.mjs +10 -10
  63. package/dist/{deploy-BTfmXd3g.mjs → deploy-BaERRh0Q.mjs} +10 -8
  64. package/dist/deploy-BaERRh0Q.mjs.map +1 -0
  65. package/dist/{deploy-CtbZj4zN.cjs → deploy-DbBGo3a6.cjs} +10 -8
  66. package/dist/deploy-DbBGo3a6.cjs.map +1 -0
  67. package/dist/{dokploy-r69yZaIt.mjs → dokploy-CMmD_ddX.mjs} +2 -2
  68. package/dist/{dokploy-r69yZaIt.mjs.map → dokploy-CMmD_ddX.mjs.map} +1 -1
  69. package/dist/{dokploy-TGk3oPwr.cjs → dokploy-DwAaKA9B.cjs} +2 -2
  70. package/dist/{dokploy-TGk3oPwr.cjs.map → dokploy-DwAaKA9B.cjs.map} +1 -1
  71. package/dist/{encryption-C-8Tt5Hh.mjs → encryption-BQmhq9_y.mjs} +3 -3
  72. package/dist/{encryption-C-8Tt5Hh.mjs.map → encryption-BQmhq9_y.mjs.map} +1 -1
  73. package/dist/{encryption-CDn7BH5O.cjs → encryption-BrN_9_CM.cjs} +3 -3
  74. package/dist/{encryption-CDn7BH5O.cjs.map → encryption-BrN_9_CM.cjs.map} +1 -1
  75. package/dist/{engine-jAINgiOB.mjs → engine-DILocOOH.mjs} +23 -404
  76. package/dist/engine-DILocOOH.mjs.map +1 -0
  77. package/dist/{engine-Bt5k_UMq.cjs → engine-DgHoFBMw.cjs} +25 -406
  78. package/dist/engine-DgHoFBMw.cjs.map +1 -0
  79. package/dist/{github-DFhuJQlp.cjs → github-Bf7pz6AV.cjs} +3 -3
  80. package/dist/{github-DFhuJQlp.cjs.map → github-Bf7pz6AV.cjs.map} +1 -1
  81. package/dist/{github-CZHAtwPT.mjs → github-xzg99wz8.mjs} +3 -3
  82. package/dist/{github-CZHAtwPT.mjs.map → github-xzg99wz8.mjs.map} +1 -1
  83. package/dist/{index-MnUJuUlj.d.mts → index-D-F_hTDz.d.cts} +21 -4
  84. package/dist/index-D-F_hTDz.d.cts.map +1 -0
  85. package/dist/{index-elsLJTUv.d.cts → index-YFNxR5Ns.d.mts} +21 -4
  86. package/dist/index-YFNxR5Ns.d.mts.map +1 -0
  87. package/dist/index.cjs +305 -69
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.mjs +306 -71
  90. package/dist/index.mjs.map +1 -1
  91. package/dist/{local-Cq8qdgNX.cjs → local-BjxDfPvy.cjs} +2 -2
  92. package/dist/{local-Cq8qdgNX.cjs.map → local-BjxDfPvy.cjs.map} +1 -1
  93. package/dist/{local-Ciq-bTPr.mjs → local-IuoIF3CT.mjs} +2 -2
  94. package/dist/{local-Ciq-bTPr.mjs.map → local-IuoIF3CT.mjs.map} +1 -1
  95. package/dist/{migrate-B1rVlwj1.cjs → migrate-7xayVtzL.cjs} +3 -3
  96. package/dist/{migrate-B1rVlwj1.cjs.map → migrate-7xayVtzL.cjs.map} +1 -1
  97. package/dist/{migrate-ggkPVJRC.mjs → migrate-CKLJ73OO.mjs} +4 -4
  98. package/dist/{migrate-ggkPVJRC.mjs.map → migrate-CKLJ73OO.mjs.map} +1 -1
  99. package/dist/{migrate-Dn7nK6sh.mjs → migrate-CpmO5qnl.mjs} +3 -3
  100. package/dist/{migrate-Dn7nK6sh.mjs.map → migrate-CpmO5qnl.mjs.map} +1 -1
  101. package/dist/{migrate-B16qgYTn.cjs → migrate-DPF7ZF8M.cjs} +3 -3
  102. package/dist/{migrate-B16qgYTn.cjs.map → migrate-DPF7ZF8M.cjs.map} +1 -1
  103. package/dist/openapi.cjs +1 -1
  104. package/dist/openapi.d.cts +1 -1
  105. package/dist/openapi.d.mts +1 -1
  106. package/dist/openapi.mjs +1 -1
  107. package/dist/ready-BpNbORRB.cjs.map +1 -1
  108. package/dist/ready-b6vm0-W-.mjs.map +1 -1
  109. package/dist/reconcile/public.d.cts +1 -1
  110. package/dist/reconcile/public.d.mts +1 -1
  111. package/dist/{reconcile--I7V4Klj.mjs → reconcile-Dh1Jk7WE.mjs} +2 -2
  112. package/dist/{reconcile--I7V4Klj.mjs.map → reconcile-Dh1Jk7WE.mjs.map} +1 -1
  113. package/dist/{resolve-BSke45k6.mjs → resolve-Dv_weZoh.mjs} +4 -4
  114. package/dist/{resolve-BSke45k6.mjs.map → resolve-Dv_weZoh.mjs.map} +1 -1
  115. package/dist/{resolve-BXeXzpmU.cjs → resolve-dlKPFNgv.cjs} +4 -4
  116. package/dist/{resolve-BXeXzpmU.cjs.map → resolve-dlKPFNgv.cjs.map} +1 -1
  117. package/dist/{rollback-DFMiBWf0.cjs → rollback-65VVIdBM.cjs} +6 -6
  118. package/dist/{rollback-DFMiBWf0.cjs.map → rollback-65VVIdBM.cjs.map} +1 -1
  119. package/dist/{rollback-CEE6dse4.mjs → rollback-DpSU2DsD.mjs} +6 -6
  120. package/dist/{rollback-CEE6dse4.mjs.map → rollback-DpSU2DsD.mjs.map} +1 -1
  121. package/dist/{runtime-HabiNBtJ.d.mts → runtime-CN_kmqxo.d.cts} +2 -2
  122. package/dist/{runtime-D48ZKk8_.d.cts.map → runtime-CN_kmqxo.d.cts.map} +1 -1
  123. package/dist/{runtime-D48ZKk8_.d.cts → runtime-Cjvon3oj.d.mts} +2 -2
  124. package/dist/{runtime-HabiNBtJ.d.mts.map → runtime-Cjvon3oj.d.mts.map} +1 -1
  125. package/dist/{seeds-CsI0A_Ef.cjs → seeds-CN6tB-va.cjs} +646 -187
  126. package/dist/{seeds-CsI0A_Ef.cjs.map → seeds-CN6tB-va.cjs.map} +1 -1
  127. package/dist/{seeds-BYdrtZsC.mjs → seeds-perzdN81.mjs} +599 -164
  128. package/dist/seeds-perzdN81.mjs.map +1 -0
  129. package/dist/{sniffer-B0EU73bj.mjs → sniffer-BTtX3NUM.mjs} +3 -3
  130. package/dist/{sniffer-B0EU73bj.mjs.map → sniffer-BTtX3NUM.mjs.map} +1 -1
  131. package/dist/{sniffer-DJYupBeK.cjs → sniffer-IAQEpsQb.cjs} +3 -3
  132. package/dist/{sniffer-DJYupBeK.cjs.map → sniffer-IAQEpsQb.cjs.map} +1 -1
  133. package/dist/{sst-DAqUoZKG.cjs → sst-BgcOsaBi.cjs} +3 -3
  134. package/dist/{sst-DAqUoZKG.cjs.map → sst-BgcOsaBi.cjs.map} +1 -1
  135. package/dist/{sst-D1Qj4sIs.mjs → sst-CJzvyrEz.mjs} +3 -3
  136. package/dist/{sst-D1Qj4sIs.mjs.map → sst-CJzvyrEz.mjs.map} +1 -1
  137. package/dist/{stages-Brn3vcdy.cjs → stages-CUn6gvXL.cjs} +2 -2
  138. package/dist/{stages-Brn3vcdy.cjs.map → stages-CUn6gvXL.cjs.map} +1 -1
  139. package/dist/{stages-DoHHz6pS.mjs → stages-CnZRYmkI.mjs} +2 -2
  140. package/dist/{stages-DoHHz6pS.mjs.map → stages-CnZRYmkI.mjs.map} +1 -1
  141. package/dist/target/public.cjs +1 -1
  142. package/dist/target/public.d.cts +2 -2
  143. package/dist/target/public.d.mts +2 -2
  144. package/dist/target/public.mjs +1 -1
  145. package/dist/{terminal-BTbZlrFV.mjs → terminal-D8TPK7o9.mjs} +3 -3
  146. package/dist/{terminal-BTbZlrFV.mjs.map → terminal-D8TPK7o9.mjs.map} +1 -1
  147. package/dist/{terminal-COv23heb.cjs → terminal-DuapRxh6.cjs} +2 -2
  148. package/dist/{terminal-COv23heb.cjs.map → terminal-DuapRxh6.cjs.map} +1 -1
  149. package/dist/{types-0oAyFOfR.d.cts → types-NqX1Hz4P.d.cts} +210 -205
  150. package/dist/types-NqX1Hz4P.d.cts.map +1 -0
  151. package/dist/{types-BZ01tZs-.d.mts → types-ZHuZ89hv.d.mts} +210 -205
  152. package/dist/types-ZHuZ89hv.d.mts.map +1 -0
  153. package/dist/workspace/index.cjs +3 -1
  154. package/dist/workspace/index.d.cts +3 -3
  155. package/dist/workspace/index.d.mts +3 -3
  156. package/dist/workspace/index.mjs +2 -2
  157. package/dist/{workspace-D2CYLMCW.mjs → workspace-CJ0lKFq-.mjs} +231 -10
  158. package/dist/workspace-CJ0lKFq-.mjs.map +1 -0
  159. package/dist/{workspace-CT0JM3Y8.cjs → workspace-CwW3Gkru.cjs} +266 -9
  160. package/dist/workspace-CwW3Gkru.cjs.map +1 -0
  161. package/package.json +15 -15
  162. package/dist/add-5MZAXybv.cjs.map +0 -1
  163. package/dist/add-D_jNqnfZ.mjs.map +0 -1
  164. package/dist/cli-7edNFwGk.mjs.map +0 -1
  165. package/dist/cli-BL8ZHsLQ.cjs.map +0 -1
  166. package/dist/compose-DbJhRQlh.mjs.map +0 -1
  167. package/dist/compose-xw86kVYi.cjs.map +0 -1
  168. package/dist/credentials-BgJ5mS_d.mjs.map +0 -1
  169. package/dist/credentials-tr26-KML.cjs.map +0 -1
  170. package/dist/deploy-BTfmXd3g.mjs.map +0 -1
  171. package/dist/deploy-CtbZj4zN.cjs.map +0 -1
  172. package/dist/engine-Bt5k_UMq.cjs.map +0 -1
  173. package/dist/engine-jAINgiOB.mjs.map +0 -1
  174. package/dist/index-MnUJuUlj.d.mts.map +0 -1
  175. package/dist/index-elsLJTUv.d.cts.map +0 -1
  176. package/dist/seeds-BYdrtZsC.mjs.map +0 -1
  177. package/dist/types-0oAyFOfR.d.cts.map +0 -1
  178. package/dist/types-BZ01tZs-.d.mts.map +0 -1
  179. package/dist/workspace-CT0JM3Y8.cjs.map +0 -1
  180. package/dist/workspace-D2CYLMCW.mjs.map +0 -1
@@ -1,10 +1,10 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { c as runOutput, s as run, v as gkmHome } from "./credentials-BgJ5mS_d.mjs";
2
+ import { c as runOutput, s as run, x as gkmHome } from "./credentials-CnVqoXMb.mjs";
3
3
  import { r as discover } from "./discover-BPelzeGW.mjs";
4
4
  import { t as appKey } from "./derive-B4vDpzgP.mjs";
5
- import { $ as ComposeTlsFileMissing, F as resolveStageTelemetry, L as telemetryEnv, P as otlpTelemetryEnv, R as LOGS_PORT, et as certificateFor, tt as proxyFor } from "./workspace-D2CYLMCW.mjs";
5
+ import { B as LOGS_PORT, I as otlpTelemetryEnv, L as resolveStageTelemetry, at as SERVER_IPV6_KEY, it as SERVER_IPV4_KEY, nt as certificateFor, ot as cnameTarget, rt as proxyFor, st as isReservedStageKey, tt as ComposeTlsFileMissing, z as telemetryEnv } from "./workspace-CJ0lKFq-.mjs";
6
6
  import { t as defineTarget } from "./define-DR8xGYfL.mjs";
7
- import { $ as suppliedOnly, $n as stackLogs, At as postgresStatements, Bt as envFor, C as stageProviderNotes, Dt as applyPostgres, E as appDockerfile, Et as applyPolicies, Ft as pgClient, Gt as WORKER_PORT, Ht as composeBuildPaths, It as appEnvKeys, J as devServicesUsed, Mt as LEGACY_LOCAL_LOGIN, Nt as ensurePostgresLogin, O as ensureDockerignore, Ot as bucketPolicies, Pt as bucketClient, Qn as logsService, Rt as networkEnv, St as withRedisPassword, Tt as applyBuckets, Ut as imageLayout, Vt as localRolePassword, Wn as initStageSecrets, Wt as TURBO_VERSION, Xn as LOGS_SERVICE, Z as reportDevServices, Zn as logsAccess, _t as REDIS_SERVICE, bt as runsRedis, cn as isTelemetryKey, ct as constructGlobs, d as recordRelease, dn as telemetryOf, er as withLogsPassword, et as workerUnits, fn as usesTelemetry, ft as LOCAL_RABBITMQ_USER, gn as portKeys, gt as withGeneratedSecrets, i as assertNoStaleSecrets, in as planFor, j as validateImageRef, k as workerDockerfile, ln as scopeTelemetryEnv, mt as postgresSuperuser, n as reportDatabaseRuns, nn as EMULATOR_ACCESS_KEY_ID, nr as isMainFrontendApp, o as verifyStageProviders, on as hostFor, pn as DEFAULT_IMAGES, pt as loadLocalCredentials, q as assertExternalServices, qt as findBuildRoot, r as reportPlannedSeeds, rr as resolveHost, s as createEmptyState, t as DeploySeedsFailed, tn as composeFor, tr as NoDomainForStage, vn as primaryPortKey, vt as STACK_CACHE, x as assertStageProvidersEnabled, xt as stackRedis, y as DeployJournal, yt as redisService, zn as imageRef, zt as workerEnvKeys } from "./seeds-BYdrtZsC.mjs";
7
+ import { At as applyPolicies, Bt as appEnvKeys, Ct as redisService, Et as withRedisPassword, F as createDnsProvider, Gt as localRolePassword, Ht as networkEnv, It as LEGACY_LOCAL_LOGIN, Jn as initStageSecrets, Jt as TURBO_VERSION, Kt as composeBuildPaths, Lt as ensurePostgresLogin, Mt as bucketPolicies, O as recordRelease, P as DnsRecordsUnreadable, Pt as postgresStatements, Q as devServicesUsed, Rt as bucketClient, S as normalizeDnsConfig, Sn as primaryPortKey, St as STACK_CACHE, T as createEmptyState, Tt as stackRedis, Un as imageRef, Ut as workerEnvKeys, Wt as envFor, Yt as WORKER_PORT, Z as assertExternalServices, Zt as findBuildRoot, _ as workerDockerfile, _n as DEFAULT_IMAGES, _t as loadLocalCredentials, an as composeFor, ar as NoDomainForStage, b as extractSubdomain, bn as portKeys, bt as withGeneratedSecrets, cn as planFor, d as stageProviderNotes, er as LOGS_SERVICE, fn as isTelemetryKey, ft as constructGlobs, g as ensureDockerignore, gn as usesTelemetry, gt as LOCAL_RABBITMQ_USER, hn as telemetryOf, i as assertNoStaleSecrets, ir as withLogsPassword, it as workerUnits, jt as applyPostgres, kt as applyBuckets, l as assertStageProvidersEnabled, m as appDockerfile, n as reportDatabaseRuns, nr as logsService, o as verifyStageProviders, on as EMULATOR_ACCESS_KEY_ID, or as isMainFrontendApp, pn as scopeTelemetryEnv, qt as imageLayout, r as reportPlannedSeeds, rr as stackLogs, rt as suppliedOnly, s as DeployJournal, sr as resolveHost, t as DeploySeedsFailed, tr as logsAccess, tt as reportDevServices, un as hostFor, vt as postgresSuperuser, wt as runsRedis, x as findRootDomain, xt as REDIS_SERVICE, y as validateImageRef, zt as pgClient } from "./seeds-perzdN81.mjs";
8
8
  import { i as DEFAULT_EVENTS } from "./SubscriberGenerator-BPqQf8fB.mjs";
9
9
  import { i as installedFrom } from "./drivers-DTZzxeIl.mjs";
10
10
  import { f as plannedSeeds, o as SeedFailed, p as seedDatabases, u as migrateDatabases } from "./databases-CzszQV0N.mjs";
@@ -14,10 +14,428 @@ import { existsSync } from "node:fs";
14
14
  import { chmod, copyFile, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
15
15
  import { hostname, userInfo } from "node:os";
16
16
  import { CLIENT_IP_HEADER, provideKey, provisionOrder, publicEnvFor } from "@geekmidas/manifest";
17
+ import { isIPv4, isIPv6 } from "node:net";
17
18
  import { spawn } from "node:child_process";
18
19
  import { createHash } from "node:crypto";
19
20
  import { stringify } from "yaml";
21
+ import { lookup } from "node:dns/promises";
20
22
  import { request } from "node:https";
23
+ //#region src/compose/dns.ts
24
+ /**
25
+ * A compose stage's DNS: the records its public hosts need, and the check that
26
+ * they resolve to its server before a certificate is asked for.
27
+ *
28
+ * - **The server** (`serverAddress`): the stage's own secrets hold its public
29
+ * addresses — `GKM_SERVER_IPV4`, and `GKM_SERVER_IPV6` where it has one.
30
+ * Reserved for gkm: never handed to an app.
31
+ * - **The plan** (`planStackDns`, pure): every public host the stack serves —
32
+ * its apex, each app, each file server, the public log UI — pointed at the
33
+ * server. An A record per host (and an AAAA with an IPv6 address), or, where
34
+ * the domain's `dns` entry says `records: { mode: 'cname', target }`, one A
35
+ * record for the target and a CNAME to it for every other host but the
36
+ * apex, which DNS forbids a CNAME at.
37
+ * - **Applying it** (`applyStackDns`): through the domain's provider in `dns`,
38
+ * a record set at a time. A record with the right value is left alone; one
39
+ * with another value is replaced and the change printed. A `manual` domain
40
+ * is printed and nothing else. A dry run reads, prints and writes nothing.
41
+ * - **The check** (`checkStackDns`): each host resolved with the system
42
+ * resolver. One that is not the server stops the deploy with
43
+ * {@link HostNotPointingAtServer}, before Caddy or Traefik ask Let's Encrypt
44
+ * for a certificate it cannot get — and use up the rate limit trying.
45
+ */
46
+ /** The line that says how to set the server's address. */
47
+ function serverAddressHint(stage) {
48
+ return `gkm secrets:set ${SERVER_IPV4_KEY} '<ip>' --stage ${stage}`;
49
+ }
50
+ /** A server address in the stage's secrets that is not an address. */
51
+ var ServerAddressInvalid = class extends Error {
52
+ stage;
53
+ key;
54
+ value;
55
+ constructor(stage, key, value) {
56
+ const ipv4 = key === SERVER_IPV4_KEY;
57
+ super(`${key} in the '${stage}' stage's secrets is ${JSON.stringify(value)}, which is not an ${ipv4 ? "IPv4 address (like 203.0.113.10)" : "IPv6 address (like 2001:db8::10)"}. Set the server's public address: gkm secrets:set ${key} '<ip>' --stage ${stage}.`);
58
+ this.stage = stage;
59
+ this.key = key;
60
+ this.value = value;
61
+ this.name = "ServerAddressInvalid";
62
+ }
63
+ };
64
+ /**
65
+ * A compose stage that serves real domains with no server address: its hosts
66
+ * cannot be pointed or checked, and a certificate would be asked for blind.
67
+ */
68
+ var ServerAddressMissing = class extends Error {
69
+ stage;
70
+ domain;
71
+ constructor(stage, domain) {
72
+ super(`The '${stage}' stage serves ${domain} and its secrets hold no ${SERVER_IPV4_KEY} — the public address of the server its stack runs on, which its hosts' DNS records point at and every deploy checks before asking for certificates. Set it: ${serverAddressHint(stage)} (and ${SERVER_IPV6_KEY} for an IPv6 address).`);
73
+ this.stage = stage;
74
+ this.domain = domain;
75
+ this.name = "ServerAddressMissing";
76
+ }
77
+ };
78
+ /**
79
+ * The server's address from the stage's secrets, checked — `undefined` when
80
+ * `GKM_SERVER_IPV4` is not set.
81
+ *
82
+ * @throws {ServerAddressInvalid} for a value that is not an address
83
+ */
84
+ function serverAddress(stage, custom) {
85
+ const ipv4 = custom?.[SERVER_IPV4_KEY]?.trim();
86
+ const ipv6 = custom?.[SERVER_IPV6_KEY]?.trim();
87
+ if (ipv6 && !isIPv6(ipv6)) throw new ServerAddressInvalid(stage, SERVER_IPV6_KEY, ipv6);
88
+ if (!ipv4) return void 0;
89
+ if (!isIPv4(ipv4)) throw new ServerAddressInvalid(stage, SERVER_IPV4_KEY, ipv4);
90
+ return {
91
+ ipv4,
92
+ ...ipv6 ? { ipv6 } : {}
93
+ };
94
+ }
95
+ /**
96
+ * A name that resolves to this machine by definition — `localhost` and every
97
+ * `*.localhost` — and is in nobody's DNS.
98
+ */
99
+ function isLocalHost(host) {
100
+ const name = host.toLowerCase().replace(/\.$/, "");
101
+ return name === "localhost" || name.endsWith(".localhost");
102
+ }
103
+ /**
104
+ * The server's address, required where the stage serves real domains — not
105
+ * `*.localhost` ones, which point at this machine already.
106
+ *
107
+ * @throws {ServerAddressMissing} when the stage has a domain and no address
108
+ * @throws {ServerAddressInvalid} for a value that is not an address
109
+ */
110
+ function requiredServerAddress(stage, domain, custom) {
111
+ const server = serverAddress(stage, custom);
112
+ if (!server && domain && !isLocalHost(domain)) throw new ServerAddressMissing(stage, domain);
113
+ return server;
114
+ }
115
+ const MANAGED = [
116
+ "A",
117
+ "AAAA",
118
+ "CNAME"
119
+ ];
120
+ /** Every public host the stack serves, once each, sorted. */
121
+ function stackHosts(stack) {
122
+ return [...new Set(stack.routes.map((route) => route.host))].sort();
123
+ }
124
+ /** The TTL a domain's records are written with. */
125
+ function recordTtl(config) {
126
+ const ttl = "ttl" in config ? config.ttl : void 0;
127
+ if (ttl) return ttl;
128
+ return config.provider === "godaddy" ? 600 : 300;
129
+ }
130
+ function providerName(config) {
131
+ return typeof config.provider === "string" ? config.provider : "custom";
132
+ }
133
+ /** The records a stage's hosts need — pure. */
134
+ function planStackDns(input) {
135
+ const { stage, server } = input;
136
+ const dns = input.dns ? normalizeDnsConfig(input.dns) : {};
137
+ const records = /* @__PURE__ */ new Map();
138
+ const uncovered = [];
139
+ const add = (host, domain, type, value) => {
140
+ const name = extractSubdomain(host, domain);
141
+ records.set(`${domain} ${name} ${type}`, {
142
+ host,
143
+ domain,
144
+ name,
145
+ type,
146
+ value,
147
+ ttl: recordTtl(dns[domain])
148
+ });
149
+ };
150
+ const pointAt = (host, domain) => {
151
+ add(host, domain, "A", server.ipv4);
152
+ if (server.ipv6) add(host, domain, "AAAA", server.ipv6);
153
+ };
154
+ for (const raw of input.hosts) {
155
+ const host = raw.toLowerCase();
156
+ const domain = findRootDomain(host, dns);
157
+ if (!domain) {
158
+ uncovered.push(host);
159
+ continue;
160
+ }
161
+ const target = cnameTarget(dns[domain].records, stage);
162
+ if (target) pointAt(target, domain);
163
+ if (target && host !== domain && host !== target) add(host, domain, "CNAME", target);
164
+ else pointAt(host, domain);
165
+ }
166
+ const domains = /* @__PURE__ */ new Map();
167
+ for (const record of [...records.values()].sort((a, b) => a.domain.localeCompare(b.domain) || a.name.localeCompare(b.name) || a.type.localeCompare(b.type))) {
168
+ const config = dns[record.domain];
169
+ const plan = domains.get(record.domain) ?? {
170
+ domain: record.domain,
171
+ provider: providerName(config),
172
+ config,
173
+ records: []
174
+ };
175
+ plan.records.push(record);
176
+ domains.set(record.domain, plan);
177
+ }
178
+ return {
179
+ stage,
180
+ server,
181
+ domains: [...domains.values()],
182
+ uncovered: uncovered.sort()
183
+ };
184
+ }
185
+ /** `api.example.com A 203.0.113.10 (TTL 600)` */
186
+ function describe(record) {
187
+ return `${record.host.padEnd(32)} ${record.type.padEnd(5)} ${record.value} (TTL ${record.ttl})`;
188
+ }
189
+ function printManual(plan, log, why) {
190
+ log(` ${plan.domain}: ${why} — create these records at its DNS host:`);
191
+ for (const record of plan.records) log(` ${describe(record)}`);
192
+ return plan.records.map((r) => ({
193
+ domain: r.domain,
194
+ name: r.name,
195
+ type: r.type,
196
+ action: "manual",
197
+ value: r.value,
198
+ ttl: r.ttl
199
+ }));
200
+ }
201
+ /** The existing records of every planned name, for the types gkm manages. */
202
+ async function existingRecords(provider, plan) {
203
+ const names = [...new Set(plan.records.map((r) => r.name))];
204
+ const wanted = names.flatMap((name) => MANAGED.map((type) => ({
205
+ name,
206
+ type
207
+ })));
208
+ if (provider.readRecords) return provider.readRecords(plan.domain, wanted);
209
+ return (await provider.getRecords(plan.domain)).filter((r) => names.includes(r.name) && MANAGED.includes(r.type));
210
+ }
211
+ const same = (a, b) => a.trim().replace(/\.$/, "").toLowerCase() === b.trim().replace(/\.$/, "").toLowerCase();
212
+ /**
213
+ * What each domain's records need, compared with what is there: creates,
214
+ * updates, and the deletes a change of mode needs — a name moving from an A
215
+ * record to a CNAME, or back, cannot hold both. Nothing else is deleted.
216
+ */
217
+ function diffDomainDns(plan, existing) {
218
+ const changes = [];
219
+ const find = (name, type) => existing.find((r) => r.name === name && r.type === type);
220
+ const planned = /* @__PURE__ */ new Map();
221
+ for (const record of plan.records) {
222
+ const types = planned.get(record.name) ?? /* @__PURE__ */ new Set();
223
+ types.add(record.type);
224
+ planned.set(record.name, types);
225
+ }
226
+ for (const [name, types] of planned) {
227
+ const conflicting = types.has("CNAME") ? ["A", "AAAA"] : ["CNAME"];
228
+ for (const type of conflicting) {
229
+ const found = find(name, type);
230
+ if (!found || found.values.length === 0) continue;
231
+ changes.push({
232
+ domain: plan.domain,
233
+ name,
234
+ type,
235
+ action: "delete",
236
+ previous: found.values.join(", ")
237
+ });
238
+ }
239
+ }
240
+ for (const record of plan.records) {
241
+ const found = find(record.name, record.type);
242
+ const base = {
243
+ domain: plan.domain,
244
+ name: record.name,
245
+ type: record.type,
246
+ value: record.value,
247
+ ttl: record.ttl
248
+ };
249
+ if (!found || found.values.length === 0) changes.push({
250
+ ...base,
251
+ action: "create"
252
+ });
253
+ else if (found.values.length === 1 && same(found.values[0], record.value)) changes.push({
254
+ ...base,
255
+ action: "unchanged"
256
+ });
257
+ else changes.push({
258
+ ...base,
259
+ action: "update",
260
+ previous: found.values.join(", ")
261
+ });
262
+ }
263
+ return changes;
264
+ }
265
+ /** A host's full name from a domain and a relative name. */
266
+ function fullName(domain, name) {
267
+ return name === "@" ? domain : `${name}.${domain}`;
268
+ }
269
+ function printChange(change, dryRun, log) {
270
+ const host = fullName(change.domain, change.name).padEnd(32);
271
+ const type = change.type.padEnd(5);
272
+ const would = dryRun ? "would " : "";
273
+ switch (change.action) {
274
+ case "unchanged":
275
+ log(` ✓ ${host} ${type} ${change.value} (TTL ${change.ttl}) — up to date`);
276
+ break;
277
+ case "create":
278
+ log(` + ${host} ${type} ${change.value} (TTL ${change.ttl}) — ${would}create`);
279
+ break;
280
+ case "update":
281
+ log(` ~ ${host} ${type} ${change.previous} → ${change.value} (TTL ${change.ttl}) — ${would}update`);
282
+ break;
283
+ case "write":
284
+ log(dryRun ? ` ? ${host} ${type} ${change.value} (TTL ${change.ttl}) — would write; current value unknown (key cannot read DNS)` : ` ! ${host} ${type} ${change.value} (TTL ${change.ttl}) — written (not diffed)`);
285
+ break;
286
+ case "delete": log(` - ${host} ${type} ${change.previous} — ${would}delete (a name cannot hold a CNAME and an A record)`);
287
+ }
288
+ }
289
+ /**
290
+ * Each domain's records, through its provider. Returns every change, the
291
+ * unchanged included; a dry run makes none of them.
292
+ */
293
+ async function applyStackDns(plan, options = {}) {
294
+ const dryRun = options.dryRun === true;
295
+ const log = options.log ?? ((line) => console.log(line));
296
+ const providerFor = options.providerFor ?? ((config) => createDnsProvider({
297
+ config,
298
+ ...options.credentials ? { godaddy: options.credentials } : {}
299
+ }));
300
+ const changes = [];
301
+ for (const host of plan.uncovered) log(` ⚠ ${host} is under no domain in dns — point it at ${plan.server.ipv4} yourself`);
302
+ for (const domain of plan.domains) {
303
+ if (domain.provider === "manual") {
304
+ changes.push(...printManual(domain, log, "manual"));
305
+ continue;
306
+ }
307
+ const provider = await providerFor(domain.config);
308
+ if (!provider) {
309
+ changes.push(...printManual(domain, log, "manual"));
310
+ continue;
311
+ }
312
+ log(` ${domain.domain} (${provider.name})${dryRun ? " — dry run" : ""}`);
313
+ let existing;
314
+ let diff;
315
+ try {
316
+ existing = await existingRecords(provider, domain);
317
+ diff = diffDomainDns(domain, existing);
318
+ } catch (error) {
319
+ if (!(error instanceof DnsRecordsUnreadable)) throw error;
320
+ log(` ℹ The ${provider.name} key cannot read ${domain.domain}'s DNS records, so changes cannot be diffed: every record is written (idempotent).`);
321
+ existing = [];
322
+ diff = domain.records.map((r) => ({
323
+ domain: r.domain,
324
+ name: r.name,
325
+ type: r.type,
326
+ action: "write",
327
+ value: r.value,
328
+ ttl: r.ttl
329
+ }));
330
+ }
331
+ for (const change of diff) printChange(change, dryRun, log);
332
+ if (!plan.server.ipv6) for (const name of new Set(domain.records.map((r) => r.name))) {
333
+ const aaaa = existing.find((r) => r.name === name && r.type === "AAAA");
334
+ const planned = domain.records.find((r) => r.name === name && r.type === "A");
335
+ if (aaaa && planned && aaaa.values.length > 0) log(` ⚠ ${fullName(domain.domain, name)} also has AAAA ${aaaa.values.join(", ")}: set ${SERVER_IPV6_KEY} in the '${plan.stage}' stage's secrets to the server's IPv6 address, or remove that record, or IPv6 clients go elsewhere`);
336
+ }
337
+ changes.push(...diff);
338
+ if (dryRun) continue;
339
+ const deletes = diff.filter((c) => c.action === "delete");
340
+ if (deletes.length > 0) await provider.deleteRecords(domain.domain, deletes.map((c) => ({
341
+ name: c.name,
342
+ type: c.type
343
+ })));
344
+ const writes = diff.filter((c) => c.action === "create" || c.action === "update" || c.action === "write");
345
+ if (writes.length > 0) await provider.upsertRecords(domain.domain, writes.map((c) => ({
346
+ name: c.name,
347
+ type: c.type,
348
+ value: c.value,
349
+ ttl: c.ttl
350
+ })));
351
+ }
352
+ return changes;
353
+ }
354
+ /** The system resolver: what the server's own clients would be told. */
355
+ const systemLookup = async (host) => {
356
+ return (await lookup(host, { all: true })).map((a) => a.address);
357
+ };
358
+ /** Hosts that do not resolve to the stage's server. */
359
+ var HostNotPointingAtServer = class extends Error {
360
+ stage;
361
+ hosts;
362
+ expected;
363
+ fix;
364
+ constructor(stage, hosts, expected, fix) {
365
+ const want = expected.ipv6 ? `${expected.ipv4} (and ${expected.ipv6})` : expected.ipv4;
366
+ const lines = hosts.map((h) => ` - ${h.host} → ${h.resolved.length > 0 ? h.resolved.join(", ") : `nothing${h.error ? ` (${h.error})` : ""}`}, expected ${want}`);
367
+ super(`${hosts.length === 1 ? "A host" : `${hosts.length} hosts`} of '${stage}' do${hosts.length === 1 ? "es" : ""} not resolve to its server, so a certificate for ${hosts.length === 1 ? "it" : "them"} cannot be issued:\n${lines.join("\n")}\n${fix}\nRecords can take a few minutes to propagate. With a CDN or proxy in front of the server, pass --skip-dns-check.`);
368
+ this.stage = stage;
369
+ this.hosts = hosts;
370
+ this.expected = expected;
371
+ this.fix = fix;
372
+ this.name = "HostNotPointingAtServer";
373
+ }
374
+ };
375
+ /**
376
+ * Every host resolved: each must resolve to the server's IPv4 address and to
377
+ * nothing but the server's addresses.
378
+ *
379
+ * @throws {HostNotPointingAtServer} naming every host that does not
380
+ */
381
+ async function checkStackDns(input) {
382
+ const lookup = input.lookup ?? systemLookup;
383
+ const { server } = input;
384
+ const ours = new Set([server.ipv4, server.ipv6].filter(Boolean).map((a) => a.toLowerCase()));
385
+ const bad = [];
386
+ const ok = [];
387
+ for (const host of input.hosts) {
388
+ let resolved;
389
+ try {
390
+ resolved = [...new Set(await lookup(host))];
391
+ } catch (error) {
392
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : error instanceof Error ? error.message : String(error);
393
+ bad.push({
394
+ host,
395
+ resolved: [],
396
+ error: code
397
+ });
398
+ continue;
399
+ }
400
+ if (resolved.some((a) => a.toLowerCase() === server.ipv4.toLowerCase()) && resolved.every((a) => ours.has(a.toLowerCase()))) ok.push(host);
401
+ else bad.push({
402
+ host,
403
+ resolved
404
+ });
405
+ }
406
+ if (bad.length > 0) throw new HostNotPointingAtServer(input.stage, bad, {
407
+ ipv4: server.ipv4,
408
+ ...server.ipv6 ? { ipv6: server.ipv6 } : {}
409
+ }, dnsFix(input, bad));
410
+ return ok;
411
+ }
412
+ /** How to fix it: `gkm setup`, or the records to create by hand. */
413
+ function dnsFix(input, bad) {
414
+ const hosts = bad.map((h) => h.host);
415
+ let plan;
416
+ try {
417
+ plan = planStackDns({
418
+ stage: input.stage,
419
+ hosts,
420
+ server: input.server,
421
+ dns: input.dns
422
+ });
423
+ } catch {
424
+ plan = {
425
+ stage: input.stage,
426
+ server: input.server,
427
+ domains: [],
428
+ uncovered: hosts
429
+ };
430
+ }
431
+ const automatic = plan.domains.filter((d) => d.provider !== "manual");
432
+ const byHand = [...plan.domains.filter((d) => d.provider === "manual").flatMap((d) => d.records.map(describe)), ...plan.uncovered.flatMap((host) => [`${host.padEnd(32)} A ${input.server.ipv4}`, ...input.server.ipv6 ? [`${host.padEnd(32)} AAAA ${input.server.ipv6}`] : []])];
433
+ const lines = [];
434
+ if (automatic.length > 0) lines.push(`Fix: gkm setup --stage ${input.stage} writes the records for ${automatic.map((d) => d.domain).join(", ")} (gkm setup --stage ${input.stage} --dry-run shows them first).`);
435
+ if (byHand.length > 0) lines.push(`${automatic.length > 0 ? "And create" : "Fix: create"} these records at the DNS host:`, ...byHand.map((line) => ` ${line}`));
436
+ return lines.join("\n");
437
+ }
438
+ //#endregion
21
439
  //#region src/compose/docker.ts
22
440
  /**
23
441
  * The Docker operations `gkm compose` needs, behind an interface.
@@ -794,6 +1212,36 @@ function pinImages(file, digests, images) {
794
1212
  });
795
1213
  }
796
1214
  //#endregion
1215
+ //#region src/compose/secrets.ts
1216
+ var secrets_exports = /* @__PURE__ */ __exportAll({ deployedStackSecrets: () => deployedStackSecrets });
1217
+ function deployedStackSecrets(workspace, stage, stored, manifest) {
1218
+ const withSeed = withGeneratedSecrets(stored ?? initStageSecrets(stage), manifest);
1219
+ const withLogs = resolveStageTelemetry({
1220
+ ...workspace.deploy?.telemetry ? { telemetry: workspace.deploy.telemetry } : {},
1221
+ stage,
1222
+ local: false,
1223
+ target: "compose",
1224
+ runtime: "server",
1225
+ selfHosted: true,
1226
+ used: usesTelemetry(manifest)
1227
+ })?.provider === "self-hosted" ? withLogsPassword(withSeed.secrets) : {
1228
+ secrets: withSeed.secrets,
1229
+ generated: []
1230
+ };
1231
+ const withRedis = runsRedis(manifest, withLogs.secrets.custom ?? {}) ? withRedisPassword(withLogs.secrets) : {
1232
+ secrets: withLogs.secrets,
1233
+ generated: []
1234
+ };
1235
+ return {
1236
+ secrets: withRedis.secrets,
1237
+ generated: [
1238
+ ...withSeed.generated,
1239
+ ...withLogs.generated,
1240
+ ...withRedis.generated
1241
+ ]
1242
+ };
1243
+ }
1244
+ //#endregion
797
1245
  //#region src/compose/caddyfile.ts
798
1246
  /**
799
1247
  * The stack's own edge, rendered for Caddy: one site block per route.
@@ -878,6 +1326,21 @@ const HEADER = `# Generated by gkm compose from the construct manifest — do no
878
1326
  * address and once on the compose network, because a browser and a sibling
879
1327
  * service reach the same API by different names.
880
1328
  */
1329
+ var stack_exports = /* @__PURE__ */ __exportAll({
1330
+ EDGE_PORT_ENV: () => EDGE_PORT_ENV,
1331
+ EnvValueMultiline: () => EnvValueMultiline,
1332
+ LOGS_PORT_ENV: () => LOGS_PORT_ENV,
1333
+ LOG_ROTATION: () => LOG_ROTATION,
1334
+ LocalCredentialsMissing: () => LocalCredentialsMissing,
1335
+ NothingToCompose: () => NothingToCompose,
1336
+ StageSecretMissing: () => StageSecretMissing,
1337
+ StageSeedMissing: () => StageSeedMissing,
1338
+ composeProject: () => composeProject,
1339
+ composeStack: () => composeStack,
1340
+ derivedCredentials: () => derivedCredentials,
1341
+ envFile: () => envFile,
1342
+ stackDir: () => stackDir
1343
+ });
881
1344
  /** Where a stack's files are written, relative to the workspace root. */
882
1345
  function stackDir(stage) {
883
1346
  return `.gkm/compose/${stage}`;
@@ -1111,7 +1574,7 @@ function composeStack(input) {
1111
1574
  reads.set(app.name, site?.kind === "site" ? Object.values(publicEnvFor(site, manifest)) : [...appEnvKeys(manifest, app.name, input.runnables) ?? []]);
1112
1575
  }
1113
1576
  for (const worker of workers) reads.set(worker.name, [...workerEnvKeys(manifest, worker.id, input.runnables) ?? []]);
1114
- const domain = workspace.deploy?.domains?.[stage];
1577
+ const domain = workspace.domains?.[stage];
1115
1578
  const telemetry = resolveStageTelemetry({
1116
1579
  ...workspace.deploy?.telemetry ? { telemetry: workspace.deploy.telemetry } : {},
1117
1580
  stage,
@@ -1226,6 +1689,7 @@ function composeStack(input) {
1226
1689
  * they can hold it, and the derivations otherwise.
1227
1690
  */
1228
1691
  const valueFor = (app, key, owner) => {
1692
+ if (isReservedStageKey(key)) return void 0;
1229
1693
  if (owner && SERVICE_KINDS[owner.kind]) {
1230
1694
  if (custom[key] !== void 0) return custom[key];
1231
1695
  if (owner.kind === "file-server") return fileServers.get(owner.id)?.url;
@@ -1509,7 +1973,7 @@ function stackWorkers(units, options) {
1509
1973
  */
1510
1974
  function hostOf(name, app, workspace, resource, options) {
1511
1975
  if (options.local) return hostFor(resource, workspace.name);
1512
- return resolveHost(name, app, options.stage, workspace.deploy?.domains, isMainFrontendApp(name, app, workspace.apps));
1976
+ return resolveHost(name, app, options.stage, workspace.domains, isMainFrontendApp(name, app, workspace.apps));
1513
1977
  }
1514
1978
  /** The compose document: the infrastructure, the apps, and the edge. */
1515
1979
  function stackFile(options) {
@@ -1840,7 +2304,8 @@ const defaultDeps = {
1840
2304
  probe: httpsProbe,
1841
2305
  healthAttempts: 90,
1842
2306
  healthIntervalMs: 2e3,
1843
- env: process.env
2307
+ env: process.env,
2308
+ lookup: systemLookup
1844
2309
  };
1845
2310
  /**
1846
2311
  * The stack the stage would run, and — for a tag — the registry's word that
@@ -1865,6 +2330,7 @@ async function validateCompose(ctx, deps, options = {}) {
1865
2330
  background
1866
2331
  });
1867
2332
  const { secrets, generated } = await stageSecrets(ctx, manifest);
2333
+ const server = push || stage === workspace.stages.local ? void 0 : requiredServerAddress(stage, workspace.domains?.[stage], secrets?.custom);
1868
2334
  if (!push) {
1869
2335
  assertNoStaleSecrets({
1870
2336
  manifest,
@@ -1915,6 +2381,7 @@ async function validateCompose(ctx, deps, options = {}) {
1915
2381
  if (stack.tls && !push) {
1916
2382
  for (const file of [stack.tls.certFile, stack.tls.keyFile]) if (!existsSync(file)) throw new ComposeTlsFileMissing(stage, file);
1917
2383
  }
2384
+ if (!push && !stack.local) await checkDns(ctx, deps, stackHosts(stack).filter((host) => !isLocalHost(host)), server);
1918
2385
  if (stack.redis && !stack.local) ctx.secrets.mask(stack.redis.password);
1919
2386
  if (stack.logs && (!stack.local || stack.logs.passwordFromSecrets)) {
1920
2387
  ctx.secrets.mask(stack.logs.password);
@@ -1982,35 +2449,46 @@ function assertRedisClient(workspace, stack) {
1982
2449
  * run goes ahead (`provision`), so a dry run, or a tag that is not there,
1983
2450
  * leaves the stage's secrets as it found them.
1984
2451
  */
2452
+ /**
2453
+ * The deploy-time DNS check: each public host resolved with the system
2454
+ * resolver and compared with the server's address from the stage's secrets.
2455
+ * A dry run warns rather than stops.
2456
+ */
2457
+ async function checkDns(ctx, deps, hosts, server) {
2458
+ const { workspace, stage } = ctx;
2459
+ if (hosts.length === 0) return;
2460
+ if (ctx.skipDnsCheck) {
2461
+ ctx.logger.info("🌐 DNS check skipped (--skip-dns-check)");
2462
+ return;
2463
+ }
2464
+ if (!server) {
2465
+ ctx.logger.info(`🌐 DNS check skipped: no ${SERVER_IPV4_KEY} in '${stage}''s secrets — ${serverAddressHint(stage)}`);
2466
+ return;
2467
+ }
2468
+ try {
2469
+ const ok = await checkStackDns({
2470
+ stage,
2471
+ hosts,
2472
+ server,
2473
+ dns: workspace.dns,
2474
+ lookup: deps.lookup ?? systemLookup
2475
+ });
2476
+ ctx.logger.info(`🌐 ${ok.length} host${ok.length === 1 ? "" : "s"} resolve${ok.length === 1 ? "s" : ""} to ${server.ipv4}`);
2477
+ } catch (error) {
2478
+ if (ctx.dryRun && error instanceof HostNotPointingAtServer) {
2479
+ ctx.logger.warn(`⚠ ${error.message}`);
2480
+ return;
2481
+ }
2482
+ throw error;
2483
+ }
2484
+ }
1985
2485
  async function stageSecrets(ctx, manifest) {
1986
2486
  const stored = await ctx.secrets.read();
1987
2487
  if (ctx.stage === ctx.workspace.stages.local) return {
1988
2488
  secrets: stored,
1989
2489
  generated: []
1990
2490
  };
1991
- const withSeed = withGeneratedSecrets(stored ?? initStageSecrets(ctx.stage), manifest);
1992
- const withLogs = resolveStageTelemetry({
1993
- ...ctx.workspace.deploy?.telemetry ? { telemetry: ctx.workspace.deploy.telemetry } : {},
1994
- stage: ctx.stage,
1995
- local: false,
1996
- target: "compose",
1997
- runtime: "server",
1998
- selfHosted: true,
1999
- used: usesTelemetry(manifest)
2000
- })?.provider === "self-hosted" ? withLogsPassword(withSeed.secrets) : {
2001
- secrets: withSeed.secrets,
2002
- generated: []
2003
- };
2004
- const withRedis = runsRedis(manifest, withLogs.secrets.custom ?? {}) ? withRedisPassword(withLogs.secrets) : {
2005
- secrets: withLogs.secrets,
2006
- generated: []
2007
- };
2008
- const secrets = withRedis.secrets;
2009
- const generated = [
2010
- ...withSeed.generated,
2011
- ...withLogs.generated,
2012
- ...withRedis.generated
2013
- ];
2491
+ const { secrets, generated } = deployedStackSecrets(ctx.workspace, ctx.stage, stored, manifest);
2014
2492
  if (secrets.seed) ctx.secrets.mask(secrets.seed);
2015
2493
  for (const value of Object.values(secrets.custom ?? {})) ctx.secrets.mask(value);
2016
2494
  return {
@@ -2657,6 +3135,6 @@ function composeTarget(options = {}) {
2657
3135
  });
2658
3136
  }
2659
3137
  //#endregion
2660
- export { stackOverrideFile as a, parseDigests as c, removeEdgeRoutes as d, EDGE_PROJECT as f, ComposePushNeedsBuild as i, pinnedRef as l, compose_exports as n, composeProject as o, dockerCompose as p, ComposePinNeedsPull as r, stackDir as s, composeTarget as t, edgeDir as u };
3138
+ export { planStackDns as _, stackOverrideFile as a, serverAddressHint as b, stack_exports as c, pinnedRef as d, edgeDir as f, applyStackDns as g, dockerCompose as h, ComposePushNeedsBuild as i, secrets_exports as l, EDGE_PROJECT as m, compose_exports as n, composeProject as o, removeEdgeRoutes as p, ComposePinNeedsPull as r, stackDir as s, composeTarget as t, parseDigests as u, requiredServerAddress as v, stackHosts as x, serverAddress as y };
2661
3139
 
2662
- //# sourceMappingURL=compose-DbJhRQlh.mjs.map
3140
+ //# sourceMappingURL=compose-Dn0oAgQ0.mjs.map