# Nereus delivery roadmap Updated: 2026-08-26 `[x]` means locally verified. `[~]` means implemented but awaiting a real integration environment. `[ ]` means not implemented. `[H]` is human-owned and must not be edited by agents. Update this file in the same change that completes or materially changes a task. Mark a task `[x]` only after its stated verification passes. ## API service - [x] Create the Go 1.26 module and dependency lock file. - [x] Create the idempotent `buoys` table migration. - [x] Create the idempotent `readings` table migration. - [x] Create `idx_readings_buoy_time` idempotently. - [x] Parse `PORT`, `CHAOS_ERROR_RATE`, and `LOG_LEVEL`. - [x] Preserve the build-injected `APP_VERSION` value. - [x] Require `DATABASE_URL` without logging it. - [x] Start HTTP without waiting for PostgreSQL. - [x] Retry migrations while PostgreSQL is unavailable. - [x] Keep `/healthz` independent of PostgreSQL. - [x] Require a reachable, migrated database for `/readyz`. - [x] Implement `GET /api/v1/buoys`. - [x] Implement `POST /api/v1/buoys`. - [x] Implement `GET /api/v1/buoys/{id}`. - [x] Implement `DELETE /api/v1/buoys/{id}`. - [x] Implement filtered, paginated `GET /api/v1/readings`. - [x] Implement `POST /api/v1/readings`. - [x] Implement the real grouped aggregate query. - [x] Bound JSON request bodies and return JSON errors. - [x] Restrict chaos injection to `/api/v1/*`. - [x] Expose all five required Prometheus metrics. - [x] Use chi route templates for the HTTP `path` label. - [x] Enable OTLP/gRPC tracing only when configured. - [x] Create HTTP spans and child database spans. - [x] Emit structured request logs containing `trace_id`. - [x] Add table-driven handler tests without a live database. - [x] Test chaos and unaffected health behavior. - [x] Add a `testing.Short()`-guarded PostgreSQL integration test. - [x] Pass `go test -short ./...`. - [x] Pass `go vet ./...`. - [x] Build with `CGO_ENABLED=0`. - [x] Run the integration test against PostgreSQL 17. - [x] Test database failure behavior on every data endpoint. - [x] Assert exact metric names and route-template labels in tests. - [x] Test malformed and oversized JSON request bodies. - [x] Run `golangci-lint run` with the CI-selected version. ## Load generator - [x] Create the standalone Go 1.26 module. - [x] Require `TARGET_URL` and default `RPS` to 5. - [x] Reuse existing buoys or seed 42 validated offshore buoys. - [x] Generate the 60/25/15 request mix. - [x] Generate plausible synthetic reading values. - [x] Count HTTP statuses and network errors. - [x] Log a JSON status summary every ten seconds. - [x] Continue traffic while requests and seeding fail. - [x] Retry seeding without blocking read traffic. - [x] Handle SIGTERM and SIGINT. - [x] Keep the implementation below 200 lines. - [x] Pass `go vet` and a static build. - [x] Test request weighting deterministically. - [x] Test seeding against an empty `httptest` API. - [x] Test continued traffic through repeated HTTP 500 responses. - [x] Run against the real API for at least five minutes. - [x] Confirm Prometheus receives continuous request samples. ## Container build and local Compose - [x] Create the API builder stage from `golang:1.26-alpine`. - [x] Cache dependencies before copying API source. - [x] Build with `CGO_ENABLED=0` and `-ldflags "-s -w"`. - [x] Inject `APP_VERSION` with `-X`. - [x] Copy only the API binary into `gcr.io/distroless/static:nonroot`. - [x] Create the load-generator multi-stage image. - [x] Use `gcr.io/distroless/static:nonroot` for the load generator. - [x] Add the required `.dockerignore` exclusions. - [x] Confirm both images run non-root and read-only. - [x] Confirm the API image is below 25 MB (7,815,877 bytes locally). - [x] Add API, PostgreSQL 17, and OTEL Collector to local Compose. - [x] Add the PostgreSQL healthcheck. - [x] Make the API wait on healthy PostgreSQL in Compose. - [x] Verify `docker compose up` reaches API readiness. - [x] Keep Mini PC and local-development Compose files separate. ## Static web dashboard - [x] Create the no-build static HTML entry point. - [x] Create the dark teal/navy glass visual system. - [x] Replace decorative background waves with an operational chart grid. - [x] Keep version, health, and error panels visible together. - [x] Show the build-injected API version from the existing health response header. - [x] Poll `/healthz` every two seconds. - [x] Poll `/readyz` every two seconds. - [x] Show each health indicator's last transition timestamp. - [x] Count non-2xx `/api/v1/*` browser responses. - [x] Calculate the rolling 60-second HTTP error rate. - [x] Distinguish network-offline events from HTTP errors. - [x] Preserve and grey the last known telemetry values offline. - [x] Recover automatically when the API returns. - [x] Add a fully local Natural Earth buoy map and pulsing markers. - [x] Add animated reading charts. - [x] Keep all API calls same-origin and relative. - [x] Remove all frontend runtime dependencies on external hosts. - [x] Verify all panels fit at 1280×720 without page scrolling. - [x] Create the unprivileged nginx image. - [x] Create the separate Mini PC Traefik Compose file. - [x] Test the finished offline/degraded rendering without API connectivity. - [x] Test the rendered dashboard while the API returns HTTP 500 responses. ## Observability - [x] Receive OTLP/gRPC traces in the Collector. - [x] Export traces from the Collector to Tempo. - [~] Ship structured Kubernetes pod logs to Loki with the cluster Collector. - [x] Start Loki locally and verify its Grafana data source readiness. - [x] Configure Prometheus to scrape `/metrics`. - [x] Add request-rate, error-rate, and latency panels. - [x] Add database-operation latency panels. - [x] Add readings-ingested and active-buoy panels. - [x] Add a deployed-version dashboard variable. - [~] Correlate logs and traces using `trace_id` after cluster log ingestion exists. - [x] Add API error-rate, latency, readiness, and scrape alerts. - [x] Load the dashboard and evaluate all six PromQL queries. - [x] Trigger chaos and distinguish the failing version in Grafana. ## Fedora 44 host provisioning - [x] Separate node 1 and node 2 inventory groups. - [x] Pin the project k3s version. - [x] Reject managed hosts that are not Fedora 44. - [x] Install common host dependencies. - [x] Enable firewalld. - [x] Trust the configured pod and service CIDRs. - [x] Open VXLAN port 8472/UDP between nodes. - [x] Open kubelet port 10250/TCP between nodes. - [x] Open API server port 6443/TCP on node 1. - [x] Install node 1 as the k3s server. - [x] Install node 2 as a k3s agent. - [x] Keep the cluster token out of repository files and output. - [x] Enable both k3s systemd services. - [x] Wait for every Kubernetes node to become Ready. - [x] Add the equivalent manual configuration checklist. - [x] Document Terraform-to-Ansible inventory handoff. - [x] Keep libvirt addresses, bridge rules, and VM network profiles out of the reusable Ansible roles. - [x] Run the playbook twice on two clean Fedora 44 QEMU machines. - [x] Confirm the second run reports no unintended changes (`changed=0`). - [x] Restrict firewall sources to actual node/operator networks. - [x] Confirm SELinux remains enforcing on both QEMU machines. - [x] Reboot both QEMU machines and confirm automatic recovery. - [x] Disconnect and reconnect node 2, then confirm it rejoins. - [x] Record tested minimum CPU, memory, and disk requirements. ## One-command QEMU lab - [x] Check QEMU, libvirt, cloud-utils, Ansible, SSH, ping, and KVM. - [x] Install missing dependencies on a Fedora workstation. - [x] Enable libvirt and its default network. - [x] Discover and cache the Fedora 44 x86_64 cloud image. - [x] Keep SSH and VM state outside the repository. - [x] Generate cloud-init media for both nodes. - [x] Create reusable copy-on-write disks. - [x] Create each VM with two vCPUs and 3 GiB RAM. - [x] Assign deterministic addresses to both local VMs. - [x] Wait for ICMP ping and SSH on both nodes. - [x] Generate the Ansible inventory automatically. - [x] Configure both nodes automatically. - [x] Check Ansible reachability, k3s files, and binaries. - [x] Check firewalld, k3s services, ports 6443 and 10250. - [x] Check Kubernetes node readiness. - [x] Add `check`, `stop`, and explicit `destroy` actions. - [x] Execute `lab.sh up` with working `/dev/kvm` and pass the full verification. - [x] Reboot both local VMs and pass the bounded post-reboot service and node checks. - [x] Execute `lab.sh check` after a workstation reboot. - [x] Test recovery from an interrupted image download and validate the resumed QCOW2. - [x] Test recovery after node 2 is deleted manually and rejoin it with a fresh identity. - [ ] Test on a workstation with no dependencies installed. - [x] Decide whether an aarch64 image path is required; keep the lab x86_64-only. - [ ] After final sign-off, destroy the QEMU lab and remove packages installed only for it with Pacman's dependency-aware cleanup. ## Local k3d rollback mechanism - [x] Define one local server and two local agent nodes. - [x] Map Traefik to host ports 8080 and 8443. - [x] Add lean kube-prometheus-stack values. - [x] Add the synthetic Argo Rollouts analysis harness. - [x] Add an idempotent `up/check/destroy` command and verify fresh creation and reconciliation. - [x] Install Argo Rollouts. - [x] Install kube-prometheus-stack with the local values. - [x] Apply the synthetic harness. - [x] Prove `vector(0.0)` permits promotion. - [x] Change the test value to `vector(1.0)`. - [x] Prove failed analysis aborts promotion with `vector(0.9)`. - [x] Tear down and recreate the test from scratch. ## Human-owned deployment and infrastructure - [H] Define infrastructure modules under `terraform/`. - [H] Export provisioned addresses for Ansible inventory. - [H] Create Kubernetes base resources and overlays. Agent-authored 2026-08-21 at the owner's request; builds and server dry-runs pass, not applied. - [H] Create API and load-generator workloads. Agent-authored 2026-08-21; not applied. - [H] Create active and preview Services. Agent-authored 2026-08-21; not applied. - [H] Create the Prometheus ServiceMonitor. Agent-authored 2026-08-21; not applied, scrape not confirmed. - [H] Create the real error-rate AnalysisTemplate. Agent-authored 2026-08-21; PromQL never evaluated against real series. - [H] Configure blue-green promotion and automated rollback. Agent-authored 2026-08-21; promotion and abort paths untested with the real API. - [H] Maintain Forgejo Actions and GitHub mirror workflows. - [H] Create sealed secrets from off-repository plaintext inputs. ## Forgejo CI/CD - [x] Run API and load-generator race tests, vet, and pinned lint in containers. - [x] Scan committed history and the working tree for secrets. - [x] Scan release configuration and images for high or critical findings. - [x] Build immutable commit-SHA API, load-generator, and web image tags. - [x] Keep active and preview traffic flowing during rollout analysis. - [~] Run the Forgejo workflow after a private `docker` runner and encrypted secrets are configured. - [~] Publish images and execute the first automated production rollout. ## End-to-end acceptance - [ ] Provision two clean Fedora hosts. - [ ] Reboot both hosts before platform installation. - [x] Install the cluster platform components. - [x] Deploy PostgreSQL and wait for health. - [x] Verify API liveness before database readiness. - [x] Verify readiness after migrations complete. - [x] Deploy the load generator and observe continuous samples. - [x] Send one request to every API endpoint in the local Compose stack. - [x] Verify every required metric and exact label name locally. - [x] Verify an HTTP trace contains a child database span. - [x] Correlate one stdout request log and Tempo trace by `trace_id`. - [x] Promote a healthy preview through analysis. - [x] Deploy a preview with a nonzero chaos rate. - [x] Observe preview errors in Prometheus. - [x] Confirm analysis aborts the unhealthy promotion. - [x] Confirm the active Service remains on the healthy version. - [x] Confirm load generation continues throughout failure. - [x] Confirm the Mini PC page remains usable with the cluster off. - [x] Capture reproducible, non-secret evidence needed for the final project report.