diff --git a/CHANGELOG.md b/CHANGELOG.md index 3764064..7310651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/). ### Changed +- Recorded final Version 2 Greenfield clean-room acceptance as live-proven. A fresh OCI Cloud Shell and a fresh clone of `main` completed `tools/bootstrap-cloud-shell` → operator-local gitignored inputs → `tools/deploy-clean-room` (Terraform additive Greenfield apply, exact FORMAT gate for genuinely blank scratch, host / scratch / MicroK8s convergence). The first deploy stopped on the live-discovered Ubuntu 24.04 `python3-pip` / `python3-wheel` defect; after PR #75 merged, the same environment resumed from actual Terraform / Ansible / Kubernetes state. Resumed deploy completed: `private-runtime-config.yml` succeeded; Argo Applications reached Synced and Healthy after bounded WAIT states; Kubernetes workloads became healthy. `tools/verify-clean-room` then passed: Terraform no-drift, scratch mount, Argo / workloads, second `private-runtime-config.yml` and `site.yml` runs with `changed=0`, exact REBOOT gate, remote reboot with a changed boot ID, MicroK8s ready after reboot, scratch mount survived, and Argo / workloads returned healthy. Destroy acceptance then passed: pre-destroy Terraform no-drift, Terraform state contained only disposable root-owned infrastructure, the inspected saved destroy plan contained only delete actions for Terraform-owned disposable resources, external Vault / secret lifecycle / Object Storage state bucket were not in the plan, the reviewed plan applied, Terraform state was empty, and a fresh post-destroy plan was create/add-only (not applied). The external state bucket survived with Versioning Enabled and NoPublicAccess; the external Vault remained ACTIVE. Vault secret-value lifecycle is not Terraform-owned; secret contents were not re-read as a post-destroy proof. Resource count is taken from actual Terraform state for each run and is not a hard-coded destroy contract. Trailing “not yet live proven” phrases on the historical Fixed entries below describe the status when those Git corrections landed and are superseded by this acceptance. - Made `docs/V2_CLEAN_ROOM_DEPLOYMENT.md` the canonical Greenfield V2 operator runbook for the existing clean-room tools, tracked Terraform provider lockfile, and destroy-acceptance procedure. - Replaced a fixed repository-wide response-language rule with a task-driven contract in `AGENTS.md` and the always-applied foundation rule - Documented the current GitHub `main-protection` required checks as present external policy, not future work diff --git a/README.md b/README.md index 1e984ea..c8de2d2 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Single-node infrastructure for quantitative research and backtesting on OCI. | Generation | Role today | Path | | --- | --- | --- | -| **Version 2** | **Current target** — Terraform → Ansible → Argo CD clean-room deploy | [`docs/V2_CLEAN_ROOM_DEPLOYMENT.md`](docs/V2_CLEAN_ROOM_DEPLOYMENT.md) | +| **Version 2** | **Current architecture** — Terraform → Ansible → Argo CD clean-room deploy | [`docs/V2_CLEAN_ROOM_DEPLOYMENT.md`](docs/V2_CLEAN_ROOM_DEPLOYMENT.md) | | **Version 1** | Historical record — not an executable repository path | [`VERSION_1_BASELINE.md`](VERSION_1_BASELINE.md) | V2 ownership: @@ -27,8 +27,19 @@ That runbook is the canonical operator contract: tools/bootstrap-cloud-shell → tools/deploy-clean-room → tools/verify-clean-room ``` -GitHub Actions static validation is not live rebuild proof. Historical V1 -executable paths are retired. +The canonical V2 clean-room path has been live-proven through: + +```text +deploy → verify / idempotency → reboot → post-reboot convergence +→ destroy → empty Terraform state → fresh create/add-only plan +``` + +The external Object Storage state bucket and Vault foundation were preserved. +The path is resumable from actual Terraform / Ansible / Kubernetes state, +including after the live-discovered PR #75 system-pip fix. GitHub Actions +static validation is not live rebuild proof. Historical V1 executable paths +are retired. This is not a claim that Version 2 is multi-node, managed +Kubernetes, or production-grade. The historical in-place SecretProviderClass handoff document ([`docs/RUNTIME_SPC_OWNERSHIP_CUTOVER.md`](docs/RUNTIME_SPC_OWNERSHIP_CUTOVER.md)) @@ -44,7 +55,7 @@ is a fallback procedure, not the primary V2 path. ## Architecture Overview -### Version 2 path (target) +### Version 2 path (current architecture) See [`docs/V2_CLEAN_ROOM_DEPLOYMENT.md`](docs/V2_CLEAN_ROOM_DEPLOYMENT.md) for the canonical operator sequence (`tools/bootstrap-cloud-shell` → @@ -83,7 +94,7 @@ argocd/ │ ├── postgres/ # PostgreSQL deployment/pvc/service + DB init job │ └── scratch/ # scratch PVC overlays for dev/prod ├── argocd/ # Argo CD Application definitions -├── VERSION_1_BASELINE.md # Version 1 ownership, limits, and V2 direction +├── VERSION_1_BASELINE.md # Historical Version 1 ownership and limits ├── CONTRIBUTING.md ├── SECURITY.md └── README.md @@ -91,32 +102,36 @@ argocd/ ## Prerequisites -- Ubuntu VM with sudo privileges -- `snap` available (used to install MicroK8s) -- Outbound network access from the VM to pull: - - snap packages - - Helm charts - - container images - - remote CRD/manifests (Prometheus Operator and Argo CD install URLs) -- OCI block device available at `/dev/oracleoci/oraclevds` +Canonical V2 deploy provisions the Ubuntu host with Terraform and configures +it with Ansible. Do not treat an already-prepared VM plus `.env` bootstrap as +the primary path. Operator procedure: +[`docs/V2_CLEAN_ROOM_DEPLOYMENT.md`](docs/V2_CLEAN_ROOM_DEPLOYMENT.md). + +Platform requirements: + +- Ubuntu host with `sudo` and `snap` (V2: Terraform-provisioned reference VM) +- Outbound network access from the VM to pull snap packages, Helm charts, + container images, and remote CRD/manifests +- Dedicated OCI scratch block volume (V2: Terraform-attached; Ansible + discovers the host device; kernel paths such as + `/dev/oracleoci/oraclevds` are not a stable contract) - OCI Vault containing all required secret names (see [Required OCI Vault secrets](#required-oci-vault-secrets)) - OCI IAM configured so the instance principal can read those vault secrets ## Configuration -Copy and edit environment variables: +Version 2 operator inputs are gitignored files completed after +`tools/bootstrap-cloud-shell`: -```bash -cp .env.example .env +```text +terraform/backend.hcl +terraform/terraform.tfvars +ansible/extra-vars/private-runtime.yml ``` -Load values into the current shell before bootstrap: - -```bash -set -a -source .env -set +a -``` +[`.env.example`](.env.example) remains a public reminder of Vault secret names +and of `VAULT_ID` / `OCI_REGION` as operator values. Ansible does not read +`.env`. Do not commit secret values or the gitignored input files. ### Environment variables @@ -127,7 +142,7 @@ set +a ## Required OCI Vault Secrets -The following secret names are referenced directly by `SecretProviderClass` manifests and must exist in OCI Vault before bootstrap. +The following secret names must exist in OCI Vault before private-runtime materialization. Ansible renders them into SecretProviderClass resources; they are not stored as secret values in Git. | Secret name | Used by | Purpose / expected value type | Source contract | | --- | --- | --- | --- | @@ -147,7 +162,10 @@ Do not commit secret values to Git. All `SecretProviderClass` resources in this repository use `authType: instance`. The OCI provider DaemonSet also sets `OCI_RESOURCE_PRINCIPAL_VERSION`, indicating instance principal authentication. -This repository does not include OCI IAM policy text. You must configure OCI IAM policies so the instance principal can read the required vault secrets. +Version 2 Terraform owns the instance-principal Dynamic Group and the +compartment-scoped `read secret-bundles` policy for the reference compute +instance. Vault lifecycle, secret **values**, and any broader tenancy IAM +remain external. ## V1 historical record @@ -256,7 +274,8 @@ Legacy V1 Bash storage bootstrap mounted `/mnt/scratch` while scratch PVCs used ## Post-Install Verification -Run these checks after bootstrap: +Canonical proof is `tools/verify-clean-room`. The commands below are host-side +debugging only and are not a substitute for that tool. ```bash sudo microk8s status @@ -269,8 +288,9 @@ sudo microk8s kubectl get pvc -A What to verify: - MicroK8s reports ready status -- Argo CD `Application` resources exist for all six apps listed above -- Pods are created in namespaces: `default`, `postgres`, `mlflow`, `monitoring`, `argo`, `dev`, `prod` +- Argo CD `Application` resources exist for the Applications listed above +- Pods are created in namespaces including `argocd`, `postgres`, `mlflow`, + `monitoring`, `argo`, `dev`, and `prod` - Expected NodePorts are present (`30007`, `32120`, `30090`, `30500`) - PVCs exist for `postgres-pvc` and `scratch-pvc` (dev/prod) @@ -328,11 +348,16 @@ For vulnerability reporting and security policy, see `SECURITY.md`. ## Out of Scope / Limitations -- Multi-node Kubernetes production setups -- Managed Kubernetes providers -- Public service exposure configuration +Live Greenfield acceptance does not remove these architecture limits: + +- Single-node MicroK8s (not a multi-node or highly available cluster) +- No managed Kubernetes provider +- Public service exposure configuration (cloud firewall / NSG remains external) - Application business logic and trade execution systems -- Vault lifecycle and Vault secret **values** (referenced by Terraform / consumed via CSI; not provisioned as secret contents here) +- Vault lifecycle and Vault secret **values** (referenced by Terraform / + consumed via CSI; not provisioned as secret contents here) +- External Object Storage Terraform state-bucket lifecycle (operator-managed + foundation; not owned by this Terraform root) Additional Version 1 limitations and evidence gaps are listed in [`VERSION_1_BASELINE.md`](VERSION_1_BASELINE.md). The V2 clean-room operator procedure is [`docs/V2_CLEAN_ROOM_DEPLOYMENT.md`](docs/V2_CLEAN_ROOM_DEPLOYMENT.md). @@ -347,7 +372,7 @@ This repository includes Cursor and agent guardrails for AI-assisted work. Start - [`docs/REPOSITORY_SECURITY.md`](docs/REPOSITORY_SECURITY.md) — repository security CI and metadata hygiene - [`docs/RUNTIME_SPC_OWNERSHIP_CUTOVER.md`](docs/RUNTIME_SPC_OWNERSHIP_CUTOVER.md) — historical in-place SPC handoff (fallback only) - `terraform/README.md` / `ansible/README.md` / `argocd/README.md` — layer ownership -- `VERSION_1_BASELINE.md` for Version 1 ownership, limitations, and Version 2 direction +- `VERSION_1_BASELINE.md` for historical Version 1 ownership and limitations - `AGENTS.md` for the cross-agent safety entry point - [`docs/AI_AGENT_WORKFLOW.md`](docs/AI_AGENT_WORKFLOW.md) for the Cursor/AI-assisted implementation and review workflow - `CONTRIBUTING.md` for contribution workflow diff --git a/VERSION_1_BASELINE.md b/VERSION_1_BASELINE.md index 01131b7..be0404a 100644 --- a/VERSION_1_BASELINE.md +++ b/VERSION_1_BASELINE.md @@ -1,8 +1,16 @@ # Version 1 Baseline -This document describes the first-generation architecture of the repository (“Version 1”) as evidenced by the repository contents. +This document is the **historical** record of the first-generation architecture +(“Version 1”) as evidenced by the repository contents at that generation. -The project uses pre-1.0 semantic versioning while the infrastructure model is being stabilized. “Version 1” names the architecture generation; it is **not** the same as a SemVer tag such as `v1.0.0`. See `CHANGELOG.md` for published release history. +Version 2 is now the **active architecture**. The canonical operator path is +[`docs/V2_CLEAN_ROOM_DEPLOYMENT.md`](docs/V2_CLEAN_ROOM_DEPLOYMENT.md). This +file does not describe how to deploy Version 2 and is not an executable +Version 1 fallback. + +The project uses pre-1.0 semantic versioning until an intentional release cut. +“Version 1” names the architecture generation; it is **not** the same as a +SemVer tag such as `v1.0.0`. See `CHANGELOG.md` for published release history. It is documentation only. It does **not** claim that this architecture generation is production-ready, fully declarative, or fully automated. @@ -23,10 +31,10 @@ Version 1 solves “bring up a usable single-node GitOps cluster on a prepared O - Version 1 is a **single-node** platform baseline. - It is **not** presented as a highly available or enterprise production platform. -- Version 2 (architecture direction) is expected to improve reproducibility and clear ownership without introducing unnecessary distributed-system complexity. +- Version 2 was the planned follow-on to improve reproducibility and clear ownership without introducing unnecessary distributed-system complexity. That generation is now the active architecture; see `docs/V2_CLEAN_ROOM_DEPLOYMENT.md`. - SemVer release tags track repository releases; they do not by themselves redefine the architecture generation names used in this document. -## Current Ownership +## Version 1 Ownership | Area | Version 1 owner | | --- | --- | @@ -117,11 +125,15 @@ These points are **not** confirmed as facts from the repository alone: 4. **Actual OCI NSG/Security List rules** — SECURITY and README describe an SSH-oriented access model; cloud firewall contents are not encoded in this repository. 5. **Terraform state backend and recovery targets** — not defined in Version 1; required before Version 2 OCI automation. -## Version 2 Direction +## Version 2 Direction (historical) + +This section records the original Version 2 direction from the Version 1 +baseline. Version 2 has since become the active architecture; the canonical +operator path is `docs/V2_CLEAN_ROOM_DEPLOYMENT.md`. -Version 1 achieves a usable single-node research cluster with GitOps-managed applications, but ownership is split across manual OCI setup, imperative Bash bootstrap, live Application patches, and Argo CD. That split limits reproducibility, idempotency, and auditability. Version 2 is needed to give each resource one clear owner and to move OCI and host lifecycle into reviewable automation. +Version 1 achieved a usable single-node research cluster with GitOps-managed applications, but ownership was split across manual OCI setup, imperative Bash bootstrap, live Application patches, and Argo CD. That split limited reproducibility, idempotency, and auditability. Version 2 was needed to give each resource one clear owner and to move OCI and host lifecycle into reviewable automation. -Planned ownership boundaries (direction only; not implemented by this baseline): +Ownership boundaries planned from this baseline (not implemented by Version 1): - **Terraform** owns OCI infrastructure (network, compute, storage attachments, IAM references, Vault references as appropriate). - **Ansible** owns host configuration, filesystems/mounts, MicroK8s, and the one-time Argo CD / root Application bootstrap. @@ -129,11 +141,12 @@ Planned ownership boundaries (direction only; not implemented by this baseline): - **GitHub Actions** validates Terraform, Ansible, shell, and Kubernetes configuration; it does not own runtime infrastructure. - Every resource should have **one** clear owner. -Version 2 should preserve single-node clarity where appropriate and avoid unnecessary multi-node complexity unless requirements change. +Version 2 was intended to preserve single-node clarity where appropriate and avoid unnecessary multi-node complexity unless requirements change. That single-node limit remains in the active architecture. ## Related Documents -- `README.md` — operator guide and bootstrap usage +- `README.md` — current operator overview +- [`docs/V2_CLEAN_ROOM_DEPLOYMENT.md`](docs/V2_CLEAN_ROOM_DEPLOYMENT.md) — canonical Version 2 operator runbook - `CHANGELOG.md` — change history and release tracking status - `SECURITY.md` — vulnerability reporting and security policy - `CONTRIBUTING.md` — contribution workflow diff --git a/ansible/README.md b/ansible/README.md index 3f288de..6d452fa 100644 --- a/ansible/README.md +++ b/ansible/README.md @@ -6,7 +6,7 @@ Ansible manages host configuration and bootstrap for the V2 reference platform. ## Ownership -Ansible owns or will own: +Ansible owns: ```text host baseline diff --git a/docs/V2_CLEAN_ROOM_DEPLOYMENT.md b/docs/V2_CLEAN_ROOM_DEPLOYMENT.md index 47168c9..36ff5ba 100644 --- a/docs/V2_CLEAN_ROOM_DEPLOYMENT.md +++ b/docs/V2_CLEAN_ROOM_DEPLOYMENT.md @@ -27,6 +27,29 @@ see [`RUNTIME_SPC_OWNERSHIP_CUTOVER.md`](RUNTIME_SPC_OWNERSHIP_CUTOVER.md). --- +## Live acceptance status + +The documented Greenfield flow has been exercised end-to-end successfully +from a fresh OCI Cloud Shell and a fresh clone: + +```text +deploy → verify / idempotency (changed=0) → reboot → post-reboot convergence +→ reviewed destroy → empty Terraform state → fresh create/add-only plan +``` + +The external Object Storage state bucket (Versioning Enabled, NoPublicAccess) +and the external Vault (ACTIVE) were preserved. The path is resumable from +actual Terraform / Ansible / Kubernetes state, including after the +live-discovered PR #75 system-pip fix. Vault secret **values** remain +operator-managed; their contents were not re-read as a post-destroy proof. + +This status statement is not a substitute for the operator procedure below. +GitHub Actions static validation remains not live rebuild proof. Live +acceptance does not make Version 2 multi-node, managed Kubernetes, or +production-grade. + +--- + ## Current operator contract vs historical context ### CURRENT OPERATOR CONTRACT diff --git a/terraform/README.md b/terraform/README.md index 1c2f2a8..913f852 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -22,7 +22,7 @@ Terraform does **not** own: - long-lived Kubernetes resources - Vault lifecycle or secret values -Ansible will own host configuration and bootstrap. +Ansible owns host configuration and bootstrap. Argo CD owns long-lived Kubernetes desired state. ## Network ownership @@ -122,22 +122,25 @@ The reference instance can read secret bundles only in the configured secret com It does not receive Vault, key, secret-management, or broad tenancy permissions. ``` -V1 resolves secrets by name inside SecretProviderClass manifests. Secret OCIDs are not present in this repository, so compartment-scoped `read secret-bundles` is the minimal practical policy for this migration stage. +V1 resolved secrets by name inside SecretProviderClass manifests. Secret OCIDs are not present in this repository, so compartment-scoped `read secret-bundles` is the minimal practical policy for this architecture. Further restriction to individual `target.secret.id` values remains a later hardening option once secret OCIDs are managed as explicit inputs. ### External Vault ```text -The Vault and secret values remain externally managed at this migration stage. +The Vault and secret values remain externally managed. +This Terraform root does not own Vault lifecycle or secret contents. ``` -`oci_vault_id` is an infrastructure reference for later CSI / Argo CD configuration, not a secret value. +`oci_vault_id` is an infrastructure reference for CSI / private-runtime consumption, not a secret value. -### Later ownership +### Later hardening ```text -Terraform → OCI identity and access -Ansible/Argo CD later → CSI/provider/bootstrap and declarative Kubernetes secret consumption +Terraform → OCI identity and access (implemented in this root) +Argo CD → CSI Driver / OCI provider (`oci-secrets`) and workload secret consumption +Ansible → host bootstrap and private-runtime SecretProviderClass materialization +Further IAM restriction to individual target.secret.id values remains later ``` ## Current managed scope