Skip to main content
This page describes the security architecture of KaireonAI as it is implemented today. Where a capability is planned but not yet shipped, it is marked explicitly.

Tenant Isolation

KaireonAI is multi-tenant by default. Every database record includes a tenantId column, and every API query is scoped to the authenticated tenant.

Application-Layer Filtering

All database access goes through Prisma ORM helpers that inject tenantId into every WHERE clause:
  • enforceTenantFilter() — injects the tenant ID into any Prisma where object before execution.
  • withTenantScope(tenantId) — returns a scoped CRUD wrapper that automatically includes the tenant filter on findMany, findUnique, create, update, and delete.
If the tenant cannot be resolved from the session or API key, the request is denied with 403 Forbidden.

Optional Row-Level Security (RLS)

For deployments that require database-level isolation as a second boundary, KaireonAI ships an admin API (POST /api/v1/admin/rls/enable) that creates PostgreSQL RLS policies on all tenant-scoped tables. RLS is opt-in because it requires superuser privileges on the database and adds a small query overhead.
RLS is a defense-in-depth layer, not a replacement for application-layer filtering. The application filter is always active regardless of RLS state.

Single-Tenant Mode

Self-hosted deployments can set SINGLE_TENANT_MODE=true to bypass multi-tenant resolution. In this mode, all data belongs to a single default tenant and the tenant resolution middleware is skipped.

Authentication

KaireonAI supports three authentication methods, each suited to a different integration pattern.

Browser Sessions (NextAuth)

Interactive users authenticate via NextAuth.js sessions:
  • Google OAuth — one-click sign-in, automatic email verification.
  • Email and password — bcrypt-hashed passwords, email verification flow, password reset via time-limited tokens.
Sessions are stored as signed HTTP-only cookies. Session tokens are rotated on each request. The session includes userId, tenantId, and role.
MFA (TOTP/WebAuthn) is on the roadmap but not yet implemented.

API Keys (Server-to-Server)

For programmatic access, users generate API keys from the Settings page. Keys are prefixed krn_ for easy identification in logs.
  • Keys are stored as one-way HMAC-SHA256 hashes — the raw key is shown once at creation and cannot be recovered.
  • Each key is bound to a specific tenant. The X-Tenant-Id header is ignored when authenticating via API key to prevent tenant spoofing.
  • Keys support optional scopes (read-only, write, admin) and expiration dates.

Internal Service Tokens

For internal service-to-service calls (e.g., pipeline workers, cron jobs), the platform accepts a shared INTERNAL_SERVICE_TOKEN validated via constant-time comparison. This token bypasses session lookup and is intended for trusted infrastructure only.
Keep INTERNAL_SERVICE_TOKEN out of client-side code and environment variable logs. Rotate it when team members leave.

CSRF Protection

KaireonAI uses a dual-strategy CSRF model:
Client TypeProtection Mechanism
Browser (UI)X-Requested-With: XMLHttpRequest header required on all mutating requests. The browser’s same-origin policy prevents cross-origin scripts from setting custom headers.
Server-to-serverAPI key in Authorization header. No CSRF risk because there is no ambient credential (no cookie).
The X-Requested-With check is enforced in the API middleware layer. Requests without this header or a valid API key are rejected with 403.
This is a standard defense — the same approach is used by Django, Rails, and Angular. It relies on the browser not allowing cross-origin XMLHttpRequest headers, which is enforced by the same-origin policy in all modern browsers.

SSRF Protection

Server-Side Request Forgery (SSRF) is a significant risk because KaireonAI makes outbound HTTP calls in several places: webhook delivery, REST API connectors, CMS content sync, external ML scoring endpoints, alert notifications, and trigger engine webhooks.

Two-Layer Validation

  1. Hostname check (synchronous)validateExternalUrl() blocks known-bad hostnames (localhost, metadata.google.internal, 169.254.169.254) and private IP ranges (10.x, 172.16-31.x, 192.168.x, 127.x, ::1, fc00::/7, fe80::/10).
  2. DNS resolution check (async)validateAndResolve() resolves the hostname via dns.promises.resolve4() and resolve6(), then verifies that every resolved IP is outside private ranges. This prevents DNS rebinding attacks where a hostname initially resolves to a public IP during validation but later resolves to a private IP.

Protected Code Paths

