Skip to content

Commit 2213c04

Browse files
authored
Merge pull request #1607 from LerianStudio/feat/br-spi-helm
feat(charts): add br-spi-helm chart for the SPI rail
2 parents 833eb3d + c2fe0ac commit 2213c04

41 files changed

Lines changed: 1528 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# CI-only render fixture for br-spi. Dummy values, NOT a production example and
2+
# NOT real credentials. Exercises the external-infra path (the target-environment
3+
# reality): all four components + the migration Job enabled, Postgres/Redis
4+
# external. The declared Bitnami subcharts stay disabled; the render gate still
5+
# runs the release-name collapse renders against them.
6+
core:
7+
configmap:
8+
POSTGRES_HOST: "pg.example.svc"
9+
STREAMING_BROKERS: "kafka-dummy:9092"
10+
secrets:
11+
POSTGRES_PASSWORD: "dummy-ci-password"
12+
13+
spi:
14+
configmap:
15+
POSTGRES_HOST: "pg.example.svc"
16+
STREAMING_BROKERS: "kafka-dummy:9092"
17+
BACEN_SPI_ENDPOINT: "http://mock-bacen.example.svc:8080"
18+
secrets:
19+
POSTGRES_PASSWORD: "dummy-ci-password"
20+
21+
brcode:
22+
configmap:
23+
POSTGRES_HOST: "pg.example.svc"
24+
secrets:
25+
POSTGRES_PASSWORD: "dummy-ci-password"
26+
27+
dict:
28+
configmap:
29+
POSTGRES_HOST: "pg.example.svc"
30+
REDIS_HOST: "redis.example.svc:6379"
31+
secrets:
32+
POSTGRES_PASSWORD: "dummy-ci-password"
33+
REDIS_PASSWORD: "dummy-ci-redis"
34+
35+
migrations:
36+
enabled: true
37+
postgres:
38+
host: "pg.example.svc"

charts/br-spi/Chart.lock

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
dependencies:
2+
- name: postgresql
3+
repository: https://charts.bitnami.com/bitnami
4+
version: 16.3.5
5+
- name: valkey
6+
repository: oci://registry-1.docker.io/bitnamicharts
7+
version: 2.4.7
8+
digest: sha256:52ff7322d903f193a03cdb12e017ea953e3f7c06cd6019216410086fef3bcd6b
9+
generated: "2026-07-02T18:30:40.65224-07:00"

charts/br-spi/Chart.yaml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
apiVersion: v2
2+
name: br-spi-helm
3+
description: A Helm chart for the br-sfn SPI (Pix) rail — the four Go binaries (core, spi, brcode, dict) that connect an FI to BACEN SPI/DICT/Pix and emit settlement CloudEvents onto a Redpanda bus
4+
5+
type: application
6+
annotations:
7+
lerian.studio/chart-type: multi-component
8+
9+
home: https://github.com/LerianStudio/helm
10+
11+
sources:
12+
- https://github.com/LerianStudio/helm/tree/main/charts/br-spi
13+
- https://github.com/LerianStudio/br-spi
14+
15+
maintainers:
16+
- name: "Lerian Studio"
17+
email: "support@lerian.studio"
18+
19+
version: 0.1.0-beta.1
20+
21+
# NOTE: appVersion is the default image tag for every component and the
22+
# migration Job. Override per component via <component>.image.tag when a
23+
# component needs to pin a different build.
24+
appVersion: "0.1.0"
25+
26+
keywords:
27+
- br-spi
28+
- spi
29+
- pix
30+
- bacen
31+
- dict
32+
- lerian
33+
- settlement
34+
35+
icon: https://avatars.githubusercontent.com/u/148895005?s=200&v=4
36+
37+
# Bitnami subcharts are DECLARED (exact pins, condition-gated) so the chart can
38+
# optionally bundle infra, but they default to disabled: on the target
39+
# environments Postgres and Redis are EXTERNAL and pre-provisioned. Chart.lock is
40+
# committed; re-verify Secret name/key against `helm template` on any pin bump.
41+
dependencies:
42+
- name: postgresql
43+
version: "16.3.5"
44+
repository: "https://charts.bitnami.com/bitnami"
45+
condition: postgresql.enabled
46+
- name: valkey
47+
version: "2.4.7"
48+
repository: "oci://registry-1.docker.io/bitnamicharts"
49+
condition: valkey.enabled

charts/br-spi/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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

Comments
 (0)