|
| 1 | +# br-spi Helm Chart |
| 2 | + |
| 3 | +Deploys the **br-sfn SPI (Pix) rail** — the four Go binaries (`core`, `spi`, `brcode`, `dict`) that connect a financial institution to BACEN SPI / DICT / Pix — into Kubernetes. When streaming is enabled, `core` becomes a CloudEvents **producer**, emitting `studio.lerian.settlement.*` onto the `br-spi.settlement` topic of a Redpanda/Kafka bus. |
| 4 | + |
| 5 | +## Chart Contract |
| 6 | + |
| 7 | +- Chart type: `multi-component` |
| 8 | +- Required secrets: **None for a default render.** For external Postgres (the default), provide each component's `POSTGRES_PASSWORD` (all four share the single `brspi` database) via `<component>.secrets.POSTGRES_PASSWORD` or `<component>.useExistingSecret`; on the target environments this is sourced from GitOps/Vault. `dict` additionally needs `REDIS_PASSWORD` when its external Redis requires auth. When the bundled `postgresql`/`valkey` subcharts are enabled instead, those passwords are **single-sourced** from the subchart Secrets (read at runtime via `secretKeyRef`) and are not stored in the app Secrets. No credential is ever placed in a ConfigMap. Optional at-rest / PII keys added under `<component>.secrets` are emitted only when set. |
| 9 | +- Dependency notes: Bundled `postgresql` (16.3.5) and `valkey` (2.4.7) Bitnami subcharts are **declared but disabled by default** — Postgres and Redis are external and pre-provisioned on the target environments. Kafka/Redpanda is external and referenced only by `STREAMING_BROKERS` (plaintext; no SASL/TLS knobs). Enable the subcharts only for a self-contained install. |
| 10 | +- Production overrides: Set `<component>.image.tag` (defaults to `Chart.appVersion`); `POSTGRES_HOST` and `POSTGRES_PASSWORD` for every enabled component; `STREAMING_BROKERS` for `core` and `spi` (a producer with `STREAMING_ENABLED=true` and empty `STREAMING_BROKERS`/`STREAMING_CLOUDEVENTS_SOURCE` **fails closed at boot** by design); `REDIS_HOST` for `dict`; and per-component `ingress`, `autoscaling`, and `resources` as needed. `useExistingSecret`/`existingSecretName` are supported per component and for the migration Job. |
| 11 | +- Source/license: Source is in `github.com/LerianStudio/helm`; chart license is Apache-2.0. The `br-spi` service source is `github.com/LerianStudio/br-spi`. |
| 12 | + |
| 13 | +## Components |
| 14 | + |
| 15 | +All four components are the same Go binary family (identical deployment shape). The chart renders them from one shared template library, so env — especially `STREAMING_*` — is emitted uniformly and a component can never silently drop a producer knob. |
| 16 | + |
| 17 | +| Component | Image (default) | Listens | Streams | Role | |
| 18 | +|-----------|-----------------|---------|---------|------| |
| 19 | +| `core` | `ghcr.io/lerianstudio/br-sfn-core` | `:8080` | **yes** | Settlement engine; sole producer of `studio.lerian.settlement.*` | |
| 20 | +| `spi` | `ghcr.io/lerianstudio/br-sfn-spi` | `:8080` | **yes** | ISO 20022 / BACEN SPI; writes the `spi->core` seam | |
| 21 | +| `brcode` | `ghcr.io/lerianstudio/br-sfn-brcode` | `:8080` | no | Pix BR Code (QR) REST | |
| 22 | +| `dict` | `ghcr.io/lerianstudio/br-sfn-dict` | `:8080` | no | DICT (Pix directory) REST; **requires `REDIS_HOST`** | |
| 23 | + |
| 24 | +Each component exposes: `<component>.enabled`, `image.{repository,tag,pullPolicy}`, `replicaCount`, `service`, `ingress`, `autoscaling`, `pdb`, `resources`, `configmap` (map, emitted verbatim), `secrets`, `useExistingSecret`/`existingSecretName`, `extraEnvVars` (raw env list escape). Probes are `/health` (liveness) and `/readyz` (readiness) on the `http` port (containerPort `8080`). |
| 25 | + |
| 26 | +### Settlement producer coupling |
| 27 | + |
| 28 | +`core` produces `settlement.*` only by **consuming** the `spi->core` seam topics that `spi` writes. Therefore **both** `core` and `spi` must have `STREAMING_ENABLED=true`, the **same** `STREAMING_BROKERS`, and the **same** Postgres, or no settlement event is emitted at all. `brcode` and `dict` are REST-only and stream `false`. |
| 29 | + |
| 30 | +## Configuration knobs (ConfigMap passthrough) |
| 31 | + |
| 32 | +`<component>.configmap` is emitted verbatim into the component ConfigMap — it is **not** a fixed allowlist. Any additional env (BACEN endpoints, Postgres pool tuning, signer/JOSE knobs) can be added under `<component>.configmap` without editing the chart. `STREAMING_ENABLED`, `STREAMING_BROKERS`, `STREAMING_CLOUDEVENTS_SOURCE`, `POSTGRES_HOST`, and `REDIS_HOST` are handled first-class by the template (the hosts derive from the bundled subchart when enabled). Secrets must go under `<component>.secrets`, never `configmap`. |
| 33 | + |
| 34 | +| Key | Default | Notes | |
| 35 | +|-----|---------|-------| |
| 36 | +| `ENV_NAME` | `development` | SPI env; `development` relaxes BACEN mTLS + accepts `http://`/mock endpoints | |
| 37 | +| `SERVER_ADDRESS` | `:8080` | host:port form (container port is 8080) | |
| 38 | +| `POSTGRES_{HOST,PORT,USER,DB,SSLMODE}` | see values | All four share the `brspi` database | |
| 39 | +| `REDIS_HOST` | `""` | host:port; **required for `dict`** | |
| 40 | +| `OUTBOX_ENABLED` | `true` | keep on for the producer | |
| 41 | +| `IDEMPOTENCY_RETRY_WINDOW_SEC` | `300` | `core` only; must be `> 0` | |
| 42 | +| `STREAMING_ENABLED` | `true` (core/spi), `false` (brcode/dict) | fail-closed when brokers/source empty | |
| 43 | +| `STREAMING_BROKERS` | `""` | CSV; **required when streaming enabled** | |
| 44 | +| `STREAMING_CLOUDEVENTS_SOURCE` | `studio.lerian.core` / `studio.lerian.spi` | required when streaming enabled | |
| 45 | +| `ENABLE_TELEMETRY` | `false` | when `true`, `OTEL_EXPORTER_OTLP_ENDPOINT` is set to `$(HOST_IP):4317` (gRPC) | |
| 46 | +| `OTEL_RESOURCE_SERVICE_NAME` | `br-spi-<component>` | distinct per binary so dashboards don't collapse | |
| 47 | + |
| 48 | +## Detached migrations |
| 49 | + |
| 50 | +`migrations.enabled` (default `true`) ships an ArgoCD **PreSync** Secret (`hook-weight: -2`) + Job (`hook-weight: -1`) that runs `ghcr.io/lerianstudio/br-sfn-spi-migrations`. That image's entrypoint applies the 6-module `golang-migrate` loop in manifest order (`global events spi dict brcode core`) to the single `brspi` database, using the per-module `x-migrations-table=schema_migrations_<module>` naming that `/readyz` reads back. `systemplane` is **not** applied by `golang-migrate` — it is managed by lib-systemplane at runtime. br-sfn runtime binaries are detached — they verify the schema on boot and refuse to start unmigrated — so the PreSync Job runs before every component Deployment. The Job pod is hardened (non-root, read-only rootfs, drop ALL, no service-account token) and waits for Postgres via a `busybox` initContainer. Supports `migrations.useExistingSecret`/`existingSecretName`. |
| 51 | + |
| 52 | +Source code: |
| 53 | +* https://github.com/LerianStudio/helm/tree/main/charts/br-spi |
| 54 | +* https://github.com/LerianStudio/br-spi |
| 55 | + |
| 56 | +## Install |
| 57 | + |
| 58 | +```console |
| 59 | +$ helm install br-spi oci://ghcr.io/lerianstudio/br-spi-helm --version 0.1.0 -n br-spi --create-namespace \ |
| 60 | + --set core.configmap.POSTGRES_HOST=pg-dev,core.secrets.POSTGRES_PASSWORD=... \ |
| 61 | + --set core.configmap.STREAMING_BROKERS=kafka-dev:9092 |
| 62 | +``` |
| 63 | + |
| 64 | +## External vs bundled infrastructure |
| 65 | + |
| 66 | +Default (external): leave `postgresql.enabled=false` and `valkey.enabled=false`, set `POSTGRES_HOST`/`REDIS_HOST` per component, and provide `POSTGRES_PASSWORD` (and `REDIS_PASSWORD` if the Redis needs auth). |
| 67 | + |
| 68 | +Bundled (self-contained): set `postgresql.enabled=true` (and `valkey.enabled=true`); the app reads the subchart-generated passwords via `secretKeyRef` (single-sourced), and `POSTGRES_HOST`/`REDIS_HOST` derive from the subchart Service names collapse-aware via `common.names.dependency.fullname`. |
| 69 | + |
| 70 | +## Support & Community |
| 71 | + |
| 72 | +- **GitHub Issues**: https://github.com/LerianStudio/br-spi/issues |
| 73 | +- **Email**: contact@lerian.studio |
0 commit comments