2443909362
---ci--- project: nova-platform milestone: v1.0 status: complete requirements: covered: [REQ-01,REQ-02,REQ-03,REQ-04,REQ-05,REQ-06,REQ-07,REQ-08,REQ-09,REQ-10,REQ-11,REQ-12,REQ-13,REQ-14,REQ-15,REQ-16,REQ-17,REQ-18,REQ-19,REQ-20,REQ-21,REQ-22,REQ-23,REQ-24,REQ-25,REQ-26,REQ-27,REQ-28,REQ-29,REQ-30,REQ-31,REQ-32,REQ-33,REQ-34,REQ-35,REQ-36,REQ-37,REQ-38] partial: [] ---/ci--- v1.0 milestone complete: simplified infrastructure-delivery platform derived from Nova (acdl). 6 phases (P0-P5 + P6 final). 38 REQ-IDs. 38 decisions (D-001..D-038). 76 tests pass. Engine boundary holds. Happy paths green (check-only + CI). 13 L1 + 2 L2 modules. 5 terraform roots. Shell reproducibility. Zero OOS files. Tags: v0.1.0 (P0) → v0.1.1..v0.1.5 (P1..P5) → v0.1.6 (P6 = milestone release on v0.1 patch line).
6.6 KiB
6.6 KiB
Nova Modules
Nova ships a two-tier module library. Modules are engine-agnostic:
their contract is declared in interface.json (stack types like
aws:s3:bucket), and an adapter translates the contract to a concrete
IaC engine (Terraform today; Pulumi/CDK possible later). All L1 modules
in this repo ship a Terraform adapter under terraform/.
L1 vs L2
| Tier | What it is | Composes | Examples |
|---|---|---|---|
| L1 | A single primitive resource (or tightly-coupled resource group) on a cloud | One stack resource | s3, vpc, ecs-cluster, alb |
| L2 | A composition of L1s expressing an architectural pattern | Multiple L1 modules | microservice, static-assets |
- L1 = one entry in the flat stack. Even multi-resource L1s (e.g.
vpc,ecs-service,alb) emit a single stack entry; theirinterface.jsonlists the child resources in aresources[]array for documentation, but the resolver does not expand them (D-012). - L2 = also one opaque entry in the flat stack. The L2's
terraform/main.tfcomposes L1 modules internally viamoduleblocks (D-012). The L2 exposes its own L2-levelinputs/outputs; children and wiring live in terraform, not in the interface.
Registry format
modules/registry.json maps module_name -> version -> entry:
{
"s3": {
"1.0.0": {
"interface": "modules/l1/s3/interface.json",
"terraform_dir": "modules/l1/s3/terraform",
"published_at": "2026-08-20T00:00:00Z",
"deprecated": false,
"kind": "l1"
}
}
}
interface— path to theinterface.jsondeclaring the contract.terraform_dir— path to the adapter's Terraform module directory (the flat stack'ssourcefield).kind—"l1"or"l2".deprecated— whentrue, the resolver warns and selects the latest non-deprecated version unless the caller pins a version.
interface.json shape (D-014)
{
"name": "s3",
"version": "1.0.0",
"kind": "l1",
"type": "aws:s3:bucket",
"description": "...",
"inputs": { "<name>": { "type": "...", "required": true, "description": "..." } },
"outputs": { "<name>": { "type": "...", "description": "..." } },
"resources": [ { "type": "aws:ec2:vpc", "inputs": [...], "outputs": [...] } ]
}
typeis stack-typed —aws:<service>:<resource>(e.g.aws:s3:bucket), not the Terraform resource name (aws_s3_bucket). The adapter performs the translation.resources[]is present only on multi-resource L1s (vpc,ecs-service,alb); it documents the child stack types but does not drive resolution.- Dropped per D-014:
nfrs(confidence signal, out of scope) andintra_refs(wire engine, eliminated by D-012). Do not re-add them.
Conventions shared by all L1 Terraform adapters
terraform/versions.tfpinsrequired_version = ">= 1.9, < 1.10"andaws ~> 5.0.- Every resource is guarded by
count = var.enabled ? 1 : 0; theenabledinput defaults totrue. locals.tfmerges module-default tags with caller-suppliedvar.tags:tags = merge({ "nova:owner" = "nova", "nova:environment" = "dev" }, var.tags)- Every
interface.jsoninput has a matchingvariableblock; every output has a matchingoutputblock. Outputs returnnull(or[]) whenenabled = false.
How to add a module
- Pick the tier. New primitive → L1. New pattern composing existing L1s → L2.
- Create
modules/l1/<name>/(ormodules/l2/<name>/). - Author
interface.json(L1) orinterface.json+ L2 terraform that composes L1s viamoduleblocks. Usemodules/README-TEMPLATE.mdas the per-module doc template. - Author
terraform/{main,variables,outputs,versions,locals}.tffollowing the conventions above. - Add an entry to
modules/registry.jsonand a row to the catalog atdocs/modules/index.md. - Verify:
python3 -c "import json; json.load(open('modules/l1/<name>/interface.json'))"andterraform validateinsideterraform/.
L1 primitives (13)
| Module | Stack type | Multi-resource? | Description |
|---|---|---|---|
s3 |
aws:s3:bucket |
no | S3 bucket with versioning + SSE-KMS |
vpc |
aws:ec2:vpc |
yes | VPC + subnets + route table + IGW |
ecs-cluster |
aws:ecs:cluster |
no | ECS cluster |
ecs-service |
aws:ecs:service |
yes | ECS task definition + service |
iam-role |
aws:iam:role |
no | IAM role with assume-role policy |
alb |
aws:alb |
yes | ALB + target group + listener |
ecr |
aws:ecr:repository |
no | ECR repository with scan-on-push |
cloudfront |
aws:cloudfront:distribution |
no | CloudFront distribution with a single origin |
waf |
aws:waf:web_acl |
no | WAFv2 web ACL (regional, default allow) |
rds |
aws:rds:instance |
no | RDS Postgres DB instance |
kms-key |
aws:kms:key |
no | KMS CMK with alias |
dynamodb |
aws:dynamodb:table |
no | DynamoDB table (PAY_PER_REQUEST default) |
uptime |
aws:uptime:monitor |
no | Uptime monitor (CloudWatch alarm stand-in) |
L2 compositions (2)
| Module | Composes | Description |
|---|---|---|
microservice |
vpc + ecs-cluster + ecs-service + iam-role + ecr + alb | Container microservice with public ALB (D-038) |
static-assets |
s3 + cloudfront + kms-key | Static site fronted by CloudFront (D-038) |