Skip to content

Example: the Penvoice API

The generic recipe is in Onboard an app. This page is Penvoice filled in on this Project Loom node. Ayde, Z1, or the next API use the same folders with different names.

flowchart LR
  Push([merge app main]) --> CI[CI → GHCR sha]
  CI --> Stg[overlays/staging newTag]
  Stg --> Argo[Argo CD]
  Promo[Promote] --> Prod[overlays/prod same digest]
  Inf[Infisical GUI] -->|operator| Sec[(penvoice-api-kc)]
  Sec --> RO[Rollout]
  Argo --> RO

Staging host test.api.penvoice.app, prod api.penvoice.app. Namespaces penvoice-staging / penvoice-prod.

Distroless static arm64, tag ghcr.io/webb1es/penvoice-api:sha-<short>. Schema applies in-process at startup.

ApplicationSet rows in apps/workloads.yaml. Files under workloads/penvoice/base/ plus overlay patches (namespace, host, Infisical slug, image tag, analysis not present).

Infisical project penvoice, cells staging and prod. Git has projectId + slug only. Not in Infisical: DB_URL (CNPG uri), container port.

Same Infisical project, a third cell: dev. penvoice-api’s .infisical.json points at the same projectId as the overlays, defaultEnvironment: dev; infisical run -- go run . (or infisical run -- air) replaces .env. DB_URL and API_PORT are dev-only (no CNPG, no shared manifest locally); KC_ISSUER reuses the staging value (same UAT Keycloak); SWAGGER_SESSION_SECRET is its own value, not shared with staging/prod. Recipe: Onboard → Secrets § Wire local dev.

Prod: 25→50→75, 60s pauses. Staging: one replica, weight 100. The API may still export /metrics for a future scrape stack. Nothing in Git scrapes it today.