2a84c0047b
---ci--- project: acdl phase: 20 milestone: v1.5 status: verify ---/ci--- REQ-46: README rewritten — platform-source vs consumer-repo distinction up front; platform flow converted to mermaid flowchart TD; L3A/L3B + spike nomenclature scrubbed from prose (code paths kept verbatim); prereqs pointer to consumer guide added. REQ-47: docs/CONSUMER_GUIDE.md (generic, all L2 modules) replaces docs/consumer-guide-static-asset.md — mermaid diagrams (model LR + pipeline TD), versioned uses: (@v1.4 floating MAJOR+MINOR, bare/@main discouraged), consumer-scoped prerequisites (no Terraform/Checkov/boto3/runner-key), run- time platform fetch via reusable workflow (consumers never invoke scripts/run_platform.sh locally for the happy path), optional local validation note. REQ-48: Credentials section rewritten — zero-trust OIDC + ABAC default (repo-identity + resource-tag scoping, blast-radius containment); static-key override in GitHub Secrets or .env.secrets with platform-managed daily rotation; consumer rotates out of band when using .env.secrets locally. REQ-49: byte-identical .gitea/workflows/deploy.yml + .github/workflows/ deploy.yml — reusable (on: workflow_call), checks out consumer repo + ACDL platform repo, installs deps, runs run_platform.sh, uploads artifacts; OIDC default (permissions: id-token: write) + static-key override via secrets. REQ-50: contracts/static-asset.yaml uses: @v1.4 (MAJOR+MINOR). REQ-51: tests/test_pipeline_contract.py extended — TestDeployPipelineSchema, TestDeployPipelineContract, TestDeployWorkflowConformance (byte-identical, reusable, contract/mode inputs, run_platform invocation, platform-repo checkout, OIDC permissions), TestSampleContractVersioning. 154 tests pass (19 new); run_ci.sh green. Fixes: modules/l2/static-asset/README.md dangling link retargeted to docs/CONSUMER_GUIDE.md.
359 lines
14 KiB
Markdown
359 lines
14 KiB
Markdown
# Consumer Guide — Declare intent, deploy to AWS
|
|
|
|
This guide walks a consumer through creating their pipeline and defining a
|
|
contract that deploys any ACDL module to AWS. It is **generic** across all
|
|
L2 modules in the registry; `static-asset` is the worked example, but every
|
|
step applies to `microservice` and any future L2 composition.
|
|
|
|
## The model
|
|
|
|
Consumers have their own repos and consume ACDL by referencing `uses:` the
|
|
central pipeline definitions. The consumer declares a **contract** (which
|
|
module, which environment, which inputs); the ACDL platform owns the
|
|
pipelines, modules, Terraform adapter, and evidence stream.
|
|
|
|
You do not write Terraform, workflow YAML, or adapter code. You write a
|
|
contract YAML file and the platform does the rest. Your repository contains
|
|
only your application code and that one contract.
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
A["your repo<br/>(app code + contract.yaml)"] -->|uses: acdl/.gitea/workflows/deploy.yml@v1.4| B
|
|
B["ACDL platform runners<br/>(modules/ + pipelines/ + adapters/ + schemas/)"] -->|contract -> resolver -> stack -> adapter<br/>-> terraform plan -> Checkov -> confidence<br/>-> apply -> evidence event to outbox| C
|
|
C["your resources in AWS"]
|
|
```
|
|
|
|
## Versioning the `uses:` reference
|
|
|
|
The central deployment pipeline is **always versioned with floating MAJOR
|
|
and MINOR tags** (e.g. `acdl/pipelines/deploy.yaml@v1.4`). Version
|
|
constraints cannot be expressed inside the contract, so the tag in
|
|
`uses:` is the only immutability lever a consumer has.
|
|
|
|
**Unversioned references are discouraged.** Do not use `@main` or a bare
|
|
`acdl/pipelines/deploy.yaml` — `main` is constantly updated and can cause
|
|
unexpected failures in your deployment. Pinning to a MAJOR+MINOR tag means:
|
|
|
|
- **Immutability** — the pipeline behavior you tested is the behavior you
|
|
get. Patch fixes flow within the tag; breaking changes land under the
|
|
next MINOR tag (`@v1.5`), which you opt into explicitly.
|
|
- **Resilience** — your deployment does not break because an unrelated
|
|
change landed on `main`.
|
|
- **DX** — your setup is stable and reproducible. You upgrade on your
|
|
schedule by bumping the tag.
|
|
|
|
All examples in this guide use `@v1.4`. When a new MINOR tag is released
|
|
(e.g. `@v1.5`), review its changelog and bump your `uses:` reference when
|
|
ready.
|
|
|
|
## Prerequisites
|
|
|
|
These are the **only** prerequisites for a consumer repo. You do **not**
|
|
need an AWS account, Terraform, Checkov, boto3, or a rotated runner key —
|
|
those are platform-repo concerns, provided by the platform runners.
|
|
|
|
- **A consumer GitHub or Gitea repository** for your application code +
|
|
`contract.yaml`.
|
|
- **An ACDL platform runner available to your org.** The platform team
|
|
provides runners with Terraform, Checkov, Python, and the AWS auth
|
|
already configured. You do not install any of these.
|
|
- **Authorization to reference the central pipeline.** Onboarding grants
|
|
your repo the right to `uses: acdl/.gitea/workflows/deploy.yml@v1.4`.
|
|
Contact the platform team if you have not been onboarded.
|
|
|
|
## Step 1 — Create a consumer repo
|
|
|
|
Create a repository for your application. The top level holds your app
|
|
code; your contract lives at `.acdl/contract.yaml`. Example for a static
|
|
site:
|
|
|
|
```
|
|
my-static-site/
|
|
index.html
|
|
assets/
|
|
style.css
|
|
logo.png
|
|
.acdl/
|
|
contract.yaml
|
|
```
|
|
|
|
Example for a microservice:
|
|
|
|
```
|
|
my-microservice/
|
|
app.py
|
|
Dockerfile
|
|
.acdl/
|
|
contract.yaml
|
|
```
|
|
|
|
Your app code lives at the top level. Your contract lives at
|
|
`.acdl/contract.yaml` regardless of the module you deploy.
|
|
|
|
## Step 2 — Reference the central pipeline
|
|
|
|
In your contract YAML, declare `uses:` pointing at the central ACDL
|
|
deployment pipeline with a **versioned tag** (floating MAJOR + MINOR):
|
|
|
|
```yaml
|
|
uses: acdl/pipelines/deploy.yaml@v1.4
|
|
```
|
|
|
|
This tells the platform to run the standard deployment pipeline:
|
|
validate-contract -> resolve-stack -> terraform-plan -> checkov ->
|
|
confidence -> apply.
|
|
|
|
## Step 3 — Define the contract
|
|
|
|
Write `.acdl/contract.yaml`. The `static-asset` example:
|
|
|
|
```yaml
|
|
uses: acdl/pipelines/deploy.yaml@v1.4
|
|
module: static-asset
|
|
environment: dev
|
|
inputs:
|
|
bucket_name: my-static-site-assets
|
|
region: us-east-1
|
|
```
|
|
|
|
A `microservice` example:
|
|
|
|
```yaml
|
|
uses: acdl/pipelines/deploy.yaml@v1.4
|
|
module: microservice
|
|
environment: dev
|
|
inputs:
|
|
image: my-registry/my-microservice:latest
|
|
port: 8080
|
|
env:
|
|
LOG_LEVEL: info
|
|
```
|
|
|
|
### Contract fields
|
|
|
|
| Field | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `uses` | string | yes | Reference to the central deployment pipeline, **versioned** with a floating MAJOR+MINOR tag (e.g. `acdl/pipelines/deploy.yaml@v1.4`). Bare or `@main` references are discouraged. |
|
|
| `module` | string | yes | Module name from the registry — any L1 primitive or L2 composition (e.g. `static-asset`, `microservice`, `s3`). See the [module catalog](../modules/README.md). |
|
|
| `environment` | enum | yes | `dev` (autonomous), `qa` (QA HITL), `prod` (SRE HITL), `dr` (SRE HITL). |
|
|
| `inputs` | object | yes | Module-specific inputs (see below). |
|
|
|
|
### Module inputs
|
|
|
|
Each module declares its inputs in its `interface.json` (L1) or
|
|
`composition.json` (L2). Consult the [module catalog](../modules/README.md)
|
|
for the full list, or read the module's own README under `modules/l1/<name>/`
|
|
or `modules/l2/<name>/`.
|
|
|
|
**`static-asset` inputs** (the worked example):
|
|
|
|
| Input | Type | Required | Description |
|
|
|-------|------|----------|-------------|
|
|
| `bucket_name` | string | yes | Globally-unique S3 bucket name. |
|
|
| `region` | string | yes | AWS region the bucket is created in. |
|
|
|
|
The contract is validated against `schemas/contract.schema.json`. An
|
|
invalid contract (missing field, unknown module, wrong type) fails at the
|
|
validate-contract stage with a clear error.
|
|
|
|
## Step 4 — Run the pipeline
|
|
|
|
You do **not** run platform scripts locally for the happy path. The
|
|
central deploy workflow is a **reusable workflow** that the platform
|
|
runners fetch and execute for you.
|
|
|
|
### The consumer workflow
|
|
|
|
Add a thin workflow file to **your** repo that invokes the reusable ACDL
|
|
deploy workflow with a **versioned tag**. For Gitea Actions
|
|
(`.gitea/workflows/deploy.yml`):
|
|
|
|
```yaml
|
|
name: deploy
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
jobs:
|
|
deploy:
|
|
uses: acdl/.gitea/workflows/deploy.yml@v1.4
|
|
with:
|
|
contract: .acdl/contract.yaml
|
|
```
|
|
|
|
For GitHub Actions (`.github/workflows/deploy.yml`), the `uses:` line is
|
|
identical — only the directory differs:
|
|
|
|
```yaml
|
|
name: deploy
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
jobs:
|
|
deploy:
|
|
uses: acdl/.github/workflows/deploy.yml@v1.4
|
|
with:
|
|
contract: .acdl/contract.yaml
|
|
```
|
|
|
|
That is the entire consumer-side workflow. When you push to `main`:
|
|
|
|
1. The forge resolves `uses: acdl/.gitea/workflows/deploy.yml@v1.4` (or
|
|
the GitHub equivalent) to the reusable workflow **at the pinned tag**.
|
|
2. A **platform-provided runner** checks out **your** repo (the consumer
|
|
repo).
|
|
3. The runner checks out the **ACDL platform repo** into the workspace
|
|
(`acdl-platform/`) — this is how the pipeline fetches the platform code
|
|
at run time. You never clone the platform repo yourself.
|
|
4. The runner installs the runtime dependencies (Python, Terraform,
|
|
Checkov) that the platform requires.
|
|
5. The runner invokes `scripts/run_platform.sh` against your
|
|
`.acdl/contract.yaml`.
|
|
|
|
You see the streamed output (terraform plan, Checkov results, confidence
|
|
signal) in your forge run logs. The `--check-only` and `--plan-only` flags
|
|
are platform-side modes visible in the pipeline logs; you do not pass them
|
|
yourself — the reusable workflow selects the mode based on the
|
|
`environment` in your contract (`dev` = full apply; higher environments
|
|
hold for HITL).
|
|
|
|
### Local validation (optional)
|
|
|
|
A consumer *may* clone the ACDL platform repo to run `--check-only`
|
|
against their contract before pushing — this is optional and not required
|
|
for the happy path. If you do this, the runtime dependencies (Python,
|
|
`jsonschema`, `pyyaml`, `boto3`) must be installed locally, and any AWS
|
|
credentials follow the [Credentials](../README.md#credentials--zero-trust)
|
|
override model: a static key in `.env.secrets` (gitignored) is rotated
|
|
**out of band by you** — the platform guarantees daily rotation for forge
|
|
runs, not for locally-held copies.
|
|
|
|
```bash
|
|
# Optional pre-push validation (clone the platform repo first):
|
|
bash scripts/run_platform.sh --check-only path/to/your/.acdl/contract.yaml
|
|
# Expected: "=== PLATFORM CHECK OK ==="
|
|
```
|
|
|
|
## Step 5 — What the pipeline does
|
|
|
|
Each stage of the central deployment pipeline (`pipelines/deploy.yaml`):
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
S1["validate-contract<br/>schema check vs contract.schema.json"] --> S2
|
|
S2["resolve-stack<br/>contract_resolver.py -> Target Stack JSON"] --> S3
|
|
S3["terraform-plan<br/>adapter.py compiles stack -> terraform plan (real AWS)"] --> S4
|
|
S4["checkov<br/>policy checks -> PolicyCheckResult records"] --> S5
|
|
S5["confidence<br/>confidence_signal.py -> score + band (dev >= 0.50)"] --> S6
|
|
S6["apply<br/>dev only: terraform apply + evidence event to outbox"]
|
|
```
|
|
|
|
1. **validate-contract** — validates your contract YAML against
|
|
`schemas/contract.schema.json`. Fails fast on missing fields, unknown
|
|
modules, or wrong types.
|
|
|
|
2. **resolve-stack** — the contract resolver
|
|
(`acdl_platform/contract_resolver.py`) resolves your contract to a
|
|
Target Stack instance. It loads the module's composition, expands its
|
|
children, wires your contract inputs to the children's inputs, and
|
|
emits a stack JSON instance.
|
|
|
|
3. **terraform-plan** — the Terraform adapter
|
|
(`adapters/terraform/adapter.py`) compiles the stack to Terraform
|
|
(`main.tf`, `terraform.tf`, `providers.tf`) and runs `terraform plan`
|
|
against real AWS. You see the plan in your run logs.
|
|
|
|
4. **checkov** — Checkov runs policy checks on the emitted Terraform. The
|
|
results are normalized to `PolicyCheckResult` records by the Checkov
|
|
adapter. Each result has a severity, rule ID, and pass/fail status.
|
|
|
|
5. **confidence** — the confidence signal
|
|
(`acdl_platform/confidence_signal.py`) computes a score from 6 inputs
|
|
(policy, validation, freshness, source, history, NFRs). For `dev`, the
|
|
threshold is >= 0.50. If the band is `pass`, the pipeline proceeds.
|
|
|
|
6. **apply** — (dev only, autonomous per the environment model) Terraform
|
|
applies the plan, creating the resources in your AWS account. An
|
|
evidence event (hash-chained) is written to the DynamoDB outbox.
|
|
|
|
## Step 6 — What gets created
|
|
|
|
After a successful `dev` run, the resources declared by your module's
|
|
composition exist in your AWS account, and an evidence event is recorded.
|
|
|
|
For the `static-asset` example:
|
|
|
|
- **An S3 bucket** named `my-static-site-assets` in `us-east-1` with
|
|
versioning enabled.
|
|
- **An evidence event** in the DynamoDB outbox (`acdl-outbox` table) with
|
|
the contract ID, stack name (`static-asset`), confidence score, and band.
|
|
- **A confidence band** of `pass` (score >= 0.50 for dev).
|
|
|
|
For other modules, consult the module's README
|
|
(`modules/l1/<name>/README.md` or `modules/l2/<name>/README.md`) for the
|
|
exact resources created.
|
|
|
|
## Step 7 — Upload your content (static-asset example)
|
|
|
|
The platform provisions the infrastructure; you upload your content. For
|
|
the `static-asset` module:
|
|
|
|
```bash
|
|
aws s3 sync ./assets s3://my-static-site-assets/ --acl public-read
|
|
```
|
|
|
|
(For a proper static site, configure the bucket for website hosting or
|
|
put a CloudFront distribution in front — both are future compliance
|
|
extension points for the `static-asset` module.)
|
|
|
|
For a `microservice`, the platform provisions the ECS service and ALB; you
|
|
push your container image to the ECR repo the platform created.
|
|
|
|
## Step 8 — Promote to qa / prod
|
|
|
|
Change `environment` in your contract (keeping the same versioned `uses:`):
|
|
|
|
```yaml
|
|
uses: acdl/pipelines/deploy.yaml@v1.4
|
|
environment: qa # QA HITL gate + confidence >= 0.75
|
|
environment: prod # SRE HITL gate + confidence >= 0.90
|
|
```
|
|
|
|
Higher environments require human attestation (forge deployment approval)
|
|
and higher confidence thresholds. The platform enforces separation of
|
|
duties (qaApprover != prodApprover) via the DynamoDB outbox.
|
|
|
|
| Environment | Autonomy | Gate |
|
|
|-------------|----------|------|
|
|
| dev | Full autonomy | Confidence >= 0.50 |
|
|
| qa | QA HITL | Confidence >= 0.75 |
|
|
| prod | SRE HITL | Confidence >= 0.90 |
|
|
| dr | SRE HITL | Confidence >= 0.95 + dr-drill |
|
|
|
|
## Step 9 — Compliance extensions
|
|
|
|
Each module lists compliance extension points for the future compliance
|
|
milestone (GDPR, SOX, SOC2, HIPAA, DORA). See each module's README under
|
|
`modules/l1/<name>/README.md` or `modules/l2/<name>/README.md` for the
|
|
per-module extension points. Common examples:
|
|
|
|
- **KMS key** — shared encryption key for SSE.
|
|
- **S3 access logs** — access logging to a separate audit bucket.
|
|
- **Object Lock** — 7-year immutable retention for evidence.
|
|
- **Public access block** — prevent data exfiltration.
|
|
|
|
## Reference
|
|
|
|
| Resource | Path | Description |
|
|
|----------|------|-------------|
|
|
| Central deployment pipeline contract | `pipelines/deploy.yaml` | The pipeline stages your contract references. |
|
|
| Reusable deploy workflow (Gitea) | `.gitea/workflows/deploy.yml` | The workflow your repo invokes via `uses:`. |
|
|
| Reusable deploy workflow (GitHub) | `.github/workflows/deploy.yml` | The workflow your repo invokes via `uses:`. |
|
|
| Contract schema | `schemas/contract.schema.json` | JSON Schema for consumer contracts. |
|
|
| Stack schema | `schemas/stack.schema.json` | JSON Schema for the resolved stack instance. |
|
|
| Module catalog | `modules/README.md` | All L1 primitives and L2 compositions. |
|
|
| Sample contract | `contracts/static-asset.yaml` | The reference example contract (uses `@v1.4`). |
|
|
| Contract resolver | `acdl_platform/contract_resolver.py` | Resolves contracts to stack instances. |
|
|
| Terraform adapter | `adapters/terraform/adapter.py` | Compiles stack instances to Terraform. |
|
|
| Platform pipeline runner | `scripts/run_platform.sh` | The pipeline runner (platform-side; consumers do not invoke it directly). |
|
|
| Platform README | `README.md` | How the platform works + how to run the platform repo locally. |
|
|
| Credentials & zero-trust | `README.md#credentials--zero-trust` | The OIDC/ABAC default + static-key override model. | |