The infrastructure update
Smart AI on brittle infrastructure is still brittle. We went through every layer and hardened each one.
The API surface now has a proper auth model: role-based access with company_admin and owner scopes, API key management, and PII masked by engagement level in analytics output. Every privileged action writes a fire-and-forget audit event — actor, resource, IP, user agent — to a dedicated security log. Sensitive fields are encrypted at rest with AES-256-GCM.
On the perimeter: CORS is locked to an explicit allowlist, per-route rate tiers run through Upstash Redis, and HTML entity escaping covers all user-facing inputs before they reach the pipeline. A public health-check endpoint returns system status in under 100ms. A secured POST runs synthetic tests across eight groups — booking, chat, Kian, admin, database, integrations, frontend, and the agent pipeline — and persists the snapshot for the status page.
Companion-grade response quality
Kian adapts tone and depth based on conversation phase — early curiosity gets exploration, late frustration gets the direct path to booking.
The planner agent now outputs a conversationPhase signal (exploring | comparing | deciding | frustrated) that feeds directly into the responder's system prompt. Tone shifts from informative to concise to action-oriented as the phase progresses. The reviewer checks tone alignment as one of its five scoring criteria, so off-key responses fail the quality gate before shipping.
Secure admin API with role-based access
Analytics, A/B results, quality metrics, and user management sit behind a scoped API key system with company_admin and owner roles.
The authorization policy enforces four roles — superadmin, admin, company_admin, owner. company_admin and owner are company-scoped: they can only reach their own data. API keys are created per company, validated on every request, and logged to the audit trail on use. PII in analytics output (names, emails) is masked based on the lead's engagement level — cold leads see full masking, converted leads show the real data to admins with the right role.
Live health-check API
A public endpoint returns overall system status in under 100ms. A secured POST runs synthetic tests across eight groups.
GET /api/admin/status/run-checks returns the last cached snapshot — overall status, group breakdown, failure count — with no auth required, so the status page can poll it freely. POST runs a fresh check set: booking slot fetch, chat message round-trip, Kian pipeline ping, admin auth validation, database read/write, integrations health, frontend reachability, and agent pipeline warmup. Each check reports status (operational | partial | outage), latency in ms, and a cleanup status for synthetic writes.
AES-256 encryption and audit logging
Sensitive fields encrypted at rest. Every privileged action writes an audit event with actor, resource, IP, and user agent.
Encryption uses AES-256-GCM with a 256-bit key. The encrypt() output is a JSON envelope with version, IV, ciphertext, and tag — making key rotation and format migration safe. safeDecrypt() handles the plaintext-to-encrypted transition gracefully so existing unencrypted values keep working. The audit log is fire-and-forget: it never blocks the main request path, but every admin action, API key use, and role change leaves a record.
Rate limiting and CORS lockdown
Per-route rate tiers via Upstash Redis. CORS restricted to an explicit allowlist — no wildcards.
Rate tiers: booking and contact at 3 req/15min (abuse-resistant), chat at 10 req/min (conversational pacing), calendar slots at 30 req/min (UI polling), admin at 60 req/min (operational headroom). CORS allowlist: vallit.net, wtm-consulting.de, and preview environments via env override. Any unlisted origin gets a hard rejection before the route handler runs.
Input hardening across all chat paths
HTML entity escaping on every user input. The safety agent blocks PII, prompt injection, and harmful content before the responder sees the message.
The chat message route escapes &, <, >, and newlines into HTML entities before any processing. The safety agent runs a two-phase check: a fast regex pass for obvious PII patterns and injection signatures, then an AI check via gpt-4o-mini for messages over 200 characters. Flagged messages are rejected with a severity level (low | medium | high) and never reach the planner or responder.