> 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/features/data-discovery/attachments.md).

# Data Entity Attachments

Operators and users can attach **files and links** to any data entity to carry additional context — runbook PDFs, sample CSVs, dashboard screenshots, links to internal wikis, ticketing references. Attachments live alongside the entity's metadata and persist across re-ingests, behaving similarly to attachments on a Jira ticket.

{% hint style="danger" %}
**The default `LOCAL` storage mode is ephemeral.** Files are written to a local container path that is wiped on any container or pod restart — routine deployment, node drain, crash, Kubernetes eviction. Use `REMOTE` (S3 / MinIO) storage for any deployment where users will actually upload attachments. See the [Attachment Storage Configuration](/configuration-and-deployment/odd-platform.md#attachment-storage-configuration) operator reference for the storage caveats and the `us-east-1` constraint on AWS S3.

**In-flight chunk staging is always node-local — even under REMOTE persistence.** The 3-step chunked-upload protocol (initiate → upload-part(s) → complete) writes every part to `/tmp/odd/chunks/{upload_id}` on the platform API container. The path is hard-coded and ignores the `attachment.storage` setting; only at COMPLETE time does the REMOTE path stream the assembled file to S3. An operator running REMOTE persistence on a Kubernetes deployment with `spec.volumes` excluding `/tmp`, or any container restart between initiate and complete, loses all in-flight upload state — partial chunks become orphan files that the housekeeping job does not clean. Multi-hour large-file uploads (the chunked protocol's intended use case) can lose partial state on container churn even when persistent storage is otherwise configured.

**Mitigation today.** For deployments expecting multi-hour large-file uploads, mount a persistent volume at `/tmp/odd/chunks` on the platform API container so chunked uploads survive routine container churn. For deployments that tolerate upload-restart on container restart, no action is required — but operators should know the failure mode so they can recognise "lost partial upload" as a deployment-state symptom rather than an attachment-subsystem bug.
{% endhint %}

{% hint style="danger" %}
**Attachment mutation endpoints discard the URL's data-entity id after the auth gate — operators with `DATA_ENTITY_ATTACHMENT_MANAGE` on entity A can mutate any other entity's attachments by file id.** The URL pattern is `/api/dataentities/{data_entity_id}/attachments/files/{file_id}`. The platform's authorization layer evaluates the gate against `data_entity_id` (entity A — granted), but the controller method operates only on `file_id`. A user with `DATA_ENTITY_ATTACHMENT_MANAGE` on entity A constructs a `PUT` against `/api/dataentities/{A_id}/attachments/files/{B_file_id}` — the auth gate passes and the mutation acts on entity B's file. The READ endpoints have no `SecurityRule` entry at all, so any authenticated user lists any data entity's attachments by id today. Combined, **per-entity ownership boundaries are silently bypassed for attachments in multi-tenant deployments**.

**Mitigation today.** Treat attachment authorization as a network-perimeter concern in multi-tenant deployments — apply a reverse-proxy filter that asserts the URL's `data_entity_id` matches the file's owning entity before forwarding the request to the platform. The upstream platform-side fix is a one-commit guard at the controller layer (assert `file.dataEntityId == pathDataEntityId` before mutating); the doc-side caveat persists until that lands.
{% endhint %}

## Attaching a file

**Step 1.** Open the **Overview tab** of any data entity and click the **Add attachment** icon:

<figure><img src="/files/6bVt4xFqa1p2on6bynFF" alt="" height="508" width="700"><figcaption></figcaption></figure>

**Step 2.** Drag-and-drop the file into the attachment window, or browse to select:

<figure><img src="/files/pV5KEW4k1D7PtZlMvPnU" alt="" height="524" width="700"><figcaption></figcaption></figure>

There is no restriction on file type — images, CSVs, PDFs, TXT files, and any other format are accepted. The single restriction is **file size**, which is capped at `attachment.max-file-size` megabytes (default `20`). Files larger than the cap are rejected by the upload API.

If the file exceeds the size cap or the operator prefers to reference an external location, attach a **link** instead (next section).

## Attaching a link

**Step 3.** To attach a link to a remotely-stored file or other web resource, insert an `http`/`https` URL and provide a customised display name. As of 0.28.0 a link attachment must use the `http` or `https` scheme — other schemes (`mailto:`, `ftp:`, `data:`, …) are rejected. Multiple links can be added in one go via the **+ Add link** icon:

<figure><img src="/files/57oKIn1qAFvXofi0aoOR" alt="" height="517" width="700"><figcaption></figcaption></figure>

Once saved, the file and the link both appear on the entity's Attachments list:

<figure><img src="/files/gRwDzOMhsuu0m3qFvMrT" alt="" height="464" width="700"><figcaption></figcaption></figure>

A single data entity can carry **multiple files and multiple links** — the platform does not cap the count.

## Editing and deleting attachments

Editing and deleting attachments — files and links alike — is performed by clicking the per-attachment icons next to each row:

<figure><img src="/files/OCD1qWQ8al1DTBQGOC1f" alt="" height="183" width="700"><figcaption></figcaption></figure>

## Storage backend (operator-configurable)

The platform supports two storage backends for the file (not the link) attachments:

* **`LOCAL`** *(default)* — files written to a local filesystem path inside the platform container. Suitable only for single-host evaluations and local development; **ephemeral on container restart**.
* **`REMOTE`** — S3-compatible object storage (AWS S3, MinIO, etc.). Required for production deployments. Configured via `attachment.storage`, `attachment.remote.url`, `attachment.remote.access-key`, `attachment.remote.secret-key`, `attachment.remote.bucket`.

The full operator-side reference (every key, the `us-east-1` AWS S3 constraint, the in-memory-buffer ceiling on `spring.codec.max-in-memory-size`) lives at [Configure ODD Platform → Attachment Storage Configuration](/configuration-and-deployment/odd-platform.md#attachment-storage-configuration). The hint at the top of this page is the operator-relevant summary; the configuration reference is authoritative.

<figure><img src="/files/eeDVh9RfxfWdLt51alRI" alt="" height="247" width="700"><figcaption></figcaption></figure>

## RBAC

Adding, deleting, and managing attachments on a data entity is gated by the `DATA_ENTITY_ATTACHMENT_MANAGE` permission. See [Permissions](/configuration-and-deployment/enable-security/authorization/permissions.md) for the full list and how to compose roles around it.

## Where to next

* [Data entity detail page](/features/data-discovery/entity-detail-page.md) — the per-entity surface where the Attachments panel lives on the Overview tab.
* [Configure ODD Platform → Attachment Storage Configuration](/configuration-and-deployment/odd-platform.md#attachment-storage-configuration) — the operator reference for the storage backend, including the LOCAL-is-ephemeral warning and the REMOTE caveats.
* [Permissions](/configuration-and-deployment/enable-security/authorization/permissions.md) — the `DATA_ENTITY_ATTACHMENT_MANAGE` permission and other data-entity permissions.
* [Data Discovery overview](/features/data-discovery.md) — the bucket landing this page sits under.


---

# 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/features/data-discovery/attachments.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.
