Skip to content

Implement on-demand original file serving endpoint #44

Open

Description

@bitbiter-dev

Scope

GET /api/v1/assets/{assetId}/original streams the original file from the NAS mount, read-only.

Open question (needs a decision before this is ready-for-agent)

CLAUDE.md says "Metadata.Orientation applies only when serving originals". Does this endpoint:

  • (a) serve the bytes unchanged, leaving the client to honour the EXIF tag (browsers already do for JPEG/HEIC <img>); or
  • (b) rotate the pixels server-side, or expose orientation so a client can?

The answer decides whether this is a byte stream or an image-processing endpoint.

Blocked by #35 and #241.

Activity

  1. changed the title [-]5.7 Implement on-demand original file serving endpoint[/-] [+]Implement on-demand original file serving endpoint[/+] on May 12, 2026
  2. modified the milestones: Epic 5: API — Queries & File Serving, on May 12, 2026
  3. added
    needs-designNeeds a design note (options, first caller, reversibility) before ready-for-agent
    and removed
    needs-triageMaintainer needs to evaluate this issue
    on Oct 17, 2026
  4. bitbiter-dev commented on Oct 17, 2026

    @bitbiter-dev
    OwnerAuthor

    Design note

    Written as the architect, in the #261 pilot of docs/agents/design-process.md.

    Problem and constraints: serve an asset's original file from the NAS.

    • Read-only. Visible to the caller only (VisibleTo), otherwise 404.
    • The path is the storage config's RootPath joined with MediaAsset.FilePath, kept inside RootPath. It goes through IOriginalStore from refactor(api): Narrow read-only storage interfaces instead of ReadOnlyFileSystem #251, not IFileSystem.
    • Streamed, never buffered, with range requests.
    • The originals are whatever the NAS holds: JPEG, HEIC, MOV and possibly RAW.
    • What limits the solution:
      • Most browsers cannot render HEIC or RAW at all.
      • Every display path in the UI already uses proxies: thumbnail, preview and video, all auto-oriented to upright at ingest.
      • The asset DTO already reports upright width/height.

    Options:

    1. Byte-identical stream. Serve the file exactly as stored; the EXIF orientation stays inside it.
      • How: content type from the extension, ETag from ContentHash, range requests via Results.Stream, so the endpoint is cheap and cacheable.
      • Cost: a client that draws the original itself (e.g. on a canvas) must apply the EXIF orientation. <img> already does for JPEG, since image-orientation: from-image is the browser default.
    2. Rotate on the server. Decode, rotate by Metadata.Orientation, re-encode.
      • Cost: the result is no longer the original. JPEG re-encoding is lossy; HEIC, RAW and video can't be done this way; and it costs CPU per request, or a cached full-size rendition.
      • That rendition is a full-resolution FullPreview, i.e. a new proxy type, not an originals endpoint.
    3. Byte-identical, plus orientation in the asset DTO for clients that draw originals themselves.
      • Cost: a contract field with no caller today, which the first-caller rule rules out until one exists.
    4. Change the pattern: no originals endpoint in v1.0. If the UI ever needs more than the 1920 px preview, add a larger upright proxy through the existing proxy endpoint (Implement proxy file serving endpoint #43). Originals stay a later "download" feature.
      • Cost: no way to fetch the original file in v1.0.

    Recommendation: first decide whether v1.0 needs this at all.

    • If a v1.0 feature downloads originals: option 1. It is the only option that keeps "original" true, and display already has its upright path through the proxies.
    • If nothing in v1.0 does: option 4. Move Implement on-demand original file serving endpoint #44 to post-1.0 rather than build an endpoint nothing calls.

    First caller: none found.

    Hard to reverse: the public contract of GET /api/v1/assets/{id}/original.

    • Once clients rely on byte identity (the ETag equals the content hash, and the file can be saved as is), switching to rotated pixels would need a different endpoint.
    • Option 4 commits to nothing.

    Decision needed:

    1. Does v1.0 need originals, and for which feature? This names the first caller. If none, Implement on-demand original file serving endpoint #44 moves to post-1.0 (option 4).
    2. If yes, byte-identical (option 1, recommended) or rotated (option 2)?
  5. added
    needs-decisionWaiting on a decision from the maintainer
    and removed
    needs-designNeeds a design note (options, first caller, reversibility) before ready-for-agent
    on Oct 17, 2026
  6. bitbiter-dev commented on Oct 17, 2026

    @bitbiter-dev
    OwnerAuthor

    Decision

    Option 4: no originals endpoint in v1.0. Decided by the maintainer, 2026-10-17, on the design note above.

  7. added
    needs-triageMaintainer needs to evaluate this issue
    deferredConsciously skipped — not needed for v1.0 but important to address later
    and removed
    v1.0Required for the v1.0 release
    needs-decisionWaiting on a decision from the maintainer
    on Oct 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    deferredConsciously skipped — not needed for v1.0 but important to address laterenhancementNew feature or requestneeds-triageMaintainer needs to evaluate this issuepost-1.0

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions