Security & compliance

Built for brokerages that take compliance seriously.

Per-brokerage isolation, encryption at rest, audit log on every action, and a permission matrix tested in CI. The security model is opinionated and visible — not hidden behind a "trust us" page.

Brokerage isolation

RLS at the database — no shared queries possible

Postgres Row-Level Security enforces tenant_id on every read and write. Even an admin connecting directly to the DB cannot see another brokerage's data without explicitly switching context. Audited in CI: cross-brokerage deep-dive runs every PR.

  • Postgres RLS on every brokerage-scoped table
  • 3 DB roles: app (RLS-bound), admin_bypass (audited), migration
  • rls-bypass-attempts.sh runs in CI
  • Cross-brokerage deep-dive tested every PR
psql · cross-tenant probe
Session
SET app.user_id = 'user_A';
SET app.tenant_id = 'tenant_1';
Query — trying to read tenant_2's lead
SELECT * FROM lead
  WHERE tenant_id = 'tenant_2';
RLS blocked
(0 rows)
policy: tenant_isolation · tenant_id = current_setting('app.tenant_id')

Encryption

AES-256-GCM for secrets and OAuth tokens

API keys, OAuth tokens (Google, FUB, BrokerMint), webhook secrets — encrypted at rest with AES-256-GCM. Keys are scoped per brokerage and never logged. Encryption key rotation supported without downtime.

  • AES-256-GCM at rest for all secrets
  • TLS 1.3 in transit (no exceptions)
  • Per-brokerage encryption boundaries
  • Key rotation tooling included
tenant_secret · stored shape
plaintextnever
sk_live_abc123…XYZ
at restaes-256
aes256:n9X2…:Pf8bQ2…
algo: AES-256-GCM · nonce 96-bit · per-brokerage keyTLS 1.3 in transit

Audit log

Every action logged — immutable, queryable

lead_event captures every state change, every mutation, every signal extraction with payload-before + actor + batch_id. Rolling back a batch is a single query. Owner sees the full timeline of every lead.

  • lead_event: append-only audit trail
  • payload.before for rollback
  • batch_id groups bulk actions
  • ai_usage_metric tracks every model call + cost
lead_event · append-only timeline1,247 entries
21:03:14
daniel@closi.colead.stage_change
connected → hot_prospect
21:01:47
sara (agent)lead.signal_extract
budget_ceiling: $1.5M
20:58:22
jose@celebrity.colead.note_create
"Showing booked Sat 2pm"
20:54:09
systemcadence.touch
WhatsApp template · hot_followup
payload.before stored · rollback by batch_id supported

Permission matrix

Tested in CI: who can do what, every PR

Three personas (platform_admin, owner, broker_agent) × every resource × every action = a matrix tested in CI. Every PR runs permission-matrix.sh to ensure no privilege escalation slipped in. Owner sees the matrix in /admin.

  • permission-matrix.sh runs in CI
  • Tested for: platform_admin, owner, broker_agent
  • Read + write + RLS coverage
  • Visible matrix in admin UI
permission-matrix · tested in CI15/15 pass
platform_admin
owner
broker_agent
Read own leads
Write own leads
Read other brokerage
Modify cadence
Delete user

What this means in practice

Concrete guarantees — not vague compliance buzzwords.

A broker leaves your team

Disable their account in /app/team. They lose access immediately. Their assigned leads are reassigned. Their audit trail stays.

Two brokerages share Closi

Even if both are on the same DB cluster, RLS makes cross-brokerage reads impossible. Vendors cannot peek either.

Investigating an incident

Every action is logged. Filter by lead, broker, date range — get the full timeline including what the AI did and why.

Compliance audit

NAR / state regulator asks for proof of communications. Export lead_event for any lead, any date range.

GDPR / CCPA request

Lead asks to be forgotten. One endpoint: anonymizes PII, preserves audit log per legal requirement.

Key rotation

Suspect a credential leaked? Rotate encryption keys with zero downtime. All re-encrypted in place.

Security as a feature, not an afterthought

Sign up to see the audit log and permission matrix in your own account.

Security & compliance · Closi · Closi