Telemetry and privacy
Release builds of the stacks CLI can send usage events to the API after showing a first-run notice. The notice run sends no event and creates no install ID. Source builds and CI do not send telemetry.
What is collected
Events use random install and telemetry project IDs. They include the CLI version, operating system, Node major version, detected agent, and event time. Scaffold and add events describe official stack and fragment IDs, fragments omitted with --without, preset, target agents, handoff type, and the --source choice when given. They include the entry origin and counts of non-official entries. A composition fingerprint is sent only when every entry is official.
Show events name the entry type and official ID. Error events name the command and a fixed error code. Verify events report scope, outcome, tamper check, step names, phases, layers, statuses, failure kinds and bucketed durations, plus the changed-step count, time since scaffold, time to first passing test, and the number of questions the agent recorded under ## Asked in NOTES.md. Non-official entry names and unrecognized step names are sent as custom.
A trusted reverse proxy supplies the client IP address in a configured header. The server hashes its IPv4 /24 or IPv6 /64 prefix with one secret key and stores the hash, never the IP address. Events from one network can be linked to each other for up to 30 days, then the link is deleted. One key preserves network counting across the published 28-day window. The proxy can also supply an ASN (autonomous system number), which identifies a network provider and is stored with the event.
What is never collected
Telemetry events do not send paths, directory or project names, git remotes, usernames, hostnames, environment variable values, decision answers, brief or NOTES text, command lines, output tails, custom entry IDs, or file contents except the allowlisted dependency names and major versions described below. Offline runs send no telemetry. The server does not store or log raw IP addresses.
Retention
Network hashes are removed from event rows after 30 days. Raw event rows are pruned after 90 days, once their private archive has been checked against the database. Events older than seven days go into that private archive without network hashes. If the archive is missing or does not match, the rows stay until a later successful maintenance run. Project records and published aggregates can remain after raw events are pruned; aggregates are final after 90 days.
Opting out
Run stacks telemetry disable to turn telemetry off and clear queued events and local telemetry identity. You can also set DO_NOT_TRACK or STACKS_TELEMETRY_DISABLED to a nonempty value other than 0 or false, or set "telemetry": false in the CLI config. These standing opt-outs clear pending events. For one command, use --no-telemetry. --offline or STACKS_OFFLINE=1 also stops sending for that invocation; pending events can remain for a later enabled run.
Event schema
See the published telemetry event v1 JSON Schema. Each released schema version has its own document at /v1/schemas/telemetry-event/v<N>.json.
Dependencies
After a second notice, the CLI can read dependency names from npm, PyPI, Go and crates manifests. It gets npm major versions from installed package metadata under node_modules, PyPI and crates majors from lock files, and Go majors from go.mod. It executes nothing to collect them. Only names on a signed allowlist can be attached to a green verify event, at most once per project every seven days. If the installed major cannot be found, it is reported as unknown.
The current allowlisted names are:
- crates: anyhow
- crates: serde
- crates: serde_json
- crates: specta
- crates: specta-typescript
- crates: tauri
- crates: tauri-build
- crates: tauri-specta
- crates: tempfile
- crates: thiserror
- crates: tokio
- crates: tracing
- crates: tracing-subscriber
- go: github.com/jackc/pgx
- go: github.com/pressly/goose
- go: github.com/spf13/cobra
- go: github.com/spf13/viper
- go: github.com/stretchr/testify
- npm: @ai-sdk/react
- npm: @astrojs/check
- npm: @astrojs/mdx
- npm: @astrojs/react
- npm: @astrojs/rss
- npm: @astrojs/sitemap
- npm: @biomejs/biome
- npm: @clerk/expo
- npm: @clerk/hono
- npm: @cloudflare/vite-plugin
- npm: @cloudflare/vitest-plugin
- npm: @effect/ai-openrouter
- npm: @effect/platform-bun
- npm: @effect/platform-node
- npm: @effect/sql-pg
- npm: @effect/vitest
- npm: @eslint/js
- npm: @faker-js/faker
- npm: @fontsource-variable/inter
- npm: @fontsource-variable/jetbrains-mono
- npm: @hono/zod-openapi
- npm: @openrouter/ai-sdk-provider
- npm: @openrouter/sdk
- npm: @opentui/core
- npm: @opentui/react
- npm: @playwright/test
- npm: @react-router/dev
- npm: @react-router/node
- npm: @react-router/serve
- npm: @resvg/resvg-js
- npm: @scalar/hono-api-reference
- npm: @sveltejs/adapter-node
- npm: @sveltejs/kit
- npm: @sveltejs/vite-plugin-svelte
- npm: @tailwindcss/postcss
- npm: @tailwindcss/typography
- npm: @tailwindcss/vite
- npm: @tanstack/react-query
- npm: @tauri-apps/api
- npm: @tauri-apps/cli
- npm: @testing-library/dom
- npm: @testing-library/jest-dom
- npm: @testing-library/react
- npm: @testing-library/react-native
- npm: @testing-library/svelte
- npm: @testing-library/user-event
- npm: @types/bun
- npm: @types/jest
- npm: @types/node
- npm: @types/pg
- npm: @types/react
- npm: @types/react-dom
- npm: @vitejs/plugin-react
- npm: @vitest/coverage-v8
- npm: @xterm/headless
- npm: ai
- npm: astro
- npm: class-variance-authority
- npm: clsx
- npm: drizzle-kit
- npm: drizzle-orm
- npm: effect
- npm: eslint
- npm: eslint-config-next
- npm: eslint-config-prettier
- npm: eslint-plugin-jsx-a11y
- npm: eslint-plugin-react-hooks
- npm: eslint-plugin-svelte
- npm: expo
- npm: expo-constants
- npm: expo-linking
- npm: expo-router
- npm: expo-secure-store
- npm: expo-status-bar
- npm: globals
- npm: happy-dom
- npm: hono
- npm: isbot
- npm: jest
- npm: jest-expo
- npm: jsdom
- npm: lucide-react
- npm: msw
- npm: nativewind
- npm: next
- npm: orval
- npm: pg
- npm: postcss
- npm: postgres
- npm: posthog-js
- npm: prettier
- npm: prettier-plugin-astro
- npm: prettier-plugin-svelte
- npm: react
- npm: react-dom
- npm: react-native
- npm: react-native-css
- npm: react-native-reanimated
- npm: react-native-safe-area-context
- npm: react-native-screens
- npm: react-native-web
- npm: react-native-worklets
- npm: react-router
- npm: satori
- npm: server-only
- npm: svelte
- npm: svelte-check
- npm: tailwind-merge
- npm: tailwindcss
- npm: typescript
- npm: typescript-eslint
- npm: vite
- npm: vitest
- npm: wrangler
- npm: yaml
- npm: zod
- npm: zustand
- pypi: alembic
- pypi: asgi-lifespan
- pypi: asyncpg
- pypi: fastapi
- pypi: greenlet
- pypi: httpx
- pypi: mypy
- pypi: pydantic
- pypi: pydantic-settings
- pypi: pytest
- pypi: pytest-asyncio
- pypi: pytest-cov
- pypi: pytest-randomly
- pypi: pytest-xdist
- pypi: ruff
- pypi: sqlalchemy
- pypi: structlog
- pypi: uvicorn
Search
stacks search ranks local entries and, when an API endpoint is configured and the call is enabled, sends the search text as the q parameter in a GET /v1/search URL, along with the result limit and optional kind. The API keeps nothing derived from the search text: its request log records the path without the query string, and no table or counter is keyed by the text.
The search text is still part of the URL. A reverse proxy or other network edge, including a proxy you use, may log the full URL. Use --local, --offline or DO_NOT_TRACK to turn off the API call. The CLI also uses local results when no endpoint is configured or the API is unavailable.