Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b997bd63b1 | |||
| 40e61e6b7b |
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"phase": 5,
|
||||
"phase": 1,
|
||||
"stage": "execute",
|
||||
"milestone": "v0.2",
|
||||
"phase_role": "final",
|
||||
"phase_role": "execution",
|
||||
"project": "atelier",
|
||||
"attempts": 0,
|
||||
"updated_at": "2026-08-05T02:40:00Z",
|
||||
"updated_at": "2026-08-05T01:40:00Z",
|
||||
"milestone_complete": false
|
||||
}
|
||||
@@ -64,30 +64,30 @@ All 35 requirements covered. 8 core principles, 11 domains, 110 domain principle
|
||||
|
||||
| REQ-ID | Requirement | Priority | Phase | Status |
|
||||
|--------|-------------|----------|-------|--------|
|
||||
| ATELIER-36 | `domains/infrastructure-as-code/first-principles.md` — 10 IaC principles (P1–P10) | P0 | 1 | covered |
|
||||
| ATELIER-37 | `domains/infrastructure-as-code/terraform.md` — Terraform derived doc | P1 | 1 | covered |
|
||||
| ATELIER-38 | `domains/infrastructure-as-code/opentofu.md` — OpenTofu derived doc | P1 | 1 | covered |
|
||||
| ATELIER-39 | `domains/infrastructure-as-code/state.md` — IaC state management derived doc | P1 | 1 | covered |
|
||||
| ATELIER-40 | `domains/infrastructure-as-code/modules.md` — IaC module composition derived doc | P1 | 1 | covered |
|
||||
| ATELIER-41 | `domains/kubernetes/first-principles.md` — 10 k8s principles (P1–P10) | P0 | 2 | covered |
|
||||
| ATELIER-42 | `domains/kubernetes/workloads.md` — workloads derived doc | P1 | 2 | covered |
|
||||
| ATELIER-43 | `domains/kubernetes/networking.md` — networking derived doc | P1 | 2 | covered |
|
||||
| ATELIER-44 | `domains/kubernetes/storage.md` — storage derived doc | P1 | 2 | covered |
|
||||
| ATELIER-45 | `domains/kubernetes/rbac.md` — RBAC derived doc incl. Pod Security Standards/Admission (cross-link security/authorization) | P1 | 2 | covered |
|
||||
| ATELIER-46 | `domains/kubernetes/helm.md` — Helm derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | covered |
|
||||
| ATELIER-47 | `domains/kubernetes/kustomize.md` — Kustomize derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | covered |
|
||||
| ATELIER-48 | Extend `matrix/principles-matrix.md` with 20 new P-rules → core C-rule mappings (10 per new domain; review check: row count per domain = 10, each row ≥1 C-rule) | P0 | 3 | covered |
|
||||
| ATELIER-49 | Extend `matrix/domain-coverage.md` with infrastructure-as-code + kubernetes (row schema: domain, P-count, derived-doc-count, manifest-listed, status) | P1 | 3 | covered |
|
||||
| ATELIER-50 | Extend `review/agent-checklist.md` with IaC + k8s trigger sections | P1 | 3 | covered |
|
||||
| ATELIER-51 | Extend `review/anti-patterns.md` with IaC + k8s violations incl. orphaned P-rule + deployable example artifact | P1 | 3 | covered |
|
||||
| ATELIER-52 | Update `MANIFEST.md` to list all new v0.2 documents (manifest authoritative) | P0 | 3 | covered |
|
||||
| ATELIER-53 | `examples/good/terraform-module.md` — good IaC example (markdown with fenced HCL only; no standalone .tf) | P2 | 4 | covered |
|
||||
| ATELIER-54 | `examples/good/k8s-deployment.md` — good k8s example (markdown with fenced YAML only; no standalone .yaml) | P2 | 4 | covered |
|
||||
| ATELIER-55 | `examples/bad/terraform-unlocked-state.md` + `examples/bad/k8s-bare-pod-no-resources.md` — 2 named bad examples (each cites the P-rule breached) | P2 | 4 | covered |
|
||||
| ATELIER-56 | Cross-links from new domains to existing devops/security/observability/data domains (review check: every new derived doc ≥1 outbound cross-link to a MANIFEST-listed doc) | P1 | 4 | covered |
|
||||
| ATELIER-36 | `domains/infrastructure-as-code/first-principles.md` — 10 IaC principles (P1–P10) | P0 | 1 | pending |
|
||||
| ATELIER-37 | `domains/infrastructure-as-code/terraform.md` — Terraform derived doc | P1 | 1 | pending |
|
||||
| ATELIER-38 | `domains/infrastructure-as-code/opentofu.md` — OpenTofu derived doc | P1 | 1 | pending |
|
||||
| ATELIER-39 | `domains/infrastructure-as-code/state.md` — IaC state management derived doc | P1 | 1 | pending |
|
||||
| ATELIER-40 | `domains/infrastructure-as-code/modules.md` — IaC module composition derived doc | P1 | 1 | pending |
|
||||
| ATELIER-41 | `domains/kubernetes/first-principles.md` — 10 k8s principles (P1–P10) | P0 | 2 | pending |
|
||||
| ATELIER-42 | `domains/kubernetes/workloads.md` — workloads derived doc | P1 | 2 | pending |
|
||||
| ATELIER-43 | `domains/kubernetes/networking.md` — networking derived doc | P1 | 2 | pending |
|
||||
| ATELIER-44 | `domains/kubernetes/storage.md` — storage derived doc | P1 | 2 | pending |
|
||||
| ATELIER-45 | `domains/kubernetes/rbac.md` — RBAC derived doc incl. Pod Security Standards/Admission (cross-link security/authorization) | P1 | 2 | pending |
|
||||
| ATELIER-46 | `domains/kubernetes/helm.md` — Helm derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | pending |
|
||||
| ATELIER-47 | `domains/kubernetes/kustomize.md` — Kustomize derived doc (with Helm vs Kustomize decision matrix) | P1 | 2 | pending |
|
||||
| ATELIER-48 | Extend `matrix/principles-matrix.md` with 20 new P-rules → core C-rule mappings (10 per new domain; review check: row count per domain = 10, each row ≥1 C-rule) | P0 | 3 | pending |
|
||||
| ATELIER-49 | Extend `matrix/domain-coverage.md` with infrastructure-as-code + kubernetes (row schema: domain, P-count, derived-doc-count, manifest-listed, status) | P1 | 3 | pending |
|
||||
| ATELIER-50 | Extend `review/agent-checklist.md` with IaC + k8s trigger sections | P1 | 3 | pending |
|
||||
| ATELIER-51 | Extend `review/anti-patterns.md` with IaC + k8s violations incl. orphaned P-rule + deployable example artifact | P1 | 3 | pending |
|
||||
| ATELIER-52 | Update `MANIFEST.md` to list all new v0.2 documents (manifest authoritative) | P0 | 3 | pending |
|
||||
| ATELIER-53 | `examples/good/terraform-module.md` — good IaC example (markdown with fenced HCL only; no standalone .tf) | P2 | 4 | pending |
|
||||
| ATELIER-54 | `examples/good/k8s-deployment.md` — good k8s example (markdown with fenced YAML only; no standalone .yaml) | P2 | 4 | pending |
|
||||
| ATELIER-55 | `examples/bad/terraform-unlocked-state.md` + `examples/bad/k8s-bare-pod-no-resources.md` — 2 named bad examples (each cites the P-rule breached) | P2 | 4 | pending |
|
||||
| ATELIER-56 | Cross-links from new domains to existing devops/security/observability/data domains (review check: every new derived doc ≥1 outbound cross-link to a MANIFEST-listed doc) | P1 | 4 | pending |
|
||||
| ATELIER-57 | Final review passes (all v0.2 phases reviewed, audit clean) | P0 | 5 | pending |
|
||||
| ATELIER-58 | Milestone v0.2 released (tag v0.1.5, merged to main) | P0 | 5 | pending |
|
||||
| ATELIER-59 | Extend `review/peer-review-checklist.md` with IaC + k8s sections (parity with agent-checklist) | P1 | 3 | covered |
|
||||
| ATELIER-59 | Extend `review/peer-review-checklist.md` with IaC + k8s sections (parity with agent-checklist) | P1 | 3 | pending |
|
||||
|
||||
## v0.2 Traceability Matrix
|
||||
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
# Atelier — v0.2 Final Review + Audit (P5)
|
||||
|
||||
> Generated during final phase P5 (REVIEW + AUDIT) of milestone v0.2. Per run.md FINAL PHASE.
|
||||
|
||||
## Review (multi-persona, ci-code-reviewer)
|
||||
|
||||
**Scope:** all v0.2 changes (32 files, +1787/-34 lines), all commits `main..atelier/phase/05-final-review-ship`.
|
||||
|
||||
### P0 checks (all pass)
|
||||
|
||||
1. ✅ IaC first-principles: exactly 10 P-rules (P1–P10)
|
||||
2. ✅ K8s first-principles: exactly 10 P-rules (P1–P10)
|
||||
3. ✅ Matrix IaC section: 10 rows, each ≥1 valid C-rule, no orphans
|
||||
4. ✅ Matrix K8s section: 10 rows, each ≥1 valid C-rule, no orphans
|
||||
5. ✅ Every P-rule name in first-principles matches its matrix row
|
||||
6. ✅ All required files exist (ATELIER-36..56, 59): 2 first-principles + 4 IaC derived + 6 k8s derived + 4 examples + matrix/manifest/review extensions
|
||||
7. ✅ MANIFEST lists both new domains with all derived docs — no manifest drift
|
||||
8. ✅ No standalone .tf/.yaml/.yml files — "no runtime code" constraint preserved (all code is fenced in .md)
|
||||
9. ✅ No hardcoded real secrets — bad examples use AWS doc placeholders and `hunter2`; good examples use `registry.example.com` + Secret refs
|
||||
10. ✅ anti-patterns.md covers secrets-in-HCL and cluster-admin
|
||||
11. ✅ rbac.md covers Pod Security Standards + Admission
|
||||
12. ✅ All cross-link targets resolve to MANIFEST-listed docs
|
||||
|
||||
**Verdict: PASS — No P0 issues.**
|
||||
|
||||
### P1+ issues (flagged, then fixed in this phase per run.md)
|
||||
|
||||
| ID | Severity | Issue | Fix applied |
|
||||
|----|----------|-------|-------------|
|
||||
| REV-1 | P1 | 5 derived docs missing cross-domain links (terraform, state, modules, workloads, networking) | Added cross-links to `domains/security/secrets.md`, `domains/devops/first-principles.md`, `domains/observability/metrics.md`, `domains/security/authorization.md` |
|
||||
| REV-2 | P2 | networking.md "Dual-Stack (P4 Locality)" — wrong P-rule label | Corrected to "(C4 Locality)" |
|
||||
| REV-3 | P2 | domain-coverage.md "Concurrency broadest (7)" stale — IaC + k8s also 7 | Updated to "Concurrency, IaC, Kubernetes tied (7 each)" |
|
||||
|
||||
All P1+ issues fixed in commit `87daca3`. No loop back to EXECUTE (per run.md final-phase rule).
|
||||
|
||||
## Audit (lead-developer)
|
||||
|
||||
### 1. Reconstruction test
|
||||
- git log `main..atelier/phase/05-final-review-ship` shows 10 v0.2 commits (P00 complete, P01 execute/verify/complete/status, P02, P03, P04, P05 review fix).
|
||||
- REQUIREMENTS.md status (covered) matches shipped phases: ATELIER-36..56, 59 all `covered`; ATELIER-57, 58 `pending` (final phase, completed at ship).
|
||||
- ROADMAP.md phase statuses match: P0–P4 `complete`, P5 `pending` (→ complete at ship).
|
||||
- ✅ Reconstruction passes.
|
||||
|
||||
### 2. Branch hygiene
|
||||
- Active branches: `atelier/milestone/v0.2-iac-k8s`, `atelier/phase/05-final-review-ship`.
|
||||
- All execution phase branches (00–04) deleted after ship. ✅
|
||||
|
||||
### 3. Commit discipline
|
||||
- All 10 v0.2 commits contain `---ci---` blocks with project, phase, milestone, status, requirements. ✅
|
||||
|
||||
### 4. File discipline
|
||||
- `.ciagent/atelier/` contains all 8 required files: PROJECT, ROADMAP, REQUIREMENTS, ARCHITECTURE, PERSONAS, PLAN, RESEARCH, CLARIFY. ✅
|
||||
- (v0.1 legacy AUDIT-P2.md, REVIEW-P7.md also present — not removed, harmless.)
|
||||
|
||||
### 5. Tag sequence
|
||||
- v0.0.0–v0.0.7 (milestone v0.1) → v0.1.0–v0.1.4 (milestone v0.2 phases 0–4).
|
||||
- All v0.1.x strictly > v0.0.7. All v0.1.x strictly increasing. ✅
|
||||
- Final phase tag will be v0.1.5 (next patch, IS the v0.2 milestone release per NFR rule).
|
||||
|
||||
### 6. Manifest discipline
|
||||
- All 12 new domain docs (infrastructure-as-code/*, kubernetes/*) listed in MANIFEST.md Domains table. ✅
|
||||
- matrix, review (agent-checklist, peer-review-checklist, anti-patterns) all listed in Cross-Cutting. ✅
|
||||
|
||||
**Audit verdict: CLEAN — no critical issues.**
|
||||
|
||||
## Conclusion
|
||||
|
||||
Review PASS (no P0, all P1+ fixed). Audit CLEAN. Milestone v0.2 is ready to ship as v0.1.5.
|
||||
@@ -47,11 +47,11 @@ NFR milestone: no separate minor tag. The final patch (v0.0.7) IS the v0.1 deliv
|
||||
|
||||
| Phase | Name | Type | Status | Key Deliverables |
|
||||
|-------|------|------|--------|------------------|
|
||||
| 0 | Pre-Execution | docs | complete | Spec, clarify, research, ideate, plan, PERSONAS.md (adds platform-engineer persona) |
|
||||
| 1 | Infrastructure as Code Domain | docs | complete | domains/infrastructure-as-code/{first-principles, terraform, opentofu, state, modules}.md |
|
||||
| 2 | Kubernetes Domain | docs | complete | domains/kubernetes/{first-principles, workloads, networking, storage, rbac, helm, kustomize}.md |
|
||||
| 3 | Matrix + Review Integration | docs | complete | matrix/principles-matrix.md (20 new mappings), matrix/domain-coverage.md, review/{agent-checklist, peer-review-checklist, anti-patterns}.md, MANIFEST.md |
|
||||
| 4 | Examples + Cross-Links | docs | complete | examples/good/{terraform-module, k8s-deployment}.md, examples/bad/{terraform-unlocked-state, k8s-bare-pod-no-resources}.md, cross-links to devops/security/observability/data |
|
||||
| 0 | Pre-Execution | docs | in_progress | Spec, clarify, research, ideate, plan, PERSONAS.md (adds platform-engineer persona) |
|
||||
| 1 | Infrastructure as Code Domain | docs | pending | domains/infrastructure-as-code/{first-principles, terraform, opentofu, state, modules}.md |
|
||||
| 2 | Kubernetes Domain | docs | pending | domains/kubernetes/{first-principles, workloads, networking, storage, rbac, helm, kustomize}.md |
|
||||
| 3 | Matrix + Review Integration | docs | pending | matrix/principles-matrix.md (20 new mappings), matrix/domain-coverage.md, review/{agent-checklist, peer-review-checklist, anti-patterns}.md, MANIFEST.md |
|
||||
| 4 | Examples + Cross-Links | docs | pending | examples/good/{terraform-module, k8s-deployment}.md, examples/bad/{terraform-unlocked-state, k8s-bare-pod-no-resources}.md, cross-links to devops/security/observability/data |
|
||||
| 5 | Final Review + Ship | docs | pending | Review passed, audit clean, milestone merged to main, tag v0.1.5 |
|
||||
|
||||
## v0.2 Phase Tag Mapping
|
||||
|
||||
+3
-7
@@ -34,15 +34,11 @@
|
||||
| Documentation | ✓ | doc-templates |
|
||||
| Concurrency | ✓ | patterns |
|
||||
| DevOps | ✓ | ci-cd, environments |
|
||||
| Infrastructure as Code | ✓ | terraform, opentofu, state, modules |
|
||||
| Kubernetes | ✓ | workloads, networking, storage, rbac, helm, kustomize |
|
||||
|
||||
## Cross-Cutting
|
||||
|
||||
| Document | Purpose |
|
||||
|-----------------------------------|----------------------------------|
|
||||
| `matrix/principles-matrix.md` | Maps domain → core principles (13 domains, 130 P-rules post-v0.2) |
|
||||
| `matrix/domain-coverage.md` | Maps core → domains; per-domain coverage |
|
||||
| `review/agent-checklist.md` | Pre-completion agent checklist (incl. IaC + k8s triggers) |
|
||||
| `review/peer-review-checklist.md` | Human peer-review checklist (incl. IaC + k8s sections) |
|
||||
| `review/anti-patterns.md` | Catalog of violations (incl. IaC + k8s + chaos anti-patterns) |
|
||||
| `matrix/principles-matrix.md` | Maps domain → core principles |
|
||||
| `review/agent-checklist.md` | Pre-completion agent checklist |
|
||||
| `review/anti-patterns.md` | Catalog of violations |
|
||||
@@ -7,7 +7,6 @@
|
||||
- A module is the unit of reuse, review, and versioning in IaC. It encapsulates a repeatable pattern behind a typed interface.
|
||||
- Composition — building large from small — is the IaC expression of core C6 Composability. Without modules, every stack is a one-off; with modules, a stack is an assembly of reviewed parts.
|
||||
- A good module has one job (a VPC, a database, a load balancer), a small typed surface, and no hidden side effects.
|
||||
- A versioned module is the IaC expression of `domains/devops/first-principles.md` P1 (Reproducibility) and P6 (Configuration as Code): a module pins a reusable, rebuildable pattern that any environment can call.
|
||||
|
||||
## Module Structure (P1 Declarative Intent, C2 Clarity)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
| Postgres | TX | DB encryption | DBA-owned infra | Row-level locking |
|
||||
|
||||
- Pick one backend per environment family. Mixing backends across environments fragments operational knowledge (C4 Locality).
|
||||
- The backend config is part of the configuration, not a runtime secret. Credentials for the backend are runtime secrets. The state file itself is a secret-bearing artifact — treat it per `domains/security/secrets.md`: encrypt at rest, restrict access, never commit it.
|
||||
- The backend config is part of the configuration, not a runtime secret. Credentials for the backend are runtime secrets.
|
||||
|
||||
## State Isolation per Environment (P4 Plan Before Apply, C4 Locality)
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
## Secrets (P10 Secrets Never in Code)
|
||||
|
||||
- Secrets via provider data sources (`aws_secretsmanager_secret_version`), environment variables, or a dedicated secrets provider. Never a literal string in a resource block.
|
||||
- State may contain plaintext secrets if a resource attribute is sensitive. Mark attributes `sensitive = true` to keep them out of plan output; use a backend that encrypts state at rest (see `state.md`). This is the IaC angle on `domains/security/secrets.md` — secret hygiene is non-tradeable.
|
||||
- State may contain plaintext secrets if a resource attribute is sensitive. Mark attributes `sensitive = true` to keep them out of plan output; use a backend that encrypts state at rest (see `state.md`).
|
||||
|
||||
## What Violates Terraform Discipline
|
||||
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# Kubernetes — First Principles
|
||||
|
||||
## 1. The Principles
|
||||
|
||||
### P1. Declarative Desired State
|
||||
You declare the desired state; controllers reconcile current →
|
||||
desired. Imperative `kubectl` is for inspection and incident
|
||||
response, not for the steady state. The cluster's job is to make
|
||||
reality match the manifest.
|
||||
|
||||
### P2. Pods are Mortal
|
||||
A pod is born, runs, and dies. Never assume its identity, its IP,
|
||||
or its lifetime. Use controllers (Deployment, StatefulSet,
|
||||
DaemonSet), not bare pods. A bare pod has no recovery, no
|
||||
scaling, no rollback.
|
||||
|
||||
### P3. Labels Select
|
||||
Labels and selectors are the join mechanism of the platform —
|
||||
workloads to services, policies to workloads, workloads to nodes.
|
||||
Label by intent (`app`, `tier`, `env`), not by infrastructure
|
||||
(`node-3`, `ip-10.0.0.5`). Selectors compose; ad-hoc naming does
|
||||
not.
|
||||
|
||||
### P4. Requests and Limits are Contracts
|
||||
Resource requests drive scheduling; limits drive quality of
|
||||
service. A workload with no requests is `BestEffort` — first
|
||||
evicted under pressure. A workload with no limits is unbounded.
|
||||
Specifying requests is not optional in production.
|
||||
|
||||
### P5. Probes Drive Health
|
||||
Liveness, readiness, and startup probes are how the platform
|
||||
sees your workload. Without a readiness probe, traffic routes to
|
||||
a pod that is not ready. Without a liveness probe, a wedged
|
||||
container runs forever. The platform cannot heal what it cannot
|
||||
see.
|
||||
|
||||
### P6. Namespaces Bound Blast Radius
|
||||
Namespaces are the unit of quota, RBAC, network policy, and
|
||||
cleanup. A namespace is the boundary of "this thing and all its
|
||||
parts." Default namespace is for nothing in production; every
|
||||
workload gets a named namespace sized to its blast radius.
|
||||
|
||||
### P7. RBAC by Intent, Not Identity
|
||||
Bind roles to service accounts by the workload's purpose, not to
|
||||
user identities. Least privilege: the role grants the minimum
|
||||
the workload needs. `cluster-admin` is a smell, not a shortcut.
|
||||
Cross-link `domains/security/authorization.md`.
|
||||
|
||||
### P8. Storage is Explicit
|
||||
Storage is ephemeral by default. Persistence requires a
|
||||
PVC, a StorageClass, and a reclaim policy decision. `emptyDir`
|
||||
for state that must survive is a bug. The choice of
|
||||
reclaim policy (`Retain`, `Delete`) is a data-safety decision,
|
||||
not a default.
|
||||
|
||||
### P9. Config and Secrets are Separate
|
||||
ConfigMaps are non-sensitive configuration; Secrets are
|
||||
sensitive configuration. Both are injected at runtime, never
|
||||
baked into the image. A configuration change should not require
|
||||
a rebuild; a secret rotation should not require a redeploy of the
|
||||
image. Cross-link `domains/security/secrets.md`.
|
||||
|
||||
### P10. Roll Forward, Roll Back
|
||||
Every Deployment has a rolling update strategy and a rollout
|
||||
history. A deploy is reversible: `kubectl rollout undo`. A deploy
|
||||
without a tested rollback is a prototype. Canary and blue-green
|
||||
are the k8s expression of `domains/devops/P5 Progressive Delivery`.
|
||||
@@ -1,68 +0,0 @@
|
||||
# Helm — Derived Rules
|
||||
|
||||
> Derives from `domains/kubernetes/first-principles.md`. Applies P1, P3, P6. For the Helm-vs-Kustomize decision, see the decision matrix at the end of this doc and in `kustomize.md`.
|
||||
|
||||
## What Helm Is (P6 Modules Compose)
|
||||
|
||||
- Helm is a package manager for Kubernetes. A chart is a versioned package of templated manifests. `helm install` renders the templates against `values.yaml` and applies the result.
|
||||
- A chart encapsulates a reusable deployment (an application, a database, a full stack). It is the k8s analogue of an IaC module — see `domains/infrastructure-as-code/modules.md`.
|
||||
- Charts live in registries (Helm registry via OCI, or the classic chart repos) and are versioned per SemVer.
|
||||
|
||||
## Chart Structure (P1 Declarative Desired State, C2 Clarity)
|
||||
|
||||
- `Chart.yaml` — metadata (name, version, appVersion, dependencies).
|
||||
- `values.yaml` — default inputs; the chart's public interface.
|
||||
- `templates/` — Go-templated manifests. `templates/_helpers.tpl` holds reusable template partials.
|
||||
- `values.schema.json` — optional schema for values, giving type checking on inputs. Use it for published charts.
|
||||
- A chart should have one logical purpose. A chart that deploys an app and a database and an ingress and an observability stack has too many jobs — split it.
|
||||
|
||||
## Values (P5 Version Everything, C2 Clarity)
|
||||
|
||||
- `values.yaml` holds defaults. Override per release: `helm install --set key=value` or `helm install -f my-values.yaml`.
|
||||
- Pin values files in git per environment. A release is reproducible from the chart version + the values file.
|
||||
- Sensitive values do not belong in `values.yaml`. Inject via Secrets (see `rbac.md` P9 and `domains/security/secrets.md`). Some charts accept `existingSecret` to reference a pre-created Secret.
|
||||
|
||||
## Release Management (P5 Version Everything, P10 Roll Forward Roll Back)
|
||||
|
||||
- A release is a named instantiation of a chart. `helm upgrade` applies a new chart version or new values. `helm rollback` reverts to the previous release revision.
|
||||
- `helm history <release>` lists revisions; `helm rollback <release> <revision>` is the rollback. The rollback must be tested like any deploy (P10).
|
||||
- Pin the chart version: `helm install --version 1.2.3`. Never `--version latest` in production — unversioned charts drift (same anti-pattern as unpinned IaC modules).
|
||||
|
||||
## Templating Discipline (P1 Declarative Desired State, C2 Clarity)
|
||||
|
||||
- Templates render to valid manifests. The chart author's job is that the rendered output is correct k8s, not that the template is clever.
|
||||
- Keep `templates/` readable. Heavy logic belongs in `_helpers.tpl` or in a values structure that the template merely projects.
|
||||
- `helm template` renders to stdout without applying — use it to review what a release will create before installing it.
|
||||
|
||||
## Registries (P5 Version Everything)
|
||||
|
||||
- OCI registries are the modern chart distribution (same registry as container images, charts as OCI artifacts). Classic chart repos are legacy.
|
||||
- Pull from a pinned registry reference: `oci://registry/chart:1.2.3`. The digest + tag is the version.
|
||||
|
||||
## Helm vs Kustomize — Decision Matrix (IDEATE-10)
|
||||
|
||||
| Axis | Helm | Kustomize |
|
||||
|------|------|----------|
|
||||
| Mechanism | Templating (Go templates) | Overlays (base + patches) |
|
||||
| Reuse unit | Chart (versioned package) | Base directory (kustomization.yaml) |
|
||||
| Distribution | Registry (OCI, chart repo) | Git (base dir in a repo) |
|
||||
| Values | `values.yaml` + overrides | `kustomization.yaml` + patches |
|
||||
| Release mgmt | `helm` tracks releases, history, rollback | None native — apply with `kubectl apply -k` |
|
||||
| Learning curve | Template language to learn | YAML patching, no DSL |
|
||||
| Blast radius | One chart, many resources, templated | One base, many overlays, patched |
|
||||
| Best for | Off-the-shelf apps, packaged stacks, multi-env via values | Internal apps, patching upstream manifests, env-specific deltas |
|
||||
| Watch out for | Template complexity, `latest` chart drift, secrets in values | No release tracking, manual rollback, patch sprawl |
|
||||
|
||||
- Use Helm when you distribute a reusable app or consume third-party charts. Use Kustomize when you patch existing manifests or keep env deltas in one repo.
|
||||
- Mixing both is fine and common: Helm for the packaged parts, Kustomize for the last-mile per-env patching. Do not fight the tool that fits the job.
|
||||
|
||||
## What Violates Helm Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `helm install --version latest` in prod | P5 Version Everything |
|
||||
| Secrets in `values.yaml` | P9 Config and Secrets are Separate, security |
|
||||
| Chart with 15 subcharts doing unrelated things | P6 Modules Compose (split it) |
|
||||
| No `values.schema.json` on a published chart | C2 Clarity |
|
||||
| `helm upgrade` without reviewing `helm template` output | P1 Declarative Desired State, P4 Plan Before Apply |
|
||||
| Untested `helm rollback` | P10 Roll Forward Roll Back |
|
||||
@@ -1,63 +0,0 @@
|
||||
# Kustomize — Derived Rules
|
||||
|
||||
> Derives from `domains/kubernetes/first-principles.md`. Applies P1, P3, P6. For the Helm-vs-Kustomize decision, see the decision matrix at the end of this doc and in `helm.md`.
|
||||
|
||||
## What Kustomize Is (P1 Declarative Desired State)
|
||||
|
||||
- Kustomize customizes manifests without templating. A base directory holds the canonical manifests; overlays hold the deltas. The result is plain YAML applied with `kubectl apply -k`.
|
||||
- No DSL, no template language, no rendering step hidden from `kubectl`. The patch is a YAML file; the result is inspectable.
|
||||
- Kustomize is built into `kubectl` (`kubectl apply -k`, `kubectl diff -k`). No separate runtime is required to apply.
|
||||
|
||||
## Base and Overlays (P6 Namespaces Bound Blast Radius, C4 Locality)
|
||||
|
||||
- A `kustomization.yaml` in a base directory lists the resources (Deployment, Service, etc.) the application needs. It is the canonical manifest.
|
||||
- An overlay is a directory with its own `kustomization.yaml` that references the base (`resources: - ../../base`) and applies patches or additional resources.
|
||||
- Typical structure: `base/`, `overlays/dev/`, `overlays/staging/`, `overlays/prod/`. The overlay is the environment axis; the base is the shared truth.
|
||||
|
||||
## Patches (P1 Declarative Desired State, C2 Clarity)
|
||||
|
||||
- Strategic merge patches — a YAML document that overrides matching fields. Simple for single-resource changes.
|
||||
- JSON patches (RFC 6902) — precise operations (`add`, `replace`, `remove`) on a path. Use when a strategic merge is ambiguous (e.g., list operations).
|
||||
- `patches` field (modern) takes a list of patch files with targets, replacing the older `patchesStrategicMerge` and `patchesJson6902`. Prefer it.
|
||||
- A patch is a delta. It is reviewed as "what changes from base," which is exactly the diff a reviewer wants to see.
|
||||
|
||||
## Generators and Transformers (P3 Labels Select)
|
||||
|
||||
- `configMapGenerator` and `secretGenerator` create ConfigMaps and Secrets from files or literals, with content hashes in the names. A change to the source file changes the hash, which changes the name, which rolls the workload. This is the kustomize pattern for "config change = redeploy."
|
||||
- `namePrefix`, `nameSuffix`, and `namespace` transformers rewrite names across the base. Use for namespace isolation (P6) or to run the same base multiple times in one cluster without collisions.
|
||||
- `commonLabels` and `commonAnnotations` stamp labels onto everything in the base — the kustomize-native way to enforce the labelling discipline of P3.
|
||||
|
||||
## No Release Tracking (P10 Roll Forward Roll Back)
|
||||
|
||||
- Kustomize has no release object, no history, no built-in rollback. `kubectl apply -k` is a one-shot apply; the previous state is in git, not in a Helm-style release record.
|
||||
- Rollback is `git revert` + `kubectl apply -k`. The git history IS the release history. This is fine — and arguably cleaner — but it means rollback is a git operation, not a `helm rollback` command.
|
||||
- Use a GitOps tool (ArgoCD, Flux) on top of Kustomize for automated reconciliation and rollback tracking. The tool watches the git ref; rollback is a git revert.
|
||||
|
||||
## Helm vs Kustomize — Decision Matrix (IDEATE-10)
|
||||
|
||||
| Axis | Kustomize | Helm |
|
||||
|------|----------|------|
|
||||
| Mechanism | Overlays (base + patches) | Templating (Go templates) |
|
||||
| Reuse unit | Base directory (kustomization.yaml) | Chart (versioned package) |
|
||||
| Distribution | Git (base dir in a repo) | Registry (OCI, chart repo) |
|
||||
| Values | `kustomization.yaml` + patches | `values.yaml` + overrides |
|
||||
| Release mgmt | None native — `kubectl apply -k` | `helm` tracks releases, history, rollback |
|
||||
| Learning curve | YAML patching, no DSL | Template language to learn |
|
||||
| Blast radius | One base, many overlays, patched | One chart, many resources, templated |
|
||||
| Best for | Internal apps, patching upstream manifests, env-specific deltas | Off-the-shelf apps, packaged stacks, multi-env via values |
|
||||
| Watch out for | No release tracking, manual rollback, patch sprawl | Template complexity, `latest` chart drift, secrets in values |
|
||||
|
||||
- Use Kustomize when you patch existing manifests or keep env deltas in one repo. Use Helm when you distribute a reusable app or consume third-party charts.
|
||||
- Mixing both is fine and common: Kustomize for the internal apps, Helm for the packaged parts. The decision is per-workload, not per-cluster.
|
||||
- `commonLabels` is the kustomize-native enforcement of P3 (Labels Select); see `domains/devops/first-principles.md` P6 (Configuration as Code) for the upstream principle that the rendered manifest — not a console click — is the source of truth.
|
||||
|
||||
## What Violates Kustomize Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Duplicated base instead of an overlay | P6 Modules Compose (use an overlay) |
|
||||
| Patch that overrides most of the base | C3 Simplicity (the base is wrong — fix the base) |
|
||||
| No `commonLabels` on a multi-team base | P3 Labels Select |
|
||||
| No git-based rollback strategy | P10 Roll Forward Roll Back |
|
||||
| Hand-edited rendered output instead of `apply -k` | P1 Declarative Desired State |
|
||||
| Patch sprawl (10 overlays each patching 15 fields) | C3 Simplicity (refactor the base) |
|
||||
@@ -1,49 +0,0 @@
|
||||
# Networking — Derived Rules
|
||||
|
||||
> Derives from `domains/kubernetes/first-principles.md`. Covers Service, Ingress, Gateway API, EndpointSlices, NetworkPolicy, and DNS. Applies P1, P3, P6.
|
||||
|
||||
## The Service (P3 Labels Select)
|
||||
|
||||
- A Service routes traffic to pods selected by a label selector. The selector is the join between the network abstraction and the workloads.
|
||||
- Service types: `ClusterIP` (in-cluster only, default), `NodePort` (exposed on every node's IP at a fixed port), `LoadBalancer` (cloud-managed LB points to the Service). Default to `ClusterIP`; expose only what must be exposed.
|
||||
- A Service fronts a Deployment (or other controller), never a bare pod. The controller keeps pods available; the Service routes to whichever are ready (per the readiness probe — see `workloads.md`).
|
||||
|
||||
## EndpointSlices (P3 Labels Select, P5 Probes Drive Health)
|
||||
|
||||
- An EndpointSlice lists the pod IPs currently backing a Service. Only pods passing their readiness probe appear.
|
||||
- The Service routes by EndpointSlice, not by selector directly. A pod with the right labels but a failed readiness probe is not in the Service.
|
||||
|
||||
## Ingress and Gateway API (P6 Namespaces Bound Blast Radius)
|
||||
|
||||
- Ingress routes HTTP/HTTPS traffic from outside the cluster to Services. It is L7 routing by host and path.
|
||||
- Gateway API is the successor to Ingress: more expressive (TCP, UDP, TLS passthrough), role-oriented (GatewayClass → Gateway → Route), and cross-platform. Prefer Gateway API for new L7 needs.
|
||||
- Both Ingress and Gateway API are implemented by a controller (nginx-ingress, Traefik, Istio, Envoy Gateway). Pick one; mixing ingress controllers in a cluster is operational debt.
|
||||
|
||||
## NetworkPolicy (P6 Namespaces Bound Blast Radius, P7 RBAC by Intent)
|
||||
|
||||
- A NetworkPolicy is a firewall rule for pods. Default-deny ingress; allow by namespace and pod selector.
|
||||
- Without a default-deny NetworkPolicy, every pod can reach every other pod. In production, default-deny is the baseline; allows are the exceptions.
|
||||
- NetworkPolicy is the network-layer expression of zero-trust authorization — see `domains/security/authorization.md`. RBAC (see `rbac.md`) governs the API; NetworkPolicy governs the network; together they bound blast radius (P6).
|
||||
- NetworkPolicy is enforced by the CNI plugin (Calico, Cilium, etc.). A NetworkPolicy with no supporting CNI is a no-op. Verify the CNI enforces before relying on it.
|
||||
|
||||
## DNS (P3 Labels Select)
|
||||
|
||||
- Every Service gets a DNS record: `<service>.<namespace>.svc.cluster.local`. Pods get `pod-ip-address.<namespace>.pod.cluster.local` (with dots replaced).
|
||||
- Headless Services (`clusterIP: None`) resolve directly to pod IPs — use for StatefulSet peer discovery (`<statefulset>-0.<service>`).
|
||||
- DNS is how workloads find each other without hardcoded IPs. Use the DNS name, not the ClusterIP.
|
||||
|
||||
## Dual-Stack (C4 Locality)
|
||||
|
||||
- IPv4/IPv6 dual-stack is opt-in per cluster. Services can be single-stack or dual-stack per Service.
|
||||
- Decide at cluster creation. Migrating a single-stack cluster to dual-stack is disruptive and rarely worth it.
|
||||
|
||||
## What Violates Networking Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `LoadBalancer` on an internal-only Service | P6 Namespaces Bound Blast Radius |
|
||||
| No default-deny NetworkPolicy | P6 Namespaces Bound Blast Radius, P7 RBAC by Intent |
|
||||
| Hardcoded pod IP in config | P3 Labels Select (use DNS) |
|
||||
| Service pointing at a bare pod | P3 Labels Select (point at a controller) |
|
||||
| Multiple ingress controllers in one cluster | C3 Simplicity (operational debt) |
|
||||
| No readiness probe on a Service-backed workload | P5 Probes Drive Health (empty EndpointSlices) |
|
||||
@@ -1,45 +0,0 @@
|
||||
# RBAC and Pod Security — Derived Rules
|
||||
|
||||
> Derives from `domains/kubernetes/first-principles.md`. P7 (RBAC by Intent, Not Identity) lives here. Cross-link `domains/security/authorization.md` for the general authorization principles and `domains/security/secrets.md` for secret handling.
|
||||
|
||||
## RBAC Objects (P7 RBAC by Intent, Not Identity)
|
||||
|
||||
- **Role** — permissions within a namespace (verb on resource). **ClusterRole** — permissions cluster-wide or usable across namespaces.
|
||||
- **RoleBinding** — binds a Role to a subject (ServiceAccount, User, Group) within a namespace. **ClusterRoleBinding** — binds a ClusterRole cluster-wide.
|
||||
- Prefer Role + RoleBinding per namespace over ClusterRole + ClusterRoleBinding. Cluster-level is the broad axe; namespace-level is the scalpel.
|
||||
|
||||
## Bind to Service Accounts, Not Users (P7 RBAC by Intent, Not Identity)
|
||||
|
||||
- A workload authenticates as a ServiceAccount. Bind the Role to the ServiceAccount, scoped to the workload's namespace.
|
||||
- The Role encodes the workload's intent: "this workload reads ConfigMaps in this namespace." Not "this user is an admin."
|
||||
- One ServiceAccount per workload (or workload family). Do not reuse the `default` ServiceAccount for production workloads; it is a shared identity.
|
||||
|
||||
## Least Privilege (P7 RBAC by Intent, C1 Correctness via security)
|
||||
|
||||
- Grant the minimum verbs on the minimum resources. `get, list, watch` on `pods` is fine for a monitoring sidecar; `*` on `*` is not.
|
||||
- `cluster-admin` is a smell. If a workload "needs" `cluster-admin`, the workload is either doing something it should not, or it is a cluster operator that should be reviewed as such.
|
||||
- Audit `ClusterRoleBindings` regularly. They are the broadest grant in the system and the easiest to leave behind.
|
||||
|
||||
## Pod Security Standards and Admission (P7 RBAC by Intent, security)
|
||||
|
||||
- Pod Security Standards (PSS) define three profiles: `privileged` (unrestricted), `baseline` (some restrictions), `restricted` (hardened).
|
||||
- Pod Security Admission (built-in) enforces a PSS profile per namespace via labels: `pod-security.kubernetes.io/enforce: restricted`. It replaces the deprecated PodSecurityPolicy.
|
||||
- Map namespaces to profiles: `restricted` for prod workloads, `baseline` for most, `privileged` only for system add-ons (CNI, CSI, node agents) that need it. A workload in `privileged` is a security event, not a default.
|
||||
|
||||
## Service Accounts and Token Automation (P9 Config and Secrets are Separate)
|
||||
|
||||
- ServiceAccount tokens are auto-mounted into pods unless `automountServiceAccountToken: false`. For workloads that do not call the API, disable auto-mount.
|
||||
- Long-lived ServiceAccount tokens are deprecated. Use projected tokens (bound to the pod, time-limited) via `TokenRequest`.
|
||||
- A workload that does not need API access should not have a token. A workload that needs API access should have a Role scoped to its intent.
|
||||
|
||||
## What Violates RBAC Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `cluster-admin` bound to a workload | P7 RBAC by Intent, Not Identity |
|
||||
| Reused `default` ServiceAccount for prod | P7 RBAC by Intent, Not Identity |
|
||||
| `automountServiceAccountToken: true` on a non-API workload | P9 Config and Secrets are Separate |
|
||||
| `privileged` PSS on an application namespace | P7 RBAC by Intent, security |
|
||||
| ClusterRoleBinding where a RoleBinding would suffice | P6 Namespaces Bound Blast Radius, P7 |
|
||||
| Long-lived static token instead of projected | P9 Config and Secrets are Separate |
|
||||
| Leftover ClusterRoleBindings after a workload is removed | P7 RBAC by Intent (audit) |
|
||||
@@ -1,59 +0,0 @@
|
||||
# Storage — Derived Rules
|
||||
|
||||
> Derives from `domains/kubernetes/first-principles.md`. P8 (Storage is Explicit) lives here. Covers Volumes, PV/PVC, StorageClass, CSI, snapshots, and reclaim policies. Cross-link `domains/data/` for the data-model angle.
|
||||
|
||||
## Ephemeral by Default (P8 Storage is Explicit)
|
||||
|
||||
- A container's filesystem is ephemeral. When the pod dies, the filesystem dies with it. This is the design, not a flaw.
|
||||
- `emptyDir` is an ephemeral volume scoped to the pod's lifetime (survives container restarts within the pod, dies with the pod). It is scratch space, never durable storage.
|
||||
- Any data that must survive a pod restart requires a PersistentVolumeClaim (PVC). The choice of "must survive" is the data-safety decision at the heart of P8.
|
||||
|
||||
## PersistentVolume and PersistentVolumeClaim (P8 Storage is Explicit)
|
||||
|
||||
- A PersistentVolume (PV) is a piece of storage in the cluster. A PersistentVolumeClaim (PVC) is a request for that storage by a workload.
|
||||
- The PV is the resource; the PVC is the consumer. A workload mounts the PVC, not the PV directly.
|
||||
- For StatefulSets, use `volumeClaimTemplates` so each replica gets its own PVC with a stable name (`data-<statefulset>-0`). Do not share one PVC across replicas of a stateful workload.
|
||||
|
||||
## StorageClass and Dynamic Provisioning (P8 Storage is Explicit, P5 Version Everything)
|
||||
|
||||
- A StorageClass describes the "flavour" of storage (e.g., `fast-ssd`, `cold-hdd`, `encrypted`). A PVC names a StorageClass or gets the cluster default.
|
||||
- Dynamic provisioning creates the PV on demand when the PVC is created, via the CSI driver. Manual PV creation is for specific cases (a pre-existing disk, a static NFS export).
|
||||
- Mark a default StorageClass only if the default is safe for all workloads. A fast-but-expensive default can cause cost surprises; a slow default can cause performance surprises.
|
||||
|
||||
## CSI (P5 Version Everything)
|
||||
|
||||
- The Container Storage Interface (CSI) is the standard driver interface. Each storage backend ships a CSI driver. Pin the CSI driver version in the cluster; treat it as infrastructure.
|
||||
- CSI enables features beyond mount/unmount: snapshots, cloning, volume expansion, and topology-aware provisioning. Not all drivers implement all features; verify before relying.
|
||||
|
||||
## Volume Snapshots (P5 Reversibility, P8 Storage is Explicit)
|
||||
|
||||
- A VolumeSnapshot is a point-in-time copy of a PVC, taken by the CSI driver. Restore creates a new PVC from the snapshot.
|
||||
- Snapshots are not backups. They are local to the storage backend and may share blocks with the source. An off-cluster backup is still required for disaster recovery.
|
||||
- Snapshot scheduling is a workload concern (use a CronJob or a tool like Velero), not a k8s-native feature.
|
||||
|
||||
## Reclaim Policies (P8 Storage is Explicit, P5 Reversibility)
|
||||
|
||||
| Policy | On PVC delete | When |
|
||||
|--------|---------------|------|
|
||||
| `Retain` | PV and its data persist; PV must be manually reclaimed | Production, data-safety default |
|
||||
| `Delete` | PV and the underlying storage are deleted | Ephemeral, dev, scratch |
|
||||
| `Recycle` (deprecated) | PV scrubbed and made available again | Do not use — use dynamic provisioning |
|
||||
|
||||
- The reclaim policy is a data-safety decision. `Delete` on a production PVC is a footgun: deleting the PVC destroys the data. Default to `Retain` for prod, `Delete` for dev.
|
||||
- For StatefulSet PVCs, the reclaim policy on the StorageClass governs what happens when the PVC is deleted (which happens when the StatefulSet is scaled down or deleted, depending on the policy).
|
||||
|
||||
## Ephemeral Volumes (P8 Storage is Explicit)
|
||||
|
||||
- `configMap`, `secret`, `downwardAPI` volumes are read-only (by default) projections injected at pod start. They are configuration, not storage.
|
||||
- `emptyDir` with `medium: Memory` is a tmpfs — fast, ephemeral, memory-charged. Use for scratch that must be fast and never persist.
|
||||
|
||||
## What Violates Storage Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| `emptyDir` for data that must survive pod restart | P8 Storage is Explicit |
|
||||
| Shared PVC across StatefulSet replicas | P8 Storage is Explicit (use `volumeClaimTemplates`) |
|
||||
| `Delete` reclaim policy on production storage | P8 Storage is Explicit, P5 Reversibility |
|
||||
| Snapshot treated as a backup | P5 Reversibility (snapshots are local, not DR) |
|
||||
| No default StorageClass decision (accidental default) | P8 Storage is Explicit |
|
||||
| Manual PV creation when dynamic provisioning exists | C3 Simplicity |
|
||||
@@ -1,54 +0,0 @@
|
||||
# Workloads — Derived Rules
|
||||
|
||||
> Derives from `domains/kubernetes/first-principles.md`. Covers Pod, ReplicaSet, Deployment, StatefulSet, DaemonSet, Job, and CronJob. Applies P1–P10.
|
||||
|
||||
## The Pod (P2 Pods are Mortal)
|
||||
|
||||
- A pod is the smallest deployable unit: one or more containers sharing network and storage namespaces.
|
||||
- Never deploy a bare pod. A bare pod has no controller to restart, scale, or replace it. Use a controller.
|
||||
- Pods are replaceable by design. Do not store state in a pod's filesystem (`emptyDir` is scratch, not storage — see `storage.md`).
|
||||
|
||||
## Controllers (P1 Declarative Desired State)
|
||||
|
||||
| Controller | When | Identity | Ordering |
|
||||
|------------|------|----------|----------|
|
||||
| Deployment | Stateless workloads | None (pods interchangeable) | No ordering |
|
||||
| StatefulSet | Stateful workloads (databases, queues) | Stable name (`pod-0`, `pod-1`) + stable PVC | Ordered, sequential |
|
||||
| DaemonSet | One pod per node (logging, monitoring, CNI) | Per-node | — |
|
||||
| Job | Run to completion (batch) | — | — |
|
||||
| CronJob | Scheduled batch | — | — |
|
||||
|
||||
- A Deployment manages a ReplicaSet, which manages pods. You interact with the Deployment; the ReplicaSet is an implementation detail except during rollouts.
|
||||
- StatefulSet gives stable network identity and stable persistent storage per replica. Use it when the workload needs a stable name or per-replica data (databases, distributed systems). Do not use StatefulSet for stateless workloads — the ordering is overhead.
|
||||
|
||||
## Probes (P5 Probes Drive Health)
|
||||
|
||||
- **Readiness probe** — is the pod ready to serve traffic? Failing readiness removes the pod from the Service's endpoints but does not restart it. Use for "warm-up" and transient unavailability.
|
||||
- **Liveness probe** — is the pod alive? Failing liveness restarts the container. Use for "wedged but running." Do not use liveness to check dependencies (it will cascade-restart on a dependency blip).
|
||||
- **Startup probe** — has the pod finished starting? Disables liveness/readiness until it succeeds. Use for slow-starting workloads so liveness does not kill them before they are ready.
|
||||
- Probes must check the workload's own health, not the health of its dependencies. A readiness probe that fails on a downstream outage causes the Service to drain all pods simultaneously.
|
||||
|
||||
## Lifecycle and Disruption (P2 Pods are Mortal, P10 Roll Forward Roll Back)
|
||||
|
||||
- `kubectl rollout status` watches a Deployment's rollout to completion. `kubectl rollout undo` reverts to the previous ReplicaSet.
|
||||
- PodDisruptionBudgets (PDBs) protect voluntary disruptions (node drain, cluster autoscaler). An involuntary disruption (node failure) ignores the PDB. Set a PDB on every workload that must keep a minimum available.
|
||||
- Rolling update strategy: `maxUnavailable` and `maxSurge` control the speed of rollout. Slow rollouts (low `maxSurge`) are safer; fast rollouts (high `maxUnavailable`) risk availability.
|
||||
|
||||
## Resource Contracts (P4 Requests and Limits are Contracts)
|
||||
|
||||
- Every container in production has a CPU request, a memory request, and a memory limit. CPU limits are optional but recommended to bound noisy neighbours.
|
||||
- QoS classes: `Guaranteed` (requests == limits), `Burstable` (requests < limits), `BestEffort` (no requests). `BestEffort` is first evicted under node pressure — never for prod.
|
||||
- A workload without requests is an unbounded gamble on the scheduler. Set them.
|
||||
- The rolling update + rollout history described below is the k8s expression of `domains/observability/metrics.md` for health and `domains/devops/first-principles.md` P5 (Progressive Delivery): the platform observes the rollout via probes and metrics and can stop or reverse it.
|
||||
|
||||
## What Violates Workload Discipline
|
||||
|
||||
| Violation | Principle |
|
||||
|-----------|-----------|
|
||||
| Bare pod (no controller) | P2 Pods are Mortal |
|
||||
| StatefulSet for a stateless workload | P1 Declarative Desired State (overhead) |
|
||||
| No probes | P5 Probes Drive Health |
|
||||
| Liveness probe checks a dependency | P5 Probes Drive Health |
|
||||
| No PDB on a critical workload | P10 Roll Forward Roll Back |
|
||||
| No resource requests in prod | P4 Requests and Limits are Contracts |
|
||||
| `emptyDir` for data that must persist | P8 Storage is Explicit |
|
||||
@@ -1,71 +0,0 @@
|
||||
# Bad Example: Bare Pod, No Resources
|
||||
|
||||
> A Kubernetes manifest that violates Atelier's Kubernetes principles. Each violation is cited.
|
||||
|
||||
## The Code
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: api
|
||||
namespace: default
|
||||
spec:
|
||||
containers:
|
||||
- name: api
|
||||
image: api:latest # :latest, unversioned
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: DATABASE_URL
|
||||
value: "postgres://admin:hunter2@db:5432/app" # secret in plaintext, in the manifest
|
||||
```
|
||||
|
||||
The team applies it with `kubectl apply -f api-pod.yaml`. When the pod crashes, they `kubectl delete pod api && kubectl apply -f api-pod.yaml` to "restart" it. There are no probes, no resource requests, no RBAC, no NetworkPolicy.
|
||||
|
||||
## What Makes It Bad
|
||||
|
||||
### Bare Pod, No Controller (k8s P2 Pods are Mortal)
|
||||
- A `kind: Pod` with no controller. When the node dies, the pod does not come back. When the team needs three replicas, they copy the YAML twice and rename it.
|
||||
- The "restart" workflow (`delete pod && apply`) is manual recovery — exactly the manual-mutation anti-pattern from `domains/devops/`.
|
||||
- **Fix:** use a `Deployment`. The controller replaces dead pods, scales, and rolls back. See `domains/kubernetes/workloads.md`.
|
||||
|
||||
### No Resource Requests (k8s P4 Requests and Limits are Contracts)
|
||||
- The container has no `resources.requests` or `resources.limits`. It is `BestEffort` — first evicted under node pressure. The scheduler has no signal to place it well; it lands wherever there is room, then gets killed when the node is full.
|
||||
- A workload without requests is an unbounded gamble on the scheduler.
|
||||
- **Fix:** set CPU and memory requests on every prod container; set a memory limit; consider a CPU limit. See `domains/kubernetes/workloads.md`.
|
||||
|
||||
### No Probes (k8s P5 Probes Drive Health)
|
||||
- No `readinessProbe` — the Service routes traffic to the pod before it is ready. Users see 502s during startup.
|
||||
- No `livenessProbe` — a wedged container runs forever; no one notices until the outage.
|
||||
- The platform cannot heal what it cannot see. A pod without probes is invisible to the controller's reconciliation.
|
||||
- **Fix:** define readiness and liveness probes that check the workload's own health. See `domains/kubernetes/workloads.md`.
|
||||
|
||||
### `:latest` Image Tag (k8s P1 + IaC P5 Version Everything)
|
||||
- `image: api:latest` is unversioned. Every `kubectl apply` pulls whatever is newest at that moment. Two pods "running the same manifest" run different images if `latest` moved between applies.
|
||||
- Rollback is impossible — there is no version to roll back to.
|
||||
- **Fix:** pin the image to a version or a digest: `image: registry.example.com/api:v1.4.2` or `image: registry.example.com/api@sha256:...`. See `domains/kubernetes/workloads.md` and `domains/infrastructure-as-code/terraform.md` (P5 Version Everything).
|
||||
|
||||
### Secret in Plaintext in the Manifest (k8s P9 Config and Secrets are Separate, IaC P10)
|
||||
- `DATABASE_URL` with the password is in the manifest in plaintext. If the manifest is committed (it is), the secret is in git.
|
||||
- Rotating the secret requires editing the manifest and re-applying — no separation of config from secret.
|
||||
- **Fix:** put the URL in a `Secret` (created out-of-band or via a secrets tool) and reference it with `valueFrom.secretKeyRef`. The manifest contains the reference, not the value. See `domains/kubernetes/rbac.md` and `domains/security/secrets.md`.
|
||||
|
||||
### `default` Namespace (k8s P6 Namespaces Bound Blast Radius)
|
||||
- The pod runs in `default`. There is no namespace boundary for quota, RBAC, or NetworkPolicy. Every other workload in `default` can reach it; an outage in one affects the namespace all share.
|
||||
- **Fix:** give every prod workload a named namespace sized to its blast radius. `default` is for nothing in production. See `domains/kubernetes/networking.md` and `domains/kubernetes/workloads.md`.
|
||||
|
||||
### No RBAC, No NetworkPolicy (k8s P7 RBAC by Intent, P6 Namespaces Bound Blast)
|
||||
- No `serviceAccountName` — the pod uses the `default` ServiceAccount, a shared identity.
|
||||
- No `NetworkPolicy` — every pod in the cluster can reach `api`. The network is flat by default.
|
||||
- **Fix:** a dedicated ServiceAccount with a least-privilege Role bound by intent. A default-deny NetworkPolicy with explicit allows. See `domains/kubernetes/rbac.md` and `domains/kubernetes/networking.md`.
|
||||
|
||||
## The Cascade
|
||||
|
||||
The violations compound. A bare pod with no probes crashes silently and is not restarted. `:latest` means the "restart" pulls a different image than the one that crashed. The plaintext secret in the manifest is now in git, so rotating it means editing the manifest and re-applying — and the new manifest still has no controller, so the next crash starts the manual loop again. `default` namespace means the blast radius is the whole cluster.
|
||||
|
||||
## Cross-Domain Links
|
||||
|
||||
- `domains/devops/P4 Rollback First` — with `:latest` and no controller, there is no rollback path.
|
||||
- `domains/security/authorization` — the shared `default` ServiceAccount and flat network are the k8s expression of zero-trust violation.
|
||||
- `domains/errors/P2 Fail Loudly` — a pod with no probes fails silently; the platform has no signal to act on.
|
||||
@@ -1,74 +0,0 @@
|
||||
# Bad Example: Unlocked Local State
|
||||
|
||||
> An IaC configuration that violates Atelier's Infrastructure as Code principles. Each violation is cited.
|
||||
|
||||
## The Code
|
||||
|
||||
```hcl
|
||||
# main.tf — committed to the repo
|
||||
terraform {
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
# no version pin
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
access_key = "AKIAIOSFODNN7EXAMPLE" # committed
|
||||
secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" # committed
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket" "logs" {
|
||||
bucket = "app-logs-prod"
|
||||
}
|
||||
|
||||
resource "aws_db_instance" "primary" {
|
||||
identifier = "app-prod"
|
||||
username = "admin"
|
||||
password = "hunter2" # committed, in plaintext, in state
|
||||
allocated_storage = 20
|
||||
engine = "postgres"
|
||||
instance_class = "db.t3.micro"
|
||||
skip_final_snapshot = true
|
||||
}
|
||||
```
|
||||
|
||||
The team runs `terraform apply` from their laptops. The state file `terraform.tfstate` is committed alongside `main.tf` "so everyone is on the same page."
|
||||
|
||||
## What Makes It Bad
|
||||
|
||||
### Local State in a Shared Environment (IaC P8 Remote State with Locking)
|
||||
- State is `terraform.tfstate` on each laptop. Two team members run `terraform apply` simultaneously; the second to finish silently overwrites the first's changes. There is no lock.
|
||||
- The state file is committed to the repo. It contains the DB password in plaintext. It is a secret-bearing artifact in version control.
|
||||
- **Fix:** use a remote backend with locking (S3 + DynamoDB, GCS, etc.). Never commit state. See `domains/infrastructure-as-code/state.md`.
|
||||
|
||||
### Hardcoded Secrets (IaC P10 Secrets Never in Code)
|
||||
- `access_key` and `secret_key` are committed in `main.tf`. The DB `password` is committed and also written to state in plaintext.
|
||||
- The secrets are now in the git history. Rotating them is not optional; the history must be scrubbed or the credentials rotated and the old ones revoked.
|
||||
- **Fix:** credentials from environment, a secrets manager, or a `data` source (`aws_secretsmanager_secret_version`). Mark sensitive attributes `sensitive = true`. See `domains/security/secrets.md`.
|
||||
|
||||
### Unpinned Provider (IaC P5 Version Everything)
|
||||
- The `aws` provider has no `version`. The next `terraform init` pulls whatever is latest — a different provider version can change resource behavior with no review.
|
||||
- **Fix:** pin `version = "~> 5.0"`. Commit the lock file (`.terraform.lock.hcl`). See `domains/infrastructure-as-code/terraform.md`.
|
||||
|
||||
### Manual Drift, No Plan Review (IaC P4 Plan Before Apply, P9 Drift is Recoverable)
|
||||
- The team applies from laptops with no `plan` review. When the DB password is wrong, someone SSHes in and changes it manually — drift that `plan` will later report as a surprise.
|
||||
- Manual changes to managed resources are an incident, not a shortcut. Each one is a future `plan` diff that no one can explain.
|
||||
- **Fix:** run `terraform plan` in CI; review the diff; `apply` from CI on merge. Treat every drift report as an incident to investigate. See `domains/infrastructure-as-code/state.md` (Drift and Reconciliation).
|
||||
|
||||
### No Module Composition (IaC P6 Modules Compose)
|
||||
- The S3 bucket and DB instance are inline. When the team needs a second bucket, they copy-paste the block and rename it. The two copies drift over time.
|
||||
- **Fix:** a versioned module for each reusable pattern. The difference is a variable, not a copy. See `domains/infrastructure-as-code/modules.md` (the module-vs-copy boundary).
|
||||
|
||||
## The Cascade
|
||||
|
||||
The violations compound. Unlocked local state lets two `apply` runs race. Committed secrets mean the race loser's changes — and the secrets — are in the repo. Manual drift hides the corruption until a `plan` surfaces a diff no one can explain. The unpinned provider means that diff might be the provider's fault, not the team's, and no one can tell which.
|
||||
|
||||
## Cross-Domain Links
|
||||
|
||||
- `domains/security/secrets.md` — secret hygiene is non-tradeable; this example violates it in three places.
|
||||
- `domains/security/supply-chain.md` — committed credentials in git are a supply-chain incident.
|
||||
- `domains/devops/P6 Configuration as Code` — config in the repo is correct; committed *state and secrets* is the violation.
|
||||
@@ -1,157 +0,0 @@
|
||||
# Good Example: Kubernetes Deployment
|
||||
|
||||
> A Kubernetes Deployment that follows Atelier's Kubernetes principles. Each aspect cites the principle it satisfies.
|
||||
|
||||
## The Deployment
|
||||
|
||||
A stateless web service deployed as a Deployment with probes, resource contracts, RBAC, and a rolling update strategy — the canonical "production workload" pattern.
|
||||
|
||||
### Manifest
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: api
|
||||
namespace: api-prod
|
||||
labels:
|
||||
app: api
|
||||
tier: web
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: api
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxSurge: 1
|
||||
maxUnavailable: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: api
|
||||
tier: web
|
||||
spec:
|
||||
serviceAccountName: api
|
||||
automountServiceAccountToken: false
|
||||
containers:
|
||||
- name: api
|
||||
image: registry.example.com/api:v1.4.2 # pinned, not :latest
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
memory: 512Mi
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz/ready
|
||||
port: 8080
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz/live
|
||||
port: 8080
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
env:
|
||||
- name: LOG_LEVEL
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: api-config
|
||||
key: log_level
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: api-secrets
|
||||
key: database_url
|
||||
volumeMounts:
|
||||
- name: config
|
||||
mountPath: /etc/api
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: api-config
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: api
|
||||
namespace: api-prod
|
||||
spec:
|
||||
selector:
|
||||
app: api
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
---
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: api-deny-ingress
|
||||
namespace: api-prod
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app: api
|
||||
policyTypes:
|
||||
- Ingress
|
||||
ingress:
|
||||
- from:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
tier: edge
|
||||
```
|
||||
|
||||
## What Makes It Good
|
||||
|
||||
### Controller, Not Bare Pod (k8s P2 Pods are Mortal)
|
||||
- A `Deployment` manages the pods. If one dies, the controller replaces it. A bare pod has no recovery.
|
||||
- See `domains/kubernetes/workloads.md`.
|
||||
|
||||
### Resource Contracts (k8s P4 Requests and Limits are Contracts)
|
||||
- Every container has CPU and memory requests and a memory limit. The workload is `Burstable`, not `BestEffort` (first evicted under pressure).
|
||||
- See `domains/kubernetes/workloads.md` for QoS classes.
|
||||
|
||||
### Probes (k8s P5 Probes Drive Health)
|
||||
- `readinessProbe` gates traffic: a pod that is not ready is removed from the Service's endpoints.
|
||||
- `livenessProbe` restarts a wedged container.
|
||||
- The probes check the workload's own health (`/healthz/ready`, `/healthz/live`), not a dependency. A liveness probe that calls the database would cascade-restart on a DB blip.
|
||||
- See `domains/kubernetes/workloads.md`.
|
||||
|
||||
### Image Pinning (k8s P1 + IaC P5 Version Everything)
|
||||
- `image: registry.example.com/api:v1.4.2` — pinned to a version, not `:latest`. A pod restart pulls the same image it was built with.
|
||||
- See `domains/infrastructure-as-code/terraform.md` and `domains/devops/P7 Immutability` for the immutability angle.
|
||||
|
||||
### RBAC (k8s P7 RBAC by Intent, Not Identity)
|
||||
- `serviceAccountName: api` — the workload runs as a dedicated ServiceAccount, not the `default` shared identity.
|
||||
- `automountServiceAccountToken: false` — the workload does not call the API, so it gets no token. See `domains/kubernetes/rbac.md`.
|
||||
- A matching `Role` + `RoleBinding` (not shown) would grant `get, list, watch` on `configmaps` in this namespace — least privilege, scoped by intent.
|
||||
|
||||
### Config and Secrets Separate (k8s P9 Config and Secrets are Separate)
|
||||
- `LOG_LEVEL` from a ConfigMap (non-sensitive). `DATABASE_URL` from a Secret (sensitive). Both injected at runtime; neither baked into the image.
|
||||
- A configuration change does not require a rebuild. A secret rotation does not require an image redeploy.
|
||||
- See `domains/kubernetes/rbac.md` and `domains/security/secrets.md`.
|
||||
|
||||
### Namespaces Bound Blast Radius (k8s P6 Namespaces Bound Blast Radius)
|
||||
- The workload lives in `api-prod`, not `default`. The namespace is the unit of quota, RBAC, and NetworkPolicy. A problem in `api-prod` does not leak to other workloads.
|
||||
- See `domains/kubernetes/networking.md`.
|
||||
|
||||
### NetworkPolicy Default-Deny (k8s P6, P7)
|
||||
- The `NetworkPolicy` allows ingress only from the `edge` namespace. Without it, every pod in the cluster could reach `api`. Default-deny is the baseline; allows are the exceptions.
|
||||
- See `domains/kubernetes/networking.md`.
|
||||
|
||||
### Roll Forward, Roll Back (k8s P10 Roll Forward Roll Back)
|
||||
- `strategy: RollingUpdate` with `maxSurge: 1, maxUnavailable: 0` — the rollout adds a new pod before removing an old one. Availability is maintained.
|
||||
- `kubectl rollout undo deployment/api` reverts to the previous ReplicaSet. The rollback is tested before it is needed.
|
||||
- See `domains/kubernetes/workloads.md` and `domains/devops/P5 Progressive Delivery`.
|
||||
|
||||
### Cross-Domain Links
|
||||
- `domains/devops/P4 Rollback First` — the rollout strategy makes the deploy reversible.
|
||||
- `domains/security/authorization` — the ServiceAccount + Role model is the k8s expression of least-privilege authorization.
|
||||
- `domains/observability/metrics` — the probes are the platform's observability into the workload's health; the workload's own metrics complete the picture.
|
||||
@@ -1,125 +0,0 @@
|
||||
# Good Example: Terraform Module
|
||||
|
||||
> A reusable Terraform module that follows Atelier's Infrastructure as Code principles. Each aspect cites the principle it satisfies.
|
||||
|
||||
## The Module
|
||||
|
||||
A versioned module that provisions an S3 bucket with logging, versioning, and encryption — the canonical "secure bucket" pattern, composed rather than copy-pasted.
|
||||
|
||||
### Consumer Call
|
||||
|
||||
```hcl
|
||||
module "logs_bucket" {
|
||||
source = "registry.example.com/infra/secure-bucket/aws"
|
||||
version = "1.2.0"
|
||||
|
||||
name = "app-logs"
|
||||
region = "us-east-1"
|
||||
force_destroy = false
|
||||
retention_days = 90
|
||||
}
|
||||
```
|
||||
|
||||
### Module Structure
|
||||
|
||||
```
|
||||
secure-bucket/
|
||||
├── main.tf # the resource
|
||||
├── variables.tf # typed inputs
|
||||
├── outputs.tf # the interface to consumers
|
||||
├── versions.tf # provider pin
|
||||
└── README.md # the module contract
|
||||
```
|
||||
|
||||
### `versions.tf` (P5 Version Everything)
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_version = ">= 1.5.0"
|
||||
required_providers {
|
||||
aws = {
|
||||
source = "hashicorp/aws"
|
||||
version = "~> 5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `variables.tf` (P1 Declarative Intent, C2 Clarity)
|
||||
|
||||
```hcl
|
||||
variable "name" {
|
||||
type = string
|
||||
description = "Globally unique bucket name."
|
||||
validation {
|
||||
condition = can(regex("^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$", var.name))
|
||||
error_message = "Bucket name must be lowercase, 3-63 chars, DNS-compatible."
|
||||
}
|
||||
}
|
||||
|
||||
variable "retention_days" {
|
||||
type = number
|
||||
default = 30
|
||||
description = "S3 lifecycle transition age in days."
|
||||
}
|
||||
```
|
||||
|
||||
### `main.tf` (P1, P3 State is Truth, P10 Secrets Never in Code)
|
||||
|
||||
```hcl
|
||||
resource "aws_s3_bucket" "this" {
|
||||
bucket = var.name
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_versioning" "this" {
|
||||
bucket = aws_s3_bucket.this.id
|
||||
versioning_configuration {
|
||||
status = "Enabled"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
|
||||
bucket = aws_s3_bucket.this.id
|
||||
rule {
|
||||
apply_server_side_encryption_by_default {
|
||||
sse_algorithm = "AES256"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_s3_bucket_lifecycle_configuration" "this" {
|
||||
bucket = aws_s3_bucket.this.id
|
||||
rule {
|
||||
id = "retention"
|
||||
status = "Enabled"
|
||||
filter { prefix = "" }
|
||||
expiration { days = var.retention_days }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## What Makes It Good
|
||||
|
||||
### Composition (IaC P6 Modules Compose, C6 Composability)
|
||||
- The bucket pattern is one module, versioned once, consumed many times. A new consumer does not copy 40 lines of HCL — they call the module with a `name` and a `retention_days`.
|
||||
- See `domains/infrastructure-as-code/modules.md` for the module-vs-copy boundary.
|
||||
|
||||
### Pinning (IaC P5 Version Everything)
|
||||
- The consumer pins `version = "1.2.0"`. The module pins its provider (`version = "~> 5.0"`) and the required Terraform version. A commit is a complete, reproducible world.
|
||||
- No `latest` anywhere. See `domains/infrastructure-as-code/terraform.md`.
|
||||
|
||||
### State Discipline (IaC P3 State is Truth, P8 Remote State with Locking)
|
||||
- The consumer's root configuration declares a remote backend with locking (S3 + DynamoDB, GCS, etc.). The module itself does not declare a backend — the consumer owns state.
|
||||
- See `domains/infrastructure-as-code/state.md` for backend selection and locking.
|
||||
|
||||
### Secrets Hygiene (IaC P10 Secrets Never in Code)
|
||||
- The bucket is encrypted at rest (SSE-S3 AES256). No secret is hardcoded; encryption is a provider-managed default. If KMS were used, the key would come from a `data` source or a dedicated KMS module — never a literal.
|
||||
- See `domains/security/secrets.md` for the general secrets principles.
|
||||
|
||||
### Plan Before Apply (IaC P4 Plan Before Apply)
|
||||
- The consumer runs `terraform plan` before `apply`. The plan shows the new bucket, versioning, encryption, and lifecycle. Every line is reviewed. The plan is the contract review; `apply` is the signature.
|
||||
|
||||
### Cross-Domain Links
|
||||
- `domains/devops/P1 Reproducibility` — the module makes the bucket reproducible from source.
|
||||
- `domains/devops/P6 Configuration as Code` — the bucket is config, not a console click.
|
||||
- `domains/security/supply-chain` — a versioned, signed module from a trusted registry is a supply-chain control.
|
||||
+10
-20
@@ -6,14 +6,14 @@
|
||||
|
||||
| Core Principle | Domains that derive from it | Count |
|
||||
|----------------|---------------------------|-------|
|
||||
| C1 Correctness | All 13 (v0.1: 11; v0.2: infrastructure-as-code, kubernetes) | Universal |
|
||||
| C2 Clarity | v0.1: uiux, api, data, testing, observability, errors, documentation, devops; v0.2: infrastructure-as-code, kubernetes | 10 |
|
||||
| C3 Simplicity | v0.1: security, data, testing, performance, documentation, concurrency, devops | 7 |
|
||||
| C4 Locality | v0.1: testing, concurrency; v0.2: infrastructure-as-code, kubernetes | 4 |
|
||||
| C5 Reversibility | v0.1: api, data, uiux, concurrency, devops; v0.2: infrastructure-as-code, kubernetes | 7 |
|
||||
| C6 Composability | v0.1: api, security, observability, errors, documentation, concurrency; v0.2: infrastructure-as-code, kubernetes | 8 |
|
||||
| C7 Observability | v0.1: api, data, testing, performance, observability, errors, devops; v0.2: infrastructure-as-code, kubernetes | 9 |
|
||||
| C8 Economy | v0.1: security, testing, performance, observability, concurrency; v0.2: kubernetes | 6 |
|
||||
| C1 Correctness | All 11 (security, data, api, testing, performance, observability, errors, uiux, documentation, concurrency, devops) | Universal |
|
||||
| C2 Clarity | uiux, api, data, testing, observability, errors, documentation, devops | 8 |
|
||||
| C3 Simplicity | security, data, testing, performance, documentation, concurrency, devops | 7 |
|
||||
| C4 Locality | testing, concurrency | 2 |
|
||||
| C5 Reversibility | api, data, uiux, concurrency, devops | 5 |
|
||||
| C6 Composability | api, security, observability, errors, documentation, concurrency | 6 |
|
||||
| C7 Observability | api, data, testing, performance, observability, errors, devops | 7 |
|
||||
| C8 Economy | security, testing, performance, observability, concurrency | 5 |
|
||||
|
||||
## Interpretation
|
||||
|
||||
@@ -37,20 +37,10 @@
|
||||
| Documentation | C1, C2, C3, C5, C6 | Docs-as-code + living docs |
|
||||
| Concurrency | C1, C3, C4, C5, C6, C7, C8 | Broadest derivation; locality (C4) unique |
|
||||
| DevOps | C1, C2, C3, C5, C7 | Reproducibility + rollback |
|
||||
| Infrastructure as Code | C1, C2, C3, C4, C5, C6, C7 | Declarative + state + composition; broadest derivation alongside Concurrency |
|
||||
| Kubernetes | C1, C2, C4, C5, C6, C7, C8 | Declarative + reversibility + economy; broad derivation (7 C-rules) |
|
||||
|
||||
## v0.2 Domain Coverage (per IDEATE-03 schema)
|
||||
|
||||
| Domain | P-count | Derived-doc-count | Manifest-listed | Status |
|
||||
|--------|---------|-------------------|-----------------|--------|
|
||||
| Infrastructure as Code | 10 | 4 (terraform, opentofu, state, modules) | ✓ | complete |
|
||||
| Kubernetes | 10 | 6 (workloads, networking, storage, rbac, helm, kustomize) | ✓ | complete |
|
||||
|
||||
## Gaps and Notes
|
||||
|
||||
- No domain derives from only one C-rule. The minimum is 4 (UI/UX: C1, C2, C3, C5, C7 — actually 5). Every domain is multi-rooted.
|
||||
- **Concurrency**, **Infrastructure as Code**, and **Kubernetes** are tied for the broadest derivation (7 C-rules each) — these domains touch the most core concerns.
|
||||
- **Concurrency** has the broadest derivation (7 C-rules) — it touches the most core concerns.
|
||||
- **UI/UX** and **API** are the most user-facing; they emphasize C2 (Clarity) heavily.
|
||||
- **Security** is the only domain with explicit non-tradeable declarations; this promotes 8 of its rules to C1-equivalent per `core/conflict-resolution.md` §6.
|
||||
- **v0.2 expansion:** C4 (Locality) grew from 2 to 4 domains (added infrastructure-as-code state locality, kubernetes namespace blast-radius). C6 (Composability) grew from 6 to 8. The two new domains are broad-derivation domains (7 C-rules each), consistent with Concurrency's breadth.
|
||||
- **Security** is the only domain with explicit non-tradeable declarations; this promotes 8 of its rules to C1-equivalent per `core/conflict-resolution.md` §6.
|
||||
@@ -173,40 +173,4 @@ C5=Reversibility · C6=Composability · C7=Observability · C8=Economy
|
||||
| P7 Immutability | C1 | Correctness of deploys |
|
||||
| P8 Security at Every Layer | C1 | Correctness includes security |
|
||||
| P9 Documentation in Pipeline | C2 | Clarity of operations |
|
||||
| P10 Failure as Expected | C1, C7 | Correctness; observability |
|
||||
|
||||
## Infrastructure as Code
|
||||
|
||||
| IaC Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Declarative Intent | C2, C3 | Clarity; simplicity of declaration |
|
||||
| P2 Idempotence | C1 | Correctness of repeated apply |
|
||||
| P3 State is Truth | C1, C7 | Correctness; observability of reality |
|
||||
| P4 Plan Before Apply | C5 | Reversibility of changes |
|
||||
| P5 Version Everything | C5 | Reversibility of reproduction |
|
||||
| P6 Modules Compose | C6 | Composability of infrastructure |
|
||||
| P7 Least Privilege Providers | C1 | Correctness via security |
|
||||
| P8 Remote State with Locking | C1, C4 | Correctness; locality of state |
|
||||
| P9 Drift is Recoverable | C5, C7 | Reversibility; observability of drift |
|
||||
| P10 Secrets Never in Code | C1 | Correctness includes secret safety |
|
||||
|
||||
## Kubernetes
|
||||
|
||||
| K8s Principle | Core | Why |
|
||||
|---------------------------|------|---------------------------------------|
|
||||
| P1 Declarative Desired State | C1, C2 | Correctness; clarity of intent |
|
||||
| P2 Pods are Mortal | C5 | Reversibility of workloads |
|
||||
| P3 Labels Select | C2, C6 | Clarity; composability of selection |
|
||||
| P4 Requests and Limits | C1, C8 | Correctness; economy of resources |
|
||||
| P5 Probes Drive Health | C1, C7 | Correctness; observability of health |
|
||||
| P6 Namespaces Bound Blast | C4 | Locality of blast radius |
|
||||
| P7 RBAC by Intent | C1 | Correctness via security |
|
||||
| P8 Storage is Explicit | C4, C5 | Locality; reversibility of data |
|
||||
| P9 Config and Secrets Sep | C2 | Clarity of configuration |
|
||||
| P10 Roll Forward, Roll Back | C5 | Reversibility of deploys |
|
||||
|
||||
## Coverage Summary (post-v0.2)
|
||||
|
||||
- 13 domains (11 v0.1 + 2 v0.2: infrastructure-as-code, kubernetes)
|
||||
- 130 domain principles total (110 v0.1 + 20 v0.2)
|
||||
- Every domain P-rule traces to ≥1 core C-rule (C1–C8). No orphans.
|
||||
| P10 Failure as Expected | C1, C7 | Correctness; observability |
|
||||
@@ -125,28 +125,6 @@ If the task touches a domain, run that domain's checklist:
|
||||
- [ ] Config is in code, not on the server
|
||||
- [ ] Environments are parity (dev = prod modulo data)
|
||||
|
||||
### If Infrastructure as Code (see `domains/infrastructure-as-code/`)
|
||||
- [ ] Configuration is declarative, not scripted (P1)
|
||||
- [ ] Provider versions are pinned, never `latest` (P5)
|
||||
- [ ] State is remote with locking; never committed (P3, P8)
|
||||
- [ ] `plan` is reviewed before every `apply` (P4)
|
||||
- [ ] No secrets in HCL; secrets via providers/stores (P10)
|
||||
- [ ] Modules are versioned; copy-paste replaced by module calls (P6)
|
||||
- [ ] Drift is treated as an incident, not a shortcut (P9)
|
||||
- [ ] Provider credentials scoped per environment, least privilege (P7)
|
||||
|
||||
### If Kubernetes (see `domains/kubernetes/`)
|
||||
- [ ] No bare pods; controllers used (P2)
|
||||
- [ ] Resource requests set on every prod container (P4)
|
||||
- [ ] Liveness/readiness/startup probes defined (P5)
|
||||
- [ ] RBAC bound to ServiceAccounts by intent; no `cluster-admin` (P7)
|
||||
- [ ] No `:latest` image tag in prod (P5 Version Everything)
|
||||
- [ ] StatefulSet PVCs use `volumeClaimTemplates`; `emptyDir` only for scratch (P8)
|
||||
- [ ] ConfigMaps and Secrets separate; secrets not in image (P9)
|
||||
- [ ] Default-deny NetworkPolicy baseline (P6)
|
||||
- [ ] Rollout history retained; rollback tested (P10)
|
||||
- [ ] Namespaces used to bound blast radius; not `default` in prod (P6)
|
||||
|
||||
## Final Gate
|
||||
|
||||
- [ ] Have I read the relevant domain's first-principles?
|
||||
|
||||
+1
-35
@@ -121,32 +121,6 @@ When you see a pattern listed here, it is a defect. Cite the principle it violat
|
||||
| Rebuild per environment | P7 Immutability | Different artifacts |
|
||||
| Snowflake server | P1, P6 | Not reproducible |
|
||||
|
||||
### Infrastructure as Code
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Unpinned provider (`source` without `version`) | P5 Version Everything | Unversioned drift |
|
||||
| `terraform apply` without a read `plan` | P4 Plan Before Apply | Unreviewed mutation |
|
||||
| Local state in a shared environment | P8 Remote State with Locking | No lock = corruption |
|
||||
| Hardcoded secret in HCL | P10 Secrets Never in Code | Committed secret |
|
||||
| Copy-pasted block instead of a module | P6 Modules Compose | Bug duplicated |
|
||||
| Manual change to a managed resource | P9 Drift is Recoverable | Unreconciled drift |
|
||||
| Admin credentials in CI | P7 Least Privilege Providers | Overbroad grant |
|
||||
| Committed `terraform.tfstate` | P3 State is Truth, P10 | Secret-bearing artifact in repo |
|
||||
|
||||
### Kubernetes
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Bare pod (no controller) | P2 Pods are Mortal | No recovery/scale |
|
||||
| No resource requests in prod | P4 Requests and Limits | BestEffort, first evicted |
|
||||
| Liveness probe checks a dependency | P5 Probes Drive Health | Cascade restart |
|
||||
| `cluster-admin` bound to a workload | P7 RBAC by Intent | Overbroad grant |
|
||||
| `:latest` image tag in prod | P5 Version Everything | Unversioned drift |
|
||||
| `emptyDir` for data that must persist | P8 Storage is Explicit | Data lost on pod death |
|
||||
| Secrets baked into the image | P9 Config and Secrets Separate | No rotation without rebuild |
|
||||
| `default` namespace in prod | P6 Namespaces Bound Blast Radius | No blast boundary |
|
||||
| Shared PVC across StatefulSet replicas | P8 Storage is Explicit | Concurrent write corruption |
|
||||
| `Delete` reclaim policy on prod storage | P8, P5 Reversibility | PVC delete = data delete |
|
||||
|
||||
## Cross-Cutting Anti-Patterns
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
@@ -155,12 +129,4 @@ When you see a pattern listed here, it is a defect. Cite the principle it violat
|
||||
| "It's just a prototype" | C5 (irreversible by default) | Prototypes go to prod |
|
||||
| Copy-paste code | C6 (no composition) | Bug duplicated |
|
||||
| God object | C3, C6 | One thing, many things |
|
||||
| Leaky abstraction | C6, C2 | Hidden coupling |
|
||||
|
||||
## v0.2 Chaos Anti-Patterns (from IDEATE-13, IDEATE-14)
|
||||
|
||||
| Anti-Pattern | Breaches | Why |
|
||||
|--------------|----------|-----|
|
||||
| Orphaned P-rule (a domain principle with no matrix row) | matrix completeness, C6 | Breaks the conflict-resolution arbiter; the rule has no core trace |
|
||||
| Deployable example artifact (standalone `.tf`/`.yaml` under `examples/`) | PROJECT.md "no runtime code", D-025 | Violates the docs-only contract; examples must be `.md` with fenced code |
|
||||
| Unlisted v0.2 doc (new doc not added to MANIFEST) | manifest rule | Not part of the framework by definition |
|
||||
| Leaky abstraction | C6, C2 | Hidden coupling |
|
||||
@@ -59,25 +59,7 @@ A peer review is a second set of eyes on correctness, clarity, and completeness.
|
||||
|
||||
## Domain-Specific (if applicable)
|
||||
|
||||
Run the relevant domain section from `agent-checklist.md` (UI/UX, API, Security, Data, Testing, Performance, Observability, Errors, Concurrency, DevOps, Infrastructure as Code, Kubernetes). The author ran it; the reviewer verifies.
|
||||
|
||||
### If Infrastructure as Code
|
||||
- [ ] Are providers pinned and modules version-pinned? (no `latest`)
|
||||
- [ ] Is state remote with locking, not committed?
|
||||
- [ ] Is the `plan` diff reviewed, not just the config?
|
||||
- [ ] Are secrets via providers/stores, not in HCL?
|
||||
- [ ] Are credentials scoped per environment?
|
||||
- [ ] Is drift being treated as an incident?
|
||||
|
||||
### If Kubernetes
|
||||
- [ ] Are controllers used, not bare pods?
|
||||
- [ ] Are resource requests set on every prod container?
|
||||
- [ ] Are probes defined and checking the workload, not its dependencies?
|
||||
- [ ] Is RBAC least-privilege, bound to ServiceAccounts by intent?
|
||||
- [ ] Are image tags pinned, not `:latest`?
|
||||
- [ ] Is persistent storage explicit (PVC + reclaim policy decided)?
|
||||
- [ ] Are ConfigMaps and Secrets separate?
|
||||
- [ ] Is the rollback path tested, not assumed?
|
||||
Run the relevant domain section from `agent-checklist.md` (UI/UX, API, Security, Data, Testing, Performance, Observability, Errors, Concurrency, DevOps). The author ran it; the reviewer verifies.
|
||||
|
||||
## Review Etiquette
|
||||
|
||||
|
||||
Reference in New Issue
Block a user