DocsFeaturesDocument Storage

Document Storage

Upload, preview, and manage candidate resumes and cover letters via S3-compatible storage in Reqcore.

Document Storage

Reqcore stores candidate documents (resumes, cover letters, work samples) in an S3-compatible object store. Documents are always accessed through authenticated server-proxied endpoints — never via direct S3 URLs.

How It Works

  1. Upload — Files are sent as multipart/form-data to the server
  2. Validation — MIME type is verified using magic bytes (not just the Content-Type header)
  3. Sanitization — Filenames are sanitized to prevent path traversal and XSS
  4. Storage — Files are stored in the S3 bucket with a unique storage key
  5. Access — Download and preview are always server-proxied through authenticated endpoints

Uploading Documents

Documents can be uploaded from:

  • Candidate detail page — Upload resumes and cover letters for any candidate
  • Public application form — Applicants can upload files when file_upload question type is configured

Limits

  • Maximum 20 documents per candidate (enforced on public application endpoint)
  • File type validation via magic bytes (prevents uploading disguised executables)
  • Filename sanitization strips dangerous characters and path segments

Viewing Documents

Download

Documents can be downloaded via the candidate detail page. The server streams the file from S3 with appropriate headers:

  • Content-Disposition: attachment for downloads
  • Cache-Control: private, no-store to prevent caching of sensitive documents

PDF Preview

PDF files can be previewed inline in the browser:

  • Preview renders in a same-origin iframe
  • Only application/pdf files support inline preview
  • DOC/DOCX files (which may contain macros) must be downloaded, not previewed
  • The preview endpoint uses X-Frame-Options: SAMEORIGIN (overriding the global DENY policy)

Security Model

MeasureDescription
Private bucketS3 bucket policy enforced as private on every startup
Server-proxied accessNo presigned URLs are ever exposed to clients
MIME validationMagic byte inspection prevents type spoofing
Filename sanitizationStrips path traversal sequences, XSS payloads, and filesystem-unsafe characters
Storage key hiddenThe internal S3 object key is never included in API responses
Per-candidate limitsMax 20 documents per candidate on public endpoints
Security headersCache-Control: private, no-store on all document endpoints

API Endpoints

MethodPathDescription
POST/api/candidates/:id/documentsUpload a document
GET/api/documents/:id/downloadDownload a document (streamed)
GET/api/documents/:id/previewInline PDF preview (streamed)
DELETE/api/documents/:idDelete a document (S3 + database)

Storage Backends

BackendUse CaseConfig
MinIOLocal developmentS3_FORCE_PATH_STYLE=true
Railway BucketsRailway deploymentsS3_FORCE_PATH_STYLE=false
AWS S3Self-hosted productionS3_FORCE_PATH_STYLE=false

Any S3-compatible storage service works — configure the endpoint, credentials, and bucket name in your environment variables.

Next Steps