Skip to Content
GuidesChoose Content Storage

Choose Content Storage

Short answer: Eneo works without S3-compatible storage. New File and Icon content uses bounded PostgreSQL storage by default. Add a private compatible endpoint only when your capacity or availability needs justify operating it.

Start here

I want to…Go to
Run the simplest supported deploymentPostgreSQL only
Use the bundled private reference storeBundled SeaweedFS
Connect MinIO or another compatible serviceExternal endpoint
Understand ownership and RAGHow content flows
Download a processed file or recover its originalDownload or recover
Plan backup, recovery, and failure handlingOperate it safely
Review lifecycle and integrity implementationArchitecture

How content flows

File and Icon own the product meaning: filename, authorization, and where the content is used. The shared object-content module owns durable bytes, SHA-256, exact size and media type, placement, lifecycle, and physical deletion.

Each content item uses one byte authority. Eneo never dual-writes or silently falls back between PostgreSQL, object storage, or a filesystem.

Object storage and RAG solve different problems:

  • durable content preserves the exact upload and named variants such as extracted text, transcription, model input, or preview;
  • knowledge ingestion turns a chosen variant into searchable facts and vectors in PostgreSQL/pgvector;
  • vectors do not replace the source file, and the source file does not replace the vector index.

What is adopted now?

Product contentCurrent behavior
New File and Icon bytesUse the shared lifecycle and PostgreSQL-inline byte backend
Existing File and Icon bytesCopied, SHA-256 verified, switched to typed references, then removed from legacy columns during upgrade
InfoBlob knowledge generations and Flow artifactsRemain separate follow-up work
Admin-selected placement and inline ↔ object-store migrationRemain a separate, verified migration workflow

Enabling an endpoint makes the optional byte plane available; it does not move existing content or change producer placement by itself.

Download or recover a File

Eneo keeps processing views and original recovery explicit:

UseWhat Eneo returns
Normal downloadThe representation Eneo uses for the File, such as extracted text or an image prepared for model input
Original downloadThe exact persisted upload, with its original filename, media type, and byte length

Both use signed access. Original-recovery links are deliberately short-lived: callers can request between one second and one hour. Original recovery is strict: Eneo never substitutes a processed representation when the original is absent. Instead it returns 404 file_original_not_found (Eneo error 9045).

For a download button, keep the File ID and mint the URL when the user clicks:

const { url, expires_at } = await eneo.files.generateOriginalSignedUrl({ fileId, contentDisposition: "attachment", }); window.location.assign(url);

Do not persist the returned URL or send it to analytics. It is a temporary bearer credential; use expires_at to decide when the application must request a new one. Eneo records who created an original-recovery link, its expiry, and whether it was intended for download or inline display; the bearer token itself is never written to the audit log.

The original response includes Repr-Digest for the SHA-256 of the complete original. That value stays the same on an audio 206 range response because it describes the full representation, not only the returned range. Range requests remain audio-only; other file types use a complete download.

This contract is independent of placement. It behaves the same whether the bytes are stored inline in PostgreSQL or in an optional configured S3-compatible endpoint. Callers do not receive bucket, object-key, or provider details, and Eneo does not require S3-compatible storage.

When a view genuinely needs several attachment payloads, Eneo validates their database records as a bounded batch and then verifies each byte stream. Remote objects still require one content read each, but they do not add one database transaction per attachment.

Views that only show names, icons, publication state, and permissions do not retrieve attachment bodies. For example, the Space applications list reads metadata without opening PostgreSQL-inline or object-store bytes. This reduces storage work without changing the response or requiring object storage.

What happens when a File is deleted?

Eneo first checks the File and its generated derivatives against concrete product relations. A File cannot be deleted while it is attached to a chat, Assistant, App, or App run. The preview returns a short count for each blocking use; it never exposes storage keys or endpoint details.

Preview is advisory. Eneo repeats the same check while holding database locks before deletion, so a concurrent attachment cannot be silently removed. Removing a File from a chat or Assistant only removes that use; the reusable File remains until its owner deletes it. Organization or user offboarding keeps its existing database-cascade behavior.

