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.
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 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) |
For inter-service encryption, deploy a service mesh like Istio or Linkerd. This adds mTLS between all pods without application changes.
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:
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:
secrets:
existingSecret: langwatch-secrets
Production (External Secret Managers)
For tighter security, use an external secrets operator to sync secrets from your cloud provider:
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:
# 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:
# 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 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:
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):
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.
npm CLI
The langwatch npm package is published with npm provenance attestations via GitHub Actions OIDC, also backed by Sigstore. The provenance link is visible on the package page and can be verified with npm audit signatures.
Production Hardening Checklist