Hosting and delivery

CI/CD from the first commit. Every version stays online, every failure goes back to the agent.

From commit to demo

Pipeline: commit, lint and types, unit and contract tests, integration, acceptance, security, image build, preview, demo. Every merge, no manual release step1Commitagent or me2Lint and typeseslint, tsc3Unit and contractvitest4Integrationreal Postgres5AcceptanceAPI, invariants6Securitydeps, SBOM7Build imagesigned8Previewper PR and tag9Demonightly reset red build goes back to the agent with the failure log
Fig. 9 The pipeline is also the agent's teacher: failures are fed back into the fix loop.

Environments

Environments: local with Docker Compose and Mailpit, CI with containers, a preview per version, and a public demo with nightly reset. Observability collects traces from all. LocalDocker ComposePostgreSQL, queue, Mailpitseed data, hot reload CIGitHub Actionsthrowaway containersall test levels Preview per versionv0-3.demo.exampleevery tag stays reachablescales to zero when idle Public demofictional datanightly resetrate limited, demo keys Observability: OpenTelemetry traces, metrics and logs from every environment, one public read only dashboard
Fig. 10 Old versions stay online, so the diary can link to exactly what each part describes.

Pipeline as code

Agents read the same file I do. When something fails, the log goes straight back into the fix loop.

name: ci
on: [push, pull_request]
jobs:
  verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
      - uses: actions/setup-node@v4
        with: { node-version: 24, cache: pnpm }
      - run: pnpm install --frozen-lockfile
      - run: pnpm biome ci && pnpm typecheck
      - run: pnpm core validate app/           # definitions load, references resolve
      - run: pnpm test --coverage              # Testcontainers starts PostgreSQL 18
      - run: pnpm test:acceptance              # API, invariants, permissions
      - run: pnpm openapi:diff                 # fails on breaking changes
  preview:
    needs: verify
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    permissions: { id-token: write, contents: read }   # keyless login to Google Cloud
    steps:
      - run: echo "build image, push to Artifact Registry, deploy Cloud Run revision tagged pr-${{ github.event.number }}"

Local in one command

Everything the system needs, including a fake mail server to see the quote mails.

services:
  core:
    build: .
    environment:
      DATABASE_URL: postgres://erp:erp@db:5432/erp
      PUBSUB_EMULATOR_HOST: pubsub:8085
      PDF_URL: http://pdf:3000
      OTEL_EXPORTER_OTLP_ENDPOINT: http://otel:4318
    ports: ["8080:8080"]
    depends_on: [db, pubsub, pdf]
  db:     { image: "postgres:18", environment: { POSTGRES_USER: erp, POSTGRES_PASSWORD: erp } }
  pubsub: { image: "gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators",
            command: ["gcloud", "beta", "emulators", "pubsub", "start", "--host-port=0.0.0.0:8085"] }
  pdf:    { image: "gotenberg/gotenberg:8" }
  mail:   { image: "axllent/mailpit", ports: ["8025:8025"] }

Hosting on Google Cloud

Managed services wherever possible, so the time goes into the experiment, not into operations. Containers, standard PostgreSQL, OpenTelemetry and Terraform keep a move possible.

ADR-013 platform · ADR-018 compute · ADR-019 database

PartServiceStatus
Core API, worker, PDFCloud Run services, scale to zeroaccepted
Batch workCloud Run Jobsaccepted
DatabaseCloud SQL for PostgreSQL 18, Enterprise editionaccepted
Events and timersPub/Sub, Cloud Schedulerproposed
ERP user interfaceFirebase Hosting, /api rewritten to Cloud Runaccepted
This websiteCloudflare Workers with static assets, deployed from GitHub on every pushaccepted
Secrets and filesSecret Manager, Cloud Storageaccepted
Images and deploysArtifact Registry, GitHub Actions without stored keysaccepted
ObservabilityCloud Trace, Logging, Monitoring via OpenTelemetryaccepted

Costs, in the open

Tokens, subscriptions and hosting are tracked per run and per month and published here once the first run is done.

tbdtokens per milestone
tbdhosting per month
tbdmy active hours
tbdcost per change request