Code PathFileProtection
Webhook deliverylib/channel/webhook-provider.tsvalidateAndResolve() before fetch
REST API connectorslib/pipeline/executor.tsvalidateAndResolve() before fetch
External ML scoringlib/scoring/external-endpoint.tsvalidateAndResolve() before fetch
CMS content synclib/cms/adapters.tsvalidateAndResolve() on all adapter URLs
Alert webhookslib/alerting.tsvalidateAndResolve() before fetch
Trigger webhookslib/trigger-engine.tsvalidateAndResolve() before fetch
SSO JWKS/token URLslib/sso-jwks.tsvalidateExternalUrl() (admin-configured URLs)

Self-Hosted Recommendation

If you run KaireonAI on a private network, consider adding an egress proxy (e.g., Squid, Envoy) that restricts outbound traffic to known-good destinations. The application-level SSRF check is a strong default, but an egress proxy provides defense in depth at the network layer.

Rate Limiting

KaireonAI includes a sliding-window rate limiter with two storage backends:
  • In-memory — suitable for single-process deployments. Tracks request timestamps per key and rejects when the count exceeds the configured limit within the window.
  • Redis-backed — uses sorted-set sliding window (ZADD + ZREMRANGEBYSCORE + ZCARD) for consistent rate limiting across multiple Node.js processes.
The checkWithFallback() method tries Redis first and falls back to in-memory if Redis is unavailable, ensuring rate limiting is always active. Rate limits are applied to:
  • Journey callback endpoints
  • The Recommend API (configurable per deployment)
For DDoS protection, we recommend using edge-level rate limiting (AWS WAF, Cloudflare rate rules, or nginx limit_req_zone) in addition to the application-level limiter.

Encryption at Rest

Connector Secrets

All sensitive connector credentials (database passwords, API tokens, OAuth client secrets) are encrypted with AES-256-GCM before storage. Each record uses a unique initialization vector (IV) and authentication tag. The encryption key is derived from the ENCRYPTION_KEY environment variable. Self-hosted deployments must set this to a cryptographically random 32-byte hex string.

API Key Hashing

Platform API keys (krn_ prefix) are stored as one-way HMAC-SHA256 hashes. The raw key is displayed once at creation and cannot be recovered from the database.

Password Hashing

User passwords are hashed with bcrypt (cost factor 12) before storage.

Audit Logging

Every mutating operation is recorded in the AuditLog table with:
  • tenantId, userId, action, entityType, entityId
  • before and after JSON snapshots (for update operations)
  • ipAddress and userAgent from the request
  • timestamp
Audit logs are immutable — they cannot be edited or deleted through the API. The logAudit() helper is called from API route handlers after successful mutations. Audit logs can be queried via the UI (Settings > Audit Log) or exported via GET /api/v1/audit-logs/export for SIEM integration.

Input Validation

All API request bodies are validated with Zod schemas before processing. Invalid requests are rejected with 400 Bad Request and a structured error response listing the validation failures. The formula engine uses a custom recursive-descent parser — dynamic code execution via eval or constructor-based evaluation is never used. External content IDs (e.g., CMS entry IDs) are sanitized through sanitizeExternalId() which strips all characters outside [a-zA-Z0-9._-].

Webhook Signature Verification

Inbound webhooks (from CMS providers, payment systems, etc.) are verified using HMAC-SHA256 signature validation with constant-time comparison to prevent timing attacks. The signing secret is configured per integration. Outbound webhooks sent by KaireonAI include an X-Kaireon-Signature header containing a sha256= prefixed HMAC, allowing recipients to verify authenticity.

What Users Should Configure

For a production deployment, ensure these are set:
SettingPurpose
ENCRYPTION_KEYAES-256 key for connector secret encryption (32-byte hex)
NEXTAUTH_SECRETSession signing key for NextAuth.js
WEBHOOK_SIGNING_SECRETHMAC key for outbound webhook signatures
INTERNAL_SERVICE_TOKENShared secret for internal service-to-service auth
DATABASE_URLPostgreSQL connection string (use SSL in production)
TLS terminationConfigure at load balancer or reverse proxy
Edge rate limitingAWS WAF, Cloudflare, or nginx for DDoS protection
Egress proxyOptional — restrict outbound traffic from the application

Planned Enhancements

The following capabilities are on the roadmap but not yet implemented:
  • Multi-factor authentication — TOTP and WebAuthn support for interactive sessions
  • OAuth 2.0 provider — Allow external applications to authenticate via OAuth flows
  • SCIM provisioning — Automated user provisioning from identity providers
  • Field-level encryption — Encrypt sensitive customer data fields at the application layer
  • IP allowlisting — Restrict API key usage to specific IP ranges