The upload service for the Sharify platform.
Zephyr works alongside other Sharify components:
- Canvas: Serves uploaded content to end users
- Spine: Web panel backend for user/content management
- Sharify-Go: Go SDK for programmatic uploads
- Sharify-Desktop: Cross-platform desktop app
Note: This project is not actively maintained and should not be used in a production environment.
Zephyr handles all the core upload functionality for Sharify - creating uploads, managing storage, user authentication, and serving the REST API that everything else talks to.
When you upload content to Sharify (via the web panel, desktop app, API, etc.), it ultimately ends up hitting Zephyr.
- Processing uploads (images, files, pastes, redirects)
- Storing everything in Cloudflare R2 with Redis caching
- User authentication via API tokens or JWT (for web panel)
- Managing upload metadata and expiration
- Bulk operations like deleting multiple uploads
API Request → Authentication → Validation → Store in R2 → Save to Database → Return URL
The interesting parts:
- Storage keys are SHA-512 hashes of hostname + secret to avoid collisions
- EXIF data is stripped from PNG/JPEG images for privacy
- Byte data is prefixed with a type byte (0x00=image, 0x01=file, etc.) before storage
- Expired temporary uploads get cleaned up automatically every 15 minutes
All endpoints need authentication via Authorization header:
# Create upload
POST /api/v1/uploads
Content-Type: multipart/form-data
# List uploads with filtering
GET /api/v1/uploads?page=1&limit=20&types=image,file&sortBy=date&order=desc
# Edit upload metadata
PATCH /api/v1/uploads/{storage_key}
Content-Type: multipart/form-data
# Bulk delete uploads
DELETE /api/v1/uploads
Content-Type: application/json
Body: ["storage_key_1", "storage_key_2"]# List available domains for user
GET /api/v1/hosts| Type | Content | Use Case |
|---|---|---|
| Image | PNG, JPEG, GIF, WebP | Screenshots, photos (EXIF stripped) |
| File | Any binary content | Documents, archives, executables |
| Paste | Plain text | Code snippets, logs, notes |
| Redirect | URL strings | Link shortening with custom domains |
# Required (choose one)
[email protected] # File upload
paste_content="code here" # Text content
long_url="https://..." # URL to shorten
# Optional metadata
title="My Upload" # Display name
host="myname.example.com" # Custom subdomain
secret="custom-path" # Custom URL path (a-Z, 0-9, -, _)
expiration="2024-12-31T23:59:59Z" # ISO 8601 datetime
duration="72" # Hours until expiration# Pagination & Sorting
?page=1&limit=50&sortBy=date&order=desc
# Content Filtering
?types=image,file # Filter by upload type
?hosts=i.example.com # Filter by hostname
?mimes=image/jpeg # Filter by MIME type
?secrets=abc123,def456 # Filter by specific secretsAll content in R2 is prefixed with a single byte type indicator:
R2 Object: [type_byte][content_data]
Type Indicators:
0x00 = Image
0x01 = File
0x02 = Paste
0x03 = Redirect
This helps us determine how to serve the data on the front-end.
- Set up Redis and MariaDB with Docker:
docker run --name=zephyr_mysql -e MYSQL_ROOT_PASSWORD=zephyr -e MYSQL_DATABASE=zephyr -p 3306:3306 -d mariadb
docker run --name=zephyr_redis -d -p 6379:6379 redis redis-server --requirepass "zephyr"- Copy environment configuration:
cp .env.example .env-
Configure required services (Redis, database, R2 credentials)
-
Run the server:
make run