Share is a secure file sharing platform that makes it easy to upload PDFs and share them with controlled access. Whether you need to share documents with specific people or make them publicly accessible, Share provides granular control over who can view, download, and comment on your files.
The platform transforms file sharing from a simple upload into a managed experience with analytics, email-gated access, customizable share links, and interactive commenting. Built on Cloudflare's serverless infrastructure, Share offers fast, reliable file delivery with enterprise-grade security.
Key features:
- Secure file storage with user authentication and ownership controls
- Flexible share links with expiration dates, email requirements, and download permissions
- Analytics tracking for access, page views, and link clicks
- Interactive commenting with page-specific annotations
- Rich metadata with custom links, text blocks, and AI-powered summaries
Share is built on a modern serverless architecture:
- Frontend: React + TypeScript application with Clerk authentication
- Backend: Cloudflare Worker handles all API requests and file operations
- Database: Cloudflare D1 (SQLite) stores file metadata, share links, comments, and analytics
- Storage: Cloudflare R2 stores the actual PDF files
When a user uploads a file, it's stored in R2 and metadata is saved to D1. Users can create multiple share links per file, each with its own settings (expiration, email requirements, download permissions). Share links use UUIDs for public access, allowing recipients to view files without authentication while maintaining security through validation checks.
The platform enforces strict access control: users can only access and modify their own files, while share links provide controlled public access with optional email gates and session management.
Files are stored in Cloudflare R2 with metadata in D1. Each file supports:
- Ownership: Every file belongs to a user (via Clerk user ID)
- Settings: Enable/disable downloads and comments per file
- Size limit: 5MB maximum (PDF only)
- Operations: Upload, view, update metadata, and delete (owner only)
Authentication is handled by Clerk, with JWT tokens verified on every request. Users can:
- Sign up and sign in through Clerk
- Access their dashboard to view all their files
- Manage file settings and share links
- View analytics for their shared files
All dashboard routes (/dashboard, /dashboard/files/:fileId) require authentication. File operations check ownership before allowing modifications.
Share links enable public access to files without requiring authentication. Each file can have multiple share links with different configurations:
- UUID-based URLs: Public links use
/view/{uuid}format - Access controls: Enable/disable, expiration dates, download permissions
- Email gates: Optional email requirement with 1-hour session expiry
- Custom greetings: Personalized messages for email-gated links
- Analytics: Track access events, page views, and link clicks
When someone accesses a share link, the Worker validates that the link is enabled, not expired, and belongs to the file. If email is required, users must enter their email before viewing, with sessions stored in localStorage and expiring after 1 hour.
Files support rich metadata through detail blocks:
- Links: Custom links displayed in the sidebar with colors and icons
- Text: Plain text content blocks
- AI Summary: Auto-generated summaries (Gemini integration)
- Images: Embedded images with alt text
Comments allow collaborative feedback:
- Anyone with file access can view and add comments
- Comments can be page-specific
- File owners can resolve or delete comments
- Comments support user and bot roles
Share implements multiple layers of security:
Authentication & Authorization:
- All authenticated requests verify Clerk JWT tokens
- File modifications require ownership (
owner_id === userId) - Share links are validated for enabled status, expiration, and file association
Access Control: Access is granted in this priority order:
- User owns the file → Access granted
- Valid share link (enabled, not expired, belongs to file) → Access granted
- Otherwise → Access denied
Security Features:
- Input validation (file size limits, type restrictions)
- SQL injection prevention (parameterized queries)
- Random UUIDs for share links (not sequential)
- Session expiry (1 hour) for email-gated links
- Data isolation (detail blocks and share links validated to belong to files)
- Cascading deletes (removing files deletes related data)
Session Management:
Share link sessions are stored in localStorage with keys like share_session_{uuid} and share_email_{uuid}, expiring after 1 hour. Each share link maintains independent session storage. For email-gated links, the Worker also requires an email query parameter on PDF view/download (the address is not cryptographically verified).
MIT — see LICENSE.
- Node.js and npm
- Cloudflare account with D1 and R2 access
- Clerk account for authentication
cp .env.example .env
# edit .env with your Clerk publishable key and VITE_WORKER_BASE_URL=http://localhost:8787
npm install
# Configure wrangler.toml D1 IDs + [vars], then:
npx wrangler secret put CLERK_SECRET_KEY
npm run worker:dev # terminal 1
npm run dev # terminal 2-
Create databases:
npx wrangler d1 create share npx wrangler d1 create share-dev
-
Copy the returned
database_idvalues intowrangler.tomlfor production and development bindings -
Apply migrations:
- Local:
npx wrangler d1 migrations apply share-dev --local - Prod:
npx wrangler d1 migrations apply share
- Local:
The Worker handles all API requests and file operations. See worker/README.md.
- Dev:
npm run worker:dev(serves on http://localhost:8787 by default) - Deploy:
npm run worker:deploy(ornpm run worker:deploy:prod) - API base URL: Set
VITE_WORKER_BASE_URLin.env(e.g.http://localhost:8787) - D1 binding name must remain
DB(matches worker code)
- Dev:
npm run dev - The app communicates with the Worker for all data operations
Copy .env.example → .env for local work. Production SPA builds use .env.production (or CI variables).
Frontend (Vite):
VITE_CLERK_PUBLISHABLE_KEY— Clerk publishable keyVITE_WORKER_BASE_URL— Worker API URL
Worker secrets (wrangler secret put):
CLERK_SECRET_KEYGEMINI_API_KEY(optional)R2_ACCESS_KEY_ID,R2_SECRET_ACCESS_KEY,CF_ACCOUNT_ID(for view-url)
Worker vars (wrangler.toml [vars]):
ALLOWED_ORIGINS— comma-separated CORS allowlistPUBLIC_APP_URL— canonical frontend originR2_PUBLIC_BASE_URL— public R2 custom domain base
Forks should replace Clerk keys, Worker URLs, D1/R2 IDs, and the [vars] origins with their own.
npm test # unit tests (CORS + email gate helpers)
npm run lint
npm run build- Files: Load dashboard, create/delete a file
- Share links: Create a share link, toggle enable/disable, expiration, download, requireEmail, customGreeting; confirm mapping resolves in
/view/:uuid - Analytics: Open a share link, page through PDF (page views), click sidebar links (link clicks); verify state updates in UI
- Comments: Add/resolve/delete comments on a file
- Auth gate: For
requireEmailshare links, confirm email gate appears, PDF only loads after email entry, and sessions expire after 1 hour