> ## Documentation Index
> Fetch the complete documentation index at: https://docs.navigara.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Security Best Practices

> Harden a Navigara deployment: network boundaries, identity, secrets, credentials, data protection, and audit logging.

This guide tells you how to deploy and operate Navigara securely. It is written for the engineer who owns the deployment, and it mirrors the controls Navigara applies to its own platform.

Each section covers one part of the system, so you can go straight to the part you are securing. Start with the [hardening checklist](#hardening-checklist) and follow the links.

<Info>
  This guide covers your responsibilities as the operator. For what each deployment model puts in your infrastructure, see [Deployment overview](/deployment/overview).
</Info>

## How we secure our own platform

Navigara runs an information security management system designed to conform to ISO/IEC 27001:2022, and aligned to SOC 2 and DORA. The recommendations on this page come from the same internal policies. In summary, our policies require:

* **Encryption everywhere.** TLS 1.2 or higher for data in transit, AES-256 or equivalent at rest — including backups. MD5, SHA-1, and DES are not permitted.
* **Identity as the perimeter.** Unique named identities, single sign-on, and multi-factor authentication on all administrative access.
* **Least privilege, with evidence.** Access granted by role and business need, reviewed at least quarterly, and revoked the same business day when someone leaves.
* **Secrets never in code.** Credentials live in a secret manager and are injected at run time. A committed secret is handled as a security incident.
* **Nothing reaches production unreviewed.** Protected branches, an independent reviewer on every change, and automated deployment from the default branch only.
* **Continuous scanning with fixed clocks.** Static analysis, dependency scanning, and image scanning on every change, with remediation targets of 15 days for critical findings, 30 for high, 60 for medium, and 90 for low. An independent penetration test at least annually.
* **Tested resilience.** Encrypted backups with validated restores, infrastructure defined as code, and recovery testing at least annually.

<Note>
  Navigara is not ISO 27001 certified. The management system is designed to conform to the standard. Contact your account representative for our current policy set, security documentation, or a data processing agreement.
</Note>

## Hardening checklist

Work through this list before you put a deployment into production. "Regulated" marks controls we consider mandatory in banking, insurance, and other regulated environments.

| Control                                                                   | Applies to | Section                                                                 |
| ------------------------------------------------------------------------- | ---------- | ----------------------------------------------------------------------- |
| Keep the collector on a private network with no inbound ports             | All        | [Collector hardening](#collector-hardening)                             |
| Terminate TLS at the reverse proxy and keep the security headers          | All        | [Network boundaries](#network-boundaries)                               |
| Block `/api/metrics` and debug routes at the proxy                        | All        | [Network boundaries](#network-boundaries)                               |
| Restrict the root login endpoint to administrator source ranges           | Regulated  | [Root admin access](#root-admin-access)                                 |
| Decide whether Navigara support access is permitted, and enforce it       | Regulated  | [Navigara support access](#navigara-support-access)                     |
| Set `TRUSTED_PROXY_CIDRS` when you run behind a load balancer             | All        | [Network boundaries](#network-boundaries)                               |
| Use single sign-on and leave password authentication off                  | All        | [Identity and access](#identity-and-access)                             |
| Store the root private key in a vault or hardware token                   | All        | [Root admin access](#root-admin-access)                                 |
| Attach the narrowest available role to every API token, and set an expiry | All        | [API tokens](#api-tokens)                                               |
| Hold every secret in a secret manager, injected at run time               | All        | [Secrets management](#secrets-management)                               |
| Generate a unique random `COLLECTOR_API_KEY`                              | All        | [Secrets management](#secrets-management)                               |
| Use the GitHub App or end-to-end encrypted tokens for Git access          | All        | [Git and issue-tracker credentials](#git-and-issue-tracker-credentials) |
| Grant provider tokens the minimum scopes, read-only where possible        | All        | [Git and issue-tracker credentials](#git-and-issue-tracker-credentials) |
| Encrypt the database at rest and require TLS to it                        | All        | [Database hardening](#database-hardening)                               |
| Run the application as a least-privilege database user                    | All        | [Database hardening](#database-hardening)                               |
| Encrypt the collector work directory at rest                              | Regulated  | [Collector hardening](#collector-hardening)                             |
| Choose an LLM endpoint that matches your data residency rules             | Regulated  | [What leaves your network](#what-leaves-your-network)                   |
| Ship audit logs to a SIEM and alert on privileged events                  | Regulated  | [Logging, audit, and monitoring](#logging-audit-and-monitoring)         |
| Set retention for every data class you enable                             | Regulated  | [Personal data and GDPR](#personal-data-and-gdpr)                       |
| Encrypt backups and test a restore                                        | All        | [Backup and recovery](#backup-and-recovery)                             |
| Pin a version and patch on a defined schedule                             | All        | [Updates and patching](#updates-and-patching)                           |

### Which controls apply to your deployment model

| Area                              | Cloud SaaS | Cloud SaaS + on-prem collector | Full on-premises |
| --------------------------------- | ---------- | ------------------------------ | ---------------- |
| Reverse proxy, TLS, and headers   | Navigara   | Navigara                       | **You**          |
| Database hardening and backups    | Navigara   | Navigara                       | **You**          |
| Root admin key custody            | Navigara   | Navigara                       | **You**          |
| Collector host and network        | Navigara   | **You**                        | **You**          |
| Git and tracker credential scopes | **You**    | **You**                        | **You**          |
| Identity provider and SSO policy  | **You**    | **You**                        | **You**          |
| API token lifecycle               | **You**    | **You**                        | **You**          |
| LLM endpoint choice and residency | Navigara   | **You**                        | **You**          |

## Network boundaries

Navigara is designed to need **no inbound network access** apart from the user-facing web interface. Keep it that way.

**Publish only the reverse proxy.** In the reference deployment, Caddy listens on ports 80 and 443. The backend and frontend bind to loopback only (`127.0.0.1:8080`, `127.0.0.1:9090`, `127.0.0.1:3000`), so they are reachable only through the proxy. Do not change these to `0.0.0.0`.

**Keep the instance off the public internet where you can.** Publish it on an internal network, or behind your VPN or zero-trust proxy. Users reach it the same way they reach your other internal tools.

**Block the operational routes at the proxy.** The backend serves Prometheus metrics at `/metrics` and, when explicitly enabled, profiling at `/debug/pprof/`. Neither requires authentication, so neither should be reachable from the internet. Add this to your Caddyfile inside the site block, before the `handle_path /api/*` block:

```
handle /api/metrics {
    respond 404
}

handle /api/debug/* {
    respond 404
}
```

Scrape metrics from inside the network on `127.0.0.1:8080/metrics` instead.

**Keep the security headers.** The reference Caddyfile in [Full on-premises](/deployment/full-on-premises#7-configure-reverse-proxy-caddy) sets HSTS, `X-Frame-Options`, `X-Content-Type-Options`, and `Referrer-Policy`. Add `Permissions-Policy` if your baseline requires it. Do not relax `Access-Control-Allow-Origin` beyond your own domain.

**Declare your proxy ranges.** Navigara reads the client IP from `X-Forwarded-For` only when the immediate peer is a trusted proxy. Without this, audit logs and rate limits record your load balancer instead of the real client.

<Warning>
  `TRUSTED_PROXY_CIDRS` **replaces** the default private ranges — it does not extend them. If you set it, include the Docker bridge range as well, or the reverse proxy and frontend stop being trusted hops:

  ```bash theme={null}
  TRUSTED_PROXY_CIDRS=10.20.0.0/16,172.16.0.0/12
  ```
</Warning>

**Keep the rate limits on.** Requests without credentials are limited per client IP, and authenticated requests per token. The defaults suit a single instance. Limits are counted per process, so the effective budget is the limit multiplied by the number of backend replicas — lower the values if you run more than one.

| Variable                                 | Default | Purpose                                                                                 |
| ---------------------------------------- | ------- | --------------------------------------------------------------------------------------- |
| `RATE_LIMIT_IP_PER_MIN`                  | `100`   | Unauthenticated requests per client IP — login, password reset, OAuth start, root login |
| `RATE_LIMIT_KEY_PER_MIN`                 | `1000`  | Requests per session token or API token                                                 |
| `RATE_LIMIT_LOGIN_ATTEMPTS_PER_EMAIL`    | `10`    | Sign-in attempts per email address                                                      |
| `RATE_LIMIT_LOGIN_ATTEMPTS_EMAIL_WINDOW` | `15m`   | Window for the attempt counter                                                          |

**Control outbound traffic too.** Navigara needs a small, fixed set of destinations. Allow those and deny the rest — see [What leaves your network](#what-leaves-your-network).

## Identity and access

**Use single sign-on.** Connect Google or Microsoft Entra ID so that your identity provider enforces multi-factor authentication, conditional access, and offboarding. Configuration is in [Integrations → Authentication](/integrations/overview#authentication).

**Leave password authentication off.** `ENABLE_PASSWORD_AUTH` is `false` by default. Keep it that way unless you have no identity provider.

<Warning>
  If you do enable password authentication, you must also set `PASSWORD_HASH_PEPPER` to a random secret of **at least 32 bytes**. The backend refuses to start without it.

  ```bash theme={null}
  PASSWORD_HASH_PEPPER=$(openssl rand -base64 48)
  ```

  This value cannot be rotated. Changing it invalidates every stored password, and every user must reset. Generate it once, and store it with your other secrets.
</Warning>

**Keep the on-premises signup brake.** `DEPLOYMENT_MODE` defaults to `onprem`. In this mode a person who authenticates through your identity provider but belongs to no organization is rejected and no organization is created for them. Access still requires an explicit invitation. Do not change this on a deployment reachable by a wide identity pool.

**Assign the smallest role that works.** Navigara separates platform roles from organization roles.

| Role     | Grants                                                                                          |
| -------- | ----------------------------------------------------------------------------------------------- |
| `owner`  | Full control of the organization, including deletion and ownership transfer                     |
| `admin`  | Everything except organization deletion, ownership transfer, and reading AI tool prompt content |
| `member` | Read access, plus maintaining contributor and team metadata                                     |
| `viewer` | Read-only access                                                                                |

Grant `owner` to no more than two people. Review the member list and the token list at least quarterly, and remove anyone who no longer needs access.

**Sessions last seven days.** Sign-out deletes the session immediately, and changing a password ends every other session for that user. Rely on your identity provider for shorter-lived control.

<Note>
  SCIM provisioning synchronizes directory identities and attributes so Navigara can attribute work correctly. It does **not** grant organization membership, and de-provisioning a user in your identity provider does not by itself remove their Navigara access. Remove the member in Navigara as part of offboarding. See [SCIM provisioning](/integrations/scim).
</Note>

## Root admin access

Full on-premises deployments have a root administrator that you control with an Ed25519 key pair. The public key goes in `ADMIN_PUBLIC_KEY`; the matching private key generates short-lived root login links. See [Full on-premises → Generate root private key](/deployment/full-on-premises#3-generate-root-private-key).

Root bypasses every permission check in every organization. Treat the private key as the most sensitive credential in the deployment.

<Warning>
  `admin_key.pem` is a root credential. Never commit it, never leave it on the application host, and never share it over chat or email.
</Warning>

<Steps>
  <Step title="Generate the key on a trusted workstation">
    Do not generate it on the application server. The server only ever needs the public half.
  </Step>

  <Step title="Store the private key in a vault">
    Put it in your secret manager, a hardware token, or a password manager with break-glass procedures. Restrict access to named administrators.
  </Step>

  <Step title="Keep login links short-lived">
    The reference script issues links that expire after 60 seconds. Keep that window. Generate a link when you need it, use it immediately, and do not store it.
  </Step>

  <Step title="Restrict the endpoint">
    Root login is verified by signature rather than by session, so the endpoint accepts requests from anywhere the instance is reachable. In regulated environments, limit it to your administrator networks at the proxy. Add this to your Caddyfile before the `handle_path /api/*` block:

    ```
    handle /api/v1/auth/impersonate {
        route {
            @not_admin not remote_ip 10.20.30.0/24
            respond @not_admin 403
            uri strip_prefix /api
            reverse_proxy backend:8080
        }
    }
    ```

    If another proxy sits in front of Caddy, use the `client_ip` matcher instead of `remote_ip` so the rule tests the real client rather than the proxy.
  </Step>

  <Step title="Monitor its use">
    Every root login writes a log record, and the resulting session is flagged as impersonated. Alert on these — see [Logging, audit, and monitoring](#logging-audit-and-monitoring).
  </Step>

  <Step title="Rotate on personnel change">
    Generate a new key pair, replace `ADMIN_PUBLIC_KEY`, and restart the backend. Destroy the old private key.
  </Step>
</Steps>

<Note>
  Your key is not the only one this endpoint trusts. Read [Navigara support access](#navigara-support-access) before you sign off on the deployment.
</Note>

## Navigara support access

We think you should know this before your security review finds it.

Every Navigara build contains a Navigara-held public key, and the root login endpoint trusts it **alongside** your `ADMIN_PUBLIC_KEY`. Setting `ADMIN_PUBLIC_KEY` adds your key to the trusted set — it does not replace ours.

**What it can do.** A token signed with the matching private key establishes a session as a user on your instance. Your own key is restricted to the `root` account; ours is not, so it can establish a session as any existing user, and that session can read whatever that user can read. As `root` it bypasses every organization permission check, exactly as described in [Root admin access](#root-admin-access). Assess it as an authentication and data-access path, because that is what it is.

**What we use it for.** Navigara staff use it to verify changes on instances we operate on a customer's behalf, which is the great majority of its use. We do not use it to reach instances we do not run for you. That is a statement about how we work, not a technical limit — the controls below are the technical limits.

**What limits it.**

* A token is bound to your domain. It carries an audience claim that must match your deployment's own hostname, so a token minted for one instance does not work on another.
* Every use is audited **in your logs, not only ours**. The record names the key that was used, who was impersonated, the client address, and the user agent. The resulting session is flagged as impersonated in the database.
* Reaching the endpoint requires network access to your instance. If we cannot reach it, we cannot use the key.

**You may refuse it.** A deployment you run is yours, and you are entitled to decide that nobody outside your organization can authenticate to it. There are two ways to enforce that, and they are worth combining.

### Turn it off in configuration

```bash theme={null}
DISABLE_NAVIGARA_SUPPORT_ACCESS=true
```

This drops our key from the trust set at startup. Your `ADMIN_PUBLIC_KEY` becomes the only key that can authenticate as root, and the backend logs the setting at boot so you can confirm it took effect. Licensing is unaffected.

<Warning>
  With this set, `ADMIN_PUBLIC_KEY` is **required** — the backend refuses to start without it, because otherwise no key could authenticate as root. Make sure you can produce a root login link before you enable it, and keep the private key recoverable.
</Warning>

<Note>
  This setting is not in Navigara 0.14.0. Check the [changelog](/changelog) for the release that introduces it, and use the proxy rule below until you are running that release.
</Note>

### Enforce it at the proxy

A configuration flag is honored by our software. A proxy rule is enforced by yours. If you need a control you can verify independently, add one:

* **Restrict by source.** Allow the endpoint only from your administrator networks, using the rule in [Root admin access](#root-admin-access). Your administrators keep root access; nobody outside those ranges can reach the endpoint.
* **Deny outright.** Return 403 permanently, and lift the rule only for the window in which you need root access. This blocks your own root login too, which is the point — nothing can authenticate through it while the rule is in place.

```
handle /api/v1/auth/impersonate {
    respond 403
}
```

Whichever you choose, alert on the audit record so that any use is visible to you — see [Logging, audit, and monitoring](#logging-audit-and-monitoring). If your policy forbids vendor access entirely, tell your account representative, so support expectations match what your deployment allows.

## API tokens

API tokens carry the `navigara_` prefix, are scoped to one organization, and are shown once at creation. Navigara stores only a hash.

**Attach the narrowest role available for the job.** Two write-only roles exist for integration tokens, and you should use them:

| Purpose                                       | Role to select     | Grants                             |
| --------------------------------------------- | ------------------ | ---------------------------------- |
| AI coding tool telemetry                      | Analytics Ingestor | Submitting usage telemetry only    |
| SCIM provisioning from your identity provider | SCIM Provisioner   | Provisioning users and groups only |

<Warning>
  A token created **without** a role carries broad organization access. The collector currently requires such a token, so treat `COLLECTOR_API_KEY` as a high-value credential: give it its own token, never reuse it for anything else, restrict who can read it, and rotate it on a schedule.
</Warning>

Rules to apply:

* **One token per purpose.** Separate the collector, AI tool ingestion, SCIM, and any scripts. A leak then affects one integration.
* **Set an expiry.** Tokens do not expire by default. Give each one a date and diarize the renewal.
* **Rotate by replacement.** Create the new token, move the consumer over, confirm it works, then delete the old one.
* **Revoke on offboarding and on suspicion.** Deleting a token takes effect immediately.
* **Never put a token in a URL, a ticket, or a chat message.** Deliver it through your secret manager.

## Secrets management

Hold every secret in a secret manager — HashiCorp Vault, AWS Secrets Manager, Google Secret Manager, or Azure Key Vault — and inject it at run time. Do not commit secrets to source control, and do not bake them into images.

For the reference Docker Compose deployment, render the `.env` file from your secret manager at deploy time, own it as `root` with mode `0600`, and delete it when the host is decommissioned.

```bash theme={null}
install -m 0600 /dev/null /opt/navigara/.env
vault kv get -format=json secret/navigara | jq -r '...' > /opt/navigara/.env
```

These are the secrets a deployment handles:

| Secret                                                                        | Component            | What it unlocks                                           |
| ----------------------------------------------------------------------------- | -------------------- | --------------------------------------------------------- |
| `DATABASE_URL`                                                                | Backend, frontend    | All application data                                      |
| `COLLECTOR_API_KEY`                                                           | Backend, collector   | The collector's connection to the backend                 |
| `LICENSE_KEY`                                                                 | Backend              | Analysis features                                         |
| `LLM_API_KEY`                                                                 | Backend, collector   | Your LLM provider account                                 |
| `GOOGLE_APPLICATION_CREDENTIALS`                                              | Backend, collector   | The Vertex AI service account, when you use it            |
| `COLLECTOR_PRIVATE_KEY_PATH` / `_PEM`                                         | Collector            | Decryption of every encrypted Git token                   |
| `PASSWORD_HASH_PEPPER`                                                        | Backend              | Password hash strength — only with password auth          |
| `PAGINATION_TOKEN_SECRET`                                                     | Backend              | Integrity of pagination tokens                            |
| `SMTP_PASSWORD`                                                               | Backend              | Your mail relay                                           |
| `GOOGLE_CLIENT_SECRET`, `MICROSOFT_CLIENT_SECRET`, and other provider secrets | Backend              | The single sign-on and connection flows                   |
| `admin_key.pem`                                                               | Held by you, offline | Root access — see [Root admin access](#root-admin-access) |

<Warning>
  Generate `COLLECTOR_API_KEY` yourself. It authenticates the collector to the backend, so a guessable value is a real exposure.

  ```bash theme={null}
  openssl rand -hex 32
  ```
</Warning>

Rotate every secret on a defined schedule, and immediately if you suspect exposure. `PASSWORD_HASH_PEPPER` is the exception — it cannot be rotated without resetting every password.

## Git and issue-tracker credentials

Navigara reads your repositories and trackers with credentials you supply. These are usually the highest-value credentials in the integration, so scope them tightly.

**Prefer the GitHub App.** For GitHub, the Navigara GitHub App issues short-lived installation tokens, scoped to the repositories you select. There is no long-lived token to store or rotate. See [On-prem collector → Git provider authentication](/deployment/on-prem-collector#git-provider-authentication).

**Encrypt tokens end to end.** When you run your own collector, encrypt each personal access token against the collector's public key. Navigara stores only the ciphertext and can never read the token; the collector decrypts it in memory when it authenticates to the Git host. See [Encrypted Git tokens](/deployment/encrypted-git-tokens).

**Grant read-only, minimum scopes.** Navigara never writes to your repositories or trackers. The required scopes for each provider are listed in [Integrations overview](/integrations/overview). Use a dedicated service account rather than a personal account, so access survives staff changes and is auditable.

**Rotate on a schedule.** Mint the new token, encrypt it, paste it in, confirm a successful sync, then revoke the old one. The collector accepts multiple decryption keys at once, so key rotation needs no downtime — see [Key rotation](/deployment/encrypted-git-tokens#key-rotation).

**Revoke on offboarding.** If a departing engineer's account issued a token, revoke it the same day.

## Collector hardening

The collector clones repositories, runs analysis, and streams results to the backend. It handles source code, so it deserves the same care as a build agent.

**It needs no inbound ports.** The collector always dials out to the backend and holds the connection open. Do not publish any port for it. Its operations endpoint is off entirely unless you set `OPS_PORT`, and when you do it binds to `127.0.0.1` (`OPS_BIND_ADDR`). Widen that only if a scraper must reach it across the network, and restrict who can.

**Put it on a private network.** Place it in a private subnet with egress to your Git host, your tracker, your LLM endpoint, and the Navigara backend. Nothing else needs to reach it.

**Turn TLS on whenever it leaves the host.** `COLLECTOR_TLS` is only safe as `false` when the collector and backend share one host and one Docker bridge, as in the all-in-one deployment. The moment the collector moves to its own machine, set it:

```bash theme={null}
COLLECTOR_TLS=true
```

<Warning>
  With `COLLECTOR_TLS=false` across a network, the collector's API key and its analysis payloads travel unencrypted.
</Warning>

**Encrypt the work directory.** The collector writes full Git clones to `WORK_DIR` (default `/tmp/git-analysis`), and Git stores the credential for the remote inside each clone. Put `WORK_DIR` on an encrypted volume, and treat the host as one that holds source code and credentials.

**Understand the cache setting.** By default the collector deletes each clone after it finishes. Setting `COLLECTOR_CACHE_REPOS=true` keeps clones on disk to speed up later runs — which also means source code and credentials persist between jobs. Enable it only on an encrypted, access-controlled volume, and monitor disk usage.

**Keep the provider guards closed.** Two settings widen what a collector may reach. Both default to `false`.

| Variable                       | Default | Notes                                                                                                                                                                                                                                       |
| ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ALLOW_PRIVATE_PROVIDER_HOSTS` | `false` | Set to `true` only on a **dedicated** collector, and only to reach a self-hosted GitLab, GitHub Enterprise, Bitbucket Data Center, or Jira Data Center on a private address. Cloud metadata and loopback addresses stay blocked either way. |
| `ALLOW_LOCAL_REPOSITORIES`     | `false` | Permits `file://` repository paths. Leave off in production — it allows reads from the collector's own filesystem.                                                                                                                          |

**Run one collector per trust boundary.** If two parts of your organization must not see each other's code, give them separate collectors with separate credentials rather than one shared collector.

**Restrict the host.** Run the containers as an unprivileged user, keep the base OS patched, and limit shell access to the platform team.

## Database hardening

PostgreSQL holds everything: analysis results, connection credentials, and user records. It is the highest-value asset in the deployment.

* **Encrypt at rest.** Enable storage encryption on your managed instance, or full-disk encryption if you run it yourself. Use AES-256 or equivalent.
* **Require TLS.** Keep `sslmode=require` in `DATABASE_URL`. Use `verify-full` with a pinned certificate authority where your baseline calls for it.
* **Use a least-privilege application user.** Follow the grants in [Full on-premises → Configure PostgreSQL](/deployment/full-on-premises#5-configure-postgresql). Never point `DATABASE_URL` at a superuser.
* **Restrict the network path.** Allow connections only from the application host's address. Never expose port 5432 to the internet.
* **Separate administrative access.** Give database administrators their own named accounts, ideally through your cloud provider's identity-based authentication rather than a shared password.
* **Audit access.** Log connections and administrative statements, and ship them to the same place as your other audit logs.

## What leaves your network

Navigara makes a small, fixed set of outbound connections. Allow these and deny the rest.

| Destination            | Component          | Purpose                         | Required                      |
| ---------------------- | ------------------ | ------------------------------- | ----------------------------- |
| Your Git host          | Collector          | Cloning and pull request data   | Yes                           |
| Your issue tracker     | Collector          | Alignment scoring               | Only with a tracker connected |
| Your LLM endpoint      | Collector, backend | Commit analysis and summaries   | Yes                           |
| PostgreSQL             | Backend            | All state                       | Yes                           |
| Your SMTP relay        | Backend            | Invitations and password resets | Only with email enabled       |
| Your identity provider | Backend            | Single sign-on                  | Only with SSO enabled         |
| `app.navigara.com:443` | Collector          | Work assignment                 | Hybrid deployments only       |

A full on-premises deployment makes **no connection to Navigara**. License keys are verified offline, and there is no automatic update or telemetry channel.

### The LLM endpoint is a processor

Commit analysis sends commit messages and the changed lines of code to the LLM endpoint that **you** configure. This is the one path on which code content leaves the deployment, and it goes to your provider, not to Navigara.

<Warning>
  If your policy requires that source code never leaves your network, host the model inside your network. Any OpenAI-compatible endpoint works, including vLLM and Ollama — see [Full on-premises → LLM API endpoint](/deployment/full-on-premises#llm-api-endpoint).
</Warning>

For regulated deployments that use a hosted model:

* Choose a **region** that matches your data residency obligations.
* Choose an endpoint with **zero data retention** and no training on your inputs, and get it in writing.
* Record the provider as a **sub-processor** in your record of processing, and put a data processing agreement in place.

### Telemetry is off by default

A self-hosted deployment sends no telemetry unless you configure it. Leave these unset:

| Variable                             | Effect when set                                             |
| ------------------------------------ | ----------------------------------------------------------- |
| `DD_TRACE_ENABLED`                   | Sends application traces to a Datadog agent                 |
| `DD_LLMOBS_ENABLED`                  | Sends **full analysis prompts, including code**, to Datadog |
| `NEXT_PUBLIC_SENTRY_DSN`             | Sends browser and server errors to Sentry                   |
| `NEXT_PUBLIC_DATADOG_APPLICATION_ID` | Enables browser session recording                           |
| `NEXT_PUBLIC_GTM_ID`                 | Loads Google Tag Manager in the browser                     |

<Warning>
  Never enable `DD_LLMOBS_ENABLED` on a deployment that analyzes confidential code. It ships the analysis prompt, including diff content, to a third party.
</Warning>

## Personal data and GDPR

Navigara does not process your customers' data. It does process **personal data about your employees and contractors**, so GDPR applies. Plan for it before you go live.

### What Navigara holds

| Category                      | Examples                                                             | Source                 |
| ----------------------------- | -------------------------------------------------------------------- | ---------------------- |
| Account identity              | Name, email address, sign-in provider                                | Your identity provider |
| Git identity                  | Author name, author email, all merged aliases, provider username     | Your repositories      |
| Directory attributes          | Job title, department, manager, when SCIM is enabled                 | Your identity provider |
| Activity                      | Commit messages, changed lines, pull request titles, review comments | Your repositories      |
| Tracker content               | Issue titles and descriptions                                        | Your issue tracker     |
| AI tool usage                 | Which tools an engineer used and how much                            | Your AI coding tools   |
| Derived metrics and summaries | Per-person performance figures and narrative summaries               | Generated by Navigara  |

### Your obligations as controller

In a self-hosted deployment you are the **controller** for this data and Navigara is not a processor at all — the data never reaches us. In Cloud SaaS and hybrid deployments Navigara acts as a **processor** on your instructions, under a data processing agreement.

Either way, plan the following:

<Steps>
  <Step title="Record the processing">
    Add Navigara to your record of processing activities. Identify your lawful basis — legitimate interest is the usual basis for engineering delivery analytics, and it requires a balancing test you can show.
  </Step>

  <Step title="Tell your engineers">
    Monitoring must be disclosed. State what is measured, why, and who can see it. We apply the same rule internally.
  </Step>

  <Step title="Assess the risk">
    Analytics about individual performance usually warrants a data protection impact assessment. Do it before rollout, not after.
  </Step>

  <Step title="Consult where required">
    In several jurisdictions, works councils or employee representatives must be consulted before performance-related monitoring begins.
  </Step>

  <Step title="List your sub-processors">
    Your LLM provider receives commit content. Your hosting provider holds the database. Both belong in your record, with agreements in place.
  </Step>
</Steps>

### Minimize what you collect

* **Analyze only the repositories you need.** Every repository you add widens the data set.
* **Leave AI prompt capture off.** `TOOL_USAGE_EVENTS_CAPTURE` is `false` by default. It records prompt and tool content, which is rarely necessary and greatly increases sensitivity.
* **Restrict who can read prompt content.** Only organization owners hold that permission. Do not widen it.
* **Keep exports controlled.** Reports and summaries name individuals. Treat them as confidential.

### Set retention

Retention is configured per data class. Set every value deliberately.

| Variable                      | Default           | Controls                                                                                                                                           |
| ----------------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TOOL_USAGE_RETENTION`        | `336h` (14 days)  | Raw AI tool usage records. `0` keeps them indefinitely.                                                                                            |
| `TOOL_USAGE_EVENTS_RETENTION` | unset             | Detailed AI tool events, which may include prompt content. **Unset means these are kept indefinitely** — always set a value if you enable capture. |
| `WEBHOOK_EVENT_RETENTION`     | `2160h` (90 days) | Raw payloads received from your Git provider.                                                                                                      |

Expired sessions and password-reset tokens are removed automatically every day.

<Note>
  Analysis records are keyed to Git identities rather than to user accounts, so removing a person's login does not remove the commit history attributed to them. Deleting an organization removes its analysis data. Plan how you will answer an erasure or access request before you go live — contact Navigara support to scope the procedure for your deployment.
</Note>

## Logging, audit, and monitoring

**Keep the audit log on.** `AUDIT_LOG_ENABLED` defaults to `true`. It writes one structured record per privileged operation, containing who acted, what they did, which resource, the outcome, and the client address. Request bodies are never logged.

**Ship logs off the host.** Forward container logs to your SIEM or log platform, and apply your standard retention there. Logs held only on the application host are lost when it is rebuilt.

**Alert on these events:**

| Event                                                       | Why it matters                                                                                                |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- |
| Root login or Navigara support access (`user impersonated`) | Highest-privilege access to the instance. The record names which key was used, so you can tell the two apart. |
| Repeated sign-in failures for one account                   | Credential attack                                                                                             |
| API token created or deleted                                | New long-lived access path                                                                                    |
| Role granted, especially `owner`                            | Privilege escalation                                                                                          |
| Connection created or deleted                               | Change to what Navigara can read                                                                              |
| Organization deleted                                        | Irreversible data loss                                                                                        |
| Rate-limit rejections in volume                             | Automated probing                                                                                             |

**Make client addresses trustworthy.** Configure your edge proxy to overwrite inbound `X-Forwarded-For`, and set `TRUSTED_PROXY_CIDRS` — otherwise a client can put any address in the audit trail.

**Watch availability too.** The backend exposes `GET /health`. The collector exposes `/health` and `/health/ready`, but only if you set `OPS_PORT`. A collector that stops connecting means analysis has stopped silently.

## Backup and recovery

* **Back up the database on a schedule** that meets your recovery point objective. See [Full on-premises → Backup and restore](/deployment/full-on-premises#backup-and-restore).
* **Encrypt the backups** with AES-256 or equivalent, and store them separately from the database.
* **Restrict access** to backups as tightly as to the database itself. A backup is a complete copy of every credential and record.
* **Test a restore at least annually**, into an isolated environment, and record the result. An untested backup is not a control.
* **Record your own secrets.** The database alone will not restore a deployment — you also need `.env`, the collector private key, and the root admin key. Keep those in your secret manager, backed up independently.
* **Define your objectives.** Internally we target 24 hours for both recovery time and recovery point. Set yours to match your own requirements.

## Updates and patching

* **Pin a version.** Set `NAVIGARA_VERSION` explicitly rather than tracking a floating tag, so that deployments are reproducible.
* **Mirror the images.** Regulated environments should pull Navigara images into their own registry, scan them there, and deploy from the mirror. Override `BACKEND_IMAGE`, `COLLECTOR_IMAGE`, and `FRONTEND_IMAGE` to point at it.
* **Patch on a clock.** Apply our remediation targets, or your own if they are stricter: 15 days for critical findings, 30 for high, 60 for medium, 90 for low.
* **Track releases.** Watch the [changelog](/changelog) for security-relevant changes. There is no automatic update — you decide when to upgrade.
* **Back up before upgrading.** Database migrations run automatically on startup.
* **Patch the host too.** The base operating system, Docker, PostgreSQL, and the reverse proxy are yours to keep current.

## Security configuration reference

Every security-relevant setting, in one place.

| Variable                              | Default        | Recommendation                                                                                                                      |
| ------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `ENABLE_PASSWORD_AUTH`                | `false`        | Leave off. Use single sign-on.                                                                                                      |
| `PASSWORD_HASH_PEPPER`                | —              | Required, 32 bytes or more, only with password auth. Not rotatable.                                                                 |
| `DEPLOYMENT_MODE`                     | `onprem`       | Leave as is. Blocks self-signup from reaching data.                                                                                 |
| `ADMIN_PUBLIC_KEY`                    | —              | Set it. Keep the private half in a vault. Required when support access is disabled.                                                 |
| `DISABLE_NAVIGARA_SUPPORT_ACCESS`     | `false`        | Set to `true` to make `ADMIN_PUBLIC_KEY` the only root credential. See [Navigara support access](#navigara-support-access).         |
| `COLLECTOR_API_KEY`                   | —              | Generate a unique random value.                                                                                                     |
| `COLLECTOR_TLS`                       | `false`        | Set to `true` whenever the collector is not on the backend's host.                                                                  |
| `COLLECTOR_CACHE_REPOS`               | `false`        | Enable only on an encrypted volume.                                                                                                 |
| `COLLECTOR_PRIVATE_KEY_PATH`          | —              | Set it to use end-to-end encrypted Git tokens.                                                                                      |
| `ALLOW_PRIVATE_PROVIDER_HOSTS`        | `false`        | Enable only on a dedicated collector reaching a self-hosted provider.                                                               |
| `ALLOW_LOCAL_REPOSITORIES`            | `false`        | Leave off in production.                                                                                                            |
| `ALLOW_HTTP_PROVIDERS`                | `false`        | Leave off. Development only.                                                                                                        |
| `TRUSTED_PROXY_CIDRS`                 | private ranges | Set behind a load balancer. Include the Docker bridge range.                                                                        |
| `ALLOWED_FRONTEND_ORIGINS`            | —              | Leave empty. It widens where sign-in may redirect; add an origin only if you deliberately serve the interface from a second domain. |
| `RATE_LIMIT_IP_PER_MIN`               | `100`          | Lower it if you run several backend replicas.                                                                                       |
| `RATE_LIMIT_KEY_PER_MIN`              | `1000`         | Lower it if you run several backend replicas.                                                                                       |
| `RATE_LIMIT_LOGIN_ATTEMPTS_PER_EMAIL` | `10`           | Keep or lower.                                                                                                                      |
| `AUDIT_LOG_ENABLED`                   | `true`         | Leave on.                                                                                                                           |
| `ENABLE_PPROF`                        | `false`        | Leave off in production.                                                                                                            |
| `ENABLE_GRPC_REFLECTION`              | `false`        | Leave off in production.                                                                                                            |
| `TOOL_USAGE_EVENTS_CAPTURE`           | `false`        | Leave off unless you need prompt content.                                                                                           |
| `TOOL_USAGE_EVENTS_RETENTION`         | unset          | Set a value if you enable capture.                                                                                                  |
| `TOOL_USAGE_RETENTION`                | `336h`         | Set to match your retention policy.                                                                                                 |
| `WEBHOOK_EVENT_RETENTION`             | `2160h`        | Set to match your retention policy.                                                                                                 |
| `DD_TRACE_ENABLED`                    | `false`        | Leave off unless you run Datadog yourself.                                                                                          |
| `DD_LLMOBS_ENABLED`                   | `false`        | Leave off. It sends code to a third party.                                                                                          |
| `DATABASE_URL`                        | —              | Use a least-privilege user and `sslmode=require`.                                                                                   |
| `SMTP_TLS`                            | `starttls`     | Keep TLS on. Do not use `none`.                                                                                                     |

## Reporting a security issue

If you find a security problem in Navigara, email [support@navigara.com](mailto:support@navigara.com) with the details and a way to reach you. Please report it to us before disclosing it publicly. We will confirm receipt, keep you updated, and credit you if you would like.

For our current security documentation, policy summaries, or a data processing agreement, contact your Navigara account representative.
