Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vorel.ai/llms.txt

Use this file to discover all available pages before exploring further.

Vorel handles customer PII on your behalf. This page documents the technical controls that protect that data — what’s in place today and what’s on the roadmap. For the formal compliance picture (DPA, certifications, regulator-facing language), see Compliance.

Multi-tenant isolation

Every Vorel customer (“tenant”) shares the same Postgres database. Cross-tenant data leakage prevention is a 4-layer defence:
1

Postgres Row-Level Security (RLS)

Every tenant-scoped table has a policy: rows are visible only when tenant_id = current_setting('app.current_tenant_id')::uuid. The Vorel app connects as the vorel_app Postgres role (NOT a superuser). Forgetting to set the tenant context returns zero rows — fail-closed by design.
2

Transaction-scoped tenant context

Every tenant-facing route opens a Postgres transaction and SET LOCAL app.current_tenant_id = '<uuid>' as the first statement. Exits cleanly on commit/rollback.
3

Operator-side admin lint

Operator-console reads use a separate Prisma client (adminPrisma) that bypasses RLS by design (it’s used for cross-tenant operator views like the tenants list). Every adminPrisma.<model>.find* call MUST filter by tenantId explicitly. Our CI runs a static check (apps/web/scripts/lint-admin-prisma.ts) that flags any call without a tenantId filter and blocks the PR. Legitimate cross-tenant queries (e.g. listing all tenants) need an explicit comment annotation explaining why.
4

Append-only triggers

Three tables — messages, audit_log, billing_events — reject UPDATE + DELETE via Postgres triggers. Even if RLS were bypassed, mutation isn’t possible from the application’s database role.
This setup was pressure-tested in a 2026-05-01 internal cross-tenant-leak walkthrough; the static lint shipped same-day to close the failure-mode class.

Encryption

LayerMechanism
At rest (Postgres)Disk-level encryption (Railway-managed)
At rest (Redis)Disk-level encryption (Railway-managed)
In transit (internal)TLS within the Railway internal network
In transit (public)Cloudflare-terminated TLS at app.vorel.ai (HSTS preloaded, 2yr)
CRM credentialsEnvelope-encrypted: AES-256-GCM with per-row data encryption keys, KEK in AWS KMS for production
Webhook outboundHMAC-signed bodies + TLS to your URLs
Tenant API keysbcrypt-hashed at rest (never stored plaintext)
JWTsHS256 over rotated shared secret, short TTL (5 min for tool calls, 2 min for worker calls)

Append-only audit

Three tables capture immutable audit trails:
  • messages — every conversation turn, immutable once written. Underpins the conversation transcript that QA scoring + analytics rely on.
  • audit_log — every operator-console read AND every mutation. Forensic trail for who-saw-what + who-did-what. Includes both operator actions (e.g. “claimed voice number for tenant X”) and reads (e.g. “operator opened tenant X’s CRM-credentials page”).
  • billing_events — every cost-of-goods event (Vapi + Telnyx + Gemini per call) AND every chargeable event (per conversation, per minute). Immutable for accounting integrity.
Append-only is enforced via Postgres triggers — even superuser writes are blocked unless explicitly bypassed via the operator-side vorel role used for retention sweeps + the right-to-erasure path.

Rate limiting

Vorel enforces a 4-layer rate-limit stack to defend against floods, accidents, and runaway integrations:
LayerLimitWhere
Per-IP webhook500 req/min/api/webhooks/* (before signature verification)
Per-Clerk-user dashboard200 req/minAll authenticated dashboard surfaces
Per-(tenant, tool)50 req/minEach of the 14 internal tool endpoints
Per-(tenant, agent-router)1000 req/minThe agent dispatch chokepoint
Per-tenant aggregate5000 req/minAcross all surfaces
Per-customer-number (chat)30 req/minWhatsApp inbound, post-payload-parse
Built on a Redis-backed fixed-window primitive with fail-open posture — a Redis blip admits all traffic rather than 429-everyone. Plan-aware limits land at the call site when our billing model launches.

Right-to-access + right-to-erasure

Vorel ships first-class endpoints for PDPL Art. 15-17 + GDPR Art. 15-17:
  • POST /api/tenant/export (operator-gated) — returns a ZIP of conversations + messages + leads + appointments + offerings + knowledge_base + audit_log for a single tenant. Default redaction: customer email + phone redacted; opt-out via include_full_pii=true (audit-logged). Includes a chain-of-custody README.
  • POST /api/tenant/forget (operator-gated, dry-run-by-default) — runs a 7-step scrub for a single customer phone within a single tenant: tombstones conversations, redacts message content, nulls leads/appointments PII, scrubs audit-log JSONB references. Wrapped in a Postgres transaction for atomicity. Salted-hash audit-log references so the deletion event is provable without re-introducing the PII.
When a customer requests their data or asks to be forgotten, your operator routes the request through these endpoints + provides you with the resulting export ZIP or audit-log reference as proof of deletion.

SLOs

We publish 5 service-level objectives at handoff/docs/SLOs.md (open-source for transparency):
SLOTarget
Tool surface success rate99.9%
Voice call success rate99.5%
Voice turn p95 latency< 3s
Operator console availability99.9%
Outbound webhook delivery99.0% (within 30 retry-window)
A 4-state error-budget policy (healthy / caution / concern / exhausted) drives engineering response — see the SLO doc.

Observability + incident response

  • Honeycomb (OpenTelemetry exporter target) for distributed tracing + custom metrics dashboards.
  • Sentry for uncaught exceptions across web + workers.
  • Pino structured logs with W3C trace-context propagation across every cross-process boundary (web → worker → n8n → web).
  • app.vorel.ai/status — public status page with operator-curated incident banner.
  • Incident response playbook — sev definitions, RACI matrix, 6-phase response, tenant comms templates aware of PDPL Art. 17 + GDPR Art. 33’s 72-hour regulator-notification window. See handoff/docs/security/INCIDENT-RESPONSE.md.

Compliance posture

StandardStatus
PDPL (UAE Personal Data Protection Law)Documented; PII inventory + DPA + automated right-to-access + erasure paths all live
GDPRSame paperwork posture. EU regions captured (tenant.region='eu'); per-region routing on roadmap
SOC 2 Type 1Foundational paperwork shipped (PII inventory, data flow, IR playbook). Drata/Vanta engagement on roadmap
SOC 2 Type 2Requires 6 months of operational data after Type 1
HIPAAPer-customer engagement (BAAs with all PHI-touching vendors). Clinic vertical pack ships with safety guards (forbidden_phrases='diagnose') but full HIPAA is not turn-key
For the formal documents (DPA template, PII inventory excerpt for buyer DD, SLO commitments), see Compliance.

Bug bounty + disclosure

We don’t run a formal bug bounty today. To report a security issue: email security@vorel.ai with details. We’ll acknowledge within 48 hours, triage within 5 business days, and credit you in the changelog once fixed if you’re comfortable with disclosure. We do NOT use Vorel-collected customer data for any purpose other than running your tenant. We do NOT sell, share, or analyse customer data across tenants. We do NOT use customer conversations for training third-party AI models.