Skip to content

About

Pixel-cat gacha platform with generators, wheels, and collection tools.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

770 Commits

Folders and files

Repository files navigation

BeastyPage

At a glance

A pixel-cat gacha site with generators, animated wheels, adoption tools, and a searchable Catdex.

Features

  • Cat Generator - Generate random pixel cats with customizable traits, accessories, tortie coats, and 20 derived coat patterns across gacha, builders, adoption, evolution, ancestry, streams, and Discord
  • Gacha Wheel - Weighted wheel spins with animated reveals
  • Catdex - Browse and search all generated cats in a Pokedex-style archive
  • Adoption Generator - Roll whole litters, trim each round, finish with your favorites
  • Visual Builder - Trait-by-trait sprite previews with instant updates
  • Stream Tools - Live session controls for audience voting and shareable builds

Tech Stack

  • Frontend: Next.js 16, React 19, TailwindCSS
  • Backend: PostgreSQL with Drizzle migrations, FastAPI renderer service
  • Package Manager: pnpm
  • Deployment: Kubernetes via FluxCD, Docker containers

Quick Start

Prerequisites

  • Node.js (v24+; required by Portless)
  • pnpm (v10+)
  • Access to the development PostgreSQL configuration in Infisical
  • Python 3.11+ with uv (for renderer service)
  • Infisical CLI with access to the development project

Full development environment

pnpm install --frozen-lockfile
pnpm --dir frontend install --frozen-lockfile --ignore-scripts
pnpm --dir backend/media_service install --frozen-lockfile
npm --prefix backend/discord-bot ci
pnpm --dir backend/image_processing_service install --frozen-lockfile
(cd backend/renderer_service && uv sync --frozen --extra dev)
pnpm dev

pnpm dev checks the generated cat-system contract and required secrets, starts the frontend, renderer, media service, Discord bot, and palette watcher, then opens the worktree-specific Portless URL after every service is ready. The exact URL is printed in the terminal; it is intentionally not hard-coded.

The frontend uses PostgreSQL through the Drizzle data layer. In production, it runs against the beastypage-postgres CNPG cluster in the webpage namespace, with migrations in the frontend init container; production runtime values come from Infisical paths /kubernetes/webpage/beastypage-postgres and /kubernetes/webpage/beastypage-frontend-runtime-config. The local full development setup instead injects the dev environment paths /frontend, /media, and /discord-bot in the BeastyPage Infisical project through the root scripts; see Local development notes.

Frontend only

pnpm --dir frontend run db:local up
pnpm --dir frontend run db:migrate
pnpm --dir frontend dev

Before the final command, copy the DATABASE_URL line from the gitignored frontend/.env.database.local into frontend/.env.local because Next.js does not load the helper's filename. Add NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY and CLERK_SECRET_KEY there for signed-in flows. The root pnpm dev:frontend script is the Infisical-backed alternative for injecting Clerk and service runtime values; it does not provide DATABASE_URL.

Renderer Service (Optional)

The renderer service generates cat card images. If not running, the frontend will work but card generation will be unavailable.

cd backend/renderer_service
uv pip install --editable .
uv run uvicorn renderer_service.app.main:app --reload --port 8001

Environment Variables

Build-time (Frontend)

Variable Description
NEXT_PUBLIC_UMAMI_HOST Self-hosted Umami base URL
NEXT_PUBLIC_UMAMI_WEBSITE_ID Umami website identifier
NEXT_PUBLIC_UMAMI_DOMAINS Comma-separated allowed analytics hostnames
NEXT_PUBLIC_GLITCHTIP_DSN GlitchTip public ingestion DSN
NEXT_PUBLIC_APP_ENV production for version tags, ci otherwise; unset locally
NEXT_PUBLIC_APP_VERSION Derived from APP_VERSION; release is beastypage@${APP_VERSION}
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY Clerk browser key

Analytics is opt-in through the cookie banner, including masked replay and heatmaps. Error tracking uses GlitchTip without browser storage or user identity. Both run only in production builds. Tag builds receive SENTRY_AUTH_TOKEN only through a BuildKit secret for debug-ID artifact-bundle source-map uploads; maps are deleted before packaging. See frontend telemetry documentation for settings, the cookie and storage inventory and the isolated pnpm test:telemetry check. The privacy policy lives at /datenschutz and /privacy.

Quick Share worker

