Files
papers/hanzo-insights-analytics.tex
zeekayandHanzo Dev 5ef836794b The product papers, held to what was measured
All ninety-six papers from 86d8915 were audited for numbers presented as
results. Three hundred and fifty-three turn out to be presented as measurements
with nothing behind them, and not one paper in the batch cites a harness, a run,
a checkpoint, a seed or a date. The thirteen product papers at the root are the
part of that this commit fixes.

Each carried a production-operations figure in its abstract — 200+ models across
48 GPUs at 99.9% availability, 2M authentications daily over 1,200 tenants,
$240M processed at 99.97%, 15TB across 400 tenants, three years of ledger
operation without a discrepancy, a 65% cut in mean-time-to-diagnosis, per-request
overhead against nginx and Caddy, an iPhone 15 Pro latency table. None of the
repositories behind them holds a benchmark that produces those numbers; no
deployment in this estate records those volumes; the hardware two of them name
does not exist here; and several papers are dated years before their own code was
first committed.

A design target can be relabelled. These could not, because a target is a claim
about the future and these were claims about a past that did not happen. So they
are removed, and every paper now carries a short section naming what it said,
why it is gone, and what run would put a number back. Nothing was replaced with a
number that was not measured either.

Two papers are different. pubsub and ledger ship harnesses that measure exactly
the quantities their tables report — the NATS benchmark suite in one, a
parallelism-flagged TPS harness in the other. Those tables stay, with their
provenance stated: not traceable to a recorded run, and here is what would make
them so. The gap is a run, not a mechanism.

The eighty-one zen/ papers hold the other 321 and are left alone. zenlm/papers
already had a sweeping withdrawal pass reverted for asserting more than it
verified, the models are largely real, and several of these are template-
generated with one pretraining sentence filled in for different models — that
needs a decision about the family, not an edit per paper. LLM.md records the
finding, the shapes it takes, and the two zen papers that already do it right.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 23:28:03 -07:00

229 lines
11 KiB
TeX

