Skip to content

Architecture

Rowsel has two components: a Flutter app and a Rust proxy. The proxy is the execution boundary between the device and the database.

Device (Flutter) Rust proxy Your database
───────────────── ───────────── ───────────────
credentials in ──HTTPS──▶ read-only policy ──▶ PostgreSQL
secure storage page-size limits (direct, or via
virtualized grid ◀──────── statement timeout ◀── SSH bastion)
error normalization
  1. The app sends the connection details and SQL to the proxy over HTTPS.
  2. The proxy opens a short-lived connection (or a pooled session), runs the statement under guardrails, and returns a normalized JSON response.
  3. Credentials are used only for the duration of the request or session — the proxy does not persist them.

Built in Rust with axum (HTTP), sqlx (PostgreSQL), russh (native SSH tunneling), and a SQL parser for statement classification. It is stateless for stateless requests, and keeps a small session registry for pooled connections and transactions.

Guardrails enforced at the proxy boundary:

  • Read-only classification — the query endpoint accepts a single statement classified as a read. Data-modifying CTEs, SELECT INTO, SELECT … FOR UPDATE, and multi-statement batches are rejected.
  • Read-only transaction — read queries run inside BEGIN READ ONLY with a SET LOCAL statement_timeout.
  • Enforced paging — user SQL is wrapped as a subquery to apply LIMIT/OFFSET, with a default page size of 200 and a maximum of 500.
  • Timeouts and body limits — default 10s / max 30s statement timeout, and a request body cap.

A Flutter app for mobile and tablet: secure on-device credential storage, a virtualized DataGrid, schema explorer, query editor, and local query history. The same proxy contract serves the app and any other client (for example a web tool calling the proxy from inside your network).

See the Proxy API reference for the full contract.