Alternatives
1. RSA Key Generation: Lambda vs Pre-Generated
Section titled “1. RSA Key Generation: Lambda vs Pre-Generated”Chosen: Lambda custom resource (Option A).
Alternative: Pre-generate key pair externally, store in Secrets Manager manually, pass ARN to CDK.
Why rejected: 5 partitions across 3 infrastructures — manual
pre-generation doesn’t scale. The existing repo has Lambda custom
resource patterns (write-ns-records-to-upstream-dns.ts). Future
rotation is a config change, not a manual ceremony.
2. Image Bucket: BulkStoresStack vs ImageStorageStack
Section titled “2. Image Bucket: BulkStoresStack vs ImageStorageStack”Chosen: Co-located in ImageStorageStack (PD-04).
Original plan: ImageAssetBucket in BulkStoresStack,
ImageAssetCdn in a separate stack.
Why changed: CDK’s S3BucketOrigin.withOriginAccessControl()
auto-adds a bucket policy referencing the distribution, creating a
cross-stack circular dependency. Co-location was also architecturally
better — bucket, CDN, and signing keys share the same lifecycle (RETAIN).
3. CDN Stack: Extend PurposeIngress vs New Stack
Section titled “3. CDN Stack: Extend PurposeIngress vs New Stack”Chosen: New ImageStorageStack (PD-01).
Alternative: Add CDN constructs to the existing PurposeIngress
stack.
Why rejected: Different lifecycle (RETAIN vs mutable), different
security model (signed cookies vs JWT), different caching (aggressive
vs disabled). Separating follows the existing pattern where
BulkStoresStack is independent from PurposeIngress.
4. Domain: io.arda.cards vs assets.arda.cards
Section titled “4. Domain: io.arda.cards vs assets.arda.cards”Chosen: Dedicated assets.arda.cards domain family (PD-02).
Alternative: Use the existing io.arda.cards subdomain zone.
Why rejected: API traffic and static asset delivery serve
fundamentally different capabilities — different security models (JWT
vs signed cookies), different caching strategies, different content
types. Sharing the io domain would conflate these concerns.
5. Presigning Role: Separate Construct vs Embedded
Section titled “5. Presigning Role: Separate Construct vs Embedded”Chosen: Embedded in ImageAssetBucket (matching UploadBucket
pattern).
Alternative: Standalone ImageUploadPresigningRole construct in
constructs/iam/.
Why rejected: The presigning role is tightly coupled to the bucket
(references bucket ARN, enforces SSE on that bucket). The existing
UploadBucket embeds its role. No use case for a standalone role.
6. Root Account Deployment: Full Automation vs Minimal Script
Section titled “6. Root Account Deployment: Full Automation vs Minimal Script”Chosen: Minimal deploy-root.sh script (PD-03).
Alternative: Full CI automation with GitHub Actions OIDC role for root account.
Why rejected: OIDC role setup for the root account is a separate infrastructure concern. The root zone changes are infrequent (new domain families are rare). A script is sufficient for the current operational model.
Copyright: (c) Arda Systems 2025-2026, All rights reserved
Copyright: © Arda Systems 2025-2026, All rights reserved