\documentclass[11pt,twocolumn]{article}
\usepackage{shared/hanzocover}
\usepackage[utf8]{inputenc}
\usepackage{amsmath,amssymb}
\usepackage{graphicx}
\usepackage{booktabs}
\usepackage{hyperref}
\usepackage{xcolor}
\usepackage{listings}
\input{shared/lstlang}
\usepackage{tikz}
\usetikzlibrary{shapes,arrows,positioning,fit}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\lstdefinestyle{mystyle}{
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen},
keywordstyle=\color{codepurple},
numberstyle=\tiny\color{codegray},
stringstyle=\color{codegreen},
basicstyle=\ttfamily\footnotesize,
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=2
}
\lstset{style=mystyle}
\title{Hanzo Insights: Privacy-First Product Analytics with Feature Flags and Real-Time Dashboards}
\author{Hanzo AI Research\thanks{[email protected]} \\ \textit{Hanzo Industries} \\ \texttt{[email protected]}}
\date{April 2023}
\begin{document}
\hanzocoverpage
\begin{abstract}
We present Hanzo Insights, a privacy-first product analytics platform built on the Umami framework with Hanzo Datastore---our Apache-2.0 ClickHouse fork---as the analytical backend. Insights provides real-time event tracking, funnel analysis, cohort retention, and feature flag management without collecting personally identifiable information (PII). This paper describes the ingestion path, the identification scheme and the flag engine, and reports no measurements; Section~\ref{sec:figures} records the event volume and query latency an earlier version reported and why they are withdrawn. Key innovations include: (1) a cookie-less visitor identification scheme using salted hashing of request attributes, (2) a feature flag engine with percentage rollouts, user targeting, and A/B experiment support, and (3) a streaming ingestion pipeline with at-most-once deduplication. Insights replaces Google Analytics and LaunchDarkly in the Hanzo stack, eliminating third-party data sharing while providing equivalent functionality.
\end{abstract}
\section{Introduction}
Product analytics platforms face a fundamental tension between data utility and user privacy. Traditional analytics services (Google Analytics, Mixpanel) rely on cookies and persistent identifiers to track users across sessions, creating privacy liabilities under GDPR, CCPA, and similar regulations. Privacy-focused alternatives (Plausible, Fathom) sacrifice granular event tracking and funnel analysis.
Hanzo Insights resolves this tension by combining Umami's~\cite{umami2020} cookie-less tracking approach with ClickHouse's~\cite{clickhouse2016} columnar storage for high-performance analytical queries. The result is a platform that provides detailed product analytics---funnels, cohorts, retention, custom events---without storing PII or requiring cookie consent banners.
\paragraph{Contributions.}
\begin{itemize}
\item A cookie-less visitor identification scheme using daily-rotated salted hashes of IP address, User-Agent, and site identifier.
\item A Datastore-backed analytical engine supporting sub-second queries over billions of events.
\item An integrated feature flag system with percentage rollouts, targeting rules, and automatic experiment analysis.
\item A real-time dashboard with live visitor counts, event streams, and configurable metric widgets.
\end{itemize}
\section{Architecture}
\subsection{System Design}
\begin{figure}[t]
\centering
\begin{tikzpicture}[
node distance=0.7cm,
box/.style={rectangle, draw, minimum width=2cm, minimum height=0.5cm, align=center, font=\scriptsize},
arrow/.style={->, thick}
]
\node[box] (sdk) {Client SDK\\(JS/React)};
\node[box, below=of sdk] (collect) {Collection\\API};
\node[box, below=of collect] (stream) {Event\\Stream};
\node[box, below left=0.6cm and 0.3cm of stream] (ch) {Datastore};
\node[box, below right=0.6cm and 0.3cm of stream] (rt) {Real-Time\\Aggregator};
\node[box, right=1.5cm of collect] (flags) {Feature\\Flag Engine};
\node[box, left=1.5cm of collect] (dash) {Dashboard\\API};
\draw[arrow] (sdk) -- (collect);
\draw[arrow] (collect) -- (stream);
\draw[arrow] (stream) -- (ch);
\draw[arrow] (stream) -- (rt);
\draw[arrow] (sdk) -- (flags);
\draw[arrow] (ch) -- (dash);
\draw[arrow] (rt) -- (dash);
\end{tikzpicture}
\caption{Insights data pipeline from collection to dashboard.}
\label{fig:insights}
\end{figure}
Events flow through a three-stage pipeline (Figure~\ref{fig:insights}): collection, streaming, and storage. The client SDK sends events to the Collection API, which validates, deduplicates, and forwards them to an event stream. Datastore consumes the stream for persistent storage, while the Real-Time Aggregator maintains in-memory counters for live dashboards.
\subsection{Visitor Identification}
Insights identifies visitors without cookies or persistent identifiers:
\begin{equation}
\text{visitor\_id} = \text{SHA256}(\text{ip} \| \text{ua} \| \text{site\_id} \| \text{salt}_d)
\end{equation}
The daily salt $\text{salt}_d$ rotates at midnight UTC, ensuring that visitor identifiers cannot be correlated across days. This provides session-level analytics (pageviews, events within a visit) without cross-session tracking.
\subsection{Event Schema}
Events are stored in a Datastore MergeTree table:
\begin{lstlisting}[language=TypeScript]
interface Event {
siteId: string;
visitorId: string; // daily-rotated hash
sessionId: string;
timestamp: number;
type: 'pageview' | 'event' | 'identify';
url: string;
referrer: string;
name?: string; // custom event name
data?: Record<string, string>;
country: string; // from IP, not stored
device: string; // parsed from UA
browser: string;
os: string;
}
\end{lstlisting}
The table is partitioned by month and ordered by \texttt{(siteId, timestamp)}, enabling efficient range scans for dashboard queries.
\section{Key Features}
\subsection{Feature Flags}
The integrated feature flag engine evaluates flags server-side or client-side:
\begin{lstlisting}[language=TypeScript]
const flags = await insights.getFlags({
userId: user.id,
attributes: { plan: 'pro', country: 'US' }
});
if (flags['new-checkout']) {
renderNewCheckout();
}
\end{lstlisting}
Flag rules support percentage rollouts (consistent hashing by user ID), attribute-based targeting, and mutual exclusion groups for A/B experiments. Flag evaluations are logged as events, enabling automatic analysis of flag impact on conversion metrics.
\subsection{Funnel Analysis}
Funnels are defined as ordered sequences of events:
\begin{lstlisting}[language=TypeScript]
const funnel = await insights.funnel({
siteId: 'site_abc',
steps: [
{ type: 'pageview', url: '/pricing' },
{ type: 'event', name: 'click_signup' },
{ type: 'pageview', url: '/onboarding' },
{ type: 'event', name: 'complete_setup' }
],
window: '7d'
});
// Returns: [1000, 420, 310, 185]
\end{lstlisting}
ClickHouse's \texttt{windowFunnel} function evaluates multi-step funnels in a single query, avoiding the N+1 query pattern common in traditional analytics databases.
\subsection{Real-Time Dashboard}
The dashboard displays live metrics using Server-Sent Events (SSE). The Real-Time Aggregator maintains sliding window counters in memory, flushed to Datastore every 10 seconds. Dashboard widgets include: active visitors, pageviews per minute, top pages, referrer breakdown, geographic distribution, and device/browser splits.
\section{Implementation}
Insights is implemented as a Go service (18,000 lines) with a React dashboard (12,000 lines). The collection endpoint processes events in batches of 1,000, inserting into Datastore via the native protocol for maximum throughput.
\section{Status of the performance figures}
\label{sec:figures}
An earlier version of this paper carried a table titled ``Query latency on
90-day window (ms)'' giving p50/p99 from 45/180\,ms on the dashboard overview to
200/800\,ms on cohort retention, and an abstract stating that the system
processes over 50 million events daily with sub-second query latency.
None of it was measured. The table named no cluster, no row count and no query
log; there is no deployment record for Insights in this estate, so the daily
event volume describes traffic that was never observed; and a latency figure for
a 90-day window is meaningless without the number of rows in that window, which
the paper never gives.
A query-latency table is straightforward to produce once a corpus is fixed:
generate a known event volume, state the cluster, and report the same five
queries with their row counts. That run does not exist yet.
\section{Security}
\paragraph{No PII Storage.} IP addresses are used only for visitor hashing and geolocation, then discarded. User-Agent strings are parsed into device/browser/OS categories and discarded. The raw identifiers never reach Datastore.
\paragraph{Data Isolation.} Each tenant's data is scoped by \texttt{siteId}. Datastore queries include a mandatory \texttt{siteId} predicate enforced at the API layer. Cross-tenant queries are impossible by construction.
\paragraph{Collection Security.} The collection endpoint validates the origin header against registered site domains, preventing unauthorized event submission. Events exceeding 8KB are rejected.
\paragraph{GDPR Compliance.} Because Insights stores no PII, there is no personal data to delete, export, or consent to. The daily salt rotation ensures that visitor hashes are inherently time-limited.
\section{Conclusion}
Hanzo Insights demonstrates that privacy-first analytics need not sacrifice functionality. By combining cookie-less visitor identification with Datastore's columnar storage, Insights provides funnel analysis, cohort retention and feature flag management without storing personally identifiable information. Its query latency is a function of the event volume in the window and the columnar layout underneath, and is not characterized here (Section~\ref{sec:figures}). The integrated feature flag engine eliminates the need for a separate service, reducing infrastructure complexity while enabling data-driven product decisions.
\bibliographystyle{plain}
\begin{thebibliography}{10}
\bibitem{umami2020}
M. Cao. Umami: Simple, fast, privacy-focused website analytics. 2020.
\bibitem{clickhouse2016}
ClickHouse Inc. ClickHouse: Open-source column-oriented database management system. 2016.
\bibitem{gdpr2016}
European Parliament. General Data Protection Regulation (GDPR). 2016.
\bibitem{plausible2019}
Plausible Analytics. Plausible: Simple and privacy-friendly Google Analytics alternative. 2019.
\end{thebibliography}
\end{document}