a0799f13e5
- adapters/README.md: fixed stale TYPE_MAP/INPUT_MAP refs (the adapter is a stateless assembler); added the blockchain-exchange consumer row + the Gitea adapter note (SPEC §10 Q1 — no cross-repo uses:) - docs/METRICS.md: Post-Pilot denominators activated (AI Decision Accuracy + Human Escalation Frequency + the third metric now have non-zero data from the blkex-pilot-apply-v0.2 run) - .ciagent/ARCHITECTURE.md §12.8: Pilot Estate (v1.26 live) — the first real consumer estate, the live apply, the Gitea adapter, the evidence stream - .ciagent/nova-blockchain-exchange/README.md: consumer onboarding guide (deploy invocation, secrets, contract shape, verification) ---ci--- project: acdl phase: 4 milestone: v1.26 status: execute wave: W2 ---
140 lines
7.3 KiB
Markdown
140 lines
7.3 KiB
Markdown
# Nova Adapters
|
|
|
|
## Overview
|
|
|
|
Adapters translate the engine-agnostic Target Stack IR to engine-specific formats. The Terraform adapter is the primary adapter (IR → HCL). Policy adapters translate security tool output into normalized `PolicyCheckResult` records that the confidence signal consumes in an engine-agnostic way.
|
|
|
|
## Existing Adapters
|
|
|
|
| Adapter | Path | Input | Output | Purpose |
|
|
| --- | --- | --- | --- | --- |
|
|
| Terraform adapter | `adapters/terraform/adapter.py` | Stack instance JSON | Terraform HCL (`main.tf`, `terraform.tf`, `providers.tf`) | Compiles IR to Terraform |
|
|
| Checkov adapter | `adapters/terraform/policy/checkov_adapter.py` | Checkov JSON | `PolicyCheckResult` records | Translates Checkov results |
|
|
| Wiz adapter | `adapters/wiz/wiz_adapter.py` | Wiz API issues JSON | `PolicyCheckResult` records | Translates Wiz security findings |
|
|
| Kyverno adapter | `adapters/kyverno/kyverno_adapter.py` | Kyverno PolicyReport JSON | `PolicyCheckResult` records | K8s-native policy translation |
|
|
| kyverno-json engine | `adapters/kyverno-json/kyverno_json_engine.py` | Any JSON/YAML payload | `PolicyCheckResult` records | **v1.25 primary policy engine** (swappable via `PolicyEngine` protocol) |
|
|
|
|
## Policy Engine Protocol (v1.25)
|
|
|
|
The `core/policy_engine.py` module defines the **swap boundary** between
|
|
Nova and its policy engines. A `PolicyEngine` Python Protocol (PEP 544)
|
|
with three members (`name`, `is_configured()`, `evaluate()`) is the
|
|
contract; a `PolicyEngineRegistry` selects the active engine from
|
|
`config.json`'s `policy.engine` key. The confidence signal and pipeline
|
|
never import an engine directly — they go through the registry.
|
|
|
|
**Implementations:**
|
|
- `KyvernoJsonEngine` (`adapters/kyverno-json/`) — shells to the `kj`
|
|
CLI; the v1.25 default.
|
|
- `NullEngine` (`core/policy_engine.py`) — fallback when the `policy`
|
|
key is absent (emits `SKIPPED`).
|
|
- Future: `OpaEngine` — implements the same protocol, shells to
|
|
`opa eval`. The OPA-equivalent surface is documented in
|
|
`.ciagent/RESEARCH.md` §4.2.
|
|
|
|
**How to add a new engine:**
|
|
1. Create `adapters/<name>/<name>_engine.py` implementing the
|
|
`PolicyEngine` protocol (`name`, `is_configured()`, `evaluate()`).
|
|
2. `evaluate()` returns `list[dict]` where each dict conforms to
|
|
`schemas/policy_check_result.schema.json`.
|
|
3. Register the engine in `core/policy_engine.py`'s `_autoload_*`
|
|
function (or call `register(name, factory)` at startup).
|
|
4. Set `config.json.policy.engine` to the engine's `name`.
|
|
5. Add the engine to the `engine` enum in
|
|
`schemas/policy_check_result.schema.json` if it needs a distinct
|
|
enum value (v1.25 reuses `"kyverno"` — see D-116).
|
|
|
|
## How to Write an Adapter
|
|
|
|
### Terraform Adapter Extension (stateless assembler — v1.11 rewrite)
|
|
|
|
> The adapter owns **no module content**. There is no `TYPE_MAP`, no
|
|
> `INPUT_MAP`, no `OUTPUT_MAP`, and no per-type branch logic (all deleted
|
|
> in the v1.11 rewrite — the 918-line monolith collapsed to a ~80-line
|
|
> assembler). Engine-specific shape lives in each L1 module's own
|
|
> `terraform/` dir (`versions.tf`/`variables.tf`/`locals.tf`/`main.tf`/
|
|
> `outputs.tf`); the adapter only assembles them.
|
|
|
|
To extend the Terraform adapter, **do not edit the adapter** — instead:
|
|
|
|
1. Add an L1 module with a real `terraform/` dir (owning its resource
|
|
shape, nested HCL blocks, and defaults).
|
|
2. Register it in `modules/registry.json` under the module name with its
|
|
`terraform_dir` path. The adapter reads `registry.json` to find each
|
|
module's directory.
|
|
3. The adapter emits `module "<rid>" { source = "<path>" }` blocks at
|
|
the root, with resolved inputs + wired `ref:` refs between modules.
|
|
No type-specific translation lives in the adapter.
|
|
|
|
> If you find yourself reaching for a "TYPE_MAP"-style constant, the L1
|
|
> module is missing a piece — fix the module, not the adapter.
|
|
|
|
### Policy Adapter Pattern
|
|
|
|
1. Define `SEVERITY_MAP` and `RESULT_MAP` dicts that translate the engine's native severity/result vocabulary to the `PolicyCheckResult` enums.
|
|
2. Implement `_to_pcr(raw_record, contract_id)` → `PolicyCheckResult` dict.
|
|
3. Implement `adapt(input_path, contract_id)` → list of `PolicyCheckResult` dicts.
|
|
4. Implement `is_configured()` → bool (env var check) so the platform can skip the adapter when credentials are absent.
|
|
|
|
## How to Wire an Adapter
|
|
|
|
- **Terraform adapter** — invoked by `scripts/run_platform.sh` Step 3 (`terraform-plan`).
|
|
- **Checkov adapter** — invoked by `scripts/run_platform.sh` Step 5 (`checkov`).
|
|
- **Wiz / Kyverno adapters** — optional Steps 5b/5c, run only when the relevant env vars are set.
|
|
- All policy adapters output records that are validated against `schemas/policy_check_result.schema.json`.
|
|
|
|
## Dependencies
|
|
|
|
- `jsonschema`, `pyyaml` — used by all adapters for loading and validating inputs.
|
|
- `boto3` — used by the Wiz adapter for AWS API access.
|
|
- `checkov` — used by the Checkov adapter to run policy scans.
|
|
- No external deps for the Terraform adapter (pure Python).
|
|
|
|
## How to Test Adapters
|
|
|
|
- `tests/test_adapter.py` — Terraform adapter (stateless assembly: registry read, `module "<rid>" { source }` emission, `ref:` wiring, outputs). No `TYPE_MAP`/`INPUT_MAP` tests — the adapter owns no type mappings.
|
|
- `tests/test_checkov_adapter.py` — Checkov adapter.
|
|
- `tests/test_wiz_adapter.py` — Wiz adapter.
|
|
- `tests/test_kyverno_adapter.py` — Kyverno adapter.
|
|
- All adapter tests load fixtures from `tests/fixtures/` and use `moto` for AWS mocking.
|
|
|
|
## Where to Write Tests
|
|
|
|
- `tests/test_<adapter_name>.py` paired with `tests/fixtures/<adapter>_fixture.json`.
|
|
|
|
## Adding a New Adapter
|
|
|
|
1. Create `adapters/<name>/<name>_adapter.py`.
|
|
2. Implement `adapt()` and (for policy adapters) `is_configured()`.
|
|
3. Add the adapter's engine name to the `engine` enum in `schemas/policy_check_result.schema.json` if it is a policy adapter.
|
|
4. Write a test (`tests/test_<name>_adapter.py`) plus a fixture (`tests/fixtures/<name>_fixture.json`).
|
|
5. Add it to `scripts/run_platform.sh` if it is invoked at runtime.
|
|
6. Update this README.
|
|
|
|
## Consumers
|
|
|
|
The Terraform adapter compiles contract IR for consumer estates. The
|
|
first real consumer estate is now live:
|
|
|
|
| Consumer | Version | Environment | Account | Forge / Adapter | Status |
|
|
| --- | --- | --- | --- | --- | --- |
|
|
| `nova-blockchain-exchange` | v0.2 | dev | `581513795199` | inline adapter (see note below) | **live** (pilot apply `blkex-pilot-apply-v0.2`, 2026-08-19) |
|
|
|
|
### Forge adapter note (SPEC §10 Q1)
|
|
|
|
Forge Actions (the consumer's forge runtime) does **not** support
|
|
cross-repo `uses:` references — the forge rejects
|
|
`uses: <owner>/<repo>/.github/workflows/<file>@<ref>` with
|
|
`expected format {owner}/{repo}/.{git_platform}/workflows/{filename}@{ref}`.
|
|
The consumer (`nova-blockchain-exchange`) therefore uses an **inline
|
|
adapter** in its `deploy.yml`: the workflow does `actions/checkout@v4`
|
|
on the consumer, then `actions/checkout@v4` `acdl/acdl` @ `ref: v1.25`
|
|
into `platform/`, and runs `bash platform/scripts/run_platform.sh ...`
|
|
directly — no `uses:` indirection.
|
|
|
|
The platform's own `.github/workflows/deploy.yml` (this repo) stays as
|
|
the **GitHub Actions reference implementation** — the reusable
|
|
`workflow_call` workflow used by GitHub-hosted consumers. The two
|
|
files share the same contract shape; the only declared difference is
|
|
the forge/runtime, not the stages or commands. See
|
|
`.ciagent/ARCHITECTURE.md` §12.8 for the live pilot-estate wiring. |