introspect (Tier-1 — live read-only)
Tier 0 reads files. Many checks need runtime truth a file cannot give: real index usage, FK-without-index joins, table/row sizes, RLS state, extensions, engine version, autovacuum/wraparound state. introspect is the only path to that, and it is read-only by contract. See references/data-tiers.md.
Connection path (prefer MCP)
- DB MCP server (preferred) — cleaner permission boundary. Use only read-class tools whose names match
*query* / *read* / *list* / *describe* / *schema*. A generic write-capable query tool is routed through the same read-only validator below; a PreToolUse hook on mcp__.* backs this up. List available tools first; never call a tool that mutates.
- Else a least-privilege read-only connection string read from
$DATABASE_URL in the environment — never echoed, never written into a finding. redactSecrets() scrubs any credential that reaches a finding, report, or log.
- Neither available → emit
status: needs_api on every dependent check with the reason and the minimal grant needed. Never silently pass and never fabricate row counts, sizes, or plans.
Read-only contract (enforced before any query)
SET default_transaction_read_only = on;
- a bounded
statement_timeout (e.g. SET statement_timeout = '15s';).
- only
SELECT / EXPLAIN (no ANALYZE write side effects beyond plan) / catalog reads. Reject anything else.
Catalogs read (no row data)
- Postgres:
version(), information_schema, pg_catalog, pg_class, pg_index, pg_indexes, pg_constraint, pg_policies, pg_extension. Tier-2 adds pg_stat_user_indexes / pg_stat_user_tables, pg_stat_statements, age(datfrozenxid).
- MySQL/MariaDB:
information_schema (TABLES, STATISTICS, KEY_COLUMN_USAGE), SHOW ENGINE INNODB STATUS (read).
- Mongo:
db.stats(), $indexStats, $collStats (Tier-2).
- Cassandra:
system_schema, nodetool tablehistograms (Tier-2).
Effect on findings
A check answered with live evidence is upgraded to confidence: established and may therefore cap a score (e.g. confirmed int4/serial PK near exhaustion, TXID wraparound imminent, RLS off on a relied-on table). Without live access those same checks stay directional or become needs_api — they never cap on static signal alone. Every finding still conforms to schema/finding.schema.json; verification.reproduce references $DATABASE_URL, never a literal credential.
Output
Return the catalog facts the calling auditor needs (index inventory, RLS state, version, sizes) plus a clear note of which checks remain needs_api and why. Respond in the user's language (EN/ES).
1---2name: introspect3description: Tier-1 live read-only introspection. Connects to the running database through a DB MCP server's read-class tools (preferred) or a least-privilege read-only connection string, reads catalogs only (no table data, no writes), and upgrades affected findings to confidence established. Enforces the read-only contract; when no live access is available it marks the dependent checks needs_api — never a silent pass. Invoked by db-orchestrator and the auditors when a check needs runtime truth.4---56# introspect (Tier-1 — live read-only)78Tier 0 reads files. Many checks need **runtime truth** a file cannot give: real index usage, FK-without-index joins, table/row sizes, RLS state, extensions, engine version, autovacuum/wraparound state. `introspect` is the only path to that, and it is **read-only by contract**. See references/data-tiers.md.910## Connection path (prefer MCP)111. **DB MCP server (preferred)** — cleaner permission boundary. Use only **read-class tools** whose names match `*query*` / `*read*` / `*list*` / `*describe*` / `*schema*`. A generic write-capable `query` tool is routed through the same read-only validator below; a PreToolUse hook on `mcp__.*` backs this up. List available tools first; never call a tool that mutates.122. **Else a least-privilege read-only connection string** read from `$DATABASE_URL` in the environment — never echoed, never written into a finding. `redactSecrets()` scrubs any credential that reaches a finding, report, or log.133. **Neither available** → emit `status: needs_api` on every dependent check with the reason and the minimal grant needed. Never silently pass and never fabricate row counts, sizes, or plans.1415## Read-only contract (enforced before any query)16- `SET default_transaction_read_only = on;`17- a bounded `statement_timeout` (e.g. `SET statement_timeout = '15s';`).18- only `SELECT` / `EXPLAIN` (no `ANALYZE` write side effects beyond plan) / catalog reads. Reject anything else.1920## Catalogs read (no row data)21- **Postgres:** `version()`, `information_schema`, `pg_catalog`, `pg_class`, `pg_index`, `pg_indexes`, `pg_constraint`, `pg_policies`, `pg_extension`. Tier-2 adds `pg_stat_user_indexes` / `pg_stat_user_tables`, `pg_stat_statements`, `age(datfrozenxid)`.22- **MySQL/MariaDB:** `information_schema` (TABLES, STATISTICS, KEY_COLUMN_USAGE), `SHOW ENGINE INNODB STATUS` (read).23- **Mongo:** `db.stats()`, `$indexStats`, `$collStats` (Tier-2).24- **Cassandra:** `system_schema`, `nodetool tablehistograms` (Tier-2).2526## Effect on findings27A check answered with live evidence is upgraded to `confidence: established` and may therefore cap a score (e.g. confirmed `int4`/serial PK near exhaustion, TXID wraparound imminent, RLS off on a relied-on table). Without live access those same checks stay `directional` or become `needs_api` — they **never** cap on static signal alone. Every finding still conforms to `schema/finding.schema.json`; `verification.reproduce` references `$DATABASE_URL`, never a literal credential.2829## Output30Return the catalog facts the calling auditor needs (index inventory, RLS state, version, sizes) plus a clear note of which checks remain `needs_api` and why. Respond in the user's language (EN/ES).