# 02-May-2026 — Inbound Forwarding warnings + Committee Settings catch-all admin [closes #256](https://github.com/nbarrett/ngx-ramblers/issues/256)

## [build 637](https://github.com/nbarrett/ngx-ramblers/actions/runs/25450246053) — [commit 4147694](https://github.com/nbarrett/ngx-ramblers/commit/4147694c1d909727dc0a320d1ce609f136bdcee5)

_____

## Part 1 — Inbound Forwarding warnings

Two alert-warning banners on the Inbound Forwarding tab, both rendered before the rule UI:

- **Domain mismatch**: fires when the site's primary host (from `systemConfig.group.href`) is not under the deployed Cloudflare zone's apex domain. This catches the Winchester-style failure where a per-env `zoneId` was empty, the deploy fell back to the global `zoneId`, and rules were silently created in the wrong zone.
- **Stale deploy**: fires when a per-env `zoneId` has been set in the staging `environments` doc but the running app's deployed `CLOUDFLARE_CONFIG` still has a different `zoneId` baked in (i.e. needs a redeploy).

The resolution chain in `cloudflare-config.ts` is unchanged; the new fields (`cloudflareZoneBaseDomain`, `primaryHost`, `perEnvironmentDbZoneId`) are added to `nonSensitiveCloudflareConfig()` purely to drive the warnings. Per-env `zoneId` is looked up by stripping the `ngx-ramblers-` prefix from `APP_NAME` and matching the `environments[]` entry.

Suffix matching uses a new `isHostUnderDomain()` helper in `projects/ngx-ramblers/src/app/functions/hosts.ts` (apex or subdomain, case-insensitive, strips a leading `www.`). Spec covers the apex, subdomain, `www-prefix`, the deceptive `notngx-ramblers.org.uk` non-match, and missing inputs.

## Part 2 — Manage zone catch-all rule from the Committee Settings admin

Today the catch-all rule for a zone (e.g. `*@ngx-ramblers.org.uk` -> `nick.barrett36@gmail.com`) is only configurable from Cloudflare's own dashboard. This brings it into NGX so the same admin surface that manages per-role rules also handles the catch-all, and adds the worker-based fan-out for multi-recipient catch-all delivery.

A new **"Catch-all rule for `*@<baseDomain>`"** card sits on the Committee Members tab of Committee Settings, between the action toolbar and the role table. It shows the current state as a status badge (Disabled / Drop / Forward -> `<addr>` / Multiple recipients (Worker)) and an **Edit** button. The editor exposes an action selector with four choices and a destination input that switches shape based on the action: a single email field for Forward, an add/remove list for Multiple. Worker-backed catch-all rules pre-load their existing recipients when editing so the operator sees what's currently configured rather than starting blank.

Server side adds an `updateCatchAllRule` helper that `PUT`s to `/zones/<zoneId>/email/routing/rules/catch_all`, plus a `PUT /api/cloudflare/email-routing/rules/catch-all` route. The route resolves the request shape via a `planCatchAllRule` helper that uploads a worker named `email-fwd-<zone>-catch-all` for the **Multiple** action (reusing the existing worker template machinery and `BREVO_RESEND` mode wiring from per-role workers) and tears down any previously-bound worker when switching to Drop, Disabled, or Forward. Rule names follow the existing `"<Mode label> - <description>"` convention so they read sensibly in Cloudflare's UI alongside per-role rules.

Frontend service exposes `updateCatchAllRule` which invalidates the relevant caches and pushes the new rule onto the existing `catchAllNotifications` `BehaviorSubject` so the badge refreshes after a save.

New model types: `CatchAllAction` enum, `UpdateCatchAllRequest` interface, `CatchAllResolution` interface (the latter for use by Part 3's catch-all-aware role view).

727/727 frontend tests pass.