Variable Description
QUICK_SHARE_WORKER_URL Public base URL used to dispatch Quick Share jobs (for example, https://beastyrabbit.com)

Quick Share background processing is event driven. See the Quick Share processing rollout and production runbook for the architecture, cost model, rollout lessons, and verification procedure.

Runtime

Variable Description Default
PORT Server port 3000
RENDERER_INTERNAL_URL Renderer service URL -
IMAGE_PROCESSING_INTERNAL_URL Image processing service URL -
MEDIA_SERVICE_INTERNAL_URL Media service URL -

Project Structure

beastypage/
├── frontend/               # Next.js application
│   ├── app/                # Next.js routes and PostgreSQL-backed API
│   └── Dockerfile          # Multi-stage build (pnpm → Node)
├── backend/
│   ├── discord-bot/        # Discord commands and cat generation
│   ├── image_processing_service/
│   ├── media_service/      # Quick Share media worker
│   └── renderer_service/   # FastAPI cat renderer
├── deploy/                 # PostgreSQL CNPG and Infisical sync templates
├── docs/                   # Development, operations, migration and rollout docs
├── scripts/                 # Sprite import tooling
└── .github/workflows/      # build-images.yml, validate.yml, sonarqube.yml

Each backend service directory has its own README with run commands and environment variables.

Additional operational notes:

Container Images

GitHub Actions builds and publishes GHCR images from .github/workflows/build-images.yml. Version-tag pushes such as v8.1.0 receive X.Y.Z, X.Y and commit SHA tags. Pushes to main build nothing, because Flux deploys semver tags only. Manual runs build the selected images and publish them under a smoke tag only when publish_images is set.

Source checks run once, in validate.yml on the pull request. A tag does not repeat them; its release-gate job proves instead that they passed for the exact tagged tree. The newest validate.yml run of the tag commit's pull request must have passed both applications and database and must have recorded the same Git tree, or a manual validate.yml run on the tag commit must have passed. Every image build, including manual publishing, needs that gate. After all five images are built, deploy-smoke waits for https://beastyrabbit.com/api/version to report the tag, then checks the home page and the database and renderer health routes. Release verification describes what to do when either job fails.

The published image names are:

  • ghcr.io/beastyrabbit/beastypage-frontend
  • ghcr.io/beastyrabbit/beastypage-renderer
  • ghcr.io/beastyrabbit/beastypage-image-processing
  • ghcr.io/beastyrabbit/beastypage-media
  • ghcr.io/beastyrabbit/beastypage-discord-bot

Cat-system release cutover

Frontend, renderer, and Discord images from one release carry the same generated cat-catalog hash. Their image builds fail when the supplied hash does not match the bundled contract, and runtime consumers fail closed when a renderer reports a different hash.

A release that changes this hash must therefore use a coordinated big-bang cutover, not an ordinary mixed-version rolling deployment: build every consumer from one tag, verify the new frontend and renderer stacks report the same hash, then switch traffic to both together. Keep the previous stack available for rollback until external smoke tests pass. Never suppress a catalog mismatch to make a rollout green; that can return a valid response containing the wrong pixels. The one-time v7.3.1 to v7.4.0 transition is compatible because the legacy renderer does not advertise a catalog hash.

Running with Your Own Database

The frontend image connects to the PostgreSQL-backed API configured by its runtime environment:

docker run --rm \
  -v /absolute/path/to/ca.crt:/etc/postgres-ca/ca.crt:ro \
  -e NODE_ENV=production \
  -e DATABASE_MIGRATION_URL='postgresql://owner:owner_password@db.example.com:5432/beastypage?sslmode=verify-full&sslrootcert=/etc/postgres-ca/ca.crt' \
  -e DATABASE_APP_ROLE=beastypage_app \
  ghcr.io/beastyrabbit/beastypage-frontend:8.1.0 \
  node scripts/db/migrate.mjs

docker run --rm -p 3000:3000 \
  -v /absolute/path/to/ca.crt:/etc/postgres-ca/ca.crt:ro \
  -e NODE_ENV=production \
  -e DATABASE_URL='postgresql://app:app_password@db.example.com:5432/beastypage?sslmode=verify-full&sslrootcert=/etc/postgres-ca/ca.crt' \
  -e NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_live_replace_me \
  -e CLERK_SECRET_KEY=sk_live_replace_me \
  ghcr.io/beastyrabbit/beastypage-frontend:8.1.0

The first command applies the schema with the owner URL before the app starts; the second uses the app URL for serving requests. For a throwaway local setup, DATABASE_ALLOW_INSECURE=1 is the documented override for a non-verified connection.

Kubernetes Deployment

For Kubernetes/Talos deployments, configure the PostgreSQL runtime values via the frontend runtime Secret and the CNPG cluster Secret:

apiVersion: apps/v1
kind: Deployment
spec:
  template:
    spec:
      containers:
        - name: frontend
          image: ghcr.io/beastyrabbit/beastypage-frontend:8.1.0
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: beastypage-frontend-runtime-config
                  key: DATABASE_URL

Credits

ClanGen Sprites

Cat sprites originate from ClanGen, licensed under CC BY-NC 4.0.

  • Original creator: just-some-cat.tumblr.com
  • Fan-edit creator: SableSteel and others

LifeGen

Built with components from LifeGen, a ClanGen mod.

Pixel Cat Maker

Additional tooling from Pixel Cat Maker.

License

See LICENSE.md for full details.

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines.

About

Pixel-cat gacha platform with generators, wheels, and collection tools.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages