Security Posture

This document describes exactly what insightral reads, what leaves your infrastructure, and how to shut it off immediately. It is written for security and compliance review, not marketing.

Last reviewed: 2026-09-04 — Contact: [email protected]

What we read

The collector executes SELECT queries against the system statistics views listed below. No DML, no DDL, no superuser privileges. Your application tables are never queried. Equivalent read-only views exist for all supported engines: Oracle uses V$ dynamic performance views, MySQL uses performance_schema, and SQL Server uses DMVs (Dynamic Management Views) — the same read-only access model applies across all four engines.

pg_stat_activity

Active and idle sessions: state (active, idle, idle in transaction), wait event type, query start time, and elapsed duration. Used to detect long-running transactions, idle-in-transaction pressure, and connection saturation. Literal query text is never stored or forwarded — only aggregate counts and duration thresholds are evaluated.

pg_stat_bgwriter

Background writer and checkpoint counters (buffers_clean, buffers_checkpoint, maxwritten_clean). Used to detect checkpoint pressure and dirty-page write amplification.

pg_stat_replication

Replication slot lag and standby send/flush/replay byte positions. Used to detect replication lag exceeding configurable thresholds. No data payload is read — only byte offsets.

pg_stat_user_tables

Per-table statistics: seq_scan, n_dead_tup, last_autovacuum, last_analyze, n_mod_since_analyze. Used by autovacuum-lag, bloat estimation, and sequential-scan rules.

pg_stat_user_indexes

Per-index statistics: idx_scan, idx_tup_read, idx_tup_fetch. Used to find unused indexes (zero scans) and redundant index pairs.

pg_locks

Lock wait graph: granted, mode, relation OID, transactionid. Used to detect lock contention and deadlock risk. No row-level data values are read — only lock metadata.

pg_database

Per-database statistics: datname, pg_database_size(), transaction wraparound distance via age(datfrozenxid). Used to detect XID wraparound risk.

Required role and grant SQL

Run this block as a superuser on Postgres 15+. The pg_monitor built-in role grants read access to all pg_stat_* views without superuser. No write privileges are granted or required.

CREATE ROLE insightral_readonly WITH LOGIN PASSWORD 'change_me';
GRANT CONNECT ON DATABASE your_database TO insightral_readonly;
GRANT USAGE ON SCHEMA public TO insightral_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO insightral_readonly;
GRANT pg_monitor TO insightral_readonly;

Replace change_me with a strong password from your secrets manager. your_database is the name of the database insightral will monitor.

What leaves your infrastructure

The following fields are the complete set of data that may be transmitted from the collector to the insightral dashboard. Nothing else is sent.

target.fingerprint

SHA-256 hash

Derived from DSN host + schema name + object identifier. The original values are not transmitted. The hash is one-way and cannot be reversed to recover a host, database, or table name.

rule_id

String

A stable rule code such as "PG-I01" or "PG-V03". No customer data is embedded.

severity

Enum

One of: critical, warning, info. Rule-defined. No customer data.

aggregate counts

Integer

Statistical values only — for example, the number of idle-in-transaction sessions, or dead-tuple count as a percentage of live tuples. Raw row data, query text, schema names, and table names are never included.

collector version

Semver string

Used for rule-compatibility checks only.

Never transmitted

  • Query text, SQL literals, or query plans
  • Table names, column names, or schema identifiers
  • Row data or column values of any kind
  • human_label — the plain-text alias you assign to a connection in the dashboard
  • DSN, host, port, or database name
  • Credentials of any kind

Kill switch

Revoking the role or removing its access to pg_catalog stops all data emission immediately. The collector will receive an authentication error on its next polling cycle (default: 60 seconds) and stop emitting findings. No restart is required.

-- Option A: revoke catalog access — stops emission without dropping the role
REVOKE ALL ON SCHEMA pg_catalog FROM insightral_readonly;

-- Option B: drop the role entirely
REASSIGN OWNED BY insightral_readonly TO postgres;
DROP OWNED BY insightral_readonly;
DROP ROLE insightral_readonly;

Option A is reversible: re-granting pg_monitor restores operation without restarting the collector. Option B (drop role) is permanent.

You can also stop emission by halting the Docker container or by revoking the collector ingest token in the insightral dashboard under Settings → Collector tokens.

Network shape

Direction

Outbound only

The collector initiates all connections. No inbound ports are opened on your network by insightral.

Protocol

WebSocket (wss://)

Findings are submitted over an outbound-only WebSocket connection (TLS 1.2+). Plaintext is rejected.

Destination

app.insightral.com (443)

Single destination hostname. You can allowlist this in your egress firewall to limit scope.

Inbound ports

None

No server process on your infrastructure. No webhook receiver. No reverse shell.

Agent process

Container only

insightral runs as a Docker container that you start and stop. Nothing is installed on the database host itself.

Container image

Distroless, non-root, cosign-signed

CGO_ENABLED=0 — statically linked binary. No shell, no package manager, no shared libraries inside the image.

Authentication & access controls

Password reset TTL

30-minute OTP

Password resets use Clerk's email OTP, which expires after 30 minutes. No hardware 2FA (TOTP/WebAuthn) is enforced today — that is on the roadmap for Enterprise tier.

CORS posture

Origin-restricted

API routes under /api/* accept requests only from the dashboard origin. No wildcard CORS. The collector ingest endpoint is server-to-server only and does not set CORS headers.

Row-level security

All tenant tables

Every table that stores tenant data is protected by Postgres RLS. Queries run as insightral_app and must set app.tenant_id for the session; cross-tenant reads return zero rows by policy, not application logic.

Connection DSN encryption

App-layer AES-256-GCM

DSNs are encrypted with AES-256-GCM before persistence (INSIGHTRAL_ENCRYPTION_KEY). This is application-layer encryption, not envelope encryption with a KMS. Enterprise BYOK lets you supply your own 256-bit key; insightral never stores the plaintext key.

Sub-processors

insightral uses the following sub-processors to deliver the service. Each processes data only as described.

ProcessorPurposeData
AWS (RDS, S3, Bedrock)Hosted database, object storage, AI inferenceFinding records, connection metadata (encrypted DSNs), AI prompt context
ClerkAuthentication & user managementEmail, name, session tokens
StripeBilling & subscription managementPayment card data (Stripe-held), subscription status
SentryError monitoring & performance tracingStack traces, request metadata — no finding payloads
PostmarkTransactional emailEmail address, notification content

A DPA with the full sub-processor schedule and EU SCCs Module 2 reference is available on request — email [email protected].

SOC 2 status

Type I

In progress

Target: 2026-07-25 (Day 90)

Type II

Planned

Target: Day 180

Penetration test

Planned

Target: Day 60

To request the current trust report, a DPA, or ask about audit scope, email [email protected]. Vulnerability reports receive a response within 2 business days.