No description
Find a file
2026-03-20 15:45:58 -04:00
.github CDN V4 (#20) 2026-01-30 13:45:56 -05:00
app charset on content-type if not provided 2026-03-20 15:45:58 -04:00
bin CDN V4 (#20) 2026-01-30 13:45:56 -05:00
config feat(storage): public R2 URLs with Cloudflare edge caching (#28) 2026-02-05 13:11:12 -05:00
db CDN V4 (#20) 2026-01-30 13:45:56 -05:00
lib/tasks useless commit to test deploy 2026-03-10 17:56:41 -04:00
log CDN V4 (#20) 2026-01-30 13:45:56 -05:00
public CDN V4 (#20) 2026-01-30 13:45:56 -05:00
script CDN V4 (#20) 2026-01-30 13:45:56 -05:00
storage CDN V4 (#20) 2026-01-30 13:45:56 -05:00
test CDN V4 (#20) 2026-01-30 13:45:56 -05:00
tmp CDN V4 (#20) 2026-01-30 13:45:56 -05:00
vendor CDN V4 (#20) 2026-01-30 13:45:56 -05:00
.dockerignore CDN V4 (#20) 2026-01-30 13:45:56 -05:00
.env.example feat(storage): public R2 URLs with Cloudflare edge caching (#28) 2026-02-05 13:11:12 -05:00
.gitattributes CDN V4 (#20) 2026-01-30 13:45:56 -05:00
.gitignore CDN V4 (#20) 2026-01-30 13:45:56 -05:00
.rubocop.yml CDN V4 (#20) 2026-01-30 13:45:56 -05:00
.ruby-version CDN V4 (#20) 2026-01-30 13:45:56 -05:00
config.ru CDN V4 (#20) 2026-01-30 13:45:56 -05:00
Dockerfile ffi 2026-01-30 14:03:44 -05:00
Gemfile redirects 2026-02-05 12:26:29 -05:00
Gemfile.lock feat(storage): public R2 URLs with Cloudflare edge caching (#28) 2026-02-05 13:11:12 -05:00
package.json CDN V4 (#20) 2026-01-30 13:45:56 -05:00
Procfile.dev CDN V4 (#20) 2026-01-30 13:45:56 -05:00
Rakefile CDN V4 (#20) 2026-01-30 13:45:56 -05:00
README.md feat(storage): public R2 URLs with Cloudflare edge caching (#28) 2026-02-05 13:11:12 -05:00
vite.config.ts fucking javascript 2026-01-30 14:25:13 -05:00
yarn.lock CDN V4 (#20) 2026-01-30 13:45:56 -05:00

flag

cdn.hackclub.com

Deep under the waves and storms there lies a vault...

Banner

Banner illustration by @maxwofford.

Slack Channel

A Rails 8 application for hosting and managing CDN uploads, with OAuth authentication via Hack Club.

Prerequisites

  • Ruby 3.4.4 (see .ruby-version)
  • PostgreSQL
  • Node.js + Yarn (for Vite frontend)
  • A Cloudflare R2 bucket (or S3-compatible storage)

Setup

  1. Clone and install dependencies:

    git clone https://github.com/hackclub/cdn.git
    cd cdn
    bundle install
    yarn install
    
  2. Configure environment variables:

    cp .env.example .env
    

    Edit .env with your credentials (see below for details).

  3. Setup the database:

    bin/rails db:create db:migrate
    
  4. Generate encryption keys (for API key encryption):

    # Generate a 32-byte hex key for Lockbox
    ruby -e "require 'securerandom'; puts SecureRandom.hex(32)"
    
    # Generate a 32-byte hex key for BlindIndex
    ruby -e "require 'securerandom'; puts SecureRandom.hex(32)"
    
    # Generate Active Record encryption keys
    bin/rails db:encryption:init
    
  5. Start the development servers:

    # In one terminal, run the Vite dev server:
    bin/vite dev
    
    # In another terminal, run the Rails server:
    bin/rails server
    

Environment Variables

See .env.example for the full list. Key variables:

Variable Description
R2_ACCESS_KEY_ID Cloudflare R2 access key
R2_SECRET_ACCESS_KEY Cloudflare R2 secret key
R2_BUCKET_NAME R2 bucket name
R2_ENDPOINT R2 endpoint URL
CDN_HOST Public hostname for CDN URLs
CDN_ASSETS_HOST Public R2 bucket hostname
HACKCLUB_CLIENT_ID OAuth client ID from Hack Club Auth
HACKCLUB_CLIENT_SECRET OAuth client secret
LOCKBOX_MASTER_KEY 64-char hex key for encrypting API keys
BLIND_INDEX_MASTER_KEY 64-char hex key for searchable encryption

DNS Setup

Domain Points to
cdn.hackclub.com Rails app (Heroku/Fly/etc.)
cdn.hackclub-assets.com R2 bucket (custom domain in R2 settings)

API

The API uses bearer token authentication. Create an API key from the web dashboard after logging in.

Upload a file:

curl -X POST https://cdn.hackclub.com/api/v4/upload \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -F "file=@image.png"

Upload from URL:

curl -X POST https://cdn.hackclub.com/api/v4/upload_from_url \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/image.png"}'

See /docs in the running app for full API documentation.

Architecture

  • Rails 8 with Vite for frontend assets
  • Phlex + Primer ViewComponents for UI
  • Active Storage with Cloudflare R2 backend
  • Solid Queue/Cache/Cable for background jobs and caching (production)
  • Pundit for authorization
  • Lockbox + BlindIndex for API key encryption

Made with 💜 for Hack Club