> Installation
Get Echelon Analytics running in under a minute.
Prerequisites
- Deno 2.x (includes SQLite via
node:sqlite) - No external database — single SQLite file, zero config
Quick Start
git clone https://github.com/janit/ea.git cd ea/echelon-analytics deno task dev
The dev server starts on http://localhost:1947 with Vite HMR.
Add the Tracker
Drop this script tag into any page you want to track:
<script async src="https://your-host/ea.js" data-site="my-site"></script>
That's it. Pageviews, bounces, and sessions are tracked automatically.
Development Commands
All commands run from echelon-analytics/:
# Development server (Vite HMR) deno task dev # Production build deno task build # Start production server (must build first) deno task start # Check formatting, lint, and type-check deno task check # Individual checks deno fmt --check . deno lint . deno check main.ts # Update Fresh framework deno task update
Docker
Multi-stage build with non-root user and health check:
# Build docker build -f confs/Dockerfile -t echelon-analytics . # Run docker run -p 1947:1947 -v echelon-data:/app/data echelon-analytics
The container exposes port 1947. Mount a volume at /app/data/ for the SQLite database (required for WAL mode to work correctly).
Docker Details
- Base image:
denoland/deno:2.7.1 - Uses
tinifor proper PID 1 signal handling - Non-root user
echelon(UID 1001) - Health check:
GET /api/healthevery 30s - Build args:
VERSION,GIT_HASH
Docker with Build Args
docker build -f confs/Dockerfile \ --build-arg VERSION=1.0.0 \ --build-arg GIT_HASH=$(git rev-parse --short HEAD) \ -t echelon-analytics .
Reverse Proxy (Caddy)
Example Caddyfile for automatic HTTPS:
echelon.example.com {
reverse_proxy localhost:1947
}
Reverse Proxy (Nginx)
server {
server_name echelon.example.com;
location / {
proxy_pass http://127.0.0.1:1947;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Set ECHELON_TRUST_PROXY=true when behind a reverse proxy to read the real client IP from X-Forwarded-For / X-Real-IP.
Cloudflare
When deployed behind Cloudflare, set:
ECHELON_BEHIND_CLOUDFLARE=true ECHELON_TRUST_PROXY=true
This enables reading Cloudflare headers (cf-ipcountry, cf-connecting-ip) for geo data and bot scoring.
Authentication Setup
API Token
ECHELON_SECRET=your-secret-token-here
Use with Authorization: Bearer your-secret-token-here header.
Username/Password
# Generate a password hash
cd echelon-analytics
deno eval "import{hashPassword}from'./lib/auth.ts';console.log(await hashPassword('yourpassword'))"
# Set environment variables
ECHELON_USERNAME=admin
ECHELON_PASSWORD_HASH=pbkdf2$600000$<salt>$<hash>
Both auth modes can be used simultaneously.
Single-Process Constraint
Echelon Analytics keeps all state in memory (sessions, rate limiter, burst maps, buffered writers). You must run with a single Deno worker — do not use the --parallel flag with deno serve.