# Configuration inventory — for centralising config

Read-only survey of the whole monorepo (`vendor/`, `node_modules/` and build output excluded).
Nothing was changed. Secret **values are masked**; every location is given as `path:line` so you
can open them directly.

**Scale:** 175 config files across 12 packs · 49 distinct env vars read from code · only 25
set anywhere on the platform · at least 16 files carry a hardcoded credential.

| Pack | config files | | Pack | config files |
|---|---|---|---|---|
| b2g_filemst | 38 | | b2g_cengex | 21 |
| b2g_digitall | 23 | | b2g_profilemgmt | 20 |
| nibe_datamanager | 22 | | b2g_mail | 20 |
| b2g_usermgmt | 22 | | b2g_gmarketapi | 20 |
| b2g_micro | 22 | | b2g / gMarket / b2g_website / b2g_filems_fe | 27 |
| b2g_confmgmt | 21 | | docker + root | 5 |

---

## 1. Mail configuration

Two different SMTP identities, both with plaintext passwords in the repo.

| Setting | Location | Value |
|---|---|---|
| SMTP host | `b2g_mail/config/web.php:103` | `mail.blu2green.earth` |
| SMTP user | `b2g_mail/config/web.php:104` | `mailsender@blu2green.earth` |
| **SMTP password** | `b2g_mail/config/web.php:105` | `SW2*****` — **plaintext** |
| Port / encryption | `b2g_mail/config/web.php:106-107` | `25`, encryption `null` (unencrypted) |
| SMTP host | `b2g_filemst/common/config/base.php:219` | `mail.businessgateways.com` |
| SMTP user | `b2g_filemst/common/config/base.php:220` | `maillocal@businessgateways.com` |
| **SMTP password** | `b2g_filemst/common/config/base.php:221` | `Mai**********` — **plaintext, in 3 files** |
| — duplicate | `b2g_filemst/api/config/base.php:17-19` | same three values |
| — duplicate | `b2g_filemst/backend/config/web.php:89-91` | same three values |
| Env-driven variant | `b2g_filemst/common/config/base.php:437-438` | `env('SMTP_HOST')`, `env('SMTP_PORT')` — **never set**, see §4 |
| `useFileTransport` | `b2g_confmgmt/config/web.php:101`, `b2g_digitall/config/web.php:107` | `true` — **mail is silently discarded to disk** |
| `useFileTransport` | `b2g_cengex/config/web.php:97` | `($env !== 'live')` |
| Sender identity | `*/config/params*.php` (`adminEmail`, `senderEmail`) | mostly still `admin@example.com` |

**Note:** `confmgmt` and `digitall` have `useFileTransport => true` unconditionally, so any mail
those two try to send is written to a file and never delivered.

---

## 2. Database configuration

**11 packs × 5 environment files each** (`db-local`, `db-dev`, `db-demo`, `db-uat`, `db-prod`) —
55 files describing the same handful of databases.

### What each pack points at (schema, from the `dsn` line)

| Pack | Schema | Representative line |
|---|---|---|
| b2g_micro, b2g_usermgmt, b2g_profilemgmt, b2g_gmarketapi | `bgi_b2g_master_*` | `b2g_micro/config/db-local.php:18` |
| b2g_confmgmt, b2g_mail | `bgi_b2g_reg_*` | `b2g_confmgmt/config/db-local.php:18` |
| b2g_cengex | `bgi_b2g_cengex_dev` | `b2g_cengex/config/db-local.php:18` |
| b2g_digitall | `bgi_b2g_digitall_*` | `b2g_digitall/config/db-local.php:18` |
| nibe_datamanager | `bgi_bridge` | `nibe_datamanager/config/db-uat.php:19` |
| b2g_filemst | 4 connections: main, `j2`, `estore`, `audit` | `b2g_filemst/common/config/base.php:230,241,252,263` |

**Inconsistency worth noting:** the `_local` and `_uat` files disagree about which schema a pack
uses. `b2g_usermgmt/config/db-local.php:18` uses `bgi_b2g_reg_dev` while
`db-uat.php:19` uses `bgi_b2g_master_uat`. Same for `profilemgmt` and `gmarketapi`. Whichever is
right, they cannot both be.

### Credentials

| What | Location | Status |
|---|---|---|
| `devops` / `Dev******` | 9 × `*/config/db-local.php` (e.g. `b2g_micro/config/db-local.php:19-20`) | Fallback only — env wins. Office LAN credential, **still live on 192.168.1.200** |
| `admin` / `CEf*****************` | `b2g_mail/config/db-demo.php:6-7`, `db-uat.php:5-7` | **RDS master-style credential, plaintext.** Points at `database-2.c5myq6iqolml…` — a *different* live RDS instance. Verified it does **not** match the current `uat-rds-db` master, but may still be valid on `database-2` |
| `PROD_DB_USER` / `PROD_DB_PASSWORD` | 8 × `*/config/db-prod.php:9-10` | Placeholders — prod configs are unfilled templates |
| `bgi*****` / `c0Z************` | `b2g_filemst/config.php:82-83` | Plaintext |
| Env-driven | `DB_HOST/PORT/USER/PASSWORD` in 33 places | The path AWS actually uses |

