# Security & data handling

What Repowise processes, what it persists, what leaves your machine under each deployment mode, and how to reduce that to zero.

This page covers self-hosted Repowise, for the person who has to sign
off before it touches source code. For the hosted platform, see
[Hosted security and data handling](https://docs.repowise.dev/hosted/data-handling).

Short version: Repowise is self-hosted by default. Your source is read
from disk, parsed in memory, and never persisted. The only content that
leaves your infrastructure is what you send to your own LLM provider
under your own key, and even that is optional. Anonymous CLI telemetry is
the one outbound channel we add, it contains no code, paths, or repo
names, and it has four independent off switches.

> **Note**
>
> Looking for the security *product* (CVE scanning, secret detection,
> SBOM/VEX, compliance reports)? That is a different page:
> [Security suite](https://docs.repowise.dev/hosted/security). The free pattern scan in the
> open-source engine is [Security signals](https://docs.repowise.dev/intelligence/security-signals).
> This page is about data handling and compliance posture.

## What is processed vs what is persisted

This is the distinction most security reviews turn on. Repowise reads a
lot more than it keeps.

| Artifact | Processed | Persisted | Where |
|---|---|---|---|
| Raw source code | Yes, read from disk and parsed into an AST in memory | **No** | transient only |
| Dependency graph (NetworkX) | Yes | Yes | local index (`.repowise/`) |
| Embeddings (LanceDB) | Yes | Yes, as non-reversible vectors | local index |
| Generated wiki pages | Yes | Yes | local index |
| Git metadata (commits, authors, churn, co-change) | Yes | Yes | local index |
| Architectural decision records | Yes | Yes | local index |
| Code-health scores and findings | Yes | Yes | local index |

Raw source is processed transiently and never persisted. What survives an
index is a structural and statistical description of the codebase plus
the documentation generated from it.

**On embeddings.** LanceDB stores float vectors produced by the
configured embedding model. They are not encrypted source and they are
not reversible back into the original text. They are still derived from
your code, so treat the index directory with the same access controls as
the repository itself.

**Where the index lives.** In the open-source distribution, everything
above sits in the `.repowise/` directory inside the repository, on the
machine that ran `repowise init`. Nothing is uploaded. A commercial
on-prem install swaps the local stores for Postgres (metadata) and
LanceDB or pgvector (embeddings) inside your own network.

## LLM usage, BYOK, and the layers that need no model

Repowise splits cleanly into layers that call an LLM and layers that do
not.

**Zero-LLM layers.** Graph, git, dead code,
[code health](https://docs.repowise.dev/intelligence/code-health), change risk, the security
pattern scan, and the
[deterministic wiki](https://docs.repowise.dev/intelligence/deterministic-wiki) are pure local
computation over tree-sitter and git data. `repowise init --no-prose`
builds all of them, wiki pages included, with **no outbound LLM calls at
all**. The health pass in particular is deterministic Python: no model,
no network.

An `init` run with no provider configured takes this path by default, so a
machine that has never seen an API key still produces a complete,
queryable wiki.

**LLM layers.** Wiki and documentation generation, chat, decision
extraction, and the opt-in refactoring code-gen call a model. These are
the only paths that can send code-derived content off the machine.

**BYOK.** You bring your own key: Anthropic, OpenAI, Google Gemini,
OpenRouter, DeepSeek, Kimi or Eden AI; Azure OpenAI in your own tenant
through LiteLLM; a local Ollama runtime; or an agent CLI you already sign in
to (Claude Code, Codex, OpenCode, Copilot). Calls go directly from your
infrastructure to the provider you configured. We never see your LLM
traffic, and we never proxy it. Zero data retention applies through your
provider's own policy (Anthropic's API zero-retention terms, your Azure
tenant's data-handling terms, and so on), which means the retention
question is settled by a contract you already hold instead of by us.

**Per-repository provider choice.** The provider is configured per repo
(see [Configuration](https://docs.repowise.dev/installation/configuration) and
[Environment variables](https://docs.repowise.dev/installation/env-vars)), so a sensitive
repository can run fully offline while a lower-sensitivity tooling repo
uses a hosted model.

**Fully offline.** Ollama plus a local embedding model means **zero
external API calls**: no LLM provider, no embedding provider, nothing
outbound from the indexing path. Combined with telemetry disabled,
Repowise makes no network calls whatsoever.

## Telemetry, stated precisely

The CLI reports anonymous, opt-out usage telemetry so we can prioritize
work. It is anonymous and opt-out, not absent, and we prefer that you
read the exact fields than take a slogan.

There are two event types: `command_run` (once per CLI invocation) and
`mcp_tool_call` (once per [MCP](https://docs.repowise.dev/mcp/overview) tool call).

**What is collected:** an anonymous envelope (`anon_id`, `session_id`,
`cli_version`, `os`, `arch`, `python_version` at major.minor only,
`is_ci`), plus per-event properties: the command name, a known subcommand
name, flag **names** only, a status enum, an exception **class name**,
and a duration. For `init` and `update`, coarse buckets describe the
shape of the run (file-count bucket, top language, docs mode on/off,
provider and model, embedder, pages bucket). MCP tool calls report the
tool name, status, duration, and coarse enums (confidence, retrieval
quality, results bucket, index-behind flag).

**What is never collected:** source code or file contents, file paths or
directory names, repository names, package names, symbol or function
names, generated documentation text, flag values, environment-variable
values, API keys or credentials, error messages or stack traces, IP
addresses, usernames, hostnames, email, or anything personally
identifiable.

**`anon_id` is a random UUID** stored in `~/.repowise/platform.json`. It
is not derived from a hostname, username, or machine identifier, so it
cannot be reversed to a person. Delete the file and a new, unrelated id
is generated.

**Retention:** events are kept for 90 days, then deleted. Only aggregate
figures are used or shared.

### Verify before you trust us

Print the exact payload without sending anything:

```bash
REPOWISE_TELEMETRY_DEBUG=1 repowise status
```

### Four independent off switches

Telemetry is on by default, with a one-time notice on first run. Any one of
these turns it off.

```bash
repowise telemetry disable        # persisted to ~/.repowise/platform.json
export REPOWISE_TELEMETRY_DISABLED=1
export DO_NOT_TRACK=1             # the cross-tool standard
export REPOWISE_OFFLINE=1         # also skips the PyPI release check
```

Check the current state with `repowise telemetry status` (see
[Other commands](https://docs.repowise.dev/cli/other)). Running fully offline also means
nothing is sent.

## Self-hosted vs hosted: where the boundary sits

| | Self-hosted (OSS, `pip install repowise`) | Self-hosted commercial (on-prem) | Hosted platform |
|---|---|---|---|
| Where source is read | your machine | your VPC | the hosted indexer |
| Where the index lives | `.repowise/` on your machine | Postgres + LanceDB/pgvector in your network | Repowise-operated infrastructure |
| Who calls the LLM | you, with your key | you, with your key | the platform |
| Outbound from your network | anonymous telemetry (disableable), your LLM provider | same, plus configured integrations | not applicable, you are sending code to the platform |
| Commercial security features (CVE triage, SBOM/VEX, hosted secret detection, compliance reports, audit trail) | not included (the local pattern scan and `repowise security scan --history` are open source) | commercial layer | GA today |

The honest framing: several commercial security capabilities are GA on
the hosted platform first. If your requirement is on-prem *and*
CVE-aware dependency analysis, SBOM/VEX, or compliance reporting, that
sequencing is a scoping conversation, not a checkbox. See
[Hosted vs self-hosted](https://docs.repowise.dev/hosted/vs-self-hosted) for the feature
split and [Teams](https://docs.repowise.dev/hosted/teams) for the workspace tier.

On the hosted secret-detection feature, only a fingerprint and a redacted
preview are stored, never the secret value.

## Authentication on a shared server

A local `repowise serve` on loopback needs no credential. A shared
server resolves every caller to a principal, on every surface that
accepts one.

| Surface | Credential |
|---|---|
| REST under `/api` | `REPOWISE_API_KEY` as `Authorization: Bearer <key>`, compared in constant time, or a scoped token |
| MCP over streamable HTTP and SSE | The same credentials. A missing bearer gets `401` with `WWW-Authenticate` pointing at an RFC 9728 protected-resource document under `/.well-known/oauth-protected-resource` |
| Webhooks (GitHub, GitLab, Gitea, Forgejo) | The forge's own signature or token, checked per request |
| Chat | Runs its tools as the principal of the request |

**Scoped tokens.** List them in `~/.repowise/tokens.yaml` (or the file
`REPOWISE_TOKENS_FILE` names). Each entry stores the SHA-256 digest of
the token, never the token, and can carry `repos` and `tools` to limit
which repositories and MCP tools it reaches, and an `expires_at` time.
A token limited to tools is refused on every REST route except chat,
which runs only the tools in its scope.

**A network-reachable server refuses without a key.** With
`REPOWISE_API_KEY` unset, a loopback caller is allowed and every other
caller gets `403`. "Local" is decided from the peer address, and
forwarding headers are not trusted, so a same-host reverse proxy still
needs the key. Set `REPOWISE_PUBLIC_URL` when serving MCP behind a proxy.
With no key and no tokens file, a local server behaves as a plain local
tool.

**Plugin seams for your identity provider and directory.**
Authentication (`authenticator`), authorisation (`authorizer`), the team
directory (`team-directory`), change metadata (`change-metadata`) and
event delivery (`event-sink`) are named seams. An installed package
fills one through a `repowise.plugins` entry point, without changes to
the open-source code, and `repowise plugins` shows what loaded, what
failed and which implementation each seam uses. A repository's
`.repowise/.env` cannot select plugins. Managed SSO and SCIM are part of
the [commercial layer](https://docs.repowise.dev/enterprise#what-the-commercial-licence-adds),
set up with your identity provider during deployment.

## AI attribution and people

Repowise reads AI provenance as evidence about changes, with its
coverage stated, and never as a measure of people.

- By default only public markers already in git history are read.
- Commits are labelled agent, human-attested or unknown. An AI share is
  shown beside the share of commits whose source is known, and withheld
  below 30% coverage.
- Edit capture for Claude Code, Codex and Cursor is opt-in per agent.
  It stores content hashes of written lines in `.repowise/attribution/`,
  never prompts, code or identities. `repowise init --yes` never turns it
  on, and `DO_NOT_TRACK` keeps it off.
- `attribution.enabled: false` stores and shows nothing about AI.
- Reports count owners and reviewers and never rank or name individuals.

Details: [AI attribution](https://docs.repowise.dev/intelligence/ai-attribution).

## On-prem and air-gapped topology

A self-hosted commercial install is a set of containers on your
infrastructure. The reference topology is Kubernetes; the same containers
run on Nomad or plain Docker. The open-source Docker images are a
full-stack image (API plus Web UI) and a lean MCP-only image that speaks
JSON-RPC over stdio, carries no Web UI, and exposes no ports.

Components:

1. **API server** (FastAPI), **indexer workers**, and **dashboard** (Next.js).
2. **Postgres** for metadata, **LanceDB** (or pgvector) for embeddings, and an in-memory NetworkX graph.
3. **Optional Ollama container** for fully-offline LLM use.
4. **Webhook receivers** for GitHub Enterprise, GitLab self-managed, or Azure DevOps, configured per tracked repository.
5. **SSO** through your existing Entra ID / Okta tenant over OIDC, set up with your deployment, and **outbound integrations** configured with signed service tokens held in the Repowise secret store.

**Air-gapped.** All services run inside your VPC or air-gapped network.
The LLM provider and the git server sit inside the same boundary. No
outbound connectivity is required in air-gapped mode. The air-gapped
install is set up with you during deployment: Python wheels, the embedding
model and container images are prefetched outside the boundary and
carried in, with an optional Ollama runtime for prose.

**Indexing cost profile, for capacity planning.** The graph, git,
dead-code, and code-health layers build with zero LLM calls, and so does
the wiki when you run keyless (`repowise init --yes --no-prose`,
see the [Quickstart](https://docs.repowise.dev/getting-started/quickstart)). One-time
model-written subsystem prose scales with repo size and can run in the
background. Incremental updates
after each commit complete in under 30 seconds.

## Compliance posture

> **Note**
>
> **Repowise never handles your code or data.** Self-hosted Repowise and
> Enterprise run entirely inside your perimeter, under your own controls and
> certifications. No Repowise-operated system receives, stores or processes
> your source, the index or any analysis, so a vendor SOC 2 report is not the
> gate for this deployment. The optional prose call goes to your own provider
> contract, and anonymous CLI telemetry carries no code and switches off with
> `DO_NOT_TRACK=1`. For the hosted platform, see
> [Hosted security and data handling](https://docs.repowise.dev/hosted/data-handling#certifications-and-single-sign-on).

Built on top of that:

- **PCI-DSS 4.0 and SOC 2 control-coverage reports** are GA on the hosted
  platform (Teams+). They are derived from live security findings with
  per-control evidence drill-ins and JSON / Markdown export. They are
  framed in-product and in every export as coverage signals, not an audit
  and not a certification. They certify nothing. Controls that automated
  findings cannot evidence are marked for manual attestation instead of
  silently passed.
- **ISO 27001 Annex A** and **GDPR / data-residency mappings** are on the
  extended roadmap, not shipped.
- **SBOM (CycloneDX 1.6) with per-dependency license detection, plus VEX
  export** is GA on the hosted platform (Pro+). SPDX and cross-format
  conversion are roadmap.
- **Audit trail** covering the hosted security surface (scans,
  vulnerability and secret views, SBOM/VEX exports, compliance views,
  finding-status changes, and MCP reads by AI agents) is insert-only with
  user, IP, and timestamp, exportable to JSON/CSV, with an opt-in
  signed-webhook stream. Coverage beyond the security surface is in
  development.
- **Audit rights.** The commercial license grants you the right to audit
  Repowise's own security and compliance practices.
- **IP indemnification and a defensive patent grant** are GA under the
  commercial license.

**Licensing, since it usually shares a review cycle:** the core engine is
AGPL-3.0, free for individuals, teams, and companies using it internally.
A commercial license removes the AGPL obligations for embedding Repowise
in your own product and adds the layer above. Details on
[Licensing](https://docs.repowise.dev/licensing).

## Questions your security team will ask

| Question | Answer |
|---|---|
| Does our source code leave our infrastructure? | Not in the self-hosted distribution. Code is read from disk and parsed in memory. The only path that can send code-derived content out is the LLM call for documentation, chat, and decision extraction, and that goes to the provider you configured with your key. Indexing with no provider (`--no-prose`) and offline mode remove it entirely, and you still get a full wiki. |
| Is raw source stored anywhere? | No. Raw source is processed transiently and never persisted. The index holds the graph, embeddings, wiki pages, git metadata, decisions, and health data. |
| Can the embeddings be reversed back into our code? | They are non-reversible vectors, not encrypted source. They are still derived from your code, so protect the index directory like the repo. |
| Can we run with no network access at all? | Yes. Ollama plus a local embedding model gives zero external API calls; disable telemetry and the process makes no outbound requests. In air-gapped mode the LLM provider and git server sit inside your boundary. |
| Do you see our LLM API traffic? | No. BYOK calls go directly from your infrastructure to your provider. We do not proxy them. |
| What is your data retention on LLM prompts? | Governed by your own provider contract (for example Anthropic's API zero-retention policy), not by us. |
| What telemetry is sent, and can we turn it off? | Anonymous command names and coarse environment only, with no code, paths, repo names, flag values, IPs, or identifiers. It is on by default with a one-time notice. Disable via `repowise telemetry disable`, `DO_NOT_TRACK=1`, `REPOWISE_TELEMETRY_DISABLED=1`, or `REPOWISE_OFFLINE=1`. Verify the exact payload with `REPOWISE_TELEMETRY_DEBUG=1`. |
| How long is telemetry retained? | 90 days, then deleted. Only aggregates are used. |
| Can telemetry be tied back to a user or a repo? | No. `anon_id` is a random UUID in `~/.repowise/platform.json`, not derived from any machine or user identifier, and no repo-identifying field is sent. |
| Do we need your SOC 2 or ISO 27001 report? | Not for self-hosted or Enterprise. Repowise never handles your code or data: the deployment runs entirely inside your perimeter, under your own controls and certifications. For Repowise Hosted, see [Hosted security and data handling](https://docs.repowise.dev/hosted/data-handling#certifications-and-single-sign-on). Separately, the hosted Security suite produces PCI-DSS 4.0 and SOC 2 **control-coverage reports** for your code, labeled as coverage signals, not an audit. ISO 27001 Annex A mapping is roadmap. |
| Can we deploy on-prem or air-gapped? | Yes. Containerized API, indexer workers, and dashboard, backed by Postgres and LanceDB/pgvector, with optional Ollama. No outbound connectivity is required in air-gapped mode, and the air-gapped install is set up with you during deployment. |
| Do you support SSO and SCIM? | Yes, delivered with a commercial deployment and scoped with you during the pilot. OIDC single sign-on (Entra ID, Okta, Auth0, Google Workspace), SAML 2.0 and SCIM provisioning are set up with your identity provider and scoped during the pilot, along with role-based access control and multi-tenant organisations. |
| Is there an audit log? | On the hosted platform, insert-only, covering the security surface, with user, IP, and timestamp, JSON/CSV export, and an opt-in signed-webhook stream. Broader coverage is in development. |
| Do you store secrets you find? | Only a fingerprint and a redacted preview. Never the secret value. |
| What does the MCP server expose to an AI agent? | Read-only query tools over the local index. The lean MCP container exposes no ports and speaks stdio only. |
| How is a shared server authenticated? | REST, MCP over HTTP (streamable HTTP and SSE) and webhooks resolve every caller to a principal. Scoped tokens are stored as SHA-256 hashes, can expire, and can be limited to repositories and MCP tools. MCP HTTP serves an RFC 9728 protected-resource document. A server reachable from the network refuses requests until a key is set. See [above](https://docs.repowise.dev/security#authentication-on-a-shared-server). |
| Can we connect our own identity provider or directory? | Authentication, authorisation, team directory, change metadata and event delivery are named plugin seams. An installed plugin fills them without changes to the open-source code. Managed SSO and SCIM are part of the commercial layer. |
| Does AI attribution monitor developers? | No. By default only public markers already in git history are read. Edit capture is opt-in per agent and stores line hashes, never prompts, code or identities. Reports count owners and reviewers and never rank or name them. |
| What license are we accepting? | AGPL-3.0 for the OSS engine, free for internal use. A commercial license is required to embed Repowise in a product without AGPL obligations. |
| Who do we contact for a security review? | [security@repowise.dev](mailto:security@repowise.dev). Commercial scoping: [hello@repowise.dev](mailto:hello@repowise.dev). |

## Reporting a vulnerability

Send it to [security@repowise.dev](mailto:security@repowise.dev). Please
include reproduction steps and the affected version
(`repowise --version`).
