Skip to content

Repository files navigation

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.7 (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.

The API surface is published as an OpenAPI 3.2 document at /openapi.json and /openapi.yaml (also /api/openapi.json / /api/openapi.yaml), generated from app/models/open_api_spec.rb. Update that file alongside any change to the /api/v4 controllers or app/views/docs/pages/api.md.

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.

Machine-readable files

Path Source Purpose
/openapi.json, /openapi.yaml app/models/open_api_spec.rb Full API description (OpenAPI 3.2)
/llms.txt app/models/llms_txt.rb Agent instructions: when to use the CDN, when not to, and how to call it
/sitemap.xml app/models/sitemap.rb Indexable pages (homepage + docs), with lastmod
/robots.txt public/robots.txt Crawl rules; points at the sitemap

/llms.txt and /sitemap.xml derive their content from the code (routes, Quota, BatchUploadService::MAX_FILES_PER_BATCH, DocPage), so adding a doc page or changing a quota updates them automatically. New doc pages need a summary: in their frontmatter — it becomes the link description in /llms.txt, and a test enforces it.

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

About

Hack Club CDN v4: File hosting for Hack Clubbers

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

41 stars

Watchers

0 watching

Forks

Used by

Contributors

Languages