> 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.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.opendatadiscovery.org/developer-guides/architecture-decision-log/adr-0012-attachment-storage-backend-boot-selection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
