Getcito

Open source AI visibility tracking and optimization — self-hosted.

Learn more »


Docs  Demo  Issues  Roadmap  License: MIT  Node 24.x

--- ## Table of Contents - [About](#about) - [Features](#features) - [Demo](#demo) - [Tech Stack](#tech-stack) - [Architecture Overview](#architecture-overview) - [Project Structure](#project-structure) - [Prerequisites](#prerequisites) - [Installation](#installation) - [Option A — Docker Compose (recommended)](#option-a--docker-compose-recommended) - [Option B — Local development](#option-b--local-development) - [Configuration](#configuration) - [Usage](#usage) - [Available Scripts](#available-scripts) - [API Documentation](#api-documentation) - [Database](#database) - [Deployment Documentation](#deployment-documentation) - [1. Local Deployment](#1-local-deployment) - [2. Demo Deployment](#2-demo-deployment) - [3. White Label Deployment](#3-white-label-deployment) - [Testing](#testing) - [Linting & Formatting](#linting--formatting) - [CI/CD](#cicd) - [Security Notes](#security-notes) - [Contributing](#contributing) - [Roadmap](#roadmap) - [License](#license) - [Acknowledgements](#acknowledgements) - [Contact](#contact) --- ## About **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. ## Features - 📊 **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, Cloro, and DataForSEO for scraping; OpenRouter, Anthropic, OpenAI, Mistral, and Azure AI Foundry for direct LLM calls. - 📄 **Reports** — background report generation over collected data. - ⚙️ **Background job engine** — durable scheduling and retries via `pg-boss` on PostgreSQL. - 🚦 **Admin operations** — inspect and reprioritize the prompt queue, review provider usage and latency, and safely delete brands. - 📅 **Flexible reporting** — share custom date ranges and compare range-average visibility with the latest result. - 🔑 **REST API** — Bearer-authenticated `/api/v1` for brands, competitors, prompts, and reports. - 🎨 **White-label mode** — rebrandable deployment with Auth0 SSO. ## Demo Try the live demo at **[demo.getcito.com](https://demo.getcito.com)** to see prompt tracking and citation analysis in action. ## Tech Stack | Layer | Technology | |-------|------------| | Language | [TypeScript](https://www.typescriptlang.org/) 6, [Node.js](https://nodejs.org/) 24 | | Monorepo | [pnpm](https://pnpm.io/) workspaces + [Turborepo](https://turbo.build/repo) | | Web app | [TanStack Start](https://tanstack.com/start) / [TanStack Router](https://tanstack.com/router) + [React 19](https://react.dev/), [Vite](https://vitejs.dev/), [Nitro](https://nitro.build/) | | UI | [Tailwind CSS 4](https://tailwindcss.com/), [Radix UI](https://www.radix-ui.com/), [Recharts](https://recharts.org/), [Storybook](https://storybook.js.org/) | | Background jobs | [pg-boss](https://github.com/timgit/pg-boss) | | Database | [PostgreSQL 16+](https://www.postgresql.org/) (Docker Compose defaults to 18) + [Drizzle ORM](https://orm.drizzle.team/) / drizzle-kit | | Auth | [Better Auth](https://www.better-auth.com/) + [Auth0](https://auth0.com/) (white-label SSO) | | AI SDKs | [Vercel AI SDK](https://sdk.vercel.ai/), `@ai-sdk/anthropic`, `@ai-sdk/openai`, `@anthropic-ai/sdk` | | Tooling | [Biome](https://biomejs.dev/) (lint/format), [Vitest](https://vitest.dev/), [Playwright](https://playwright.dev/), [Knip](https://knip.dev/), [Changesets](https://github.com/changesets/changesets) | | Delivery | [Docker](https://www.docker.com/) / Docker Compose | ## Architecture Overview 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. ```mermaid flowchart LR User([User / Browser]) -->|HTTP| Web[apps/web
TanStack Start dashboard + /api/v1] Web -->|read/write| DB[(PostgreSQL
Drizzle schema)] Web -->|enqueue jobs| DB Worker[apps/worker
pg-boss jobs] -->|poll queues| DB Worker -->|scrape AI answers| Scrapers[BrightData / Oxylabs
Olostep / DataForSEO] Worker -->|direct LLM calls| LLM[OpenRouter / Anthropic
OpenAI / Mistral] ``` **Prompt tracking lifecycle:** ```mermaid 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 * * * *`). ## Project Structure ```text 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 ``` ## Prerequisites **For Docker deployment:** - [Docker](https://docs.docker.com/get-docker/) with Docker Compose **For local development:** - [Node.js](https://nodejs.org/) **24.x** (enforced via `engines`; use `nvm use 24`) - [pnpm](https://pnpm.io/) **11.10.0** (`corepack enable pnpm`) - [PostgreSQL 16 or newer](https://www.postgresql.org/) (or run it via Docker, which defaults to 18) ## Installation ### Option A — Docker Compose (recommended) Builds and runs the full stack (PostgreSQL, migrations, web, worker, Adminer) from source. ```bash # 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 # Create the environment file and fill in real values (see Configuration) cp .env.example .env # Build and start docker compose build docker compose up -d ``` Services 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. > [!IMPORTANT] > Upgrading an existing Compose installation does not upgrade its database files. Set `POSTGRES_IMAGE_TAG=16-alpine` in `.env` before pulling this release. Move to PostgreSQL 18 only after dumping the PostgreSQL 16 database and restoring it into a fresh PostgreSQL 18 volume. ### Option B — Local development ```bash # Install dependencies corepack enable pnpm pnpm install # Env file is read from the repo root AND apps/web/.env (see Configuration) # Copy the committed templates, then replace their placeholder values. cp .env.example .env cp apps/web/.env.example apps/web/.env # Run all dev servers via Turborepo pnpm dev ``` Dev port: **web** → http://localhost:3000. The **worker** loads env from `apps/web/.env` (via `--env-file`) and is only needed for background job processing. ## Configuration 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] > Start from `.env.example` at the repo root and `apps/web/.env.example`. Because the worker reads `apps/web/.env` during `pnpm dev`, copy the app template there as shown above. **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. ### Core | 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`. | | `VITE_APP_TIMEZONE` | client | optional | IANA timezone used across charts and dates. Falls back to the runtime timezone; `Asia/Kolkata` is an example deployment value. | | `VITE_APP_LOCALE` | client | optional | BCP 47 locale used for date/number formatting. Falls back to the runtime locale; `en-IN` is an example deployment value. | | `ONBOARDING_LLM_TARGET` | server | optional | Explicit `model:provider` used for structured onboarding research. | | `DISABLE_TELEMETRY` | server | optional | Set to any value to disable telemetry. | | `ENVIRONMENT` | server | optional | Environment name reported to Sentry. | ### Scraper providers (required when referenced by `SCRAPE_TARGETS`) | 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. | | `CLORO_API_KEY` | cloro | Cloro API key. Cloro targets must use `:online`. | ### Direct LLM providers (required when referenced by `SCRAPE_TARGETS`) | 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. | | `AZURE_FOUNDRY_API_KEY` / `AZURE_FOUNDRY_BASE_URL` | azure-foundry-api | Azure AI Foundry key and endpoint URL. | ### White-label (Auth0) | 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. | ### Optional integrations | 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. | ## Usage ```bash 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 Biome ``` **Production (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`. ## Available Scripts **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` | ## API Documentation The web app exposes a REST API under **`/api/v1`**. The full contract is defined in [`packages/api-spec/src/openapi.json`](packages/api-spec/src/openapi.json) (OpenAPI 3). **Authentication:** Bearer token — `Authorization: Bearer `. | 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: ```bash curl -H "Authorization: Bearer $GETCITO_TOKEN" \ http://localhost:3001/api/v1/brands ``` ## Database - **PostgreSQL 16+**, accessed via **Drizzle ORM** (schema in `packages/lib/src/db`). Docker Compose defaults new installations to PostgreSQL 18. - Migrations are managed with **drizzle-kit**. Run from `packages/lib`: ```bash DATABASE_URL=postgres://postgres:postgres@localhost:5432/getcito \ npx drizzle-kit migrate ``` Under 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. ## Deployment Documentation 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. ### 1. Local Deployment The local deployment builds and runs the full stack (PostgreSQL, automatic migrations, web dashboard, background worker, and Adminer DB UI) from source. **Prerequisites:** - [Docker](https://docs.docker.com/get-docker/) with Docker Compose plugin. - A clone of this repository. **Step-by-step Setup:** 1. **Configure Environment Variables** Copy the committed template to `.env`, then fill in your actual values. Ensure `DEPLOYMENT_MODE` is set to `local`: ```bash cp .env.example .env ``` *Required variables include: `DATABASE_URL`, `DEPLOYMENT_MODE`, `BETTER_AUTH_SECRET`, and any required `SCRAPE_TARGETS` API keys.* 2. **Build Docker Images** ```bash docker compose build ``` 3. **Start the Stack** ```bash docker compose up -d ``` **What happens at startup:** ```mermaid 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` ### 2. Demo Deployment 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_EMAIL` and `VITE_DEMO_PASSWORD` from your environment variables to pre-fill the login form for test users. - Relies on the same standard infrastructure (PostgreSQL, web, worker). **Setup:** 1. Configure your `.env` file with `DEPLOYMENT_MODE=demo` and `VITE_DEPLOYMENT_MODE=demo`. 2. Add your demo credentials: ```env VITE_DEMO_EMAIL=team@getcito.com VITE_DEMO_PASSWORD=your_demo_password ``` 3. Deploy using the standard Compose commands: ```bash 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).* ### 3. White Label Deployment 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. - Includes a Docker Compose file with configurable host ports (Web `3000`, DB `5432`, Adminer `8080` by default). **Setup:** 1. **Review the Compose Template** The repository includes [`docker-compose.whitelabel.yml`](docker-compose.whitelabel.yml). It contains no secrets; all credentials and branding come from `.env`.
Click to view docker-compose.whitelabel.yml template ```yaml services: postgres: image: postgres:${POSTGRES_IMAGE_TAG:-18-alpine} ports: - "127.0.0.1:${POSTGRES_PORT:-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: - "${ADMINER_PORT:-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} VITE_APP_TIMEZONE: ${VITE_APP_TIMEZONE:-} VITE_APP_LOCALE: ${VITE_APP_LOCALE:-} env_file: - .env ports: - "${WEB_PORT:-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: ```
2. **Configure Environment Variables** Copy `.env.whitelabel.example` to `.env`, then provide the required Auth0, branding, and provider values. Existing PostgreSQL 16 deployments must keep `POSTGRES_IMAGE_TAG=16-alpine` until completing a dump/restore migration. ```env POSTGRES_IMAGE_TAG=18-alpine 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="..." VITE_APP_TIMEZONE=Asia/Kolkata VITE_APP_LOCALE=en-IN ``` 3. **Build and Deploy** *Important: Client variables (`VITE_*`) are read during the build step and baked into the image.* ```bash docker compose -f docker-compose.whitelabel.yml build docker compose -f docker-compose.whitelabel.yml up -d ``` ## Testing ```bash 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-local ``` E2E tests live in `e2e/` and are separate from unit tests. ## Linting & Formatting Both linting and formatting are handled by **[Biome](https://biomejs.dev/)** (config in `biome.json`). ```bash pnpm lint # turbo lint (biome lint per package) pnpm format # biome format --write . pnpm knip # detect unused files, dependencies, and exports ``` > [!NOTE] > Biome reports Tailwind `@apply` directive warnings in CSS files — these are pre-existing and expected. ## CI/CD Releases are managed on `master` by the GitHub Actions release workflow and **Changesets** (`pnpm changeset` → `pnpm version-packages` → GitHub Release). ## Security Notes - **Never commit secrets.** API keys, `.env` contents, and auth secrets must stay out of version control. Generate `BETTER_AUTH_SECRET` as random hex per deployment. - Docker runtime images run as **non-root** users. - `pnpm-workspace.yaml` pins security-patched transitive dependency versions (see the `overrides` block) and enforces a minimum release age / no-downgrade trust policy for new packages. - Report vulnerabilities privately per [`SECURITY.md`](SECURITY.md) — email [team@getcito.com](mailto:team@getcito.com), do not open a public issue. ## Contributing Contributions are welcome. For minor fixes, open a PR directly; for larger changes, open an issue first. See [`CONTRIBUTING.md`](CONTRIBUTING.md) and [`AGENTS.md`](AGENTS.md). - **Branching / PRs:** open PRs from your own fork/account against `master`. - **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`](.github/contributors.txt) — see [`CLA.md`](CLA.md). - **Code of Conduct:** see [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md). ## Roadmap Tracked on the [GitHub Project board](https://github.com/orgs/ai-search-guru/projects/3/views/1) and in [Issues](https://github.com/ai-search-guru/getcito-worlds-first-open-source-aio-aeo-or-geo-tool/issues). ## License Released under the [MIT License](LICENSE.md). GetCito v9 is a fork of [Elmo](https://github.com/elmohq/elmo), © 2026 Blue Whale Software, LLC, used under the MIT License. Modifications and additions in this repository are © 2026 GetCito. Both copyright notices are retained in [LICENSE.md](LICENSE.md). Contributions are covered by the [Contributor License Agreement](CLA.md). ## Acknowledgements Based on [Elmo](https://github.com/elmohq/elmo) and built with TanStack Start, Drizzle ORM, Better Auth, pg-boss, Fumadocs, Biome, and Turborepo, and integrates BrightData, Oxylabs, Olostep, Cloro, DataForSEO, Azure AI Foundry, OpenRouter, Anthropic, OpenAI, and Mistral. ## Contact - 📧 [team@getcito.com](mailto:team@getcito.com) - 📞 India: +91-96505-10773 · US: +1-623-223-7423