Skip to content

fix(files): serve mothership chat attachments stored under a workspace key - #6789

Merged
waleedlatif1 merged 1 commit into
stagingfrom
fix/mothership-attachment-serve
Aug 17, 2026
Merged

fix(files): serve mothership chat attachments stored under a workspace key#6789
waleedlatif1 merged 1 commit into
stagingfrom
fix/mothership-attachment-serve

Conversation

@icecrasher321

Copy link
Copy Markdown
Collaborator

Summary

  • Chat attachment thumbnails and click-throughs were 404ing with {"error":"FileNotFoundError","message":"File not found"}. A mothership attachment is minted with the same key shape as a workspace file (resolveUploadStorage calls generateWorkspaceFileKey for mothership_attachment) but its row carries context = 'mothership', so the serve route's key-prefix branch sent it into the workspace-file use case, which matches on context = 'workspace' and resolved it to nothing. Regressed in fix(files): preserve principals when serving generated documents #6654.
  • Serve resolves the storage context from the key's stored binding (resolveStoredFileContext) rather than the prefix alone, and threads it into the cloud/local handlers instead of re-inferring. Genuine workspace files still go through the authorized use case.
  • verifyWorkspaceFileAccess takes the context, so an attachment authorizes from its DB row instead of falling back to storage-object metadata — a soft-deleted attachment is now denied, matching workspace files.
  • Fixes previews in chat history, restored drafts, queued messages, and HEIC at upload time — all of them build the same serve URL. A freshly picked PNG still previewed because that thumbnail is a local blob URL, which is why this looked partial.

Type of Change

  • Bug fix

Testing

The serve route tests mocked inferContextFromKey to return 'mothership' for a workspace/… key, which it never does — that fiction is what kept this green. Made the mock honest and added a regression test; nine of the existing tests fail against the pre-fix route, confirming the reproduction.

  • bun run test app/api/files lib/uploads — 725 pass (53 files)
  • type-check, lint, check:audits (29 audits), check:api-validation, block-registry audit — all clean
  • Not exercised against a running app

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

…e key

A mothership chat attachment is minted with the same storage key shape as a
workspace file — `resolveUploadStorage` calls `generateWorkspaceFileKey` for
`mothership_attachment` — but its row is written with `context = 'mothership'`.
The serve route branched on the key prefix alone, so every attachment entered
the workspace-file use case, which matches on `context = 'workspace'` and
resolved it to nothing: `{"error":"FileNotFoundError","message":"File not
found"}` for every chat thumbnail and click-through. The `context=mothership`
query param on the serve URL is decorative; the route never read it.

Serve now resolves the storage context from the key's stored binding, which is
server-authored at upload and the only thing that separates the two, and passes
it into the cloud and local handlers instead of re-inferring. Genuine workspace
files still go through the authorized use case.

`verifyWorkspaceFileAccess` takes the context too, so an attachment authorizes
from its database row rather than falling through to storage-object metadata.
A soft-deleted attachment is now denied, matching workspace files.

The route tests are what let this ship: they mocked `inferContextFromKey` to
return 'mothership' for a `workspace/…` key, which it never does. With the mock
made honest, nine of them fail against the old route.
@vercel

vercel Bot commented Aug 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Skipped Skipped Aug 17, 2026 10:50pm

Request Review

@cursor

cursor Bot commented Aug 17, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Touches file serve routing and authorization for all non-public keys with a workspace prefix; behavior change is intentional but could affect edge cases where metadata is missing or stale.

Overview
Fixes 404s when serving mothership chat attachments whose storage keys look like workspace files (workspace/…) but are recorded with context = 'mothership'.

The serve route now uses resolveStoredFileContext (DB binding on the key) instead of inferring context from the prefix alone, so attachments skip the workspace-files use case and go through session auth + verifyFileAccess with the correct context. That resolved context is passed into local and cloud handlers instead of re-inferring from the key.

Authorization threads workspace vs mothership through verifyWorkspaceFileAccess and lookupWorkspaceFileByKey, so metadata lookups and soft-deleted attachment denial match workspace files.

Tests cover resolveStoredFileContext and a regression case for cloud serve of a mothership attachment under a workspace key.

Reviewed by Cursor Bugbot for commit 26ac161. Configure here.

@greptile-apps

greptile-apps Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR fixes serving mothership chat attachments whose workspace-shaped storage keys previously routed them through the workspace-files use case.

  • Resolves storage context from the active metadata binding rather than the key prefix alone.
  • Threads the resolved context consistently through authorization, local serving, and cloud serving.
  • Adds regression coverage for mothership attachments, workspace files, and context fallback behavior.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or independently actionable non-blocking issues identified.

The active metadata binding is unique per key, and the resolved context is consistently applied across route selection, authorization, deletion checks, and storage reads.

Important Files Changed

Filename Overview
apps/sim/lib/uploads/server/metadata.ts Adds deterministic context resolution from the unique active metadata binding, with prefix inference retained for unbound and non-workspace keys.
apps/sim/app/api/files/serve/[...path]/route.ts Uses the resolved storage context consistently to select authentication and drive local or cloud authorization and reads.
apps/sim/app/api/files/authorization.ts Extends workspace-scoped metadata authorization to mothership bindings while preserving deleted-record denial and workspace permission checks.
apps/sim/app/api/files/serve/[...path]/route.test.ts Adds regression coverage proving workspace-prefixed mothership attachments bypass the workspace-file use case and are served under the mothership context.
apps/sim/lib/uploads/server/metadata.test.ts Covers stored mothership and workspace bindings, unbound workspace keys, and non-workspace inference without a database lookup.

Sequence Diagram

sequenceDiagram
  participant Client
  participant Serve as File Serve Route
  participant Metadata
  participant Auth
  participant Storage
  Client->>Serve: GET /api/files/serve/workspace/...
  Serve->>Metadata: resolveStoredFileContext(key)
  Metadata-->>Serve: mothership or workspace
  alt Workspace file
    Serve->>Auth: internalWorkspaceFileServeAuth
  else Mothership attachment
    Serve->>Auth: session/internal authentication
  end
  Serve->>Auth: verifyFileAccess(key, resolvedContext)
  Auth-->>Serve: authorized
  Serve->>Storage: read key using resolvedContext
  Storage-->>Client: attachment bytes
Loading

Reviews (1): Last reviewed commit: "fix(files): serve mothership chat attach..." | Re-trigger Greptile

@waleedlatif1
waleedlatif1 merged commit 1d43639 into staging Aug 17, 2026
30 checks passed
@waleedlatif1
waleedlatif1 deleted the fix/mothership-attachment-serve branch August 17, 2026 23:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants