Skip to content

ADR 13: Twelve-Factor App Methodology

Status: Accepted Date: 2026-04-10

Context

Substratum's cloud infrastructure, specifically the apps/gateway API and P2P node, must be highly available, horizontally scalable, and easy to deploy across environments (e.g., local development, staging, DigitalOcean production). To ensure our architecture remains robust and cloud-native, we need a formalized set of rules governing configuration, logging, and process management.

Decision

We will strictly adhere to the Twelve-Factor App methodology for all deployable applications in the Substratum monorepo.

For our Rust backend (apps/gateway), this requires specific technical implementations:

  1. Config (III): We will store all configuration in the environment. The gateway will use the dotenvy crate for local development but rely purely on environment variables (e.g., DATABASE_URL, S3_ENDPOINT, OAUTH_PRIVATE_KEY) in production. No secrets or environment-specific configs will be hardcoded.
  2. Disposability (IX): We will implement graceful shutdown using Tokio's signal handling. Upon receiving a SIGTERM, the Axum web server will stop accepting new connections, finish processing active HTTP requests, cleanly close the SeaORM database pool, and gracefully disconnect the libp2p swarm before exiting.
  3. Dev/Prod Parity (X): We will use docker-compose.yml to spin up local instances of PostgreSQL and an S3-compatible object store (like MinIO). This ensures local development perfectly mirrors our DigitalOcean Managed Database and Spaces production environment.
  4. Logs (XI): We will treat logs as event streams. The gateway will not write to local log files. Instead, it will use the tracing and tracing-subscriber crates to output structured JSON logs directly to stdout, allowing the hosting provider to capture and route them.
  5. Admin Processes (XII): Database migrations will not run automatically on application startup. We will use the SeaORM CLI (sea-orm-cli migrate up) as a one-off administrative process executed during the deployment pipeline.

Consequences

  • Positive:
    • Scalability: The gateway remains entirely stateless, allowing us to scale horizontally behind a DigitalOcean Load Balancer without conflict.
    • Portability: By treating backing services (PostgreSQL, S3) as attached resources via environment variables, we can easily migrate between cloud providers if necessary.
    • Reliability: Graceful shutdown prevents data corruption and dropped connections during deployments.
  • Negative:
    • Boilerplate: Requires additional setup in the Rust main function to wire up tracing, signal handlers, and environment variable parsing.
    • Operational Discipline: Developers must strictly avoid writing state to the local filesystem or relying on local configuration files in production.