Overview
Featul uses S3-compatible storage for:- User avatars
- Workspace assets (logos, images)
- Post images (feedback attachments)
- Comment images
- Amazon S3
- Cloudflare R2 (recommended)
- Any S3-compatible storage provider
Cloudflare R2 Configuration
Cloudflare R2 is the recommended storage provider for Featul (S3-compatible, zero egress fees).1. Create R2 Bucket
- Log in to Cloudflare Dashboard
- Navigate to R2 Object Storage
- Create a new bucket
- Note your Account ID from the R2 overview page
2. Create API Token
- Go to R2 > Manage R2 API Tokens
- Create a new API token with:
- Permissions: Object Read & Write
- Bucket scope: Apply to specific bucket (select your bucket)
- Save the Access Key ID and Secret Access Key
3. Configure Public Access
- Go to your bucket settings
- Enable Public Access or configure a custom domain
- Note the public URL (e.g.,
https://pub-xxxxx.r2.dev)
4. Environment Variables
Add these to your.env file:
.env
The R2_PUBLIC_BASE_URL should not include a trailing slash.
packages/api/src/services/storage-signer.ts:17-32
Amazon S3 Configuration
To use Amazon S3 instead of Cloudflare R2:1. Create S3 Bucket
- Go to AWS S3 Console
- Create a new bucket
- Configure bucket for public read access (if needed)
- Note the bucket name and region
2. Create IAM User
- Go to IAM > Users
- Create a new user with programmatic access
- Attach policy with S3 permissions:
3. Environment Variables
Replace the R2 variables with S3 equivalents:.env
Upload Policies
Featul enforces upload policies for different asset types:Avatar Uploads
- Max size: Defined in
AVATAR_UPLOAD_POLICY - Allowed types: Images
- Path:
users/{userId}/avatar/{id}-{filename} - Rate limit: Per user
packages/api/src/router/storage.ts:27-56
Workspace Assets
- Max size: Defined in
WORKSPACE_UPLOAD_POLICIES - Allowed types: Images, documents
- Path:
workspaces/{slug}/{folder}/{id}-{filename} - Rate limit: Per user
- Access control: Workspace members only
packages/api/src/router/storage.ts:58-103
Post Images
- Max size: Defined in
POST_IMAGE_UPLOAD_POLICY - Allowed types: Images
- Path:
workspaces/{slug}/posts/{id}-{filename} - Rate limit: Per user (authenticated) or per IP (anonymous)
- Access control: Public boards allow uploads
packages/api/src/router/storage.ts:105-154
Comment Images
- Max size: Defined in
COMMENT_IMAGE_UPLOAD_POLICY - Allowed types: Images
- Path:
workspaces/{slug}/comments/{id}-{filename} - Rate limit: Per user
- Access control: Workspace members or public board participants
packages/api/src/router/storage.ts:156-219
Signed Upload URLs
Featul uses pre-signed URLs for secure direct uploads:- Client requests upload URL from API
- API generates pre-signed URL with:
- Short expiration time (default: 300 seconds)
- Content-Type validation
- Content-Length validation
- Client uploads directly to S3/R2
- Client receives public URL for the uploaded file
packages/api/src/services/storage-signer.ts:35-59
Pre-signed URLs expire after a short time for security. The TTL is defined in
SIGNED_UPLOAD_URL_TTL_SECONDS.Rate Limiting
Upload URL requests are rate-limited per endpoint:- Avatar uploads: Per user
- Workspace uploads: Per user
- Public post images: Per user or per IP (anonymous)
- Comment images: Per user
packages/api/src/router/storage.ts:7-13
Storage Structure
Dependencies
The storage system uses AWS SDK packages:packages/api/package.json:23-24