Self-hosted product analytics: event tracking, user identification, funnel analysis, retention cohorts, real-time counters, and quota enforcement — all stored in your own Postgres. No third-party analytics vendor, no data leaving your instance. Ships a JavaScript browser SDK plus server-side SDKs for Go and TypeScript. Events are ingested asynchronously via a Redis-backed queue and materialized into query-ready aggregates on a configurable schedule.
nself plugin install analytics
nself build
nself start| Variable | Required | Default | Description |
|---|---|---|---|
ANALYTICS_RETENTION_DAYS | No | 90 | Raw event retention before automatic pruning |
ANALYTICS_AGGREGATE_INTERVAL | No | 5m | How often to materialize aggregates from raw events |
ANALYTICS_QUEUE_BUFFER | No | 10000 | Max in-flight events in the Redis ingest queue |
ANALYTICS_BATCH_SIZE | No | 500 | Events flushed to Postgres per write cycle |
ANALYTICS_QUOTA_ENABLED | No | false | Enable real-time quota enforcement via counter checks |
ANALYTICS_SAMPLE_RATE | No | 1.0 | Event sampling ratio (0.1 = 10%). Applied server-side after receipt |
ANALYTICS_IP_HASH | No | true | Hash IP addresses before storage for GDPR compliance |
# Track a server-side event via REST
curl -X POST https://api.yoursite.com/analytics/track \
-H "Authorization: Bearer $TOKEN" \
-d '{
"event": "subscription.created",
"user_id": "user-uuid",
"properties": {
"plan": "pro",
"amount_cents": 999
}
}'
# Identify a user (set profile properties)
curl -X POST https://api.yoursite.com/analytics/identify \
-H "Authorization: Bearer $TOKEN" \
-d '{
"user_id": "user-uuid",
"traits": {
"email": "alice@example.com",
"name": "Alice"
}
}'# Install the JS SDK
pnpm add @nself/analytics-jsimport { Analytics } from '@nself/analytics-js'
const analytics = new Analytics({
writeKey: 'your-write-key',
host: 'https://api.yoursite.com',
})
analytics.track('page.viewed', { path: window.location.pathname })
analytics.identify(userId, { email: user.email })When ANALYTICS_QUOTA_ENABLED=true, each track call increments a Redis counter for the event name and user. Call /analytics/quota/check to test whether a user has exceeded a configured limit before performing an action. Quota definitions are stored in np_analytics_quotas and can be updated without a rebuild.
| Endpoint | Method | Description |
|---|---|---|
/analytics/track | POST | Ingest a single event |
/analytics/batch | POST | Ingest up to 100 events in one request |
/analytics/identify | POST | Set or update user traits |
/analytics/page | POST | Record a page view with URL and referrer |
/analytics/funnel | GET | Conversion funnel query across an ordered event sequence |
/analytics/retention | GET | Cohort retention table for a given signup date range |
/analytics/counters/:event | GET | Real-time event count (uses Redis counter, not Postgres) |
/analytics/quota/check | POST | Check whether a user has exceeded a quota limit |
/analytics/health | GET | Queue depth and aggregate lag check |
np_analytics_events — raw event log (pruned per retention policy)np_analytics_users — user trait store (identified profiles)np_analytics_aggregates — materialized event counts per time bucketnp_analytics_funnels — saved funnel definitionsnp_analytics_quotas — quota rule definitions per event and user segmentPro Plugin — ɳSelf+ | Port: 3206 | v1.1.1