feat(P22): rename static-asset→static-assets + cloudfront/waf primitives + production stack + @v1.6 bump
---ci---
phase: 22
title: rename-and-production-static-assets-stack
status: complete
verification:
- scripts/run_ci.sh: PASS (CI PIPELINE OK)
- python3 -m pytest tests/ -v: 175 passed
- scripts/run_platform.sh --check-only: PASS (PLATFORM CHECK OK)
- grep -R "static-asset[^s]" . (excl .git/): 0 hits
- grep -R "static-asset$" . (excl .git/): 0 hits
- floating git tags v1.6 + v1 point at v1.6.0 (a90a756)
changed_files:
- Task 1 (rename): contracts/static-asset.yaml→static-assets.yaml (git mv); modules/l2/static-asset→static-assets (git mv); sed replaceAll static-asset→static-assets in 22 files (README, docs, scripts/run_platform.sh, pipelines/deploy.yaml, modules/registry.json, tests/*, .ciagent/* historical narrative)
- Task 2 (cloudfront primitive): modules/l1/cloudfront/interface.json + README.md
- Task 3 (waf primitive): modules/l1/waf/interface.json + README.md
- Task 4 (registry): modules/registry.json (+cloudfront, +waf, static-assets renamed)
- Task 5 (augment static-assets): modules/l2/static-assets/composition.json (s3+cloudfront+waf, depth 1); modules/l1/s3/interface.json +instance.json (+bucket_regional_domain_name output); modules/l2/static-assets/README.md (production stack docs)
- Task 6 (adapter): adapters/terraform/adapter.py (+TYPE_MAP/INPUT_MAP/OUTPUT_MAP for cloudfront distribution+OAC+wafv2 webacl; special handling in _emit_resource for OAC defaults, distribution origin/cache_behavior/restrictions/viewer_certificate/web_acl_id, waf scope/default_action/visibility_config/managed rules)
- Task 7 (contract schema): no change needed (generic inputs object; new module names match ^[a-z][a-z0-9-]*$)
- Task 8 (@v1.6 bump): contracts/static-assets.yaml, .gitea/.github/workflows/deploy.yml (ref: v1.6 + header comments), docs/consumer-guide.md, docs/contracts/index.md, docs/pipeline/versioning.md, docs/pipeline/index.md, docs/architecture.md, README.md, modules/l2/microservice/README.md, tests/test_environment_check.py, tests/test_pipeline_contract.py
- Task 9 (floating tags): git tag -f v1.6 v1.6.0; git tag -f v1 v1.6.0
- Task 10 (tests): tests/test_adapter.py (registry 11 entries/9 L1/2 L2; cloudfront+waf type map tests; TestS3Output bucket_regional_domain_name; TestStaticAssetsStack 4 tests); tests/test_contract_resolver.py (+s3/cloudfront/waf resource assertions)
generated:
- terraform/spike/main.tf + terraform.tf (regenerated by run_platform.sh --check-only; reflect static-assets production stack + backend key spike/static-assets/)
notes:
- D-048 full rewrite of .ciagent/ historical narrative (verbatim phase descriptions, REQ-25/27/50, D-036) — produces intentional tautologies (e.g. "Rename static-assets → static-assets") per the decision to override the v1.6 preservation precedent.
- cloudfront interface.json resources array ordered distribution-first so the resolver (first-match wire resolution) routes bucket_regional_domain_name/waf_web_acl_arn/region to the distribution; the OAC gets adapter-provided defaults (name=acdl-oac, origin_type=s3, signing_behavior=always).
- .ciagent/ @v1.4 references left as historical record (D-048 scope was static-asset rename only; @v1.4 is historical narrative of Phase 20).
- s3 OUTPUT_MAP bucket_regional_domain_name not added (identity fallback in adapt() already handles it; OUTPUT_MAP documents non-identity mappings only).
---ci---
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
# cloudfront — CloudFront distribution
|
||||
|
||||
> **Module kind:** primitive | **Version:** 1.0.0
|
||||
|
||||
A CloudFront distribution with an S3 origin via Origin Access Control
|
||||
(OAC). The distribution serves the bucket's static content from the
|
||||
global edge network with HTTPS redirection by default. An optional WAF
|
||||
web ACL can be associated to filter traffic before it reaches the
|
||||
origin.
|
||||
|
||||
## Resources
|
||||
|
||||
| Resource | Type | Purpose |
|
||||
|----------|------|---------|
|
||||
| `oac` | `aws_cloudfront_origin_access_control` | Origin Access Control signing the S3 origin |
|
||||
| `distribution` | `aws_cloudfront_distribution` | The CloudFront distribution with an S3 origin via OAC |
|
||||
|
||||
## Inputs
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
|------|------|----------|---------|-------------|
|
||||
| `bucket_regional_domain_name` | string | yes | — | The S3 bucket regional domain name (ref to s3 origin) |
|
||||
| `price_class` | string | no | `PriceClass_100` | CloudFront price class |
|
||||
| `viewer_protocol_policy` | string | no | `redirect-to-https` | Viewer protocol policy |
|
||||
| `default_ttl` | number | no | 3600 | Default TTL in seconds |
|
||||
| `max_ttl` | number | no | 86400 | Max TTL in seconds |
|
||||
| `waf_web_acl_arn` | string | no | — | WAF web ACL ARN to associate (ref to waf) |
|
||||
| `region` | string | yes | — | AWS region (CloudFront is global but the provider region is used for the OAC) |
|
||||
|
||||
## Outputs
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `distribution_arn` | arn | The CloudFront distribution ARN |
|
||||
| `distribution_domain_name` | string | The CloudFront distribution domain name (e.g. d111111abcdef8.cloudfront.net) |
|
||||
| `oac_id` | string | The Origin Access Control ID |
|
||||
|
||||
## Usage
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "cloudfront",
|
||||
"type": "aws:cloudfront:distribution",
|
||||
"module": "cloudfront@1.0.0",
|
||||
"inputs": {
|
||||
"bucket_regional_domain_name": "ref:s3.bucket_regional_domain_name",
|
||||
"price_class": "PriceClass_100",
|
||||
"viewer_protocol_policy": "redirect-to-https",
|
||||
"default_ttl": 3600,
|
||||
"max_ttl": 86400,
|
||||
"waf_web_acl_arn": "ref:waf.web_acl_arn",
|
||||
"region": "us-east-1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `bucket_regional_domain_name` and `waf_web_acl_arn` inputs are
|
||||
typically wired as `ref:` expressions from the `s3` and `waf` primitives
|
||||
inside a module composition (see `modules/l2/static-assets`).
|
||||
|
||||
## Compliance extension points
|
||||
|
||||
- **TLS/HTTPS** — viewer protocol policy defaults to `redirect-to-https`;
|
||||
a custom ACM certificate + `viewer_certificate` block can pin TLS to a
|
||||
customer domain (SOC2 CC6.1, GDPR Art.32).
|
||||
- **Geo restriction** — the `restrictions.geo_restriction` block can
|
||||
whitelist/blacklist countries for data-residency compliance (GDPR
|
||||
Art.44, SOC2 CC6.1).
|
||||
- **Logging** — CloudFront access logs to an S3 bucket for auditability
|
||||
(SOC2 CC7.2, DORA audit trail).
|
||||
- **Field-level encryption** — add field-level encryption for PII fields
|
||||
in POST bodies (HIPAA §164.312(a)(2)(iv), GDPR Art.32).
|
||||
|
||||
## Versioning
|
||||
|
||||
`1.0.0` — interface MAJOR, behavior MINOR, lifecycle PATCH. MAJOR bumps
|
||||
require a new registry entry (immutable publication); old entries enter
|
||||
a 12-month deprecation window.
|
||||
@@ -0,0 +1,80 @@
|
||||
{
|
||||
"name": "cloudfront",
|
||||
"version": "1.0.0",
|
||||
"kind": "l1",
|
||||
"type": "aws:cloudfront:distribution",
|
||||
"description": "CloudFront distribution primitive (substrate-agnostic stack types aws:cloudfront:distribution + aws:cloudfront:originaccesscontrol; the Terraform adapter translates to aws_cloudfront_distribution + aws_cloudfront_origin_access_control).",
|
||||
"inputs": {
|
||||
"bucket_regional_domain_name": {
|
||||
"type": "string",
|
||||
"description": "The S3 bucket regional domain name (ref to s3 origin).",
|
||||
"required": true
|
||||
},
|
||||
"price_class": {
|
||||
"type": "string",
|
||||
"description": "CloudFront price class (default PriceClass_100).",
|
||||
"required": false,
|
||||
"default": "PriceClass_100"
|
||||
},
|
||||
"viewer_protocol_policy": {
|
||||
"type": "string",
|
||||
"description": "Viewer protocol policy (default redirect-to-https).",
|
||||
"required": false,
|
||||
"default": "redirect-to-https"
|
||||
},
|
||||
"default_ttl": {
|
||||
"type": "number",
|
||||
"description": "Default TTL in seconds (default 3600).",
|
||||
"required": false,
|
||||
"default": 3600
|
||||
},
|
||||
"max_ttl": {
|
||||
"type": "number",
|
||||
"description": "Max TTL in seconds (default 86400).",
|
||||
"required": false,
|
||||
"default": 86400
|
||||
},
|
||||
"waf_web_acl_arn": {
|
||||
"type": "string",
|
||||
"description": "WAF web ACL ARN to associate (optional, ref to waf).",
|
||||
"required": false
|
||||
},
|
||||
"region": {
|
||||
"type": "string",
|
||||
"description": "AWS region (CloudFront is global but the provider region is used for the OAC).",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"distribution_arn": {
|
||||
"type": "arn",
|
||||
"description": "The CloudFront distribution ARN."
|
||||
},
|
||||
"distribution_domain_name": {
|
||||
"type": "string",
|
||||
"description": "The CloudFront distribution domain name (e.g. d111111abcdef8.cloudfront.net)."
|
||||
},
|
||||
"oac_id": {
|
||||
"type": "string",
|
||||
"description": "The Origin Access Control ID."
|
||||
}
|
||||
},
|
||||
"nfrs": {},
|
||||
"resources": [
|
||||
{
|
||||
"type": "aws:cloudfront:distribution",
|
||||
"description": "CloudFront distribution with S3 origin via OAC.",
|
||||
"inputs": ["bucket_regional_domain_name", "price_class", "viewer_protocol_policy", "default_ttl", "max_ttl", "waf_web_acl_arn", "oac_id"],
|
||||
"outputs": ["distribution_arn", "distribution_domain_name"]
|
||||
},
|
||||
{
|
||||
"type": "aws:cloudfront:originaccesscontrol",
|
||||
"description": "Origin Access Control for the S3 origin.",
|
||||
"inputs": ["name", "origin_type", "signing_behavior"],
|
||||
"outputs": ["oac_id"]
|
||||
}
|
||||
],
|
||||
"intra_refs": [
|
||||
{"from": "aws:cloudfront:distribution.oac_id", "to": "aws:cloudfront:originaccesscontrol.oac_id"}
|
||||
]
|
||||
}
|
||||
@@ -16,7 +16,8 @@
|
||||
},
|
||||
"outputs": {
|
||||
"bucket_arn": {"type": "arn", "description": "The S3 bucket ARN."},
|
||||
"bucket_name": {"type": "string", "description": "The bucket name."}
|
||||
"bucket_name": {"type": "string", "description": "The bucket name."},
|
||||
"bucket_regional_domain_name": {"type": "string", "description": "The bucket regional domain name."}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
"bucket_name": {
|
||||
"type": "string",
|
||||
"description": "The bucket name (echoes the input)."
|
||||
},
|
||||
"bucket_regional_domain_name": {
|
||||
"type": "string",
|
||||
"description": "The bucket regional domain name (e.g. acdl-spike-bucket.s3.us-east-1.amazonaws.com)."
|
||||
}
|
||||
},
|
||||
"nfrs": {
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# waf — WAFv2 Web ACL
|
||||
|
||||
> **Module kind:** primitive | **Version:** 1.0.0
|
||||
|
||||
A WAFv2 Web ACL scoped to CloudFront. It applies managed rule groups by
|
||||
default and can be associated with a CloudFront distribution to filter
|
||||
traffic before it reaches the origin. CloudFront-scoped Web ACLs are
|
||||
always created in `us-east-1`.
|
||||
|
||||
## Resources
|
||||
|
||||
| Resource | Type | Purpose |
|
||||
|----------|------|---------|
|
||||
| `webacl` | `aws_wafv2_web_acl` | WAFv2 Web ACL with managed rules |
|
||||
|
||||
## Inputs
|
||||
|
||||
| Name | Type | Required | Default | Description |
|
||||
|------|------|----------|---------|-------------|
|
||||
| `name` | string | yes | — | Name of the Web ACL |
|
||||
| `scope` | string | no | `cloudfront` | Scope of the Web ACL (default cloudfront for CloudFront associations) |
|
||||
| `default_action` | string | no | `allow` | Default action (default allow) |
|
||||
| `rules` | string | no | — | Optional custom rules as JSON (default: managed rules only) |
|
||||
| `region` | string | yes | — | AWS region (CloudFront-scoped WAF is always us-east-1; the adapter ignores this for cloudfront scope) |
|
||||
|
||||
## Outputs
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `web_acl_arn` | arn | The WAF Web ACL ARN |
|
||||
|
||||
## Usage
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "waf",
|
||||
"type": "aws:wafv2:webacl",
|
||||
"module": "waf@1.0.0",
|
||||
"inputs": {
|
||||
"name": "acdl-static-assets-waf",
|
||||
"scope": "cloudfront",
|
||||
"default_action": "allow",
|
||||
"region": "us-east-1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `web_acl_arn` output is typically wired as a `ref:` expression into
|
||||
the `cloudfront` primitive's `waf_web_acl_arn` input inside a module
|
||||
composition (see `modules/l2/static-assets`).
|
||||
|
||||
## Compliance extension points
|
||||
|
||||
- **Rate limiting** — add a rate-based rule to cap requests per IP
|
||||
(SOC2 CC6.1, DORA operational resilience).
|
||||
- **Geo blocking** — add a geo-match statement to block/allow countries
|
||||
for data-residency compliance (GDPR Art.44, SOC2 CC6.1).
|
||||
- **Custom rules** — add custom rule statements for application-specific
|
||||
filtering (e.g. block SQLi/IP allow-lists) (SOC2 CC6.1).
|
||||
- **Logging** — enable WAF access logging to S3/CloudWatch/Kinesis for
|
||||
auditability (SOC2 CC7.2, DORA audit trail).
|
||||
|
||||
## Versioning
|
||||
|
||||
`1.0.0` — interface MAJOR, behavior MINOR, lifecycle PATCH. MAJOR bumps
|
||||
require a new registry entry (immutable publication); old entries enter
|
||||
a 12-month deprecation window.
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "waf",
|
||||
"version": "1.0.0",
|
||||
"kind": "l1",
|
||||
"type": "aws:wafv2:webacl",
|
||||
"description": "WAFv2 Web ACL primitive for CloudFront (substrate-agnostic stack type aws:wafv2:webacl; the Terraform adapter translates to aws_wafv2_web_acl). CloudFront-scoped WAF is always in us-east-1.",
|
||||
"inputs": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name of the Web ACL.",
|
||||
"required": true
|
||||
},
|
||||
"scope": {
|
||||
"type": "string",
|
||||
"description": "Scope of the Web ACL (default cloudfront for CloudFront associations).",
|
||||
"required": false,
|
||||
"default": "cloudfront"
|
||||
},
|
||||
"default_action": {
|
||||
"type": "string",
|
||||
"description": "Default action (default allow).",
|
||||
"required": false,
|
||||
"default": "allow"
|
||||
},
|
||||
"rules": {
|
||||
"type": "string",
|
||||
"description": "Optional custom rules as JSON (default: managed rules only).",
|
||||
"required": false
|
||||
},
|
||||
"region": {
|
||||
"type": "string",
|
||||
"description": "AWS region (CloudFront-scoped WAF is always us-east-1; the adapter ignores this for cloudfront scope).",
|
||||
"required": true
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"web_acl_arn": {
|
||||
"type": "arn",
|
||||
"description": "The WAF Web ACL ARN."
|
||||
}
|
||||
},
|
||||
"nfrs": {},
|
||||
"resources": [
|
||||
{
|
||||
"type": "aws:wafv2:webacl",
|
||||
"description": "WAFv2 Web ACL with managed rules.",
|
||||
"inputs": ["name", "scope", "default_action", "rules"],
|
||||
"outputs": ["web_acl_arn"]
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user