Database access governance — every query reviewed before it runs
AccessFlow is a proxy that every database query passes through: the query is parsed, risk-scored, routed to whoever should decide, executed under masking and row-level security, and recorded in a log nobody can quietly edit. It is the middle ground between the two ways most teams govern database access — hand out a shared read-only credential and hope, or route everything through a DBA and watch the queue grow. This page follows one query the whole way.
Every query is parsed before it runs
AccessFlow is a full proxy, not a linter bolted onto a client. Nothing reaches the database except through it, and nothing goes through it unparsed. Each submitted statement is parsed first; anything that will not parse is rejected outright rather than passed along and hoped for. From the parsed statement AccessFlow reads the statement type — read, write, or schema change — and the exact set of tables and columns referenced, which is what the schema allow-list is checked against. Matching on what the statement really references rather than on its text is the difference between a rule you can rely on and one that a comment or an alias can walk straight past.
The same treatment covers the engines that are not plain SQL. MongoDB, Couchbase, Redis, Cassandra, ScyllaDB, Elasticsearch, OpenSearch, DynamoDB and Neo4j each bring their own classifier, and so do the cloud warehouses — Snowflake, BigQuery and Databricks — so a document-store command and a warehouse query reach the same checks by a different route. Server-side scripting and the destructive administrative commands are blocked before review rather than after it. Every engine AccessFlow connects to →
What the proxy enforces at execution
Approval decides whether a query runs; these decide what it is allowed to see when it does.
- Row-level security rewrites the query so a user only ever receives the rows they are cleared for, from policies evaluated against their own attributes.
- Per-table row limits cap how many rows a query may return from a given table, for everyone or for chosen people and teams; a query across several limited tables gets the lowest limit.
- Dynamic masking hides or partially redacts sensitive columns per person, so the same query returns different results to a support agent and to a DBA.
- Data-classification tags (PII, PCI, PHI, GDPR) attach to columns, derive masking automatically, and raise the risk score of any query that touches them.
- Automated discovery samples column data on a schedule and proposes those tags for you — emails, card numbers, SSNs, IBANs and phone numbers are found by local checksum-verified detectors, optionally aided by your AI analyzer, which only ever sees redacted samples. An admin confirms or dismisses each proposal.
Knowing the cost before approving
A reviewer approving a query they cannot estimate is guessing. Every submission carries a pre-flight cost estimate — the database's own EXPLAIN plan plus an exact affected-row count for updates and deletes — shown alongside the request, folded into the AI analysis, and usable in routing rules such as "escalate any full-scan DELETE over 100k rows". A dry-run preview shows a statement's impact without changing data; on the cloud warehouses it reports the bytes the query would scan, which is the number the bill is based on. Every executed query is kept as an exact snapshot that can be replayed in a test environment afterwards. Datasource, masking & row-security docs →
Risk-scored before a human sees it
An AI analyzer reads every query before it reaches a reviewer and returns a 0–100 risk score, missing-index detection, anti-pattern flags, and concrete fixes offered as a draft edit. The point is not to replace the reviewer — it is to make the queue triageable, so the twenty routine reads are visibly routine and the one unbounded delete is visibly not.
The provider is chosen per organization: Anthropic, OpenAI, Ollama, any OpenAI-compatible backend, or Hugging Face. The system prompt is editable, and a RAG knowledge base lets you feed the analyzer your own house rules so it flags what your team actually cares about. Configurations can be marked as an ordered fallback pool — typically a local Ollama behind a cloud model — so analysis keeps working when the primary provider is unreachable, which is what makes an air-gapped deployment practical.
More than one opinion
Several models can run on the same query, with their verdicts combined by weighted, highest-risk, or majority voting — a fast local model alongside a deep cloud one, for instance — and per-model cost and latency charted so the trade-off is visible rather than assumed. Guardrails block configured prompt patterns before a model is ever called. Text-to-query is opt-in: describe what you want in plain language, and the draft comes back in the database's own dialect, still routed through review like anything else.
Signals beyond the single query
Behavioral anomaly detection learns each user's normal pattern and escalates what falls outside it. Separately, a purely statistical model trained on your team's own past decisions shows reviewers the historical approval likelihood of a pending request. That one is deliberately advisory: it is a triage hint for a busy queue and it never approves or rejects anything. AI analysis docs → Governed access for AI agents →
Who signs off — and what happens when nobody does
A review plan is attached per datasource and says who must approve what. Chains can be multi-stage, reads can be auto-approved while writes are not, decisions can be taken in bulk, and each queue is scoped to the right team by user or by group. One rule holds everywhere and is enforced in the workflow service rather than the UI: nobody approves their own query, regardless of role.
Routing policies sit in front of the plan and decide where a request goes without anyone touching it — auto-approve, auto-reject, escalate, or demand extra approvals, keyed on query type, AI risk, who is asking, time of day, and where the request came from. Standing just-in-time grants can pre-approve queries within their capability and table scope, but they lose to any matching routing policy and are suppressed on high or critical risk, on an open anomaly, and whenever the SQL fails to re-parse.
For the rules that do not fit a form, point AccessFlow at your own policy service — an Open Policy Agent sidecar or an in-house API. It is asked about every request no routing policy matched and can allow it, escalate it, demand more approvals or reject it, but never approve it; if it is slow, down or answers with anything AccessFlow cannot verify, the request goes to a person. Requests and answers are signed both ways, and the SQL text is only sent if you switch that on.
/admin/routing-policies — rules are evaluated in priority order, and the first match decides where the request goes.See what a rule would do before you save it
A policy is easy to write and hard to predict: the condition that blocks one dangerous DELETE can quietly block a nightly job nobody remembered. So every routing, masking and row-security form has a Simulate button. Choose a date range and AccessFlow replays your own past queries twice — once against the rules you have today, once with the draft added or replacing the one you are editing — and reports the difference between those two runs: how many past requests would take a different path, which ones, and which people would feel it.
It compares rules against rules rather than against history, because a past decision may have come from an emergency, a ticket or a standing grant the draft has no say over. And it is strictly a preview: nothing is saved, no query is re-run, and AccessFlow never connects to your database to produce it. Where the replay has to approximate — memberships are read as they stand today, and a couple of engines cannot work out a row rule offline — the result says so plainly, and lists those queries as unclassifiable rather than counting them as unaffected. Simulating is optional; nothing blocks you from saving. Routing policy docs →
Ask why, instead of probing production
The simulator answers "what would this rule change?"; the effective-access explainer answers the other question an admin actually gets asked — "why did this request go where it went, and what would that one do?". Describe a hypothetical request and AccessFlow replays it through the very evaluators that decide real ones, returning a trace with one entry per stage: the datasource and quota gates, the merged permission with every contributing grant named, each routing policy in priority order including the ones that nearly matched, the review plan, who could approve, and the row-security and masking that would apply. Nothing is written, run or sent to an AI provider, and the things a hypothetical request cannot know are named in the answer rather than guessed. The same trace exists for governed API calls and for deployments — where it can say whether a release at a given moment would be held by a freeze window, computed by the very function the pipeline blocks on.
The reverse index comes with it: for one table, everyone who could write to it and every source that grants it — a direct row, a group, a just-in-time grant, break-glass, or a query-admin role that bypasses per-datasource permissions and would otherwise appear on no screen at all.
Rules that are written down
An AI verdict is a judgement and a routing policy is a single yes-or-no; neither tells an author, while they are still typing, that their SQL breaks a rule the organisation has actually agreed on. A catalog of eighteen named checks does — a DELETE with no WHERE, an always-true WHERE 1 = 1 that would defeat that check, SELECT *, an unbounded read, a cross join, a leading-wildcard LIKE, DROP and TRUNCATE, a banned function, a protected table named by pattern, a data change outside a transaction, and risky migrations such as a NOT NULL column added without a default or a PostgreSQL index built without CONCURRENTLY. Each runs at a severity you set per environment — off, warn, or block — so production can be strict while development stays quiet, and the findings appear in the editor as the query is written, with no AI call and no cost.
Block is deliberately not reject. A blocking finding switches off every path that would have approved the query without a person — a routing auto-approve, a standing grant, a plan that waves reads through — and puts a reviewer in front of it; it never turns a query down on its own, and it never softens a routing rule that rejects. The check runs at submission, before the AI is asked, so it still holds on a datasource with AI analysis off or a provider that is down. SQL review rules docs →
Reviews that stall
The failure mode of an approval workflow is silence, so both remedies are built in and both are notify-only — waiting never changes who is allowed to approve.
- Out-of-office delegation hands a reviewer's duty to a named colleague for a set window, across queries, API calls and grouped requests. Every decision records both names, a delegate can never act on the delegator's own requests, and delegation never grants a permission the delegate does not already hold.
- Escalation windows and nudge cadences are set per review plan. AccessFlow raises a stalled request to the reviewers it is actually waiting on and to your admins, and reminds them, well before the approval timeout auto-rejects it.
Where reviewers actually decide
Notifications go out over email, Slack, Discord, Telegram, Teams, PagerDuty or webhook. ServiceNow and Jira ticketing opens an incident when a query is rejected, escalated, or times out, and a ticket resolution can sync back as the decision. Reviewers can approve straight from Slack, or with one tap on a mobile push behind a step-up check. While a query waits, the reviewer and the submitter can co-author it live — and any edit re-enters review, so an approval is never inherited by different SQL.
Emergencies
Break-glass exists because the alternative is a shared credential in a password manager. It is gated by a per-user, per-datasource permission that everyone needs including admins; it runs the query immediately, still through the allow-list, masking, row security and row caps; and it pays for that with an instant fan-out to every org admin, a prominent audit entry, and a mandatory retro-review that an admin — never the submitter — must acknowledge. Review workflow docs → Notification docs →
Several steps, one approval
A migration is rarely one statement. A grouped request bundles ordered members — queries across different datasources, calls to governed API connectors, or both — into a single element that is reviewed, approved and executed as a unit. The builder lets you add steps, drag to reorder them, and read a per-step AI risk preview beside an aggregate badge for the whole group.
Bundling never weakens a member's policy. Each member is validated against your permission for its own target, a break-glass group requires the break-glass grant on every target rather than one, the required approvers are the union across all member plans, and the group is approved only when every member's plan is satisfied. You still cannot approve your own group.
One property is worth stating plainly rather than discovering: there is no distributed rollback. On execute, members run in order and the first failure stops the run, skipping the rest — or, if you chose continue-on-error, the remainder still run. Either way an approved group is not atomic, and members that already applied stay applied. The UI says so before you submit. Each member records its own snapshot and audit row alongside the group-level audit, and progress is streamed live while the group runs. Grouped request docs →
When data should be retired
Access governance answers who may read a row. It says nothing about whether the row should still exist. Retention and erasure rules close that gap: per datasource, target a table, a column set, or a classification tag, give it a retention window plus arbitrary conditions — a structured, parameter-bound predicate builder, with a parser-validated raw WHERE clause as the escape hatch — and choose an action: hard-delete, soft-delete, or pseudonymize. Rules run on an optional cron schedule through a clustered scan job, with a dry-run preview first and execution through the proxy like any other statement.
A GDPR or CCPA right-to-erasure request takes the same configuration and adds people to it: AI-assisted scope detection proposes what is in range, and the request goes through review-plan-based peer review — reviewer-eligible, multi-stage, no self-approval, auto-rejected on timeout — before anything is removed.
Enforcement is at the proxy and therefore transparent to the caller: soft-deleted rows stop appearing in reads, a DELETE against them becomes a marker update, and aged PII resolves to an irreversible salted hash at read time, so aggregates survive while the personal data does not — pseudonymization is enforced on the way out rather than by a destructive batch write. A completed erasure request writes a tamper-evident proof-of-deletion audit row naming the affected rows, tables and method; a scheduled retention run writes its own execution record. Data lifecycle docs →
Which team is this for
The same checkpoint answers a different question depending on who is standing at it — a platform team removing shared credentials, an on-call engineer who needs production access at 03:00, a DBA drowning in review requests, or a privacy team that has to prove erasure happened. Platform engineering, SRE and on-call, DBAs and data platform, and privacy and data protection each get the long version. For where the credentials live and how the audit chain is built, see the security architecture.