From c07f2d1135fe914012ed748233bc536a0b81191e Mon Sep 17 00:00:00 2001 From: Tyler Gillispie Date: Sun, 23 Aug 2026 09:53:38 -0700 Subject: [PATCH] docs: add root COMPOSE_CONVENTIONS.md documenting compose, storage, and smart image standards --- COMPOSE_CONVENTIONS.md | 112 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 COMPOSE_CONVENTIONS.md diff --git a/COMPOSE_CONVENTIONS.md b/COMPOSE_CONVENTIONS.md new file mode 100644 index 0000000..66222d7 --- /dev/null +++ b/COMPOSE_CONVENTIONS.md @@ -0,0 +1,112 @@ +# Compose & Infrastructure Conventions + +This document outlines the standard conventions and best practices for Docker +and Podman Compose files, volume layouts, networking, and smart image packaging +across our infrastructure. + +--- + +## 1. Storage & Host Volume Conventions + +- **Root Project Directory:** All project state lives under + `/volume1/docker//` (e.g., `/volume1/docker/auth-yes/`, + `/volume1/docker/spire/`). +- **Standard Folder Naming:** Always use **`data`** for persistent application + and database storage (never `db` or fragmented subfolders when a single root + directory suffices). +- **Local Bind Mount Syntax:** Always use the standard `driver_opts` local bind + definition: + ```yaml + volumes: + -data: + driver: local + driver_opts: + type: none + device: ${_DATA_PATH} + o: bind + ``` +- **Zero-Trust Inter-Process Sockets:** + - Sockets shared across stacks (e.g., the SPIFFE Workload API `agent.sock`) + MUST use a **named volume** (`spire-socket`). + - Application containers mount this volume read-only (`:ro`), ensuring zero + access to sensitive server CA keys, SQLite databases, or internal host + state. + - Client stacks declare `spire-socket` as `external: true`. + +--- + +## 2. Smart Container Images & Scripting Guidelines + +- **Pragmatic Scripting in Compose:** + - Short one-line commands, flags, or glue checks in `command:` or + `entrypoint:` are fine. + - Complex configuration generation, heredoc templating (`cat << 'EOF'`), and + heavy lifecycle bootstrapping MUST NOT be embedded in Compose files. +- **Smart Image Packaging & Repository Segregation:** + - Self-seeding configuration and container lifecycle scripts belong in the + container image. + - All smart image logic, Dockerfiles, entrypoint scripts (`entrypoint.sh`), + and configuration templates (`templates/`) must be cleanly segregated in + dedicated directories in the codebase (e.g., `spire/`). +- **Persistence & User Modification:** + - Smart image entrypoints must follow the self-seeding pattern: check if the + target configuration exists on the host mount. + - If missing on first boot $\rightarrow$ atomically write the master template + with guiding comments. + - If already present $\rightarrow$ leave it untouched, preserving all user + modifications across restarts and image updates. + +--- + +## 3. Environment Variables & Clean YAML + +- **No Inline Variable Fallbacks in Compose:** + - Always write clean variable references: `${REG}`, `${GHCR_REG}`, + `${SYSTEM_DOMAIN}`, `${SPIRE_DATA_PATH}`. + - Do NOT use inline defaults (e.g., `${REG:-quay.atyg.org}`) inside the + Compose file. + - All default values belong strictly in `.env` or the interactive setup wizard + (`infra/setup.ts`). +- **Parameterize Registry Endpoints:** + - Always prefix images with `${REG}/library/:` or + `${GHCR_REG}//:`. + +--- + +## 4. Networking & Traefik Routing + +- **Ingress Network:** + - Web-facing containers connect to the pre-existing external network + `traefik-net` (`external: true`). +- **Network Routing Ambiguity Guard:** + - Whenever a service connects to more than one network (e.g., internal + bridge + `traefik-net`), always specify: + ```yaml + labels: + - "traefik.docker.network=traefik-net" + ``` +- **Internal Service Mesh:** + - Backends, databases, Valkey, and internal daemons communicate over a project + bridge network (e.g., `auth-internal-net`) without exposing ports directly + to the host. +- **Standard Websecure Labels:** + ```yaml + labels: + - "traefik.enable=true" + - "traefik.docker.network=traefik-net" + - "traefik.http.routers..rule=Host(`${SYSTEM_DOMAIN}`)" + - "traefik.http.routers..entrypoints=websecure" + - "traefik.http.routers..tls=true" + - "traefik.http.services..loadbalancer.server.port=" + ``` + +--- + +## 5. Stack Independence & Decoupling + +- **Standalone Infrastructure Stacks:** + - General-purpose infrastructure components (e.g., SPIRE, Auth-Yes, Registry + Caches) are standalone stacks with their own dedicated project directories + (`/volume1/docker/spire`, `/volume1/docker/auth-yes`). + - Never nest one stack's storage or lifecycle under another stack's directory + structure.