aegida-console / docs / superpowers / specs / 2026-07-26-chat-attachments-design.md
2026-07-26-chat-attachments-design.md
Raw

Chat attachments design

Goal

Add production-shaped attachments to the corporate AI chat. Users can attach files to a message, see them in history, and the configured AI Gateway receives temporary, authenticated access to them.

Scope

  • Supported files: PNG, JPEG, WebP, PDF, TXT, Markdown, DOCX.
  • Limits: up to 10 files per message; up to 20 MiB per file.
  • The local Docker environment runs MinIO as the S3-compatible object store.
  • The application works with any S3-compatible provider in production through environment variables.
  • Browser uploads are sent to the authenticated application API. The API validates ownership, MIME type, file size and file signature where practical before storing the object.
  • The composer supports an attach button, drag-and-drop, selected-file cards, image previews and removing an item before sending.
  • Stored message history renders attachment cards and image previews.

Storage and data model

Create a chat_attachments table:

  • UUID primary key and message_id foreign key with cascading delete;
  • owner user_id foreign key, used for authorization checks;
  • original filename, normalized content type, byte size and S3 object key;
  • created timestamp.

Objects are stored under a non-guessable key users/<user-id>/attachments/<attachment-id>. The S3 bucket is private. Database rows are created only after a successful object write; an unsuccessful database write schedules/deletes the newly written object. Deleting a conversation cascades attachment metadata; application cleanup deletes corresponding objects.

API contract

POST /api/attachments accepts multipart FormData containing one file. It requires the existing Bearer JWT and returns attachment metadata for a temporary, not-yet-sent upload. The upload belongs to the authenticated user and expires after 24 hours if it is not linked to a message.

POST /api/chat accepts JSON with the existing fields plus attachmentIds: string[]. It verifies every attachment belongs to the current user and is pending, creates the user message, atomically attaches the records to it, then starts the assistant stream.

GET /api/attachments/:id verifies owner access and returns a short-lived redirect or stream from the private S3 object. User JWTs never reach object storage or the AI Gateway.

The outbound AI Gateway request gains an attachments array alongside model and messages:

{
  "id": "attachment UUID",
  "name": "report.pdf",
  "contentType": "application/pdf",
  "size": 182736,
  "url": "short-lived signed URL"
}

The present mock gateway reports which attachment names it received. A real AI Gateway decides which MIME types it can inspect and fetches the temporary URLs itself.

Security and error handling

  • Validate file count, declared MIME type, extension, byte length and magic bytes for images/PDF.
  • Reject executable, archive and unknown content types.
  • No public bucket, directory listing or unauthenticated object URL.
  • Do not trust the browser-provided filename or content type.
  • Return normalized 400 validation errors, 401 authentication errors, 404 for inaccessible attachment IDs, and 503 for object-storage failures.
  • Explicitly use the Node.js route runtime. Request.formData() has no App Router size limit, so application-level checks are mandatory; the deployment proxy must enforce the same 20 MiB request limit.

UI behavior

The attach control is keyboard accessible and opens the native file picker. Dragging valid files over the composer shows a drop target. Selected files upload immediately, show per-file progress/error state and can be removed before the message is sent. The send button remains disabled while an upload is pending or failed. A text-only message continues to work unchanged.

Verification

  • Unit tests cover validation, ownership, repository operations and Gateway payload creation.
  • Route tests cover multipart upload, unauthorized access, bad type/size and attachment-to-message linking.
  • Component tests cover selection, removal, pending upload state and sending attachment IDs.
  • Compose smoke test uploads a sample text file, sends a chat turn and confirms the attachment appears in history.