Your leads. Your keys.
We literally can't read your data.
SaudaFlow is the first Indian CRM where staff cannot read tenant data. Not in theory. Not by policy. By construction. This document explains how, in terms your security team can verify.
Why this matters in Indian real estate
Brokers and builders generate the most commercially sensitive data in their pipeline: lead names from 99acres, deal sizes that competitors can't see, owner contacts that tax authorities shouldn't. Most Indian CRMs exploit this aggregated tenant data — that's the unstated norm. Our wedge is being the first credible exception.
We chose a cryptographic boundary, not a policy boundary. Policies depend on people. Cryptography depends on math.
The key hierarchy
Each tenant is bound to a Tenant Master Key (TMK) derived client-side from the owner's passphrase via scrypt with OWASP 2024 parameters (N=32768, r=8, p=1). SaudaFlow servers never see the unwrapped TMK or the passphrase.
Per-record Data Encryption Keys (DEKs) are wrapped by the TMK. AES-256-GCM with authenticated additional data (AAD = tenant_id + record_type + record_id) binds each ciphertext to its logical position. A copied ciphertext cannot be replayed against another record.
The plaintext-window contract
Some integration messages (a WhatsApp webhook, a 99acres lead JSON, a Facebook Lead Ad payload) must arrive in plaintext. The plaintext-window rule defines the only zone where unencrypted tenant data is allowed:
- Lifetime: one request tick, never persisted.
- Logs and traces strip the plaintext body before emission.
- The handler wraps with the integration's DEK before commit and zeroes the plaintext reference.
- Every handler is annotated with a
// PLAINTEXT_WINDOWmarker; a pre-commit lint rule enforces it.
Recovery without escrow
A SaudaFlow tenant is shown a BIP-39 12-word mnemonic exactly once at setup and must echo it back. The mnemonic is the only path back into the data; we hold no copy.
Tenants who want a safety net can opt into Shamir 3-of-5 escrow with custodians of their own choosing (a co-founder, an attorney, a partner firm). Each custodian holds one share. Three are required to reconstruct the TMK. SaudaFlow is not a custodian by default.
What SaudaFlow staff cannot see
What SaudaFlow staff can see (and why)
Each entry is enumerated in code; nothing is left to operator discretion. The Sentry beforeSend filter is unit-tested against PII regression.
Indian compliance posture
- DPDP Act 2023: Data Processor; tenants are Data Fiduciaries. Privacy notice, consent capture, breach notification, and Data Principal Rights workflow are in the product.
- Data residency: all tenant data in Hostinger Mumbai region. No cross-border transfer without explicit tenant consent.
- TRAI: SMS via DLT-registered headers, call recording disclosures rendered in the tenant's chosen language before recording starts.
- GST: GSTR-1 compliant invoices, AB Corp GSTIN on every invoice, tenant GSTIN captured at signup, place-of-supply rules respected.
- RERA: SaudaFlow is a software vendor, not a regulated real estate entity. We do not store agreements as registered records.
Cross-tenant sealed envelopes (builder → broker push)
From v1.1 onward, real-estate builders on SaudaFlow can push leads to their channel partners (brokers) with one click. Doing this naively would break the zero-knowledge contract: if the lead PII were re-encrypted under a key the server could touch, AB Corp staff would become a passive man-in-the-middle. We refuse to ship that. The design that ships solves this with X25519 sealed envelopes — every cross-tenant byte is sealed to the recipient’s public key on the sender’s device, and only the recipient can open it.
8.1 The construction
Each tenant has a long-lived X25519 keypair generated client-side on first unlock. The public key is stored in tenant_keys.pub_key (32 bytes). The secret key is wrapped under that tenant’s TMK with AAD tenant:box-sec:v1 and stored in tenant_keys.wrapped_sec_key. The server never sees the unwrapped secret key.
When a builder pushes a lead to a broker:
- The builder’s browser generates a fresh random
DEK_share(32 bytes, never reused across shares). - PII fields (name, phone, email, notes) are encrypted under
DEK_sharewith AADlead_share:{shareId}:v1. DEK_shareis sealed to the broker’s X25519 public key using HPKE BASE mode: an ephemeral keypair, HKDF-SHA256 key derivation from the X25519 shared secret + recipient’s public key + a context tagsaudaflow:box:v1, then AES-256-GCM with a 12-byte random nonce and AAD bound to the share id. Output layout:ephemeralPub(32) ‖ nonce(12) ‖ ct+tag.- The ciphertext, the sealed DEK, the share id and metadata land on the server as one
lead_sharerow. The server cannot open it — it never possessed the broker’s secret key.
When the broker logs in, their browser fetches thelead_share row, unwraps their own secret key with their TMK, opens the sealed envelope to recover DEK_share, decrypts the PII, and on acceptance re-encrypts the PII under the broker’s ownDEK_data with AAD bound to their newly minted lead id. DEK_share is discarded the moment the lead is persisted under the broker’s key. No long-lived shared key exists between the two tenants.
8.2 Forward secrecy
Each push generates a new ephemeral X25519 keypair (step 3 above). Compromise of the recipient’s long-term secret key after a push does not let an attacker decrypt past pushes unless they also captured the ciphertext at the time of the push. This is the standard HPKE single-shot forward-secrecy property, applied at the share boundary.
8.3 Activity reports go the other way (broker → builder)
When the broker reports back (called, visit booked, qualified, booked…), the event row in broker_activity_event carries only the event type, the share id, and timestamps — no PII. The SSE notification that wakes the builder’s feed is the same. The builder already holds the lead identity in their own encrypted store; they don’t need it re-shipped over the wire.
8.4 What HQ staff can and cannot see, cross-tenant
Even with database superuser access:
- Cannot see: the lead’s name, phone, email, or notes — at any point. Either side of the share, the bytes on disk are ciphertext under a DEK that HQ cannot derive.
- Can see: that builder X pushed some lead to broker Y at time T, share id, expiry, and acceptance state. We use this for billing, churn analytics, and dispute resolution — never for personalisation.
8.5 Independent audit hooks
The construction is implemented in packages/crypto/src/box.ts using @noble/curves for X25519, @noble/hashes for HKDF-SHA256, and @noble/ciphers for AES-256-GCM. The same primitives are used both on the browser (via Web Worker) and in our Node-side seed-and-test pipeline. Unit tests round-trip every code path; the wire format is locked so an auditor can verify a captured envelope offline. See ADR-0020 in the repo for the full design rationale, failure modes considered, and the v1.1 roadmap (per-tenant attribute-based access tokens layered on top of this).