返回资讯中心
外部精选
软件工程
#开发者实时流

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 最新dobreandl2026/10/27 · 昨天24 分钟阅读

以下正文同步自 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 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →