> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/developer-guides/architecture-decision-log/adr-0040-notifications-disabled-by-default.md).

# ADR-0040: Notifications ship disabled by default behind one condition

## Status

**Accepted.** Reconstructed from the codebase on 2026-05-30; the decision is live in the source today.

## Context

The Notifications subsystem is heavyweight: it consumes the Postgres write-ahead log via a replication slot, runs a background subscriber, and fans alerts out to external channels. It spans several Spring components (a bean-wiring `@Configuration`, a startup subscriber, a message processor). Most deployments don't need it. The platform needs it off by default, turned on by a single switch, with no risk that one component activates while another stays dormant.

## Decision

**The Notifications subsystem ships `notifications.enabled: false` and is gated by a single `Condition` class consulted through one `@ConditionalOnNotifications` meta-annotation applied to every Notifications component.** The condition (`NotificationsFeatureCondition`) reads `notifications.enabled` from Spring's `Environment`, defaulting to `false`. The reusable `@ConditionalOnNotifications` annotation wraps `@Conditional(NotificationsFeatureCondition.class)` and is placed on all three top-level components — the `@Configuration` that wires the senders, the startup subscriber, and the alert message processor. When the property is not `true`, none of the three register and the WAL subscriber never starts.

The design is **single-source-of-truth gating**: one property, one condition class, one meta-annotation. A developer changing the gating semantics (say, sourcing the flag from a feature-flag service) edits one file rather than each component's annotation. This is the meta-annotation variant of the platform's ship-disabled-by-default family — the same posture as GenAI (ADR-0004, inline) and Data Collaboration (ADR-0019, which uses the identical meta-annotation idiom).

## Consequences

* A default deployment runs with notifications entirely absent — no subscriber, no WAL replication slot created, no senders wired.
* The subsystem turns on as a unit: setting `notifications.enabled: true` activates all three components together, so there is no partially-enabled state where (for example) the processor exists but the subscriber doesn't.
* Enabling the subsystem is necessary but not sufficient to receive notifications — individual channels then activate by the presence of their own keys (ADR-0041). The two together form a two-stage opt-in.
* Centralising the flag in one condition class avoids the drift that scattered per-component property checks would cause; the trade-off is one extra indirection (a meta-annotation) over a bare `@ConditionalOnProperty`, chosen deliberately because the flag has three consumers.

## Evidence

* `odd-platform-api/src/main/resources/application.yml:173` — `enabled: false` under the `notifications:` key (line 172), the verbatim shipped default.
* `odd-platform-api/.../notification/config/NotificationsFeatureCondition.java:11-13` — reads `FeatureResolver.NOTIFICATIONS_ENABLED_PROPERTY` from the `Environment` with default `false`.
* `odd-platform-api/.../notification/config/ConditionalOnNotifications.java:9-12` — the meta-annotation: `@Conditional(NotificationsFeatureCondition.class)`.
* The annotation is applied to all three components: `NotificationConfiguration.java:27` (`@Configuration`), `NotificationSubscriberStarter.java:17` (startup subscriber), `AlertNotificationMessageProcessor.java:15` (alert processor).

## See also

* [ADR-0041 — Notification channels activate by the presence of their keys](/developer-guides/architecture-decision-log/adr-0041-notification-per-channel-presence-activation.md) — the second stage of the opt-in, once the subsystem is enabled.
* [ADR-0004 — GenAI ships disabled by default](/developer-guides/architecture-decision-log/adr-0004-genai-disabled-by-default.md) and [ADR-0019 — Data Collaboration ships disabled by default](/developer-guides/architecture-decision-log/adr-0019-data-collaboration-disabled-by-default.md) — the same ship-off-by-default posture.
