@geekmidas/cli 10.0.0-alpha.122 → 10.0.0-alpha.124
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.
- package/dist/{EndpointGenerator-BDa6pPgE.cjs → EndpointGenerator-B2MUa4M2.cjs} +2 -2
- package/dist/{EndpointGenerator-BDa6pPgE.cjs.map → EndpointGenerator-B2MUa4M2.cjs.map} +1 -1
- package/dist/{EndpointGenerator-CP_z_E3e.mjs → EndpointGenerator-Z_BUTocT.mjs} +2 -2
- package/dist/{EndpointGenerator-CP_z_E3e.mjs.map → EndpointGenerator-Z_BUTocT.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-BJ9j9mR7.cjs → GoDaddyProvider-0z5RAN1k.cjs} +3 -3
- package/dist/{GoDaddyProvider-BJ9j9mR7.cjs.map → GoDaddyProvider-0z5RAN1k.cjs.map} +1 -1
- package/dist/{GoDaddyProvider-wIKYdm7V.mjs → GoDaddyProvider-55x4o-4z.mjs} +3 -3
- package/dist/{GoDaddyProvider-wIKYdm7V.mjs.map → GoDaddyProvider-55x4o-4z.mjs.map} +1 -1
- package/dist/{HostingerProvider-BNpS9aRJ.mjs → HostingerProvider-Cjh_2Ml6.mjs} +2 -2
- package/dist/{HostingerProvider-BNpS9aRJ.mjs.map → HostingerProvider-Cjh_2Ml6.mjs.map} +1 -1
- package/dist/{HostingerProvider-DR1iws1P.cjs → HostingerProvider-OdQmKXE3.cjs} +2 -2
- package/dist/{HostingerProvider-DR1iws1P.cjs.map → HostingerProvider-OdQmKXE3.cjs.map} +1 -1
- package/dist/{LegacyStateStore-CkXUJP7i.mjs → LegacyStateStore-C-vumKSN.mjs} +3 -3
- package/dist/{LegacyStateStore-CkXUJP7i.mjs.map → LegacyStateStore-C-vumKSN.mjs.map} +1 -1
- package/dist/{LegacyStateStore-D1S87I_B.cjs → LegacyStateStore-DRpB-yC5.cjs} +3 -3
- package/dist/{LegacyStateStore-D1S87I_B.cjs.map → LegacyStateStore-DRpB-yC5.cjs.map} +1 -1
- package/dist/{LocalStateStore-DmXfpvu4.cjs → LocalStateStore-C3ummuDT.cjs} +2 -2
- package/dist/{LocalStateStore-DmXfpvu4.cjs.map → LocalStateStore-C3ummuDT.cjs.map} +1 -1
- package/dist/{LocalStateStore-BWoWeSq0.mjs → LocalStateStore-CtOjpV1w.mjs} +2 -2
- package/dist/{LocalStateStore-BWoWeSq0.mjs.map → LocalStateStore-CtOjpV1w.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-CsrukCE-.mjs → OpenApiTsGenerator-MtnMQL_g.mjs} +2 -2
- package/dist/{OpenApiTsGenerator-CsrukCE-.mjs.map → OpenApiTsGenerator-MtnMQL_g.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs → OpenApiTsGenerator-t3_E98Fi.cjs} +2 -2
- package/dist/{OpenApiTsGenerator-FNhY7Ssz.cjs.map → OpenApiTsGenerator-t3_E98Fi.cjs.map} +1 -1
- package/dist/{S3StateStore-D19JSGE9.mjs → S3StateStore-BC4Jm3Tw.mjs} +2 -2
- package/dist/{S3StateStore-D19JSGE9.mjs.map → S3StateStore-BC4Jm3Tw.mjs.map} +1 -1
- package/dist/{S3StateStore-BEdsjjP4.cjs → S3StateStore-BeArp0aF.cjs} +2 -2
- package/dist/{S3StateStore-BEdsjjP4.cjs.map → S3StateStore-BeArp0aF.cjs.map} +1 -1
- package/dist/{SSMStateStore-7xKw0Zz9.mjs → SSMStateStore-Crl76aYZ.mjs} +2 -2
- package/dist/{SSMStateStore-7xKw0Zz9.mjs.map → SSMStateStore-Crl76aYZ.mjs.map} +1 -1
- package/dist/{SSMStateStore--2bcxoiH.cjs → SSMStateStore-DcstR6cW.cjs} +2 -2
- package/dist/{SSMStateStore--2bcxoiH.cjs.map → SSMStateStore-DcstR6cW.cjs.map} +1 -1
- package/dist/{SubscriberGenerator-C8CPxrCK.cjs → SubscriberGenerator-C3QTae-R.cjs} +237 -54
- package/dist/SubscriberGenerator-C3QTae-R.cjs.map +1 -0
- package/dist/{SubscriberGenerator-y3pncae_.mjs → SubscriberGenerator-COAVPgM-.mjs} +227 -56
- package/dist/SubscriberGenerator-COAVPgM-.mjs.map +1 -0
- package/dist/{add-DlFUKdjX.cjs → add-BFjb6mMq.cjs} +7 -7
- package/dist/{add-DlFUKdjX.cjs.map → add-BFjb6mMq.cjs.map} +1 -1
- package/dist/{add-Bu6FkaTn.mjs → add-CIjmgzkb.mjs} +6 -6
- package/dist/{add-Bu6FkaTn.mjs.map → add-CIjmgzkb.mjs.map} +1 -1
- package/dist/{bundler-Bg0lPDoi.cjs → bundler-CiUoIsku.cjs} +5 -5
- package/dist/{bundler-Bg0lPDoi.cjs.map → bundler-CiUoIsku.cjs.map} +1 -1
- package/dist/{bundler-CgE5XTyr.mjs → bundler-D6KFNpQk.mjs} +5 -5
- package/dist/{bundler-CgE5XTyr.mjs.map → bundler-D6KFNpQk.mjs.map} +1 -1
- package/dist/{cli-z5S3bHyK.mjs → cli-DNtSTtMc.mjs} +4 -4
- package/dist/{cli-z5S3bHyK.mjs.map → cli-DNtSTtMc.mjs.map} +1 -1
- package/dist/{cli-Cvjlnpon.cjs → cli-r4eUr98C.cjs} +4 -4
- package/dist/{cli-Cvjlnpon.cjs.map → cli-r4eUr98C.cjs.map} +1 -1
- package/dist/{compose-bKLAtHLj.cjs → compose-BfXXWJSp.cjs} +362 -59
- package/dist/compose-BfXXWJSp.cjs.map +1 -0
- package/dist/{compose-CgBPA44v.mjs → compose-DJvvmF3X.mjs} +336 -33
- package/dist/compose-DJvvmF3X.mjs.map +1 -0
- package/dist/{config-D48erO2j.d.mts → config-IgK6YaGp.d.cts} +3 -4
- package/dist/config-IgK6YaGp.d.cts.map +1 -0
- package/dist/{config-DHDtsQrB.d.cts → config-kE-p5hxq.d.mts} +3 -4
- package/dist/config-kE-p5hxq.d.mts.map +1 -0
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +2 -2
- package/dist/{credentialSchemas-B67WJOOm.cjs → credentialSchemas-AhIBuQxQ.cjs} +2 -2
- package/dist/{credentialSchemas-B67WJOOm.cjs.map → credentialSchemas-AhIBuQxQ.cjs.map} +1 -1
- package/dist/{credentialSchemas-DDOLRCLF.mjs → credentialSchemas-DLKbxYBy.mjs} +2 -2
- package/dist/{credentialSchemas-DDOLRCLF.mjs.map → credentialSchemas-DLKbxYBy.mjs.map} +1 -1
- package/dist/{credentials-Zqr3QQYB.cjs → credentials-D7XJypNd.cjs} +173 -3
- package/dist/credentials-D7XJypNd.cjs.map +1 -0
- package/dist/{credentials-B5N5yKk6.mjs → credentials-DpfnUvIR.mjs} +132 -4
- package/dist/credentials-DpfnUvIR.mjs.map +1 -0
- package/dist/deploy/public.cjs +16 -17
- package/dist/deploy/public.cjs.map +1 -1
- package/dist/deploy/public.d.cts +3 -5
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +3 -5
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +11 -12
- package/dist/deploy/public.mjs.map +1 -1
- package/dist/{deploy-CI85F6dr.cjs → deploy-BbI1UMh3.cjs} +477 -156
- package/dist/deploy-BbI1UMh3.cjs.map +1 -0
- package/dist/{deploy-Ddy8UBoE.mjs → deploy-CiRYwFmr.mjs} +456 -117
- package/dist/deploy-CiRYwFmr.mjs.map +1 -0
- package/dist/{discover-C5roJuc9.mjs → discover-BvJOVsYG.mjs} +66 -14
- package/dist/discover-BvJOVsYG.mjs.map +1 -0
- package/dist/{discover-DozUKs8f.cjs → discover-ojkcbxHw.cjs} +88 -12
- package/dist/discover-ojkcbxHw.cjs.map +1 -0
- package/dist/{dokploy-CiozgdKO.cjs → dokploy-C0aQrCgs.cjs} +2 -2
- package/dist/{dokploy-CiozgdKO.cjs.map → dokploy-C0aQrCgs.cjs.map} +1 -1
- package/dist/{dokploy-Boj1NSHV.mjs → dokploy-MM7c3IE0.mjs} +2 -2
- package/dist/{dokploy-Boj1NSHV.mjs.map → dokploy-MM7c3IE0.mjs.map} +1 -1
- package/dist/{drivers-DbjiYpW6.cjs → drivers-CQ32PB5w.cjs} +2 -2
- package/dist/{drivers-DbjiYpW6.cjs.map → drivers-CQ32PB5w.cjs.map} +1 -1
- package/dist/{drivers-BjGDYfnC.mjs → drivers-Chb5Akov.mjs} +2 -2
- package/dist/{drivers-BjGDYfnC.mjs.map → drivers-Chb5Akov.mjs.map} +1 -1
- package/dist/{eas-DCg41jKK.mjs → eas-1Ee6kql0.mjs} +2 -2
- package/dist/{eas-DCg41jKK.mjs.map → eas-1Ee6kql0.mjs.map} +1 -1
- package/dist/{eas-BmBjIFf_.cjs → eas-CJoys9yh.cjs} +2 -2
- package/dist/{eas-BmBjIFf_.cjs.map → eas-CJoys9yh.cjs.map} +1 -1
- package/dist/{emit-C1e8qD08.mjs → emit-CNpuV_6Q.mjs} +17 -1
- package/dist/emit-CNpuV_6Q.mjs.map +1 -0
- package/dist/{emit-Dmc1nvdS.cjs → emit-qvHxMV9b.cjs} +17 -1
- package/dist/emit-qvHxMV9b.cjs.map +1 -0
- package/dist/{encryption-QOQbGXgV.mjs → encryption-B8Bk1Mvu.mjs} +3 -3
- package/dist/{encryption-QOQbGXgV.mjs.map → encryption-B8Bk1Mvu.mjs.map} +1 -1
- package/dist/{encryption-CwLWV2ig.cjs → encryption-Bo9VlRnl.cjs} +3 -3
- package/dist/{encryption-CwLWV2ig.cjs.map → encryption-Bo9VlRnl.cjs.map} +1 -1
- package/dist/{engine-HVqo3mQu.mjs → engine-BuinpJfS.mjs} +7 -8
- package/dist/{engine-HVqo3mQu.mjs.map → engine-BuinpJfS.mjs.map} +1 -1
- package/dist/{engine-D7YNY3v7.cjs → engine-esxTxcIx.cjs} +10 -11
- package/dist/{engine-D7YNY3v7.cjs.map → engine-esxTxcIx.cjs.map} +1 -1
- package/dist/{github-CO8W9SDs.cjs → github-BEA4K3_X.cjs} +80 -47
- package/dist/github-BEA4K3_X.cjs.map +1 -0
- package/dist/{github-CQt3xc3o.mjs → github-BUlKJfOV.mjs} +78 -45
- package/dist/github-BUlKJfOV.mjs.map +1 -0
- package/dist/{googlePlay-Cfu17aEp.mjs → googlePlay-CK05QiTq.mjs} +4 -4
- package/dist/{googlePlay-Cfu17aEp.mjs.map → googlePlay-CK05QiTq.mjs.map} +1 -1
- package/dist/{googlePlay-DSuF6jbF.cjs → googlePlay-CiqAS2ma.cjs} +5 -5
- package/dist/{googlePlay-DSuF6jbF.cjs.map → googlePlay-CiqAS2ma.cjs.map} +1 -1
- package/dist/{index-8OfpOtSI.d.cts → index-4CO0HD27.d.cts} +2 -2
- package/dist/{index-8OfpOtSI.d.cts.map → index-4CO0HD27.d.cts.map} +1 -1
- package/dist/{index-Dpji6Bxq.d.mts → index-DhUf0B0m.d.mts} +2 -2
- package/dist/{index-Dpji6Bxq.d.mts.map → index-DhUf0B0m.d.mts.map} +1 -1
- package/dist/index.cjs +81 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +64 -65
- package/dist/index.mjs.map +1 -1
- package/dist/{local-BwC2v2Y8.mjs → local-B7Zv1NoA.mjs} +2 -2
- package/dist/{local-BwC2v2Y8.mjs.map → local-B7Zv1NoA.mjs.map} +1 -1
- package/dist/{local-DNhnkugT.cjs → local-OPC9BCZt.cjs} +3 -3
- package/dist/{local-DNhnkugT.cjs.map → local-OPC9BCZt.cjs.map} +1 -1
- package/dist/{migrate-CGkir5LM.mjs → migrate-BzCHT9qs.mjs} +3 -3
- package/dist/{migrate-CGkir5LM.mjs.map → migrate-BzCHT9qs.mjs.map} +1 -1
- package/dist/{migrate-CXsMaHbb.cjs → migrate-C-hmbjpG.cjs} +3 -3
- package/dist/{migrate-CXsMaHbb.cjs.map → migrate-C-hmbjpG.cjs.map} +1 -1
- package/dist/{migrate-Bkz8BGaZ.mjs → migrate-CnbvrVag.mjs} +4 -4
- package/dist/{migrate-Bkz8BGaZ.mjs.map → migrate-CnbvrVag.mjs.map} +1 -1
- package/dist/{migrate-dbAI2XF8.cjs → migrate-DeQJBexh.cjs} +4 -4
- package/dist/{migrate-dbAI2XF8.cjs.map → migrate-DeQJBexh.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{publish-eUcWvSdP.mjs → publish-BISBrrqR.mjs} +2 -2
- package/dist/{publish-eUcWvSdP.mjs.map → publish-BISBrrqR.mjs.map} +1 -1
- package/dist/{publish-Bbd-2PI2.cjs → publish-DtSIB-hZ.cjs} +2 -2
- package/dist/{publish-Bbd-2PI2.cjs.map → publish-DtSIB-hZ.cjs.map} +1 -1
- package/dist/{ready-BOfogqzr.mjs → ready-DqsyadOs.mjs} +6 -6
- package/dist/{ready-BOfogqzr.mjs.map → ready-DqsyadOs.mjs.map} +1 -1
- package/dist/{ready-BFFP838V.cjs → ready-IVGu-q18.cjs} +6 -6
- package/dist/{ready-BFFP838V.cjs.map → ready-IVGu-q18.cjs.map} +1 -1
- package/dist/reconcile/public.cjs +2 -2
- package/dist/reconcile/public.d.cts +1 -2
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +1 -2
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +2 -2
- package/dist/{resolve-DSilYpzm.mjs → resolve-BT7fkPN5.mjs} +4 -4
- package/dist/{resolve-DSilYpzm.mjs.map → resolve-BT7fkPN5.mjs.map} +1 -1
- package/dist/{resolve-CyprRcQo.cjs → resolve-C7GKQcE4.cjs} +4 -4
- package/dist/{resolve-CyprRcQo.cjs.map → resolve-C7GKQcE4.cjs.map} +1 -1
- package/dist/{rollback-DfwsnwGO.cjs → rollback-BEiB-Pje.cjs} +7 -7
- package/dist/{rollback-DfwsnwGO.cjs.map → rollback-BEiB-Pje.cjs.map} +1 -1
- package/dist/{rollback-C4ri3h7i.mjs → rollback-DbP0KuDw.mjs} +7 -7
- package/dist/{rollback-C4ri3h7i.mjs.map → rollback-DbP0KuDw.mjs.map} +1 -1
- package/dist/{runtime-ACdfK59n.d.cts → runtime-BFJQNqwd.d.mts} +2 -2
- package/dist/{runtime-ACdfK59n.d.cts.map → runtime-BFJQNqwd.d.mts.map} +1 -1
- package/dist/{runtime-YyQuekxi.d.mts → runtime-BJO-FFef.d.cts} +2 -2
- package/dist/{runtime-YyQuekxi.d.mts.map → runtime-BJO-FFef.d.cts.map} +1 -1
- package/dist/{s3-DQXZdmCM.mjs → s3-B4FTQQkV.mjs} +2 -2
- package/dist/{s3-DQXZdmCM.mjs.map → s3-B4FTQQkV.mjs.map} +1 -1
- package/dist/{s3-CrPoA89T.cjs → s3-DwKLXwUT.cjs} +2 -2
- package/dist/{s3-CrPoA89T.cjs.map → s3-DwKLXwUT.cjs.map} +1 -1
- package/dist/sandbox/credentials-worker.cjs +2 -2
- package/dist/sandbox/credentials-worker.mjs +2 -2
- package/dist/sandbox/discover-worker.cjs +1 -1
- package/dist/sandbox/discover-worker.mjs +1 -1
- package/dist/sandbox/migrate-worker.cjs +1 -1
- package/dist/sandbox/migrate-worker.mjs +1 -1
- package/dist/{seeds-CFG-f24B.cjs → seeds-DOfxmDRO.cjs} +362 -34
- package/dist/seeds-DOfxmDRO.cjs.map +1 -0
- package/dist/{seeds-oY0qDqui.mjs → seeds-rG-Evfz9.mjs} +341 -31
- package/dist/seeds-rG-Evfz9.mjs.map +1 -0
- package/dist/{serverSetup-CQZt0OT6.cjs → serverSetup-CWWVcU6Y.cjs} +6 -6
- package/dist/{serverSetup-CQZt0OT6.cjs.map → serverSetup-CWWVcU6Y.cjs.map} +1 -1
- package/dist/{serverSetup-D8nngcNL.mjs → serverSetup-xGw1fxuZ.mjs} +6 -6
- package/dist/{serverSetup-D8nngcNL.mjs.map → serverSetup-xGw1fxuZ.mjs.map} +1 -1
- package/dist/{sniffer-BAmtXzou.cjs → sniffer-C7O9Pna_.cjs} +9 -9
- package/dist/{sniffer-BAmtXzou.cjs.map → sniffer-C7O9Pna_.cjs.map} +1 -1
- package/dist/{sniffer-Cgx608Tl.mjs → sniffer-DVPgfyHr.mjs} +9 -9
- package/dist/{sniffer-Cgx608Tl.mjs.map → sniffer-DVPgfyHr.mjs.map} +1 -1
- package/dist/{dns-CfEZHt7r.cjs → snsSubscriptions-CSkteK2J.cjs} +520 -3
- package/dist/snsSubscriptions-CSkteK2J.cjs.map +1 -0
- package/dist/{dns-BgxvvcQa.mjs → snsSubscriptions-CvBIGGp0.mjs} +431 -6
- package/dist/snsSubscriptions-CvBIGGp0.mjs.map +1 -0
- package/dist/{sst-C1pxbFEd.mjs → sst-CTqZZl1_.mjs} +3 -4
- package/dist/{sst-C1pxbFEd.mjs.map → sst-CTqZZl1_.mjs.map} +1 -1
- package/dist/{sst-B-Uz4cvm.cjs → sst-lEkBwzGc.cjs} +4 -5
- package/dist/{sst-B-Uz4cvm.cjs.map → sst-lEkBwzGc.cjs.map} +1 -1
- package/dist/{stage-BKDdQm65.mjs → stage-2EKpzPQ1.mjs} +6 -6
- package/dist/{stage-BKDdQm65.mjs.map → stage-2EKpzPQ1.mjs.map} +1 -1
- package/dist/{stage-CJysBXWB.cjs → stage-fRfFtLwj.cjs} +7 -7
- package/dist/{stage-CJysBXWB.cjs.map → stage-fRfFtLwj.cjs.map} +1 -1
- package/dist/{stages-D4SnG5yl.cjs → stages-B7mCWI_R.cjs} +2 -2
- package/dist/{stages-D4SnG5yl.cjs.map → stages-B7mCWI_R.cjs.map} +1 -1
- package/dist/{stages-Cb9rqRbN.mjs → stages-DSltTW2d.mjs} +2 -2
- package/dist/{stages-Cb9rqRbN.mjs.map → stages-DSltTW2d.mjs.map} +1 -1
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{terminal-CW_dWeTe.mjs → terminal-BAVrDhhN.mjs} +3 -3
- package/dist/{terminal-CW_dWeTe.mjs.map → terminal-BAVrDhhN.mjs.map} +1 -1
- package/dist/{terminal-DJlqcW_U.cjs → terminal-D1aeTkgv.cjs} +3 -3
- package/dist/{terminal-DJlqcW_U.cjs.map → terminal-D1aeTkgv.cjs.map} +1 -1
- package/dist/{types-HqVPddRK.d.mts → types-CFU2uvrz.d.cts} +263 -4
- package/dist/types-CFU2uvrz.d.cts.map +1 -0
- package/dist/{types-B6UXlEkf.d.cts → types-D3BeGucy.d.mts} +262 -5
- package/dist/types-D3BeGucy.d.mts.map +1 -0
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-BwqQnool.cjs → workspace-C82GLPnf.cjs} +8 -2
- package/dist/workspace-C82GLPnf.cjs.map +1 -0
- package/dist/{workspace-D1QbNd55.mjs → workspace-DUnnVjqC.mjs} +8 -2
- package/dist/workspace-DUnnVjqC.mjs.map +1 -0
- package/package.json +17 -16
- package/dist/SubscriberGenerator-C8CPxrCK.cjs.map +0 -1
- package/dist/SubscriberGenerator-y3pncae_.mjs.map +0 -1
- package/dist/compose-CgBPA44v.mjs.map +0 -1
- package/dist/compose-bKLAtHLj.cjs.map +0 -1
- package/dist/config-D48erO2j.d.mts.map +0 -1
- package/dist/config-DHDtsQrB.d.cts.map +0 -1
- package/dist/credentials-B5N5yKk6.mjs.map +0 -1
- package/dist/credentials-Zqr3QQYB.cjs.map +0 -1
- package/dist/deploy-CI85F6dr.cjs.map +0 -1
- package/dist/deploy-Ddy8UBoE.mjs.map +0 -1
- package/dist/discover-C5roJuc9.mjs.map +0 -1
- package/dist/discover-CSW_BMWa.d.cts +0 -125
- package/dist/discover-CSW_BMWa.d.cts.map +0 -1
- package/dist/discover-DozUKs8f.cjs.map +0 -1
- package/dist/discover-xtT7fgWg.d.mts +0 -125
- package/dist/discover-xtT7fgWg.d.mts.map +0 -1
- package/dist/dns-BgxvvcQa.mjs.map +0 -1
- package/dist/dns-CfEZHt7r.cjs.map +0 -1
- package/dist/emit-C1e8qD08.mjs.map +0 -1
- package/dist/emit-Dmc1nvdS.cjs.map +0 -1
- package/dist/github-CO8W9SDs.cjs.map +0 -1
- package/dist/github-CQt3xc3o.mjs.map +0 -1
- package/dist/run-BJLIerMM.mjs +0 -133
- package/dist/run-BJLIerMM.mjs.map +0 -1
- package/dist/run-fVZ-rLnc.cjs +0 -174
- package/dist/run-fVZ-rLnc.cjs.map +0 -1
- package/dist/sandbox-C4g1eG31.d.mts +0 -104
- package/dist/sandbox-C4g1eG31.d.mts.map +0 -1
- package/dist/sandbox-CdKgQ3XU.d.cts +0 -104
- package/dist/sandbox-CdKgQ3XU.d.cts.map +0 -1
- package/dist/seeds-CFG-f24B.cjs.map +0 -1
- package/dist/seeds-oY0qDqui.mjs.map +0 -1
- package/dist/types-B6UXlEkf.d.cts.map +0 -1
- package/dist/types-HqVPddRK.d.mts.map +0 -1
- package/dist/workspace-BwqQnool.cjs.map +0 -1
- package/dist/workspace-D1QbNd55.mjs.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dns-CfEZHt7r.cjs","names":["checkStageBackups","kebabCase","GkmError","postgresRoleSlug","dirname","fileURLToPath","join","existsSync","readFileSync","createRequire","DEFAULT_POSTGRES_VERSION","postgresImage","createHash","localRolePassword","quoteIdentifier","SERVER_IPV4_KEY","GkmError","SERVER_IPV6_KEY","isIPv6","isIPv4","normalizeDnsConfig","extractSubdomain","findRootDomain","cnameTarget","createDnsProvider","DnsRecordsUnreadable","dnsLookup"],"sources":["../src/providers/dns.ts","../src/backups/config.ts","../src/backups/service.ts","../src/compose/dns.ts"],"sourcesContent":["/**\n * What the deploy's DNS step needs to know about a workspace before its\n * stack is run: whether it deploys with compose.\n *\n * The step itself is `stageDns` in `compose/dns.ts`, run by every deploy\n * (`gkm deploy`, and `gkm compose`, the same run) where the deploy runs — a\n * CI runner or a developer's machine, which hold the DNS provider's token.\n * The server never does.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\n/** Whether the workspace deploys through the compose target. */\nexport function deploysWithCompose(workspace: NormalizedWorkspace): boolean {\n\treturn (\n\t\tworkspace.deploy?.default === 'compose' ||\n\t\tObject.values(workspace.apps).some(\n\t\t\t(app) => app.resolvedDeployTarget === 'compose',\n\t\t)\n\t);\n}\n","/**\n * Which stages take backups, and where they are kept.\n *\n * A deployed compose stage whose stack runs Postgres is backed up by\n * default — every day at 02:00 UTC, kept 30 days — unless\n * `deploy.backups.<stage>` says otherwise. The local stage never is.\n *\n * Every backup is kept in the project bucket (`providers/projectBucket.ts`),\n * beside the stage's deploy state, one folder per run and one file per\n * database:\n *\n * ```\n * gkm/<project>/<stage>/backups/2026-10-10/02-00-00Z/database.sql.gz\n * gkm/<project>/<stage>/backups/2026-10-10/02-00-00Z/auth-database.sql.gz\n * ```\n *\n * The file is named after the database's construct, the way its migrations\n * folder is: `AuthDatabase` is `db/auth-database`, and `auth-database.sql.gz`.\n */\n\nimport { type ConstructManifest, kebabCase } from '@geekmidas/manifest';\nimport { deploysWithCompose } from '../providers/dns.js';\nimport type { PrefixExpiry } from '../providers/projectBucket.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { checkStageBackups, type StageBackups } from './schedule.js';\n\n/** What a stage's backups come to. */\nexport type StageBackupsChoice =\n\t| {\n\t\t\tmode: 'on';\n\t\t\tbackups: StageBackups;\n\t\t\t/** Whether the stage named it, or took the default. */\n\t\t\tsource: 'config' | 'default';\n\t }\n\t/** `deploy.backups.<stage>: false`. */\n\t| { mode: 'disabled' }\n\t/** Nothing to back up, or nowhere it applies. */\n\t| { mode: 'none'; reason: 'local' | 'not-compose' | 'no-postgres' };\n\n/** The workspace parts {@link stageBackups} reads. */\nexport type BackupsWorkspace = Pick<\n\tNormalizedWorkspace,\n\t'name' | 'apps' | 'deploy' | 'stages' | 'state'\n>;\n\n/** Whether the stack runs Postgres: some construct is a database. */\nexport function runsPostgres(manifest: ConstructManifest): boolean {\n\treturn Object.values(manifest).some((d) => d.kind === 'database');\n}\n\n/**\n * What `stage`'s backups are: on — by its entry or the default — off by\n * `false`, or none, for the local stage, a stage compose does not deploy,\n * and a stack with no database.\n */\nexport function stageBackups(\n\tworkspace: BackupsWorkspace,\n\tmanifest: ConstructManifest,\n\tstage: string,\n): StageBackupsChoice {\n\tif (stage === workspace.stages?.local) {\n\t\treturn { mode: 'none', reason: 'local' };\n\t}\n\tif (!deploysWithCompose(workspace as NormalizedWorkspace)) {\n\t\treturn { mode: 'none', reason: 'not-compose' };\n\t}\n\tif (!runsPostgres(manifest)) return { mode: 'none', reason: 'no-postgres' };\n\tconst value = (\n\t\tworkspace.deploy?.backups as Record<string, unknown> | undefined\n\t)?.[stage];\n\tconst checked = checkStageBackups(stage, value ?? {});\n\tif (checked === false) return { mode: 'disabled' };\n\treturn {\n\t\tmode: 'on',\n\t\tbackups: checked,\n\t\tsource: value === undefined ? 'default' : 'config',\n\t};\n}\n\n/**\n * Where a stage's backups are, in the project bucket — under the same\n * `<prefix>/<project>/<stage>` its deploy state is: `gkm/shop/production/backups`.\n */\nexport function backupsPrefix(\n\tworkspace: Pick<NormalizedWorkspace, 'name' | 'state'>,\n\tstage: string,\n): string {\n\tconst state = workspace.state;\n\tconst prefix =\n\t\tstate?.provider === 's3' && state.prefix !== undefined\n\t\t\t? state.prefix.replace(/\\/+$/, '')\n\t\t\t: 'gkm';\n\treturn `${prefix ? `${prefix}/` : ''}${workspace.name}/${stage}/backups`;\n}\n\n/** One run's folder: `2026-10-10/02-00-00Z`, for the time it started. */\nexport function runFolder(at: Date): string {\n\tconst iso = at.toISOString();\n\treturn `${iso.slice(0, 10)}/${iso.slice(11, 19).replace(/:/g, '-')}Z`;\n}\n\n/** A run folder's time back, or `undefined` for a key that is not one. */\nexport function runTime(folder: string): Date | undefined {\n\tconst match = /^(\\d{4}-\\d{2}-\\d{2})\\/(\\d{2})-(\\d{2})-(\\d{2})Z$/.exec(folder);\n\tif (!match) return undefined;\n\tconst at = new Date(`${match[1]}T${match[2]}:${match[3]}:${match[4]}Z`);\n\treturn Number.isNaN(at.getTime()) ? undefined : at;\n}\n\n/** One database the stack backs up. */\nexport interface BackupDatabase {\n\t/** Its construct: `AuthDatabase`. */\n\tid: string;\n\t/** Its file in a run's folder, less `.sql.gz`: `auth-database`. */\n\tfile: string;\n\t/** Its name in the stack's Postgres: `auth_database_production`. */\n\tname: string;\n}\n\n/** A database's file in a run's folder: `auth-database.sql.gz`. */\nexport function backupFile(database: Pick<BackupDatabase, 'file'>): string {\n\treturn `${database.file}.sql.gz`;\n}\n\n/** Each database the stack runs, by its construct and its name in Postgres. */\nexport function backupDatabases(\n\tresources: readonly { id: string; kind: string; name: string }[],\n): BackupDatabase[] {\n\treturn resources\n\t\t.filter((r) => r.kind === 'database')\n\t\t.map((r) => ({ id: r.id, file: kebabCase(r.id), name: r.name }))\n\t\t.sort((a, b) => a.file.localeCompare(b.file));\n}\n\n/**\n * The expiry of every stage's backups, by each stage's own entry — what the\n * project bucket's lifecycle carries beside its own rule.\n *\n * A put replaces a bucket's whole lifecycle, so this is built from every\n * deployed stage each time, never from the stage being deployed alone: one\n * stage's deploy keeps every other's rule. A stage set to `false` has none,\n * and its backups are kept until someone deletes them.\n */\nexport function backupExpiries(\n\tworkspace: BackupsWorkspace,\n\tmanifest: ConstructManifest,\n): PrefixExpiry[] {\n\tconst expiries: PrefixExpiry[] = [];\n\tfor (const stage of [...(workspace.stages?.deployed ?? [])].sort()) {\n\t\tconst choice = stageBackups(workspace, manifest, stage);\n\t\tif (choice.mode !== 'on') continue;\n\t\texpiries.push({\n\t\t\tid: `gkm-backups-${stage}`,\n\t\t\tprefix: `${backupsPrefix(workspace, stage)}/`,\n\t\t\tdays: choice.backups.keepDays,\n\t\t\tabortIncompleteDays: 1,\n\t\t});\n\t}\n\treturn expiries;\n}\n","/**\n * The stack's `backups` service: what a deployed compose stage with Postgres\n * runs beside its apps, unless `deploy.backups.<stage>` is `false`.\n *\n * - **The image** is built from the stack's own Postgres image —\n * `postgres:<major>-alpine`, already on the server for the stack, and with\n * the `pg_dump` that matches the server — with Node added and gkm's runner\n * (`runner.ts`) copied in. Nothing is pulled that the stack does not have,\n * and nothing is mounted from the host: the build context is two files.\n * - **Its key** is `BACKUPS_URL`, from the stage's secrets, like any app's:\n * allowed to put objects under the stage's backups prefix and nothing else.\n * The server holds no AWS credentials of its own.\n * - **Its login** is a role of its own, `<project>_backups`, granted\n * `pg_read_all_data` — it can read every database and write none. Postgres\n * before 14 has no such role, and there it signs in as the stack's\n * superuser.\n * - **Its health** is the last good run: unhealthy once that is more than\n * twice the schedule's longest gap old, after a startup grace.\n */\n\nimport { createHash } from 'node:crypto';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { DEFAULT_POSTGRES_VERSION } from '@geekmidas/manifest';\nimport { GkmError } from '../errors';\nimport { postgresImage } from '../reconcile/containers.js';\nimport { localRolePassword } from '../reconcile/env.js';\nimport { postgresRoleSlug } from '../reconcile/localCredentials.js';\nimport type { Plan } from '../reconcile/plan.js';\nimport { quoteIdentifier, type Statement } from '../reconcile/provision.js';\nimport {\n\ttype BackupDatabase,\n\ttype BackupsWorkspace,\n\tbackupDatabases,\n\tbackupsPrefix,\n} from './config.js';\nimport type { StageBackups } from './schedule.js';\n\n/** The service's name in the stack. */\nexport const BACKUPS_SERVICE = 'backups';\n\n/** The secret its key is written to. */\nexport const BACKUPS_URL_KEY = 'BACKUPS_URL';\n\n/** Where the runner is, in the image. */\nexport const RUNNER_PATH = '/gkm/backup.mjs';\n\n/** The first Postgres with `pg_read_all_data`. */\nconst READ_ALL_DATA_SINCE = 14;\n\n/** A stage that backs up, and no key to back up with. */\nexport class BackupsUrlMissing extends GkmError {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`'${stage}' runs Postgres, so its stack backs it up, and its secrets ` +\n\t\t\t\t`hold no ${BACKUPS_URL_KEY} to back up with. The deploy writes it — ` +\n\t\t\t\t\"the IAM user and key it creates in the stage's AWS account — so \" +\n\t\t\t\t'run the deploy with the ' +\n\t\t\t\t\"account's credentials. To take no backups on this stage, set \" +\n\t\t\t\t`deploy.backups.${stage}: false.`,\n\t\t);\n\t\tthis.name = 'BackupsUrlMissing';\n\t}\n}\n\n/** The backups role: `<project>_backups`, as the superuser is `<project>_admin`. */\nexport function backupsRole(project: string): string {\n\treturn `${postgresRoleSlug(project)}_backups`;\n}\n\n/** The stack's backups, as its compose file and env file have them. */\nexport interface StackBackups {\n\tbackups: StageBackups;\n\tdatabases: BackupDatabase[];\n\t/** `gkm/<project>/<stage>/backups`. */\n\tprefix: string;\n\t/** The Postgres image it is built from. */\n\tfrom: string;\n\t/** `<project>-backups:pg18-<hash>`: built on the server, never pushed. */\n\timage: string;\n\t/** Its build context: the Dockerfile and the runner. */\n\tfiles: { Dockerfile: string; 'backup.mjs': string };\n\t/** What it signs in to Postgres as. */\n\tlogin: { user: string; password: string };\n\t/** Whether that is its own read-only role (else the superuser). */\n\treadOnly: boolean;\n\t/** Its environment — empty in a stack that only builds. */\n\tenv: Record<string, string>;\n}\n\n/**\n * The runner's source, as JavaScript: the built `backups/runner.mjs` beside\n * the CLI's bundle, or — run from source — `runner.ts`, with its types\n * stripped by esbuild.\n */\nexport function runnerSource(): string {\n\tconst here = dirname(fileURLToPath(import.meta.url));\n\tfor (const path of [\n\t\tjoin(here, 'backups', 'runner.mjs'),\n\t\tjoin(here, 'runner.mjs'),\n\t\tjoin(here, '..', 'backups', 'runner.mjs'),\n\t]) {\n\t\tif (existsSync(path)) return readFileSync(path, 'utf-8');\n\t}\n\tconst { transformSync } = createRequire(import.meta.url)(\n\t\t'esbuild',\n\t) as typeof import('esbuild');\n\treturn transformSync(readFileSync(join(here, 'runner.ts'), 'utf-8'), {\n\t\tloader: 'ts',\n\t\tformat: 'esm',\n\t\ttarget: 'node22',\n\t}).code;\n}\n\n/** The image's Dockerfile: the stack's Postgres, Node, and the runner. */\nexport function backupsDockerfile(from: string): string {\n\treturn `# Generated by gkm — the stage's backups: pg_dump from ${from}, and gkm's uploader.\nFROM ${from}\nRUN apk add --no-cache nodejs\nCOPY backup.mjs ${RUNNER_PATH}\nUSER postgres\nENTRYPOINT []\nCMD [\"node\", \"${RUNNER_PATH}\", \"serve\"]\n`;\n}\n\nexport interface StackBackupsInput {\n\tworkspace: BackupsWorkspace;\n\tstage: string;\n\tproject: string;\n\tplan: Plan;\n\tbackups: StageBackups;\n\t/** The stage's secrets, by key. */\n\tcustom: Readonly<Record<string, string>>;\n\tseed: string;\n\t/** The stack's superuser. */\n\tsuperuser: { user: string; password: string };\n\t/** What the stage's telemetry hands a process, when it is on. */\n\ttelemetry?: Readonly<Record<string, string>>;\n\t/** The stack only builds: no environment, no key needed. */\n\tbuildOnly?: boolean;\n}\n\n/**\n * The stack's backups service, for a stage whose backups are on.\n *\n * @throws {BackupsUrlMissing} when the stage's secrets hold no key\n */\nexport function stackBackups(input: StackBackupsInput): StackBackups {\n\tconst { plan, stage } = input;\n\tconst version =\n\t\tplan.resources.find((r) => r.version)?.version ?? DEFAULT_POSTGRES_VERSION;\n\tconst from = postgresImage(version);\n\tconst script = runnerSource();\n\tconst dockerfile = backupsDockerfile(from);\n\tconst hash = createHash('sha256')\n\t\t.update(dockerfile)\n\t\t.update(script)\n\t\t.digest('hex')\n\t\t.slice(0, 12);\n\n\tconst readOnly = version >= READ_ALL_DATA_SINCE;\n\tconst role = backupsRole(input.workspace.name);\n\tconst login = readOnly\n\t\t? {\n\t\t\t\tuser: role,\n\t\t\t\tpassword: localRolePassword(\n\t\t\t\t\tinput.workspace.name,\n\t\t\t\t\tplan,\n\t\t\t\t\trole,\n\t\t\t\t\tinput.seed,\n\t\t\t\t),\n\t\t\t}\n\t\t: input.superuser;\n\n\tconst databases = backupDatabases(plan.resources);\n\tconst prefix = backupsPrefix(input.workspace, stage);\n\tconst { kind, ...schedule } = input.backups.schedule;\n\n\tlet env: Record<string, string> = {};\n\tif (!input.buildOnly) {\n\t\tconst url = input.custom[BACKUPS_URL_KEY];\n\t\tif (!url) throw new BackupsUrlMissing(stage);\n\t\tenv = {\n\t\t\tBACKUPS_URL: url,\n\t\t\tBACKUPS_PREFIX: prefix,\n\t\t\tBACKUPS_DATABASES: JSON.stringify(\n\t\t\t\tdatabases.map(({ file, name }) => ({ file, name })),\n\t\t\t),\n\t\t\tBACKUPS_SCHEDULE: JSON.stringify(\n\t\t\t\tkind === 'cron'\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tkind,\n\t\t\t\t\t\t\tminutes: input.backups.schedule.minutes,\n\t\t\t\t\t\t\thours: input.backups.schedule.hours,\n\t\t\t\t\t\t\tdays: input.backups.schedule.days,\n\t\t\t\t\t\t\tmonths: input.backups.schedule.months,\n\t\t\t\t\t\t\tweekdays: input.backups.schedule.weekdays,\n\t\t\t\t\t\t}\n\t\t\t\t\t: { kind, ...schedule },\n\t\t\t),\n\t\t\tBACKUPS_MAX_GAP_SECONDS: String(input.backups.maxGapSeconds),\n\t\t\tPGHOST: 'postgres',\n\t\t\tPGPORT: '5432',\n\t\t\tPGUSER: login.user,\n\t\t\tPGPASSWORD: login.password,\n\t\t\t...(input.telemetry?.OTEL_EXPORTER_OTLP_ENDPOINT\n\t\t\t\t? {\n\t\t\t\t\t\tOTEL_EXPORTER_OTLP_ENDPOINT:\n\t\t\t\t\t\t\tinput.telemetry.OTEL_EXPORTER_OTLP_ENDPOINT,\n\t\t\t\t\t\t...(input.telemetry.OTEL_EXPORTER_OTLP_HEADERS\n\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\tOTEL_EXPORTER_OTLP_HEADERS:\n\t\t\t\t\t\t\t\t\t\tinput.telemetry.OTEL_EXPORTER_OTLP_HEADERS,\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\tOTEL_SERVICE_NAME: BACKUPS_SERVICE,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t};\n\t}\n\n\treturn {\n\t\tbackups: input.backups,\n\t\tdatabases,\n\t\tprefix,\n\t\tfrom,\n\t\timage: `${input.project}-backups:pg${version}-${hash}`,\n\t\tfiles: { Dockerfile: dockerfile, 'backup.mjs': script },\n\t\tlogin,\n\t\treadOnly,\n\t\tenv,\n\t};\n}\n\n/** The service in the stack's compose file. */\nexport function backupsService(backups: StackBackups, envFiles: boolean) {\n\treturn {\n\t\timage: backups.image,\n\t\t// Built on the server from two files; there is nothing to pull.\n\t\tbuild: { context: `./${BACKUPS_SERVICE}`, dockerfile: 'Dockerfile' },\n\t\tpull_policy: 'build' as const,\n\t\trestart: 'unless-stopped',\n\t\t...(envFiles\n\t\t\t? {\n\t\t\t\t\tenv_file: [\n\t\t\t\t\t\t{ path: `./${BACKUPS_SERVICE}.env`, format: 'raw' as const },\n\t\t\t\t\t],\n\t\t\t\t}\n\t\t\t: {}),\n\t\tdepends_on: { postgres: { condition: 'service_healthy' as const } },\n\t\thealthcheck: {\n\t\t\ttest: ['CMD', 'node', RUNNER_PATH, 'health'],\n\t\t\tinterval: '1m',\n\t\t\ttimeout: '10s',\n\t\t\tretries: 3,\n\t\t\tstart_period: '30s',\n\t\t},\n\t};\n}\n\n/**\n * The backups role, created and kept: able to sign in and read every\n * database, and nothing else. None when it signs in as the superuser.\n */\nexport function backupsRoleStatements(backups: StackBackups): Statement[] {\n\tif (!backups.readOnly) return [];\n\tconst role = quoteIdentifier(backups.login.user);\n\tconst password = `'${backups.login.password.replace(/'/g, \"''\")}'`;\n\treturn [\n\t\t{\n\t\t\tid: BACKUPS_SERVICE,\n\t\t\tdescribe: `role ${backups.login.user}`,\n\t\t\texists: {\n\t\t\t\tsql: 'SELECT 1 FROM pg_roles WHERE rolname = $1',\n\t\t\t\tvalues: [backups.login.user],\n\t\t\t},\n\t\t\tcreate: `CREATE ROLE ${role} LOGIN PASSWORD ${password}`,\n\t\t},\n\t\t{\n\t\t\tid: BACKUPS_SERVICE,\n\t\t\tdescribe: `role ${backups.login.user}: its password`,\n\t\t\tcreate: `ALTER ROLE ${role} WITH LOGIN PASSWORD ${password}`,\n\t\t},\n\t\t{\n\t\t\tid: BACKUPS_SERVICE,\n\t\t\tdescribe: `role ${backups.login.user}: read every database`,\n\t\t\tcreate: `GRANT pg_read_all_data TO ${role}`,\n\t\t},\n\t];\n}\n","/**\n * A compose stage's DNS: the records its public hosts need, and the check that\n * they resolve to its server before a certificate is asked for.\n *\n * - **The server** (`serverAddress`): the stage's own secrets hold its public\n * addresses — `GKM_SERVER_IPV4`, and `GKM_SERVER_IPV6` where it has one.\n * Reserved for gkm: never handed to an app.\n * - **The plan** (`planStackDns`, pure): every public host the stack serves —\n * its apex, each app, each file server, the public log UI — pointed at the\n * server. An A record per host (and an AAAA with an IPv6 address), or, where\n * the domain's `dns` entry says `records: { mode: 'cname', target }`, one A\n * record for the target and a CNAME to it for every other host but the\n * apex, which DNS forbids a CNAME at.\n * - **Applying it** (`applyStackDns`): through the domain's provider in `dns`,\n * a record set at a time. A record with the right value is left alone; one\n * with another value is replaced and the change printed. A `manual` domain\n * is printed and nothing else. A dry run reads, prints and writes nothing.\n * - **The check** (`checkStackDns`): each host resolved with the system\n * resolver. One that is not the server stops the deploy with\n * {@link HostNotPointingAtServer}, before Caddy or Traefik ask Let's Encrypt\n * for a certificate it cannot get — and use up the rate limit trying.\n * - **What gkm wrote before** (`diffRecordedDns`, pure): the plan against the\n * records the stage's state says gkm wrote — added, kept, and stale: a\n * host the stage no longer serves, after a domain or an app changed.\n * - **The prune** (`pruneStackDns`): each stale record deleted through its\n * domain's provider, if it still holds the value gkm wrote. One someone\n * pointed elsewhere is left alone; one no provider can delete is printed.\n * - **The step** (`stageDns`): all of it, as every deploy runs it — the plan applied through each provider,\n * what was written read back from the provider, the stale records pruned,\n * and only the hosts no provider wrote resolved publicly. A brand-new name\n * is never looked up before its record exists, so no resolver caches it as\n * missing.\n */\n\nimport { lookup as dnsLookup } from 'node:dns/promises';\nimport { isIPv4, isIPv6 } from 'node:net';\nimport type {\n\tCredentialProvider,\n\tGoDaddyCredential,\n\tHostingerCredential,\n} from '../deploy/credentials';\nimport type { DnsResourceRecord } from '../deploy/dnsResources';\nimport {\n\tcreateDnsProvider,\n\ttype DnsProvider,\n\ttype DnsRecord,\n\tDnsRecordsUnreadable,\n\ttype DnsConfig as SchemaDnsConfig,\n} from '../target/dokploy/dns/DnsProvider';\nimport type { GoDaddyProviderOptions } from '../target/dokploy/dns/GoDaddyProvider';\nimport {\n\textractSubdomain,\n\tfindRootDomain,\n\tnormalizeDnsConfig,\n} from '../target/dokploy/dns/index';\nimport type {\n\tDnsProvider as DnsProviderConfig,\n\tWorkspaceDnsConfig,\n} from '../workspace/types';\nimport {\n\tcnameTarget,\n\ttype DnsRecordsMode,\n\tisReservedStageKey,\n\tSERVER_IPV4_KEY,\n\tSERVER_IPV6_KEY,\n} from './dnsConfig';\n\nexport { isReservedStageKey, SERVER_IPV4_KEY, SERVER_IPV6_KEY };\n\nimport { GkmError } from '../errors';\nimport type { ComposeStack } from './stack';\n\n/** A deployed stage's server: what its hosts' records point at. */\nexport interface ServerAddress {\n\tipv4: string;\n\tipv6?: string;\n}\n\n/** The line that says how to set the server's address. */\nexport function serverAddressHint(stage: string): string {\n\treturn `gkm secrets:set ${SERVER_IPV4_KEY} '<ip>' --stage ${stage}`;\n}\n\n/** A server address in the stage's secrets that is not an address. */\nexport class ServerAddressInvalid extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly key: string,\n\t\treadonly value: string,\n\t) {\n\t\tconst ipv4 = key === SERVER_IPV4_KEY;\n\t\tsuper(\n\t\t\t`${key} in the '${stage}' stage's secrets is ${JSON.stringify(value)}, ` +\n\t\t\t\t`which is not an ${ipv4 ? 'IPv4 address (like 203.0.113.10)' : 'IPv6 address (like 2001:db8::10)'}. ` +\n\t\t\t\t`Set the server's public address: gkm secrets:set ${key} '<ip>' --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'ServerAddressInvalid';\n\t}\n}\n\n/**\n * A compose stage that serves real domains with no server address: its hosts\n * cannot be pointed or checked, and a certificate would be asked for blind.\n */\nexport class ServerAddressMissing extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly domain: string,\n\t) {\n\t\tsuper(\n\t\t\t`The '${stage}' stage serves ${domain} and its secrets hold no ` +\n\t\t\t\t`${SERVER_IPV4_KEY} — the public address of the server its stack runs ` +\n\t\t\t\t\"on, which its hosts' DNS records point at and every deploy checks \" +\n\t\t\t\t`before asking for certificates. Set it: ${serverAddressHint(stage)} ` +\n\t\t\t\t`(and ${SERVER_IPV6_KEY} for an IPv6 address).`,\n\t\t);\n\t\tthis.name = 'ServerAddressMissing';\n\t}\n}\n\n/**\n * The server's address from the stage's secrets, checked — `undefined` when\n * `GKM_SERVER_IPV4` is not set.\n *\n * @throws {ServerAddressInvalid} for a value that is not an address\n */\nexport function serverAddress(\n\tstage: string,\n\tcustom: Readonly<Record<string, string>> | undefined,\n): ServerAddress | undefined {\n\tconst ipv4 = custom?.[SERVER_IPV4_KEY]?.trim();\n\tconst ipv6 = custom?.[SERVER_IPV6_KEY]?.trim();\n\tif (ipv6 && !isIPv6(ipv6)) {\n\t\tthrow new ServerAddressInvalid(stage, SERVER_IPV6_KEY, ipv6);\n\t}\n\tif (!ipv4) return undefined;\n\tif (!isIPv4(ipv4))\n\t\tthrow new ServerAddressInvalid(stage, SERVER_IPV4_KEY, ipv4);\n\treturn { ipv4, ...(ipv6 ? { ipv6 } : {}) };\n}\n\n/**\n * A name that resolves to this machine by definition — `localhost` and every\n * `*.localhost` — and is in nobody's DNS.\n */\nexport function isLocalHost(host: string): boolean {\n\tconst name = host.toLowerCase().replace(/\\.$/, '');\n\treturn name === 'localhost' || name.endsWith('.localhost');\n}\n\n/**\n * The server's address, required where the stage serves real domains — not\n * `*.localhost` ones, which point at this machine already.\n *\n * @throws {ServerAddressMissing} when the stage has a domain and no address\n * @throws {ServerAddressInvalid} for a value that is not an address\n */\nexport function requiredServerAddress(\n\tstage: string,\n\tdomain: string | undefined,\n\tcustom: Readonly<Record<string, string>> | undefined,\n): ServerAddress | undefined {\n\tconst server = serverAddress(stage, custom);\n\tif (!server && domain && !isLocalHost(domain)) {\n\t\tthrow new ServerAddressMissing(stage, domain);\n\t}\n\treturn server;\n}\n\n/** The record types the DNS step writes. */\nexport type StackRecordType = 'A' | 'AAAA' | 'CNAME';\n\nconst MANAGED: readonly StackRecordType[] = ['A', 'AAAA', 'CNAME'];\n\n/** One record a stack's host needs. */\nexport interface PlannedDnsRecord {\n\t/** The full name: `api.example.com`. */\n\thost: string;\n\t/** The root domain in `dns` it is under. */\n\tdomain: string;\n\t/** Relative to the domain, as providers take it: `api`, `@`. */\n\tname: string;\n\ttype: StackRecordType;\n\tvalue: string;\n\tttl: number;\n}\n\n/** One `dns` domain's share of the plan. */\nexport interface DomainDnsPlan {\n\tdomain: string;\n\t/** `godaddy`, `route53`, `manual`, … — `custom` for an object. */\n\tprovider: string;\n\tconfig: DnsProviderConfig;\n\trecords: PlannedDnsRecord[];\n}\n\nexport interface StackDnsPlan {\n\tstage: string;\n\tserver: ServerAddress;\n\tdomains: DomainDnsPlan[];\n\t/** Hosts under no `dns` domain: theirs to point by hand. */\n\tuncovered: string[];\n}\n\n/** Every public host the stack serves, once each, sorted. */\nexport function stackHosts(stack: Pick<ComposeStack, 'routes'>): string[] {\n\treturn [...new Set(stack.routes.map((route) => route.host))].sort();\n}\n\n/** The TTL a domain's records are written with. */\nexport function recordTtl(config: DnsProviderConfig): number {\n\tconst ttl = 'ttl' in config ? config.ttl : undefined;\n\tif (ttl) return ttl;\n\treturn config.provider === 'godaddy' ? 600 : 300;\n}\n\nfunction providerName(config: DnsProviderConfig): string {\n\treturn typeof config.provider === 'string' ? config.provider : 'custom';\n}\n\nexport interface PlanStackDnsInput {\n\tstage: string;\n\thosts: readonly string[];\n\tserver: ServerAddress;\n\tdns: WorkspaceDnsConfig | undefined;\n}\n\n/**\n * A wildcard host: gkm writes one record per public host, so a stage on\n * another server — or a name someone else points elsewhere — is never caught\n * by a record it did not ask for.\n */\nexport class DnsWildcardRefused extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly host: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}' would point ${host} at its server, and gkm never writes a ` +\n\t\t\t\t'wildcard record: each public host gets its own. Give the app a ' +\n\t\t\t\t'subdomain of its own instead.',\n\t\t);\n\t\tthis.name = 'DnsWildcardRefused';\n\t}\n}\n\n/** The records a stage's hosts need — pure. */\nexport function planStackDns(input: PlanStackDnsInput): StackDnsPlan {\n\tconst { stage, server } = input;\n\tconst dns = input.dns ? normalizeDnsConfig(input.dns) : {};\n\tconst records = new Map<string, PlannedDnsRecord>();\n\tconst uncovered: string[] = [];\n\n\tconst add = (\n\t\thost: string,\n\t\tdomain: string,\n\t\ttype: StackRecordType,\n\t\tvalue: string,\n\t) => {\n\t\tconst name = extractSubdomain(host, domain);\n\t\trecords.set(`${domain} ${name} ${type}`, {\n\t\t\thost,\n\t\t\tdomain,\n\t\t\tname,\n\t\t\ttype,\n\t\t\tvalue,\n\t\t\tttl: recordTtl(dns[domain]!),\n\t\t});\n\t};\n\tconst pointAt = (host: string, domain: string) => {\n\t\tadd(host, domain, 'A', server.ipv4);\n\t\tif (server.ipv6) add(host, domain, 'AAAA', server.ipv6);\n\t};\n\n\tfor (const raw of input.hosts) {\n\t\tconst host = raw.toLowerCase();\n\t\tif (host.includes('*')) throw new DnsWildcardRefused(stage, host);\n\t\tconst domain = findRootDomain(host, dns);\n\t\tif (!domain) {\n\t\t\tuncovered.push(host);\n\t\t\tcontinue;\n\t\t}\n\t\t// A target under the domain itself — the schema refuses one that is\n\t\t// not — gets the A record every other host's CNAME points at.\n\t\tconst target = cnameTarget(\n\t\t\t(dns[domain] as { records?: DnsRecordsMode }).records,\n\t\t\tstage,\n\t\t);\n\t\tif (target) pointAt(target, domain);\n\t\t// The apex can hold no CNAME — it has the zone's SOA and NS — so it\n\t\t// is always an A record.\n\t\tif (target && host !== domain && host !== target) {\n\t\t\tadd(host, domain, 'CNAME', target);\n\t\t} else {\n\t\t\tpointAt(host, domain);\n\t\t}\n\t}\n\n\tconst domains = new Map<string, DomainDnsPlan>();\n\tfor (const record of [...records.values()].sort(\n\t\t(a, b) =>\n\t\t\ta.domain.localeCompare(b.domain) ||\n\t\t\ta.name.localeCompare(b.name) ||\n\t\t\ta.type.localeCompare(b.type),\n\t)) {\n\t\tconst config = dns[record.domain]!;\n\t\tconst plan = domains.get(record.domain) ?? {\n\t\t\tdomain: record.domain,\n\t\t\tprovider: providerName(config),\n\t\t\tconfig,\n\t\t\trecords: [],\n\t\t};\n\t\tplan.records.push(record);\n\t\tdomains.set(record.domain, plan);\n\t}\n\n\treturn {\n\t\tstage,\n\t\tserver,\n\t\tdomains: [...domains.values()],\n\t\tuncovered: uncovered.sort(),\n\t};\n}\n\n/** One change applying a plan makes — or, in a dry run, would. */\nexport interface DnsChange {\n\tdomain: string;\n\tname: string;\n\ttype: StackRecordType;\n\t/**\n\t * `write`: the provider's key cannot read records, so the record is\n\t * written without knowing what is there — idempotent, and not diffed.\n\t */\n\taction: 'create' | 'update' | 'delete' | 'unchanged' | 'write' | 'manual';\n\t/** The value it is set to — absent for a delete. */\n\tvalue?: string;\n\t/** What it was, for an update or a delete. */\n\tprevious?: string;\n\tttl?: number;\n}\n\nexport interface ApplyStackDnsOptions {\n\tdryRun?: boolean;\n\tlog?: (line: string) => void;\n\t/** Where the GoDaddy provider reads its key: the environment and the CLI's home. */\n\tcredentials?: GoDaddyProviderOptions;\n\t/** The Hostinger token, when the caller already has it. */\n\thostinger?: HostingerCredential;\n\t/** The provider for a domain — `createDnsProvider` by default. */\n\tproviderFor?: (config: DnsProviderConfig) => Promise<DnsProvider | null>;\n\t/**\n\t * Read every record written back from its provider, and throw\n\t * {@link DnsRecordsNotConfirmed} for one that does not hold its value.\n\t */\n\tconfirm?: boolean;\n}\n\n/** What applying a plan did, and what of it the providers confirmed. */\nexport interface StackDnsApplied {\n\t/** Every change, the unchanged included; a dry run makes none of them. */\n\tchanges: DnsChange[];\n\t/**\n\t * Hosts whose every record the provider holds with its value — read\n\t * before the run (unchanged) or after it wrote them (`confirm`).\n\t */\n\tconfirmed: string[];\n\t/**\n\t * Hosts no provider vouches for: a `manual` domain's, those under no\n\t * domain in `dns`, and those of a key that can write and not read.\n\t */\n\tunconfirmed: string[];\n}\n\n/** One record a provider did not hold with its value after it was written. */\nexport interface UnconfirmedRecord {\n\thost: string;\n\ttype: StackRecordType;\n\texpected: string;\n\t/** What the provider read back — empty when the record is not there. */\n\tfound: string[];\n}\n\n/** Records a provider accepted and then did not read back with their value. */\nexport class DnsRecordsNotConfirmed extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly domain: string,\n\t\treadonly provider: string,\n\t\treadonly records: readonly UnconfirmedRecord[],\n\t) {\n\t\tconst lines = records.map(\n\t\t\t(r) =>\n\t\t\t\t` - ${r.host} ${r.type}: ${r.found.length > 0 ? r.found.join(', ') : 'nothing'}, expected ${r.expected}`,\n\t\t);\n\t\tsuper(\n\t\t\t`${provider} accepted '${stage}''s records for ${domain}, and reading them ` +\n\t\t\t\t`back does not show them:\\n${lines.join('\\n')}\\n` +\n\t\t\t\t\"Deploy again; if it persists, check the records in the provider's \" +\n\t\t\t\t'console — another tool may be writing the same names.',\n\t\t);\n\t\tthis.name = 'DnsRecordsNotConfirmed';\n\t}\n}\n\n/** `api.example.com A 203.0.113.10 (TTL 600)` */\nfunction describe(record: PlannedDnsRecord): string {\n\treturn `${record.host.padEnd(32)} ${record.type.padEnd(5)} ${record.value} (TTL ${record.ttl})`;\n}\n\nfunction printManual(\n\tplan: DomainDnsPlan,\n\tlog: (line: string) => void,\n\twhy: string,\n): DnsChange[] {\n\tlog(` ${plan.domain}: ${why} — create these records at its DNS host:`);\n\tfor (const record of plan.records) log(` ${describe(record)}`);\n\treturn plan.records.map((r) => ({\n\t\tdomain: r.domain,\n\t\tname: r.name,\n\t\ttype: r.type,\n\t\taction: 'manual',\n\t\tvalue: r.value,\n\t\tttl: r.ttl,\n\t}));\n}\n\n/** The existing records of every planned name, for the types gkm manages. */\nasync function existingRecords(\n\tprovider: DnsProvider,\n\tplan: DomainDnsPlan,\n): Promise<DnsRecord[]> {\n\tconst names = [...new Set(plan.records.map((r) => r.name))];\n\treturn readRecords(\n\t\tprovider,\n\t\tplan.domain,\n\t\tnames.flatMap((name) => MANAGED.map((type) => ({ name, type }))),\n\t);\n}\n\n/** Exactly these names and types, as the provider holds them. */\nasync function readRecords(\n\tprovider: DnsProvider,\n\tdomain: string,\n\twanted: readonly { name: string; type: StackRecordType }[],\n): Promise<DnsRecord[]> {\n\tif (provider.readRecords) return provider.readRecords(domain, wanted);\n\tconst all = await provider.getRecords(domain);\n\treturn all.filter((r) =>\n\t\twanted.some((w) => w.name === r.name && w.type === r.type),\n\t);\n}\n\n/** The provider for a domain's config: `providerFor`, or the real one. */\nfunction providerResolver(\n\toptions: Pick<\n\t\tApplyStackDnsOptions,\n\t\t'providerFor' | 'credentials' | 'hostinger'\n\t>,\n): (config: DnsProviderConfig) => Promise<DnsProvider | null> {\n\treturn (\n\t\toptions.providerFor ??\n\t\t((config: DnsProviderConfig) =>\n\t\t\tcreateDnsProvider({\n\t\t\t\tconfig: config as SchemaDnsConfig,\n\t\t\t\t...(options.credentials ? { godaddy: options.credentials } : {}),\n\t\t\t\t...(options.hostinger ? { hostinger: options.hostinger } : {}),\n\t\t\t}))\n\t);\n}\n\nconst same = (a: string, b: string) =>\n\ta.trim().replace(/\\.$/, '').toLowerCase() ===\n\tb.trim().replace(/\\.$/, '').toLowerCase();\n\n/**\n * What each domain's records need, compared with what is there: creates,\n * updates, and the deletes a change of mode needs — a name moving from an A\n * record to a CNAME, or back, cannot hold both. Nothing else is deleted.\n */\nexport function diffDomainDns(\n\tplan: DomainDnsPlan,\n\texisting: readonly DnsRecord[],\n): DnsChange[] {\n\tconst changes: DnsChange[] = [];\n\tconst find = (name: string, type: string) =>\n\t\texisting.find((r) => r.name === name && r.type === type);\n\n\tconst planned = new Map<string, Set<StackRecordType>>();\n\tfor (const record of plan.records) {\n\t\tconst types = planned.get(record.name) ?? new Set();\n\t\ttypes.add(record.type);\n\t\tplanned.set(record.name, types);\n\t}\n\n\t// The deletes first: a CNAME where an A record goes, A and AAAA where a\n\t// CNAME goes.\n\tfor (const [name, types] of planned) {\n\t\tconst conflicting: StackRecordType[] = types.has('CNAME')\n\t\t\t? ['A', 'AAAA']\n\t\t\t: ['CNAME'];\n\t\tfor (const type of conflicting) {\n\t\t\tconst found = find(name, type);\n\t\t\tif (!found || found.values.length === 0) continue;\n\t\t\tchanges.push({\n\t\t\t\tdomain: plan.domain,\n\t\t\t\tname,\n\t\t\t\ttype,\n\t\t\t\taction: 'delete',\n\t\t\t\tprevious: found.values.join(', '),\n\t\t\t});\n\t\t}\n\t}\n\n\tfor (const record of plan.records) {\n\t\tconst found = find(record.name, record.type);\n\t\tconst base = {\n\t\t\tdomain: plan.domain,\n\t\t\tname: record.name,\n\t\t\ttype: record.type,\n\t\t\tvalue: record.value,\n\t\t\tttl: record.ttl,\n\t\t};\n\t\tif (!found || found.values.length === 0) {\n\t\t\tchanges.push({ ...base, action: 'create' });\n\t\t} else if (\n\t\t\tfound.values.length === 1 &&\n\t\t\tsame(found.values[0]!, record.value)\n\t\t) {\n\t\t\tchanges.push({ ...base, action: 'unchanged' });\n\t\t} else {\n\t\t\tchanges.push({\n\t\t\t\t...base,\n\t\t\t\taction: 'update',\n\t\t\t\tprevious: found.values.join(', '),\n\t\t\t});\n\t\t}\n\t}\n\treturn changes;\n}\n\n/** A host's full name from a domain and a relative name. */\nfunction fullName(domain: string, name: string): string {\n\treturn name === '@' ? domain : `${name}.${domain}`;\n}\n\nfunction printChange(\n\tchange: DnsChange,\n\tdryRun: boolean,\n\tlog: (line: string) => void,\n) {\n\tconst host = fullName(change.domain, change.name).padEnd(32);\n\tconst type = change.type.padEnd(5);\n\tconst would = dryRun ? 'would ' : '';\n\tswitch (change.action) {\n\t\tcase 'unchanged':\n\t\t\tlog(\n\t\t\t\t` ✓ ${host} ${type} ${change.value} (TTL ${change.ttl}) — up to date`,\n\t\t\t);\n\t\t\tbreak;\n\t\tcase 'create':\n\t\t\tlog(\n\t\t\t\t` + ${host} ${type} ${change.value} (TTL ${change.ttl}) — ${would}create`,\n\t\t\t);\n\t\t\tbreak;\n\t\tcase 'update':\n\t\t\tlog(\n\t\t\t\t` ~ ${host} ${type} ${change.previous} → ${change.value} (TTL ${change.ttl}) — ${would}update`,\n\t\t\t);\n\t\t\tbreak;\n\t\tcase 'write':\n\t\t\tlog(\n\t\t\t\tdryRun\n\t\t\t\t\t? ` ? ${host} ${type} ${change.value} (TTL ${change.ttl}) — would write; current value unknown (key cannot read DNS)`\n\t\t\t\t\t: ` ! ${host} ${type} ${change.value} (TTL ${change.ttl}) — written (not diffed)`,\n\t\t\t);\n\t\t\tbreak;\n\t\tcase 'delete':\n\t\t\tlog(\n\t\t\t\t` - ${host} ${type} ${change.previous} — ${would}delete (a name cannot hold a CNAME and an A record)`,\n\t\t\t);\n\t\t\tbreak;\n\t}\n}\n\n/**\n * Each domain's records, through its provider. Returns every change, the\n * unchanged included; a dry run makes none of them.\n */\nexport async function applyStackDns(\n\tplan: StackDnsPlan,\n\toptions: ApplyStackDnsOptions = {},\n): Promise<DnsChange[]> {\n\treturn (await settleStackDns(plan, options)).changes;\n}\n\n/**\n * Each domain's records, through its provider — and, with `confirm`, what was\n * written read back from it. A record already holding its value is neither\n * written nor read again.\n *\n * @throws {DnsRecordsNotConfirmed} when a written record does not read back\n */\nexport async function settleStackDns(\n\tplan: StackDnsPlan,\n\toptions: ApplyStackDnsOptions = {},\n): Promise<StackDnsApplied> {\n\tconst dryRun = options.dryRun === true;\n\tconst log = options.log ?? ((line: string) => console.log(line));\n\tconst providerFor = providerResolver(options);\n\n\tconst changes: DnsChange[] = [];\n\tconst confirmed = new Set<string>();\n\tconst unconfirmed = new Set<string>();\n\tconst hostsOf = (domain: DomainDnsPlan) =>\n\t\tnew Set(domain.records.map((r) => r.host));\n\tfor (const host of plan.uncovered) {\n\t\tlog(\n\t\t\t` ⚠ ${host} is under no domain in dns — point it at ${plan.server.ipv4} yourself`,\n\t\t);\n\t}\n\n\tfor (const domain of plan.domains) {\n\t\tif (domain.provider === 'manual') {\n\t\t\tchanges.push(...printManual(domain, log, 'manual'));\n\t\t\tfor (const host of hostsOf(domain)) unconfirmed.add(host);\n\t\t\tcontinue;\n\t\t}\n\t\tconst provider = await providerFor(domain.config);\n\t\tif (!provider) {\n\t\t\tchanges.push(...printManual(domain, log, 'manual'));\n\t\t\tfor (const host of hostsOf(domain)) unconfirmed.add(host);\n\t\t\tcontinue;\n\t\t}\n\n\t\tlog(` ${domain.domain} (${provider.name})${dryRun ? ' — dry run' : ''}`);\n\t\tlet existing: DnsRecord[];\n\t\tlet diff: DnsChange[];\n\t\tlet unreadable = false;\n\t\ttry {\n\t\t\texisting = await existingRecords(provider, domain);\n\t\t\tdiff = diffDomainDns(domain, existing);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof DnsRecordsUnreadable)) throw error;\n\t\t\tlog(\n\t\t\t\t` ℹ The ${provider.name} key cannot read ${domain.domain}'s DNS records, so changes cannot be diffed: every record is written (idempotent).`,\n\t\t\t);\n\t\t\tunreadable = true;\n\t\t\texisting = [];\n\t\t\tdiff = domain.records.map((r) => ({\n\t\t\t\tdomain: r.domain,\n\t\t\t\tname: r.name,\n\t\t\t\ttype: r.type,\n\t\t\t\taction: 'write' as const,\n\t\t\t\tvalue: r.value,\n\t\t\t\tttl: r.ttl,\n\t\t\t}));\n\t\t}\n\t\tfor (const change of diff) printChange(change, dryRun, log);\n\n\t\t// An AAAA nobody planned, at a name that points here: IPv6 clients —\n\t\t// Let's Encrypt among them — would go somewhere else.\n\t\tif (!plan.server.ipv6) {\n\t\t\tfor (const name of new Set(domain.records.map((r) => r.name))) {\n\t\t\t\tconst aaaa = existing.find((r) => r.name === name && r.type === 'AAAA');\n\t\t\t\tconst planned = domain.records.find(\n\t\t\t\t\t(r) => r.name === name && r.type === 'A',\n\t\t\t\t);\n\t\t\t\tif (aaaa && planned && aaaa.values.length > 0) {\n\t\t\t\t\tlog(\n\t\t\t\t\t\t` ⚠ ${fullName(domain.domain, name)} also has AAAA ${aaaa.values.join(', ')}: ` +\n\t\t\t\t\t\t\t`set ${SERVER_IPV6_KEY} in the '${plan.stage}' stage's secrets to the server's IPv6 address, ` +\n\t\t\t\t\t\t\t'or remove that record, or IPv6 clients go elsewhere',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tchanges.push(...diff);\n\n\t\t// A host every one of whose records was already right is confirmed by\n\t\t// the read that found it so; one with a write is confirmed by reading\n\t\t// it back — or, with a key that cannot read, by nobody.\n\t\tconst written = new Set(\n\t\t\tdiff\n\t\t\t\t.filter((c) => c.action !== 'unchanged')\n\t\t\t\t.map((c) => fullName(c.domain, c.name)),\n\t\t);\n\t\tfor (const host of hostsOf(domain)) {\n\t\t\t// A dry run writes nothing, so nothing it would write is resolved:\n\t\t\t// a name looked up before its record exists is cached as missing.\n\t\t\tif (unreadable) {\n\t\t\t\tif (!dryRun) unconfirmed.add(host);\n\t\t\t} else if (!written.has(host)) confirmed.add(host);\n\t\t}\n\t\tif (dryRun) continue;\n\n\t\tconst deletes = diff.filter((c) => c.action === 'delete');\n\t\tif (deletes.length > 0) {\n\t\t\tawait provider.deleteRecords(\n\t\t\t\tdomain.domain,\n\t\t\t\tdeletes.map((c) => ({ name: c.name, type: c.type })),\n\t\t\t);\n\t\t}\n\t\tconst writes = diff.filter(\n\t\t\t(c) =>\n\t\t\t\tc.action === 'create' || c.action === 'update' || c.action === 'write',\n\t\t);\n\t\tif (writes.length > 0) {\n\t\t\tawait provider.upsertRecords(\n\t\t\t\tdomain.domain,\n\t\t\t\twrites.map((c) => ({\n\t\t\t\t\tname: c.name,\n\t\t\t\t\ttype: c.type,\n\t\t\t\t\tvalue: c.value!,\n\t\t\t\t\tttl: c.ttl!,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\n\t\tif (!options.confirm || unreadable || written.size === 0) continue;\n\t\tconst rewritten = {\n\t\t\t...domain,\n\t\t\trecords: domain.records.filter((r) => written.has(r.host)),\n\t\t};\n\t\tconst after = await existingRecords(provider, rewritten);\n\t\tconst missing: UnconfirmedRecord[] = [];\n\t\tfor (const record of rewritten.records) {\n\t\t\tconst found = after.find(\n\t\t\t\t(r) => r.name === record.name && r.type === record.type,\n\t\t\t);\n\t\t\tconst values = found?.values ?? [];\n\t\t\tif (values.length !== 1 || !same(values[0]!, record.value)) {\n\t\t\t\tmissing.push({\n\t\t\t\t\thost: record.host,\n\t\t\t\t\ttype: record.type,\n\t\t\t\t\texpected: record.value,\n\t\t\t\t\tfound: values,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\tif (missing.length > 0) {\n\t\t\tthrow new DnsRecordsNotConfirmed(\n\t\t\t\tplan.stage,\n\t\t\t\tdomain.domain,\n\t\t\t\tprovider.name,\n\t\t\t\tmissing,\n\t\t\t);\n\t\t}\n\t\tfor (const host of written) confirmed.add(host);\n\t\tlog(\n\t\t\t` ✓ ${written.size} host${written.size === 1 ? '' : 's'} read back from ${provider.name}`,\n\t\t);\n\t}\n\tfor (const host of plan.uncovered) unconfirmed.add(host);\n\treturn {\n\t\tchanges,\n\t\tconfirmed: [...confirmed].sort(),\n\t\tunconfirmed: [...unconfirmed].filter((h) => !confirmed.has(h)).sort(),\n\t};\n}\n\n// ============================================================================\n// What gkm wrote before: the diff and the prune\n// ============================================================================\n\n/** A record gkm wrote for the stage, as its state keeps it. */\nexport type RecordedDnsRecord = DnsResourceRecord & { fqdn: string };\n\n/** The plan against the records the stage's state says gkm wrote. */\nexport interface RecordedDnsDiff {\n\t/** Planned, and never written by gkm for the stage. */\n\tadded: PlannedDnsRecord[];\n\t/** Planned, and written by gkm before — at this value or another. */\n\tkept: PlannedDnsRecord[];\n\t/** Written by gkm before, and no longer planned. */\n\tstale: RecordedDnsRecord[];\n}\n\n/** `api.example.com A` — a record's identity, whatever its value. */\nconst recordKey = (fqdn: string, type: string) =>\n\t`${fqdn.trim().replace(/\\.$/, '').toLowerCase()} ${type.toUpperCase()}`;\n\n/**\n * The plan against the records gkm wrote for the stage — pure. A record is\n * its full name and its type: one the plan has at another value is kept (the\n * apply updates it), and one whose name or type the plan no longer has is\n * stale — a host the stage stopped serving when its domain or an app changed,\n * or an AAAA once its server has no IPv6 address.\n */\nexport function diffRecordedDns(\n\tplan: StackDnsPlan,\n\trecorded: readonly RecordedDnsRecord[],\n): RecordedDnsDiff {\n\tconst planned = plan.domains.flatMap((domain) => domain.records);\n\tconst wanted = new Set(planned.map((r) => recordKey(r.host, r.type)));\n\tconst written = new Set(recorded.map((r) => recordKey(r.fqdn, r.type)));\n\treturn {\n\t\tadded: planned.filter((r) => !written.has(recordKey(r.host, r.type))),\n\t\tkept: planned.filter((r) => written.has(recordKey(r.host, r.type))),\n\t\tstale: recorded.filter((r) => !wanted.has(recordKey(r.fqdn, r.type))),\n\t};\n}\n\n/** What the prune did with one stale record — or, in a dry run, would. */\nexport interface PrunedDnsRecord {\n\trecord: RecordedDnsRecord;\n\t/**\n\t * - `delete`: it still held the value gkm wrote, and is deleted\n\t * - `gone`: the provider no longer has it\n\t * - `changed`: it was pointed elsewhere since, and is left as it is\n\t * - `manual`: no provider gkm can use deletes it — printed for a person\n\t *\n\t * Each is forgotten from the stage's state: none is gkm's any more.\n\t */\n\taction: 'delete' | 'gone' | 'changed' | 'manual';\n\t/** What the provider holds, for `changed`. */\n\tfound?: string[];\n}\n\nexport interface PruneStackDnsOptions\n\textends Pick<\n\t\tApplyStackDnsOptions,\n\t\t'dryRun' | 'log' | 'credentials' | 'hostinger' | 'providerFor'\n\t> {\n\tstage: string;\n\tdns: WorkspaceDnsConfig | undefined;\n}\n\n/**\n * Each stale record deleted through its domain's provider — only where the\n * provider still holds exactly the value gkm wrote, so a name someone has\n * since pointed elsewhere is never taken from them. Where no provider can\n * read and delete it — a `manual` domain, one gone from `dns`, a key that\n * can only write — it is printed for a person to remove. A dry run reads\n * and prints, and deletes nothing.\n */\nexport async function pruneStackDns(\n\tstale: readonly RecordedDnsRecord[],\n\toptions: PruneStackDnsOptions,\n): Promise<PrunedDnsRecord[]> {\n\tif (stale.length === 0) return [];\n\tconst dryRun = options.dryRun === true;\n\tconst log = options.log ?? ((line: string) => console.log(line));\n\tconst providerFor = providerResolver(options);\n\tconst dns = options.dns ? normalizeDnsConfig(options.dns) : {};\n\n\tconst byDomain = new Map<string, RecordedDnsRecord[]>();\n\tfor (const record of stale) {\n\t\tbyDomain.set(record.domain, [\n\t\t\t...(byDomain.get(record.domain) ?? []),\n\t\t\trecord,\n\t\t]);\n\t}\n\n\tlog(` No longer served by '${options.stage}':`);\n\tconst pruned: PrunedDnsRecord[] = [];\n\tconst byHand = (\n\t\tdomain: string,\n\t\trecords: RecordedDnsRecord[],\n\t\twhy: string,\n\t) => {\n\t\tlog(` ${domain}: ${why} — remove these records at its DNS host:`);\n\t\tfor (const record of records) {\n\t\t\tlog(\n\t\t\t\t` ${record.fqdn.padEnd(32)} ${record.type.padEnd(5)} ${record.value}`,\n\t\t\t);\n\t\t\tpruned.push({ record, action: 'manual' });\n\t\t}\n\t};\n\n\tfor (const [domain, records] of byDomain) {\n\t\tconst config = dns[domain];\n\t\tif (!config) {\n\t\t\tbyHand(domain, records, 'no longer in dns');\n\t\t\tcontinue;\n\t\t}\n\t\tconst provider =\n\t\t\tproviderName(config) === 'manual' ? null : await providerFor(config);\n\t\tif (!provider) {\n\t\t\tbyHand(domain, records, 'manual');\n\t\t\tcontinue;\n\t\t}\n\n\t\tlet existing: DnsRecord[];\n\t\ttry {\n\t\t\texisting = await readRecords(\n\t\t\t\tprovider,\n\t\t\t\tdomain,\n\t\t\t\trecords.map((r) => ({ name: r.name, type: r.type as StackRecordType })),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!(error instanceof DnsRecordsUnreadable)) throw error;\n\t\t\tbyHand(\n\t\t\t\tdomain,\n\t\t\t\trecords,\n\t\t\t\t`the ${provider.name} key cannot read its records, so none is deleted unchecked`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst would = dryRun ? 'would ' : '';\n\t\tconst deletes: PrunedDnsRecord[] = [];\n\t\tfor (const record of records) {\n\t\t\tconst host = record.fqdn.padEnd(32);\n\t\t\tconst type = record.type.padEnd(5);\n\t\t\tconst found =\n\t\t\t\texisting.find((r) => r.name === record.name && r.type === record.type)\n\t\t\t\t\t?.values ?? [];\n\t\t\tif (found.length === 0) {\n\t\t\t\tlog(` · ${host} ${type} already gone`);\n\t\t\t\tpruned.push({ record, action: 'gone' });\n\t\t\t} else if (found.length === 1 && same(found[0]!, record.value)) {\n\t\t\t\tlog(` - ${host} ${type} ${record.value} — ${would}delete`);\n\t\t\t\tdeletes.push({ record, action: 'delete' });\n\t\t\t} else {\n\t\t\t\tlog(\n\t\t\t\t\t` ⚠ ${host} ${type} ${found.join(', ')} — pointed elsewhere since gkm wrote ${record.value}; left as it is`,\n\t\t\t\t);\n\t\t\t\tpruned.push({ record, action: 'changed', found });\n\t\t\t}\n\t\t}\n\t\tif (!dryRun && deletes.length > 0) {\n\t\t\tawait provider.deleteRecords(\n\t\t\t\tdomain,\n\t\t\t\tdeletes.map(({ record }) => ({\n\t\t\t\t\tname: record.name,\n\t\t\t\t\ttype: record.type as StackRecordType,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\t\tpruned.push(...deletes);\n\t}\n\treturn pruned;\n}\n\n// ============================================================================\n// The check\n// ============================================================================\n\n/** Resolves a host to every address it has — `dns.promises.lookup`, `all`. */\nexport type HostLookup = (host: string) => Promise<string[]>;\n\n/** The system resolver: what the server's own clients would be told. */\nexport const systemLookup: HostLookup = async (host) => {\n\tconst found = await dnsLookup(host, { all: true });\n\treturn found.map((a) => a.address);\n};\n\n/** One host that does not resolve to the server. */\nexport interface MisdirectedHost {\n\thost: string;\n\t/** What it resolves to — empty when it does not resolve. */\n\tresolved: string[];\n\t/** Why it did not resolve, when it did not. */\n\terror?: string;\n}\n\n/** Hosts that do not resolve to the stage's server. */\nexport class HostNotPointingAtServer extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly hosts: readonly MisdirectedHost[],\n\t\treadonly expected: { ipv4: string; ipv6?: string },\n\t\t/** The records to create, where `dns` covers none of them. */\n\t\treadonly fix: string,\n\t) {\n\t\tconst want = expected.ipv6\n\t\t\t? `${expected.ipv4} (and ${expected.ipv6})`\n\t\t\t: expected.ipv4;\n\t\tconst lines = hosts.map(\n\t\t\t(h) =>\n\t\t\t\t` - ${h.host} → ${h.resolved.length > 0 ? h.resolved.join(', ') : `nothing${h.error ? ` (${h.error})` : ''}`}, expected ${want}`,\n\t\t);\n\t\tsuper(\n\t\t\t`${hosts.length === 1 ? 'A host' : `${hosts.length} hosts`} of '${stage}' ` +\n\t\t\t\t`do${hosts.length === 1 ? 'es' : ''} not resolve to its server, so a ` +\n\t\t\t\t`certificate for ${hosts.length === 1 ? 'it' : 'them'} cannot be issued:\\n` +\n\t\t\t\t`${lines.join('\\n')}\\n${fix}\\n` +\n\t\t\t\t'Records can take a few minutes to propagate. With a CDN or proxy in ' +\n\t\t\t\t'front of the server, pass --skip-dns.',\n\t\t);\n\t\tthis.name = 'HostNotPointingAtServer';\n\t}\n}\n\nexport interface CheckStackDnsInput {\n\tstage: string;\n\thosts: readonly string[];\n\tserver: ServerAddress;\n\tdns: WorkspaceDnsConfig | undefined;\n\tlookup?: HostLookup;\n}\n\n/**\n * Every host resolved: each must resolve to the server's IPv4 address and to\n * nothing but the server's addresses.\n *\n * @throws {HostNotPointingAtServer} naming every host that does not\n */\nexport async function checkStackDns(\n\tinput: CheckStackDnsInput,\n): Promise<string[]> {\n\tconst lookup = input.lookup ?? systemLookup;\n\tconst { server } = input;\n\tconst ours = new Set(\n\t\t[server.ipv4, server.ipv6].filter(Boolean).map((a) => a!.toLowerCase()),\n\t);\n\tconst bad: MisdirectedHost[] = [];\n\tconst ok: string[] = [];\n\tfor (const host of input.hosts) {\n\t\tlet resolved: string[];\n\t\ttry {\n\t\t\tresolved = [...new Set(await lookup(host))];\n\t\t} catch (error) {\n\t\t\tconst code =\n\t\t\t\terror && typeof error === 'object' && 'code' in error\n\t\t\t\t\t? String((error as { code: unknown }).code)\n\t\t\t\t\t: error instanceof Error\n\t\t\t\t\t\t? error.message\n\t\t\t\t\t\t: String(error);\n\t\t\tbad.push({ host, resolved: [], error: code });\n\t\t\tcontinue;\n\t\t}\n\t\tconst pointsHere =\n\t\t\tresolved.some((a) => a.toLowerCase() === server.ipv4.toLowerCase()) &&\n\t\t\tresolved.every((a) => ours.has(a.toLowerCase()));\n\t\tif (pointsHere) ok.push(host);\n\t\telse bad.push({ host, resolved });\n\t}\n\n\tif (bad.length > 0) {\n\t\tthrow new HostNotPointingAtServer(\n\t\t\tinput.stage,\n\t\t\tbad,\n\t\t\t{ ipv4: server.ipv4, ...(server.ipv6 ? { ipv6: server.ipv6 } : {}) },\n\t\t\tdnsFix(input, bad),\n\t\t);\n\t}\n\treturn ok;\n}\n\n/** How to fix it: the deploy, or the records to create by hand. */\nfunction dnsFix(input: CheckStackDnsInput, bad: readonly MisdirectedHost[]) {\n\tconst hosts = bad.map((h) => h.host);\n\tlet plan: StackDnsPlan;\n\ttry {\n\t\tplan = planStackDns({\n\t\t\tstage: input.stage,\n\t\t\thosts,\n\t\t\tserver: input.server,\n\t\t\tdns: input.dns,\n\t\t});\n\t} catch {\n\t\tplan = {\n\t\t\tstage: input.stage,\n\t\t\tserver: input.server,\n\t\t\tdomains: [],\n\t\t\tuncovered: hosts,\n\t\t};\n\t}\n\tconst automatic = plan.domains.filter((d) => d.provider !== 'manual');\n\tconst byHand = [\n\t\t...plan.domains\n\t\t\t.filter((d) => d.provider === 'manual')\n\t\t\t.flatMap((d) => d.records.map(describe)),\n\t\t...plan.uncovered.flatMap((host) => [\n\t\t\t`${host.padEnd(32)} A ${input.server.ipv4}`,\n\t\t\t...(input.server.ipv6\n\t\t\t\t? [`${host.padEnd(32)} AAAA ${input.server.ipv6}`]\n\t\t\t\t: []),\n\t\t]),\n\t];\n\tconst lines: string[] = [];\n\tif (automatic.length > 0) {\n\t\tlines.push(\n\t\t\t`Fix: gkm deploy --stage ${input.stage} writes the records for ${automatic.map((d) => d.domain).join(', ')} through their provider (--dry-run shows them first).`,\n\t\t);\n\t}\n\tif (byHand.length > 0) {\n\t\tlines.push(\n\t\t\t`${automatic.length > 0 ? 'And create' : 'Fix: create'} these records at the DNS host:`,\n\t\t\t...byHand.map((line) => ` ${line}`),\n\t\t);\n\t}\n\treturn lines.join('\\n');\n}\n\n// ============================================================================\n// The step: plan, apply, confirm, check\n// ============================================================================\n\n/** The environment variable each token-authenticated provider reads. */\nexport const DNS_TOKEN_KEYS = {\n\tgodaddy: 'GODADDY_API_TOKEN',\n\thostinger: 'HOSTINGER_API_TOKEN',\n} as const;\n\n/** A provider whose credential is a token gkm reads. */\nexport type TokenDnsProvider = keyof typeof DNS_TOKEN_KEYS;\n\n/** The tokens a stage's records are written with. */\nexport interface DnsCredentials {\n\tgodaddy?: GoDaddyCredential;\n\thostinger?: HostingerCredential;\n}\n\n/**\n * A domain whose records the deploy writes, and no token to write them with.\n * Raised before anything is built, started or written.\n */\nexport class DnsCredentialMissing extends GkmError {\n\tconstructor(\n\t\treadonly stage: string,\n\t\treadonly domain: string,\n\t\treadonly provider: TokenDnsProvider,\n\t\t/** The variable the token is read from: `GODADDY_API_TOKEN`. */\n\t\treadonly key: string,\n\t) {\n\t\tsuper(\n\t\t\t`'${stage}''s hosts are under ${domain}, whose DNS is ${provider}'s, and ` +\n\t\t\t\t`the deploy writes their records — but ${key} is not set. In CI, add ` +\n\t\t\t\t`${key} as a secret on the '${stage}' GitHub environment and pass it ` +\n\t\t\t\t`to the step that writes the records: env: { ${key}: \\${{ secrets.${key} }} }. ` +\n\t\t\t\t`On this machine, export ${key} or run gkm login --provider ${provider}. ` +\n\t\t\t\t'With a CDN or proxy in front of the server, pass --skip-dns.',\n\t\t);\n\t\tthis.name = 'DnsCredentialMissing';\n\t}\n}\n\n/** One `dns` domain and its provider's config. */\nexport interface DnsDomain {\n\tdomain: string;\n\tconfig: DnsProviderConfig;\n}\n\n/**\n * The `dns` domain a stage's own domain (`domains.<stage>`) is under — where\n * its hosts' records go — or none.\n */\nexport function stageDnsDomains(\n\tstage: string,\n\tdomains: Readonly<Record<string, string>> | undefined,\n\tdns: WorkspaceDnsConfig | undefined,\n): DnsDomain[] {\n\tconst own = domains?.[stage];\n\tif (!own || !dns || isLocalHost(own)) return [];\n\tconst normalized = normalizeDnsConfig(dns);\n\tconst root = findRootDomain(own.toLowerCase(), normalized);\n\treturn root ? [{ domain: root, config: normalized[root]! }] : [];\n}\n\n/**\n * Each token the domains' providers need, from `credentials` — `manual`,\n * Route53 (the AWS SDK's own chain) and a provider object need none.\n *\n * @throws {DnsCredentialMissing} naming the first domain with no token\n */\nexport async function dnsCredentials(\n\tstage: string,\n\tdomains: readonly DnsDomain[],\n\tcredentials: CredentialProvider,\n): Promise<DnsCredentials> {\n\tconst found: DnsCredentials = {};\n\tfor (const { domain, config } of domains) {\n\t\tconst provider = config.provider;\n\t\tif (provider !== 'godaddy' && provider !== 'hostinger') continue;\n\t\tif (found[provider]) continue;\n\t\tconst token = await credentials.get({ kind: provider });\n\t\tif (!token?.token) {\n\t\t\tthrow new DnsCredentialMissing(\n\t\t\t\tstage,\n\t\t\t\tdomain,\n\t\t\t\tprovider,\n\t\t\t\tDNS_TOKEN_KEYS[provider],\n\t\t\t);\n\t\t}\n\t\tfound[provider] = token;\n\t}\n\treturn found;\n}\n\nexport interface StageDnsInput {\n\tstage: string;\n\t/** The stage's public hosts, read off its stack. */\n\thosts: readonly string[];\n\tserver: ServerAddress;\n\tdns: WorkspaceDnsConfig | undefined;\n\t/** Where the providers' tokens come from. */\n\tcredentials: CredentialProvider;\n\tdryRun?: boolean;\n\tlog?: (line: string) => void;\n\t/** Resolves the hosts no provider confirmed — the system resolver by default. */\n\tlookup?: HostLookup;\n\t/** The provider for a domain — `createDnsProvider` by default. */\n\tproviderFor?: (config: DnsProviderConfig) => Promise<DnsProvider | null>;\n\t/** GoDaddy's API, for tests: its base URL and how a 429 is waited out. */\n\tgodaddy?: Pick<GoDaddyProviderOptions, 'baseUrl' | 'sleep' | 'maxAttempts'>;\n\t/** The records the stage's state says gkm wrote: the stale ones are pruned. */\n\trecorded?: readonly RecordedDnsRecord[];\n}\n\nexport interface StageDnsResult {\n\tplan: StackDnsPlan;\n\tchanges: DnsChange[];\n\t/** Hosts whose records their provider holds with the server's address. */\n\tconfirmed: string[];\n\t/** Hosts the system resolver found pointing at the server. */\n\tresolved: string[];\n\t/** The plan against the records gkm wrote for the stage before. */\n\trecorded: RecordedDnsDiff;\n\t/**\n\t * The stale records pruned, through the providers this step used — called\n\t * once the stack serves its new hosts, so a run that stops short leaves\n\t * the old ones answering. In a dry run it reads and prints.\n\t */\n\tprune: () => Promise<PrunedDnsRecord[]>;\n}\n\n/**\n * A stage's DNS, as every deploy runs it: the\n * stage's own hosts planned, written through each domain's provider where\n * they differ, and read back from it — and each record gkm wrote for a host\n * the stage no longer serves found, for `prune` to delete. Only A, AAAA and CNAME records of those\n * names are touched; nothing is a wildcard, and no other name is read,\n * written or deleted. What no provider confirms — a `manual` domain, a host\n * under no domain in `dns`, a key that cannot read — is resolved publicly.\n * A dry run prints the plan and writes nothing.\n *\n * @throws {DnsCredentialMissing} before anything is read or written\n * @throws {DnsRecordsNotConfirmed} when a written record does not read back\n * @throws {HostNotPointingAtServer} when a resolved host is not the server\n */\nexport async function stageDns(input: StageDnsInput): Promise<StageDnsResult> {\n\tconst { stage, server } = input;\n\tconst log = input.log ?? ((line: string) => console.log(line));\n\tconst plan = planStackDns({\n\t\tstage,\n\t\thosts: input.hosts,\n\t\tserver,\n\t\tdns: input.dns,\n\t});\n\tconst recorded = diffRecordedDns(plan, input.recorded ?? []);\n\t// A stale record's domain needs its provider's token too — the stage's\n\t// old domain, after it moved to another.\n\tconst dns = input.dns ? normalizeDnsConfig(input.dns) : {};\n\tconst staleDomains = [...new Set(recorded.stale.map((r) => r.domain))]\n\t\t.filter(\n\t\t\t(domain) => dns[domain] && !plan.domains.some((d) => d.domain === domain),\n\t\t)\n\t\t.map((domain) => ({ domain, config: dns[domain]! }));\n\tconst tokens = await dnsCredentials(\n\t\tstage,\n\t\t[\n\t\t\t...plan.domains.map(({ domain, config }) => ({ domain, config })),\n\t\t\t...staleDomains,\n\t\t],\n\t\tinput.credentials,\n\t);\n\tconst providers = {\n\t\tlog,\n\t\tcredentials: {\n\t\t\t...(input.godaddy ?? {}),\n\t\t\t...(tokens.godaddy ? { credential: tokens.godaddy } : {}),\n\t\t\tlog,\n\t\t},\n\t\t...(tokens.hostinger ? { hostinger: tokens.hostinger } : {}),\n\t\t...(input.providerFor ? { providerFor: input.providerFor } : {}),\n\t};\n\t// No `dns` at all: nothing to write, and every host is the person's to\n\t// point — checked, without a line per host saying so.\n\tconst applied: StackDnsApplied = !input.dns\n\t\t? { changes: [], confirmed: [], unconfirmed: [...plan.uncovered] }\n\t\t: await settleStackDns(plan, {\n\t\t\t\t...(input.dryRun ? { dryRun: true } : { confirm: true }),\n\t\t\t\t...providers,\n\t\t\t});\n\n\t// A record the apply already deleted — a change of mode — is not pruned.\n\tconst deleted = new Set(\n\t\tapplied.changes\n\t\t\t.filter((c) => c.action === 'delete')\n\t\t\t.map((c) => recordKey(fullName(c.domain, c.name), c.type)),\n\t);\n\tconst stale = recorded.stale.filter(\n\t\t(r) => !deleted.has(recordKey(r.fqdn, r.type)),\n\t);\n\tconst prune = () =>\n\t\tpruneStackDns(stale, {\n\t\t\tstage,\n\t\t\tdns: input.dns,\n\t\t\t...(input.dryRun ? { dryRun: true } : {}),\n\t\t\t...providers,\n\t\t});\n\n\tlet resolved: string[] = [];\n\tif (applied.unconfirmed.length > 0) {\n\t\tresolved = await checkStackDns({\n\t\t\tstage,\n\t\t\thosts: applied.unconfirmed,\n\t\t\tserver,\n\t\t\tdns: input.dns,\n\t\t\t...(input.lookup ? { lookup: input.lookup } : {}),\n\t\t});\n\t}\n\treturn {\n\t\tplan,\n\t\tchanges: applied.changes,\n\t\tconfirmed: applied.confirmed,\n\t\tresolved,\n\t\trecorded,\n\t\tprune,\n\t};\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"emit-C1e8qD08.mjs","names":[],"sources":["../src/reconcile/emit.ts"],"sourcesContent":["/**\n * The manifest, written out as a module.\n *\n * Discovery imports the application's own code — that is how it finds\n * constructs — so anything calling it evaluates the whole runtime graph: React\n * email templates, database clients, modules that read env at import time. That\n * is fine inside `gkm dev`, which is already running the app. It is not fine\n * inside a deploy config, where it fails on the first thing the deploy toolchain\n * does not expect and would keep failing on the next.\n *\n * So discovery happens once, in the build, and everything downstream reads what\n * it wrote. The same split `RestApi` makes by naming a routes glob rather than\n * importing route modules.\n *\n * A module rather than JSON, because a manifest is not just data to this\n * codebase — `as const satisfies ConstructManifest` is what keeps every id,\n * kind and provided key a *literal*, so `IdsOf`, `ProvidedKeys` and the rest\n * can select out of it. `JSON.parse` returns `any` and gives all of that up.\n */\n\nimport type {\n\tConstructManifest,\n\tCronInfo,\n\tDeclaration,\n\tDependency,\n\tFunctionInfo,\n\tQueueInfo,\n\tRestApiEndpoint,\n\tRouteInfo,\n\tSubscriberInfo,\n} from '@geekmidas/manifest';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport type { CacheBackend, EmailBackend } from '../types.js';\n\n/**\n * Fold the build's generated routes into the surface that serves them.\n *\n * The routes already exist: `gkm build` generates one handler per endpoint and\n * records its path, method and environment. What was missing is that the\n * *construct* manifest never learned about them, so a `rest-api` node said\n * `endpoints: []` while five handlers sat in `.gkm/`. Two documents describing\n * the same routes, one of them wrong.\n *\n * Merging here rather than making the construct name a glob or import its\n * endpoints: the build is the thing that already walked the filesystem and\n * already knows where it wrote each handler. Asking the declaration to restate\n * any of that is how the two come to disagree.\n *\n * Attribution is to the surface the build served — the one whose `path` is\n * the app being built. Without one, routes go to the surface that declared none\n * of its own; a surface with static endpoints (an auth server's single\n * wildcard) keeps them.\n *\n * **Only from a provider that generates one handler per route.** The `server`\n * provider generates a single catch-all — `ALL *` pointing at the Hono app that\n * routes in-process — which is a true description of that build and a false\n * description of the application. Merging it wrote `endpoints: [{ method:\n * 'ALL', path: '*' }]` into a manifest for an app with five routes, and made the\n * document's contents depend on which `--provider` ran last. A manifest must not\n * do that.\n */\nexport function withRoutes(\n\tmanifest: ConstructManifest,\n\troutes: readonly RouteInfo[],\n\toptions: { perRoute: boolean; surface?: string },\n): ConstructManifest {\n\tif (routes.length === 0 || !options.perRoute) return manifest;\n\n\tconst target = Object.entries(manifest).find(([id, declaration]) =>\n\t\toptions.surface\n\t\t\t? id === options.surface\n\t\t\t: declaration.kind === 'rest-api' && declaration.endpoints.length === 0,\n\t);\n\n\tif (!target) return manifest;\n\n\tconst [id, declaration] = target;\n\tif (declaration.kind !== 'rest-api') return manifest;\n\n\treturn {\n\t\t...manifest,\n\t\t[id]: {\n\t\t\t...declaration,\n\t\t\tendpoints: routes.map((route) => asEndpoint(manifest, id, route)),\n\t\t},\n\t};\n}\n\n/**\n * Fold in the compute the build generated: functions, crons, queue workers and\n * topic subscribers.\n *\n * All four are named by their *export*, the same way an endpoint's handler is —\n * `CronGenerator` uses the export key as the name — so none of them can declare\n * themselves and all of them are the build's to add. That is why a `Function`\n * has no `declare()` and never will.\n *\n * A worker and a subscriber **nest** inside the queue or topic that triggers\n * them, because position carries the trigger. A function and a cron are\n * top-level, because nothing triggers them but a caller or a clock.\n */\nexport function withCompute(\n\tmanifest: ConstructManifest,\n\tcompute: {\n\t\tfunctions?: readonly FunctionInfo[];\n\t\tcrons?: readonly CronInfo[];\n\t\tqueues?: readonly QueueInfo[];\n\t\tsubscribers?: readonly SubscriberInfo[];\n\t},\n): ConstructManifest {\n\tconst next: Record<string, Declaration> = { ...manifest };\n\n\tfor (const fn of compute.functions ?? []) {\n\t\tnext[fn.name] = {\n\t\t\tkind: 'function',\n\t\t\tid: fn.name,\n\t\t\thandler: fn.handler,\n\t\t\tdependencies: edgesTo(manifest, fn.dependencies),\n\t\t\tprovides: [provideKey(fn.name, 'url')],\n\t\t};\n\t}\n\n\tfor (const cron of compute.crons ?? []) {\n\t\tnext[cron.name] = {\n\t\t\tkind: 'cron',\n\t\t\tid: cron.name,\n\t\t\thandler: cron.handler,\n\t\t\tschedule: cron.schedule,\n\t\t\tdependencies: edgesTo(manifest, cron.dependencies),\n\t\t};\n\t}\n\n\t// A worker drains exactly one queue, so it is matched by the queue it names\n\t// rather than by anything about the handler.\n\tfor (const worker of compute.queues ?? []) {\n\t\tconst queue = next[canonicalId(worker.name)];\n\t\tif (queue?.kind !== 'queue') continue;\n\n\t\tnext[queue.id] = {\n\t\t\t...queue,\n\t\t\tworker: {\n\t\t\t\tid: `${queue.id}Worker`,\n\t\t\t\thandler: worker.handler,\n\t\t\t\tdependencies: edgesTo(manifest, worker.dependencies),\n\t\t\t},\n\t\t};\n\t}\n\n\tfor (const subscriber of compute.subscribers ?? []) {\n\t\tconst topicId = subscriber.topic && canonicalId(subscriber.topic);\n\t\tconst topic = topicId ? next[topicId] : undefined;\n\t\tif (topic?.kind !== 'topic') continue;\n\n\t\tnext[topic.id] = {\n\t\t\t...topic,\n\t\t\tsubscribers: [\n\t\t\t\t...topic.subscribers,\n\t\t\t\t{\n\t\t\t\t\tid: subscriber.name,\n\t\t\t\t\thandler: subscriber.handler,\n\t\t\t\t\tevents: subscriber.subscribedEvents,\n\t\t\t\t\tdependencies: edgesTo(manifest, subscriber.dependencies),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\treturn next;\n}\n\n/**\n * Construct ids as manifest edges.\n *\n * A `Dependency` carries the target's kind as well as its id, and the kind is\n * read off the manifest rather than recorded beside the edge — the same rule\n * `edgeTo` follows, for the same reason: a hand-written kind is one fact stated\n * twice, and the copy is what goes stale.\n *\n * An id that names nothing is dropped rather than thrown on. A handler can\n * depend on a construct that declares no node of its own — a lifted `Service`\n * is exactly that — and a build is the wrong place to refuse it; what a target\n * needs from this list is the subset it can actually grant.\n */\nfunction edgesTo(\n\tmanifest: ConstructManifest,\n\tids: readonly string[] = [],\n): Dependency[] {\n\treturn ids.flatMap((target) => {\n\t\tconst declaration = manifest[target];\n\n\t\treturn declaration ? [{ target, kind: declaration.kind }] : [];\n\t});\n}\n\n/** One generated route as the manifest's shape. */\nfunction asEndpoint(\n\tmanifest: ConstructManifest,\n\tsurface: string,\n\troute: RouteInfo,\n): RestApiEndpoint {\n\treturn {\n\t\t// The handler path is already unique per route and is what a target needs\n\t\t// to point a function at, so it is also the natural id.\n\t\tid: `${surface}${route.method}${route.path}`,\n\t\thandler: route.handler,\n\t\tmethod: route.method,\n\t\tpath: route.path,\n\t\t// The edges the handler declared, which is what a target reads to grant\n\t\t// this one function access to exactly those constructs. `environment` is\n\t\t// the same edges' shadow — the keys read — and cannot be turned back into\n\t\t// them, which is why the ids are carried rather than inferred.\n\t\tdependencies: edgesTo(manifest, route.dependencies),\n\t\t...(route.authorizer ? { authorizer: route.authorizer } : {}),\n\t};\n}\n\n/**\n * The manifest as TypeScript source.\n *\n * Pure, so what gets written can be asserted without touching a filesystem.\n */\nexport function manifestModule(\n\tmanifest: ConstructManifest,\n\tbackends: Backends = {},\n): string {\n\treturn `${HEADER}\nexport const constructs = ${serialise(manifest)} as const satisfies ConstructManifest;\n\n/**\n * Where the backends that are *config* rather than declaration resolved to.\n *\n * Recorded because they were being answered twice: once by the build, which\n * registers exactly one cache driver, and once by a deploy config, which told\n * the provisioner something else. The result was a deployed URL the running code\n * had no driver for — two documents disagreeing about one fact, which is what\n * this manifest exists to stop.\n */\nexport const backends = ${serialise(backends)} as const;\n\n// Derived types\nexport type Ids = IdsOf<typeof constructs>;\nexport type Construct<Id extends Ids> = DeclarationOf<typeof constructs, Id>;\nexport type Kind = Construct<Ids>['kind'];\n\n// Useful union types\nexport type ProvidedKeys = AllProvidedKeys<typeof constructs>;\nexport type Surfaces = IdsOfKind<typeof constructs, 'rest-api'>;\nexport type CacheBackend = (typeof backends)['cache'];\nexport type EmailBackend = (typeof backends)['email'];\n`;\n}\n\n/** The choices that are deployment config rather than declaration. */\nexport interface Backends {\n\tcache?: CacheBackend;\n\temail?: EmailBackend;\n}\n\nconst HEADER = `// Generated by \\`gkm build\\`. Do not edit.\n//\n// Discovery imports application code, so it runs once here rather than in every\n// consumer — a deploy config that discovered for itself would evaluate the whole\n// runtime graph inside its own toolchain. This is the result, as literals.\n\nimport type {\n\tAllProvidedKeys,\n\tConstructManifest,\n\tDeclarationOf,\n\tIdsOf,\n\tIdsOfKind,\n} from '@geekmidas/manifest';\n`;\n\n/**\n * JSON, re-indented with tabs and with keys left unquoted where they can be.\n *\n * Cosmetic, and worth it: this file is read by people trying to work out what\n * their app declares, and a wall of quoted keys reads as output rather than as\n * something anyone is expected to understand.\n */\nfunction serialise(value: object): string {\n\treturn JSON.stringify(value, null, '\\t')\n\t\t.replace(/^(\\t*)\"([A-Za-z_$][\\w$]*)\":/gm, '$1$2:')\n\t\t.replace(/\"/g, \"'\");\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"emit-Dmc1nvdS.cjs","names":["provideKey","canonicalId"],"sources":["../src/reconcile/emit.ts"],"sourcesContent":["/**\n * The manifest, written out as a module.\n *\n * Discovery imports the application's own code — that is how it finds\n * constructs — so anything calling it evaluates the whole runtime graph: React\n * email templates, database clients, modules that read env at import time. That\n * is fine inside `gkm dev`, which is already running the app. It is not fine\n * inside a deploy config, where it fails on the first thing the deploy toolchain\n * does not expect and would keep failing on the next.\n *\n * So discovery happens once, in the build, and everything downstream reads what\n * it wrote. The same split `RestApi` makes by naming a routes glob rather than\n * importing route modules.\n *\n * A module rather than JSON, because a manifest is not just data to this\n * codebase — `as const satisfies ConstructManifest` is what keeps every id,\n * kind and provided key a *literal*, so `IdsOf`, `ProvidedKeys` and the rest\n * can select out of it. `JSON.parse` returns `any` and gives all of that up.\n */\n\nimport type {\n\tConstructManifest,\n\tCronInfo,\n\tDeclaration,\n\tDependency,\n\tFunctionInfo,\n\tQueueInfo,\n\tRestApiEndpoint,\n\tRouteInfo,\n\tSubscriberInfo,\n} from '@geekmidas/manifest';\nimport { canonicalId, provideKey } from '@geekmidas/manifest';\nimport type { CacheBackend, EmailBackend } from '../types.js';\n\n/**\n * Fold the build's generated routes into the surface that serves them.\n *\n * The routes already exist: `gkm build` generates one handler per endpoint and\n * records its path, method and environment. What was missing is that the\n * *construct* manifest never learned about them, so a `rest-api` node said\n * `endpoints: []` while five handlers sat in `.gkm/`. Two documents describing\n * the same routes, one of them wrong.\n *\n * Merging here rather than making the construct name a glob or import its\n * endpoints: the build is the thing that already walked the filesystem and\n * already knows where it wrote each handler. Asking the declaration to restate\n * any of that is how the two come to disagree.\n *\n * Attribution is to the surface the build served — the one whose `path` is\n * the app being built. Without one, routes go to the surface that declared none\n * of its own; a surface with static endpoints (an auth server's single\n * wildcard) keeps them.\n *\n * **Only from a provider that generates one handler per route.** The `server`\n * provider generates a single catch-all — `ALL *` pointing at the Hono app that\n * routes in-process — which is a true description of that build and a false\n * description of the application. Merging it wrote `endpoints: [{ method:\n * 'ALL', path: '*' }]` into a manifest for an app with five routes, and made the\n * document's contents depend on which `--provider` ran last. A manifest must not\n * do that.\n */\nexport function withRoutes(\n\tmanifest: ConstructManifest,\n\troutes: readonly RouteInfo[],\n\toptions: { perRoute: boolean; surface?: string },\n): ConstructManifest {\n\tif (routes.length === 0 || !options.perRoute) return manifest;\n\n\tconst target = Object.entries(manifest).find(([id, declaration]) =>\n\t\toptions.surface\n\t\t\t? id === options.surface\n\t\t\t: declaration.kind === 'rest-api' && declaration.endpoints.length === 0,\n\t);\n\n\tif (!target) return manifest;\n\n\tconst [id, declaration] = target;\n\tif (declaration.kind !== 'rest-api') return manifest;\n\n\treturn {\n\t\t...manifest,\n\t\t[id]: {\n\t\t\t...declaration,\n\t\t\tendpoints: routes.map((route) => asEndpoint(manifest, id, route)),\n\t\t},\n\t};\n}\n\n/**\n * Fold in the compute the build generated: functions, crons, queue workers and\n * topic subscribers.\n *\n * All four are named by their *export*, the same way an endpoint's handler is —\n * `CronGenerator` uses the export key as the name — so none of them can declare\n * themselves and all of them are the build's to add. That is why a `Function`\n * has no `declare()` and never will.\n *\n * A worker and a subscriber **nest** inside the queue or topic that triggers\n * them, because position carries the trigger. A function and a cron are\n * top-level, because nothing triggers them but a caller or a clock.\n */\nexport function withCompute(\n\tmanifest: ConstructManifest,\n\tcompute: {\n\t\tfunctions?: readonly FunctionInfo[];\n\t\tcrons?: readonly CronInfo[];\n\t\tqueues?: readonly QueueInfo[];\n\t\tsubscribers?: readonly SubscriberInfo[];\n\t},\n): ConstructManifest {\n\tconst next: Record<string, Declaration> = { ...manifest };\n\n\tfor (const fn of compute.functions ?? []) {\n\t\tnext[fn.name] = {\n\t\t\tkind: 'function',\n\t\t\tid: fn.name,\n\t\t\thandler: fn.handler,\n\t\t\tdependencies: edgesTo(manifest, fn.dependencies),\n\t\t\tprovides: [provideKey(fn.name, 'url')],\n\t\t};\n\t}\n\n\tfor (const cron of compute.crons ?? []) {\n\t\tnext[cron.name] = {\n\t\t\tkind: 'cron',\n\t\t\tid: cron.name,\n\t\t\thandler: cron.handler,\n\t\t\tschedule: cron.schedule,\n\t\t\tdependencies: edgesTo(manifest, cron.dependencies),\n\t\t};\n\t}\n\n\t// A worker drains exactly one queue, so it is matched by the queue it names\n\t// rather than by anything about the handler.\n\tfor (const worker of compute.queues ?? []) {\n\t\tconst queue = next[canonicalId(worker.name)];\n\t\tif (queue?.kind !== 'queue') continue;\n\n\t\tnext[queue.id] = {\n\t\t\t...queue,\n\t\t\tworker: {\n\t\t\t\tid: `${queue.id}Worker`,\n\t\t\t\thandler: worker.handler,\n\t\t\t\tdependencies: edgesTo(manifest, worker.dependencies),\n\t\t\t},\n\t\t};\n\t}\n\n\tfor (const subscriber of compute.subscribers ?? []) {\n\t\tconst topicId = subscriber.topic && canonicalId(subscriber.topic);\n\t\tconst topic = topicId ? next[topicId] : undefined;\n\t\tif (topic?.kind !== 'topic') continue;\n\n\t\tnext[topic.id] = {\n\t\t\t...topic,\n\t\t\tsubscribers: [\n\t\t\t\t...topic.subscribers,\n\t\t\t\t{\n\t\t\t\t\tid: subscriber.name,\n\t\t\t\t\thandler: subscriber.handler,\n\t\t\t\t\tevents: subscriber.subscribedEvents,\n\t\t\t\t\tdependencies: edgesTo(manifest, subscriber.dependencies),\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t}\n\n\treturn next;\n}\n\n/**\n * Construct ids as manifest edges.\n *\n * A `Dependency` carries the target's kind as well as its id, and the kind is\n * read off the manifest rather than recorded beside the edge — the same rule\n * `edgeTo` follows, for the same reason: a hand-written kind is one fact stated\n * twice, and the copy is what goes stale.\n *\n * An id that names nothing is dropped rather than thrown on. A handler can\n * depend on a construct that declares no node of its own — a lifted `Service`\n * is exactly that — and a build is the wrong place to refuse it; what a target\n * needs from this list is the subset it can actually grant.\n */\nfunction edgesTo(\n\tmanifest: ConstructManifest,\n\tids: readonly string[] = [],\n): Dependency[] {\n\treturn ids.flatMap((target) => {\n\t\tconst declaration = manifest[target];\n\n\t\treturn declaration ? [{ target, kind: declaration.kind }] : [];\n\t});\n}\n\n/** One generated route as the manifest's shape. */\nfunction asEndpoint(\n\tmanifest: ConstructManifest,\n\tsurface: string,\n\troute: RouteInfo,\n): RestApiEndpoint {\n\treturn {\n\t\t// The handler path is already unique per route and is what a target needs\n\t\t// to point a function at, so it is also the natural id.\n\t\tid: `${surface}${route.method}${route.path}`,\n\t\thandler: route.handler,\n\t\tmethod: route.method,\n\t\tpath: route.path,\n\t\t// The edges the handler declared, which is what a target reads to grant\n\t\t// this one function access to exactly those constructs. `environment` is\n\t\t// the same edges' shadow — the keys read — and cannot be turned back into\n\t\t// them, which is why the ids are carried rather than inferred.\n\t\tdependencies: edgesTo(manifest, route.dependencies),\n\t\t...(route.authorizer ? { authorizer: route.authorizer } : {}),\n\t};\n}\n\n/**\n * The manifest as TypeScript source.\n *\n * Pure, so what gets written can be asserted without touching a filesystem.\n */\nexport function manifestModule(\n\tmanifest: ConstructManifest,\n\tbackends: Backends = {},\n): string {\n\treturn `${HEADER}\nexport const constructs = ${serialise(manifest)} as const satisfies ConstructManifest;\n\n/**\n * Where the backends that are *config* rather than declaration resolved to.\n *\n * Recorded because they were being answered twice: once by the build, which\n * registers exactly one cache driver, and once by a deploy config, which told\n * the provisioner something else. The result was a deployed URL the running code\n * had no driver for — two documents disagreeing about one fact, which is what\n * this manifest exists to stop.\n */\nexport const backends = ${serialise(backends)} as const;\n\n// Derived types\nexport type Ids = IdsOf<typeof constructs>;\nexport type Construct<Id extends Ids> = DeclarationOf<typeof constructs, Id>;\nexport type Kind = Construct<Ids>['kind'];\n\n// Useful union types\nexport type ProvidedKeys = AllProvidedKeys<typeof constructs>;\nexport type Surfaces = IdsOfKind<typeof constructs, 'rest-api'>;\nexport type CacheBackend = (typeof backends)['cache'];\nexport type EmailBackend = (typeof backends)['email'];\n`;\n}\n\n/** The choices that are deployment config rather than declaration. */\nexport interface Backends {\n\tcache?: CacheBackend;\n\temail?: EmailBackend;\n}\n\nconst HEADER = `// Generated by \\`gkm build\\`. Do not edit.\n//\n// Discovery imports application code, so it runs once here rather than in every\n// consumer — a deploy config that discovered for itself would evaluate the whole\n// runtime graph inside its own toolchain. This is the result, as literals.\n\nimport type {\n\tAllProvidedKeys,\n\tConstructManifest,\n\tDeclarationOf,\n\tIdsOf,\n\tIdsOfKind,\n} from '@geekmidas/manifest';\n`;\n\n/**\n * JSON, re-indented with tabs and with keys left unquoted where they can be.\n *\n * Cosmetic, and worth it: this file is read by people trying to work out what\n * their app declares, and a wall of quoted keys reads as output rather than as\n * something anyone is expected to understand.\n */\nfunction serialise(value: object): string {\n\treturn JSON.stringify(value, null, '\\t')\n\t\t.replace(/^(\\t*)\"([A-Za-z_$][\\w$]*)\":/gm, '$1$2:')\n\t\t.replace(/\"/g, \"'\");\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6DA,SAAgB,WACf,UACA,QACA,SACoB;CACpB,IAAI,OAAO,WAAW,KAAK,CAAC,QAAQ,UAAU,OAAO;CAErD,MAAM,SAAS,OAAO,QAAQ,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,iBAClD,QAAQ,UACL,OAAO,QAAQ,UACf,YAAY,SAAS,cAAc,YAAY,UAAU,WAAW,CACxE;CAEA,IAAI,CAAC,QAAQ,OAAO;CAEpB,MAAM,CAAC,IAAI,eAAe;CAC1B,IAAI,YAAY,SAAS,YAAY,OAAO;CAE5C,OAAO;EACN,GAAG;GACF,KAAK;GACL,GAAG;GACH,WAAW,OAAO,KAAK,UAAU,WAAW,UAAU,IAAI,KAAK,CAAC;EACjE;CACD;AACD;;;;;;;;;;;;;;AAeA,SAAgB,YACf,UACA,SAMoB;CACpB,MAAM,OAAoC,EAAE,GAAG,SAAS;CAExD,KAAK,MAAM,MAAM,QAAQ,aAAa,CAAC,GACtC,KAAK,GAAG,QAAQ;EACf,MAAM;EACN,IAAI,GAAG;EACP,SAAS,GAAG;EACZ,cAAc,QAAQ,UAAU,GAAG,YAAY;EAC/C,UAAU,EAAA,GAACA,oBAAAA,WAAAA,CAAW,GAAG,MAAM,KAAK,CAAC;CACtC;CAGD,KAAK,MAAM,QAAQ,QAAQ,SAAS,CAAC,GACpC,KAAK,KAAK,QAAQ;EACjB,MAAM;EACN,IAAI,KAAK;EACT,SAAS,KAAK;EACd,UAAU,KAAK;EACf,cAAc,QAAQ,UAAU,KAAK,YAAY;CAClD;CAKD,KAAK,MAAM,UAAU,QAAQ,UAAU,CAAC,GAAG;EAC1C,MAAM,QAAQ,MAAA,GAAKC,oBAAAA,YAAAA,CAAY,OAAO,IAAI;EAC1C,IAAI,OAAO,SAAS,SAAS;EAE7B,KAAK,MAAM,MAAM;GAChB,GAAG;GACH,QAAQ;IACP,IAAI,GAAG,MAAM,GAAG;IAChB,SAAS,OAAO;IAChB,cAAc,QAAQ,UAAU,OAAO,YAAY;GACpD;EACD;CACD;CAEA,KAAK,MAAM,cAAc,QAAQ,eAAe,CAAC,GAAG;EACnD,MAAM,UAAU,WAAW,UAAA,GAASA,oBAAAA,YAAAA,CAAY,WAAW,KAAK;EAChE,MAAM,QAAQ,UAAU,KAAK,WAAW,KAAA;EACxC,IAAI,OAAO,SAAS,SAAS;EAE7B,KAAK,MAAM,MAAM;GAChB,GAAG;GACH,aAAa,CACZ,GAAG,MAAM,aACT;IACC,IAAI,WAAW;IACf,SAAS,WAAW;IACpB,QAAQ,WAAW;IACnB,cAAc,QAAQ,UAAU,WAAW,YAAY;GACxD,CACD;EACD;CACD;CAEA,OAAO;AACR;;;;;;;;;;;;;;AAeA,SAAS,QACR,UACA,MAAyB,CAAC,GACX;CACf,OAAO,IAAI,SAAS,WAAW;EAC9B,MAAM,cAAc,SAAS;EAE7B,OAAO,cAAc,CAAC;GAAE;GAAQ,MAAM,YAAY;EAAK,CAAC,IAAI,CAAC;CAC9D,CAAC;AACF;;AAGA,SAAS,WACR,UACA,SACA,OACkB;CAClB,OAAO;EAGN,IAAI,GAAG,UAAU,MAAM,SAAS,MAAM;EACtC,SAAS,MAAM;EACf,QAAQ,MAAM;EACd,MAAM,MAAM;EAKZ,cAAc,QAAQ,UAAU,MAAM,YAAY;EAClD,GAAI,MAAM,aAAa,EAAE,YAAY,MAAM,WAAW,IAAI,CAAC;CAC5D;AACD;;;;;;AAOA,SAAgB,eACf,UACA,WAAqB,CAAC,GACb;CACT,OAAO,GAAG,OAAO;4BACU,UAAU,QAAQ,EAAE;;;;;;;;;;;0BAWtB,UAAU,QAAQ,EAAE;;;;;;;;;;;;;AAa9C;AAQA,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;AAsBf,SAAS,UAAU,OAAuB;CACzC,OAAO,KAAK,UAAU,OAAO,MAAM,GAAI,CAAC,CACtC,QAAQ,iCAAiC,OAAO,CAAC,CACjD,QAAQ,MAAM,GAAG;AACpB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"github-CO8W9SDs.cjs","names":["GkmError","projectBucketName","deployIdentity","PROJECT_BUCKET_CREATE_ACTIONS","s3SecretsLocation","s3SecretsKey","secretsParameterName","secretsManagerSecretName","stageProvider","bucketName","iamUserName","IAM_USER_PATH","stageBackups","backupsUserName","PROJECT_BUCKET_LIFECYCLE_ACTIONS","stageDnsDomains","execFileSync","ListOpenIDConnectProvidersCommand","CreateOpenIDConnectProviderCommand","GetRoleCommand","UpdateAssumeRolePolicyCommand","ListRoleTagsCommand","CreateRoleCommand","DetachRolePolicyCommand","DeleteRolePolicyCommand","AttachRolePolicyCommand","TagRoleCommand","PutRolePolicyCommand","UntagRoleCommand","ListAttachedRolePoliciesCommand","IAMClient","loadWorkspaceConfig","discover","constructGlobs","readKey","keystoreProject","getKeyPath","isRemoteStore","secretsStoreFor","GkmError"],"sources":["../src/deploy/githubOidc.ts","../src/deploy/githubPolicy.ts","../src/deploy/github.ts"],"sourcesContent":["/**\n * The `sub` GitHub puts in a deploy job's OIDC token — what the deploy role's\n * trust policy has to match exactly.\n *\n * It is not always `repo:<owner>/<name>:environment:<stage>`. A repository\n * (or its organisation) decides the format, and says which through\n * `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`:\n *\n * - the default: `repo:<owner>/<name>:environment:<stage>`;\n * - the immutable subject — the default for repositories created after\n * 15 July 2026, opt-in before — puts the owner's and the repository's ids in\n * the `repo` segment: `repo:<owner>@<ownerId>/<name>@<repoId>:environment:<stage>`.\n * The API hands that segment back as `sub_claim_prefix`;\n * - a custom template (`use_default: false`) builds the `sub` from\n * `include_claim_keys`, in order, each as `<key>:<value>` joined by `:`,\n * where `context` is `environment:<stage>` in an environment job.\n *\n * A `:` inside a value is sent as `%3A`.\n *\n * See https://docs.github.com/en/actions/reference/security/oidc.\n */\n\nimport { GkmError } from '../errors';\nimport type { Gh } from './github.js';\n\n/** `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`. */\nexport interface OidcSubjectCustomization {\n\tuse_default: boolean;\n\tinclude_claim_keys?: string[];\n\tuse_immutable_subject?: boolean;\n\t/** The `repo` segment of the subject, as GitHub will send it. */\n\tsub_claim_prefix?: string;\n}\n\n/** What `GET /repos/{owner}/{repo}` says that a subject may include. */\nexport interface RepoFacts {\n\townerId: number;\n\trepoId: number;\n\tvisibility: string;\n}\n\n/**\n * Template keys whose values are known for a repository's environment job\n * before the job runs. Anything else — `job_workflow_ref`, `ref`, `sha`,\n * `run_id`, `workflow`, `actor`, a custom property — depends on the run.\n */\nexport const SUPPORTED_CLAIM_KEYS = [\n\t'repo',\n\t'context',\n\t'environment',\n\t'repository',\n\t'repository_owner',\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n] as const;\n\nconst NEEDS_FACTS = new Set([\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n]);\n\n/** A `:` inside a claim value is sent as `%3A`. */\nfunction claim(value: string): string {\n\treturn value.replaceAll(':', '%3A');\n}\n\n/** Which kind of subject the repository uses, for the output. */\nexport type OidcSubjectKind = 'default' | 'immutable' | 'custom' | 'assumed';\n\n/** Whether {@link oidcSubject} needs the repository's ids or visibility. */\nexport function needsRepoFacts(\n\tcustomization: OidcSubjectCustomization,\n): boolean {\n\tconst immutableWithoutPrefix =\n\t\tcustomization.use_immutable_subject === true &&\n\t\t!customization.sub_claim_prefix;\n\tif (customization.use_default) return immutableWithoutPrefix;\n\n\tconst keys = customization.include_claim_keys ?? [];\n\treturn keys.some(\n\t\t(key) => NEEDS_FACTS.has(key) || (key === 'repo' && immutableWithoutPrefix),\n\t);\n}\n\n/**\n * The exact `sub` of a job in `repo`'s `stage` environment.\n *\n * @throws OidcSubjectNotSupported when a custom template includes a claim\n * whose value depends on the run, so no one subject can be trusted.\n */\nexport function oidcSubject(\n\trepo: string,\n\tstage: string,\n\tcustomization: OidcSubjectCustomization,\n\tfacts?: RepoFacts,\n): { subject: string; kind: OidcSubjectKind } {\n\tconst [owner = '', name = ''] = repo.split('/');\n\tconst environment = `environment:${claim(stage)}`;\n\n\tconst repoSegment = (): string => {\n\t\tif (customization.sub_claim_prefix) return customization.sub_claim_prefix;\n\t\tif (customization.use_immutable_subject) {\n\t\t\tif (!facts) throw new RepoFactsRequired(repo);\n\t\t\treturn `repo:${owner}@${facts.ownerId}/${name}@${facts.repoId}`;\n\t\t}\n\t\treturn `repo:${repo}`;\n\t};\n\tconst immutable =\n\t\tcustomization.use_immutable_subject === true ||\n\t\tcustomization.sub_claim_prefix !== undefined;\n\n\tif (customization.use_default) {\n\t\treturn {\n\t\t\tsubject: `${repoSegment()}:${environment}`,\n\t\t\tkind: immutable ? 'immutable' : 'default',\n\t\t};\n\t}\n\n\tconst keys = customization.include_claim_keys ?? [];\n\tconst unsupported = keys.filter(\n\t\t(key) => !(SUPPORTED_CLAIM_KEYS as readonly string[]).includes(key),\n\t);\n\tif (keys.length === 0 || unsupported.length > 0) {\n\t\tthrow new OidcSubjectNotSupported(repo, keys, unsupported);\n\t}\n\tif (keys.some((key) => NEEDS_FACTS.has(key)) && !facts) {\n\t\tthrow new RepoFactsRequired(repo);\n\t}\n\n\tconst parts = keys.map((key) => {\n\t\tswitch (key) {\n\t\t\tcase 'repo':\n\t\t\t\treturn repoSegment();\n\t\t\tcase 'context':\n\t\t\tcase 'environment':\n\t\t\t\treturn environment;\n\t\t\tcase 'repository':\n\t\t\t\treturn `repository:${claim(repo)}`;\n\t\t\tcase 'repository_owner':\n\t\t\t\treturn `repository_owner:${claim(owner)}`;\n\t\t\tcase 'repository_owner_id':\n\t\t\t\treturn `repository_owner_id:${facts!.ownerId}`;\n\t\t\tcase 'repository_id':\n\t\t\t\treturn `repository_id:${facts!.repoId}`;\n\t\t\tdefault:\n\t\t\t\treturn `repository_visibility:${claim(facts!.visibility)}`;\n\t\t}\n\t});\n\treturn { subject: parts.join(':'), kind: 'custom' };\n}\n\n/** What {@link resolveOidcSubject} found. */\nexport interface ResolvedOidcSubject {\n\tsubject: string;\n\tkind: OidcSubjectKind;\n\t/** Set when the format could not be read and the default was assumed. */\n\twarning?: string;\n}\n\n/** The endpoint a repository's subject format is read from. */\nexport function customizationEndpoint(repo: string): string {\n\treturn `repos/${repo}/actions/oidc/customization/sub`;\n}\n\n/**\n * The `sub` GitHub will send for `repo`'s `stage` environment, read from the\n * repository's OIDC settings through `gh`.\n *\n * A repository on a custom template that names no keys of its own uses its\n * organisation's, read from `orgs/{owner}/actions/oidc/customization/sub`.\n * When the settings cannot be read at all — no permission, or a GitHub\n * Enterprise Server without the endpoint — the default format is assumed, with\n * a warning saying where to check it.\n */\nexport function resolveOidcSubject(\n\tgh: Gh,\n\trepo: string,\n\tstage: string,\n): ResolvedOidcSubject {\n\tconst endpoint = customizationEndpoint(repo);\n\tlet customization: OidcSubjectCustomization;\n\ttry {\n\t\tcustomization = JSON.parse(gh(['api', endpoint]));\n\t} catch {\n\t\treturn {\n\t\t\tsubject: `repo:${repo}:environment:${claim(stage)}`,\n\t\t\tkind: 'assumed',\n\t\t\twarning: `Could not read ${endpoint}, so the default subject format is assumed. Check it with: gh api ${endpoint} — a repository on the immutable subject or a custom template sends a different sub, and the role would not be assumable.`,\n\t\t};\n\t}\n\n\tif (\n\t\t!customization.use_default &&\n\t\t(customization.include_claim_keys ?? []).length === 0\n\t) {\n\t\t// The repository follows its organisation's template.\n\t\tconst [owner = ''] = repo.split('/');\n\t\tconst orgEndpoint = `orgs/${owner}/actions/oidc/customization/sub`;\n\t\tlet org: { include_claim_keys?: string[]; use_immutable_subject?: boolean };\n\t\ttry {\n\t\t\torg = JSON.parse(gh(['api', orgEndpoint]));\n\t\t} catch {\n\t\t\tthrow new OrgOidcTemplateUnreadable(repo, orgEndpoint);\n\t\t}\n\t\tcustomization = {\n\t\t\t...customization,\n\t\t\tinclude_claim_keys: org.include_claim_keys ?? [],\n\t\t\tuse_immutable_subject:\n\t\t\t\tcustomization.use_immutable_subject ?? org.use_immutable_subject,\n\t\t};\n\t}\n\n\tconst facts = needsRepoFacts(customization)\n\t\t? readRepoFacts(gh, repo)\n\t\t: undefined;\n\treturn oidcSubject(repo, stage, customization, facts);\n}\n\nfunction readRepoFacts(gh: Gh, repo: string): RepoFacts {\n\tconst raw = JSON.parse(gh(['api', `repos/${repo}`])) as {\n\t\tid: number;\n\t\towner: { id: number };\n\t\tvisibility: string;\n\t};\n\treturn {\n\t\townerId: raw.owner.id,\n\t\trepoId: raw.id,\n\t\tvisibility: raw.visibility,\n\t};\n}\n\n/**\n * The repository's custom subject template includes a claim whose value\n * depends on the run — or names none — so no one subject can be trusted.\n */\nexport class OidcSubjectNotSupported extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly includeClaimKeys: string[],\n\t\treadonly unsupportedKeys: string[],\n\t) {\n\t\tsuper(\n\t\t\t`${repo} builds its OIDC subject from a custom template, ${JSON.stringify(includeClaimKeys)}, and ${\n\t\t\t\tunsupportedKeys.length > 0\n\t\t\t\t\t? `${unsupportedKeys.join(', ')} depend${unsupportedKeys.length === 1 ? 's' : ''} on the run`\n\t\t\t\t\t: 'it names no claims'\n\t\t\t}, so the deploy role cannot trust one exact subject. Set the repository back to the default subject — gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true — (or keep the immutable one by also passing -F use_immutable_subject=true), or use a template built only from ${SUPPORTED_CLAIM_KEYS.join(', ')}; then run this again.`,\n\t\t);\n\t\tthis.name = 'OidcSubjectNotSupported';\n\t}\n}\n\n/** The subject needs the repository's ids, and none were read. */\nexport class RepoFactsRequired extends GkmError {\n\tconstructor(readonly repo: string) {\n\t\tsuper(\n\t\t\t`The OIDC subject of ${repo} includes its owner and repository ids; read them with gh api repos/${repo} and pass them in.`,\n\t\t);\n\t\tthis.name = 'RepoFactsRequired';\n\t}\n}\n\n/**\n * The repository uses its organisation's subject template, and that template\n * could not be read — so what GitHub sends is unknown, and is not guessed.\n */\nexport class OrgOidcTemplateUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly endpoint: string,\n\t) {\n\t\tsuper(\n\t\t\t`${repo} uses its organisation's OIDC subject template, and ${endpoint} could not be read. Run this as an organisation owner (gh auth refresh -s admin:org), or set the repository back to the default subject with gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true; then run this again.`,\n\t\t);\n\t\tthis.name = 'OrgOidcTemplateUnreadable';\n\t}\n}\n","/**\n * What the deploy role may do in the stage's account.\n *\n * An SST stage creates its own stack, which is most of AWS, so its role gets\n * `AdministratorAccess`. A stage deployed with the `compose` target builds and\n * runs containers on a server; from AWS its deploy job needs only the stage's\n * secrets — read, and written back when a deploy generates a new one: the one\n * object in the project bucket, or the SSM parameter or Secrets Manager secret\n * — and,\n * when the deploy state is kept in AWS, the stage's state, and, when the\n * stage's domain is in Route53, the records of its hosted zone, and, when an\n * `s3` provider backs its buckets, those buckets and the IAM user each is\n * reached with — by the names the provider gives them — and, when it runs\n * Postgres, the project bucket its backups go to and the backups user. So a\n * compose stage gets an inline policy naming exactly those resources and\n * nothing else.\n *\n * `--policy-arn` replaces either.\n */\n\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { stageBackups } from '../backups/config.js';\nimport { backupsUserName } from '../backups/provision.js';\nimport { stageDnsDomains } from '../compose/dns.js';\nimport { stageProvider } from '../providers/config.js';\nimport {\n\tPROJECT_BUCKET_CREATE_ACTIONS,\n\tPROJECT_BUCKET_LIFECYCLE_ACTIONS,\n\tprojectBucketName,\n} from '../providers/projectBucket.js';\nimport {\n\tbucketName,\n\tIAM_USER_PATH,\n\tiamUserName,\n\tSUFFIX_LENGTH,\n} from '../providers/s3/naming.js';\nimport { secretsParameterName } from '../secrets/aws.js';\nimport { s3SecretsKey, s3SecretsLocation } from '../secrets/providers.js';\nimport { secretsManagerSecretName } from '../secrets/secretsManager.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { deployIdentity } from './identity.js';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default\n * outside compose — said out loud in the output, and replaceable with\n * `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\n/** The name of the inline policy a compose stage's role is given. */\nexport const SCOPED_POLICY_NAME = 'gkm-deploy';\n\n/** One statement of an IAM policy document. */\nexport interface PolicyStatement {\n\tSid: string;\n\tEffect: 'Allow';\n\tAction: string[];\n\tResource: string | string[];\n\tCondition?: Record<string, Record<string, string>>;\n}\n\n/** What the role is given. */\nexport type DeployAccess =\n\t| {\n\t\t\tkind: 'managed';\n\t\t\tpolicyArn: string;\n\t\t\t/** Why, for the output. */\n\t\t\treason: string;\n\t }\n\t| {\n\t\t\tkind: 'scoped';\n\t\t\t/** The statements, in the account the role is in. */\n\t\t\tstatements(account: string): PolicyStatement[];\n\t\t\t/** What each statement is for, for the output. */\n\t\t\tdescribe: string[];\n\t };\n\n/** Every target the stage's apps deploy through. */\nexport function deployTargets(workspace: NormalizedWorkspace): string[] {\n\tconst targets = new Set<string>([workspace.deploy?.default ?? 'dokploy']);\n\tfor (const app of Object.values(workspace.apps)) {\n\t\ttargets.add(app.resolvedDeployTarget);\n\t}\n\treturn [...targets].sort();\n}\n\n/**\n * The access the stage's deploy role is given: `--policy-arn` when passed,\n * the scoped policy for a stage only the compose target deploys, and\n * `AdministratorAccess` otherwise.\n */\nexport function deployAccess(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tpolicyArn?: string,\n\t/** The stage's constructs: its buckets, and whether it runs Postgres. */\n\tmanifest: ConstructManifest = {},\n): DeployAccess {\n\tconst buckets = Object.entries(manifest)\n\t\t.filter(([, d]) => d.kind === 'objects')\n\t\t.map(([id]) => id);\n\tif (policyArn) {\n\t\treturn { kind: 'managed', policyArn, reason: '--policy-arn' };\n\t}\n\n\tconst targets = deployTargets(workspace);\n\tif (targets.length !== 1 || targets[0] !== 'compose') {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `the ${targets.join(', ')} target${targets.length === 1 ? '' : 's'} deploy${targets.length === 1 ? 's' : ''} infrastructure; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst project = workspace.name;\n\tconst store = workspace.secrets.store ?? 'file';\n\tconst state = workspace.state;\n\tconst custom =\n\t\t(typeof store === 'object' && typeof store.provider === 'object') ||\n\t\t(state !== undefined && typeof state.provider === 'object');\n\tif (custom) {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `a custom ${typeof store === 'object' && typeof store.provider === 'object' ? 'secrets store' : 'state provider'} cannot be scoped; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst describe: string[] = [];\n\tconst build: ((account: string) => PolicyStatement[])[] = [];\n\n\t// The project bucket's name is fixed by the account, so the role is given\n\t// exactly it, and the actions that create it for whichever run is first.\n\tconst projectBucketIn = (account: string) =>\n\t\tprojectBucketName(deployIdentity(workspace, stage).scope, account);\n\tconst projectBucketStatement = (account: string): PolicyStatement => ({\n\t\tSid: 'ProjectBucket',\n\t\tEffect: 'Allow',\n\t\tAction: [...PROJECT_BUCKET_CREATE_ACTIONS],\n\t\tResource: `arn:aws:s3:::${projectBucketIn(account)}`,\n\t});\n\t// Whether the state's statements already cover the secrets' bucket.\n\tconst stateInSecretsBucket =\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 's3' &&\n\t\tstate?.provider === 's3' &&\n\t\tstate.bucket === (store as { bucket?: string }).bucket;\n\n\tif (typeof store === 'object' && store.provider === 's3') {\n\t\tconst location = s3SecretsLocation(store, state);\n\t\tconst key = s3SecretsKey(location.prefix, project, stage);\n\t\tconst named = location.bucket;\n\t\tconst bucketIn = (account: string) => named ?? projectBucketIn(account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and update s3://${named}/${key}`\n\t\t\t\t: `read and update ${key} in the project bucket ${projectBucketIn('<account>')}${stateInSecretsBucket ? '' : ', creating it'}`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['s3:GetObject', 's3:PutObject'],\n\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}/${key}`,\n\t\t\t},\n\t\t\t// The state's statements give these when it is in the same bucket.\n\t\t\t...(stateInSecretsBucket\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// HeadBucket, and secrets not written yet a 404, not a 403.\n\t\t\t\t\t\t\tSid: 'StageSecretsBucket',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['s3:ListBucket'],\n\t\t\t\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...(named ? [] : [projectBucketStatement(account)]),\n\t\t\t\t\t]),\n\t\t]);\n\t} else if (typeof store === 'object' && store.provider === 'ssm') {\n\t\tconst { region } = store;\n\t\tconst name = secretsParameterName(project, stage);\n\t\tdescribe.push(`read and update ${name} (SSM, ${region})`);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${name}`,\n\t\t\t},\n\t\t]);\n\t} else if (\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 'secrets-manager'\n\t) {\n\t\tconst { region, kmsKeyId } = store as { region: string; kmsKeyId?: string };\n\t\tconst name = secretsManagerSecretName(project, stage);\n\t\tdescribe.push(`read and update ${name} (Secrets Manager, ${region})`);\n\t\tbuild.push((account) => {\n\t\t\tconst statements: PolicyStatement[] = [\n\t\t\t\t{\n\t\t\t\t\tSid: 'StageSecrets',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: [\n\t\t\t\t\t\t'secretsmanager:GetSecretValue',\n\t\t\t\t\t\t'secretsmanager:PutSecretValue',\n\t\t\t\t\t\t'secretsmanager:CreateSecret',\n\t\t\t\t\t],\n\t\t\t\t\t// Secrets Manager appends six random characters to a secret's ARN.\n\t\t\t\t\tResource: `arn:aws:secretsmanager:${region}:${account}:secret:${name}-??????`,\n\t\t\t\t},\n\t\t\t];\n\t\t\tif (kmsKeyId) {\n\t\t\t\tstatements.push({\n\t\t\t\t\tSid: 'StageSecretsKey',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: ['kms:Decrypt', 'kms:GenerateDataKey'],\n\t\t\t\t\tResource: kmsKeyId.startsWith('arn:') ? kmsKeyId : '*',\n\t\t\t\t\tCondition: {\n\t\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t\t'kms:ViaService': `secretsmanager.${region}.amazonaws.com`,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn statements;\n\t\t});\n\t}\n\n\tif (state?.provider === 'ssm') {\n\t\tconst { region } = state;\n\t\tconst path = `/gkm/${project}/${stage}/`;\n\t\tdescribe.push(\n\t\t\t`read and write the deploy state under ${path} (SSM, ${region})`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageState',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter', 'ssm:DeleteParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${path}*`,\n\t\t\t},\n\t\t]);\n\t} else if (state?.provider === 's3') {\n\t\tconst prefix = (state.prefix ?? 'gkm').replace(/\\/+$/, '');\n\t\tconst path = `${prefix ? `${prefix}/` : ''}${project}/${stage}/`;\n\t\tconst named = state.bucket;\n\t\t// No bucket named: the project bucket, which the first deploy creates.\n\t\tconst bucketIn = (account: string) => named ?? projectBucketIn(account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and write the deploy state under s3://${named}/${path}`\n\t\t\t\t: `create the project bucket ${projectBucketIn('<account>')}, and read and write the deploy state under ${path} in it`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t...stateBucketStatements(bucketIn(account), path),\n\t\t\t...(named ? [] : [projectBucketStatement(account)]),\n\t\t]);\n\t}\n\n\t// The deploy creates each bucket an s3 provider backs, and the IAM user\n\t// and key it is reached with: by their names — the good one, or the good\n\t// one with a clash's six-character suffix — and nothing else.\n\tconst objects = stageProvider(workspace, 'objects', stage);\n\tif (objects.mode === 'provider' && objects.name === 's3' && buckets.length) {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst names = [...buckets].sort().map((id) => ({\n\t\t\tbucket: bucketName({ scope, stage, id }),\n\t\t\tuser: iamUserName({ scope, stage, id }),\n\t\t}));\n\t\tconst suffix = '?'.repeat(SUFFIX_LENGTH - 1);\n\t\tdescribe.push(\n\t\t\t`create and configure the buckets ${names.map((n) => n.bucket).join(', ')} and their IAM users (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBuckets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:CreateBucket',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketTagging',\n\t\t\t\t\t's3:PutBucketTagging',\n\t\t\t\t\t's3:GetBucketPublicAccessBlock',\n\t\t\t\t\t's3:PutBucketPublicAccessBlock',\n\t\t\t\t\t's3:GetBucketPolicy',\n\t\t\t\t\t's3:PutBucketPolicy',\n\t\t\t\t\t's3:GetEncryptionConfiguration',\n\t\t\t\t\t's3:PutEncryptionConfiguration',\n\t\t\t\t\t's3:GetBucketVersioning',\n\t\t\t\t\t's3:PutBucketVersioning',\n\t\t\t\t\t's3:GetBucketCORS',\n\t\t\t\t\t's3:PutBucketCORS',\n\t\t\t\t\t// A construct's `expire`, and removing it: S3 authorises\n\t\t\t\t\t// DeleteBucketLifecycle as PutLifecycleConfiguration.\n\t\t\t\t\t's3:GetLifecycleConfiguration',\n\t\t\t\t\t's3:PutLifecycleConfiguration',\n\t\t\t\t],\n\t\t\t\tResource: names.flatMap((n) => [\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}`,\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}-${suffix}`,\n\t\t\t\t]),\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBucketUsers',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: names.map(\n\t\t\t\t\t(n) => `arn:aws:iam::${account}:user${IAM_USER_PATH}${n.user}`,\n\t\t\t\t),\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The stage's backups: the deploy creates the project bucket if it is not\n\t// there, keeps its lifecycle, and creates the backups user and its key.\n\t// The role never reads the backups — `gkm backup:list` and `:restore`\n\t// run with a person's own credentials.\n\tif (stageBackups(workspace, manifest, stage).mode === 'on') {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst user = backupsUserName(scope, stage);\n\t\tconst bucketIn = (account: string) => projectBucketName(scope, account);\n\t\tdescribe.push(\n\t\t\t`create the project bucket ${bucketIn('<account>')} and keep its lifecycle, and create the backups user ${user} and its key (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBackupsBucket',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t...new Set([\n\t\t\t\t\t\t...PROJECT_BUCKET_CREATE_ACTIONS,\n\t\t\t\t\t\t...PROJECT_BUCKET_LIFECYCLE_ACTIONS,\n\t\t\t\t\t]),\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBackupsUser',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:iam::${account}:user${IAM_USER_PATH}${user}`,\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The deploy writes the stage's hosts' records: in Route53, only that\n\t// zone's — and, with no hostedZoneId to name it, the zone is looked up\n\t// by name, which IAM cannot scope.\n\tfor (const { domain, config } of stageDnsDomains(\n\t\tstage,\n\t\tworkspace.domains,\n\t\tworkspace.dns,\n\t)) {\n\t\tif (config.provider !== 'route53') continue;\n\t\tconst zoneId = (config as { hostedZoneId?: string }).hostedZoneId?.replace(\n\t\t\t/^\\/?hostedzone\\//,\n\t\t\t'',\n\t\t);\n\t\tdescribe.push(\n\t\t\t`write the A, AAAA and CNAME records of ${domain} (Route53${zoneId ? `, ${zoneId}` : ''})`,\n\t\t);\n\t\tbuild.push(() => [\n\t\t\t{\n\t\t\t\tSid: 'StageDnsRecords',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'route53:ChangeResourceRecordSets',\n\t\t\t\t\t'route53:ListResourceRecordSets',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:route53:::hostedzone/${zoneId ?? '*'}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageDnsChanges',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['route53:GetChange'],\n\t\t\t\tResource: 'arn:aws:route53:::change/*',\n\t\t\t},\n\t\t\t...(zoneId\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSid: 'StageDnsZones',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['route53:ListHostedZonesByName'],\n\t\t\t\t\t\t\tResource: '*',\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t]);\n\t}\n\n\treturn {\n\t\tkind: 'scoped',\n\t\tstatements: (account) => build.flatMap((statements) => statements(account)),\n\t\tdescribe,\n\t};\n}\n\n/**\n * Reading and writing one stage's deploy state in `bucket`, as the deploy\n * role is given it.\n */\nexport function stateBucketStatements(\n\tbucket: string,\n\tpath: string,\n): PolicyStatement[] {\n\treturn [\n\t\t{\n\t\t\tSid: 'StageState',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],\n\t\t\tResource: `arn:aws:s3:::${bucket}/${path}*`,\n\t\t},\n\t\t// HeadBucket, and a state not written yet a 404 rather than a 403.\n\t\t{\n\t\t\tSid: 'StageStateBucket',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:ListBucket'],\n\t\t\tResource: `arn:aws:s3:::${bucket}`,\n\t\t},\n\t];\n}\n\n/** The scoped access as a policy document, or null when it allows nothing. */\nexport function scopedPolicyDocument(\n\tstatements: PolicyStatement[],\n): string | null {\n\tif (statements.length === 0) return null;\n\treturn JSON.stringify({ Version: '2012-10-17', Statement: statements });\n}\n\n/** The account an IAM ARN is in: `arn:aws:iam::<account>:…`. */\nexport function accountOf(arn: string): string {\n\treturn arn.split(':')[4] ?? '';\n}\n","/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * S3, SSM or Secrets Manager the deploy job reads them with the role instead, so\n * no key is handed to GitHub, and this checks the stage's store in the same\n * account, with the same profile. Re-running it converges rather than duplicating.\n *\n * The role trusts the exact `sub` GitHub sends for the environment, read from\n * the repository's OIDC subject settings (`githubOidc.ts`) — the default, the\n * immutable subject with the owner's and repository's ids, or a custom\n * template. A re-run rewrites an existing role's trust to it, so running this\n * again repairs a role that trusted the wrong format. What the role may do is\n * `githubPolicy.ts`: `AdministratorAccess` for SST, and for a compose stage\n * only its own secrets (and deploy state, when that is in AWS).\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tDeleteRolePolicyCommand,\n\tDetachRolePolicyCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListAttachedRolePoliciesCommand,\n\tListOpenIDConnectProvidersCommand,\n\tListRoleTagsCommand,\n\tPutRolePolicyCommand,\n\tTagRoleCommand,\n\tUntagRoleCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { GkmError } from '../errors';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport { resolveOidcSubject } from './githubOidc.js';\nimport {\n\taccountOf,\n\tdeployAccess,\n\tSCOPED_POLICY_NAME,\n\tscopedPolicyDocument,\n} from './githubPolicy.js';\n\nexport { DEFAULT_POLICY_ARN, SCOPED_POLICY_NAME } from './githubPolicy.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * The tag recording which managed policy gkm attached to the role — so a\n * re-run detaches only what gkm put there, never a policy someone else did.\n */\nexport const POLICY_TAG = 'gkm:policy-arn';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets, in place of the default. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\n/** The system's `gh`. */\nexport const ghCli: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t// stderr captured, so a read that fails (a 404) is not printed as noise.\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t...(input === undefined ? {} : { input }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job whose OIDC token carries exactly\n * `subject` — the repository's `<stage>` environment, in the format the\n * repository sends (see `githubOidc.ts`), and nothing else. Not another\n * branch's job, not another environment's: that is what keeps a staging\n * deploy from using the production role.\n */\nexport function trustPolicy(providerArn: string, subject: string): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: subject,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/**\n * The subject a trust policy trusts, or undefined when it names none. IAM\n * hands documents back URL-encoded.\n */\nexport function trustedSubject(\n\tdocument: string | undefined,\n): string | undefined {\n\tif (!document) return undefined;\n\tlet parsed: {\n\t\tStatement?: { Condition?: Record<string, Record<string, unknown>> }[];\n\t};\n\ttry {\n\t\tparsed = JSON.parse(safeDecode(document));\n\t} catch {\n\t\treturn undefined;\n\t}\n\tfor (const statement of parsed.Statement ?? []) {\n\t\tfor (const operator of Object.values(statement.Condition ?? {})) {\n\t\t\tconst value = operator[`${GITHUB_OIDC_HOST}:sub`];\n\t\t\tif (typeof value === 'string') return value;\n\t\t\tif (Array.isArray(value)) return value.join(', ');\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction safeDecode(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn value;\n\t}\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/**\n * What the role is given: a managed policy, attached; or an inline policy\n * document — null for none at all.\n */\nexport type RoleAccess = { managed: string } | { inline: string | null };\n\n/** What {@link ensureRole} did. */\nexport interface RoleOutcome {\n\tarn: string;\n\tcreated: boolean;\n\t/** The subject the role trusted before, when it existed. */\n\tpreviousSubject?: string;\n\t/** Managed policies gkm had attached and detached now. */\n\tdetached: string[];\n\t/**\n\t * Managed policies still attached that the role no longer needs, which gkm\n\t * did not record attaching — left alone, for a person to detach.\n\t */\n\tleftAttached: string[];\n}\n\nfunction isNoSuchEntity(error: unknown): boolean {\n\treturn (error as { name?: string }).name === 'NoSuchEntityException';\n}\n\n/**\n * The deploy role, created or brought back to this trust and access.\n *\n * Converges: re-running rewrites the trust, and moves the role from one access\n * to another — detaching a managed policy only when the role's\n * {@link POLICY_TAG} says gkm attached it.\n */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\taccess: RoleAccess,\n): Promise<RoleOutcome> {\n\tlet arn: string;\n\tlet created = false;\n\tlet previousSubject: string | undefined;\n\tlet recorded: string | undefined;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tpreviousSubject = trustedSubject(Role!.AssumeRolePolicyDocument);\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t\tconst { Tags = [] } = await iam.send(\n\t\t\tnew ListRoleTagsCommand({ RoleName: name }),\n\t\t);\n\t\trecorded = Tags.find((tag) => tag.Key === POLICY_TAG)?.Value;\n\t} catch (error) {\n\t\tif (!isNoSuchEntity(error)) throw error;\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t\tcreated = true;\n\t}\n\n\tconst detached: string[] = [];\n\tconst leftAttached: string[] = [];\n\tconst detach = async (policyArn: string) => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DetachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t\t\t);\n\t\t\tdetached.push(policyArn);\n\t\t} catch (error) {\n\t\t\t// Already gone — by hand, or a run that stopped halfway.\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\tconst deleteInline = async () => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DeleteRolePolicyCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\n\tif ('managed' in access) {\n\t\t// Attaching an attached policy is a no-op, so this converges too.\n\t\tawait iam.send(\n\t\t\tnew AttachRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyArn: access.managed,\n\t\t\t}),\n\t\t);\n\t\tif (recorded && recorded !== access.managed) await detach(recorded);\n\t\tif (recorded !== access.managed) {\n\t\t\tawait iam.send(\n\t\t\t\tnew TagRoleCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tTags: [{ Key: POLICY_TAG, Value: access.managed }],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (!created) await deleteInline();\n\t\treturn { arn, created, previousSubject, detached, leftAttached };\n\t}\n\n\tif (access.inline) {\n\t\tawait iam.send(\n\t\t\tnew PutRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\tPolicyDocument: access.inline,\n\t\t\t}),\n\t\t);\n\t} else if (!created) {\n\t\tawait deleteInline();\n\t}\n\tif (created) return { arn, created, previousSubject, detached, leftAttached };\n\n\tif (recorded) {\n\t\tawait detach(recorded);\n\t\tawait iam.send(\n\t\t\tnew UntagRoleCommand({ RoleName: name, TagKeys: [POLICY_TAG] }),\n\t\t);\n\t}\n\t// A role from before gkm recorded what it attached: what is attached may\n\t// be gkm's or someone's own, so it is named, not detached.\n\tconst { AttachedPolicies = [] } = await iam.send(\n\t\tnew ListAttachedRolePoliciesCommand({ RoleName: name }),\n\t);\n\tfor (const policy of AttachedPolicies) {\n\t\tif (policy.PolicyArn && !detached.includes(policy.PolicyArn)) {\n\t\t\tleftAttached.push(policy.PolicyArn);\n\t\t}\n\t}\n\treturn { arn, created, previousSubject, detached, leftAttached };\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string; subject: string }> {\n\tconst gh = deps.gh ?? ghCli;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\t// The exact subject GitHub will send — read from the repository's OIDC\n\t// settings, before anything is changed, so a format no role can trust\n\t// fails here.\n\tconst oidc = resolveOidcSubject(gh, repo, options.stage);\n\t// The buckets an s3 provider creates are named after their constructs,\n\t// and a stage with a database is backed up: the role is scoped to\n\t// exactly those.\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t});\n\tconst access = deployAccess(\n\t\tworkspace,\n\t\toptions.stage,\n\t\toptions.policyArn,\n\t\tmanifest,\n\t);\n\t// Read through the keystore so a key still at the place keys used to be kept\n\t// is found, and copied to where it is kept now.\n\tconst key = await readKey(options.stage, keystoreProject(workspace));\n\tconst keyPath = getKeyPath(options.stage, keystoreProject(workspace));\n\tconst hasKey = key !== null;\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tif (access.kind === 'managed') {\n\t\tlogger.log(` Policy: ${access.policyArn} (${access.reason})`);\n\t} else if (access.describe.length > 0) {\n\t\tlogger.log(\n\t\t\t` Policy: inline \"${SCOPED_POLICY_NAME}\", scoped to the compose deploy — only:`,\n\t\t);\n\t\tfor (const line of access.describe) logger.log(` ${line}`);\n\t} else {\n\t\tlogger.log(\n\t\t\t' Policy: none — a compose deploy with file secrets and local state reads nothing from AWS',\n\t\t);\n\t}\n\tlogger.log(\n\t\t` Trusted by: ${oidc.subject} only (${SUBJECT_KINDS[oidc.kind]})`,\n\t);\n\tif (oidc.warning) logger.log(` ⚠ ${oidc.warning}`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tif (access.kind === 'scoped') {\n\t\t\tconst document = scopedPolicyDocument(access.statements('<account>'));\n\t\t\tif (document) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`\\n Inline policy:\\n${JSON.stringify(JSON.parse(document), null, 2).replace(/^/gm, ' ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo, subject: oidc.subject };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst outcome = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, oidc.subject),\n\t\taccess.kind === 'managed'\n\t\t\t? { managed: access.policyArn }\n\t\t\t: {\n\t\t\t\t\tinline: scopedPolicyDocument(\n\t\t\t\t\t\taccess.statements(accountOf(providerArn)),\n\t\t\t\t\t),\n\t\t\t\t},\n\t);\n\tconst roleArn = outcome.arn;\n\tlogger.log(`\\n ✓ AWS: ${roleArn}${outcome.created ? ' (created)' : ''}`);\n\tif (!outcome.created) {\n\t\tlogger.log(\n\t\t\toutcome.previousSubject === oidc.subject\n\t\t\t\t? ` ✓ Trust: unchanged, ${oidc.subject}`\n\t\t\t\t: ` ✓ Trust: ${outcome.previousSubject ?? '(no subject)'} → ${oidc.subject}`,\n\t\t);\n\t}\n\tfor (const detached of outcome.detached) {\n\t\tlogger.log(` ✓ Detached ${detached} (attached by gkm, no longer needed)`);\n\t}\n\tif (access.kind === 'scoped') {\n\t\tfor (const attached of outcome.leftAttached) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ ${attached} is still attached to ${role}. gkm has no record of attaching it, so it is left; the compose deploy does not need it. Detach it with: aws iam detach-role-policy --role-name ${role} --policy-arn ${attached}${options.profile ? ` --profile ${options.profile}` : ''}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\tkey!,\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo, subject: oidc.subject };\n}\n\nconst SUBJECT_KINDS = {\n\tdefault: \"the repository's default subject\",\n\timmutable: \"the repository's immutable subject\",\n\tcustom: \"the repository's custom subject template\",\n\tassumed: 'assumed: the default format',\n} as const;\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends GkmError {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"github-CQt3xc3o.mjs","names":[],"sources":["../src/deploy/githubOidc.ts","../src/deploy/githubPolicy.ts","../src/deploy/github.ts"],"sourcesContent":["/**\n * The `sub` GitHub puts in a deploy job's OIDC token — what the deploy role's\n * trust policy has to match exactly.\n *\n * It is not always `repo:<owner>/<name>:environment:<stage>`. A repository\n * (or its organisation) decides the format, and says which through\n * `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`:\n *\n * - the default: `repo:<owner>/<name>:environment:<stage>`;\n * - the immutable subject — the default for repositories created after\n * 15 July 2026, opt-in before — puts the owner's and the repository's ids in\n * the `repo` segment: `repo:<owner>@<ownerId>/<name>@<repoId>:environment:<stage>`.\n * The API hands that segment back as `sub_claim_prefix`;\n * - a custom template (`use_default: false`) builds the `sub` from\n * `include_claim_keys`, in order, each as `<key>:<value>` joined by `:`,\n * where `context` is `environment:<stage>` in an environment job.\n *\n * A `:` inside a value is sent as `%3A`.\n *\n * See https://docs.github.com/en/actions/reference/security/oidc.\n */\n\nimport { GkmError } from '../errors';\nimport type { Gh } from './github.js';\n\n/** `GET /repos/{owner}/{repo}/actions/oidc/customization/sub`. */\nexport interface OidcSubjectCustomization {\n\tuse_default: boolean;\n\tinclude_claim_keys?: string[];\n\tuse_immutable_subject?: boolean;\n\t/** The `repo` segment of the subject, as GitHub will send it. */\n\tsub_claim_prefix?: string;\n}\n\n/** What `GET /repos/{owner}/{repo}` says that a subject may include. */\nexport interface RepoFacts {\n\townerId: number;\n\trepoId: number;\n\tvisibility: string;\n}\n\n/**\n * Template keys whose values are known for a repository's environment job\n * before the job runs. Anything else — `job_workflow_ref`, `ref`, `sha`,\n * `run_id`, `workflow`, `actor`, a custom property — depends on the run.\n */\nexport const SUPPORTED_CLAIM_KEYS = [\n\t'repo',\n\t'context',\n\t'environment',\n\t'repository',\n\t'repository_owner',\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n] as const;\n\nconst NEEDS_FACTS = new Set([\n\t'repository_owner_id',\n\t'repository_id',\n\t'repository_visibility',\n]);\n\n/** A `:` inside a claim value is sent as `%3A`. */\nfunction claim(value: string): string {\n\treturn value.replaceAll(':', '%3A');\n}\n\n/** Which kind of subject the repository uses, for the output. */\nexport type OidcSubjectKind = 'default' | 'immutable' | 'custom' | 'assumed';\n\n/** Whether {@link oidcSubject} needs the repository's ids or visibility. */\nexport function needsRepoFacts(\n\tcustomization: OidcSubjectCustomization,\n): boolean {\n\tconst immutableWithoutPrefix =\n\t\tcustomization.use_immutable_subject === true &&\n\t\t!customization.sub_claim_prefix;\n\tif (customization.use_default) return immutableWithoutPrefix;\n\n\tconst keys = customization.include_claim_keys ?? [];\n\treturn keys.some(\n\t\t(key) => NEEDS_FACTS.has(key) || (key === 'repo' && immutableWithoutPrefix),\n\t);\n}\n\n/**\n * The exact `sub` of a job in `repo`'s `stage` environment.\n *\n * @throws OidcSubjectNotSupported when a custom template includes a claim\n * whose value depends on the run, so no one subject can be trusted.\n */\nexport function oidcSubject(\n\trepo: string,\n\tstage: string,\n\tcustomization: OidcSubjectCustomization,\n\tfacts?: RepoFacts,\n): { subject: string; kind: OidcSubjectKind } {\n\tconst [owner = '', name = ''] = repo.split('/');\n\tconst environment = `environment:${claim(stage)}`;\n\n\tconst repoSegment = (): string => {\n\t\tif (customization.sub_claim_prefix) return customization.sub_claim_prefix;\n\t\tif (customization.use_immutable_subject) {\n\t\t\tif (!facts) throw new RepoFactsRequired(repo);\n\t\t\treturn `repo:${owner}@${facts.ownerId}/${name}@${facts.repoId}`;\n\t\t}\n\t\treturn `repo:${repo}`;\n\t};\n\tconst immutable =\n\t\tcustomization.use_immutable_subject === true ||\n\t\tcustomization.sub_claim_prefix !== undefined;\n\n\tif (customization.use_default) {\n\t\treturn {\n\t\t\tsubject: `${repoSegment()}:${environment}`,\n\t\t\tkind: immutable ? 'immutable' : 'default',\n\t\t};\n\t}\n\n\tconst keys = customization.include_claim_keys ?? [];\n\tconst unsupported = keys.filter(\n\t\t(key) => !(SUPPORTED_CLAIM_KEYS as readonly string[]).includes(key),\n\t);\n\tif (keys.length === 0 || unsupported.length > 0) {\n\t\tthrow new OidcSubjectNotSupported(repo, keys, unsupported);\n\t}\n\tif (keys.some((key) => NEEDS_FACTS.has(key)) && !facts) {\n\t\tthrow new RepoFactsRequired(repo);\n\t}\n\n\tconst parts = keys.map((key) => {\n\t\tswitch (key) {\n\t\t\tcase 'repo':\n\t\t\t\treturn repoSegment();\n\t\t\tcase 'context':\n\t\t\tcase 'environment':\n\t\t\t\treturn environment;\n\t\t\tcase 'repository':\n\t\t\t\treturn `repository:${claim(repo)}`;\n\t\t\tcase 'repository_owner':\n\t\t\t\treturn `repository_owner:${claim(owner)}`;\n\t\t\tcase 'repository_owner_id':\n\t\t\t\treturn `repository_owner_id:${facts!.ownerId}`;\n\t\t\tcase 'repository_id':\n\t\t\t\treturn `repository_id:${facts!.repoId}`;\n\t\t\tdefault:\n\t\t\t\treturn `repository_visibility:${claim(facts!.visibility)}`;\n\t\t}\n\t});\n\treturn { subject: parts.join(':'), kind: 'custom' };\n}\n\n/** What {@link resolveOidcSubject} found. */\nexport interface ResolvedOidcSubject {\n\tsubject: string;\n\tkind: OidcSubjectKind;\n\t/** Set when the format could not be read and the default was assumed. */\n\twarning?: string;\n}\n\n/** The endpoint a repository's subject format is read from. */\nexport function customizationEndpoint(repo: string): string {\n\treturn `repos/${repo}/actions/oidc/customization/sub`;\n}\n\n/**\n * The `sub` GitHub will send for `repo`'s `stage` environment, read from the\n * repository's OIDC settings through `gh`.\n *\n * A repository on a custom template that names no keys of its own uses its\n * organisation's, read from `orgs/{owner}/actions/oidc/customization/sub`.\n * When the settings cannot be read at all — no permission, or a GitHub\n * Enterprise Server without the endpoint — the default format is assumed, with\n * a warning saying where to check it.\n */\nexport function resolveOidcSubject(\n\tgh: Gh,\n\trepo: string,\n\tstage: string,\n): ResolvedOidcSubject {\n\tconst endpoint = customizationEndpoint(repo);\n\tlet customization: OidcSubjectCustomization;\n\ttry {\n\t\tcustomization = JSON.parse(gh(['api', endpoint]));\n\t} catch {\n\t\treturn {\n\t\t\tsubject: `repo:${repo}:environment:${claim(stage)}`,\n\t\t\tkind: 'assumed',\n\t\t\twarning: `Could not read ${endpoint}, so the default subject format is assumed. Check it with: gh api ${endpoint} — a repository on the immutable subject or a custom template sends a different sub, and the role would not be assumable.`,\n\t\t};\n\t}\n\n\tif (\n\t\t!customization.use_default &&\n\t\t(customization.include_claim_keys ?? []).length === 0\n\t) {\n\t\t// The repository follows its organisation's template.\n\t\tconst [owner = ''] = repo.split('/');\n\t\tconst orgEndpoint = `orgs/${owner}/actions/oidc/customization/sub`;\n\t\tlet org: { include_claim_keys?: string[]; use_immutable_subject?: boolean };\n\t\ttry {\n\t\t\torg = JSON.parse(gh(['api', orgEndpoint]));\n\t\t} catch {\n\t\t\tthrow new OrgOidcTemplateUnreadable(repo, orgEndpoint);\n\t\t}\n\t\tcustomization = {\n\t\t\t...customization,\n\t\t\tinclude_claim_keys: org.include_claim_keys ?? [],\n\t\t\tuse_immutable_subject:\n\t\t\t\tcustomization.use_immutable_subject ?? org.use_immutable_subject,\n\t\t};\n\t}\n\n\tconst facts = needsRepoFacts(customization)\n\t\t? readRepoFacts(gh, repo)\n\t\t: undefined;\n\treturn oidcSubject(repo, stage, customization, facts);\n}\n\nfunction readRepoFacts(gh: Gh, repo: string): RepoFacts {\n\tconst raw = JSON.parse(gh(['api', `repos/${repo}`])) as {\n\t\tid: number;\n\t\towner: { id: number };\n\t\tvisibility: string;\n\t};\n\treturn {\n\t\townerId: raw.owner.id,\n\t\trepoId: raw.id,\n\t\tvisibility: raw.visibility,\n\t};\n}\n\n/**\n * The repository's custom subject template includes a claim whose value\n * depends on the run — or names none — so no one subject can be trusted.\n */\nexport class OidcSubjectNotSupported extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly includeClaimKeys: string[],\n\t\treadonly unsupportedKeys: string[],\n\t) {\n\t\tsuper(\n\t\t\t`${repo} builds its OIDC subject from a custom template, ${JSON.stringify(includeClaimKeys)}, and ${\n\t\t\t\tunsupportedKeys.length > 0\n\t\t\t\t\t? `${unsupportedKeys.join(', ')} depend${unsupportedKeys.length === 1 ? 's' : ''} on the run`\n\t\t\t\t\t: 'it names no claims'\n\t\t\t}, so the deploy role cannot trust one exact subject. Set the repository back to the default subject — gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true — (or keep the immutable one by also passing -F use_immutable_subject=true), or use a template built only from ${SUPPORTED_CLAIM_KEYS.join(', ')}; then run this again.`,\n\t\t);\n\t\tthis.name = 'OidcSubjectNotSupported';\n\t}\n}\n\n/** The subject needs the repository's ids, and none were read. */\nexport class RepoFactsRequired extends GkmError {\n\tconstructor(readonly repo: string) {\n\t\tsuper(\n\t\t\t`The OIDC subject of ${repo} includes its owner and repository ids; read them with gh api repos/${repo} and pass them in.`,\n\t\t);\n\t\tthis.name = 'RepoFactsRequired';\n\t}\n}\n\n/**\n * The repository uses its organisation's subject template, and that template\n * could not be read — so what GitHub sends is unknown, and is not guessed.\n */\nexport class OrgOidcTemplateUnreadable extends GkmError {\n\tconstructor(\n\t\treadonly repo: string,\n\t\treadonly endpoint: string,\n\t) {\n\t\tsuper(\n\t\t\t`${repo} uses its organisation's OIDC subject template, and ${endpoint} could not be read. Run this as an organisation owner (gh auth refresh -s admin:org), or set the repository back to the default subject with gh api --method PUT ${customizationEndpoint(repo)} -F use_default=true; then run this again.`,\n\t\t);\n\t\tthis.name = 'OrgOidcTemplateUnreadable';\n\t}\n}\n","/**\n * What the deploy role may do in the stage's account.\n *\n * An SST stage creates its own stack, which is most of AWS, so its role gets\n * `AdministratorAccess`. A stage deployed with the `compose` target builds and\n * runs containers on a server; from AWS its deploy job needs only the stage's\n * secrets — read, and written back when a deploy generates a new one: the one\n * object in the project bucket, or the SSM parameter or Secrets Manager secret\n * — and,\n * when the deploy state is kept in AWS, the stage's state, and, when the\n * stage's domain is in Route53, the records of its hosted zone, and, when an\n * `s3` provider backs its buckets, those buckets and the IAM user each is\n * reached with — by the names the provider gives them — and, when it runs\n * Postgres, the project bucket its backups go to and the backups user. So a\n * compose stage gets an inline policy naming exactly those resources and\n * nothing else.\n *\n * `--policy-arn` replaces either.\n */\n\nimport type { ConstructManifest } from '@geekmidas/manifest';\nimport { stageBackups } from '../backups/config.js';\nimport { backupsUserName } from '../backups/provision.js';\nimport { stageDnsDomains } from '../compose/dns.js';\nimport { stageProvider } from '../providers/config.js';\nimport {\n\tPROJECT_BUCKET_CREATE_ACTIONS,\n\tPROJECT_BUCKET_LIFECYCLE_ACTIONS,\n\tprojectBucketName,\n} from '../providers/projectBucket.js';\nimport {\n\tbucketName,\n\tIAM_USER_PATH,\n\tiamUserName,\n\tSUFFIX_LENGTH,\n} from '../providers/s3/naming.js';\nimport { secretsParameterName } from '../secrets/aws.js';\nimport { s3SecretsKey, s3SecretsLocation } from '../secrets/providers.js';\nimport { secretsManagerSecretName } from '../secrets/secretsManager.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { deployIdentity } from './identity.js';\n\n/**\n * What SST needs to create a stack is most of AWS, so this is the default\n * outside compose — said out loud in the output, and replaceable with\n * `--policy-arn`.\n */\nexport const DEFAULT_POLICY_ARN = 'arn:aws:iam::aws:policy/AdministratorAccess';\n\n/** The name of the inline policy a compose stage's role is given. */\nexport const SCOPED_POLICY_NAME = 'gkm-deploy';\n\n/** One statement of an IAM policy document. */\nexport interface PolicyStatement {\n\tSid: string;\n\tEffect: 'Allow';\n\tAction: string[];\n\tResource: string | string[];\n\tCondition?: Record<string, Record<string, string>>;\n}\n\n/** What the role is given. */\nexport type DeployAccess =\n\t| {\n\t\t\tkind: 'managed';\n\t\t\tpolicyArn: string;\n\t\t\t/** Why, for the output. */\n\t\t\treason: string;\n\t }\n\t| {\n\t\t\tkind: 'scoped';\n\t\t\t/** The statements, in the account the role is in. */\n\t\t\tstatements(account: string): PolicyStatement[];\n\t\t\t/** What each statement is for, for the output. */\n\t\t\tdescribe: string[];\n\t };\n\n/** Every target the stage's apps deploy through. */\nexport function deployTargets(workspace: NormalizedWorkspace): string[] {\n\tconst targets = new Set<string>([workspace.deploy?.default ?? 'dokploy']);\n\tfor (const app of Object.values(workspace.apps)) {\n\t\ttargets.add(app.resolvedDeployTarget);\n\t}\n\treturn [...targets].sort();\n}\n\n/**\n * The access the stage's deploy role is given: `--policy-arn` when passed,\n * the scoped policy for a stage only the compose target deploys, and\n * `AdministratorAccess` otherwise.\n */\nexport function deployAccess(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\tpolicyArn?: string,\n\t/** The stage's constructs: its buckets, and whether it runs Postgres. */\n\tmanifest: ConstructManifest = {},\n): DeployAccess {\n\tconst buckets = Object.entries(manifest)\n\t\t.filter(([, d]) => d.kind === 'objects')\n\t\t.map(([id]) => id);\n\tif (policyArn) {\n\t\treturn { kind: 'managed', policyArn, reason: '--policy-arn' };\n\t}\n\n\tconst targets = deployTargets(workspace);\n\tif (targets.length !== 1 || targets[0] !== 'compose') {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `the ${targets.join(', ')} target${targets.length === 1 ? '' : 's'} deploy${targets.length === 1 ? 's' : ''} infrastructure; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst project = workspace.name;\n\tconst store = workspace.secrets.store ?? 'file';\n\tconst state = workspace.state;\n\tconst custom =\n\t\t(typeof store === 'object' && typeof store.provider === 'object') ||\n\t\t(state !== undefined && typeof state.provider === 'object');\n\tif (custom) {\n\t\treturn {\n\t\t\tkind: 'managed',\n\t\t\tpolicyArn: DEFAULT_POLICY_ARN,\n\t\t\treason: `a custom ${typeof store === 'object' && typeof store.provider === 'object' ? 'secrets store' : 'state provider'} cannot be scoped; --policy-arn to narrow`,\n\t\t};\n\t}\n\n\tconst describe: string[] = [];\n\tconst build: ((account: string) => PolicyStatement[])[] = [];\n\n\t// The project bucket's name is fixed by the account, so the role is given\n\t// exactly it, and the actions that create it for whichever run is first.\n\tconst projectBucketIn = (account: string) =>\n\t\tprojectBucketName(deployIdentity(workspace, stage).scope, account);\n\tconst projectBucketStatement = (account: string): PolicyStatement => ({\n\t\tSid: 'ProjectBucket',\n\t\tEffect: 'Allow',\n\t\tAction: [...PROJECT_BUCKET_CREATE_ACTIONS],\n\t\tResource: `arn:aws:s3:::${projectBucketIn(account)}`,\n\t});\n\t// Whether the state's statements already cover the secrets' bucket.\n\tconst stateInSecretsBucket =\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 's3' &&\n\t\tstate?.provider === 's3' &&\n\t\tstate.bucket === (store as { bucket?: string }).bucket;\n\n\tif (typeof store === 'object' && store.provider === 's3') {\n\t\tconst location = s3SecretsLocation(store, state);\n\t\tconst key = s3SecretsKey(location.prefix, project, stage);\n\t\tconst named = location.bucket;\n\t\tconst bucketIn = (account: string) => named ?? projectBucketIn(account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and update s3://${named}/${key}`\n\t\t\t\t: `read and update ${key} in the project bucket ${projectBucketIn('<account>')}${stateInSecretsBucket ? '' : ', creating it'}`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['s3:GetObject', 's3:PutObject'],\n\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}/${key}`,\n\t\t\t},\n\t\t\t// The state's statements give these when it is in the same bucket.\n\t\t\t...(stateInSecretsBucket\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\t// HeadBucket, and secrets not written yet a 404, not a 403.\n\t\t\t\t\t\t\tSid: 'StageSecretsBucket',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['s3:ListBucket'],\n\t\t\t\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t\t\t\t},\n\t\t\t\t\t\t...(named ? [] : [projectBucketStatement(account)]),\n\t\t\t\t\t]),\n\t\t]);\n\t} else if (typeof store === 'object' && store.provider === 'ssm') {\n\t\tconst { region } = store;\n\t\tconst name = secretsParameterName(project, stage);\n\t\tdescribe.push(`read and update ${name} (SSM, ${region})`);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageSecrets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${name}`,\n\t\t\t},\n\t\t]);\n\t} else if (\n\t\ttypeof store === 'object' &&\n\t\tstore.provider === 'secrets-manager'\n\t) {\n\t\tconst { region, kmsKeyId } = store as { region: string; kmsKeyId?: string };\n\t\tconst name = secretsManagerSecretName(project, stage);\n\t\tdescribe.push(`read and update ${name} (Secrets Manager, ${region})`);\n\t\tbuild.push((account) => {\n\t\t\tconst statements: PolicyStatement[] = [\n\t\t\t\t{\n\t\t\t\t\tSid: 'StageSecrets',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: [\n\t\t\t\t\t\t'secretsmanager:GetSecretValue',\n\t\t\t\t\t\t'secretsmanager:PutSecretValue',\n\t\t\t\t\t\t'secretsmanager:CreateSecret',\n\t\t\t\t\t],\n\t\t\t\t\t// Secrets Manager appends six random characters to a secret's ARN.\n\t\t\t\t\tResource: `arn:aws:secretsmanager:${region}:${account}:secret:${name}-??????`,\n\t\t\t\t},\n\t\t\t];\n\t\t\tif (kmsKeyId) {\n\t\t\t\tstatements.push({\n\t\t\t\t\tSid: 'StageSecretsKey',\n\t\t\t\t\tEffect: 'Allow',\n\t\t\t\t\tAction: ['kms:Decrypt', 'kms:GenerateDataKey'],\n\t\t\t\t\tResource: kmsKeyId.startsWith('arn:') ? kmsKeyId : '*',\n\t\t\t\t\tCondition: {\n\t\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t\t'kms:ViaService': `secretsmanager.${region}.amazonaws.com`,\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn statements;\n\t\t});\n\t}\n\n\tif (state?.provider === 'ssm') {\n\t\tconst { region } = state;\n\t\tconst path = `/gkm/${project}/${stage}/`;\n\t\tdescribe.push(\n\t\t\t`read and write the deploy state under ${path} (SSM, ${region})`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageState',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['ssm:GetParameter', 'ssm:PutParameter', 'ssm:DeleteParameter'],\n\t\t\t\tResource: `arn:aws:ssm:${region}:${account}:parameter${path}*`,\n\t\t\t},\n\t\t]);\n\t} else if (state?.provider === 's3') {\n\t\tconst prefix = (state.prefix ?? 'gkm').replace(/\\/+$/, '');\n\t\tconst path = `${prefix ? `${prefix}/` : ''}${project}/${stage}/`;\n\t\tconst named = state.bucket;\n\t\t// No bucket named: the project bucket, which the first deploy creates.\n\t\tconst bucketIn = (account: string) => named ?? projectBucketIn(account);\n\t\tdescribe.push(\n\t\t\tnamed\n\t\t\t\t? `read and write the deploy state under s3://${named}/${path}`\n\t\t\t\t: `create the project bucket ${projectBucketIn('<account>')}, and read and write the deploy state under ${path} in it`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t...stateBucketStatements(bucketIn(account), path),\n\t\t\t...(named ? [] : [projectBucketStatement(account)]),\n\t\t]);\n\t}\n\n\t// The deploy creates each bucket an s3 provider backs, and the IAM user\n\t// and key it is reached with: by their names — the good one, or the good\n\t// one with a clash's six-character suffix — and nothing else.\n\tconst objects = stageProvider(workspace, 'objects', stage);\n\tif (objects.mode === 'provider' && objects.name === 's3' && buckets.length) {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst names = [...buckets].sort().map((id) => ({\n\t\t\tbucket: bucketName({ scope, stage, id }),\n\t\t\tuser: iamUserName({ scope, stage, id }),\n\t\t}));\n\t\tconst suffix = '?'.repeat(SUFFIX_LENGTH - 1);\n\t\tdescribe.push(\n\t\t\t`create and configure the buckets ${names.map((n) => n.bucket).join(', ')} and their IAM users (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBuckets',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:CreateBucket',\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t's3:GetBucketTagging',\n\t\t\t\t\t's3:PutBucketTagging',\n\t\t\t\t\t's3:GetBucketPublicAccessBlock',\n\t\t\t\t\t's3:PutBucketPublicAccessBlock',\n\t\t\t\t\t's3:GetBucketPolicy',\n\t\t\t\t\t's3:PutBucketPolicy',\n\t\t\t\t\t's3:GetEncryptionConfiguration',\n\t\t\t\t\t's3:PutEncryptionConfiguration',\n\t\t\t\t\t's3:GetBucketVersioning',\n\t\t\t\t\t's3:PutBucketVersioning',\n\t\t\t\t\t's3:GetBucketCORS',\n\t\t\t\t\t's3:PutBucketCORS',\n\t\t\t\t\t// A construct's `expire`, and removing it: S3 authorises\n\t\t\t\t\t// DeleteBucketLifecycle as PutLifecycleConfiguration.\n\t\t\t\t\t's3:GetLifecycleConfiguration',\n\t\t\t\t\t's3:PutLifecycleConfiguration',\n\t\t\t\t],\n\t\t\t\tResource: names.flatMap((n) => [\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}`,\n\t\t\t\t\t`arn:aws:s3:::${n.bucket}-${suffix}`,\n\t\t\t\t]),\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBucketUsers',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: names.map(\n\t\t\t\t\t(n) => `arn:aws:iam::${account}:user${IAM_USER_PATH}${n.user}`,\n\t\t\t\t),\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The stage's backups: the deploy creates the project bucket if it is not\n\t// there, keeps its lifecycle, and creates the backups user and its key.\n\t// The role never reads the backups — `gkm backup:list` and `:restore`\n\t// run with a person's own credentials.\n\tif (stageBackups(workspace, manifest, stage).mode === 'on') {\n\t\tconst { scope } = deployIdentity(workspace, stage);\n\t\tconst user = backupsUserName(scope, stage);\n\t\tconst bucketIn = (account: string) => projectBucketName(scope, account);\n\t\tdescribe.push(\n\t\t\t`create the project bucket ${bucketIn('<account>')} and keep its lifecycle, and create the backups user ${user} and its key (S3, IAM)`,\n\t\t);\n\t\tbuild.push((account) => [\n\t\t\t{\n\t\t\t\tSid: 'StageBackupsBucket',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t's3:ListBucket',\n\t\t\t\t\t...new Set([\n\t\t\t\t\t\t...PROJECT_BUCKET_CREATE_ACTIONS,\n\t\t\t\t\t\t...PROJECT_BUCKET_LIFECYCLE_ACTIONS,\n\t\t\t\t\t]),\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:s3:::${bucketIn(account)}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageBackupsUser',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'iam:GetUser',\n\t\t\t\t\t'iam:CreateUser',\n\t\t\t\t\t'iam:TagUser',\n\t\t\t\t\t'iam:ListUserTags',\n\t\t\t\t\t'iam:GetUserPolicy',\n\t\t\t\t\t'iam:PutUserPolicy',\n\t\t\t\t\t'iam:ListAccessKeys',\n\t\t\t\t\t'iam:CreateAccessKey',\n\t\t\t\t\t'iam:DeleteAccessKey',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:iam::${account}:user${IAM_USER_PATH}${user}`,\n\t\t\t},\n\t\t]);\n\t}\n\n\t// The deploy writes the stage's hosts' records: in Route53, only that\n\t// zone's — and, with no hostedZoneId to name it, the zone is looked up\n\t// by name, which IAM cannot scope.\n\tfor (const { domain, config } of stageDnsDomains(\n\t\tstage,\n\t\tworkspace.domains,\n\t\tworkspace.dns,\n\t)) {\n\t\tif (config.provider !== 'route53') continue;\n\t\tconst zoneId = (config as { hostedZoneId?: string }).hostedZoneId?.replace(\n\t\t\t/^\\/?hostedzone\\//,\n\t\t\t'',\n\t\t);\n\t\tdescribe.push(\n\t\t\t`write the A, AAAA and CNAME records of ${domain} (Route53${zoneId ? `, ${zoneId}` : ''})`,\n\t\t);\n\t\tbuild.push(() => [\n\t\t\t{\n\t\t\t\tSid: 'StageDnsRecords',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: [\n\t\t\t\t\t'route53:ChangeResourceRecordSets',\n\t\t\t\t\t'route53:ListResourceRecordSets',\n\t\t\t\t],\n\t\t\t\tResource: `arn:aws:route53:::hostedzone/${zoneId ?? '*'}`,\n\t\t\t},\n\t\t\t{\n\t\t\t\tSid: 'StageDnsChanges',\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tAction: ['route53:GetChange'],\n\t\t\t\tResource: 'arn:aws:route53:::change/*',\n\t\t\t},\n\t\t\t...(zoneId\n\t\t\t\t? []\n\t\t\t\t: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tSid: 'StageDnsZones',\n\t\t\t\t\t\t\tEffect: 'Allow' as const,\n\t\t\t\t\t\t\tAction: ['route53:ListHostedZonesByName'],\n\t\t\t\t\t\t\tResource: '*',\n\t\t\t\t\t\t},\n\t\t\t\t\t]),\n\t\t]);\n\t}\n\n\treturn {\n\t\tkind: 'scoped',\n\t\tstatements: (account) => build.flatMap((statements) => statements(account)),\n\t\tdescribe,\n\t};\n}\n\n/**\n * Reading and writing one stage's deploy state in `bucket`, as the deploy\n * role is given it.\n */\nexport function stateBucketStatements(\n\tbucket: string,\n\tpath: string,\n): PolicyStatement[] {\n\treturn [\n\t\t{\n\t\t\tSid: 'StageState',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:GetObject', 's3:PutObject', 's3:DeleteObject'],\n\t\t\tResource: `arn:aws:s3:::${bucket}/${path}*`,\n\t\t},\n\t\t// HeadBucket, and a state not written yet a 404 rather than a 403.\n\t\t{\n\t\t\tSid: 'StageStateBucket',\n\t\t\tEffect: 'Allow',\n\t\t\tAction: ['s3:ListBucket'],\n\t\t\tResource: `arn:aws:s3:::${bucket}`,\n\t\t},\n\t];\n}\n\n/** The scoped access as a policy document, or null when it allows nothing. */\nexport function scopedPolicyDocument(\n\tstatements: PolicyStatement[],\n): string | null {\n\tif (statements.length === 0) return null;\n\treturn JSON.stringify({ Version: '2012-10-17', Statement: statements });\n}\n\n/** The account an IAM ARN is in: `arn:aws:iam::<account>:…`. */\nexport function accountOf(arn: string): string {\n\treturn arn.split(':')[4] ?? '';\n}\n","/**\n * `gkm deploy:github` — let GitHub Actions deploy one stage, with no keys.\n *\n * A stage usually lives in its own AWS account — staging in one, production in\n * another — so this runs once per stage, with that account's profile:\n *\n * gkm deploy:github --stage staging --profile acme-dev\n * gkm deploy:github --stage prod --profile acme-prod\n *\n * In the stage's account it makes sure GitHub's OIDC provider exists, and a\n * role that only this repository's `<stage>` environment can assume. On\n * GitHub it creates that environment and gives it what the generated deploy\n * workflow reads: `AWS_ROLE_ARN`, and — when the stage's secrets are kept in\n * the local file — its key as `GKM_SECRETS_KEY`. With `secrets.store` set to\n * S3, SSM or Secrets Manager the deploy job reads them with the role instead, so\n * no key is handed to GitHub, and this checks the stage's store in the same\n * account, with the same profile. Re-running it converges rather than duplicating.\n *\n * The role trusts the exact `sub` GitHub sends for the environment, read from\n * the repository's OIDC subject settings (`githubOidc.ts`) — the default, the\n * immutable subject with the owner's and repository's ids, or a custom\n * template. A re-run rewrites an existing role's trust to it, so running this\n * again repairs a role that trusted the wrong format. What the role may do is\n * `githubPolicy.ts`: `AdministratorAccess` for SST, and for a compose stage\n * only its own secrets (and deploy state, when that is in AWS).\n */\n\nimport { execFileSync } from 'node:child_process';\nimport {\n\tAttachRolePolicyCommand,\n\tCreateOpenIDConnectProviderCommand,\n\tCreateRoleCommand,\n\tDeleteRolePolicyCommand,\n\tDetachRolePolicyCommand,\n\tGetRoleCommand,\n\tIAMClient,\n\ttype IAMClientConfig,\n\tListAttachedRolePoliciesCommand,\n\tListOpenIDConnectProvidersCommand,\n\tListRoleTagsCommand,\n\tPutRolePolicyCommand,\n\tTagRoleCommand,\n\tUntagRoleCommand,\n\tUpdateAssumeRolePolicyCommand,\n} from '@aws-sdk/client-iam';\nimport { loadWorkspaceConfig } from '../config.js';\nimport { GkmError } from '../errors';\nimport { discover } from '../reconcile/discover.js';\nimport { constructGlobs } from '../reconcile/workspace.js';\nimport { getKeyPath, keystoreProject, readKey } from '../secrets/keystore.js';\nimport { isRemoteStore, secretsStoreFor } from '../secrets/store.js';\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport { resolveOidcSubject } from './githubOidc.js';\nimport {\n\taccountOf,\n\tdeployAccess,\n\tSCOPED_POLICY_NAME,\n\tscopedPolicyDocument,\n} from './githubPolicy.js';\n\nexport { DEFAULT_POLICY_ARN, SCOPED_POLICY_NAME } from './githubPolicy.js';\n\nconst logger = console;\n\nconst GITHUB_OIDC_HOST = 'token.actions.githubusercontent.com';\nconst AUDIENCE = 'sts.amazonaws.com';\n\n/**\n * The tag recording which managed policy gkm attached to the role — so a\n * re-run detaches only what gkm put there, never a policy someone else did.\n */\nexport const POLICY_TAG = 'gkm:policy-arn';\n\nexport interface DeployGithubOptions {\n\tstage: string;\n\t/** The AWS profile for the stage's account. */\n\tprofile?: string;\n\t/** `owner/name`; defaults to the repository `gh` sees here. */\n\trepo?: string;\n\t/** The policy the deploy role gets, in place of the default. */\n\tpolicyArn?: string;\n\tdryRun?: boolean;\n}\n\n/** Runs `gh` with the given arguments and optional stdin, returning stdout. */\nexport type Gh = (args: string[], input?: string) => string;\n\n/** The system's `gh`. */\nexport const ghCli: Gh = (args, input) =>\n\texecFileSync('gh', args, {\n\t\tencoding: 'utf-8',\n\t\t// stderr captured, so a read that fails (a 404) is not printed as noise.\n\t\tstdio: ['pipe', 'pipe', 'pipe'],\n\t\t...(input === undefined ? {} : { input }),\n\t}).trim();\n\n/** The role a stage deploys as: `<project>-github-<stage>`, within IAM's 64. */\nexport function roleName(project: string, stage: string): string {\n\treturn `${project}-github-${stage}`\n\t\t.toLowerCase()\n\t\t.replace(/[^a-z0-9+=,.@_-]/g, '-')\n\t\t.slice(0, 64);\n}\n\n/**\n * Who may assume the role: a workflow job whose OIDC token carries exactly\n * `subject` — the repository's `<stage>` environment, in the format the\n * repository sends (see `githubOidc.ts`), and nothing else. Not another\n * branch's job, not another environment's: that is what keeps a staging\n * deploy from using the production role.\n */\nexport function trustPolicy(providerArn: string, subject: string): string {\n\treturn JSON.stringify({\n\t\tVersion: '2012-10-17',\n\t\tStatement: [\n\t\t\t{\n\t\t\t\tEffect: 'Allow',\n\t\t\t\tPrincipal: { Federated: providerArn },\n\t\t\t\tAction: 'sts:AssumeRoleWithWebIdentity',\n\t\t\t\tCondition: {\n\t\t\t\t\tStringEquals: {\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:aud`]: AUDIENCE,\n\t\t\t\t\t\t[`${GITHUB_OIDC_HOST}:sub`]: subject,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t],\n\t});\n}\n\n/**\n * The subject a trust policy trusts, or undefined when it names none. IAM\n * hands documents back URL-encoded.\n */\nexport function trustedSubject(\n\tdocument: string | undefined,\n): string | undefined {\n\tif (!document) return undefined;\n\tlet parsed: {\n\t\tStatement?: { Condition?: Record<string, Record<string, unknown>> }[];\n\t};\n\ttry {\n\t\tparsed = JSON.parse(safeDecode(document));\n\t} catch {\n\t\treturn undefined;\n\t}\n\tfor (const statement of parsed.Statement ?? []) {\n\t\tfor (const operator of Object.values(statement.Condition ?? {})) {\n\t\t\tconst value = operator[`${GITHUB_OIDC_HOST}:sub`];\n\t\t\tif (typeof value === 'string') return value;\n\t\t\tif (Array.isArray(value)) return value.join(', ');\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction safeDecode(value: string): string {\n\ttry {\n\t\treturn decodeURIComponent(value);\n\t} catch {\n\t\treturn value;\n\t}\n}\n\n/** GitHub's OIDC provider in this account, created if it is not there. */\nexport async function ensureOidcProvider(iam: IAMClient): Promise<string> {\n\tconst { OpenIDConnectProviderList = [] } = await iam.send(\n\t\tnew ListOpenIDConnectProvidersCommand({}),\n\t);\n\tconst existing = OpenIDConnectProviderList.find((p) =>\n\t\tp.Arn?.endsWith(`oidc-provider/${GITHUB_OIDC_HOST}`),\n\t)?.Arn;\n\tif (existing) return existing;\n\n\tconst created = await iam.send(\n\t\tnew CreateOpenIDConnectProviderCommand({\n\t\t\tUrl: `https://${GITHUB_OIDC_HOST}`,\n\t\t\tClientIDList: [AUDIENCE],\n\t\t}),\n\t);\n\treturn created.OpenIDConnectProviderArn!;\n}\n\n/**\n * What the role is given: a managed policy, attached; or an inline policy\n * document — null for none at all.\n */\nexport type RoleAccess = { managed: string } | { inline: string | null };\n\n/** What {@link ensureRole} did. */\nexport interface RoleOutcome {\n\tarn: string;\n\tcreated: boolean;\n\t/** The subject the role trusted before, when it existed. */\n\tpreviousSubject?: string;\n\t/** Managed policies gkm had attached and detached now. */\n\tdetached: string[];\n\t/**\n\t * Managed policies still attached that the role no longer needs, which gkm\n\t * did not record attaching — left alone, for a person to detach.\n\t */\n\tleftAttached: string[];\n}\n\nfunction isNoSuchEntity(error: unknown): boolean {\n\treturn (error as { name?: string }).name === 'NoSuchEntityException';\n}\n\n/**\n * The deploy role, created or brought back to this trust and access.\n *\n * Converges: re-running rewrites the trust, and moves the role from one access\n * to another — detaching a managed policy only when the role's\n * {@link POLICY_TAG} says gkm attached it.\n */\nexport async function ensureRole(\n\tiam: IAMClient,\n\tname: string,\n\ttrust: string,\n\taccess: RoleAccess,\n): Promise<RoleOutcome> {\n\tlet arn: string;\n\tlet created = false;\n\tlet previousSubject: string | undefined;\n\tlet recorded: string | undefined;\n\ttry {\n\t\tconst { Role } = await iam.send(new GetRoleCommand({ RoleName: name }));\n\t\tpreviousSubject = trustedSubject(Role!.AssumeRolePolicyDocument);\n\t\tawait iam.send(\n\t\t\tnew UpdateAssumeRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyDocument: trust,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t\tconst { Tags = [] } = await iam.send(\n\t\t\tnew ListRoleTagsCommand({ RoleName: name }),\n\t\t);\n\t\trecorded = Tags.find((tag) => tag.Key === POLICY_TAG)?.Value;\n\t} catch (error) {\n\t\tif (!isNoSuchEntity(error)) throw error;\n\t\tconst { Role } = await iam.send(\n\t\t\tnew CreateRoleCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tAssumeRolePolicyDocument: trust,\n\t\t\t\tDescription: 'Deploys from GitHub Actions (gkm deploy:github)',\n\t\t\t\t// SST deploys can run long; the default hour is tight.\n\t\t\t\tMaxSessionDuration: 3600 * 2,\n\t\t\t}),\n\t\t);\n\t\tarn = Role!.Arn!;\n\t\tcreated = true;\n\t}\n\n\tconst detached: string[] = [];\n\tconst leftAttached: string[] = [];\n\tconst detach = async (policyArn: string) => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DetachRolePolicyCommand({ RoleName: name, PolicyArn: policyArn }),\n\t\t\t);\n\t\t\tdetached.push(policyArn);\n\t\t} catch (error) {\n\t\t\t// Already gone — by hand, or a run that stopped halfway.\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\tconst deleteInline = async () => {\n\t\ttry {\n\t\t\tawait iam.send(\n\t\t\t\tnew DeleteRolePolicyCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\t}),\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tif (!isNoSuchEntity(error)) throw error;\n\t\t}\n\t};\n\n\tif ('managed' in access) {\n\t\t// Attaching an attached policy is a no-op, so this converges too.\n\t\tawait iam.send(\n\t\t\tnew AttachRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyArn: access.managed,\n\t\t\t}),\n\t\t);\n\t\tif (recorded && recorded !== access.managed) await detach(recorded);\n\t\tif (recorded !== access.managed) {\n\t\t\tawait iam.send(\n\t\t\t\tnew TagRoleCommand({\n\t\t\t\t\tRoleName: name,\n\t\t\t\t\tTags: [{ Key: POLICY_TAG, Value: access.managed }],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t\tif (!created) await deleteInline();\n\t\treturn { arn, created, previousSubject, detached, leftAttached };\n\t}\n\n\tif (access.inline) {\n\t\tawait iam.send(\n\t\t\tnew PutRolePolicyCommand({\n\t\t\t\tRoleName: name,\n\t\t\t\tPolicyName: SCOPED_POLICY_NAME,\n\t\t\t\tPolicyDocument: access.inline,\n\t\t\t}),\n\t\t);\n\t} else if (!created) {\n\t\tawait deleteInline();\n\t}\n\tif (created) return { arn, created, previousSubject, detached, leftAttached };\n\n\tif (recorded) {\n\t\tawait detach(recorded);\n\t\tawait iam.send(\n\t\t\tnew UntagRoleCommand({ RoleName: name, TagKeys: [POLICY_TAG] }),\n\t\t);\n\t}\n\t// A role from before gkm recorded what it attached: what is attached may\n\t// be gkm's or someone's own, so it is named, not detached.\n\tconst { AttachedPolicies = [] } = await iam.send(\n\t\tnew ListAttachedRolePoliciesCommand({ RoleName: name }),\n\t);\n\tfor (const policy of AttachedPolicies) {\n\t\tif (policy.PolicyArn && !detached.includes(policy.PolicyArn)) {\n\t\t\tleftAttached.push(policy.PolicyArn);\n\t\t}\n\t}\n\treturn { arn, created, previousSubject, detached, leftAttached };\n}\n\n/**\n * An IAM client for the stage's account.\n *\n * `fromIni` for a named profile, deliberately: it resolves exactly that\n * profile — keys, assume-role, `credential_process` or SSO, whichever\n * ~/.aws/config says — and never falls back to AWS_* in the environment. The\n * provider chain checks the environment first, so exported staging keys would\n * quietly win over `--profile prod` and the production role would land in the\n * staging account.\n */\nexport async function iamFor(\n\tprofile?: string,\n\tendpoint?: string,\n): Promise<IAMClient> {\n\tconst config: IAMClientConfig = { region: 'us-east-1' };\n\tif (profile) {\n\t\tconst { fromIni } = await import('@aws-sdk/credential-providers');\n\t\tconfig.credentials = fromIni({ profile });\n\t}\n\tif (endpoint) {\n\t\tconfig.endpoint = endpoint;\n\t\tconfig.credentials = { accessKeyId: 'test', secretAccessKey: 'test' };\n\t}\n\treturn new IAMClient(config);\n}\n\nexport async function deployGithubCommand(\n\toptions: DeployGithubOptions,\n\tdeps: { iam?: IAMClient; gh?: Gh; cwd?: string } = {},\n): Promise<{ roleArn?: string; repo: string; subject: string }> {\n\tconst gh = deps.gh ?? ghCli;\n\tconst { workspace } = await loadWorkspaceConfig(deps.cwd);\n\tassertDeployedStage(workspace.stages, options.stage);\n\n\tconst repo =\n\t\toptions.repo ??\n\t\tgh(['repo', 'view', '--json', 'nameWithOwner', '-q', '.nameWithOwner']);\n\tconst role = roleName(workspace.name, options.stage);\n\t// The exact subject GitHub will send — read from the repository's OIDC\n\t// settings, before anything is changed, so a format no role can trust\n\t// fails here.\n\tconst oidc = resolveOidcSubject(gh, repo, options.stage);\n\t// The buckets an s3 provider creates are named after their constructs,\n\t// and a stage with a database is backed up: the role is scoped to\n\t// exactly those.\n\tconst manifest = await discover({\n\t\tpatterns: constructGlobs(workspace),\n\t\tcwd: workspace.root,\n\t});\n\tconst access = deployAccess(\n\t\tworkspace,\n\t\toptions.stage,\n\t\toptions.policyArn,\n\t\tmanifest,\n\t);\n\t// Read through the keystore so a key still at the place keys used to be kept\n\t// is found, and copied to where it is kept now.\n\tconst key = await readKey(options.stage, keystoreProject(workspace));\n\tconst keyPath = getKeyPath(options.stage, keystoreProject(workspace));\n\tconst hasKey = key !== null;\n\t// A stage whose secrets are in a store is read from there by the deploy job,\n\t// with the role; it needs no key on GitHub.\n\tconst remote = isRemoteStore(workspace, options.stage);\n\t// Read from the stage's own store, with the same credentials the role is\n\t// created with: what the deploy job will find there.\n\tconst store = await secretsStoreFor(workspace, options.stage, {\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n\tconst hasSecrets = remote && (await store.read(options.stage)) !== null;\n\tconst via = options.profile\n\t\t? `profile \"${options.profile}\"`\n\t\t: 'the default credentials';\n\n\tlogger.log(`\\n🔐 GitHub → AWS for stage \"${options.stage}\"\\n`);\n\tlogger.log(` Repository: ${repo}`);\n\tlogger.log(` AWS profile: ${options.profile ?? '(default credentials)'}`);\n\tlogger.log(` Role: ${role}`);\n\tif (access.kind === 'managed') {\n\t\tlogger.log(` Policy: ${access.policyArn} (${access.reason})`);\n\t} else if (access.describe.length > 0) {\n\t\tlogger.log(\n\t\t\t` Policy: inline \"${SCOPED_POLICY_NAME}\", scoped to the compose deploy — only:`,\n\t\t);\n\t\tfor (const line of access.describe) logger.log(` ${line}`);\n\t} else {\n\t\tlogger.log(\n\t\t\t' Policy: none — a compose deploy with file secrets and local state reads nothing from AWS',\n\t\t);\n\t}\n\tlogger.log(\n\t\t` Trusted by: ${oidc.subject} only (${SUBJECT_KINDS[oidc.kind]})`,\n\t);\n\tif (oidc.warning) logger.log(` ⚠ ${oidc.warning}`);\n\tif (remote) {\n\t\tlogger.log(\n\t\t\t` Secrets: ${hasSecrets ? `in the store (${store.name}), read with ${via}` : `none in the store yet — gkm secrets:set <KEY> '…' --stage ${options.stage}`}`,\n\t\t);\n\t} else {\n\t\tlogger.log(\n\t\t\t` Secrets key: ${hasKey ? keyPath : `none at ${keyPath} — run gkm secrets:init --stage ${options.stage} first`}`,\n\t\t);\n\t}\n\n\tif (options.dryRun) {\n\t\tif (access.kind === 'scoped') {\n\t\t\tconst document = scopedPolicyDocument(access.statements('<account>'));\n\t\t\tif (document) {\n\t\t\t\tlogger.log(\n\t\t\t\t\t`\\n Inline policy:\\n${JSON.stringify(JSON.parse(document), null, 2).replace(/^/gm, ' ')}`,\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tlogger.log('\\n --dry-run: nothing changed.\\n');\n\t\treturn { repo, subject: oidc.subject };\n\t}\n\n\tconst iam = deps.iam ?? (await iamFor(options.profile));\n\tconst providerArn = await ensureOidcProvider(iam).catch((error) => {\n\t\t// The first AWS call is where an expired SSO login surfaces; the SDK's\n\t\t// message does not say which profile, and this command knows.\n\t\tif (/SSO session/i.test(String(error?.message)) && options.profile) {\n\t\t\tthrow new SsoSessionExpired(options.profile);\n\t\t}\n\t\tthrow error;\n\t});\n\tconst outcome = await ensureRole(\n\t\tiam,\n\t\trole,\n\t\ttrustPolicy(providerArn, oidc.subject),\n\t\taccess.kind === 'managed'\n\t\t\t? { managed: access.policyArn }\n\t\t\t: {\n\t\t\t\t\tinline: scopedPolicyDocument(\n\t\t\t\t\t\taccess.statements(accountOf(providerArn)),\n\t\t\t\t\t),\n\t\t\t\t},\n\t);\n\tconst roleArn = outcome.arn;\n\tlogger.log(`\\n ✓ AWS: ${roleArn}${outcome.created ? ' (created)' : ''}`);\n\tif (!outcome.created) {\n\t\tlogger.log(\n\t\t\toutcome.previousSubject === oidc.subject\n\t\t\t\t? ` ✓ Trust: unchanged, ${oidc.subject}`\n\t\t\t\t: ` ✓ Trust: ${outcome.previousSubject ?? '(no subject)'} → ${oidc.subject}`,\n\t\t);\n\t}\n\tfor (const detached of outcome.detached) {\n\t\tlogger.log(` ✓ Detached ${detached} (attached by gkm, no longer needed)`);\n\t}\n\tif (access.kind === 'scoped') {\n\t\tfor (const attached of outcome.leftAttached) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ ${attached} is still attached to ${role}. gkm has no record of attaching it, so it is left; the compose deploy does not need it. Detach it with: aws iam detach-role-policy --role-name ${role} --policy-arn ${attached}${options.profile ? ` --profile ${options.profile}` : ''}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t// The environment the generated deploy workflow runs the stage in.\n\tgh(['api', '--method', 'PUT', `repos/${repo}/environments/${options.stage}`]);\n\tgh([\n\t\t'variable',\n\t\t'set',\n\t\t'AWS_ROLE_ARN',\n\t\t'--env',\n\t\toptions.stage,\n\t\t'--repo',\n\t\trepo,\n\t\t'--body',\n\t\troleArn,\n\t]);\n\tlogger.log(` ✓ GitHub: environment \"${options.stage}\", AWS_ROLE_ARN`);\n\n\tif (remote) {\n\t\t// Nothing to send: the deploy job reads the stage's store itself.\n\t\tif (!hasSecrets) {\n\t\t\tlogger.log(\n\t\t\t\t` ⚠ The \"${options.stage}\" store holds no secrets yet; set them with gkm secrets:set <KEY> '…' --stage ${options.stage}${options.profile ? ` (AWS_PROFILE=${options.profile})` : ''}.`,\n\t\t\t);\n\t\t}\n\t} else if (hasKey) {\n\t\t// Through stdin, so the key is never an argument a process list shows.\n\t\tgh(\n\t\t\t[\n\t\t\t\t'secret',\n\t\t\t\t'set',\n\t\t\t\t'GKM_SECRETS_KEY',\n\t\t\t\t'--env',\n\t\t\t\toptions.stage,\n\t\t\t\t'--repo',\n\t\t\t\trepo,\n\t\t\t],\n\t\t\tkey!,\n\t\t);\n\t\tlogger.log(' ✓ GitHub: GKM_SECRETS_KEY');\n\t} else {\n\t\tlogger.log(\n\t\t\t` ⚠ No secrets key for \"${options.stage}\"; run gkm secrets:init --stage ${options.stage}, then this again.`,\n\t\t);\n\t}\n\n\tlogger.log(\n\t\t`\\n Protect it: add required reviewers to the \"${options.stage}\" environment in the repository settings.\\n`,\n\t);\n\treturn { roleArn, repo, subject: oidc.subject };\n}\n\nconst SUBJECT_KINDS = {\n\tdefault: \"the repository's default subject\",\n\timmutable: \"the repository's immutable subject\",\n\tcustom: \"the repository's custom subject template\",\n\tassumed: 'assumed: the default format',\n} as const;\n\n/** The profile's SSO login has lapsed; the fix is one command away. */\nexport class SsoSessionExpired extends GkmError {\n\tconstructor(readonly profile: string) {\n\t\tsuper(\n\t\t\t`The SSO session for profile \"${profile}\" has expired or is invalid. Run: aws sso login --profile ${profile}`,\n\t\t);\n\t\tthis.name = 'SsoSessionExpired';\n\t}\n}\n"],"mappings":"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"}
|