Security and privacy
What we built, and what we do not claim
Call recordings are among the most sensitive data a company holds. This page describes the controls that exist in the product today, as they are described in our architecture decisions, and lists what we do not claim.
Through the layers
Four layers between a recording and anyone who should not see it
Scroll: pass through each layer
Network wall
In the self-hosted package no application container has a route to the internet.
Tenant isolation
The application's authorization matrix and PostgreSQL row-level security, enforced twice.
Personal-data masking
Phone numbers, e-mails, IDs and card numbers become stable placeholders in the transcript.
Retention and deletion
Explicit retention modes and a tombstone-first deletion that removes the bytes.
Masking, simulated
Watch a phone number disappear from the transcript
1 phone number masked
Scroll: the number dissolves
You can reach me at [PHONE_1] after five.
Personal-data masking in transcripts
A workspace can turn on masking. Personal data in the transcript is replaced by stable placeholders such as [PHONE_1], and analysis, search and exports then use the masked version.
What is detected
- E-mail addresses.
- Phone numbers: Iranian mobile and landline in every common spelling, and international numbers with a + or 00 prefix.
- Iranian national IDs, checked by their check digit.
- Bank card numbers, checked with the Luhn rule.
- IBANs, checked with the mod-97 rule.
- Iranian postal codes, only near a postal-code keyword.
What is not detected, stated plainly
- Names, addresses, dates of birth and license plates.
- Numbers written in words, or split across transcript segments.
- The audio recording itself is not masked. Masking applies to text.
How originals are protected
- Masked versions are immutable. A change in policy creates a new version instead of rewriting one.
- Viewing an original transcript needs a separate owner-only permission and is written to the audit log.
- Each replaced value is stored only as AES-256-GCM ciphertext, with the keys kept outside the database. Without a key nothing original is kept.
- Revealing a single placeholder needs a reason and is audited without the value.
Tenant isolation
Isolation is enforced twice: by the application's authorization matrix and by PostgreSQL row-level security.
- Every query runs under a database role that cannot bypass row-level security. Production start-up refuses a superuser or bypass role.
- Policies are forced, so even the table owner is subject to them. A connection with no tenant context sees no tenant rows.
- Tenant context is bound per transaction from the validated session, job or share token, so it cannot survive connection-pool reuse.
- The backend test suite runs as the restricted role and includes cross-tenant read, write and re-homing tests.
Retention and deletion
Retention has three explicit modes, and deletion is designed to actually remove data, not only hide it.
- Retention modes: finite (1 to 3650 days after ingestion), indefinite (only an explicit deletion removes data) or immediate (eligible as soon as processing ends). Ambiguous values are refused, never guessed.
- A legal hold overrides retention. Retention never removes a held conversation.
- Deletion is tombstone-first: access is denied at once, share links are revoked, and a durable purge then removes transcripts, analyses, evidence, summaries and media bytes in retried attempts.
- What remains is a non-content receipt: ids, counters, timestamps and an error code. Titles and file names are emptied.
- Tombstones are journaled outside the database so a restored backup re-applies deletions.
- A presigned download URL issued before a deletion stays valid until its own short expiry or until the bytes are removed. We say this instead of promising instant revocation.
Self-hosted: no route to the internet
In the on-premise package, denial is enforced at the application layer and at the network layer.
- No application container has a route out. Application networks are internal; only nginx publishes a port.
- Optional outbound HTTP(S) goes only through a deny-by-default proxy that forwards to the exact host and port lines of an allowlist you control. The air-gapped default runs no proxy at all.
- At the application layer, a networked AI adapter can only be called under a permit granted for that exact policy-approved endpoint. Without it the request is refused before any connection.
- Cloud-metadata addresses, link-local, loopback and private addresses are refused for cloud endpoints. Private endpoints on your LAN are approved explicitly.
AI providers and routing
Which model sees your data is a policy decision recorded with every run.
- Each workspace has an AI policy: allowed endpoint kinds, regions and models per stage, and every model passes a license check. Candidates are filtered by the policy before anything else, and a refused candidate is never called and never a fallback.
- If speech to text is refused, the attempt fails with the reason and nothing is sent. If analysis is refused, the stage is reported unavailable and the transcript is kept.
- A cloud analysis endpoint receives the transcript masked with every supported class by default, even when the workspace shows originals.
- Permission for text never covers audio. Cloud speech recognition needs its own stage policy.
- A failed local model is retried on the same endpoint. Cloud credentials present on the server are never used for a local-only workspace.
Access and authentication
- JWT session tokens on signed-in API routes, checked against a live session; passwords hashed with Argon2.
- Role-based access with owner, admin, member and viewer roles, plus team supervisors and agents.
- Rate limits per route, for example on sign-in, uploads and API keys.
- Google sign-in with PKCE and ID-token checks, next to e-mail and password.
- A global exception handler so stack traces are never returned to a client.
What we do not claim
We publish tested behavior only.
- No ISO 27001 or SOC 2 certification or attestation is claimed.
- No penetration-test report is published.
- No uptime SLA is published.
- Masking is not anonymization. Sensitive facts can remain in the text of a conversation.