**On the platform:** all 9 backends run `APP_ENV=local` → they load `db-local.php`, whose
env vars are injected from Secrets Manager (`b2g/uat/db-app`). So the `db-uat.php` /
`db-demo.php` / `db-prod.php` files are **dead code in AWS** — they only matter as an exposure.

---

## 3. Third-party integrations

| Integration | Location | Notes |
|---|---|---|
| **Keycloak / SSO** | `b2g_digitall/config/params-uat.php:64-74`; also `params-local.php:112`, `params-demo.php:69` | `baseUrl` `ssouat.blu2green.earth`, realm `master`, `adminClientId` `b2g-ad…`, **`adminClientSecret` `CVCg3i**********` plaintext**. Consumed by `components/KeycloakService.php`, `KeycloakServiceFixed.php:222-223` |
| **Synesgy** | `b2g_digitall/commands/SynesgyController.php:75`; `components/SynesgyTokenService.php:122-123, 202-203` | `service.uat.synesgy.com` (28 refs), `api.uat.synesgy.com` (12), `service.synesgy.com` (7 — prod). `serviceAccountClientId` / `serviceAccountClientSecret` from params |
| **Breathe ESG** | `b2g_digitall/config/params-local.php:45` (`apiKey`), `params-uat.php`, `params-demo.php:43` | **API key `cGfwfLDUX-…` plaintext (86 chars).** Endpoints: `api.breatheesg.com`, `testapiv1.breatheesg.com`, `app.breatheesg.com`, `esg-dev.breatheesg.com`. Read at `components/Breathesg.php:29`, `TokenRefreshService.php:27`, `ConfenggController.php:2237,2308` |
| **PostHog** (analytics) | all 7 × `b2g/src/environments/environment*.ts` (e.g. `environment.docker.ts:37`) | `posthogKey: 'phc_ykQ…'`, host `us.i.posthog.com`. Same key in every environment. Ships in the browser bundle, so public by design |
| **Zoho SalesIQ** | `b2g/src/app/core/services/zoho-salesiq.service.ts:83` | `salesiqzohopublic.com` widget |
| **Google reCAPTCHA** | `b2g/src/index.html` | `google.com/recaptcha/api.js` |
| **AWS S3** (file storage) | `b2g_filemst/common/config/web.php:52-58` | `S3_BUCKET/REGION/KEY/SECRET/PREFIX` via `env()` — **`S3_KEY` and `S3_SECRET` are never set**, see §4 |
| **GitHub / Facebook OAuth** | `b2g_filemst/frontend/config/web.php:20-21, 25-26` | `*_CLIENT_ID` / `*_CLIENT_SECRET` via `env()` — never set |
| **ARA / contract import** | `b2g_filemst/config.php:422` | `userName` `JSR******`, `password` `Sap*******`, three import URLs — plaintext |
| **Additional creds** | `b2g_filemst/config.php:425, 486, 503, 618-619, 633-634` | 5 more username/password/key pairs, plaintext |
| **Placeholder domains** | `*/config/params-demo.php`, `params-uat.php`, `environment.prod.ts` | `yourdomain.com` (23), `uat.yourdomain.com` (21), `demo.yourdomain.com` (20), `your-prod-domain.com` (16) — unconfigured templates |

---

## 4. Common settings and environment variables

**49 distinct env vars are read from code. Only 24 of them are ever set.** The other **25
silently resolve to null/false at runtime:**

```
ADMIN_EMAIL          API_BASE_URL        API_HOST_INFO       APP_MAINTENANCE
AUDIT_IP_ADDRESS     BACKEND_BASE_URL    BACKEND_HOST_INFO   DB_CHARSET
FACEBOOK_CLIENT_ID   FACEBOOK_CLIENT_SECRET   FRONTEND_BASE_URL   FRONTEND_HOST_INFO
GITHUB_CLIENT_ID     GITHUB_CLIENT_SECRET     GLIDE_MAX_IMAGE_SIZE GLIDE_SIGN_KEY
HOST_IP              LINK_ASSETS         ROBOT_EMAIL         S3_KEY
S3_SECRET            SMTP_HOST           SMTP_PORT           STORAGE_BASE_URL
STORAGE_HOST_INFO
```

