A self-hostable URL shortener built for Cloudflare Workers with Rust (WebAssembly), designed for personal/family use with multi-tenant capability.
The name Rushomon is both descriptive and conceptual. In its basic sense, the "Rush" part stands for Rust Url Shortener, while the "Mon" part stands for Monitoring as the application includes analytics capabilities.
Conceptually, it's a homage to Akira Kurosawa's film Rashomon, which explores the idea of multiple perspectives of the same event. In the same way as an event can be perceived differently by different people, the same URL can be accessed from different angles, e.g. different short links.
Never forget that naming things is a hard problem to solve. Nevertheless, I'm glad I came up with such a cool name for this project.
- Fast Edge Redirects: Sub-millisecond URL resolution via Cloudflare KV
- Custom Redirect Codes: User-chosen slugs with random fallback
- Per-Link Analytics: Detailed analytics data for individual links with filtering
- OAuth Authentication: GitHub OAuth with secure JWT sessions (Other providers coming soon)
- Instance Admin: First user becomes admin; admin dashboard for user management and settings
- Signup Control: Admins can disable new signups to lock down the instance
- Multi-tenant Ready: Organization/team model from day one
- Self-hostable: Run on your own custom domain, with straightforward deployment in Cloudflare's free tier
- Abuse Reporting: Users can report abusive links
- Tagging: Links can be tagged for better organization and filtering
- Link Status Management: Active/Disabled/Deleted states with soft delete functionality
- Usage Tracking & Limits: Monthly counters with tier-based limits (free/unlimited tiers)
- Advanced Security: Destination blacklist and user suspension capabilities
- Admin Moderation: Link review workflow and comprehensive abuse report management
- Title Fetching: Automatic title extraction for URLs during link creation
- Rate Limiting: Comprehensive IP, user, and session-based rate limiting
- Instance Settings: Configurable admin settings including signup control and default tiers
- Analytics aggregation: Advanced queries and dashboard UI
- More OAuth providers: Add other popular providers
- Team/organization management: Enhanced collaborative features and permissions
- QR Codes Generation: Generate QR codes for links
- Bulk link operations: Import/export and batch management
- Custom domains per organization: Organization-specific branded domains
Rushomon can be used in two ways:
-
Self-hosted: Deploy your own instance on Cloudflare Workers. Check the SELF_HOSTING.md file for detailed instructions. You will need to deploy your own domain and ensure the whole configuration is working.
-
Managed service: Use the public instance at https://rushomon.cc. It's currently in beta and you can sign up to try the free tier via GitHub OAuth. Paid tiers will be made available in the near future. Signing up for a Rushomon subscription is the best way to support the open-source development of this project.
Rushomon uses a robust versioning system with Cargo.toml as the single source of truth. See docs/VERSIONING.md for complete version management instructions using the provided Makefile targets.
For self-hosted users:
- Stable releases: Use version tags (e.g.,
git checkout v0.1.0) - Latest features: Pull from
mainbranch (e.g.,git pull origin main)
The project is designed to be deployed on a single Cloudflare worker, bundling both the backend and frontend. In future iterations we'll add support for running the public service managing redirects on a separate worker instance.
- Backend: Rust compiled to WebAssembly for Cloudflare Workers
- Frontend: SvelteKit + Tailwind CSS v4
- Storage: Cloudflare KV (URL mappings) + D1 (metadata & analytics)
- Rust: Install via rustup
- Wasm target:
rustup target add wasm32-unknown-unknown - worker-build:
cargo install worker-build - Cloudflare account: Sign up at cloudflare.com
- Wrangler CLI:
npm install -g wranglerorcargo install wrangler - Node.js: For frontend development (v20+ recommended)
- expect (macOS only): For colored output in development script -
brew install expect
git clone git@github.com:piffio/rushomon.git
cd rushomon
# Set up development hooks and configuration
./repo-config/scripts/setup.sh# Authenticate with Cloudflare
wrangler login
# Create KV namespace for URL mappings
wrangler kv namespace create "URL_MAPPINGS"
# Save the returned 'id' for wrangler.toml
# Create D1 database
wrangler d1 create rushomon
# Save the returned 'database_id' for wrangler.toml
# Apply database migrations
wrangler d1 migrations apply rushomon --local
wrangler d1 migrations apply rushomon --remote.dev.vars in .gitignore to prevent accidental exposure.
-
Set up local development environment:
# Copy the example files cp .dev.vars.example .dev.vars cp wrangler.example.toml wrangler.toml # Edit .dev.vars and wrangler.toml with your development credentials # Use DIFFERENT credentials than production!
-
Configure
wrangler.toml:- Replace
your-kv-namespace-id-herewith KV namespace ID - Replace
your-preview-kv-id-herewith preview KV namespace ID - Replace
your-database-id-herewith D1 database ID - Set your domain in
DOMAINvariable - Update
ALLOWED_ORIGINSwith your frontend URLs (comma-separated)
- Replace
-
Set up GitHub OAuth App (create separate apps for dev and production):
For Development:
- Go to GitHub Settings β Developer settings β OAuth Apps β New OAuth App
- Application name: "Rushomon URL Shortener (Dev)"
- Homepage URL:
http://localhost:5173 - Authorization callback URL:
http://localhost:8787/api/auth/callback - Save Client ID and Client Secret to
.dev.vars
For Production:
- Create a NEW OAuth App (do not reuse development app)
- Application name: "Rushomon URL Shortener"
- Homepage URL:
https://yourdomain.com - Authorization callback URL:
https://yourdomain.com/api/auth/callback - Store secrets via Wrangler (see below)
-
Store production secrets (NEVER in wrangler.toml):
# Store GitHub OAuth client secret wrangler secret put GITHUB_CLIENT_SECRET # Generate and store JWT secret (minimum 32 characters required) # Generate secure random string: openssl rand -base64 32 # Store it: wrangler secret put JWT_SECRET
π Important: Production secrets are stored via Cloudflare Workers Secrets API and are never visible in your codebase or dashboard.
# Start both backend and frontend with colored output
./scripts/start-local-environment.sh
# Backend: http://localhost:8787
# Frontend: http://localhost:5173
# Press Ctrl+C to stop both servicesBackend (Rust Worker)
# Start the Worker locally
wrangler dev
# The Worker will be available at http://localhost:8787Frontend (SvelteKit)
# Navigate to frontend directory
cd frontend
# Install dependencies
npm install
# Start development server
npm run dev
# Frontend will be available at http://localhost:5173
# Configure .env for local backend: VITE_API_URL=http://localhost:8787Note: For local development, run both the backend (wrangler dev) and frontend (npm run dev) simultaneously. The convenience script handles both services with proper color output and logging.
For a complete self-hosting guide with custom domains, see docs/SELF_HOSTING.md.
# Backend: Build and deploy the Worker
worker-build --release
wrangler d1 migrations apply rushomon --remote -c wrangler.production.toml
wrangler deploy -c wrangler.production.toml
# Frontend: Build and deploy to Cloudflare Pages
cd frontend
npm ci
PUBLIC_VITE_API_BASE_URL=https://your-api-domain.com npm run build
npx wrangler pages deploy build --project-name=rushomon-ui --branch=mainThis project uses a sophisticated repository configuration system for consistent development experience. See repo-config/README.md for complete setup and customization instructions.
Quick Setup:
./repo-config/scripts/setup.shThis installs:
- Pre-commit hooks with unit tests, formatting, and linting
- Configurable checks (personalize via
repo-config/config/user.sh)
rushomon/
βββ src/ # Backend (Rust Worker)
β βββ lib.rs # Wasm entry point
β βββ router.rs # Route handlers
β βββ models/ # Data models
β βββ auth/ # OAuth & session management
β βββ api/ # API endpoints
β βββ db/ # D1 queries
β βββ kv/ # KV operations
β βββ utils/ # Utilities (short codes, validation)
β βββ bin/ # Binary utilities
βββ frontend/ # Frontend (SvelteKit)
β βββ src/
β β βββ routes/ # SvelteKit routes
β β βββ lib/ # Shared components and utilities
βββ docs/ # Documentation
βββ migrations/ # D1 schema migrations
βββ scripts/ # Utility scripts
βββ tests/ # Test suite
βββ repo-config/ # Repository configuration system
βββ .github/ # GitHub workflows
βββ Cargo.toml # Rust dependencies
βββ wrangler.example.toml # Cloudflare config template
βββ README.md # This file
Rushomon comes with an extensive test suite including unit tests and integration tests for the Rust backend.
# Run unit tests
cargo test
# Run integration tests (includes mock OAuth server)
./scripts/run-integration-tests.shIntegration Tests: The project includes a mock OAuth server for testing the complete authentication flow without requiring real GitHub OAuth credentials. The integration test script automatically starts the mock server, runs all tests, and cleans up afterward.
Currently there aren't dedicated unit tests for the frontend, but you should at least run the type checker and build test to ensure the frontend is working correctly.
# Navigate to frontend directory
cd frontend
# Run type checking
npm run check
# Run build test
npm run build- organizations: Multi-tenant org structure with tier system
- users: OAuth user accounts with suspension support
- links: Link metadata with status, tags, and click tracking
- analytics_events: Detailed click tracking with geo data
- settings: Instance-level configuration key-value store
- destination_blacklist: Blocked URLs/domains for security
- link_reports: User-reported abusive links with review workflow
- link_tags: Many-to-many relationship for link organization
- monthly_counters: Usage tracking for tier limits
- URL mappings:
{short_code}β{destination_url, link_id, expires_at, is_active}(global namespace) - Sessions:
session:{jwt_token}β{user_id, org_id, created_at}(JWT-based with refresh tokens) - OAuth state:
oauth_state:{random}β{redirect_uri, created_at}(secure CSRF protection) - Rate limiting:
ratelimit:{type}:{identifier}β{count, window_start}(IP, user, session-based)
Phase 1 uses a global namespace (short codes are unique across all orgs) for simplicity and performance. This enables:
- Single KV lookup for redirects (no org resolution needed)
- Best performance for single custom domain deployments
Future enhancement: Add per-org custom domains and/or org-prefixed keys.
- Better for SEO (link equity transfer)
- Browser/proxy caching reduces server load
- Acceptable trade-off: harder to change destination
- One of the reasons why I started this project was to learn Rust
- Type safety and compile-time guarantees
- Good performance
- Growing ecosystem for Cloudflare Workers
AGPL-3.0
Contributions welcome! Please open an issue first to discuss proposed changes.
For issues or questions, please open a GitHub issue.