feat(billing): deploy the billing service, inert until enabled #507

Sammanfogat
supernaut sammanfogade 9 incheckningar från feat/billing-service-deploy in i main 2026-09-30 21:03:49 +00:00
Ägare

What

Deploys the billing service. Inert until billing_enabled: true, so merging and applying changes nothing that runs.

  • Role billing: Quadlet container from the org registry, pinned tag with a Renovate annotation, no auto-update. Read-only rootfs, all caps dropped, no-new-privileges, memory caps, healthcheck on /healthz. Env file mode 0600, rendered with no_log. Asserts every vault value exists when enabled.
  • Money gate: billing_payments_enabled (default false) renders PAYMENTS_ENABLED. Enabling the role alone moves no money.
  • Migrations: ExecStartPre runs node dist/src/migrate.js from the same image. Idempotent, and a failed migration fails the start.
  • Network: on the bitborg podman network, no published port. The portal reaches /trpc with a bearer token.
  • Caddy: only POST /webhooks/mollie on www.bitborg.se reaches the service, in its own rate-limit zone (60/min per IP, private ranges exempt). Every other /webhooks/* request returns 404. /trpc is never routed publicly.
  • Postgres: its own database and role, same mechanism as the portal. The password is reset from the vault on every run, and PUBLIC loses access to the database.
  • Kanidm: service account bitborg-billing is entry manager of tier_pro and nothing else. No person reads: the portal passes display name and email. overwriteMembers stays false.
  • Portal: gets BILLING_API_URL and BILLING_API_TOKEN only when enabled. The unit renders byte-identical when disabled.
  • Backup: the nightly job dumps the database (fails closed on an empty dump). The weekly verify runs pg_restore --list, and the drill restores it and asserts at least one table. All guarded on the file existing.
  • Monitoring: added to monitoring_core_units when enabled, so the existing unit-down, unhealthy and flapping alerts cover it.
  • Runbook: section "Billing service" covers deploy, first enable, rotation and rollback.

No webhook secret. Mollie does not sign webhooks. The service re-fetches each payment by id and trusts only that, so the issue's "webhook secret" item does not apply.

Verified

  • Pre-push: ansible-lint, ruff, gitleaks, markdownlint, prettier, tofu validate, Renovate annotations, metric names. shellcheck ran on the local 0.11 binary, not the pinned 0.10.
  • Caddy routing with a local Caddy and stub backends: only POST /webhooks/mollie reached billing. GET and other paths returned 404, and /trpc/x went to the portal.
  • Kanidm state rendered with the flag off (identical to main) and on (one added line: entryManagedBy: bitborg-billing on tier_pro).
  • Read-only prod --check, billing disabled: changed=3 failed=0. The backup, verify and drill scripts gain billing blocks that do nothing while billing is off.
  • --check -e billing_enabled=true fails at the vault assert with a clear message.

Not verified until first enable

  • Whether an account (not a group) as entry_managed_by grants member write on tier_pro. It is a runbook verify step.
  • The image: the first release tag does not exist yet. bitborg_billing_image_tag is a placeholder v0.2.0.

First enable

  1. Release the billing image and set bitborg_billing_image_tag to it.
  2. Create the bitborg-billing Kanidm service account and a read-write API token.
  3. Add the vault values: vault_payments_mollie_api_key_test (test_ only), vault_billing_db_password, vault_billing_api_token, vault_kanidm_billing_token.
  4. Set billing_enabled: true, dry-run, apply --tags kanidm,billing,web,caddy,backup,monitoring-agent.
  5. Verify as in the runbook, then set billing_payments_enabled: true and run a test checkout.

Refs #496. It closes once a test payment grants the paid tier end to end and the drill restores the billing database.

## What Deploys the billing service. Inert until `billing_enabled: true`, so merging and applying changes nothing that runs. - **Role `billing`:** Quadlet container from the org registry, pinned tag with a Renovate annotation, no auto-update. Read-only rootfs, all caps dropped, no-new-privileges, memory caps, healthcheck on `/healthz`. Env file mode 0600, rendered with `no_log`. Asserts every vault value exists when enabled. - **Money gate:** `billing_payments_enabled` (default false) renders `PAYMENTS_ENABLED`. Enabling the role alone moves no money. - **Migrations:** `ExecStartPre` runs `node dist/src/migrate.js` from the same image. Idempotent, and a failed migration fails the start. - **Network:** on the `bitborg` podman network, no published port. The portal reaches `/trpc` with a bearer token. - **Caddy:** only `POST /webhooks/mollie` on `www.bitborg.se` reaches the service, in its own rate-limit zone (60/min per IP, private ranges exempt). Every other `/webhooks/*` request returns 404. `/trpc` is never routed publicly. - **Postgres:** its own database and role, same mechanism as the portal. The password is reset from the vault on every run, and `PUBLIC` loses access to the database. - **Kanidm:** service account `bitborg-billing` is entry manager of `tier_pro` and nothing else. No person reads: the portal passes display name and email. `overwriteMembers` stays false. - **Portal:** gets `BILLING_API_URL` and `BILLING_API_TOKEN` only when enabled. The unit renders byte-identical when disabled. - **Backup:** the nightly job dumps the database (fails closed on an empty dump). The weekly verify runs `pg_restore --list`, and the drill restores it and asserts at least one table. All guarded on the file existing. - **Monitoring:** added to `monitoring_core_units` when enabled, so the existing unit-down, unhealthy and flapping alerts cover it. - **Runbook:** section "Billing service" covers deploy, first enable, rotation and rollback. **No webhook secret.** Mollie does not sign webhooks. The service re-fetches each payment by id and trusts only that, so the issue's "webhook secret" item does not apply. ## Verified - Pre-push: ansible-lint, ruff, gitleaks, markdownlint, prettier, tofu validate, Renovate annotations, metric names. shellcheck ran on the local 0.11 binary, not the pinned 0.10. - Caddy routing with a local Caddy and stub backends: only `POST /webhooks/mollie` reached billing. `GET` and other paths returned 404, and `/trpc/x` went to the portal. - Kanidm state rendered with the flag off (identical to main) and on (one added line: `entryManagedBy: bitborg-billing` on `tier_pro`). - Read-only prod `--check`, billing disabled: `changed=3 failed=0`. The backup, verify and drill scripts gain billing blocks that do nothing while billing is off. - `--check -e billing_enabled=true` fails at the vault assert with a clear message. ## Not verified until first enable - Whether an account (not a group) as `entry_managed_by` grants member write on `tier_pro`. It is a runbook verify step. - The image: the first release tag does not exist yet. `bitborg_billing_image_tag` is a placeholder `v0.2.0`. ## First enable 1. Release the billing image and set `bitborg_billing_image_tag` to it. 2. Create the `bitborg-billing` Kanidm service account and a read-write API token. 3. Add the vault values: `vault_payments_mollie_api_key_test` (`test_` only), `vault_billing_db_password`, `vault_billing_api_token`, `vault_kanidm_billing_token`. 4. Set `billing_enabled: true`, dry-run, apply `--tags kanidm,billing,web,caddy,backup,monitoring-agent`. 5. Verify as in the runbook, then set `billing_payments_enabled: true` and run a test checkout. Refs #496. It closes once a test payment grants the paid tier end to end and the drill restores the billing database.
supernaut lade till 9 incheckningar 2026-09-30 19:24:19 +00:00
supernaut sammanfogade incheckning 3517b05392 till main 2026-09-30 21:03:49 +00:00
supernaut tog bort grenen feat/billing-service-deploy 2026-09-30 21:03:49 +00:00
Logga in för att delta i denna konversation.
Inga granskare
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-infra!507
Ingen beskrivning angiven.