{"_id":"@aoctech/cdk","_rev":"11-8119f3828d5ac75627d5c6d087eb00e6","name":"@aoctech/cdk","dist-tags":{"latest":"0.8.0"},"versions":{"0.1.0":{"name":"@aoctech/cdk","version":"0.1.0","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.1.0","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"f2b59a912e9cf4f628adf42d358f4df1acaf1dfe","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.1.0.tgz","fileCount":22,"integrity":"sha512-XTIzpsZUjbzYkklaxbgQOtEi+tej9dq556VDFy9hbSLUiIfvy3uQ+wygas53noUgfL7gNdS7kWtVs6ZImCpsYQ==","signatures":[{"sig":"MEUCIQD/kn+Raxe6h7HzWfF0nK9VDZEGxt6R8lOe9ASsqxgQ0gIgeCf7XAKeDisobBOk21YSkueDnX6iuIlgJPU16UvcG3s=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":153077},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"ec43b9ec5f34742e3e626d1ce5f4618b74449258","scripts":{"cdk":"cdk","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"aoctech","email":"dev@aoctech.app"},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"11.16.0","description":"Shared CDK constructs for the CTech platform's private-IPv4-only (no NAT Gateway) EC2 service pattern.","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1126.0","ts-node":"^10.9.2","constructs":"^10.6.0","typescript":"~6.0.3","@types/node":"^25.9.2","aws-cdk-lib":"^2.258.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.258.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.1.0_1784369381566_0.8557229239653186","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@aoctech/cdk","version":"0.1.1","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.1.1","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"9238dccb418ccf102a2676af0d65f57a18090fe0","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.1.1.tgz","fileCount":24,"integrity":"sha512-5AvIhKC/VRmy+Fb1GDlEbdkzH4J686KCjJIGBQmyWsPhpXKp5v49egXU7GAu6pJ1uB0HbLLfCnuItPFdsseGng==","signatures":[{"sig":"MEUCIFW/+HlBeF+5NFiN3Mt1B86i5j9MdWWFdM6s7txyYyYeAiEAy5Jz0LkVfLQLai6Ljvi8xhxieqSfqUjDRsk0mQo9CfI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.1.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":190950},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"8b1f25b694e5b1bc99cb2f27219eb5540b75b231","scripts":{"cdk":"cdk","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared CDK constructs for the CTech platform's private-IPv4-only (no NAT Gateway) EC2 service pattern.","directories":{},"_nodeVersion":"24.18.1","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1126.0","ts-node":"^10.9.2","constructs":"^10.6.0","typescript":"~6.0.3","@types/node":"^25.9.2","aws-cdk-lib":"^2.258.0"},"peerDependencies":{"constructs":"^10.6.0","aws-cdk-lib":"^2.258.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.1.1_1785528183980_0.10164577989371781","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@aoctech/cdk","version":"0.2.0","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.2.0","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"88a7523276308f04f8623957e3cf13194a07630a","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.2.0.tgz","fileCount":30,"integrity":"sha512-EJKGa4ZxQS+tKbgA1s4vqSNX68FiMnZb62C9Xn+DcnOFM2FokCfLZpzTfpw/P96aeWsCwWSoUHPjDOUQQkOivw==","signatures":[{"sig":"MEYCIQCjA4KP/YQGEtpgHRnHki9OO8KFEZJenv8y04as24cYyQIhAIserAx3dEHgeWn/jh60tuw+8bH9IRI61cpJmsn5BBuN","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.2.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":272225},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"1d203efce4e46c97ccdf37b577b8dcd80d57b3d9","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1136.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.2.0_1786724829973_0.07879951801047547","host":"s3://npm-registry-packages-npm-production"}},"0.2.1":{"name":"@aoctech/cdk","version":"0.2.1","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.2.1","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"9ac46371ec89b2863d1d30d2872bb7de2e140ea2","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.2.1.tgz","fileCount":30,"integrity":"sha512-w5onqY83MMm2Py5GsoVBqIMvJqjOzue129PQp6NKIVqQ6OYLF9cYoZwAjpB0JM9f1TeEdJ8iUJug4ix/AsbRaQ==","signatures":[{"sig":"MEUCIG6oc5Yf4DboAxE1myi6X8a6AvaiybvDyLDF45c/mMvlAiEAvR0SMMX3SAdvmEnqy5QJ0GM7G6GdUrne8yq1eqVKrzE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.2.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":278600},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"f02727cabefaf4810c0436272c5d86d446d327ac","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1136.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.2.1_1786731920957_0.5954570311820935","host":"s3://npm-registry-packages-npm-production"}},"0.4.0":{"name":"@aoctech/cdk","version":"0.4.0","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.4.0","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"61c1d55961e221b9ca1cf1c47dda98f5ca43c26e","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.4.0.tgz","fileCount":34,"integrity":"sha512-Otp1NnwFiXGJ0MAVG1pfwp1pwdLTKiNgrm6GuXfoXZDczUNriLTxZm3NW/pP80kpN0uM5/bKhmh6bsEhjBhTlA==","signatures":[{"sig":"MEUCIFDsXzsXGP8mOAVORc6fnFV+qlQIgFsSloy8j30vtTMkAiEA0iVQhdzMtEmCdgBUxqIGzFnFajwdDLDfNtw1S77mlA0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.4.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":355007},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"da3fd68f9eb07bbe85760ffdfae67f73e3e89e2f","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1137.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.4.0_1787105524668_0.6353502446338444","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@aoctech/cdk","version":"0.4.1","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.4.1","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"91aab8418a7c69c7152b2e29ea0222db29a6952a","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.4.1.tgz","fileCount":34,"integrity":"sha512-46ug/apisOmFD4XYVqAhDd1u5HfPsKrGxDS8xZNxFpIv+CFLmxslpfswtoxp6EGba9kbGWa8qGPIp5O/YN7I3g==","signatures":[{"sig":"MEUCIDEtp0HeK3mcMwW/Z1gw6v0S2A041Fo77zvaVNPIpAhEAiEAnUmvoqNlJKTy02dLbfXKDvxZ8FytYh99WR16UtXTN/8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.4.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":359738},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"aeedcb29058c4d3d4e58c9a91df4f2e64f29ee24","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1137.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.4.1_1787175569579_0.7065445662434402","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@aoctech/cdk","version":"0.5.0","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.5.0","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"3db29bb45f0e6dbf867f7967e680badc4c38ed4a","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.5.0.tgz","fileCount":32,"integrity":"sha512-Cm8A5LUBbhnEasnJSBGtLZ3mcZVTOt7m91XCoYEm3sZO2rdeYzjN7dkWarfMNC2P4KqSUKRIRotN1xbakw4C2w==","signatures":[{"sig":"MEUCIE7OvFciPXfC/UsbgCjgI6VndExYaomsITpZPeXF1RaKAiEA8v+g1d5rgAiBt9ijT4zL7D7m0czcfUxnELyJcyNHHkg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQCSqFtJG+lWCIOaB2yBjJ19FhUTiftDeORpq6maAak6rAIgGbmun9b8QSVsHYUpR/3fztbXcTfuMFtO2oIgBvNnIGQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.5.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":326348},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"9a08ed0e660dec8b595457287f5c5d29cfc7906c","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1137.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.5.0_1787347772748_0.22736594175991587","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@aoctech/cdk","version":"0.7.0","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.7.0","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"a27c7705a5db3ee524657c2ddfe569ad727a7b4a","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.7.0.tgz","fileCount":36,"integrity":"sha512-HtRAQfPzr1liBviojYwA0BikQUCqYFoYu4CPmWPHRKwWsTdxK0PWmrfhESTN8YEby3eZJTqOpGZul/NSNwLehg==","signatures":[{"sig":"MEUCIQDYDFR+FCdDzs1apSY3bL9Sw08IrmhRZDSSShpVD2JQcQIgIB7pS/81YtrFgJ1M88zbNtfcnY/Rj9JP+JJnWJBbRKc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCICtUbsVm6wDr6FEshuzaPQpWpp8XExU4KoTIMYP4BXMKAiEAi/Hd7xQU96eIZzuJce7fQHzFwD9sLbKQ8uLDHOK7DR4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.7.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":393547},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"778b966c6c4e16b086a74d7c5267a77bd3c6d919","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1137.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.7.0_1787577036534_0.6810893526280302","host":"s3://npm-registry-packages-npm-production"}},"0.7.1":{"name":"@aoctech/cdk","version":"0.7.1","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.7.1","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"d9d8d135ea9737192c629633416cabf2ab7a9a57","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.7.1.tgz","fileCount":36,"integrity":"sha512-sfd7xZ89xk06Nuzmv4yISUnwemCZa5JGZHL9nC2v0xz+V0mrhgJea1MhlGI3r4tr1y80+V1862/Axbripb0OHg==","signatures":[{"sig":"MEUCIDfjfrKP7ERdtTZLpPJb0blT8GgXLxNmCWDHdo2knguoAiEAi6Uo2wmj2kkL2HrZVVseq/smXCqZbOg9CrMSYBDpBSk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIFMtU8e42KvxzxEmj1noJGmV3tf3V4kP8ViYErhv15toAiEAsKb9QdDuis5x41vTqqaKEuKEIyu4+l0okelJxiGu+Zo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.7.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":393539},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"e706aff17bfa598974549bf27f160c449ef7622b","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1137.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.7.1_1787577809841_0.8264426689897053","host":"s3://npm-registry-packages-npm-production"}},"0.7.2":{"name":"@aoctech/cdk","version":"0.7.2","license":"SEE LICENSE IN LICENSE.md","_id":"@aoctech/cdk@0.7.2","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"7ab29442b9fd22465b656108f4ba28bf28499228","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.7.2.tgz","fileCount":36,"integrity":"sha512-NJ6BL6e2orjcXPjvN0VRojpZqk0/5wGV6Mn2uAWC1C8hquSA5tOV/68RVrgaYeLlO7Fq5e2mLeE4aOg0zWFwfg==","signatures":[{"sig":"MEYCIQCYKXmdTLMwNZ/TFrC9NgrzMSackATImp17CbqQ3ZY/qQIhAMvtEsY5rFOqTa/6k+o4qtCRMfEhuFnDmmCvsOhKJ2vc","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIQD7g+3rw8FxQBt7Ciqt/H0WpquLfwUUfsNjmpyCFcHQxQIgAvDMNzQmKf56uRoW6FUzoyz5CKxb4QX0vs9Oa4bvHos=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.7.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":394287},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"56b4eac3702798cf9ed184d327ef65f3d92fb28d","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1137.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.2.0","aws-cdk-lib":"^2.265.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.265.0"},"_npmOperationalInternal":{"tmp":"tmp/cdk_0.7.2_1787781498642_0.809084822913156","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"_id":"@aoctech/cdk@0.8.0","bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"dist":{"shasum":"972306c165dc54a654be1fe96aff138b71096a1f","tarball":"https://registry.npmjs.org/@aoctech/cdk/-/cdk-0.8.0.tgz","fileCount":38,"integrity":"sha512-zh5j6RHB6gbuB9jtVE0FjljEm2RAHqT+hj0tK1nc//fIvZ4AL1C70oAn+nAoO0Qk1mzBTNvo+dSnKYVfeWIG3Q==","signatures":[{"sig":"MEUCIQCGvv5RceOmvI3knUaGlr+jqSEBi0wbVFu7X7F6vFrxsAIgd7Gt7OemFiWtxE82KhqRukFl+JHtbDCW4e066WrGpcw=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIEuC/skcDBItmRBqpiLJkZ26iZ0gg1gbDfuRaZRmIKBrAiBHi/93pP2L9BZxVTZ8XGSDd6BEmTnwL54yOcb2zmBQCA=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@aoctech%2fcdk@0.8.0","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":410931},"main":"dist/index.js","name":"@aoctech/cdk","types":"dist/index.d.ts","gitHead":"880b68f0fa177f7ce920fd6bf4f0200676f2673f","license":"SEE LICENSE IN LICENSE.md","scripts":{"cdk":"cdk","test":"TS_NODE_PREFER_TS_EXTS=true node --require ts-node/register --test test/*.test.ts","build":"tsc","watch":"tsc -w","prepare":"tsc -p tsconfig.build.json"},"version":"0.8.0","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:5ce12818-0303-4c99-a1c1-dab41a6b51ee"}},"homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"_npmVersion":"12.0.2","description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","directories":{},"maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"_nodeVersion":"24.19.0","publishConfig":{"access":"public","provenance":true},"_hasShrinkwrap":false,"devDependencies":{"aws-cdk":"^2.1139.0","ts-node":"^10.9.2","constructs":"^10.8.1","typescript":"~6.0.3","@types/node":"^26.4.1","aws-cdk-lib":"^2.268.0"},"peerDependencies":{"constructs":"^10.8.1","aws-cdk-lib":"^2.268.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/cdk_0.8.0_1788397777136_0.5277774974754954"}}},"time":{"created":"2026-07-18T10:09:41.436Z","modified":"2026-09-03T01:09:37.566Z","0.1.0":"2026-07-18T10:09:41.703Z","0.1.1":"2026-07-31T20:03:04.119Z","0.2.0":"2026-08-14T16:27:10.112Z","0.2.1":"2026-08-14T18:25:21.102Z","0.4.0":"2026-08-19T02:12:04.855Z","0.4.1":"2026-08-19T21:39:29.744Z","0.5.0":"2026-08-21T21:29:32.822Z","0.7.0":"2026-08-24T13:10:36.662Z","0.7.1":"2026-08-24T13:23:29.935Z","0.7.2":"2026-08-26T21:58:18.764Z","0.8.0":"2026-09-03T01:09:37.222Z"},"bugs":{"url":"https://github.com/artur-oliveira/ctech-cdk/issues"},"license":"SEE LICENSE IN LICENSE.md","homepage":"https://github.com/artur-oliveira/ctech-cdk#readme","repository":{"url":"git+https://github.com/artur-oliveira/ctech-cdk.git","type":"git"},"description":"Shared AWS CDK constructs and utilities for CTech EC2 services, HAProxy discovery, and static frontends.","maintainers":[{"name":"aoctech","email":"dev@aoctech.app"}],"readme":"# ctech-cdk\n\nAWS CDK (TypeScript) for shared, account-level CTech infrastructure and the\n`@aoctech/cdk` package used by service CDKs.\n\nThe deployed platform owns the dual-stack VPC, GitHub Actions OIDC provider,\nshared S3 buckets, the production private hosted zone, and the shared Dragonfly\ncache. Public and private API ingress is provided by\n[ctech-lbalancer](https://github.com/artur-oliveira/ctech-lbalancer), not by an\nAWS Application Load Balancer.\n\n## Current architecture\n\n```text\nCtech-Global\n  ├── GitHub Actions OIDC provider\n  ├── ctech-gha-infra role\n  └── global SSM pointers (OIDC provider and ACM certificate)\n\nCtech-{Env}-Network\n  ├── dual-stack VPC across all six pinned us-east-1 AZs, no NAT Gateway\n  ├── S3 and DynamoDB gateway endpoints\n  ├── shared edge security-group identity\n  ├── production-only private zone: internal.aoctech.app\n  └── network/private-zone SSM parameters\n\nCtech-{Env}-S3\n  ├── {env}-ctech-deployments (30-day lifecycle)\n  └── {env}-ctech-application-logs\n      ├── objects >128 KiB → Glacier Flexible Retrieval after 90 days\n      └── all objects expire after 400 days; bucket remains retained\n\nCtech-{Env}-Ec2Scripts\n  ├── {env}-ctech-ec2-scripts, objects under a content-hash prefix\n  └── /ctech/{env}/ec2-scripts/{bucket,version}\n\nCtech-{Env}-Dragonfly\n  ├── DragonflyDB on an EC2 Auto Scaling Group, t4g.nano\n  ├── desired/min/max = 1/1/1 in every environment\n  ├── nightly schedule takes it to zero 22:00-10:00 BRT\n  └── cache.internal.aoctech.app + /ctech/{env}/valkey/url\n```\n\nOnly `GlobalStack`, `NetworkStack`, `S3Stack`, `Ec2ScriptsStack`, and\n`DragonflyStack` are instantiated by `bin/ctech-cdk.ts`. `lib/alb-stack.ts` and\n`PrivateIpv4Ec2Service` are retained as legacy migration code; neither\nrepresents the current production ingress architecture.\n\nThe SSM path `/ctech/{env}/network/alb-sg-id` and the physical security-group\nname retain `alb` for compatibility. The resource is now the shared edge SG\nidentity attached to ctech-lbalancer and trusted by service SGs. Renaming it\nrequires a coordinated migration across every service.\n\n## Traffic and discovery\n\n```text\npublic:  client → Cloudflare → origin.aoctech.app AAAA → HAProxy → service private IPv4\nprivate: service → *.internal.aoctech.app → HAProxy private IPv4 → service private IPv4\n```\n\nThe current four bootstrap route parameters and private aliases are owned by\n`ctech-lbalancer`. Each service CDK owns:\n\n- its API ASG and service security group;\n- its CloudFront/S3 frontend and public API hostname configuration.\n\nFor a new service, `HaproxyEc2Service` can also own its SSM Standard route under\n`/ctech/{env}/lbalancer/routes/{service}` and its private CNAME to\n`lbalancer.internal.aoctech.app`. Existing bootstrap routes must not be created\nfrom a second stack until CloudFormation ownership is explicitly transferred.\n\nctech-lbalancer reconciles route manifests every 30 seconds, discovers healthy\n`InService` instances, reloads HAProxy only after validating the generated\nconfiguration, and updates the Cloudflare origin AAAA record.\n\n## SSM parameters written by deployed stacks\n\n| Path | Description |\n|---|---|\n| `/ctech/global/oidc/provider-arn` | Shared GitHub OIDC provider ARN |\n| `/ctech/global/acm/cert-arn` | Wildcard `*.aoctech.app` ACM certificate ARN |\n| `/ctech/global/dns/private-hosted-zone-id` | Production-owned private zone ID |\n| `/ctech/global/dns/private-hosted-zone-name` | `internal.aoctech.app` |\n| `/ctech/{env}/network/vpc-id` | Shared VPC ID |\n| `/ctech/{env}/network/alb-sg-id` | Compatibility name for the shared edge SG |\n| `/ctech/{env}/s3/deployments-bucket` | Shared deployment-artifact bucket |\n| `/ctech/{env}/s3/logs-bucket` | Shared application-log archive bucket |\n| `/ctech/{env}/valkey/url` | Cache base URL (now Dragonfly); consumers append their DB number |\n| `/ctech/{env}/ec2-scripts/bucket` | Bucket holding the shared EC2 bootstrap scripts |\n| `/ctech/{env}/ec2-scripts/version` | Content hash of `assets/ec2`, and the S3 key prefix the scripts live under |\n| `/ctech/{env}/alerts/topic-arn` | Shared alert topic every service publishes its own failures to |\n\n`SSM.alb(env)` remains exported for compatibility with legacy ALB code, but\nthe current entrypoint does not deploy `AlbStack` and therefore does not write\n`/ctech/{env}/alb/*`.\n\n## npm package\n\n```bash\nnpm install @aoctech/cdk\n```\n\nSource version 0.3.0 exports from `lib/index.ts`:\n\n- `Environment`, `SSMParams`;\n- `SSM`, `DEFAULT_AWS_ACCOUNT`, `DEFAULT_AWS_REGION`;\n- `GithubActionsDeployRoles`, its props, and `githubTrustPrincipal`;\n- the deprecated `PrivateIpv4Ec2Service`;\n- EC2 user-data fragments for dual-stack SSM, CloudWatch, swap, real-IP\n  refresh, and Cloudflare Origin CA trust (superseded by `assets/ec2/*.sh`);\n- `Ec2ScriptRunner`, which emits user data that downloads and runs the shared\n  bootstrap scripts;\n- `AsgScheduleProps`, `DEFAULT_ASG_SCHEDULE` and `addAsgSchedule` for the\n  nightly ASG stop/start pair;\n- `HaproxyEc2Service`, the current private-IPv4 + IPv6 ASG, edge-SG and optional\n  route-registration pattern, with `spot.instanceTypes` for diversifying Spot\n  capacity across compatible instance types;\n- `buildCloudWatchAgentConfig`, with a bounded four-series host/process metric\n  set;\n- `createNextjsStaticFrontend`, which centralizes the repeated S3 + OAC + KVS +\n  CloudFront + CSP + API-origin pattern while accepting service-specific\n  behaviours and rewrite code.\n\nDo not build new services on `PrivateIpv4Ec2Service`, because it creates an ALB\ntarget group and listener rule. `createNextjsStaticFrontend` deliberately adds\nresources directly to the supplied stack with the established IDs (`Bucket`,\n`OAC`, `RouteStore`, `UrlRewrite`, `SecurityHeaders`, `Distribution`), allowing\nan existing frontend stack to migrate without changing logical IDs. Always\nprove that with a template diff before deployment; Wallet's locale rewrite,\nDFE's docs CSP and Poker's avatar behaviour use the documented escape hatches.\n\nDiversify a service across compatible Spot pools through the Spot\nconfiguration. The launch template's `instanceType` remains the fallback when\nthis list is omitted:\n\n```ts\nspot: {\n  instanceTypes: [\n    new ec2.InstanceType('t4g.nano'),\n    new ec2.InstanceType('t4g.micro'),\n  ],\n},\n```\n\n### Releasing\n\n1. Land a green change on `main`.\n2. Bump `package.json` using semver.\n3. Create release/tag `vX.Y.Z` (the next release is `v0.3.0`).\n4. `.github/workflows/publish.yml` publishes with npm trusted publishing and\n   provenance; no `NPM_TOKEN` is stored.\n5. Upgrade consumers deliberately and run their synths.\n\n## Deployment\n\nPrerequisites: Node.js 24, a bootstrapped CDK account, and AWS credentials with\nthe required infrastructure permissions.\n\n```bash\nnpm ci\nnpm run build\nENVIRONMENT=prod npx cdk synth\nENVIRONMENT=prod npx cdk diff --all\nENVIRONMENT=prod npx cdk deploy --all --require-approval never\n```\n\nThe initial `Ctech-Global` deployment must be performed with credentials that\ncan create the OIDC provider and `ctech-gha-infra` role. Later GitHub Actions\ndeploys use OIDC rather than long-lived AWS keys.\n\n| Variable | Default | Purpose |\n|---|---|---|\n| `ENVIRONMENT` | `dev` | `dev`, `stage`, or `prod` |\n| `AWS_ACCOUNT` | account constant | Target AWS account |\n| `AWS_REGION` | `us-east-1` | Target region |\n| `AWS_CERTIFICATE_ARN` | constant | Wildcard ACM certificate |\n| `GITHUB_REPO` | `artur-oliveira/ctech-cdk` | Repository trusted by the infra role |\n\nBranch-to-environment behavior is defined by the workflow, not by the CDK\nentrypoint. Inspect the workflow before assuming a branch mapping.\n\n### Service URL parameters\n\nAfter the private zone and ctech-lbalancer aliases exist, seed the non-secret\nruntime URLs before replacing service instances:\n\n```bash\nCTECH_AWS_PROFILE=ctech ./scripts/configure-service-url-parameters.sh prod\n```\n\nThe command creates transport-only internal account/JWKS URLs, each service's\nprivate base URL, the public audience/browser URLs, Poker's public avatar base\nURL, and Poker's private Wallet URL. It deliberately does not overwrite ctech-account's existing\n`base-url`/`app-url`: those values participate in OAuth issuer and redirect\ncontracts and must remain public. Dev/stage private URLs require their VPC to\nbe associated with the private hosted zone before use.\n\n## Alerts\n\nOne SNS topic per environment, `ctech-{env}-alerts`, with a single confirmed\ne-mail subscription. Every service publishes its own failures to it through\n`gopkg.aoctech.app/api-commons/alerts`, reading the ARN from\n`/ctech/{env}/alerts/topic-arn`.\n\nDeliberately not CloudWatch: an alarm is billed per alarm per month and the\nfamily would need dozens of them to say the one thing that matters — \"this job\ndid not do its work\" — which every job already knows at the moment it happens.\nSNS e-mail is free for the first thousand notifications a month, well past the\nvolume at which anybody would stop reading them.\n\nWhat it does not buy is liveness. A process that never runs publishes nothing,\nand silence here reads exactly like health, so a service that needs \"did it run\nat all\" has to assert it from the next run rather than from a metric.\n\n```bash\nALERT_EMAIL=you@example.com ENVIRONMENT={env} npx cdk deploy Ctech-{Env}-Alerts\n```\n\nThe stack is skipped entirely when `ALERT_EMAIL` is unset — an address baked\ninto source is one nobody notices is wrong. AWS sends a confirmation e-mail on\nfirst deploy and the subscription delivers nothing until it is accepted.\n\n## Shared cache (Dragonfly)\n\n`DragonflyStack` replaces `ValkeyStack` and deliberately keeps its contract:\nthe same `/ctech/{env}/valkey/url` parameter and the same\n`cache.internal.aoctech.app` record, so no service repository changes. The two\nstacks own the same parameter and the same record and cannot coexist. Cut over\none environment at a time:\n\n```bash\naws cloudformation delete-stack --stack-name Ctech-{Env}-Valkey\nENVIRONMENT={env} npx cdk deploy Ctech-{Env}-Dragonfly\n```\n\nThe cache is empty on both sides of that gap by design. `lib/valkey-stack.ts`\nis kept only so the previous template can still be read; nothing instantiates\nit.\n\nThe binary is the official `dragonfly-aarch64` release, downloaded and verified\nagainst a SHA-256 pinned in `assets/dragonfly/install.sh` and republished as a\nCDK asset — the instance has no NAT and no public IPv4, so it can only fetch\nfrom S3. Version and digest live in that script rather than in TypeScript\nbecause the asset hash is the hash of the directory: editing the script is what\ninvalidates the S3 object and versions the launch template.\n\n`install.sh` only downloads and verifies, so bundling runs directly on the synth\nhost through CDK's `local` hook - the CI runner (`ubuntu-slim`) has no Docker\ndaemon. The `amazonlinux:2023` container stays as the fallback for a host without\n`curl` or `tar`. When run locally the script honours `ASSET_OUTPUT_DIR`.\n\nFlag choices are driven by the 512 MiB t4g.nano:\n\n| Flag | Value | Why |\n| --- | --- | --- |\n| `--maxmemory` | `256mb` | Hard floor, not a sizing choice: Dragonfly exits with `There are 1 threads, so 256.00MiB are required` below 256 MiB per proactor thread. Dataset cap, not process RSS |\n| `--rss_oom_deny_ratio` | `0.7` | Denies OOM-prone writes at ~180 MiB RSS. The 1.25 default assumes a host sized for `--maxmemory`; on a 512 MiB nano it would feed the OOM killer |\n| `--proactor_threads` | `1` | Default is one per core; a second thread on a nano only buys a second set of arenas |\n| `--dbnum` | `8` | `/0` cache, `/1` ws pub/sub, `/2+` per service |\n| `--dbfilename` | empty | Disables the shutdown snapshot on a cache that is scaled to zero nightly |\n| `--cache_mode` | `true` | Evicts under pressure instead of failing writes; matches the previous `allkeys-lru` |\n| `--publish_buffer_limit` | `16mb` | Default is 196 MB per IO thread, and the hard limit is 4x the soft one |\n| `--pipeline_buffer_limit` | `32mb` | Default is 128 MB per IO thread |\n| `--pubsub_slow_subscriber_timeout_ms` | `5000` | Off by default; drops a subscriber that stopped draining instead of parking every publisher |\n\nPub/Sub buffers are process memory and are **not** counted against\n`--maxmemory`. `ctech-go-common/ws` holds one `PSUBSCRIBE` connection per API\ninstance (ctech-dfe websockets), so a single slow consumer is enough to reach\nthose limits. It resubscribes on close, at the cost of the messages published\nduring the gap - Pub/Sub is fire-and-forget either way.\n\n`--cache_mode` does not affect Pub/Sub: messages are not keyspace entries, and\n`PUBLISH` is not an OOM-denied command. It only decides what happens to keys at\nthe cap - eviction, versus every write failing, including the wallet `SETNX`\nlock. On this box the binding limit is `--rss_oom_deny_ratio`, not the 256 MiB\ndataset cap: the real working set is around 64 MiB, so eviction at 256 MiB would\nnever fire before the host ran out of RAM.\n\nThe instance also gets 512 MiB of swap through `setup-swap.sh`. Without it the\nOOM killer picks the largest RSS — Dragonfly — and `Restart=always` brings it\nback empty, which presents as a healthy cache that silently lost everything.\n\nA boot that never gets a `PONG` within 60 seconds calls\n`autoscaling:SetInstanceHealth` on itself: the ASG health check is EC2-level\nand would otherwise keep an empty instance serving the DNS record. There is no\nscale-from-zero policy, because that needs a metric published while the cache\nis down and nothing in the organisation publishes one.\n\n## Cost and resilience constraints\n\n- The VPC has zero NAT Gateways; workloads use IPv6 and free gateway endpoints.\n- Dragonfly intentionally remains one `t4g.nano` instance with no persistence\n  (`--dbfilename=`). It is a cache/pub-sub service, not a durable store.\n  Clustering/sharding is deferred because its operational complexity and extra\n  compute are not justified by the present traffic or SLO.\n- Production LBalancer intentionally remains one instance because Cloudflare's\n  origin is one IPv6 address. Multiple nodes require a separate multi-origin or\n  balancing strategy and would add cost without a demonstrated availability\n  need. Service ASGs also keep one baseline instance and retain max 3 where\n  configured; this decision should be revisited only from measured SLO/traffic.\n- `internal.aoctech.app` is created only by the production network stack.\n  Enabling private M2M for another VPC requires explicitly associating that VPC\n  with the existing zone.\n- Shared application-log objects have a 400-day retention. Archives over 128\n  KiB transition to Glacier after 90 days; smaller daily archives stay Standard\n  because archival minimum billable sizes can cost more than the storage saved.\n- `ctech-gha-infra` has `AdministratorAccess` for broad CDK deployments.\n  It is protected by GitHub OIDC, but reducing its blast radius remains a\n  worthwhile hardening project.\n\n## Adding a service\n\n1. Read the shared VPC and edge-SG IDs from SSM/CI.\n2. Create an independent service IAM role, health endpoint and service-specific\n   user data.\n3. Add `addCloudflareOriginCaCommands(userData)` before starting a client that\n   calls `*.internal.aoctech.app`; it downloads only the official Cloudflare\n   Origin CA RSA root, verifies its pinned SHA-256 and X.509 validity, then runs\n   `update-ca-trust extract`.\n4. Use `HaproxyEc2Service` for its ASG, logs, SG, scaling and validated route\n   manifest; allow only the service port from the shared edge SG.\n5. Create the private DNS alias only where the hosted zone is associated.\n6. Use `buildCloudWatchAgentConfig` and scope deployment/log bucket access to the\n   service prefix.\n7. Use `createNextjsStaticFrontend` for the static SPA and add only genuinely\n   service-specific behaviours through its callback.\n8. Use OIDC roles separated by API, frontend, and infrastructure duties.\n9. Run tests, TypeScript compilation, and CDK synth before deployment.\n\n### Bootstrapping an instance\n\nUser data is a list of script invocations, not a list of files. Compose it with\n`Ec2ScriptRunner`:\n\n```ts\nconst scripts = new Ec2ScriptRunner(this, 'Scripts', {environment});\nscripts.install(userData);\nscripts.run(userData, 'setup-base.sh', 'ctech-example', 'nginx');\nscripts.run(userData, 'setup-swap.sh', '256');\nscripts.run(userData, 'setup-dualstack.sh');\nscripts.run(userData, 'setup-cloudflare-ca.sh');\nscripts.run(userData, 'setup-realip.sh', vpc.vpcCidrBlock);\nscripts.run(userData, 'setup-nginx.sh', '8080', '8000', '/v1.0/health-check');\n```\n\nThe instance role needs `s3:GetObject` on the scripts bucket:\n`scripts.grantRead(role)`.\n\nOnly values CloudFormation has to resolve stay inline: bucket names, log group\nnames, the CloudWatch agent JSON, and `/etc/app-static.env`. Per-service derived\nenvironment variables go in `/opt/app/service-env.sh`. `setup-nginx.sh` has three\nextension points, all optional:\n\n| File | Included in | Use for |\n|---|---|---|\n| `/etc/nginx/conf.d/http-*.conf` | `http {}` | extra `limit_req_zone`, `map`, gzip overrides |\n| `/etc/nginx/conf.d/location-*.conf` | `server {}` | extra `location` blocks |\n| `/etc/nginx/conf.d/proxy-*.conf` | `location / {}` | extra `limit_req` / `limit_conn` on the catch-all |\n\nThe scripts themselves live in `assets/ec2/` in this repository. Editing one\nchanges the asset hash, which changes every consuming service's user data on its\nnext deploy — that is what triggers the instance refresh, and it means a script\nchange is a cross-repository change.\n\n#### Zero-downtime rolling deploy on a single instance\n\nBy default `deploy.sh` restarts one `app` process, so a deploy has a brief gap\nwhile it comes back up. A service can opt into a second process on an\nalternate port instead — nginx round-robins across both, and `deploy.sh` rolls\nthem one at a time so the instance keeps serving throughout:\n\n```ts\nscripts.run(userData, 'setup-nginx.sh', '8080', '8000', '/v1.0/health-check', '100', '1m', '8001');\nscripts.run(userData, 'setup-app-service.sh', 'CTech Example API', 'app', 'network.target nginx.service', '8001');\nscripts.run(userData, 'setup-deploy.sh', deploymentsBucketName, 'app', 'http://127.0.0.1:8000/v1.0/health-check');\n```\n\n`setup-deploy.sh` needs no change to opt in — it detects the alt port from\n`/opt/app/alt-port`, written by `setup-app-service.sh`. Omitting the alt-port\nargument on both calls (the existing behavior of every current service) keeps\nthe traditional single-process restart. The trade-off is a permanently\nresident second process (RAM, always-on) in exchange for no restart gap;\nskip it on memory-constrained instances that can tolerate the few-second blip.\n\nDo not add service-specific tables, Lambdas, or buckets to this repository.\n","readmeFilename":"README.md"}