Multi-tenant isolation
Every Vorel customer (“tenant”) shares the same Postgres database. Cross-tenant data leakage prevention is a 4-layer defence:Postgres Row-Level Security (RLS)
Every tenant-scoped table has a policy: rows are visible only when the row’s
tenant_id matches the current request’s tenant context. The Vorel app connects as a non-superuser database role. Forgetting to set the tenant context returns zero rows: fail-closed by design.Transaction-scoped tenant context
Every tenant-facing route opens a Postgres transaction and sets the current tenant context as the first statement. Exits cleanly on commit/rollback.
Operator-side admin lint
Operator-console reads use a separate database client that bypasses RLS by design (it’s used for cross-tenant operator views like the tenants list). Every such query MUST filter by tenant explicitly. Our CI runs a static check that flags any cross-tenant query without an explicit tenant filter and blocks the PR. Legitimate cross-tenant queries (e.g. listing all tenants) need an explicit annotation explaining why.
Encryption
| Layer | Mechanism |
|---|---|
| At rest (database) | Disk-level encryption (managed by our hosting provider) |
| At rest (cache/queue) | Disk-level encryption (managed by our hosting provider) |
| In transit (internal) | TLS within the private hosting network |
| In transit (public) | Edge-terminated TLS at app.vorel.ai (HSTS max-age=63072000; includeSubDomains; preload) |
| CRM credentials | AES-256-GCM, 12-byte random IV per row, master key from a secret-store environment variable. A KMS-backed envelope-encryption path (per-row DEK + KMS-wrapped KEK) is implemented in code but dormant today; it activates when KMS is configured. |
| Webhook outbound | HMAC-SHA256 signed bodies (X-Webhook-Signature) + TLS to your URLs |
| Tenant API keys | scrypt-hashed at rest (Node native, N=2^14, r=8, p=1, 64-byte derived key, per-key 16-byte salt); 58-char vapk_live_<48 hex> issuance shape; plaintext shown once at creation |
| JWTs | HS256 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 vendor cost-of-goods event (voice, telephony, and model spend per call) AND every chargeable event (per conversation, per minute). Immutable for accounting integrity.
pci_vault_redirect_issued / pci_vault_redirect_completed / pci_vault_redirect_failed (PCI vault-redirect payment flows), pci_enable_toggled / pci_vendor_changed (per-tenant PCI configuration), crm_as_sor_enforcement_disabled (per-tenant CRM-write enforcement override), and impersonation_started / impersonation_ended (operator view-as-tenant sessions).
Rate limiting
Vorel enforces a layered rate-limit stack to defend against floods, accidents, and runaway integrations:| Layer | Limit | Where |
|---|---|---|
| Per-IP webhook | 500 req/min | /api/webhooks/* (before signature verification) |
| Per-authenticated-user | 200 req/min | All authenticated dashboard surfaces |
| Per-API-key | 200 req/min | Each issued vapk_* key has its own bucket |
| Per-(tenant, tool) | 50 req/min | Each internal tool endpoint |
| Per-(tenant, agent-router) | 1000 req/min | The agent dispatch chokepoint |
| Per-tenant aggregate | 5000 req/min | Across all authenticated surfaces |
| Per-customer-number (chat) | 30 req/min | WhatsApp inbound, post-payload-parse |
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 ofconversations + messages + leads + appointments + offerings + knowledge_base + audit_logfor a single tenant. Default redaction: customer email + phone redacted; opt-out viainclude_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.
SLOs
We maintain a set of 5 service-level objectives:| SLO | Target |
|---|---|
| Tool surface success rate | 99.9% |
| Voice call success rate | 99.5% |
| Voice turn p95 latency | < 3s |
| Operator console availability | 99.9% |
| Outbound webhook delivery | 99.0% (within 6 attempts; backoff ladder [60s, 300s, 1800s, 7200s, 43200s]) |
Observability + incident response
- OpenTelemetry tracing exported to a managed observability backend for distributed tracing + custom metrics dashboards.
- Centralized error monitoring for uncaught exceptions across web + workers.
- Structured JSON logs with W3C trace-context propagation across every cross-process boundary (web, workers, and workflow automation).
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.
Compliance posture
| Standard | Status |
|---|---|
| PDPL (UAE Personal Data Protection Law) | Designed to support PDPL obligations: PII inventory + DPA template + automated right-to-access + erasure paths are all live. Not a certification (PDPL has no certification scheme); the DPA template requires counsel review before signing |
| GDPR | Aligned with the same data-subject-rights paperwork posture. EU regions are captured as declared posture (tenant.region='eu'); per-region storage routing is on the roadmap, so EU-region data is hosted in our US region today and disclosed via the DPA |
| SOC 2 | Not yet certified. Foundational evidence is in place (PII inventory, data-flow diagram, incident-response playbook, control matrix, SLOs). SOC 2 Type 1 is targeted for Q4 2026, contingent on selecting and onboarding a compliance-automation platform (founder decision pending). A Type 2 follow-on may follow for enterprise customers |
| PCI | SAQ-A: outsourced vault-redirect payment processing; PAN/CVC/expiry never touch Vorel infrastructure; CI lint gate refuses DTMF imports on payment paths. See Payments + PCI |
| HIPAA | Not turn-key; available only via per-customer engagement (BAAs with all PHI-touching vendors). The clinic vertical pack ships with safety guards (forbidden_phrases='diagnose') but a HIPAA-grade workflow requires a separate engagement |
| CRM-as-SoR | Architecturally enforced: class-(c) transcript + PII tables purge on policy-locked TTL windows after CRM-write success. CI hard-fail gate refuses schema PRs that lack a class declaration. See Data retention |
Bug bounty + disclosure
We don’t run a formal bug bounty today. To report a security issue: emailsecurity@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.