Last Updated: 2026-03-31 Status: Active
This document outlines the strict data privacy, multi-tenant boundaries, and cryptographic security measures implemented within the Chat Engine. Since the system ingests proprietary business documents (Knowledge Hub) and handles end-user chats (PII), security is our highest priority.
1. Multi-Tenant Data Isolation
1.1 pgvector Semantic Isolation
Vector databases are prone to "cross-tenant leakage" if queries are not rigorously filtered.
Invariant: Every vector similarity search MUST enforce a hard WHERE tenant_id = ? clause at the lowest SQL execution level.
-- DANGEROUS (NEVER DO THIS):
SELECT raw_text FROM knowledge_chunks ORDER BY embedding <-> '[0.1, ...]' LIMIT 5;
-- CORRECT:
SELECT raw_text FROM knowledge_chunks
WHERE tenant_id = 'tnt_123'
ORDER BY embedding <-> '[0.1, ...]' LIMIT 5;If we fail to enforce this, Tenant A's pricing guide could be fed into the LLM when Tenant B's customer asks a question.
1.2 Redis Memory Isolation
Chat histories stored in Redis for fast caching must use prefixed keys that include both the tenantId and sessionId.
- Format:
chat:{tenantId}:session:{sessionId}:history - This ensures that a bug regarding identical
sessionIdsacross different accounts cannot result in cross-account chat history loading.
2. Cryptographic Security for Third-Party Channels
The Chat Engine connects to logic outside of LogicSpike via Meta, Telegram, and standard Webhooks.
2.1 The Credentials Store
When a Business Owner connects their WhatsApp Business API, they provide a long-lived Access Token.
Rule: ChannelIntegration.credentials is NEVER stored in plain text.
- Before INSERT/UPDATE, the token is encrypted using AES-256-GCM.
- The decryption key is held strictly in environment variables (
ENCRYPTION_SECRET), injected only at runtime into thechat-servicememory. - The Dashboard Frontend NEVER receives the plaintext token back in a
GETrequest. It only receives{"status": "active", "has_token": true}.
2.2 Ingress Webhook Verification
We cannot trust payloads simply because they strike our /webhooks/whatsapp endpoint.
Rule: Every incoming WhatsApp webhook must be cryptographically verified using crypto.createHmac.
- Meta signs the HTTP body with the
App Secret. - The
X-Hub-Signature-256header must match our newly generated HMAC checksum of the raw request body. - If it fails, we drop the connection with a
401 Unauthorized. This prevents malicious actors from spoofing messages from customers to inflate LLM token billing costs.
3. PII (Personally Identifiable Information) Redaction
Customers frequently send credit card numbers, social security numbers, or sensitive medical info to chatbots, assuming it is human and secure.
LLM PII Bleed Prevention
Before the Chat Orchestrator sends a customer's message to the external LLM provider APIs (OpenAI / Gemini), a Regex-based PII scrubber intercepts the payload:
- Credit cards, generic regex: replaced with
[REDACTED_CARD] - SSNs, generic regex: replaced with
[REDACTED_ID]
This ensures we do not train foundational models on our customers' sensitive data, maintaining SOC2 compliance.
4. LLM API Key Rotation & Zero-Trust
The chat-service communicates continuously with OpenAI/Gemini.
- Key Storing: The primary
OPENAI_API_KEYis rotated every 30 days via Cloudflare Secrets. - Provider Opt-Out: LogicSpike exclusively uses enterprise-tier LLM endpoints that legally guarantee zero retention of our prompt data (OpenAI API Data Privacy policy), meaning Tenant data is not used to train the general ChatGPT model.
5. Widget CORS & Origin Validation
The embeddable Website Widget loads inside a sandboxed <iframe> on third-party domains. Strict origin controls prevent abuse.
- Allowed Origin: When a tenant enables the Widget channel, they register their domain (e.g.,
https://rahuls-saas.com). TheChannelIntegration.identifierstores this domain. - Validation Flow: Every
POST /chat/widget/messagerequest includes anOriginheader. The Chat Service checks: Does theX-Widget-Tokenbelong to aChannelIntegrationwhoseidentifiermatches thisOrigin? If not →403 FORBIDDEN. - Why This Matters: Without this, an attacker could copy the
<script>tag, embed it on their own site, and burn through the tenant's AI credits by sending thousands of junk messages.
6. Per-Tenant Rate Limiting
To prevent a single workspace from accidentally (or maliciously) overwhelming the system:
| Scope | Limit | Window | Action on Breach |
|---|---|---|---|
| Widget Messages (per session) | 30 messages | 1 minute | Return 429 RATE_LIMIT_EXCEEDED. Show friendly: "You're typing too fast! Please wait a moment." |
| Webhook Ingress (per tenant) | 200 messages | 1 minute | Enqueue normally but flag the tenant for review. |
| Dashboard API (per user) | 100 requests | 1 minute | Return 429. Standard API protection. |
| LLM Token Budget (per tenant) | Configurable monthly cap | 30 days | Return 402 CREDIT_EXHAUSTED. All sessions auto-escalate to human_escalated. |