Getting started with Analytics
Clone the source, configure PostgreSQL, sessions, and S3 storage, apply migrations, create the first admin, load demo data or add a site, install the script, and deploy.
Requirements
- Node.js matching
package.jsonengines(>=20.9.0). The app uses Next.js 16 and React 19. - npm (the repository ships a
package-lock.json). - A PostgreSQL database you are allowed to migrate. Reports use PostgreSQL SQL directly, and the collector uses
pg_advisory_xact_lock, so other databases are not supported. - S3-compatible object storage settings when
NODE_ENV=production.
Live demo
A hosted demo runs at analytics.openship.org. The public site lists the demo dashboards, and the operator dashboard is at analytics.openship.org/dashboard. Its numbers come from generated demo traffic.
Install
git clone https://github.com/openshiporg/analytics.git
cd analytics
npm cinpm install also works. The repository does not ship a .env.example, so create .env yourself.
Environment
features/keystone/index.ts refuses to start without DATABASE_URL and SESSION_SECRET, and rejects a session secret shorter than 32 characters. The five S3 values fall back to development placeholders outside production and are required when NODE_ENV=production.
DATABASE_URL=postgresql://user:password@127.0.0.1:5432/openfront_analytics
SESSION_SECRET=replace-with-at-least-32-random-characters # openssl rand -hex 32
S3_BUCKET_NAME=your-bucket
S3_REGION=your-region
S3_ACCESS_KEY_ID=your-access-key-id
S3_SECRET_ACCESS_KEY=your-secret-access-key
S3_ENDPOINT=https://your-s3-compatible-endpointSESSION_SECRET does more than sign dashboard sessions. The collector derives the daily visitor salt from it, and shared-link unlock tokens are signed with it. Changing it signs everyone out, invalidates unlocked shared-link cookies, and means the same visitor gets a new hash for the rest of that day.
Optional settings read by the code:
| Variable | Used for |
|---|---|
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD, SMTP_FROM | Password reset email. Without SMTP_HOST, the reset page says no mail transport is configured. |
SMTP_STORE_LINK | Base URL used in reset links |
MAIL_USER | If it contains ethereal.email, the mailer logs a test preview link |
PUBLIC_SIGNUPS_ALLOWED | Set to true to open /dashboard/signup |
OPENROUTER_API_KEY, OPENROUTER_MODEL, OPENROUTER_MAX_TOKENS | Shared key and model for the dashboard AI chat (/api/completion); max tokens defaults to 4000 |
NEXT_PUBLIC_VIEW_ORDER | Dashboard field view ordering (generated by npm run migrate:gen) |
Keep .env out of source control.
Migrations and local run
npm run devnpm run dev runs keystone build --no-ui, applies the checked-in migrations with prisma migrate deploy, and starts Next.js. The repository has one migration, 20261025211115_initial_analytics. To change the schema, run npm run migrate:gen against a disposable database. It rebuilds the schema, regenerates the dashboard view order, and runs keystone prisma migrate dev. Commit model changes and their migrations together.
npm run dev applies migrations to whatever DATABASE_URL points at. Do not point it at a shared or production database.
Once running:
- Public site:
/ - Operator dashboard:
/dashboard - GraphQL API:
/api/graphql - Tracker script:
/assets/script.js - Collector:
/api/track
First user
On an empty database, every page redirects to /dashboard/init. Create the first account there. It gets an Admin role with every permission turned on. Then sign in. Later users are added from the generic User list, or through /dashboard/signup if PUBLIC_SIGNUPS_ALLOWED=true. Give them a Role with the permissions they need.
Onboarding
The sidebar shows a Get started card to users with canManageOnboarding. It opens a setup dialog with three templates:
| Template | What it loads |
|---|---|
| Complete Setup | Store settings, three sites, nine goals, three funnels, three segments, two shared links, and 60 days of traffic per site |
| Basic Setup | Store settings and the Northwind Coffee site with 14 days of traffic |
| Custom Setup | Your own JSON in the same shape as features/platform/onboarding/lib/seed.json |
Each record is written through the ensureOnboardingRecord mutation. It reuses an existing record with the same natural key (for example the site domain) and never overwrites it, so setup can be run again. Traffic is generated in the browser and sent through importSiteSessions with stable keys, so a re-run skips visits it already imported. The seed marks two sites as public and gives one shared link a password; change or delete those on a real instance.
Add a real site
- Open
/dashboard/platform/sitesand create a site with a name, domain (a hostname such asexample.com; subdomains are included), timezone (an IANA name), and currency (a three-letter code). - Open the site's Settings tab and copy the tracking snippet. It looks like this:
<script defer src="https://your-analytics-host/assets/script.js" data-site-id="site_..."></script>- Add it to the head of every page. Hits from
localhostare skipped unless the script tag hasdata-allow-localhost="true". - Visit the site and check the Realtime tab.
Deployment
Scripts in package.json:
| Script | Effect |
|---|---|
npm run build | keystone build --no-ui && next build; does not migrate |
npm run migrate | prisma migrate deploy |
npm start | next start |
npm run lint | ESLint |
npm test | Node test runner over tests/*.test.ts and tests/*.test.cjs |
A typical release builds, applies migrations against a backed-up database, then starts:
npm run build
npm run migrate
npm startThe included railway.toml builds with keystone build --no-ui && next build and starts with npm run migrate && next start, because the build cannot reach an internal database. The README also has a Vercel deploy button. Either way, provision PostgreSQL and S3-compatible storage yourself and share the same database and SESSION_SECRET across all app processes. Put the app behind a proxy that sets X-Forwarded-For correctly, and ideally one that sets a country header such as cf-ipcountry or x-vercel-ip-country (see Integrations and boundaries).
Openfront Analytics
Self-hosted web analytics with a cookieless tracking script, a site dashboard, goals, funnels, realtime, Core Web Vitals, and public or shared dashboards.
Analytics architecture and workflows
Keystone lists, how a hit becomes a visit, the report engine, goals, funnels, segments, public and shared dashboards, imports, deletion, GraphQL operations, and access control.