Open source AI visibility tracking and optimization — self-hosted.
Learn more »
- About
- Features
- Demo
- Tech Stack
- Architecture Overview
- Project Structure
- Prerequisites
- Installation
- Configuration
- Usage
- Available Scripts
- API Documentation
- Database
- Deployment Documentation
- Testing
- Linting & Formatting
- CI/CD
- Security Notes
- Contributing
- Roadmap
- License
- Acknowledgements
- Contact
Getcito is an open-source, self-hosted platform for tracking and optimizing your AI visibility — also known as:
- AEO — Answer Engine Optimization
- GEO — Generative Engine Optimization
- LLMO — LLM Optimization
Getcito tracks how AI answer engines (ChatGPT, Google AI Mode, Perplexity, Gemini, Copilot, Grok, and others) mention, cite, and describe your brand — so you can benchmark competitors and grow your presence in AI answers. Run it on your own infrastructure, own your data, and audit exactly how every metric is calculated.
It is a free alternative to hosted tools like Profound, Peec, and Otterly.
- 📊 Prompt tracking — schedule prompts and record how AI engines answer them over time.
- 🔎 Citation & mention analysis — extract which brands, domains, and sources each AI response cites.
- 🏷️ Brand & competitor benchmarking — track your brand alongside competitors on the same prompts.
- 🤖 Multiple AI surfaces — ChatGPT, Google AI Mode, Google AI Overview, Perplexity, Gemini, Copilot, Grok.
- 🔌 Pluggable scrapers & LLM providers — BrightData, Oxylabs, Olostep, DataForSEO for scraping; OpenRouter, Anthropic, OpenAI, Mistral for direct LLM calls.
- 📄 Reports — background report generation over collected data.
- ⚙️ Background job engine — durable scheduling and retries via
pg-bosson PostgreSQL. - 🔑 REST API — Bearer-authenticated
/api/v1for brands, competitors, prompts, and reports. - 🎨 White-label mode — rebrandable deployment with Auth0 SSO.
Try the live demo at demo.getcito.com to see prompt tracking and citation analysis in action.
| Layer | Technology |
|---|---|
| Language | TypeScript 6, Node.js 24 |
| Monorepo | pnpm workspaces + Turborepo |
| Web app | TanStack Start / TanStack Router + React 19, Vite, Nitro |
| UI | Tailwind CSS 4, Radix UI, Recharts, Storybook |
| Background jobs | pg-boss |
| Database | PostgreSQL 16 + Drizzle ORM / drizzle-kit |
| Auth | Better Auth + Auth0 (white-label SSO) |
| AI SDKs | Vercel AI SDK, @ai-sdk/anthropic, @ai-sdk/openai, @anthropic-ai/sdk |
| Tooling | Biome (lint/format), Vitest, Playwright, Knip, Changesets |
| Delivery | Docker / Docker Compose |
Getcito is a pnpm/Turborepo monorepo of two apps backed by shared packages. The web app serves the dashboard and REST API; the worker runs scheduled scraping, analysis, and report jobs off a pg-boss queue in PostgreSQL. Scraper and LLM providers are called by the worker to collect AI answers.
flowchart LR
User([User / Browser]) -->|HTTP| Web[apps/web<br/>TanStack Start dashboard + /api/v1]
Web -->|read/write| DB[(PostgreSQL 16<br/>Drizzle schema)]
Web -->|enqueue jobs| DB
Worker[apps/worker<br/>pg-boss jobs] -->|poll queues| DB
Worker -->|scrape AI answers| Scrapers[BrightData / Oxylabs<br/>Olostep / DataForSEO]
Worker -->|direct LLM calls| LLM[OpenRouter / Anthropic<br/>OpenAI / Mistral]
Prompt tracking lifecycle:
sequenceDiagram
participant Sched as schedule-maintenance (cron */5)
participant Q as pg-boss queue
participant W as process-prompt job
participant P as Scraper / LLM provider
participant DB as PostgreSQL
Sched->>Q: enqueue due prompts
Q->>W: dispatch process-prompt
W->>P: run prompt on AI surface
P-->>W: answer + citations
W->>DB: persist snapshot & metrics
Worker queues (from apps/worker/src): process-prompt, generate-report, analyze-brand, schedule-maintenance (cron */5 * * * *), and sync-auth0-memberships (white-label only, */15 * * * *).
getcito/
├── apps/
│ ├── web/ # TanStack Start dashboard + /api/v1 (Vite dev :3000, Nitro build)
│ └── worker/ # pg-boss background jobs (tsx runtime)
├── packages/
│ ├── api-spec/ # OpenAPI 3 spec (openapi.json)
│ ├── config/ # Env registry/validation, scrape-target parsing, shared constants
│ ├── deployment/ # Deployment-mode wiring (local / demo / whitelabel)
│ ├── lib/ # DB (Drizzle), auth (Better Auth), providers, onboarding, DataForSEO
│ ├── local/ # Local-mode UI/components
│ ├── og/ # Open Graph image rendering (Takumi)
│ ├── ui/ # Design system (Radix + Tailwind)
│ └── whitelabel/ # White-label theming + Auth0 hooks
├── docker/ # Multi-stage Dockerfile + entrypoint
├── e2e/ # Playwright end-to-end tests
├── scripts/ # License check, release notes, version sync
├── docker-compose.yml
├── docker-compose.whitelabel.yml
├── turbo.json
└── pnpm-workspace.yaml
For Docker deployment:
- Docker with Docker Compose
For local development:
- Node.js 24.x (enforced via
engines; usenvm use 24) - pnpm 11.10.0 (
corepack enable pnpm) - PostgreSQL 16 (or run it via Docker)
Builds and runs the full stack (PostgreSQL, migrations, web, worker, Adminer) from source.
# Clone
git clone https://github.com/ai-search-guru/getcito-worlds-first-open-source-aio-aeo-or-geo-tool.git
cd getcito-worlds-first-open-source-aio-aeo-or-geo-tool
# Edit the .env file at the repo root and fill in real values (see Configuration)
# Build and start
docker compose build
docker compose up -dServices exposed on the host:
| Service | URL | Notes |
|---|---|---|
| Web app | http://localhost:3001 | Maps to container port 3000 |
| Adminer (DB UI) | http://localhost:8081 | PostgreSQL admin |
| PostgreSQL | localhost:5433 | Maps to container port 5432 |
Tip
Watch this repo's releases to get notified of major updates.
# Install dependencies
corepack enable pnpm
pnpm install
# Env file is read from the repo root AND apps/web/.env (see Configuration)
# Note: Complete .env.local files have been generated for you. You can copy them to .env:
cp .env.local .env
cp apps/web/.env.local apps/web/.env
# Run all dev servers via Turborepo
pnpm devDev port: web → http://localhost:3000.
The worker loads env from apps/web/.env (via --env-file) and is only needed for background job processing.
Environment variables are the single source of truth. The canonical registry lives in packages/config/src/env-registry.ts; startup validation reports any missing required vars for the active DEPLOYMENT_MODE.
Important
A complete .env.local file has been pre-generated for you in the repo root and within each app directory. You can use this file for your local overrides.
Because the worker strictly reads from apps/web/.env via the Node --env-file flag during pnpm dev, ensure you copy your .env.local content to .env in those locations.
Deployment modes (DEPLOYMENT_MODE): local, demo, whitelabel, cloud.
Required-column legend: local = required in local mode · wl = required in white-label mode · scraper = required only when SCRAPE_TARGETS references that provider · optional = never required at startup.
| Variable | Scope | Required | Description |
|---|---|---|---|
DATABASE_URL |
server | local, demo, wl | PostgreSQL connection string. |
DEPLOYMENT_MODE |
server | local, demo, wl | local, demo, whitelabel, or cloud. |
BETTER_AUTH_SECRET |
server | local, demo, wl | Session cookie encryption secret (random hex). |
SCRAPE_TARGETS |
server | local, demo, wl | Comma-separated model:provider[:version][:online] entries, e.g. chatgpt:brightdata:online,google-ai-mode:brightdata:online. |
APP_URL |
server | optional | Public base URL of the web app. |
VITE_APP_URL |
client | wl | Client-visible app URL. |
VITE_DEPLOYMENT_MODE |
client | optional | Client-visible copy of DEPLOYMENT_MODE. |
DISABLE_TELEMETRY |
server | optional | Set to any value to disable telemetry. |
ENVIRONMENT |
server | optional | Environment name reported to Sentry. |
| Variable | Provider | Description |
|---|---|---|
BRIGHTDATA_API_TOKEN |
brightdata | BrightData API token. |
OXYLABS_USERNAME / OXYLABS_PASSWORD |
oxylabs | Oxylabs Web Scraper API credentials. |
OLOSTEP_API_KEY |
olostep | Olostep API key. |
DATAFORSEO_LOGIN / DATAFORSEO_PASSWORD |
dataforseo | DataForSEO account credentials. |
| Variable | Provider | Description |
|---|---|---|
OPENROUTER_API_KEY |
openrouter | OpenRouter API key (one key, many models). |
ANTHROPIC_API_KEY |
anthropic-api | Anthropic API key. |
OPENAI_API_KEY |
openai-api | OpenAI API key. |
MISTRAL_API_KEY |
mistral-api | Mistral API key. |
| Variable | Scope | Required | Description |
|---|---|---|---|
AUTH0_CLIENT_ID |
server | wl | Auth0 client ID. |
AUTH0_CLIENT_SECRET |
server | wl | Auth0 client secret. |
AUTH0_MGMT_API_DOMAIN |
server | wl | Auth0 Management API domain. |
AUTH0_DOMAIN / AUTH0_AUDIENCE / AUTH0_SCOPE |
server | optional | Auth0 tenant/API config. |
VITE_APP_NAME / VITE_APP_ICON |
client | wl | White-label app name and icon URL. |
VITE_APP_PARENT_NAME / VITE_APP_PARENT_URL |
client | wl | Parent brand name and URL. |
VITE_OPTIMIZATION_URL_TEMPLATE |
client | wl | Optimization link template ({brandId}, {prompt}, {webQuery}). |
VITE_AUTH0_DOMAIN / VITE_AUTH0_CLIENT_ID |
client | optional | Client-exposed Auth0 config. |
| Variable | Scope | Description |
|---|---|---|
SENTRY_DSN / VITE_SENTRY_DSN |
server / client | Error reporting DSNs. |
SENTRY_ORG / SENTRY_PROJECT / SENTRY_AUTH_TOKEN |
server | Sourcemap upload at build time. |
VITE_POSTHOG_KEY |
client | PostHog project API key. |
VITE_PLAUSIBLE_DOMAIN / VITE_CLARITY_PROJECT_ID |
client | Analytics config. |
VITE_CHART_COLORS |
client | Comma-separated chart palette override. |
ADMIN_AUTH0_SUB / ADMIN_API_KEYS |
server | Admin access (subject claim / bearer tokens). |
DEFAULT_BRAND_DOMAINS |
server | Comma-separated domains added as default brands. |
DBOS_SYSTEM_DATABASE_URL |
server | Override for the DBOS system database URL. |
pnpm dev # start all dev servers (web :3000) via Turborepo
pnpm build # build all apps/packages for production
pnpm test # run unit tests (Vitest)
pnpm lint # lint with Biome
pnpm format # format with BiomeProduction (per app): pnpm build emits a Nitro server bundle for web; start with node .output/server/index.mjs (see the app's start script). The worker runs via tsx src/index.ts.
Root (package.json):
| Script | Command | Purpose |
|---|---|---|
dev |
turbo dev |
Start all dev servers. |
build |
turbo build |
Build all packages. |
lint |
turbo lint |
Biome lint across the workspace. |
test |
turbo test |
Run unit tests. |
format |
biome format --write . |
Format the codebase. |
knip |
knip |
Report unused files/deps/exports. |
changeset |
changeset |
Create a changeset. |
version-packages |
changeset version && node scripts/sync-root-version.mjs |
Bump versions. |
release |
pnpm build && changeset publish |
Build & publish. |
test:e2e |
pnpm --filter e2e test:e2e |
Run Playwright E2E tests. |
test:e2e-local |
bash e2e/run-local.sh |
Run E2E against a local stack. |
license-check |
node scripts/check-licenses.mjs |
Audit dependency licenses. |
Per app / package (selected):
| Package | Scripts |
|---|---|
apps/web |
dev (vite dev --port 3000), build, start, preview, test, test:storybook, storybook, lint, check-types |
apps/worker |
dev (tsx --env-file=../web/.env src/index.ts), start, build, check-types |
packages/lib |
test, generate:auth-schema, compare:onboarding |
The web app exposes a REST API under /api/v1. The full contract is defined in packages/api-spec/src/openapi.json (OpenAPI 3).
Authentication: Bearer token — Authorization: Bearer <token>.
| Resource | Endpoints |
|---|---|
| Brands | GET/POST /brands · GET/PATCH /brands/{brandId} |
| Competitors | GET/POST /competitors · GET/PATCH/DELETE /competitors/{competitorId} |
| Prompts | GET/POST /prompts · GET/PATCH/DELETE /prompts/{promptId} · GET /prompts/{promptId}/snapshot |
| Reports | GET/POST /reports · GET /reports/{reportId} |
| Tools | POST /tools/analyze |
Example:
curl -H "Authorization: Bearer $GETCITO_TOKEN" \
http://localhost:3001/api/v1/brands- PostgreSQL 16, accessed via Drizzle ORM (schema in
packages/lib/src/db). - Migrations are managed with drizzle-kit. Run from
packages/lib:
DATABASE_URL=postgres://postgres:postgres@localhost:5432/getcito \
npx drizzle-kit migrateUnder Docker Compose, the db-migrate service runs migrations automatically before web and worker start. pg-boss also creates its own queue schema on worker startup.
Getcito supports multiple deployment models via Docker Compose. Below are the comprehensive guides for deploying Getcito locally, as a demo, or as a fully white-labeled instance.
The local deployment builds and runs the full stack (PostgreSQL, automatic migrations, web dashboard, background worker, and Adminer DB UI) from source.
Prerequisites:
- Docker with Docker Compose plugin.
- A clone of this repository.
Step-by-step Setup:
-
Configure Environment Variables A complete
.env.localfile has been pre-generated in the repository root. Open.env.localand fill in your actual values. Ensure theDEPLOYMENT_MODEis set tolocal. For Docker Compose to pick it up properly via theenv_filedirective, copy it to.env:cp .env.local .env
Required variables include:
DATABASE_URL,DEPLOYMENT_MODE,BETTER_AUTH_SECRET, and any requiredSCRAPE_TARGETSAPI keys. -
Build Docker Images
docker compose build
-
Start the Stack
docker compose up -d
What happens at startup:
sequenceDiagram
participant D as Docker Compose
participant DB as PostgreSQL
participant M as db-migrate
participant W as Web / Worker
D->>DB: Start Database
DB-->>D: Healthy (port 5433)
D->>M: Run Migrations (drizzle-kit)
M-->>D: Completed
D->>W: Start Web and Worker
Access URLs:
- Web App: http://localhost:3001
- Adminer (Database UI): http://localhost:8081
- PostgreSQL:
localhost:5433(Username:postgres, Password:postgres, DB:getcito)
Common Commands:
- View logs:
docker compose logs -f web - Stop services:
docker compose down - Rebuild containers:
docker compose build --no-cache - Wipe database volume:
docker compose down -v
The Demo deployment mode (DEPLOYMENT_MODE=demo) is designed to host a public or staging instance where users can test the application.
Demo Capabilities Verified in Repo:
- The login screen will automatically read
VITE_DEMO_EMAILandVITE_DEMO_PASSWORDfrom your environment variables to pre-fill the login form for test users. - Relies on the same standard infrastructure (PostgreSQL, web, worker).
Setup:
- Configure your
.envfile withDEPLOYMENT_MODE=demoandVITE_DEPLOYMENT_MODE=demo. - Add your demo credentials:
VITE_DEMO_EMAIL=team@getcito.com VITE_DEMO_PASSWORD=your_demo_password
- Deploy using the standard Compose commands:
docker compose build docker compose up -d
(Note: External cloud hosting or demo-specific infrastructure is not provided in this repository; it relies solely on the demo mode application flag).
Getcito supports a fully rebrandable White Label mode powered by Auth0 SSO.
White Label Capabilities:
- Custom branding baked into the client bundle at build time: App Name, App Icon, Parent Brand URLs, and Custom Optimization URL Templates.
- Authentication delegated completely to Auth0.
- Allows deploying via a custom Docker Compose file that maps to standard ports (Web on
3000, DB on5432, Adminer on8080).
Setup:
-
Create the Compose Template Create a new file named
docker-compose.whitelabel.ymlin the root directory and paste the following configuration. This file is intentionally omitted from the repository to prevent accidental leakage of proprietary modifications.Click to view
docker-compose.whitelabel.ymltemplateservices: postgres: image: postgres:16-alpine ports: - "127.0.0.1:5432:5432" environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: getcito volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 5s timeout: 5s retries: 5 adminer: image: adminer restart: always ports: - "8080:8080" environment: ADMINER_DEFAULT_SERVER: postgres start_period: 30s db-migrate: build: context: . dockerfile: docker/Dockerfile target: migrate environment: - DATABASE_URL=postgres://postgres:postgres@postgres:5432/getcito depends_on: postgres: condition: service_healthy web: build: context: . dockerfile: docker/Dockerfile target: web args: DEPLOYMENT_MODE: whitelabel VITE_DEPLOYMENT_MODE: whitelabel VITE_APP_NAME: ${VITE_APP_NAME} VITE_APP_ICON: ${VITE_APP_ICON} VITE_APP_URL: ${VITE_APP_URL} VITE_APP_PARENT_NAME: ${VITE_APP_PARENT_NAME} VITE_APP_PARENT_URL: ${VITE_APP_PARENT_URL} VITE_OPTIMIZATION_URL_TEMPLATE: ${VITE_OPTIMIZATION_URL_TEMPLATE} VITE_ONBOARDING_REDIRECT_URL_TEMPLATE: ${VITE_ONBOARDING_REDIRECT_URL_TEMPLATE} VITE_AUTH0_DOMAIN: ${VITE_AUTH0_DOMAIN} VITE_AUTH0_CLIENT_ID: ${VITE_AUTH0_CLIENT_ID} env_file: - .env ports: - "3000:3000" depends_on: postgres: condition: service_healthy db-migrate: condition: service_completed_successfully worker: build: context: . dockerfile: docker/Dockerfile target: worker env_file: - .env depends_on: postgres: condition: service_healthy db-migrate: condition: service_completed_successfully volumes: postgres_data:
-
Configure Environment Variables Ensure
DEPLOYMENT_MODE=whitelabeland provide the required Auth0 and branding values in your.envfile.DEPLOYMENT_MODE=whitelabel VITE_DEPLOYMENT_MODE=whitelabel AUTH0_CLIENT_ID=your_client_id AUTH0_CLIENT_SECRET=your_client_secret AUTH0_MGMT_API_DOMAIN=your_mgmt_api_domain VITE_APP_NAME="Acme AI Search" VITE_APP_ICON="https://cdn.example.com/icon.png" VITE_APP_URL="http://localhost:3000" VITE_APP_PARENT_NAME="Acme" VITE_APP_PARENT_URL="http://localhost:3000" VITE_OPTIMIZATION_URL_TEMPLATE="..."
-
Build and Deploy Important: Client variables (
VITE_*) are read during the build step and baked into the image.docker compose -f docker-compose.whitelabel.yml build docker compose -f docker-compose.whitelabel.yml up -d
pnpm test # unit tests (Vitest) across the workspace
pnpm --filter @workspace/web test:storybook # Storybook interaction tests
# End-to-end (Playwright) — requires browsers + a running app
pnpm exec playwright install
pnpm test:e2e # or: pnpm test:e2e-localE2E tests live in e2e/ and are separate from unit tests.
Both linting and formatting are handled by Biome (config in biome.json).
pnpm lint # turbo lint (biome lint per package)
pnpm format # biome format --write .
pnpm knip # detect unused files, dependencies, and exportsNote
Biome reports Tailwind @apply directive warnings in CSS files — these are pre-existing and expected.
This repo does not ship GitHub Actions workflows. Automation currently present in .github/:
dependabot.yml— automated dependency updates.CODEOWNERS,contributors.txt(CLA signatures),FUNDING.yml.
Releases are managed with Changesets (pnpm changeset → pnpm version-packages → pnpm release).
- Never commit secrets. API keys,
.envcontents, and auth secrets must stay out of version control. GenerateBETTER_AUTH_SECRETas random hex per deployment. - Docker runtime images run as non-root users.
pnpm-workspace.yamlpins security-patched transitive dependency versions (see theoverridesblock) and enforces a minimum release age / no-downgrade trust policy for new packages.- Report vulnerabilities privately per
SECURITY.md— email team@getcito.com, do not open a public issue.
Contributions are welcome. For minor fixes, open a PR directly; for larger changes, open an issue first. See CONTRIBUTING.md and AGENTS.md.
- Branching / PRs: open PRs from your own fork/account against
main. - Coding standards: TypeScript; format & lint with Biome (
pnpm format,pnpm lint); keep changes type-clean (check-types). - Changesets: add a short, end-user-focused changeset scoped to the affected packages (
pnpm changeset). - CLA: before your first PR merges, add your GitHub username to
.github/contributors.txt— seeCLA.md. - Code of Conduct: see
CODE_OF_CONDUCT.md.
Tracked on the GitHub Project board and in Issues.
Released under the MIT License. © 2026 GetCito. Contributions are covered by the Contributor License Agreement.
Built on TanStack Start, Drizzle ORM, Better Auth, pg-boss, Fumadocs, Biome, and Turborepo, and integrates BrightData, Oxylabs, Olostep, DataForSEO, OpenRouter, Anthropic, OpenAI, and Mistral.
- 📧 team@getcito.com
- 📞 India: +91-96505-10773 · US: +1-623-223-7423