Skip to content
Projects

Runarks · Governed access to production databases

My own product: every query against a production database is reviewed, approved, run once and logged, and nobody sees a password.

Overview

Runarks is my own SaaS for teams that need to read or fix data in production. Instead of sharing a database password, people raise a request with the statement and a reason, a colleague approves it, and they run it once. I founded it and build it on my own.

Three screens of Runarks with demo data. In front, the Requests list: each request with its title, labels, statement, connection, a status such as draft, awaiting approval, approved or failed, and who opened it. Tilted behind it, the Activity feed of submissions, guardrail checks, approvals and bypass sessions on the left, and the New request form with its connection and labels on the right.
The requests list in front, with the activity log on the left and the new request form on the right.

The problem

Most teams fix production by pasting a query from chat into a database client.

  • No review. Nobody sees the statement first, so one missing filter can change every row.
  • No record. Afterwards nobody can say who ran what, or what changed.
  • Passwords spread. Everyone who ever needed production keeps the credential.
  • Data leaks by accident. A quick SELECT * puts customers' emails and card details on screen.

The solution

A query becomes a request that someone else approves, and the password never leaves Runarks.

Review first. A colleague approves each statement on what it will actually do, and only the requester runs it, once.

A record of everything. Every step is written to a log that can be added to but never edited.

No passwords to share. Runarks holds the credentials and opens the connection itself.

Masked by default. Sensitive fields are masked before rows leave Runarks.

What I did

  • Defined the product and its security model: who may ask and approve, what may run and what is never shown.
  • Designed the executor, the only component allowed to open a connection to a customer's database.
  • Built the analyzer that describes a statement the same way for five database engines.
  • Designed masking that follows the data, and the append-only activity log.
  • Split the code into domains behind contracts, and wrote down each design decision with its reasons.
  • Wrote the skills and agents that the coding agents follow in this codebase.

What it offers

  • Every statement is read firstEach statement is described and checked against grants before anyone is asked to approve it.
  • Two people for every changeReads need one approval and writes two by default, and nobody approves their own request.
  • Writes are measured firstA dry run counts the rows a write will change, and that count caps the real run.
  • Masking that follows the dataSensitive columns stay masked through aliases, functions and joins.
  • No shared passwordsCredentials are encrypted and used only by the executor; nobody is ever shown one.
  • A trail nobody can editEvery step goes to a log that the application and the database both keep append-only.

System design

One web application, with a single executor at its edge and the analyzer in its own process.

Its domains talk only through each other's contracts, and the back office sees metadata only. A queue and a scheduler expire lapsed requests, check connections and send notifications.

How a request moves

Every transition is guarded, and every one writes to the activity log.

There is no path from pending approval straight to executing, and rejecting takes the same permission as approving. Reads needed during an incident use a separate bypass: reads only, and flagged in the log.

One door to production

The credential is decrypted last, and the connection closes as soon as the statement finishes.

Every path to a customer's database goes through the executor, and a test fails the build if anything else opens a connection. The analyzer has no credentials or network, and anything it cannot describe is refused.

Approving what a write will do

A dry run that is always rolled back sees the triggers and cascades a parser misses.

The approver judges the rows a write would really change, and that count is a hard cap when it runs: if more rows change, it rolls back.

Masking that follows the data

Masking rules are written per column but follow the data, so SELECT ssn AS x and CONCAT(first_name, ssn) stay masked. Filtering on a masked column is refused, so nobody can recover a value one LIKE at a time.

In the product

The Requests list in Runarks, showing requests with their SQL, labels, connection, status and who opened them.
Each request carries its statement, labels and status.
The New request form: a title, the statement editor, the connection, a reason for the reviewer and a Submit for approval button.
The statement is checked as you submit it, before any reviewer sees it.
The Activity page: a day-by-day feed of submissions, guardrail checks, approvals and bypass sessions, with a filter for bypasses only.
The activity log can be added to but never edited.