# 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= 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. 💅