Choose the right deployment

ChoiceStart here when…Additional responsibility
PostgreSQL inline (default)You want the fewest moving partsMonitor database growth, WAL, and backup time
Bundled SeaweedFSA single-node private reference store is enoughOperate its volume and pair its backup with PostgreSQL
External compatible endpointYou already operate storage or need multi-node availabilityOwn endpoint security, TLS, capacity, credentials, and recovery

Start inline unless measured volume, backup duration, database growth, or availability requirements say otherwise. “Enterprise” does not automatically mean “more services.”

Set up your choice

Leave remote-only OBJECT_CONTENT_* settings absent and run Eneo normally:

docker compose up -d curl -fsS https://eneo.example.eu/api/readyz \ | jq -e '.detail.object_content.code == "object_store_not_configured"'

object_store_not_configured is healthy in this mode. PostgreSQL contains both the control records and bounded payload bytes, so the normal PostgreSQL backup is the complete content backup.

Set OBJECT_CONTENT_INLINE_MAXIMUM_BYTES in env_backend.env to a measured deployment ceiling. It is an operational safety bound, not a hidden product upload policy. It must be at least the largest configured File, image, or audio upload limit. Eneo rejects a conflicting configuration at startup instead of accepting an upload that PostgreSQL-inline storage cannot persist.

Operate it safely

Backup rule

  • Only inline rows: PostgreSQL backup contains control and bytes.
  • Any object-store rows: PostgreSQL and the endpoint are one recovery unit.

For the second case, stop writers and reconciliation, give both backups one recovery ID, preserve OBJECT_CONTENT_DEPLOYMENT_ID and the bucket marker, restore both halves in isolation, verify checksums and sample reads, then reopen traffic. Never combine a newer database with an older bucket or the reverse.

During the File/Icon normalization upgrade, keep backend and worker producers stopped until Alembic succeeds. Retry a stopped migration before accepting new uploads; intervening writes deliberately block an unsafe retry.

The copy phase is row- and byte-bounded and hashes copied content before the final write fence. The fenced pass compares copied payloads with the legacy File/Icon columns once before dropping them. Measure this on a restored production-size database and plan a maintenance window proportional to total File/Icon bytes.

Failure rule

SituationEneo behavior
No endpoint configured and no remote rowsHealthy inline operation
Endpoint temporarily unavailableInline work continues; remote operations return a typed temporary failure
Remote rows exist but configuration is missingFails closed with configuration_required
Bucket does not match this PostgreSQL databaseFails closed; Eneo does not adopt or mutate it

Monitor PostgreSQL growth, endpoint capacity and latency, failed/pending content, delete backlog, reconciliation lag, temporary spool space, and stale multipart uploads. Keep endpoint APIs and consoles private.

FAQ

Do we need S3-compatible storage to run Eneo?

No. PostgreSQL inline is the default and needs no endpoint, bucket, credentials, or extra container.

Are files stored in both PostgreSQL and object storage?

No. PostgreSQL always holds identity and lifecycle facts. The bytes for each content item live either inline or in the configured endpoint.

Will enabling the endpoint move existing files?

No. Placement changes require an explicit copy, full verification, authority switch, and cleanup workflow.

Why can I not delete an uploaded File?

The File is still used by a chat, Assistant, App, or App run. Check its deletion preview, remove those uses deliberately, and try again. Eneo blocks the delete instead of silently breaking those resources.

What happens when an endpoint is down?

Content already owned by that endpoint is temporarily unavailable. Eneo does not create a second copy elsewhere as a fallback.

Can we use a European or self-hosted service?

Yes. Data resides wherever you operate the configured endpoint and its backups. The region value is a signing scope, not a hosting decision.

Does this replace pgvector or document processing?

No. Object content preserves bytes; document processing produces searchable text and vectors. Both remain necessary for RAG.

Can we turn object storage off later?

Only after every remote item has been copied back, verified, and switched by a supported migration. Removing configuration while remote rows exist fails closed.

Where are the deep technical and security details?

Last updated on