# Vector 5 documentation Source: https://docs.vector5.ai # Introduction Vector 5 is cookieless analytics that measures humans, AI crawlers, and assistant referrals — with a public collection manifest anyone can fetch. Every privacy-first analytics tool runs in the browser. GPTBot, ClaudeBot, and PerplexityBot do not. Humans who click a ChatGPT citation often arrive with no referrer and get filed as Direct. Vector 5 was built for that gap. It is a clean-room project — not a Plausible fork. The server and dashboard are AGPL-3.0. The tracker, middleware, MCP server, and agent skills are MIT so you can paste them into any site. - **~1 KB** Tracker size (no cookies, no localStorage) - **5** Actors (classified in Go, server-side) - **6** MCP tools (read-only aggregates) - **1** Database (Postgres. No extra warehouse.) ## Two ingest paths The browser script counts people. The server middleware counts everything that never runs JavaScript. Both post to the same Go API, which classifies every hit with an actor and discards the IP after hashing and a country lookup. | Path | Who it sees | Endpoint | Package | | --- | --- | --- | --- | | v5.js | Browsers that execute JavaScript | POST /api/event | @vector5/tracker · MIT | | Middleware | Crawlers, agents, and optional humans | POST /api/hit | @vector5/middleware · MIT | ## Five actors - `human` — browser visitor - `human_via_ai` — arrived from an assistant - `ai_crawler` — GPTBot and peers - `ai_agent` — headless agent - `bot` — other automation Dashboards default to humans. The AI Traffic panel reports crawlers and assistant referrals. Agent metrics are task-oriented — not bounce rate. Read the full model in Actors. ## What you get - A 1 KB cookieless tracker with SPA, outbound, and download events out of the box. - Server middleware for Next.js, Express, Hono, Cloudflare Workers, and Node. - A public collection manifest at /api/sites/{domain}/manifest — cookies, IPs, and fingerprints are all false. - AI insights that only ever see rollups. Ollama, OpenAI, Anthropic, or Gemini. - An MCP server so Claude, Cursor, or Codex can query the same aggregates the dashboard shows. - Self-host with Docker Compose: Postgres, the API, and the dashboard. ## What you do not get - Who a visitor is. - Whether they returned after midnight UTC. - Cross-site or cross-device identity. - Exact address (city is optional and off by default). - Anything about visitors who block the request entirely. > **Hosted product.** vector5.ai is invite-only. Request access or self-host today. ## Licence Server and dashboard: AGPL-3.0. Tracker, middleware, MCP, and skills: MIT. See Licence and ADR 0001. - [Quick start](/quick-start) — Compose up, add a site, drop the tag. - [Concepts](/concepts) — Actors, hashes, rollups, and manifests. - [Install the tracker](/tracker) — One script before . - [Add middleware](/middleware) — See the crawlers JavaScript never sees. --- # Quick start Self-host Vector 5 with Docker Compose, add a site, and ship the tracker plus middleware in under ten minutes. Self-host with Docker Compose. Postgres, the Go API, and the Next.js dashboard come up together. On a self-hosted instance you sign in with the operator account from .env. ## 1. Start the stack ```bash git clone https://github.com/vector5-ai/analytics cd analytics cp .env.example .env docker compose up --build # Dashboard http://localhost:3000 # API http://localhost:8080 ``` > **Change the defaults.** V5_ADMIN_PASSWORD and V5_JWT_SECRET in .env are placeholders. Do not ship them to a public host. ## 2. Sign in Open http://localhost:3000/app/login. Default operator account: admin@localhost / changeme. Add a site — the domain must match what you will put in data-domain. ## 3. Add the tracker ```html ``` The script is about 1 KB, sets no cookies, and writes nothing to localStorage. Full options live in Tracker. ## 4. Add the middleware Crawlers never load v5.js. Install @vector5/middleware so GPTBot and friends still count. ```ts import { vector5 } from '@vector5/middleware/next' export const middleware = vector5({ endpoint: 'http://localhost:8080/api/hit', domain: 'example.com', }) ``` Same package ships Express, Hono, Workers, and Node adapters. See Middleware. ## 5. Verify 1. **Hit a page as yourself.** Open the site. The dashboard realtime strip should show a human within a few seconds. 2. **Simulate a crawler.** curl -A "GPTBot" https://example.com/docs. The AI Traffic panel should increment GPTBot. 3. **Fetch the manifest.** GET /api/sites/example.com/manifest should report cookies: false, ip_stored: false, and the daily-hash formula. ## Hosted instead The hosted product at vector5.ai is invite-only. Request access. For your own cloud deploy set V5_AUTH_MODE=supabase and point the app at your Supabase project — see Supabase auth. --- # Concepts The vocabulary Vector 5 uses everywhere: sites, actors, visitor hashes, rollups, manifests, and insights. If you only read one page besides the quick start, read this. Every dashboard filter, API query, and MCP tool uses the same nouns. ## Site A site is a hostname you register in the dashboard (example.com). The tracker's data-domain and the middleware's domain must match it. Events for unknown domains are rejected with 404 unknown site. ## Event vs hit | | `POST /api/event` | `POST /api/hit` | | --- | --- | --- | | Sender | v5.js in the browser | Server middleware | | JavaScript executed? | Yes — jsExecuted = true | No | | Typical actor | human, human_via_ai | ai_crawler, ai_agent, bot | | Payload | URL, referrer, UTMs, screen, props | method, path, status, UA, referer, IP | ## Actor A deterministic label on every row. Classification happens in Go (apps/api/internal/classify) from user-agent, referrer, UTM, and whether JS ran. See Actors. ## Visitor hash ```text visitor_hash = SHA-256(daily_salt || site_id || ip || user_agent) ``` The salt rotates at 00:00 UTC per site. After hashing, the IP is dropped. A visitor who returns tomorrow is counted as new. That is intentional. Visitor hash. ## Rollups Raw events are partitioned by month. The dashboard and MCP never scan them for charts — they read hourly and daily rollups computed inside the API process. No extra workers. Insights and anomaly flags are generated from rollups only. ADR 0002 and ADR 0005. ## Collection manifest Every site publishes a machine-readable JSON document at /api/sites/{domain}/manifest. It states what is collected, what is not, how uniques work, and the retention window. Anyone — including an auditor or an agent — can fetch it without auth. Manifest. ## Insights Optional. Configure a provider per team (PATCH /api/team/llm): Ollama (llama3.2 by default), an OpenAI-compatible endpoint, Anthropic, or Gemini. The model receives period totals, crawler counts, and crawl→referral pairs — never event rows. Every derived number is labelled observed or inferred. --- # Architecture A Go ingest API, a Next.js dashboard, Postgres, and four MIT client packages. One compose file. No extra workers. Vector 5 is a monorepo: apps/api (Go), apps/web (Next.js dashboard + marketing), apps/docs (this site), and MIT packages under packages/. ## Repository layout | Path | Role | Licence | | --- | --- | --- | | apps/api | Go binary v5d — ingest, classify, rollup, query, auth | AGPL-3.0 | | apps/web | Dashboard, marketing site, waitlist | AGPL-3.0 | | apps/docs | Documentation (docs.vector5.ai) | AGPL-3.0 | | packages/tracker | v5.js — ~1 KB browser script | MIT | | packages/middleware | Next / Express / Hono / Workers / Node | MIT | | packages/mcp | stdio MCP server, six tools | MIT | | packages/skills | Ready-made agent prompts | MIT | ## What happens on a pageview 1. The browser posts { d, n, u, r, h, w, p, utm_* } to /api/event via sendBeacon. 2. The API looks up the site, loads today's salt, parses the UA, classifies the actor, looks up country, hashes the visitor, discards the IP. 3. The row is written to events. Hourly/daily rollups update in-process. 4. If the visitor was GPTBot, this step never happened — the middleware posted /api/hit instead, with jsExecuted = false. ## Storage Postgres 17. Events and agent hits are partitioned by month. Dashboard queries hit rollups. A storage interface is left so ClickHouse can be added later; it is not required. See ADR 0002. ## Auth modes - V5_AUTH_MODE=admin — single operator from .env. Default for self-host and local. - V5_AUTH_MODE=supabase — hosted / multi-user. JWT from your Supabase project. API keys (v5_live_…) are created under Settings and sent as Authorization: Bearer. They are scoped to the team and work for every read route plus MCP. --- # Hosted vs self-host Same software. Invite-only cloud at vector5.ai, or Docker Compose on your metal. ## Side by side | | Hosted (vector5.ai) | Self-host | | --- | --- | --- | | Access | Invite-only waitlist | Clone and compose up | | Auth | Supabase | Admin user, or your own Supabase | | Tracker URL | https:///js/v5.js | http://localhost:8080/js/v5.js or your domain | | Data residency | Our region | Yours | | LLM | Optional, your key or ours later | Ollama on-prem, or any provider | | Updates | We ship them | You pull | | Licence | Same AGPL + MIT split | Same — cloud resellers must publish modifications | ## When to self-host - You need the data to stay in your VPC. - You want Ollama and zero third-party model calls. - You are evaluating before requesting hosted access. ## When to use hosted - You do not want to operate Postgres. - You want the dashboard on a Vector 5 domain with backups handled. - You are fine with invite-only while the product is early. - [Docker Compose](/self-host/docker) — The supported self-host path. - [Supabase auth](/self-host/supabase) — Multi-user cloud on your project. - [Request access](https://vector5.ai/access) — Hosted waitlist on vector5.ai. --- # Tracker script A ~1 KB cookieless script. One tag before . No cookies, no localStorage identifiers, no fingerprints. v5.js is the browser half of Vector 5. It counts people who execute JavaScript. Crawlers that never run JS are the middleware's job. ## Install ```html ``` The API serves the script at /js/v5.js (5-minute cache). You can also vendor packages/tracker/dist/v5.js and point data-api at your ingest host. - [Framework recipes](/tracker/install) — Next.js, WordPress, Shopify, Astro, Remix, SvelteKit, Webflow. - [Attributes](/tracker/attributes) — data-domain, data-api, data-hash. - [Custom events](/tracker/events) — window.v5 and data-v5-event. - [SPAs](/tracker/spa) — pushState is already hooked. ## What it sends | Field | JSON key | Source | | --- | --- | --- | | Domain | d | data-domain or location.hostname | | Event name | n | pageview unless you pass one | | URL | u | location.href | | Referrer | r | document.referrer | | Hostname | h | location.hostname | | Screen width | w | screen.width | | Props | p | Optional object from window.v5 | | UTMs | utm_* | From location.search if present | The server derives referrer host, country, device, browser, and OS, then discards the IP. The script never sends a visitor id. ## Automatic events - Pageviews on load, pushState, replaceState, and popstate. - Hash changes when data-hash="true". - Outbound Link on clicks to another host. - File Download for pdf, zip, csv, xlsx, docx, png, mp4. - data-v5-event on any clicked element. ## Ignore yourself ```ts localStorage.v5_ignore = '1' ``` The script reads that key once at boot and exits. It is the only localStorage access, and it is opt-in on your machine. Ignore visits. --- # Install on any stack Drop v5.js into Next.js, WordPress, Shopify, Astro, Remix, SvelteKit, Webflow, or a raw HTML file. The script must load on every public page. Put it in the document head with defer. Do not load it on admin or preview routes if you want to keep editors out of the numbers — or use v5_ignore. ## HTML ```html ``` ## Next.js ```ts // App Router · app/layout.tsx import Script from 'next/script' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ``` ## Shopify Online Store → Themes → Edit code → theme.liquid, just before . ## Astro, Remix, SvelteKit, Webflow - Astro — put the script in src/layouts/Layout.astro . - Remix — app/root.tsx inside via a ``` ## data-hash Set data-hash="true" for docs sites and older SPAs that route on the fragment. Modern pushState apps do not need it — those navigations are already hooked. --- # Goals and custom events Call window.v5, mark an element with data-v5-event, then create a matching goal in the dashboard. ## window.v5 ```ts // Custom event with props window.v5('signup', { plan: 'pro' }) // A pageview you triggered yourself window.v5('pageview') ``` window.v5 is assigned after the script boots. If you fire events from a bundle that may load first, guard with window.v5?.('signup') or call after load. ## data-v5-event ```html Download ``` Clicks on [data-v5-event] win over the automatic outbound / download detectors on the same element. ## Goals Create a goal in the dashboard (or POST /api/sites/{id}/goals) with the same name. Conversion rates are filterable by actor, so agents never inflate them. See Dashboard → Goals. ## Props Props are a JSON object on the event (p). Use them for plan names, variants, or file URLs. Do not put emails, user ids, or anything that could identify a person — the privacy model assumes event props are non-identifying. The API accepts at most 64 KB per request. --- # SPAs and hash routing pushState, replaceState, and popstate are hooked automatically. Hash routing is opt-in. ## History API The script wraps history.pushState and history.replaceState, and listens for popstate. Next.js, Remix, SvelteKit, Vue Router, and React Router all go through those primitives, so client-side navigations count without extra code. > **Next.js App Router.** You do not need a usePathname effect. The tracker already sees the URL change. ## Hash routing ```html ``` ## Manual pageviews If you have a custom router that does not touch history (rare), call window.v5('pageview') after you update the URL. --- # Ignore your own visits A single localStorage flag on your machine. The script never writes it — you do. ## The flag ```ts localStorage.v5_ignore = '1' ``` On boot the script reads localStorage.v5_ignore inside a try/catch and returns if it is set. That is the only localStorage access. The collection manifest still reports local_storage: false because Vector 5 never writes an identifier there. To resume counting, delete localStorage.v5_ignore and reload. ## Staff and preview - Do not load the script on /admin, /studio, or CMS preview iframes. - Staging hostnames should be a separate site, or omitted from the dashboard. - The middleware never sees this flag — it only applies to v5.js. --- # Next.js tracker Script in the root layout, middleware for crawlers, and optional hash tracking for docs. ## Root layout ```ts import Script from 'next/script' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children}