Skip to content

Add Rybbit - #32

Open
ClaydeCode wants to merge 1 commit into
mainfrom
feat/add-rybbit
Open

Add Rybbit#32
ClaydeCode wants to merge 1 commit into
mainfrom
feat/add-rybbit

Conversation

@ClaydeCode

@ClaydeCode ClaydeCode commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Rybbit — Add to Freeshard app store

Upstream: https://github.com/rybbit-io/rybbit
Homepage: https://rybbit.com
License: AGPL-3.0 (foss)
Image: ghcr.io/rybbit-io/rybbit-client:v2.7.0 (entrypoint is a caddy:2.10.0 proxy in front of it)
Last release: 2026-07-07 (v2.7.0)
Preview zip (after CI): https://storageaccountportab0da.blob.core.windows.net/app-store/preview/feat/add-rybbit/all_apps/rybbit/rybbit.zip

What is it

Open-source, privacy-friendly web and product analytics — a self-hostable Google Analytics / Plausible alternative.

Rybbit gives you real-time visitor dashboards, funnels, user journeys, sessions, retention, and event tracking without cookies and without shipping your visitors' data to a third party. A lightweight (~18 KB) tracking script is GDPR/CCPA-friendly and needs no cookie banner. Fully self-contained stack: Next.js dashboard, Node API, ClickHouse for event storage, Postgres for app/auth data, Redis for sessions and jobs. AGPL-3.0; self-hosting is free with no license keys or mandatory cloud services.

⚠️ This is the heaviest app in the store and the template is unverified. 6 containers (current max is 4/immich); first internal path-routing proxy; first ClickHouse. Config (Caddyfile + ClickHouse XMLs) ships as sidecar files bind-mounted via {{ fs.installation_dir }} — the same packaging immich uses for its .env (build_store_data zips every file in the app folder). It needs a real smoke-test deploy before merge — see Follow-ups.

Integration decisions

Entrypoint = internal caddy proxy. Rybbit has no single container serving both UI and API. The browser calls same-origin /api/* (must reach backend:3001) and everything else (must reach client:3002). Freeshard routes one subdomain to one port, so a caddy:2.10.0 container (container_name: rybbit, port 80) does the path split, reproducing upstream's own Caddyfile. The Caddyfile ships as a sidecar file in the app folder, bind-mounted read-only via {{ fs.installation_dir }}/Caddyfile (immich .env precedent).

Access = mixed paths. "" (dashboard) → private (pairing gates it; better-auth login sits behind pairing; no auth-proxy header support upstream). /api/public because the tracking snippet (/api/script.js) and event ingestion must be reachable by visitors of the tracked sites. Backend enforces better-auth on dashboard-data endpoints, matching rybbit's own cloud model.

Lifecycle = always_on: true. Rybbit is an event collector; idle shutdown would drop incoming events from tracked sites.

minimum_portal_size: "m" (d2-8, 8 GB). Idle ~2–3 GB exceeds s (4 GB); l (16 GB) pads against bias-small policy. m is the compromise — headroom over idle without the largest tier. Smoke test should confirm it holds under ingestion load.

Secrets = static (librechat precedent; no random generator in Freeshard templates). BETTER_AUTH_SECRET + DB/Redis passwords are fixed values; DBs live on an isolated internal rybbit network and the app is single-tenant + private-gated.

user: "0:0" on backend + clickhouse (write to root-owned fs.app_data bind mounts → EACCES otherwise; librechat/vikunja precedent). Client is stateless, left default.

ClickHouse config shipped as sidecar files clickhouse/config.d/rybbit.xml (listen 0.0.0.0, quieter logging) + clickhouse/users.d/rybbit.xml (default-user password + enable_json_type profile), bind-mounted into the image's config dirs. Default entrypoint picks them up — no command override.

Telemetry disabled (DISABLE_TELEMETRY=true). No shared-media volumes (analytics app).

Soft warnings

  • g — heavy footprint: research estimated resource class l (ClickHouse is RAM-hungry, always-on stack). Deployed at m as a compromise; watch memory under load.

Rejected image candidates

  • Docker Hub rybbit/* — no official/verified-publisher image; upstream README + compose point to ghcr.io/rybbit-io (org match + docs-linked).
  • rybbit-backend — supporting API service, not the web entrypoint.
  • caddy:2.10.0 — upstream's generic proxy, used as the routing entrypoint, not the app identity.

Notes from research

Full compose topology, auth model, inter-service env wiring, and deployment quirks are captured in .add-app-scratch/research_notes.md in the worktree. Key points:

  • Stack: caddy (80, entrypoint) → client (Next.js, 3002) + backend (Fastify, 3001); clickhouse (8123), postgres (5432), redis (6379).
  • Auth: better-auth, self-registers via web UI on first run — no shell/docker exec bootstrap (no exit-j). DISABLE_SIGNUP left false so the owner can register; pairing gates the dashboard.
  • Client BASE_URL is build-time baked (NEXT_PUBLIC_BACKEND_URL); prebuilt ghcr client should call relative /api (same-origin) — must verify it doesn't hit a hardcoded cloud URL.
  • Redis needs --maxmemory-policy noeviction + AOF (set).
  • Actively maintained, ~12.4k stars, releases not all-prerelease.

Checklist (from agents.md)

  • just new-app rybbit
  • Docker image identified (ghcr.io/rybbit-io/rybbit-client, tag v2.7.0) — all 6 stack images verified
  • docker-compose.yml.template set
  • app_meta.json set (app_version, name, pretty_name, icon, entrypoints, paths, lifecycle, store_info)
  • upstream_repo set
  • update_check.py present (implemented — latest_github_release)
  • Icon present (brand-green frog SVG)
  • Auth-proxy configured (n/a — no header-trust support upstream)
  • Shared data path mounted (n/a — analytics app, no shared media)
  • minimum_portal_size set (m)
  • always_on set
  • Telemetry/analytics disabled
  • docker manifest inspect passed (all 6 images)

Follow-ups

Must smoke-test on a live shard before trusting this template:

  1. Same-origin client routing — confirm the prebuilt ghcr client calls relative /api (same-origin) and not a hardcoded cloud URL. If it bakes an absolute URL, the client image may need rebuilding with NEXT_PUBLIC_BACKEND_URL=https://rybbit.<domain>.
  2. ClickHouse JSON type — confirm enable_json_type is the correct setting name for ClickHouse 25.4 and that rybbit's schema migrations succeed against it.
  3. Caddy path split + first-run — confirm /api/*→backend and /→client both resolve, and admin registration completes through the private dashboard.
  4. Consider setting DISABLE_SIGNUP=true post-registration if leaving the dashboard private isn't deemed enough.

Privacy-focused web/product analytics (AGPL-3.0). 6-container stack
behind an internal caddy path-routing proxy: /api/* -> backend:3001,
/ -> client:3002. ClickHouse + Postgres + Redis backing services.
Config (Caddyfile, ClickHouse XMLs) ships as sidecar files bind-mounted
via fs.installation_dir (immich .env precedent), not heredoc injection.
Mixed access (dashboard private, /api public for tracking ingestion),
always_on, minimum_portal_size m. Template is best-effort and needs a
live smoke test (same-origin client routing, ClickHouse enable_json_type,
caddy split) before it can be trusted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant