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

# Security

> Security model, encryption, secrets management, and hardening for LangWatch

This page covers the security features and best practices for self-hosted LangWatch deployments.

## Authentication & Authorization

**NextAuth.js** handles user authentication with support for:

* Email/password (default)
* SSO providers: Azure AD, Okta, Auth0, AWS Cognito, Google, GitHub, GitLab

See [SSO Configuration](/docs/self-hosting/configuration/sso) for setup guides.

**Role-Based Access Control (RBAC)** controls what users can do within a project:

* Organization-level roles (owner, admin, member)
* Project-level permissions

**SCIM provisioning** (Enterprise) enables automated user lifecycle management from your identity provider.

**API tokens** are signed with JWT (`API_TOKEN_JWT_SECRET`) for SDK authentication.

## Encryption

### At Rest

| Data Store         | Encryption Method                                            |
| ------------------ | ------------------------------------------------------------ |
| PostgreSQL         | Provider-level encryption (RDS: AES-256, Cloud SQL: AES-256) |
| ClickHouse         | Encrypted volumes (EBS encryption, PD encryption)            |
| S3                 | Server-side encryption (SSE-S3 or SSE-KMS)                   |
| Stored credentials | Application-level encryption via `CREDENTIALS_SECRET`        |

The `CREDENTIALS_SECRET` environment variable is used to encrypt API keys and credentials stored in PostgreSQL (e.g., LLM provider keys configured in the UI). This is application-level encryption on top of database-level encryption.

### In Transit

| Path                                         | Encryption                                                |
| -------------------------------------------- | --------------------------------------------------------- |
| Client to App                                | TLS at Ingress, Load Balancer                             |
| App to PostgreSQL                            | TLS (configure via connection string: `?sslmode=require`) |
| App to ClickHouse                            | HTTPS (configure ClickHouse with TLS certificates)        |
| App to Redis                                 | TLS (configure via connection string: `rediss://...`)     |
| Inter-service (App, Workers, NLP, LangEvals) | Plain HTTP within cluster (use a service mesh for mTLS)   |

<Tip>
  For inter-service encryption, deploy a service mesh like Istio or Linkerd. This adds mTLS between all pods without application changes.
</Tip>

## Secrets Management

### Development (Auto-Generated)

For development, enable `autogen.enabled: true` in the Helm chart. This generates random secrets automatically. Not suitable for production, secrets change on reinstall.

### Production (Kubernetes Secrets)

Create secrets manually and reference them in the Helm chart:

```bash theme={null}
kubectl create secret generic langwatch-secrets \
  --namespace langwatch \
  --from-literal=credentialsEncryptionKey=$(openssl rand -hex 32) \
  --from-literal=nextAuthSecret=$(openssl rand -hex 32) \
  --from-literal=cronApiKey=$(openssl rand -hex 32)
```

Reference in `values.yaml`:

```yaml theme={null}
secrets:
  existingSecret: langwatch-secrets
```

### Production (External Secret Managers)

For tighter security, use an external secrets operator to sync secrets from your cloud provider:

* **AWS Secrets Manager**: via [External Secrets Operator](https://external-secrets.io/)
* **HashiCorp Vault**: via [Vault Secrets Operator](https://developer.hashicorp.com/vault/docs/platform/k8s/vso)
* **Azure Key Vault**: via [Azure Key Vault Provider](https://azure.github.io/secrets-store-csi-driver-provider-azure/)

The Helm chart's `secretKeyRef` pattern works with any Kubernetes Secret, regardless of how it was created.

## Network Security

### Recommended Network Architecture

* **Only the LangWatch App should be exposed externally** via Ingress or Load Balancer
* All other components (Workers, NLP, LangEvals, PostgreSQL, ClickHouse, Redis) should be on internal networks only (ClusterIP services)
* Place databases in private subnets with no internet access
* Use VPC endpoints, PrivateLink for S3 access

### Kubernetes Network Policies

Restrict traffic between pods:

```yaml theme={null}
# Example: only allow app and workers to reach ClickHouse
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: clickhouse-access
  namespace: langwatch
spec:
  podSelector:
    matchLabels:
      app: clickhouse
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app.kubernetes.io/component: app
        - podSelector:
            matchLabels:
              app.kubernetes.io/component: workers
      ports:
        - port: 8123
```

### Firewall Rules

| Source         | Destination   | Port | Protocol |
| -------------- | ------------- | ---- | -------- |
| Internet, VPN  | App (Ingress) | 443  | HTTPS    |
| App            | PostgreSQL    | 5432 | TCP      |
| App, Workers   | ClickHouse    | 8123 | HTTP     |
| App, Workers   | Redis         | 6379 | TCP      |
| Workers        | NLP           | 5561 | HTTP     |
| Workers        | LangEvals     | 5562 | HTTP     |
| NLP, LangEvals | External LLMs | 443  | HTTPS    |
| CronJobs       | App           | 5560 | HTTP     |

## Pod Security

The Helm chart applies secure defaults to all pods:

```yaml theme={null}
# Pod-level (applied via global.podSecurityContext)
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000

# Container-level (applied via global.containerSecurityContext)
allowPrivilegeEscalation: false
capabilities:
  drop:
    - ALL
readOnlyRootFilesystem: true
```

These ensure:

* No containers run as root
* No privilege escalation is possible
* Containers cannot modify their own filesystem
* All Linux capabilities are dropped

## PII Redaction

LangWatch includes a built-in PII redaction pipeline step that automatically detects and masks personally identifiable information in traces before storage.

* **Enabled by default** in the Helm chart
* Disable with `app.features.disablePiiRedaction: true` (not recommended)
* Runs as part of the event sourcing pipeline in workers

## Multitenancy

LangWatch enforces tenant isolation at the application level:

* Every ClickHouse query includes `WHERE TenantId = ...` as the first predicate
* PostgreSQL queries include `projectId` in WHERE clauses
* API tokens are scoped to a specific project
* Cross-tenant data access is prevented at the query layer

## Supply Chain

LangWatch container images and CLI packages are published with verifiable supply-chain attestations so operators can confirm an artifact was built by LangWatch CI from a specific source commit.

### Container images (Docker Hub)

Every release of `langwatch/langwatch`, `langwatch/langwatch_nlp`, `langwatch/langevals`, and `langwatch/ai-gateway` is signed with [Sigstore](https://www.sigstore.dev/) cosign using keyless OIDC. Both the multi-arch index manifest and each per-platform manifest (linux/amd64, linux/arm64) are signed by digest. A CycloneDX SBOM is generated per platform and attached as a cosign attestation against the matching platform manifest digest, so the SBOM you verify always corresponds to the architecture you actually pulled.

Verify a signature with [cosign](https://github.com/sigstore/cosign):

```bash theme={null}
cosign verify langwatch/langwatch:<tag> \
  --certificate-identity-regexp '^https://github\.com/langwatch/langwatch/' \
  --certificate-oidc-issuer https://token.actions.githubusercontent.com
```

Inspect the attached SBOM for the platform you pulled (cosign resolves the right per-platform manifest digest automatically when you pass a tag):

```bash theme={null}
cosign download attestation \
  --predicate-type https://cyclonedx.org/bom \
  langwatch/langwatch:<tag> \
  | jq -r '.payload | @base64d | fromjson | .predicate' \
  > langwatch.cdx.json
```

The per-platform `*.cdx.json` files (e.g. `langwatch-linux-amd64.cdx.json`, `langwatch-linux-arm64.cdx.json`) are also attached to each `langwatch@vX.Y.Z` [GitHub release](https://github.com/langwatch/langwatch/releases).

### npm CLI

The `langwatch` npm package is published with [npm provenance attestations](https://docs.npmjs.com/generating-provenance-statements) via GitHub Actions OIDC, also backed by Sigstore. The provenance link is visible on the [package page](https://www.npmjs.com/package/langwatch) and can be verified with `npm audit signatures`.

## Production Hardening Checklist

* [ ] `autogen.enabled: false`, use manually created secrets
* [ ] All secrets stored in a secrets manager (not inline in values.yaml)
* [ ] TLS enabled on Ingress (HTTPS only)
* [ ] Database connections use TLS (`?sslmode=require`)
* [ ] PostgreSQL, ClickHouse, Redis in private subnets (no public access)
* [ ] Network policies restrict pod-to-pod traffic
* [ ] S3 buckets have public access blocked
* [ ] ClickHouse backups enabled and tested
* [ ] Monitoring and alerting configured
* [ ] Secret rotation procedure documented
* [ ] Pod security contexts verified (non-root, read-only filesystem)
* [ ] Ingress rate limiting configured
* [ ] Audit logs enabled (Enterprise)
* [ ] Image signatures verified at pull time (admission controller or `cosign verify` in CI)
