Single-binary self-hosted website analytics on Rust axum: collector API, dashboards, world map, and PDF reports.
analyticsaxumdockerrustself-hostedsqliteviteweb-analytics
1{% extends 'base.html' %}
2
3
4{% block extra_head %}
5{% include "includes/social.html" %}
6{% endblock %}
7
8
9{% block extra_css %}
10<link href="{{ vite_asset('static_src/pages/index.js', 'css') }}" rel="stylesheet">
11{% endblock %}
12
13
14{% block breadcrumb_wrapper %}{% endblock %}
15
16
17{% block main %}
18<section class="hero">
19 <div class="container">
20 <div class="row">
21 <div class="col-12 col-lg-7">
22 <span class="hero-eyebrow">
23 <span class="eyebrow-dot" aria-hidden="true"></span>
24 v0.1.0 · alpha · self-hosted
25 </span>
26 <h1 class="hero-title">
27 Website analytics for operators who host their <span class="accent">own pipeline</span>.
28 </h1>
29 <p class="hero-sub">
30 Page views, clicks, scrolls, sessions, and custom events. GeoIP maps,
31 UTM attribution, device and browser breakdowns, period-over-period
32 comparisons, and PDF reports. Collected through a single POST
33 endpoint you control. No third-party script, no hidden trackers,
34 no data leaving your infrastructure.
35 </p>
36 <div class="hero-ctas">
37 <a href="/login" class="btn btn-primary">Access dashboard →</a>
38 <a href="/documentation" class="btn btn-outline-light">Docs</a>
39 <a href="https://github.com/overshard/analytics-rust" target="_blank" class="btn btn-outline-light">View source</a>
40 </div>
41 </div>
42 <div class="col-12 col-lg-5 mt-5 mt-lg-0 d-flex align-items-center">
43 <div class="terminal-block w-100">
44 <span class="t-line"><span class="t-comment"># collector push</span></span>
45 <span class="t-line"><span class="t-prompt">site$</span><span class="t-out">POST /collect/</span></span>
46 <span class="t-line"><span class="t-key">event</span>=<span class="t-val">page_view</span> <span class="t-key">url</span>=<span class="t-val">/pricing</span></span>
47 <span class="t-line"><span class="t-key">referrer</span>=<span class="t-val">bing.com</span></span>
48 <span class="t-line"><span class="t-key">geo</span>=<span class="t-val">US · CA</span> <span class="t-key">ua</span>=<span class="t-val">firefox · linux</span></span>
49 <span class="t-line"><span class="t-prompt">site$</span> <span class="t-cursor"></span></span>
50 </div>
51 </div>
52 </div>
53 </div>
54</section>
55
56<section class="stat-strip">
57 <div class="container">
58 <div class="row g-4">
59 <div class="col-6 col-md-3">
60 <div class="stat">
61 <div class="stat-label">properties</div>
62 <div class="stat-value">{{ total_properties }}</div>
63 </div>
64 </div>
65 <div class="col-6 col-md-3">
66 <div class="stat">
67 <div class="stat-label">events logged</div>
68 <div class="stat-value">{{ total_events }}</div>
69 </div>
70 </div>
71 <div class="col-6 col-md-3">
72 <div class="stat">
73 <div class="stat-label">operators</div>
74 <div class="stat-value">{{ total_users }}</div>
75 </div>
76 </div>
77 <div class="col-6 col-md-3">
78 <div class="stat">
79 <div class="stat-label">collecting since</div>
80 <div class="stat-value" style="font-size: 1.05rem;">
81 {% if first_event_created_at %}{{ first_event_created_at }}{% else %}—{% endif %}
82 </div>
83 </div>
84 </div>
85 </div>
86 </div>
87</section>
88
89<section class="container my-5 py-4">
90 <div class="row g-4">
91 <div class="col-12 col-md-6 col-lg-4">
92 <div class="feature">
93 <div class="feature-label">collection</div>
94 <div class="feature-title">One-line site tag</div>
95 <p class="feature-desc">
96 Drop a tiny async script in your <code><head></code>. It posts
97 session_start, page_view, click, scroll, and page_leave to one
98 endpoint. Custom events queue from any frontend code.
99 </p>
100 </div>
101 </div>
102 <div class="col-12 col-md-6 col-lg-4">
103 <div class="feature">
104 <div class="feature-label">attribution</div>
105 <div class="feature-title">UTM + referrer tracking</div>
106 <p class="feature-desc">
107 Slice traffic by source, medium, and campaign. Top referrers,
108 pages, and custom events rank in the dashboard with
109 period-over-period deltas baked in.
110 </p>
111 </div>
112 </div>
113 <div class="col-12 col-md-6 col-lg-4">
114 <div class="feature">
115 <div class="feature-label">geo + device</div>
116 <div class="feature-title">Maps, browsers, platforms</div>
117 <p class="feature-desc">
118 A Natural Earth world map with click-through admin-1 drill-down.
119 Doughnut breakdowns for device, browser, platform, and screen
120 size. GeoIP runs locally, IPs never leave your host.
121 </p>
122 </div>
123 </div>
124 <div class="col-12 col-md-6 col-lg-4">
125 <div class="feature">
126 <div class="feature-label">reports</div>
127 <div class="feature-title">Dashboard + PDF export</div>
128 <p class="feature-desc">
129 Flip any date range to PDF or markdown with one click, rendered
130 in-process via embedded Typst (no chromium subprocess). Custom
131 cards pin bespoke metrics. A public toggle shares without a login.
132 </p>
133 </div>
134 </div>
135 <div class="col-12 col-md-6 col-lg-4">
136 <div class="feature">
137 <div class="feature-label">schema</div>
138 <div class="feature-title">Typed schema, JSON extras</div>
139 <p class="feature-desc">
140 Hot fields (url, referrer, geo, UA, UTMs) live in typed columns
141 for fast queries. Custom keys land in an <code>extra</code> JSON
142 column, queryable via SQLite JSON1 (json_extract). Bot traffic
143 routes to a separate table at write time so human dashboards
144 never have to filter it.
145 </p>
146 </div>
147 </div>
148 <div class="col-12 col-md-6 col-lg-4">
149 <div class="feature">
150 <div class="feature-label">stack</div>
151 <div class="feature-title">Built in Rust, ultralight</div>
152 <p class="feature-desc">
153 One statically linked Rust binary plus SQLite, shipped as a tiny
154 Alpine Docker image behind your own reverse proxy. Your domain,
155 your retention policy. No vendor, no pixel, no lock-in.
156 </p>
157 </div>
158 </div>
159 </div>
160</section>
161{% endblock %}