商业产业 Hacker News AI 2026-08-14T18:17 正文 ok 1113 words

🔶 Show HN:开源且 AI 原生的网站分析工具

英文标题:🔶 Show HN: Open-source and AI native web analytics

OpenAnalytics 是一款采用 AGPL-3.0 许可、可自托管的隐私优先网站分析平台,使用轻量级追踪脚本,不使用 Cookie、跨站用户画像或原始 IP 存储。项目以 pnpm monorepo 形式提供追踪、数据采集、ClickHouse 查询网关、实时仪表板、CLI、MCP Server 及 AI 助手等组件,并支持漏斗、留存、收入分析和数据导入导出。其自托管部署要求 Linux、Docker、4 条 DNS 记录及约 4 GB 内存,官方发布版通过十个容器镜像快速安装。对开发者而言,该项目通过 CI 强制执行前后端边界、查询签名、追踪器体积预算及数据库架构限制,并提供本地 GeoIP 查询和隐私保护机制。
打开原文相关 2 · 机会 2
media479.5 KB media353.6 KB media329.5 KB media530.5 KB media60.7 KB

中文

开源、隐私优先的网站分析工具。一个轻量级追踪脚本,不使用 Cookie,没有跨站用户画像,仅聚合读取——可依据 AGPL-3.0 在你自己的硬件上自托管。

作者运营的托管实例位于 getopen.so:相同的代码,使用他人的服务器。

概览页面:显示一天内的访客、页面浏览量、跳出率、平均访问时长和收入,以及下方的热门页面、引荐来源和收入

▶ 视频/嵌入内容:观看动态仪表板,在 YouTube 上浏览上述相同页面。

此仓库包含什么

产品以一个 pnpm monorepo 的形式提供:

