The grain of authorization is wrong
An account can express "this person may connect to this instance", not "they may SELECT, but a DROP goes to approval".
Six ways in, one decision chain. Every statement is judged on its own — allow, block, or send for approval — and every one of them lands in an audit chain that cannot be edited afterwards.
One binary serves both the API and the console · PostgreSQL is its only store
They cannot answer the question that actually matters: should this statement run, here, now?
An account can express "this person may connect to this instance", not "they may SELECT, but a DROP goes to approval".
The ticket gets approved, and the engineer still picks up credentials and runs it themselves. Which statement was approved and which one ran — the system knows neither.
Logs scatter across general logs on each host, and whoever can reach the database can usually reach the log.
All 4 layers are stored per tier, and a tier is not an environment: prod-hk and prod-sh are governed by the tier they both hang off, so adding one more production instance copies no rule rows at all.
dictDenyPROD may not run directly — sent for approvalThis demo runs entirely offline in your browser. The logic is ported from the gateway's decision engine, seeded with the built-in default rule set. Your own deployment can change the dictionary, the capability matrix, or tier properties — the verdicts follow. Multiple statements are split on semicolons and judged one by one: quotes, comments, MySQL executable comments and custom delimiters are all recognised. Whole stored-procedure bodies and similar script forms are out of scope here — judge those with the product itself.
The mechanism and the boundary of each are set out on the how-it-works and security pages.
The decision sits in the function all six entry points call, not on the entry points. A pasted batch is split on semicolons and judged one statement at a time; the batch takes the strictest verdict.
Terminal tickets: approval only authorises, someone still has to come back and press execute, and every gate is recomputed against whoever actually pressed the button. Releases: execution belongs to the pipeline stage. Execution-window and export tickets: no runnable command at all.
A window is itself approved, is scoped to one database, and stops covering the present moment when its time is up. It only downgrades "approve" to "allow"; a deny is never relaxed.
Each record hashes as SHA256(previous hash + payload), and prev_hash carries a single-column unique index — a forked chain is refused by the database rather than by application discipline.
Only two places in the gateway read result rows out of a user’s SQL: terminal results and data exports. Masking hangs on both.
87 built-in rules: 62 are lifted from four internal database standards and cite the clause they came from, and 25 are the platform’s own. It is not a gate: it takes no part in the execution decision.
A UI-level replica rendered from the real rule data, not a screenshot. The approval ticket below has been approved, and the button still says "Execute".
AP-20260919-0042ApprovedSQL
ALTER TABLE orders ADD COLUMN note varchar(64)Rule hit
dictDenyPROD may not run directly — sent for approvalApproval chain
The gateway re-checks the rules before executing — this ticket may have sat in the queue for hours.
Illustrative button, not clickable — approved ≠ executed on your behalf (see the "how it works" page, layer two).
Console UI replica · not a screenshot
The engine dropdown for a new instance is exactly these 7, and the families are drawn by which wire protocol an engine speaks, not by vendor. What gets judged is always what you typed — meta-commands are translated only after the verdict is in.
PolarDB in that list means the MySQL-compatible edition; PolarDB for PostgreSQL speaks the PostgreSQL protocol and belongs to the other family.
A local PostgreSQL has to exist first; the server applies its own migrations on startup.
# First: createdb vela_gateway
cd backend && go run ./cmd/server# Vite proxies /api to :8080
cd frontend && npm install && npm run devIf the migrations do not finish on startup, it exits — a gateway missing half its tables still turns the health check green while every request comes back 500.