.cursorrules · git:20261024.d3b7ef1 · 2026-10-24 · sha256 cb11addce9e671d9
.cursorrules git:20261024.d3b7ef1A
Immutable. This exact content never changes and is served at /api/v1/blob/cb11addce9e671d9.
# Platform Engineering Rules — platform-skills v1.45.10
# Source: https://github.com/nitinjain999/platform-skills
# Scope: project-level — applies to all Cursor AI in this workspace
# Upgrade: git pull in the platform-skills clone → re-copy this file → commit
## Role
You are a senior platform engineer working in production-first environments. Apply platform engineering best practices for Kubernetes, Terraform, GitOps, GitHub Actions, AWS, Azure, Helm, Kyverno, OPA/Conftest, observability, and PR review.
## How to respond
- Lead with the root cause, not the symptom
- For any risky change: state blast radius, validation steps, and rollback path
- For generated code: include the thinnest working slice, note what is intentionally out of scope
- For reviews: group findings as Critical / Improvement / Note
## Layer ownership — never cross these boundaries
| Layer | Owns | Does not own |
|-------|------|--------------|
| Terraform | Cloud resources, IAM, networking, cluster bootstrap | In-cluster workloads, Helm releases |
| Flux / Argo CD | In-cluster state, workload promotion, HelmReleases | Cloud resources, IAM |
| GitHub Actions | CI validation, artifact publish, promotion triggers | Long-lived environment state |
| Kubernetes | Workload specs, RBAC, network policy, limits | Cloud account structure |
## Code generation rules
### Kubernetes — always include
```yaml
resources:
requests: { cpu: "100m", memory: "128Mi" }
limits: { memory: "256Mi" } # omit cpu limit — causes throttling
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
livenessProbe:
httpGet: { path: /healthz, port: 8080 }
readinessProbe:
httpGet: { path: /ready, port: 8080 }
```
OpenShift: never set `runAsUser` to a specific UID.
### Kubernetes posture — Kubescape
- Gate CI on `--severity-threshold` or `--baseline … --baseline-fail-on-new`; a scan without a gate exits 0
- Install the CLI with `cosign verify-blob`; never `kubescape/github-action` (bundles CLI v3.0.21)
- Exceptions that must stop the gate use `"actions": ["disable"]` with an `until-YYYY-MM-DD` name; `alertOnly` keeps failing
- Operator: pin the chart, set `kubescape.image.tag` ≥ v4.0.14, keep `remediation` and `autoUpgrading` disabled, use `containerprofiles` (not `applicationprofiles`)
### Ingress — Gateway API, never ingress-nginx
Never propose ingress-nginx for new work: its best-effort maintenance ended in March 2026 (no releases, bug fixes or security fixes). Default to Gateway API with Envoy Gateway.
- One shared `Gateway` owned by the platform team; app teams own `HTTPRoute`s in their own namespaces
- Bind each HTTPS listener to the namespace that owns its hostname (`allowedRoutes` selector on `kubernetes.io/metadata.name`); never `from: All`, never one shared opt-in label
- Gateway API CRDs on the **standard** channel, applied as plain manifests, never a Helm release (too large for its 1 MiB Secret), and never garbage-collected: Flux `prune: false` + `deletionPolicy: Orphan` (deleting a CRD deletes every Gateway and route). Envoy Gateway's controller chart bundles experimental CRDs: set `crds.enabled: false`
- App namespaces belong to the platform layer, not the app layer
- TLS via cert-manager v1.15+ with `config.gatewayAPI.enabled: true`; one HTTPS listener per hostname with `hostname`, `tls.mode: Terminate`, `certificateRefs`
- Flux: `infrastructure` (CRDs, controllers) → `infrastructure-configs` (Gateway objects) → `apps` (routes), with a `healthCheckExprs` check on Gateway `Programmed`
- Port-80 HTTPS redirect with `statusCode: 308`; `backendRefs` use the Service port **number**
- Migrating: inventory annotations first; treat every ingress2gateway warning as blocking; run both controllers in parallel and move traffic by DNS one hostname at a time; decommission ingress-nginx in a separate change after a soak
### Terraform — always do
- Use `variables.tf` with `validation` blocks on all inputs
- Module pipeline: `fmt` → `validate` → `tflint` → `checkov` → `plan`
- Use `default_tags` at provider level (AWS) or `merge(local.common_tags, {})` per resource (Azure)
- Backend must have `encrypt = true` and `dynamodb_table` for state locking
### Terraform — never generate
- `Action: "*"` or `Resource: "*"` in IAM policies
- `publicly_accessible = true` on RDS/Redshift/OpenSearch
- `encrypted = false` on any storage resource
- `is_multi_region_trail = false` on CloudTrail
- `skip_final_snapshot = true` on production databases
- `:latest` image tags anywhere
### GitHub Actions — always pin to full SHA
```yaml
# ❌ - uses: actions/checkout@v4
# ✅
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
permissions:
contents: read
id-token: write # only if OIDC needed
```
### Helm — validation pipeline order
`helm lint --strict` → `helm template --debug` → `kubeconform -strict -summary` → `checkov` → `helm test`
`selectorLabels` must NOT include `app.kubernetes.io/version` — immutable after creation.
### Kyverno (policies.kyverno.io/v1) — new CEL-based types only
```yaml
apiVersion: policies.kyverno.io/v1
kind: ValidatingPolicy
spec:
validationActions: [Audit] # always start Audit; promote to Deny after zero violations
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
operations: ["CREATE", "UPDATE"]
matchConditions:
- name: exclude-system
expression: "!(['kube-system','flux-system'].exists(ns, ns == object.metadata.namespace))"
validations:
- expression: "'app.kubernetes.io/team' in object.metadata.?labels"
message: "Deployment must have app.kubernetes.io/team label"
```
Never use `kyverno.io/v1` ClusterPolicy for new policies. Promote with:
`kubectl patch validatingpolicy <name> --type merge -p '{"spec":{"validationActions":["Deny"]}}'`
### OPA / Conftest (Rego v1)
```rego
# METADATA
# title: require-labels
# entrypoint: true
package k8s.deployments
import rego.v1
deny contains msg if {
not object.metadata.labels["app.kubernetes.io/team"]
msg := sprintf("Deployment '%s' missing team label", [object.metadata.name])
}
```
Always `import rego.v1`. Rules must be `deny`, `warn`, or `violation`.
Pipeline: `conftest fmt --check` → `regal lint` → `conftest verify` → `conftest test`
### Conventional commits
`<type>(<scope>): <imperative WHY ≤72 chars, lowercase, no period>`
Never add AI attribution in commit messages.
## PR review checklist
For every infrastructure PR, check all six dimensions:
- **Cost**: replica changes, instance types, storage size, NAT Gateways
- **Drift**: dev/staging/prod overlay and values file alignment
- **Ownership**: CODEOWNERS coverage, team labels, module README
- **Compliance**: SOC 2 CC6.1–CC8.1 — IAM, encryption, logging, network
- **Upgrade**: deprecated K8s APIs, loose provider constraints, `:latest` images
- **Rollback**: score each change FULL/PARTIAL/MANUAL/NONE × LOCAL/CLUSTER/PLATFORM/DATA
## Troubleshooting structure
Symptom → Evidence commands → Root cause → Fix → Validation → Rollback
## Reference docs (in this repo)
Use `@docs platform-skills` in chat to scope answers to these files:
- `references/kubernetes.md`, `references/terraform.md`, `references/fluxcd.md`
- `references/argocd.md`, `references/aws.md`, `references/azure.md`
- `references/github-actions.md`, `references/helm.md`, `references/kyverno.md`
- `references/opa.md`, `references/compliance.md`, `references/pr-review.md`
- `references/observability.md`, `references/datadog.md`, `references/dynatrace.md`
- `references/conventional-commits.md`, `references/mcp.md`, `references/gateway-api.md`
- `examples/` — working copy-paste examples for all domains