docs: add infrastructure conventions — House Rules, Darling 🏠
Do's and don'ts, kustomize patterns, secret management, deployment conventions, naming conventions, AI config, domain patterns.
This commit is contained in:
185
docs/conventions.md
Normal file
185
docs/conventions.md
Normal file
@@ -0,0 +1,185 @@
|
||||
# House Rules, Darling 🏠
|
||||
|
||||
These are the conventions for working in The Super Boujee Business Box ✨ infrastructure. Follow them and everything stays clean. Ignore them and things get messy. We don't do messy.
|
||||
|
||||
---
|
||||
|
||||
## The Don'ts ❌
|
||||
|
||||
| Rule | Why |
|
||||
|------|-----|
|
||||
| No Helm charts for simple resources | Plain YAML when a Deployment + Service + ConfigMap is all you need |
|
||||
| No Ingress resources | Everything routes through Pingora — add a route in the proxy config |
|
||||
| No Terraform / Pulumi | Kustomize + sunbeam CLI. That's the stack. |
|
||||
| No RBAC / NetworkPolicy / PodSecurityPolicy | Linkerd handles mTLS. Keep it simple. |
|
||||
| No new namespaces without discussion | The namespace layout is intentional |
|
||||
| No hardcoded domains | Use `DOMAIN_SUFFIX` placeholder — gets replaced at deploy time |
|
||||
| No TLS keys in Git | Secrets live in OpenBao, synced by Vault Secrets Operator |
|
||||
| No configMapGenerator / secretGenerator | Plain YAML ConfigMaps and Secrets |
|
||||
| No commonLabels / commonAnnotations | They cause merge conflicts and confusion |
|
||||
| No abstract base layers or nested overlays | One level: base → overlay. That's it. |
|
||||
|
||||
---
|
||||
|
||||
## The Do's ✅
|
||||
|
||||
### Kustomize Patterns
|
||||
|
||||
**Base holds canonical config.** Everything goes in `base/{namespace}/`:
|
||||
|
||||
```
|
||||
base/{namespace}/
|
||||
├── namespace.yaml
|
||||
├── kustomization.yaml
|
||||
├── {app}-deployment.yaml
|
||||
├── {app}-service.yaml
|
||||
├── {app}-config.yaml
|
||||
├── {chart}-values.yaml # Helm chart values
|
||||
├── vault-secrets.yaml # VaultStaticSecret + VaultDynamicSecret
|
||||
├── patch-{what}.yaml # Strategic merge patches
|
||||
├── {component}-alertrules.yaml # PrometheusRule resources
|
||||
└── {component}-servicemonitor.yaml
|
||||
```
|
||||
|
||||
**Overlays hold only patches.** Environment-specific overrides in `overlays/{env}/`:
|
||||
|
||||
```
|
||||
overlays/{env}/
|
||||
├── kustomization.yaml
|
||||
├── patch-*.yaml # Resource limits, env-specific config
|
||||
└── values-*.yaml # Helm value overrides
|
||||
```
|
||||
|
||||
### ConfigMaps & Secrets
|
||||
|
||||
- **ConfigMaps:** Use `envFrom` to inject all keys as env vars
|
||||
- **Secrets:** Use individual `env` entries with `secretKeyRef` (explicit about what's secret)
|
||||
- **Shared ConfigMaps** in lasuite namespace: `lasuite-postgres`, `lasuite-valkey`, `lasuite-s3`, `lasuite-oidc-provider`, `lasuite-resource-server`
|
||||
|
||||
### Secret Management
|
||||
|
||||
Every secret follows the Vault pattern:
|
||||
|
||||
```yaml
|
||||
# Static secret (OIDC keys, Django secrets, DKIM keys)
|
||||
VaultStaticSecret:
|
||||
mount: secret
|
||||
type: kv-v2
|
||||
refreshAfter: 30s
|
||||
|
||||
# Dynamic secret (database credentials)
|
||||
VaultDynamicSecret:
|
||||
mount: database
|
||||
path: static-creds/{app}
|
||||
refreshAfter: 5m
|
||||
rolloutRestartTargets:
|
||||
- kind: Deployment
|
||||
name: {app}
|
||||
```
|
||||
|
||||
Dynamic secrets rotate every 5 minutes. The Vault Secrets Operator handles the K8s Secret lifecycle.
|
||||
|
||||
### Deployments
|
||||
|
||||
- **Init containers** run migrations: `python manage.py migrate`
|
||||
- **Short image names** — the registry is set via `images:` in the overlay kustomization
|
||||
- **Liveness probes:** `/__heartbeat__`
|
||||
- **Readiness probes:** `/__lbheartbeat__`
|
||||
- **Resource limits** on every pod — no unbounded memory
|
||||
- **Linkerd injection:** `linkerd.io/inject: enabled` annotation on all application namespaces
|
||||
|
||||
### OIDC Clients
|
||||
|
||||
Declare as `HydraOAuth2Client` CRD. Hydra Maester creates the K8s Secret:
|
||||
|
||||
```yaml
|
||||
apiVersion: hydra.ory.sh/v1alpha1
|
||||
kind: OAuth2Client
|
||||
metadata:
|
||||
name: oidc-{app}
|
||||
namespace: lasuite
|
||||
spec:
|
||||
grantTypes: [authorization_code, refresh_token]
|
||||
responseTypes: [code]
|
||||
scope: openid email profile
|
||||
redirectUris:
|
||||
- https://{app}.DOMAIN_SUFFIX/api/v1.0/callback/
|
||||
secretName: oidc-{app}
|
||||
```
|
||||
|
||||
### Helm Charts
|
||||
|
||||
Used for complex components where plain YAML isn't practical:
|
||||
|
||||
| Chart | Component |
|
||||
|-------|-----------|
|
||||
| CloudNativePG | PostgreSQL operator |
|
||||
| Ory Kratos | Identity management |
|
||||
| Ory Hydra | OAuth2/OIDC |
|
||||
| Gitea | Git hosting |
|
||||
| kube-prometheus-stack | Monitoring |
|
||||
| Loki | Log aggregation |
|
||||
| Tempo | Tracing |
|
||||
| Alloy | Collection agent |
|
||||
| LiveKit | Video conferencing |
|
||||
| OpenBao | Secrets management |
|
||||
| cert-manager | TLS certificates |
|
||||
| Longhorn | Volume management |
|
||||
| Drive | File management |
|
||||
| Docs | Document editing |
|
||||
|
||||
Values go in `{chart}-values.yaml` in the base directory. Overlay-specific overrides in `values-{chart}.yaml`.
|
||||
|
||||
---
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
| Thing | Pattern | Example |
|
||||
|-------|---------|---------|
|
||||
| Deployments | `{app}-deployment.yaml` | `sol-deployment.yaml` |
|
||||
| Services | `{app}-service.yaml` | `sol-service.yaml` |
|
||||
| ConfigMaps | `{app}-config.yaml` | `sol-config.yaml` |
|
||||
| Secrets | From Vault: `{app}-{purpose}` | `messages-django-secret` |
|
||||
| OIDC clients | `oidc-{app}` | `oidc-drive` |
|
||||
| Alert rules | `{component}-alertrules.yaml` | `postgres-alertrules.yaml` |
|
||||
| ServiceMonitors | `{component}-servicemonitor.yaml` | `gitea-servicemonitor.yaml` |
|
||||
| Dashboards | `dashboards-{domain}.yaml` | `dashboards-ingress.yaml` |
|
||||
| Helm values | `{chart}-values.yaml` | `drive-values.yaml` |
|
||||
| Patches | `patch-{what}.yaml` | `patch-drive-wopi-init.yaml` |
|
||||
| S3 buckets | `sunbeam-{purpose}` | `sunbeam-game-assets` |
|
||||
|
||||
---
|
||||
|
||||
## AI Configuration
|
||||
|
||||
Three env vars, consistent across all components:
|
||||
|
||||
```
|
||||
AI_BASE_URL=https://api.scaleway.ai/v1/
|
||||
AI_API_KEY=<SCW_SECRET_KEY>
|
||||
AI_MODEL=mistral-small-3.2-24b-instruct-2506
|
||||
```
|
||||
|
||||
Scaleway Generative APIs — hosted in Paris, GDPR-compliant, OpenAI-compatible. ~€1-5/month for a three-person studio.
|
||||
|
||||
---
|
||||
|
||||
## Domain Pattern
|
||||
|
||||
All apps follow `{prefix}.DOMAIN_SUFFIX`:
|
||||
|
||||
```
|
||||
docs, meet, drive, mail, messages, people, find, src, auth,
|
||||
integration, cal, projects, s3, livekit, metrics, systemmetrics,
|
||||
systemlogs, systemtracing, vault, search, id, hydra
|
||||
```
|
||||
|
||||
`DOMAIN_SUFFIX` is replaced at deploy time:
|
||||
- Production: `sunbeam.pt`
|
||||
- Local: `{LIMA_IP}.sslip.io`
|
||||
|
||||
---
|
||||
|
||||
## The Golden Rule
|
||||
|
||||
If you can derive it from the code, don't store it separately. If it needs to be secret, put it in OpenBao. If it's environment-specific, put it in the overlay. Everything else goes in base. Keep it flat, keep it obvious, keep it boujee. 💅
|
||||
Reference in New Issue
Block a user