应用 角色
apps/tracker 浏览器代码片段——仅数 KiB,并由 CI 强制执行字节预算
apps/collector 数据接收:验证、清理、限流、入队
apps/worker 将队列写入 ClickHouse;负责会话、汇总、导出、邮件、删除
apps/api 控制平面:认证、站点、密钥、共享、事件定义、漏斗、组件、收入、AI 助手、MCP
apps/query-gateway 唯一允许读取 ClickHouse 的进程;验证签名查询信封
apps/realtime 实时仪表板背后的 SSE 流
apps/web 仪表板(Next.js)
apps/cli oa——站点设置、统计、设备流登录
packages/* domain、postgres(含迁移)、clickhouse(含迁移)、redis、auth、contracts(OpenAPI)、observability、integrations、migrations、testkit

存储:Postgres(控制平面)、ClickHouse(事件和汇总)、Valkey ×2(一个持久事件队列,一个可丢失的实时缓存)。

功能列表:页面浏览、自定义事件和属性驱动事件、会话、Web Vitals、漏斗、按站点统计的留存、可嵌入组件、公开分享链接、基于你自己的 Stripe 账户的收入分析、CSV/JSON 导入导出、MCP Server 和 CLI。

当前有哪些人在站点上:数据来自在线状态缓存,而不是表扫描。每位访客的名称均为自动生成,且只在其生成当日具备意义。

实时页面:显示 5 位在线访客及其当前页面,下方列出过去 24 小时内见到的所有访客

单个访客去过哪里:按会话查看。轨迹背后的身份是每日轮换的加盐哈希,因此该轨迹可以覆盖一次访问,但绝不会长期对应一个人。

一位访客的轨迹:两次访问,一次直接进入,另一次来自 X;每次访问均列出其打开的页面

他们在哪里:精确到城市级别,且仅在站点主动启用时提供。查询在你自己的磁盘数据库中运行,数据绝不会离开主机。

一个地球仪,标出位于东欧、北非、中东和南美洲的各一位访客

由 CI 强制执行的架构规则,而非约定:

  • apps/web 仅可导入 packages/contracts——OpenAPI 文档是前端和后端之间唯一的接口。
  • ClickHouse 只能通过 query gateway 访问;该网关验证由 api 签发的 Ed25519 签名查询信封。
  • tracker 具有严格的字节预算;任何超出预算的变更都会导致 CI 失败。
  • 此仓库构建的 Postgres schema 不含任何计费表,且 CI 任务会针对真实数据库而非文件列表断言这一点。

SELF-HOSTING.md 是部署指南:包含生成器脚本、一条 docker compose up -d 命令、自动 TLS,以及对每个密钥和每种故障模式的说明。要求为一台安装 Docker 的 Linux 主机、四条 DNS 记录和约 4 GB 内存。

安装是拉取,不是构建。 一个发布版本会向 ghcr.io/openlabs-so/openanalytics 发布十个镜像,因此一台全新主机可在几分钟内完成部署,无需安装任何工具链。

开始前,将四个名称指向主机。 证书会在首次启动时签发;没有它们,签发会失败,而你会在半小时后才发现问题,且很难找到根本原因:

app.example.com   api.example.com   c.example.com   rt.example.com



git clone https://github.com/OpenLabs-so/openanalytics
cd openanalytics
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"   # 最新发布版,而不是 main
cd infra/selfhost
./generate-secrets.sh --domain example.com --email you@example.com --with-geoip
docker compose pull && docker compose up -d
# 然后打开 https://app.example.com 并创建第一个账户

检出操作决定版本,并且只决定一次。 生成器会从当前所在的代码树中读取 tag,并将 .env 指向该发布版本的镜像,同时打印所选版本及其原因;如果位于分支、main 或完全没有 git 的环境中,则会写入构建默认值。这并非只是便利功能——compose 文件、环境变量模板和迁移文件均随镜像一起发布,因此,将一个发布版的镜像与另一个代码树搭配使用,是一种未经测试的配置。

镜像支持 amd64。对于 arm64,或者要运行某个分支,请在此处构建这十个镜像:使用相同的 compose 文件,仅需一个标志,在 4 GB 主机上约需十分钟,并会发生 swap。之后,./upgrade.sh 可用于在不同发布版本之间升级,并创建 ./rollback.sh 所需的快照,因为迁移不会向下执行:回退方式是恢复,而恢复会丢弃升级后到达的数据。脚本会在开始前告知你这一点,而不是等到你已无选择余地的回滚时才说明。RELEASING.md 说明了这里的版本号含义。

若要改为从源码运行——用于开发,或将服务接入你已有的基础设施——请遵循从源码运行。**执行顺序很重要,而且一个看似显而易见的顺序无法工作:**迁移运行器是编译后的产物,因此必须先执行 pnpm run build,再执行 pnpm run migrate:postgres。

infra/selfhost/env/*.env.example 记录了每项服务读取的所有变量,并且有意为每项服务提供单独的文件——环境 schema 禁止某些服务使用某些键,因此单一共享 .env 不可能正确。AI 助手(OPENAI_API_KEY)和对象存储均为可选项:不设置时,这些功能会自行禁用,其他功能照常运行。

以 CI 的方式运行测试套件:

pnpm run test          # 单元测试 + 合约测试 + tracker 测试,无需基础设施
pnpm run verify        # CI 检查的全部内容,包括边界和体积预算

不使用 Cookie,不进行指纹识别,不使用跨站标识符。访客身份是每日轮换的加盐哈希;绝不存储原始 IP 地址。采集器会在写入任何数据之前遵守 Do Not Track 和 Global Privacy Control。城市级地理定位按站点选择启用。

地理定位在你自己磁盘上的数据库中本地解析——任何查询都不会离开主机。系统未内置该数据库(下载大小为 60 MB,且一个月内就会过期);infra/selfhost/geoip/fetch-dbip.sh 会下载一个。

IP Geolocation by DB-IP — https://db-ip.com — 根据 CC BY 4.0 使用。

Pull Request 会在这里合并,提交中会保留你的名字。 此仓库过去会定期从私有 monorepo 接收导出内容,而已经合并的 PR 会在下一次导出时被压平;这一情况于 2026 年 8 月结束。机器人会要求你签署一次 CLA——这不是版权转让,你仍保留自己的版权——此外也无需额外进行 DCO 签署。

CONTRIBUTING.md 包含环境设置、CI 强制执行的基本规则,以及入门方向。Discussions 用于提问,以及讨论值得事先充分交流的想法。

安全报告:SECURITY.md——请不要提交公开 issue。

代码许可:AGPL-3.0。如果你将修改后的 OpenAnalytics 作为网络服务运行,AGPL 要求你向其用户提供修改后的源代码。

“OpenAnalytics”名称和托管服务域名用于标识由其作者运营的实例,不属于许可授予的一部分。自托管实例运行的是软件,而不是该品牌。

English / 原文

Open-source, privacy-first web analytics. One lightweight tracker script, no cookies, no cross-site profiles, aggregate-only reads — self-hostable on your own hardware under AGPL-3.0.

A hosted instance runs at getopen.so , operated by the authors: the same code, someone else's servers.

The Overview screen: visitors, pageviews, bounce rate, average visit and revenue across a day, with top pages, referrers and revenue underneath

▶ Watch the dashboard in motion , a tour of these same screens on YouTube.

What is in this repository

The product, as one pnpm monorepo:

App Role
apps/tracker The browser snippet — a few KiB, with a byte budget CI enforces
apps/collector Ingest: validates, sanitizes, rate-limits, enqueues
apps/worker Drains the queue into ClickHouse; sessions, rollups, exports, mail, deletions
apps/api Control plane: auth, sites, keys, sharing, event definitions, funnels, widgets, revenue, AI assistant, MCP
apps/query-gateway The only process allowed to read ClickHouse; verifies signed query envelopes
apps/realtime The SSE stream behind the live dashboard
apps/web The dashboard (Next.js)
apps/cli oa — site setup, stats, device-flow login
packages/* domain, postgres (+migrations), clickhouse (+migrations), redis, auth, contracts (OpenAPI), observability, integrations, migrations, testkit

Stores: Postgres (control plane), ClickHouse (events and rollups), Valkey ×2 (one durable event queue, one losable realtime cache).

What it does, in one list: page views, custom and attribute-driven events, sessions, web vitals, funnels, per-site retention, embeddable widgets, public share links, revenue analytics from your Stripe account, CSV/JSON import and export, an MCP server, and a CLI.

Who is on the site now , from a presence cache rather than a table scan. Names are generated per visitor and mean nothing outside the day they were minted.

The realtime screen: five visitors online with the page each is on, and everyone seen in the last 24 hours below them

Where one visitor went , session by session. The identity behind a trail is a salted hash that rotates every night, so the trail is as long as a visit and never as long as a person.

One visitor's trail: two visits, one arrived direct and one from X, each listing the pages opened inside it

Where they are , at city level and only when a site opts in. The lookup runs against a database on your own disk and never leaves the host.

A globe with visitors placed on it, one each in eastern Europe, north Africa, the Middle East and South America

Architecture rules CI enforces, not conventions:

  • apps/web may import only packages/contracts — the OpenAPI document is the single seam between frontend and backend.
  • ClickHouse is reachable only through the query gateway, which verifies Ed25519-signed query envelopes minted by the api.
  • The tracker has a hard byte budget; a change that exceeds it fails CI.
  • The Postgres schema this repository builds contains no billing tables, and a CI job asserts that against a real database rather than a file list.

SELF-HOSTING.md is the guide: a generator script, one docker compose up -d, automatic TLS, and an explanation of every secret and every failure mode. Requirements are a Linux host with Docker, four DNS records and about 4 GB of RAM.

Installing is a pull, not a build. A release publishes ten images to ghcr.io/openlabs-so/openanalytics, so a fresh host is a few minutes and needs no toolchain on it.

Point four names at the host before you start. Certificates are issued on the first boot and issuance fails without them, half an hour later and nowhere near the cause:

app.example.com   api.example.com   c.example.com   rt.example.com



git clone https://github.com/OpenLabs-so/openanalytics
cd openanalytics
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"   # newest release, not main
cd infra/selfhost
./generate-secrets.sh --domain example.com --email you@example.com --with-geoip
docker compose pull && docker compose up -d
# then open https://app.example.com and create the first account

The checkout is where the version is chosen, and it is chosen once. The generator reads the tag back out of the tree it is standing in and points .env at that release's images, printing which it picked and why; on a branch, on main, or with no git at all it writes the build defaults instead. That is not a convenience — the compose file, the env templates and the migrations ship with the images, so a release's images against another tree is a configuration nobody has tested.

Images are amd64. On arm64, or to run a branch, build the ten here instead: same compose file, one flag, about ten minutes and swap on a 4 GB box. Later, ./upgrade.sh moves between releases and takes the snapshot ./rollback.sh needs, because migrations do not go down : the way back is a restore, and a restore discards what arrived after the upgrade. It tells you that before it starts, not at rollback time when you no longer have a choice. RELEASING.md is what a version number here means.

To run it from source instead — for development, or to slot the services into infrastructure you already have — follow Running from source. The order matters and one obvious order does not work: the migration runners are compiled output, so pnpm run build comes before pnpm run migrate:postgres.

infra/selfhost/env/*.env.example documents every variable each service reads, and there is one file per service on purpose — the environment schema forbids some keys to some services, so a single shared .env cannot be correct. The AI assistant (OPENAI_API_KEY) and object storage are optional: unset, those surfaces disable themselves and everything else runs.

Run the test suite the way CI does:

pnpm run test          # unit + contract + tracker, no infrastructure needed
pnpm run verify        # everything CI checks, including boundaries and the size budget

No cookies, no fingerprinting, no cross-site identifiers. Visitor identity is a daily-rotating salted hash; raw IP addresses are never stored. Do Not Track and Global Privacy Control are honored at the collector, before anything is written. City-level geolocation is opt-in per site.

Geolocation is resolved locally against a database on your own disk — no lookup ever leaves the host. None is bundled (a 60 MB download, and stale within a month); infra/selfhost/geoip/fetch-dbip.sh downloads one.

IP Geolocation by DB-IP — https://db-ip.com — used under CC BY 4.0.

Pull requests are merged here, with your name on the commit. This repository used to receive periodic exports from a private monorepo, and a merged PR was flattened by the next one; that ended in August 2026. A bot asks you to sign the CLA once — it is not an assignment, you keep your copyright — and there is no DCO sign-off on top of it.

CONTRIBUTING.md has the setup, the ground rules CI enforces, and where to start. Discussions are for questions and for ideas worth talking through first.

Security reports: SECURITY.md — please not a public issue.

Code: AGPL-3.0. If you run a modified OpenAnalytics as a network service, the AGPL requires you to offer your modified source to its users.

The "OpenAnalytics" name and the hosted service's domain identify the instance its authors operate and are not part of the license grant. A self-hosted instance runs the software, not the brand.