Built for the operations security team.
Isolation is a property of the architecture, not a configuration toggle. Every mutation, every read, and every secret access is governed by policies enforced in the database — so the security model holds even when application code has bugs.
Security pillars
The nine controls that govern every tenant read, write, and secret access on Clarity Stream.
No inbound firewall rules. Agents originate mTLS to our edge, identified per device. NAT-friendly, dynamic-IP friendly, zero public DB exposure.
TLS 1.3 in transit on every hop. AES-256 at rest on managed Postgres and object storage. Per-tenant key wrapping for sensitive extras with rotating KMS keys.
Every read model is row-level-secured on tenant_id in Postgres — not in app code. Policies are verified by automated tests and the Supabase security linter on every migration.
tenant_webhooks.secret, helium_integrations.webhook_secret, and mqtt_username are revoked from SELECT for all app roles. Only service roles can read material.
Owner, admin, and operator roles enforced via has_tenant_role() in policies for connectors, data sources, and notification preferences.
Cross-tenant rollups can only aggregate keys explicitly allowlisted by ops. No accidental leakage through fleet-wide aggregates.
Every connector change, role assignment, and data-source mutation written to audit_log with tenant scoping and configurable retention.
Failed messages land in a tenant-scoped DLQ with hashed payloads and resolution tracking. Operators can replay without exposing raw secrets.
Supabase security linter and bespoke policy tests run on every migration. Findings are tracked alongside the codebase, not in a side channel.
Security guarantees
Properties of the system that hold by construction. If any of these breaks, it's a P0 — not a configuration drift.
- No cross-tenant reads, ever
Database-enforced via RLS on every read model. An authenticated user cannot construct a query that returns another tenant's rows — even by mistake.
- No inbound database exposure
Customer databases stay behind their firewall. Our connector agent originates the connection outbound over mTLS.
- No plaintext secrets in API responses
Webhook signing keys, MQTT credentials, and integration tokens are column-level revoked. The UI never has access to read them back.
- No silent role escalation
Tenant role changes flow through has_tenant_role() and are written to audit_log atomically with the change.
- No anonymous writes
Every mutation requires an authenticated session bound to a tenant membership. Anonymous role has no INSERT, UPDATE, or DELETE on tenant tables.
How it's enforced
We push security into layers that fail closed — Postgres policies, mTLS device identity, and an immutable audit trail.
tenant_id columns + RLS + SECURITY DEFINER helpers (is_tenant_member, has_tenant_role) keep the security model inside the database where it can't be bypassed by a buggy handler.
Mutual TLS between connector agents and the edge ingest. Device identity is pinned per install; rotation is automated.
audit_log captures the actor, tenant, action, and before/after snapshot for every connector, data source, and notification change.
Security documents
Download the latest public versions. The full Trust Center has the complete index plus our SOC 2 request flow.
Controller/processor terms, sub-processors, SCCs, and TOMs.
Components, tenancy model, network design, residency, and SLAs.
Severity, response timeline, customer notification SLAs, post-mortem process.
In progress. Controls mapped to CC1–CC9.
Planned for the year following SOC 2.
Available on Enterprise with a signed BAA.
Responsible disclosure
Found something? We publish our vulnerability disclosure policy and respond to reports within one business day. No-test-on-prod, safe-harbor language included.