The server API is protected by API key authentication via the auth.ts middleware:
- API keys are passed in the
X-API-Keyrequest header - The server validates against a SHA-256 hash stored in the
API_KEY_HASHenvironment variable - Unauthenticated requests receive a
401 Unauthorizedresponse - The health check endpoint (
GET /api/ping) is exempt from authentication
# Generate an API key hash
echo -n "your-api-key" | sha256sum
# Store the hash in environment
export API_KEY_HASH="<sha256-hash>"The dashboard stores the API key in localStorage and injects it into requests via an Axios interceptor. For production deployments, consider:
- OAuth 2.0 / OIDC integration (planned for future release)
- Reverse proxy authentication (nginx
auth_request) - Network-level access control (VPN, private subnet)
- No secrets in code: All credentials (database URLs, API tokens, connector keys) are passed via environment variables
- Kubernetes secrets: The Helm chart references
devpulse-db-secretfor database credentials - Docker Compose: Uses environment variable interpolation (
${GITHUB_TOKEN}) - Config file:
devpulse.config.yamlsupports environment variable substitution (${VAR}syntax)
- PostgreSQL connections use SSL in production (
?sslmode=requirein connection string) - Database credentials are stored in Kubernetes secrets, not ConfigMaps
- The demo mode uses isolated seed data that does not persist across restarts
- No PII is stored by default — contributor names can be anonymized via
anonymize_contributors: true
- CORS: The server restricts cross-origin requests to the
DASHBOARD_URLorigin - Rate limiting: API rate limiter middleware prevents abuse (configurable limits)
- nginx security headers: The dashboard nginx config sets:
X-Frame-Options: DENY— prevents clickjackingX-Content-Type-Options: nosniff— prevents MIME type sniffingX-XSS-Protection: 1; mode=block— enables browser XSS filtering
- TLS: Helm chart supports TLS termination at the ingress with configurable
tls.secretName
Each connector authenticates to its respective API using tokens provided via environment variables:
| Connector | Auth Method | Header |
|---|---|---|
| GitHub | Bearer token | Authorization: Bearer <token> |
| Jira | Basic auth (email + API token) | Authorization: Basic <base64> |
| PagerDuty | Token auth | Authorization: Token token=<token> |
| OpsGenie | GenieKey | Authorization: GenieKey <key> |
| Jenkins | Basic auth (user + API token) | Authorization: Basic <base64> |
- GitHub tokens should be scoped to
repo:readandactions:read - Jira tokens need only read access to project issues
- PagerDuty tokens should be read-only API tokens
- Connectors never write to external systems — they only read data
- All connector HTTP requests use
AbortSignal.timeout()to prevent hanging connections - Rate limiting is enforced per-connector to avoid overwhelming external APIs
- Retry logic uses exponential backoff with jitter to prevent thundering herd
- CodeQL scanning: Automated security analysis runs on every PR and weekly on
main - Dependency scanning: GitHub Dependabot alerts for vulnerable dependencies
- Secrets: GitHub Actions secrets for
GITHUB_TOKEN(auto-provided) and container registry credentials - No
--no-verify: Pre-commit hooks are never bypassed in CI
- Multi-stage builds: Production images don't include build tools or dev dependencies
- Non-root user: Server Dockerfile runs as
nodeuser (UID 1000), not root - Alpine base: Minimal attack surface with
node:20-alpineandnginx:alpine - No unnecessary packages: Production images only contain runtime dependencies
- Database passwords are referenced from Kubernetes secrets, not Helm values
- Pod security contexts can be configured via values.yaml
- Ingress TLS is supported with configurable certificate secrets
DevPulse processes engineering metadata (PR titles, incident descriptions, defect summaries). While not typically classified as PII, this data may contain:
- Employee names (PR authors, incident responders)
- Internal project names and component names
- Incident descriptions that may reference customer-impacting events
- Anonymization:
anonymize_contributors: falsecan be set totrueto hide individual names - Access control: API key authentication restricts data access
- Network isolation: Deploy on internal network or behind VPN for sensitive environments
- Audit logging: Server logs API requests with timestamp and endpoint (no request bodies)
| Vector | Risk | Mitigation |
|---|---|---|
| API key exposure | Medium | SHA-256 hashing, environment variables, secret management |
| SQL injection | Low | Parameterized queries via node-pg (no ORM, but safe query binding) |
| XSS | Low | React auto-escaping, CSP headers via nginx |
| CSRF | Low | API-key auth (not cookie-based), CORS restrictions |
| Connector token theft | Medium | Environment variables, Kubernetes secrets, least-privilege scoping |
| Denial of service | Medium | Rate limiting middleware, connection pooling, resource limits in Helm |
- Set
API_KEY_HASHenvironment variable with SHA-256 hash - Use SSL/TLS for database connections (
?sslmode=require) - Enable TLS on ingress (set
ingress.tls.enabled=truein Helm) - Scope connector tokens to read-only access
- Set
DASHBOARD_URLfor CORS restriction - Create Kubernetes secrets for database credentials
- Review and restrict network access to PostgreSQL and Redis
- Enable
anonymize_contributorsif running in sensitive environments - Run CodeQL scans and address findings before production deployment
- Set appropriate resource limits in Kubernetes to prevent resource exhaustion