# Nova > **Nova — The New Dawn of DevSecOps.** Security as a seamless enabler of fast deployments — not a bottleneck, not a "no" department. Consumers declare intent; the platform delivers safe production deployment through an agentic stack — automatically, safely, and with a complete audit trail. A merged change progresses through lower environments end-to-end without a platform engineer joining a thread; a non-technical consumer ships a production deployment by declaring intent, without authoring a workflow, a configuration file, or an infrastructure module. - **Consumer guide:** [`docs/consumer-guide.md`](docs/consumer-guide.md) - **Modules:** [`docs/modules/`](docs/modules/) - **Contracts:** [`docs/contracts/`](docs/contracts/) - **Pipeline:** [`docs/pipeline/`](docs/pipeline/) - **Versioning:** [`docs/pipeline/versioning.md`](docs/pipeline/versioning.md) - **Environments:** [`docs/environments/`](docs/environments/) - **Architecture:** [`docs/architecture.md`](docs/architecture.md) - **Vision:** [`docs/vision.md`](docs/vision.md) ## Repository roles There are two kinds of repository in the Nova model: - **Platform repo (this one).** This is the **source code of the platform**. It owns `modules/`, `adapters/`, `core/`, `schemas/`, `pipelines/`, `scripts/`, and the reusable workflow files. Platform engineers work here. A **consumer never clones it.** - **Consumer repo (yours).** A consumer repo contains only: 1. **Its application code** — the service or site being deployed. 2. **One or more contracts** — small YAML files at `.nova/contract.yml` that declare infrastructure (one or more modules by name + version), select an environment, and supply module-specific inputs. 3. **One or more CI definitions** — thin `.github/workflows/*.yml` files that `uses:` the central reusable deploy workflow, pointing at the appropriate environment + contract. The consumer does not write infrastructure modules, workflow YAML beyond the thin `uses:` wrapper, or adapter code — they write a contract YAML file and the platform does the rest. The rest of this README describes the **platform repo** (how the platform works, how to run it locally, how it's laid out). If you are a consumer, jump to the [Consumer guide](docs/consumer-guide.md). ## Features A referenceable list of what the platform provides today, for consumers and platform engineers alike: - **Contract-driven deploys** — a consumer writes a YAML contract; the platform resolves it to a stack, compiles it, and deploys it. - **Reusable versioned deploy workflow** — consumer repos `uses:` a versioned central workflow; no platform code is cloned by the consumer. - **Module catalog** — primitives (single resources) and modules (patterns of primitives) with self-documented inputs/outputs. See [docs/modules/](docs/modules/). - **Zero-trust credentials** — OIDC federation + attribute-based authorization (ABAC) by default; no long-lived keys in consumer repos. - **Security + policy checks** — a security-check stage and a policy-check stage run before any infrastructure is created. - **Confidence signal** — a computed, explainable score gates promotion. - **Evidence outbox** — every deployment writes a hash-chained evidence event to an audit outbox. - **Shell reproducibility** — `scripts/run_ci.sh` mirrors the CI pipeline locally; `scripts/run_platform.sh --check-only` runs offline. - **Platform-managed environments** — consumers provide no AWS account, VPC, subnet, or state bucket; the platform manages environments. See [docs/environments/](docs/environments/). - **Central pipeline contract** — a declarative YAML instance is the single source of truth for both the CI and deploy workflows. ## Roadmap Planned future features (no dates; tracked in the internal roadmap): - **Dynamic module creation from a contract** — an agentic flow where a consumer creates a module directly from the contract file (the "composition" mechanism, redesigned). - **Compliance milestone** — per-module compliance extension points (GDPR, SOX, SOC2, DORA) wired into the pipeline. - **Additional engine adapters** — beyond the Terraform adapter. - **Environment self-service** — a consumer-facing flow to request and provision a new platform-managed environment (today it is a platform-team action). - **HITL gates for qa / prod / dr** — human attestation + higher confidence thresholds for higher environments. - **OIDC for all platform runners** — zero-trust credentials everywhere. ## How the platform works The platform is **four layers + six cross-cutting concerns**, bound by the vision's "Two Consumer Surfaces, One Platform" tenet: consumers declare intent via a contract; the platform delivers the deployment through the same contract schema, the same policy envelope, and the same evidence stream. Consumers have their own repos and consume Nova by writing a contract that declares infrastructure. A consumer declares a contract (id + name + environment + infrastructure); the platform resolves it to a stack instance, compiles it, runs security + policy checks, computes a confidence signal, writes an evidence event to the audit outbox, and applies the infrastructure. ### The platform flow (end-to-end) ```mermaid flowchart TD A["consumer contract
(id + name + environment + infrastructure)"] --> B B["schema validation
(contract schema)"] --> C C["resolve to Target Stack
(contract resolver)"] --> D D["security checks
(adapter)"] --> E E["infrastructure plan
(adapter compiles the stack)"] --> F F["policy checks
(adapter -> PolicyCheckResult records)"] --> G G["confidence signal
(6 inputs: policy, validation,
freshness, source, history, NFRs)"] --> H H["evidence event
(hash-chained, to the audit outbox)"] --> I I["infrastructure apply
(dev only, autonomous)"] ``` The platform validates the architecture's claim that the **stack commitments do not require a polyglot mess**: the adapter is the only engine-specific code. `modules/`, `schemas/`, `contracts/`, `core/confidence_signal.py`, `core/contract_resolver.py`, and `core/outbox_writer.py` are all engine-agnostic (no `aws_s3_bucket` / `aws_` infrastructure terms). ## How to run ### Prerequisites > These prerequisites are for running the **platform repo** locally. A > consumer does not need any of these — see the > [Consumer guide](docs/consumer-guide.md) for the consumer happy path. - A platform-managed environment (see [docs/environments/](docs/environments/)). For local testing, `core/environments/dev.json` is provided as the sample. - AWS credentials for the dev environment (in `.env.secrets`, gitignored; see [Credentials & zero-trust](#credentials--zero-trust)). - `terraform` (pin `1.9.*`), `checkov` (pin `>=3.2,<4`), `python3` + `boto3` + `jsonschema`. ### Run the platform pipeline end-to-end ```bash # 1. Bootstrap the AWS state backend + runner IAM user (one-time, idempotent) # (requires the bootstrap root key in env — skip if the state bucket + # nova-spike-runner already exist) ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \ python3 terraform/bootstrap/create_state_backend.py ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \ python3 terraform/bootstrap/create_iam_user.py # prints the initial key # 2. Rotate the runner key (writes .env.secrets, gitignored) ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=... ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=... \ bash scripts/rotate_spike_key.sh # 3. Run the full platform pipeline (contract -> environment check -> stack -> # adapter -> security checks -> infrastructure plan -> policy checks -> # confidence -> evidence event -> apply). Output is streamed to stdout. bash scripts/run_platform.sh contracts/static-assets.yml # Expected: "=== PLATFORM E2E OK ===" # Or plan-only (contract -> stack -> adapter -> infrastructure plan; no # policy checks / outbox): bash scripts/run_platform.sh --plan-only contracts/static-assets.yaml # Add --quiet to suppress streaming (output to log files only): bash scripts/run_platform.sh --quiet contracts/static-assets.yaml ``` ### Test the platform (offline, no AWS required) ```bash # Install test dependencies pip install -r requirements-test.txt # Run the test suite (all offline — uses moto for DynamoDB mocking) python3 -m pytest tests/ -v # Run the platform in check-only mode (offline — no AWS, no policy checks, # no outbox). Uses the default sample contract (contracts/static-assets.yaml) # and the sample dev environment (core/environments/dev.json). bash scripts/run_platform.sh --check-only # Expected: "=== PLATFORM CHECK OK ===" # Reproduce the full CI pipeline locally (lint -> test -> check-only) bash scripts/run_ci.sh # Expected: "=== CI PIPELINE OK ===" ``` ### CI/CD pipelines The CI/CD pipeline is defined by a **central pipeline contract** — a declarative YAML instance (`pipelines/ci.yml`) validated against a JSON Schema (`schemas/pipeline.schema.json`). Both platform-runner workflows implement the same contract: - `.github/workflows/ci.yml` — GitHub Actions (production) Both run three stages: **lint** (py_compile), **test** (pytest), and **check-only** (`run_platform.sh --check-only`). Both trigger on push to `main` and on pull requests. A test (`tests/test_pipeline_contract.py`) validates that the workflow conforms to the contract. `scripts/run_ci.sh` mirrors the CI pipeline locally — running the same three stages in sequence. This makes the pipeline fully reproducible from the shell, not just in CI: ```bash bash scripts/run_ci.sh # run all 3 stages (lint, test, check-only) bash scripts/run_ci.sh --quiet # suppress per-stage banners ``` ### Reusable deploy workflow The deployment pipeline is defined by a **central deployment pipeline contract** (`pipelines/contract.yml`, validated against `schemas/deploy-pipeline.schema.json`) and exposed to consumer repos as a **reusable workflow**: - `.github/workflows/deploy.yml` — GitHub Actions (production) The workflow implements the same stages as `pipelines/contract.yml` (validate-contract → resolve-stack → security checks → infrastructure plan → policy checks → confidence → evidence event → apply). A consumer repo invokes the reusable workflow via a **versioned tag** (floating MAJOR + MINOR, e.g. `acdl/.github/workflows/deploy.yml@v1.13`). The workflow checks out the consumer repo, then checks out the Nova platform repo into the runner workspace, and runs `scripts/run_platform.sh` against the consumer's contract — the consumer never clones the platform repo or invokes its scripts locally. See the [Consumer guide](docs/consumer-guide.md) for the end-to-end happy path. ### Output streaming (run_platform.sh) `scripts/run_platform.sh` streams output by default so the user can see what the platform is doing: - **`--check-only`**: streams the emitted infrastructure file content to stdout. - **`--plan-only`** and **full mode**: streams the infrastructure plan output via `tee` (visible and logged). - **Full mode**: prints policy-check results and each `PolicyCheckResult` record with severity, rule ID, and pass/fail status. A `--quiet` flag suppresses streaming (output to log files only) for backwards-compatible log-only mode. ## Consumer guide A step-by-step guide for a consumer to create their pipeline and define a contract that deploys any Nova module to AWS is at [`docs/consumer-guide.md`](docs/consumer-guide.md). The guide is generic across all modules; `static-assets` is the worked example. ## Repository layout | Path | Purpose | Status | |------|---------|--------| | `core/` | Platform code: contract resolver, confidence signal, outbox writer, environment check, environments, separation of duties, HITL/ledger designs | active | | `schemas/` | JSON Schemas: stack, contract, PolicyCheckResult, pipeline contract, deploy pipeline contract (draft 2020-12) | active | | `pipelines/` | Central pipeline contracts: `ci.yml` (CI), `contract.yml` (deployment) | active | | `adapters/` | Angine adapters — the engine adapter (the only engine-specific code per §12) + the policy adapter | active | | `terraform/` | State backend (S3 + DynamoDB) + platform TF (`terraform/spike/`) + bootstrap scripts (`terraform/bootstrap/`) | active | | `modules/` | Primitives + modules + `registry.json`. Primitives: s3, vpc, ecs-cluster, ecs-service, iam-role, alb, ecr, cloudfront, waf, rds. Modules: microservice, static-assets. Each module has a `examples/` directory with validated contract examples | active | | `contracts/` | Sample consumer contracts (`static-assets.yaml`, `microservice.yaml`) | active | | `scripts/` | Platform run script (`run_platform.sh` with `--check-only`/`--plan-only`/`--quiet`), CI pipeline script (`run_ci.sh`), key rotation | active | | `tests/` | Pytest suite (all offline — adapter, confidence signal, policy adapter, outbox writer, pipeline contract, contract resolver, streaming, environment check) | active | | `.github/workflows/` | GitHub Actions workflows: `ci.yml` (CI), `deploy.yml` (reusable deploy, invoked by consumer repos) | active | | `docs/` | GitHub Pages documentation site: consumer guide, modules, contracts, pipeline, versioning, environments, architecture, vision | active | ## Credentials & zero-trust ### Default — zero-trust OIDC + attribute-based authorization Consumer repos are **zero-trust**: they hold **no long-lived AWS keys** and no static credentials in repo secrets. - **Authentication** is **OIDC federation** between the platform runners (GitHub Actions) and AWS. Each job mints a short-lived STS token; no credential is ever stored in the consumer repo or in a runner secret. - **Authorization** is **attribute-based (ABAC)**, not role-based (RBAC). AWS IAM roles and session policies are scoped by two attribute classes: - **Repository identity** — the runner claim (e.g. `repo:org/consumer-repo:ref:refs/heads/main`) binds the role's trust policy to the exact consumer repo + branch that invoked the workflow. - **Resource-creation attributes** — every resource the pipeline creates is tagged with `nova:owner=` and `nova:contract=`. The session policy grants view/update/delete **only on resources whose tags match the calling repo**. The effect: a consumer's pipeline can only view and update the resources it created. Blast radius is contained to that consumer's own stack instances — one consumer can never touch another consumer's resources, and the consumer cannot escape its own scope. ### Alternative — static AWS key Where OIDC is not yet available, a static AWS key **may** be used as a documented alternative: - The key is stored in **GitHub Secrets** (consumer repo) for platform-runner runs, or in **`.env.secrets`** (gitignored, chmod 600) for local testing. - The platform rotates platform-runner keys on a **daily cadence** — rotation is not the consumer's burden in the platform-runner path. - **When `.env.secrets` is used locally**, rotating the key **out of band is the consumer's responsibility**. The platform guarantees daily rotation for platform-runner runs; it does not guarantee rotation for locally-held copies. The consumer must rotate a local key via `scripts/rotate_spike_key.sh` (or equivalent) on their own cadence. No long-lived credential is permitted persistently — the platform-runner key's useful lifetime is one workflow run, and the local alternative is rotated at least daily (platform-runner) or out of band (local).