Files
acdl/docs/CONSUMER_GUIDE.md
T
Jon Chery 2a84c0047b feat(P20): consumer happy path + reusable deploy workflow (v1.5.0)
---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.
2026-07-22 17:14:12 +00:00

14 KiB

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.

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.yamlmain 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):

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:

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:

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.
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 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):

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:

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 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.

# 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):

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:

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:):

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.