Open source, privacy-first web analytics for Zo Computers.
Track page views on your Zo Space without cookies, fingerprinting, or external services. Everything runs on your own Zo Computer — data never leaves your machine.
- Zero cookies — GDPR/CCPA compliant by design
- No fingerprinting — captures only path, referrer, viewport, and timestamp
- Self-hosted — SQLite database on your Zo Computer, no third parties
- No Zo Service slot — uses Zo Space routes only
- Auto-pruning — data older than 90 days is removed automatically
- Dashboard — dark-themed, mobile-responsive analytics UI at
/analytics - Lightweight — tracker.js is ~485 bytes gzipped
# 1. Clone
git clone https://github.com/NYTEMODEONLY/zolytics /home/workspace/zolytics
# 2. Install (deploys routes to your Zo Space)
bash /home/workspace/zolytics/install.sh
# 3. Save the auth token printed at the end of install!
# 4. Add the tracker to your pages (see snippet.sh below)
bash /home/workspace/zolytics/snippet.shYour dashboard will be live at https://[yourdomain].zo.space/analytics.
Zolytics protects the query API and dashboard with an auth token. The collection endpoint (/api/analytics/collect) remains unauthenticated so it can receive anonymous page view hits from visitors.
During installation, a 32-character hex token is generated automatically and saved to /home/workspace/zolytics/.auth_token. The token is displayed at the end of installation — save it.
To set your own token instead of the auto-generated one:
bash install.sh --token mysecretpasswordcat /home/workspace/zolytics/.auth_tokenGenerate a new random token at any time:
openssl rand -hex 16 > /home/workspace/zolytics/.auth_tokenThe query API reads the token file on every request, so rotation takes effect immediately — no restart needed. You will need to log in again on the dashboard with the new token.
zolytics/
tracker.js # Browser tracking snippet (~485B gzipped)
install.sh # One-command installer
snippet.sh # Tracking snippet generator
lint.sh # Syntax / style checker
README.md # This file
INTEGRATION.md # Integration guide
api/
collect.js # Zo Space API route: POST /api/analytics/collect
query.js # Zo Space API route: GET /api/analytics/query
dashboard/
page-component.jsx # Zo Space page: GET /analytics
lib/
db.js # SQLite helper (shared schema)
If you prefer to set up step-by-step:
mcporter call zo.update_space_route \
path=/api/analytics/collect \
route_type=api \
public=true \
code="$(cat /home/workspace/zolytics/api/collect.js)"mcporter call zo.update_space_route \
path=/api/analytics/query \
route_type=api \
public=true \
code="$(cat /home/workspace/zolytics/api/query.js)"python3 /home/workspace/paperclip-companies/zoey/scripts/deploy-zo-page.py \
/analytics \
/home/workspace/zolytics/dashboard/page-component.jsxSee INTEGRATION.md for how to add tracking to your pages.
| Variable | Default | Description |
|---|---|---|
ZOLYTICS_DIR |
/home/workspace/zolytics |
Path to zolytics checkout |
DB_PATH |
/home/workspace/zolytics/analytics.db |
SQLite database path |
ANALYTICS_ROUTE |
/analytics |
Dashboard URL path |
Pass as env vars or flags to install.sh:
ZOLYTICS_DIR=/my/path bash install.sh --analytics-path /my-analytics --yesRetention period: The collection API auto-prunes data older than 90 days every 100 writes. There is no config option for this currently — edit the 100 and 90 constants in api/collect.js if needed.
Records a page view.
Request body:
{
"path": "/about",
"referrer": "https://google.com",
"viewport_width": 1440,
"timestamp": "2026-03-22T12:00:00Z"
}Responses:
204— recorded successfully400— invalid or missing fields429— rate limit exceeded (100 req/IP/min)
Returns analytics summary for a time period. Requires authentication.
Query params:
token— required — your auth token from.auth_tokenperiod—7d,30d(default), or90dlimit— max items per list (default 10, max 50)
Auth errors:
401— missing, invalid, or no token configured
Response:
{
"period": "30d",
"total": 1423,
"todayTotal": 47,
"daily": [{ "date": "2026-03-01", "count": 38 }, ...],
"topPages": [{ "path": "/zoey", "count": 612 }, ...],
"referrers": [{ "referrer": "https://twitter.com", "count": 89 }, ...],
"devices": [{ "device": "mobile", "count": 834 }, ...]
}Location: /home/workspace/zolytics/analytics.db
Schema:
CREATE TABLE page_views (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp TEXT NOT NULL, -- ISO 8601 from client
path TEXT NOT NULL, -- URL path + query string
referrer TEXT, -- referrer URL or "direct"
viewport_width INTEGER, -- browser width in px
device_category TEXT, -- "mobile" | "tablet" | "desktop"
country TEXT, -- from CF-IPCountry header (if available)
created_at TEXT DEFAULT (datetime('now'))
);Backup:
cp /home/workspace/zolytics/analytics.db ~/analytics-backup-$(date +%Y%m%d).dbWhat is tracked:
- Page path and query string
- HTTP referrer
- Viewport width (used to classify device type: mobile/tablet/desktop)
- Client-supplied timestamp
- Country code from Cloudflare headers (when available — not always present)
What is NOT tracked:
- IP addresses (not stored)
- Cookies or localStorage
- User agent string
- Fingerprinting data
- Any personally identifiable information
GDPR/CCPA: No consent banner required. No personal data is collected or stored.
Browser
│
│ POST /api/analytics/collect
▼
Zo Space API Route (collect.js)
│ rate limit (100 req/IP/min)
│ validate fields
▼
SQLite (analytics.db) ◄── auto-prune every 100 writes
│
│ GET /api/analytics/query
▼
Zo Space API Route (query.js)
│ readonly connection
│ returns JSON summary
▼
Dashboard Page (/analytics)
│ React JSX, self-contained
│ fetches /api/analytics/query
└── dark theme, mobile-first, no CDN
bash /home/workspace/zolytics/lint.shcurl -X POST https://[yourdomain].zo.space/api/analytics/collect \
-H "Content-Type: application/json" \
-d '{"path":"/test","referrer":"direct","viewport_width":1440,"timestamp":"2026-03-22T12:00:00Z"}'
# Expect: HTTP 204- Fork the repo
- Make your change
- Run
bash lint.sh - Open a PR with a clear description
Please keep the tracker under 1KB gzipped and maintain zero production dependencies.
MIT © NYTEMODE
Built for the Zo Computer community. Self-host everything.