
Show HN: Grovs – Open-Source Alternative to Branch, Amplitude and Braze
Deep linking, attribution, analytics and engagement for mobile apps, on your own servers.
以下正文同步自 Hacker News 最新,版权归原站所有,已转换为易读排版。
Deep linking, attribution, analytics and engagement for mobile apps, on your own servers.
One open-source platform instead of Branch, AppsFlyer, Amplitude, Braze and OneSignal.
Self-host in one command · Try grovs Cloud (free) · Docs · Website
Grovs does what an MMP does (deep links, install attribution, campaign analytics) without sharing your users' data with ad networks. No ad-network partnerships, no cross-app tracking, no probabilistic fingerprinting. This repository is the backend: the Rails API, link resolver, SDK endpoints, analytics pipeline and background workers.
Layer Technology
Framework Ruby on Rails 8.1 / Ruby 3.4
Database PostgreSQL 16
Analytics store ClickHouse 25.3+ (optional, off by default)
Cache & Queues Redis 6
Background Jobs Sidekiq with sidekiq-scheduler
Authentication Devise + Doorkeeper (OAuth2) + OmniAuth (Google, Microsoft) + OIDC/SCIM (EE)
File Storage S3 or any S3-compatible store via ActiveStorage
Payments Stripe (billing), App Store Server API + Google Play Developer API (IAP)
Push Notifications RPush (APNs + FCM)
Deployment Docker / Kamal / docker-compose
Architecture
React Dashboard ──→ Doorkeeper OAuth2 ──→ Rails API ──→ PostgreSQL │ Mobile SDKs (iOS/Android) ──→ SDK API ───────┤ │ Apple/Google Webhooks ──→ Webhook API ───────┤ │ Sidekiq Workers ──→ Redis │ ClickHouse (optional analytics store)
The app is multi-tenant: Instance is the top-level tenant, each instance has a test and production Project. Users belong to instances via InstanceRole. Domains are attached to projects and resolve short links.
Events arrive from the SDKs and link clicks, are queued in Redis, and are processed in batches into daily statistics. With ClickHouse enabled the same events are also written there and the analytics dashboards read from ClickHouse rollups.
Subdomain Purpose Auth
api.* Dashboard API (CRUD, analytics, config) Doorkeeper OAuth2
sdk.* Mobile SDK (events, links, purchases) Device fingerprint
go.* Short link redirects None
preview.* Link previews None
mcp.* MCP server for AI tools OAuth 2.1
<project>.<domain> Project deep links (built-in and custom domains) None
SERVER_HOST must be the bare domain (for example links.example.com), not the api. host. The reserved subdomains derive from it. Nested domains with three or more labels are supported.
The fastest way to run the full stack (backend, dashboard, databases and a TLS proxy) is the self-host repository: one installer, or one-click templates for Railway, Render, Coolify, Dokploy and the major clouds. The steps below run the backend on its own.
No source checkout needed: the stack runs the published Community Edition image.
1. Fetch the compose file and the env template
mkdir grovs && cd grovs curl -fsSLO https://github.com/grovs-io/backend/releases/latest/download/docker-compose.yml curl -fsSL https://github.com/grovs-io/backend/releases/latest/download/.env.example -o .env
2. Generate the secrets and put them in .env
docker compose run --rm --no-deps web bin/rails secret # SECRET_KEY_BASE docker compose run --rm --no-deps web bin/rails db:encryption:init # the 3 ACTIVE_RECORD_ENCRYPTION_* keys
Also set BOOTSTRAP_ADMIN_EMAIL / BOOTSTRAP_ADMIN_PASSWORD for your first login.
3. Start everything (creates, migrates and seeds the database on first boot)
docker compose up -d
The API listens on http://localhost:3000 (WEB_PORT in .env changes it). This starts PostgreSQL, Redis, ClickHouse, the Rails API and the 5 Sidekiq worker processes. Uploaded assets are stored in the storage volume and served by the app; set ACTIVE_STORAGE_SERVICE=amazon and the AWS_S3_* variables to use S3 or MinIO instead.
Pin a release with GROVS_IMAGE=ghcr.io/grovs-io/backend:3.0.0 in .env rather than tracking latest. To run from a source checkout instead, docker build -t grovs-backend . and set GROVS_IMAGE=grovs-backend.
For a real deployment set SERVER_HOST, REACT_HOST, DOMAIN_LIVE, DOMAIN_TEST, S3_ASSET_PREFIX and PREVIEW_BASE_URL to your own domain, and put a TLS-terminating reverse proxy in front of port 3000 that forwards the api., sdk., go., preview. and link subdomains to it.
ClickHouse is the event store (CLICKHOUSE_PRIMARY=true in .env.example); web creates and migrates its database on boot.
Prerequisites: Ruby 3.4, PostgreSQL 16, Redis 6+, ClickHouse (optional — only needed if running analytics locally via bin/dev)
Install dependencies and set up git hooks
bin/setup
Generate encryption keys (first time)
bin/rails db:encryption:init
Add the output to your .env
Create and seed the database
bin/rails db:create db:migrate db:seed
Start all services (Rails, Sidekiq, ClickHouse)
bin/dev
This uses Foreman to start all processes defined in Procfile.dev. Logs from each service are color-coded and prefixed. Press Ctrl+C to stop everything.
bin/dev -m web,sidekiq
Configuration
Copy .env.example to .env and configure. Key groups:
See .env.example for full documentation of every variable.
Analytics run on PostgreSQL by default. ClickHouse can take over event storage and the analytics reads; every flag defaults to off, so an install without ClickHouse behaves exactly as before.
Create the database and run the ClickHouse migrations
bin/rails clickhouse:setup
Apply pending ClickHouse migrations only
bin/rails clickhouse:migrate
Rebuild rollups (after a backfill or when changing read flags)
bin/rails clickhouse:rebuild_rollups
Enable the flags in order: writes first, then reads, then the rollup and attribution read flags, and CLICKHOUSE_PRIMARY last. bin/dev starts a local ClickHouse via run_clickhouse.sh. ClickHouse 25.3 or newer is required.
Everything under ee/ loads when GROVS_EE=true. Without it the core edition runs unchanged and the enterprise routes do not exist.
Production use of enterprise features requires a subscription. See ee/LICENSE.
Set GROVS_SELF_HOSTED=true for a single-tenant deployment. It serves uploaded assets through the app instead of signed S3 redirects, skips the SaaS-only seed data, grants custom-domain entitlement without billing, and turns the audit log on.
The app uses 5 Sidekiq processes with dedicated queues:
All workers start automatically with bin/dev. To run workers individually:
bundle exec sidekiq -C config/sidekiq_worker.yml
Testing
The project uses Minitest with fixtures. .env.test ships with deterministic dummy values so the suite runs out of the box.
Core suite plus the Enterprise Edition suite (run this before pushing)
bin/rails test:full
Core suite only (test/)
bin/rails test
Enterprise Edition suite only (ee/test, sets GROVS_EE=true)
bin/rails test:ee
Run a specific file or directory
bin/rails test test/models/device_test.rb bin/rails test test/services/
ClickHouse-backed tests skip when no ClickHouse is reachable on CLICKHOUSE_URL. Set CLICKHOUSE_REQUIRED=true to make them fail instead, as CI should.
Code style is enforced with RuboCop. A pre-commit hook runs automatically if you ran bin/setup.
Run RuboCop
bundle exec rubocop
Auto-correct safe offenses
bundle exec rubocop -a
Project Structure
app/ controllers/ api/v1/ # Dashboard API & SDK API endpoints public/ # Link redirect and display controllers models/ # ActiveRecord models services/ # Business logic (events, devices, attribution, ClickHouse reads) jobs/ # Sidekiq background jobs mailers/ # Transactional emails ee/ # Enterprise features (segments, push, in-app, journeys, event streaming, revenue, audit log, SSO/SCIM) — see license below app/ # Controllers, models, services, jobs test/ # EE test suite config/ sidekiq_*.yml # Sidekiq process configs routes.rb # Subdomain-based routing db/ schema.rb # Database schema (source of truth) migrate/ # PostgreSQL migrations clickhouse/migrate/ # ClickHouse migrations (forward-only, idempotent) test/ # Minitest test suite with fixtures lib/ clickhouse/ # ClickHouse migration framework tasks/ # Rake tasks (metrics, backfills, ClickHouse, debugging)
Deployment
Docker Compose (self-hosted)
ghcr.io/grovs-io/backend is the Community Edition image: built from Dockerfile with GROVS_EE=false, which deletes ee/ at build time, so the Enterprise Edition (segments, push, in-app, journeys, event streaming, revenue, audit log, OIDC/SCIM) is not present and cannot be switched on by environment. It runs as a non-root user with precompiled assets; docker-compose.yml runs every role from that one image. Upgrade with:
bump GROVS_IMAGE in .env, then
docker compose pull && docker compose up -d # web runs db:prepare, so pending migrations apply on boot
Releases are cut by hand from Actions -> Release (.github/workflows/release.yml). Every run executes the core and EE test suites, bundler-audit, Brakeman, a Trivy CVE scan of the built image, and a boot of this compose file against it with GROVS_EE=true to confirm the flag is inert. With "publish" on, it pushes the amd64 and arm64 image with SBOM and provenance, creates the v* tag, and attaches this compose file and .env.example to the GitHub Release.
Copy config/deploy.yml.example to config/deploy.yml and fill in your server IPs and registry credentials.
First-time setup
bundle exec kamal setup
Deploy
bundle exec kamal deploy
SDKs
Platform Repository
iOS grovs-io/grovs-ios
Android grovs-io/grovs-android
React Native grovs-io/grovs-react-native
Flutter grovs-io/grovs-flutter
Unity grovs-io/grovs-unity
Web grovs-io/grovs-js
Contributing
We welcome contributions! Here's how:
Please open an issue first for major changes to discuss the approach.
See LICENSE and ee/LICENSE for full terms.
正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。