RepoPilotOpen in app →

usefathom/fathom

Fathom Lite. Simple, privacy-focused website analytics. Built with Golang & Preact.

Healthy

Healthy across the board

weakest axis
Use as dependencyHealthy

Permissive license, no critical CVEs, actively maintained — safe to depend on.

Fork & modifyHealthy

Has a license, tests, and CI — clean foundation to fork and modify.

Learn fromHealthy

Documented and popular — useful reference codebase to read through.

Deploy as-isHealthy

No critical CVEs, sane security posture — runnable as-is.

  • Last commit 7w ago
  • 15 active contributors
  • Distributed ownership (top contributor 49% of recent commits)
Show all 6 evidence items →
  • MIT licensed
  • CI configured
  • Tests present

Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests

Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.

Embed the "Healthy" badge

Paste into your README — live-updates from the latest cached analysis.

Variant:
RepoPilot: Healthy
[![RepoPilot: Healthy](https://repopilot.app/api/badge/usefathom/fathom)](https://repopilot.app/r/usefathom/fathom)

Paste at the top of your README.md — renders inline like a shields.io badge.

Preview social card (1200×630)

This card auto-renders when someone shares https://repopilot.app/r/usefathom/fathom on X, Slack, or LinkedIn.

Onboarding doc

Onboarding: usefathom/fathom

Generated by RepoPilot · 2026-05-09 · Source

🤖Agent protocol

If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:

  1. Verify the contract. Run the bash script in Verify before trusting below. If any check returns FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding.
  2. Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
  3. Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/usefathom/fathom shows verifiable citations alongside every claim.

If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.

🎯Verdict

GO — Healthy across the board

  • Last commit 7w ago
  • 15 active contributors
  • Distributed ownership (top contributor 49% of recent commits)
  • MIT licensed
  • CI configured
  • Tests present

<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>

Verify before trusting

This artifact was generated by RepoPilot at a point in time. Before an agent acts on it, the checks below confirm that the live usefathom/fathom repo on your machine still matches what RepoPilot saw. If any fail, the artifact is stale — regenerate it at repopilot.app/r/usefathom/fathom.

What it runs against: a local clone of usefathom/fathom — the script inspects git remote, the LICENSE file, file paths in the working tree, and git log. Read-only; no mutations.

| # | What we check | Why it matters | |---|---|---| | 1 | You're in usefathom/fathom | Confirms the artifact applies here, not a fork | | 2 | License is still MIT | Catches relicense before you depend on it | | 3 | Default branch master exists | Catches branch renames | | 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code | | 5 | Last commit ≤ 80 days ago | Catches sudden abandonment since generation |

<details> <summary><b>Run all checks</b> — paste this script from inside your clone of <code>usefathom/fathom</code></summary>
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of usefathom/fathom. If you don't
# have one yet, run these first:
#
#   git clone https://github.com/usefathom/fathom.git
#   cd fathom
#
# Then paste this script. Every check is read-only — no mutations.

set +e
fail=0
ok()   { echo "ok:   $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }

# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
  echo "FAIL: not inside a git repository. cd into your clone of usefathom/fathom and re-run."
  exit 2
fi

# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "usefathom/fathom(\\.git)?\\b" \\
  && ok "origin remote is usefathom/fathom" \\
  || miss "origin remote is not usefathom/fathom (artifact may be from a fork)"

# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
   || grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
  && ok "license is MIT" \\
  || miss "license drift — was MIT at generation time"

# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
  && ok "default branch master exists" \\
  || miss "default branch master no longer exists"

# 4. Critical files exist
test -f "assets/src/js/script.js" \\
  && ok "assets/src/js/script.js" \\
  || miss "missing critical file: assets/src/js/script.js"
test -f "assets/src/js/lib/client.js" \\
  && ok "assets/src/js/lib/client.js" \\
  || miss "missing critical file: assets/src/js/lib/client.js"
test -f "assets/src/js/pages/dashboard.js" \\
  && ok "assets/src/js/pages/dashboard.js" \\
  || miss "missing critical file: assets/src/js/pages/dashboard.js"
test -f "assets/src/js/components/Chart.js" \\
  && ok "assets/src/js/components/Chart.js" \\
  || miss "missing critical file: assets/src/js/components/Chart.js"
test -f "Makefile" \\
  && ok "Makefile" \\
  || miss "missing critical file: Makefile"

# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 80 ]; then
  ok "last commit was $days_since_last days ago (artifact saw ~50d)"
else
  miss "last commit was $days_since_last days ago — artifact may be stale"
fi

echo
if [ "$fail" -eq 0 ]; then
  echo "artifact verified (0 failures) — safe to trust"
else
  echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/usefathom/fathom"
  exit 1
fi

Each check prints ok: or FAIL:. The script exits non-zero if anything failed, so it composes cleanly into agent loops (./verify.sh || regenerate-and-retry).

</details>

TL;DR

Fathom Lite is a self-hosted, privacy-focused website analytics engine built in Go with a Preact frontend dashboard. It provides page view tracking, visitor analytics, and basic reporting without cookies or banners—designed as a lightweight alternative to Google Analytics that runs on your own infrastructure. Monolithic Go backend + Preact frontend bundled together. Backend code likely in cmd/ and internal/ directories, frontend assets in assets/src/ (containing Preact components under JavaScript 48k lines). Static assets (CSS in assets/src/css/, fonts in assets/src/fonts/) are embedded via gobuffalo/packr/v2. Database migrations handled via rubenv/sql-migrate. Single database abstraction using sqlx supports SQLite, MySQL, and PostgreSQL.

👥Who it's for

Site owners and DevOps engineers who want to self-host analytics infrastructure with privacy guarantees and no external dependencies. Contributors are typically Golang backend developers and JavaScript/Preact frontend developers interested in privacy-respecting web analytics.

🌱Maturity & risk

Production-ready and stable but in maintenance-only mode. The project is 4+ years old with a substantial codebase (79k Go, 48k JavaScript), established CI/CD pipelines (.github/workflows/test.yml, .github/workflows/release.yml via GoReleaser), and active maintenance (fixes bugs, maintains long-term support). No new features are being added; focus is purely on stability.

Low risk for a maintenance-focused project. Dependencies are well-established (Gorilla mux, sqlx, logrus, go-sql-driver) with no bleeding-edge requirements. However, Go version is pinned to 1.19 (consider eventual upgrades), and as a single-maintainer/team project, architecture changes would require community consensus. The codebase is self-contained with no microservice dependencies.

Active areas of work

Project is in long-term maintenance mode with no active feature development. Work focuses on dependency updates, Go version compatibility, and bug fixes. The .github/workflows indicate automated testing and release processes are in place but likely only run on critical issues or dependency updates.

🚀Get running

Clone the repository, copy .env.example to .env, and use the Makefile: git clone https://github.com/usefathom/fathom && cd fathom && cp .env.example .env && make build && make run

Daily commands: Using Makefile (presence confirmed): make build to compile, make run to start the development server. Requires Go 1.19+, a supported database (SQLite default), and environment variables from .env.example (database connection, secret key, port).

🗺️Map of the codebase

  • assets/src/js/script.js — Main entry point for the frontend application; initializes Preact app and routing logic
  • assets/src/js/lib/client.js — Core HTTP client for all API communication between frontend and backend; every contributor must understand request patterns
  • assets/src/js/pages/dashboard.js — Dashboard page component; largest and most complex frontend page that drives analytics display logic
  • assets/src/js/components/Chart.js — Chart rendering component; critical for data visualization on the main dashboard
  • Makefile — Build orchestration for Go backend and asset bundling; defines how the entire project compiles
  • .goreleaser.yml — Release configuration defining binary outputs and deployment strategy across multiple platforms
  • docker-compose.yml — Local development environment configuration; essential for onboarding and reproducible setup

🛠️How to make changes

Add a new dashboard metric widget

  1. Create a new Preact component file in assets/src/js/components/ (e.g., MyMetricWidget.js) that accepts data props and renders the metric display (assets/src/js/components/MyMetricWidget.js)
  2. Import the component in assets/src/js/pages/dashboard.js and add it to the JSX render tree with appropriate data passed from the parent state (assets/src/js/pages/dashboard.js)
  3. Add any required CSS styling to assets/src/css/styles.css following the existing naming conventions (assets/src/css/styles.css)
  4. If the metric requires new API endpoints, add the route handler in the Go backend (not shown in file list but typically in a pkg/handlers or routes package)

Add a new tracking event type in the JavaScript tracker

  1. Edit assets/src/js/tracker.js to add a new event handler function that captures the desired event data (click, scroll, etc.) (assets/src/js/tracker.js)
  2. Extend the event payload structure to include the new event type and relevant metadata (assets/src/js/tracker.js)
  3. Ensure the tracker sends the event via the existing HTTP client to the backend analytics endpoint

Add a new configuration environment variable

  1. Add the new variable to .env.example with a description comment and example value (.env.example)
  2. Update docs/Configuration.md to document the new variable, its purpose, and valid values (docs/Configuration.md)
  3. In the Go backend config struct (typically in pkg/config), add the new field with envconfig struct tags to bind the environment variable

Add a new UI page (e.g., reports, settings)

  1. Create a new page file in assets/src/js/pages/ (e.g., reports.js) following the pattern of dashboard.js and login.js with Preact component export (assets/src/js/pages/reports.js)
  2. Update assets/src/js/script.js to add a route that loads the new page and wires it into the main router (assets/src/js/script.js)
  3. Add a navigation link in assets/src/js/components/Sidebar.js or relevant component to allow users to access the new page (assets/src/js/components/Sidebar.js)
  4. Create any page-specific components in assets/src/js/components/ and reuse shared utility functions from assets/src/js/lib/ (assets/src/js/lib/util.js)

🔧Why these technologies

  • Go (golang.org/x) — Type-safe, fast HTTP server with minimal dependencies; ideal for small self-hosted analytics backend
  • Preact — Lightweight (3KB) alternative to React; fast client-side rendering with minimal JavaScript bundle size for analytics UI
  • SQLite + MySQL + PostgreSQL support — Multi-database backend enables deployment flexibility: SQLite for single-user/dev, MySQL/PostgreSQL for production clustering
  • Gorilla Mux + Sessions — Lightweight HTTP router and session management without heavy web framework; explicit request routing
  • Gobuffalo Packr — Embed static assets (CSS, JS, fonts) directly into binary for zero-dependency deployment

⚖️Trade-offs already made

  • No real-time streaming; request-response API only

    • Why: Simplifies architecture and avoids WebSocket/SSE complexity; sufficient for analytics use case with minute-level granularity
    • Consequence: Dashboard requires explicit page refresh for new data; metrics are delayed by aggregation windows
  • Session-based authentication (cookies) instead of JWT

    • Why: Simpler server-side state management; secure by default in SPA context
    • Consequence: Not suitable for mobile apps or third-party API clients; CSRF protection required
  • Client-side analytics tracker embedded in each site

    • Why: Privacy-focused: avoids centralized third-party cookie tracking; user controls their data
    • Consequence: Requires manual tracker.js injection; no automatic tag management like Google Analytics
  • Single-user dashboard per instance (no multi-tenancy in Lite)

    • Why: Drastically simplifies authentication, authorization, and data isolation logic
    • Consequence: Not suitable for agencies or SaaS platforms; each organization needs separate self-hosted instance

🚫Non-goals (don't propose these)

  • Real-time analytics streaming (minute-level aggregation only)
  • Multi-tenant SaaS (single user per instance)
  • Cross-domain tracking (first-party only)
  • Third-party integrations (Zapier, webhooks, etc.)
  • Advanced attribution modeling (single-touch last-click only)
  • Predictive analytics or anomaly detection
  • Windows native installer (Docker/binary only)

🪤Traps & gotchas

Database initialization: Must set DATABASE_URL (or FATHOM_DATABASE_DRIVER/FATHOM_DATABASE_DSN) in .env before first run; migrations run automatically. Static asset embedding: Frontend CSS/fonts are embedded at build time via packr; local CSS changes require make build to reflect in binary. Go 1.19 lock: Project requires Go 1.19 exactly per go.mod; newer versions may have compatibility issues with older dependencies (e.g., sql-migrate). Session secrets: FATHOM_SECRET env var must be set (check .env.example) or sessions will fail. No hot reload: Development requires rebuilding binaries; no live-reload dev server configured.

🏗️Architecture

💡Concepts to learn

  • Cookie-free Analytics Tracking — Fathom Lite's core value proposition is privacy compliance without analytics cookies; understanding how server-side fingerprinting and session tokens replace cookies is essential to modifying tracking behavior
  • Database Migration Patterns (sql-migrate) — Fathom Lite uses rubenv/sql-migrate for schema versioning; understanding migrations is critical when adding fields, tables, or changing data models without losing production data
  • Cross-Database Abstraction (sqlx) — The codebase supports SQLite, MySQL, and PostgreSQL through sqlx; learning how to write database code that works across all three is essential for contributions
  • Static Asset Embedding (gobuffalo/packr) — Frontend CSS, fonts, and HTML are embedded into the binary at compile time using packr; understanding this workflow prevents confusion when local CSS changes don't appear in dev builds
  • HTTP Middleware Chains (Gorilla handlers) — Fathom Lite uses Gorilla handlers for logging, CORS, and session middleware; modifying request/response pipelines requires understanding middleware composition patterns
  • User-Agent Parsing for Bot Detection — The mssola/user_agent dependency suggests bot/crawler filtering is part of analytics tracking; understanding UA parsing is relevant when debugging why certain pageviews are excluded
  • CLI Application Structure (urfave/cli) — Fathom Lite likely exposes administrative commands via CLI (e.g., user management, data export); urfave/cli is the framework used for command definition and parsing
  • plausible/analytics — Privacy-focused analytics alternative to Fathom Lite built in Elixir; direct competitor offering similar self-hosted option with more recent active development
  • matomo/matomo — Mature self-hosted Google Analytics alternative (PHP-based); larger ecosystem with more features but also more operational complexity than Fathom Lite
  • ClickHouse/ClickHouse — Column-oriented SQL database commonly used as backend for analytics systems; relevant if scaling Fathom Lite's data storage architecture
  • usefathom/fathom-api — Official Fathom Analytics API documentation and examples; useful reference for understanding data structures if building integrations with the hosted Fathom product
  • preactjs/preact — The lightweight React alternative used in Fathom Lite's frontend; essential for understanding component patterns and optimization techniques in the dashboard UI

🪄PR ideas

To work on one of these in Claude Code or Cursor, paste: Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.

Add GitHub Action workflow for goimports verification and auto-fix

The repo has .github/workflows/goimports.yml defined but no corresponding pre-commit or auto-fix workflow. This workflow should verify import formatting on PRs and fail if imports are not properly organized. Currently, the test.yml and release.yml exist but goimports enforcement is unclear. Adding a robust CI check prevents import organization issues from being merged.

  • [ ] Examine .github/workflows/goimports.yml to understand current implementation
  • [ ] Create a new workflow that runs goimports -w on all Go files and fails CI if changes are needed
  • [ ] Add step to comment on PRs showing which files have import issues
  • [ ] Test workflow locally using act before submitting
  • [ ] Update CONTRIBUTING.md (if exists) to document the goimports requirement

Add unit tests for database migration layer (rubenv/sql-migrate)

The project uses github.com/rubenv/sql-migrate for migrations but there's no visible test coverage for migration logic. New contributors should add tests validating that migrations apply/rollback correctly and handle edge cases (duplicate migrations, partial failures). This ensures database integrity across versions.

  • [ ] Create pkg/migrations_test.go (or appropriate test file based on existing structure)
  • [ ] Write tests for migration up/down execution using in-memory SQLite database
  • [ ] Add tests for migration ordering and dependency validation
  • [ ] Test rollback scenarios and error handling
  • [ ] Add tests to .github/workflows/test.yml to ensure migrations run in CI

Add asset embedding tests and verify Packr v2 build consistency

The repo uses github.com/gobuffalo/packr/v2 to embed assets (fonts, CSS, HTML from assets/src/) but lacks tests verifying embedded assets are correctly packaged. Missing asset files at runtime would break the application. Add tests to ensure all assets in assets/src/ are properly embedded and accessible at runtime.

  • [ ] Create pkg/assets_test.go to test that packr box contains expected files
  • [ ] Add test to verify all files in assets/src/css/, assets/src/fonts/, and assets/src/404.html are accessible
  • [ ] Add test to check asset file sizes match source (detect missing files early)
  • [ ] Add build step in .github/workflows/test.yml to run packr box generation and verify no uncommitted changes
  • [ ] Document the asset build process in a DEVELOPMENT.md or BUILD.md file

🌿Good first issues

  • Add test coverage for database migration files (migrations/ directory likely exists but tests may be minimal) by writing integration tests using testify that verify schema changes work across SQLite, MySQL, and PostgreSQL.
  • Document the Makefile targets in a CONTRIBUTING.md file with examples of common tasks (e.g., 'make test', 'make lint', 'make release') since only generic targets are visible and junior developers need clarity on development workflow.
  • Create TypeScript/JSDoc type hints for the Preact components in assets/src/ to improve maintainability; refactor a high-usage component (e.g., the dashboard chart component, inferred from assets/src/css/chart.css) as a reference example.

Top contributors

Click to expand

📝Recent commits

Click to expand
  • 2d895d8 — Update readme (JackEllis)
  • 3fcc689 — free trial change from 7 to 30 (pjrvs)
  • b252c74 — Merge pull request #362 from oetiker/patch-1 (LKaemmerling)
  • 0bee50e — Make SiteSwitcher work (oetiker)
  • ae8d578 — Build for linux only as openbsd/freebsd should use docker (LKaemmerling)
  • 15d7e3f — Build for linux only as openbsd/freebsd should use docker (LKaemmerling)
  • 854fd02 — Fix build and use packerv2 (LKaemmerling)
  • 99f4c3a — Fix release (LKaemmerling)
  • 12e9e4c — Fix release (LKaemmerling)
  • e4ff4a0 — Fix release (LKaemmerling)

🔒Security observations

  • Critical · Hardcoded Database Credentials in docker-compose.yml — docker-compose.yml (lines with FATHOM_DATABASE_PASSWORD, MYSQL_PASSWORD, MYSQL_ROOT_PASSWORD). The docker-compose.yml file contains hardcoded database passwords and credentials in plain text (FATHOM_DATABASE_PASSWORD=password01, MYSQL_ROOT_PASSWORD=password01). This is a critical security risk as version control systems typically expose these files, and credentials become visible to anyone with repository access. Fix: Use Docker secrets, environment variable files (.env - not committed to repo), or orchestration platform secret management. Never commit credentials to version control. Use .env files for local development and exclude them via .gitignore.
  • High · Weak Default Secret in .env.example — .env.example (FATHOM_SECRET line). The .env.example file contains a weak default secret (FATHOM_SECRET=abcdefghijklmnopqrstuvwxyz1234567890) that appears predictable. While this is an example file, users may not change it in production, leading to compromised session security and authentication bypass. Fix: Document that users MUST generate a strong, random secret using a cryptographically secure method. Generate a minimum 32-byte random string. Add prominent warnings in documentation about changing this value before production use.
  • High · Outdated and Vulnerable Go Dependencies — go.mod (dependencies). Multiple dependencies have known security vulnerabilities and are outdated: golang.org/x/crypto v0.3.0 (should be v0.17.0+), gorilla/securecookie with old version pattern, and other transitive dependencies. These may contain known CVEs. Fix: Run 'go get -u' to update to latest versions. Run 'go mod tidy' and verify with 'go list -json -m all | nancy' or similar vulnerability scanner. Implement dependency scanning in CI/CD pipeline.
  • High · Unencrypted Database Communication (MySQL) — docker-compose.yml (mysql service, FATHOM_DATABASE_HOST). The docker-compose.yml exposes MySQL on 127.0.0.1:3306 without TLS/SSL enforcement. Connection strings in FATHOM_DATABASE_HOST don't specify SSL mode, allowing unencrypted credential transmission over the network. Fix: Enable MySQL SSL connections with REQUIRE SSL directive. Use connection strings with sslmode=required or similar. Configure MySQL with proper certificates. Restrict MySQL network access to internal networks only.
  • High · Missing Security Headers in HTTP Configuration — Dockerfile, docker-compose.yml (port exposure). The application exposes port 8080 without visible security headers configuration (HSTS, CSP, X-Frame-Options, etc.). The Dockerfile doesn't indicate security header middleware implementation. Fix: Implement security headers middleware in the Go application: HSTS, Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, Referrer-Policy. Use gorilla/handlers or similar security middleware.
  • High · Permissive MySQL Root Password and ALLOW_EMPTY_PASSWORD Configuration — docker-compose.yml (mysql environment variables). docker-compose.yml sets MYSQL_ALLOW_EMPTY_PASSWORD=false but uses weak password 'password01'. Additionally, root password is set to a simple value exposed in configuration. Fix: Use strong, randomly generated passwords. Disable root account or use complex credentials. Implement least-privilege accounts with restricted permissions. Never commit actual passwords; use secrets management.
  • Medium · SQL Injection Risk via sql-migrate and SQLx — go.mod (github.com/jmoiron/sqlx), codebase pattern unknown from structure. The project uses github.com/jmoiron/sqlx which allows raw SQL queries. If user input is concatenated into SQL without proper parameterization (common in analytics applications processing query parameters), SQL injection is possible. Fix: Audit all database queries to ensure parameterized queries (sqlx.In, prepared statements). Never concatenate user input into SQL. Use ORM or query builders if applicable. Implement input validation and sanitization.
  • Medium · XSS Risk in Analytics Dashboard Components — undefined. The Preact component structure (Chart.js, components rendering Fix: undefined

LLM-derived; treat as a starting point, not a security audit.


Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.

Healthy signals · usefathom/fathom — RepoPilot