This is not theoretical. **`ROBOT_EMAIL` and `ADMIN_EMAIL` being unset is what took every
`/filemst/api/` route down with a 500 on 11 Aug**: `base.php` attaches a Yii `EmailTarget` whose
`from`/`to` come from those two, both resolved to null, and Yii rejected the log target with
`InvalidConfigException` before any controller ran. `S3_KEY`/`S3_SECRET` unset means the S3
storage driver cannot authenticate if `STORAGE_DRIVER=s3` is ever switched on.

### What the platform actually provides

| Service group | Plain env | From Secrets Manager |
|---|---|---|
| 8 backends (micro, usermgmt, profilemgmt, confmgmt, digitall, cengex, gmarketapi, mail) | 13 each | `DB_USER`, `DB_PASSWORD` |
| filemst | 17 | `DB_USER`, `DB_PASSWORD`, `BACKEND_COOKIE_VALIDATION_KEY`, `FRONTEND_COOKIE_VALIDATION_KEY` |
| nibe | 3 | `DB_USER`, `DB_PASSWORD`, `COOKIE_VALIDATION_KEY` |
| kong | 2 | — |
| 5 frontends | 0–1 | — |

Secrets in AWS today: `b2g/uat/rds-master`, `b2g/uat/db-app`, `b2g/uat/filemst-cookie-keys`,
`b2g/uat/nibe-cookie-key`.

### East-west service URLs

The same handful of inter-service URLs is redeclared per pack in `params-local.php`:
`MICRO_BASE_URL` (8 refs), `USERMGMT_BASE_URL` (5), `FILEMST_BASE_URL` (5),
`PROFILEMGMT_BASE_URL` (3), plus `CENGEX_BASE_URL`, `DIGITALL_BASE_URL`, `GMARKETAPI_BASE_URL`,
`MAIL_BASE_URL`, `MICRO_API_BASE_URL`, `BSGPACK_BASE_URL` — each declared once per consuming pack
rather than once overall.

### Cookie validation keys

| Location | Status |
|---|---|
| `nibe_datamanager/config/web.php:30` | `getenv('COOKIE_VALIDATION_KEY')` with a committed literal fallback |
| `b2g_filemst/backend/config/web.php:100`, `frontend/config/web.php:50` | env-driven, secrets set |
| 8 backends: `*/config/web.php`, `params-*.php`, `test.php` | committed literals — 3 distinct real values across ~40 files |

---

## 5. Findings, most serious first

1. **A live-looking RDS master credential is committed** — `b2g_mail/config/db-uat.php:5-7` and
   `db-demo.php:5-7`, user `admin`, pointing at `database-2.c5myq6iqolml…`. It does not match the
   current `uat-rds-db` master, but `database-2` still exists. **Verify and rotate.**
2. **Two SMTP passwords in plaintext**, one duplicated across three files. Both mailboxes are
   real and presumably still work. `b2g_mail` also sends on **port 25 with `encryption => null`**.
3. **Breathe ESG API key and Keycloak `adminClientSecret` in plaintext** in `b2g_digitall/config/`.
   These are third-party credentials — rotating them needs coordination with those vendors.
4. **25 env vars are read but never set.** One instance already caused a production outage.
5. **Everything above is in git history**, so rotating the value matters more than editing the
   file — the old value stays readable in the history of whoever has cloned it.
6. **`confmgmt` and `digitall` discard all outbound mail** (`useFileTransport => true`).
7. **`db-local` vs `db-uat` disagree on the schema** for usermgmt, profilemgmt and gmarketapi.
8. **`db-prod.php` files are unfilled placeholders** in all 8 packs — production has no config.

---

## 6. A note on the "one common file" goal

Consolidating this is clearly right — but in a microservices setup on ECS the target is usually
**two layers, not one file**:

- **Non-secret shared settings** (service URLs, schema names, timeouts, feature flags) →
  one source per environment, injected as env vars. AWS AppConfig or SSM Parameter Store
  fits, and both are readable by the existing task role. A single committed file would still
  have to be rebuilt into every image to change a value, which is what makes today's setup
  painful.
- **Secrets** (DB, SMTP, API keys, cookie keys) → **Secrets Manager only**, referenced by ARN
  in the task definition, as `DB_USER`/`DB_PASSWORD` already are for all 10 backends. That
  pattern works today and is worth extending to the remaining ~10 credentials rather than
  inventing a new one.

The single biggest simplification available is not the file format: it is deleting the
**55 `db-*.php` files down to one per pack**. All 9 backends run `APP_ENV=local`, so 4 of every
5 of those files are dead code that exists only to leak credentials.

Worth deciding early: whether config is resolved **at image build** (baked in, needs a rebuild
per change) or **at container start** (env/AppConfig, no rebuild). The repo currently does both,
which is the root of the `db-uat.php`-shaped confusion.
