Skip to content
All work
OPEN SOURCE · MITSOLO BUILD

DATA DESK 

A database investigation platform for QA engineers. Stop writing the same SQL every day. Search an ID, follow the data, prove the bug.

  • Python
  • Streamlit
  • MySQL
  • Gemini API
  • Playwright
  • PyTest
  • Docker
  • Caddy / Nginx
ROLE
Solo: product, build, tests, docs, deploy
TIMELINE
Sep 2026 → ongoing
STATUS
Open source, self-hosted
01

The problem

QA work means proving things about data, and proving things about data meant writing the same exploratory SQL over and over: finding which table holds a record, chasing foreign keys by hand, screenshotting results into Jira. Every investigation started from zero.

02

What I built

NO-SQL TABLE FINDER

Type a table name, a topic or a column and it finds the right table through aliases and schema search. The feature that started the whole thing.

START AN INVESTIGATION

Paste any identifier and it searches the whole database for it, then drafts an investigation plan.

RECORD EXPLORER

Linked tables, a relationship graph and a timeline for a single record, built by following the foreign keys.

API vs DATABASE VALIDATION

Compare an API response against the database and against expected values, with response rules. This is the part no SQL client does.

BUG EVIDENCE GENERATOR

Turns a finished investigation into a masked Jira or Markdown bug report from a template, with SQL, API and expected-vs-actual included.

CHECK LIBRARY + QA DASHBOARD

Reusable data-quality checks with severity, tags and owners; runs recorded over time with change detection, trends and TXT/CSV/XLSX exports.

SQL WORKBENCH

Named parameters, query library and history, an EXPLAIN analyzer and plain-English 'in short' summaries of what a query does.

AI ASSIST (GEMINI)

Table suggestions, natural-language-to-SQL, explain and fix. Only table and column NAMES are ever sent to the model, never a single row of data.

03

The part I'm proudest of

It physically cannot write to your database.

Read-only isn't a policy here, it's two independent layers: a statement guard that rejects anything that isn't a read, including comment-smuggled SQL, LOAD_FILE and locking reads, and a MySQL SET SESSION TRANSACTION READ ONLY backstop underneath it. If the guard were ever bypassed, the database itself refuses the write. I verified it at the engine level: error 1792.

04

Built like a product, not a script

Accounts with admin approval, a one-time setup code for the first admin, role-based masking of emails and phones, secret columns hidden by name, and login lockout after 5 failed attempts.

384 passing tests plus real-Chrome Playwright end-to-end suites: 20/20, 15/15 and 32/32 green.

Hardened for the open web: privacy policy and terms, cookie notice, XSRF on, CORS off, and Caddy/Nginx configs with HTTPS redirect, HSTS and security headers.

A real docs folder (user guide, configuration reference, architecture, security, scheduler and development notes) so someone else can actually run it.

05

Engineering decisions

  • GENERIC BY DESIGN

    The core knows nothing about my employer's schema. Every company-specific check, chart and note lives in a git-ignored local/ folder, which is why it could be published at all.

  • CONNECTION POOLING

    The same query went from 0.87s to 0.08s. Charts and heavy panels build only when you ask for them, because in Streamlit a collapsed panel still runs its code on every rerun.

  • SCHEDULED CHECKS WITHOUT A SERVER

    A headless runner you point cron at, so data-quality checks keep running whether anyone has the app open or not.

06

The result

automated tests
384
tools in one app
8
writes to your database
0
faster queries after pooling
10x

Built for my own QA work, then made generic enough that anyone with a MySQL database can point it at theirs.

Architecture · DATA DESKOpen full size(opens in a new tab)

No public demo, because it connects to a database, so you self-host it. Setup is two commands, in SETUP.md.

Previous project

INTERNAL PORTAL & REPORTING AUTOMATION

Next project

OPTI-RECOURSE

Need something like this? →