> 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-0012-attachment-storage-backend-boot-selection.md).

# ADR-0012: Attachment storage backend is selected at boot

## Status

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

## Context

Data-entity attachments (uploaded files and remote-URL links) need somewhere to live. ODD Platform supports two backends: a **LOCAL** filesystem path inside the platform container, and a **REMOTE** S3-compatible object store (MinIO or AWS S3). The platform needs a way to choose between them, and a default for operators who set nothing.

The choice is *which moment* the backend is fixed: as a runtime switch the platform could flip per request, or as a boot-time decision baked into the bean graph. ODD chose boot-time wiring — the backend is decided once, when the application context starts.

## Decision

**The attachment storage backend is selected at boot via `@ConditionalOnProperty` on the `attachment.storage` property.** Each backend's beans are conditionally created: the LOCAL implementations carry `@ConditionalOnProperty(value = "attachment.storage", havingValue = "LOCAL", matchIfMissing = true)`, and the REMOTE (MinIO) implementations carry `havingValue = "REMOTE"`. Exactly one backend's beans are instantiated for the lifetime of the process.

**`LOCAL` is the implicit default.** The `matchIfMissing = true` on the LOCAL beans means a deployment that never sets `attachment.storage` runs LOCAL. The shipped `application.yml` also states `storage: LOCAL` explicitly, so the default is visible to an operator reading the config, not only implied by the code.

Because the selection is a `@ConditionalOnProperty` condition evaluated at context startup, **switching backends requires a Platform restart** — there is no runtime toggle.

## Consequences

* An operator chooses the backend with one property and a restart; the rest of the attachment code is backend-agnostic (both implement the same `FileUploadService` / `FilePathConstructor` interfaces).
* 📌 **The default backend is durability-limited, and the operator docs carry the caveat.** Under `LOCAL`, files are written to a container-local path (`attachment.local.path`, default `/tmp/odd/attachments`). On a containerised deployment that path is wiped on any container or pod restart, so a default deployment loses uploaded attachments on restart. This is a consequence operators must plan for — the [Attachments](/features/data-discovery/attachments.md) page and the [Attachment Storage Configuration](/configuration-and-deployment/odd-platform.md#attachment-storage-configuration) operator reference carry the full guidance (use `REMOTE` for any deployment where users actually upload files, plus the in-flight chunk-staging and AWS S3 region caveats). This ADR records *why* the boot-time `LOCAL`-default selection exists; those pages tell an operator *what to do about it*.
* Adding a third backend means adding a new `@ConditionalOnProperty(havingValue = "…")` implementation set, not a runtime branch — the selection mechanism scales by adding conditioned bean sets.

## Evidence

* `odd-platform-api/.../service/attachment/local/LocalFileUploadServiceImpl.java:26` and `.../local/LocalFilePathConstructor.java:13` — `@ConditionalOnProperty(value = "attachment.storage", havingValue = "LOCAL", matchIfMissing = true)`; LOCAL is the implicit default.
* `odd-platform-api/.../service/attachment/remote/RemoteFileUploadServiceImpl.java:36` and `.../config/MinioConfig.java:10` — `@ConditionalOnProperty(value = "attachment.storage", havingValue = "REMOTE")`; the MinIO/S3 backend.
* `odd-platform-api/src/main/resources/application.yml:216` — `storage: LOCAL` (the explicit shipped default); `:219` — `local.path: /tmp/odd/attachments` (the container-local default path).
* `odd-platform-api/.../service/attachment/local/LocalFilePathConstructor.java:15-16` — the LOCAL path is bound from `attachment.local.path`, confirming LOCAL writes to a configurable container path.

## See also

* [Attachments](/features/data-discovery/attachments.md) — the user-facing feature and the storage-mode caveats.
* [Attachment Storage Configuration](/configuration-and-deployment/odd-platform.md#attachment-storage-configuration) — the operator reference for `LOCAL` vs `REMOTE`, persistence, and the AWS S3 region constraint.
