Files
sbbb/docs/conventions.md
Sienna Meridian Satterwhite ceb038382f docs: add infrastructure conventions — House Rules, Darling 🏠
Do's and don'ts, kustomize patterns, secret management, deployment
conventions, naming conventions, AI config, domain patterns.
2026-03-24 11:47:40 +00:00

5.9 KiB

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:

# 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:

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