← Back

ZeroIndex

A browser-based SQL query tool for engineering teams. ZeroIndex gives your team controlled, audited access to production databases without jump servers, shared credentials, or GUI licenses. It also works with any JDBC-compatible database.

The Problem

Most engineering teams end up with some version of the same setup: a Windows RDP or bastion server living inside the production subnet. You install a database GUI on it, and the team shares it whenever they need to run a query.

It gets the job done, but it creates three compounding problems that usually get ignored until something goes wrong.

Access contention. RDS and GUI licenses usually have tight concurrent session limits. When the team grows beyond that cap, things break down. Engineers end up queuing for sessions during incidents, pinging each other on Slack, and waiting around while the clock ticks.

Subnet exposure. A jump server inside the production subnet is not a scalpel. Anyone with access can call internal APIs by IP, probe adjacent services, and reach infrastructure that has nothing to do with querying a database. The blast radius for a simple SELECT query becomes huge.

No audit trail. Once someone is inside a GUI, there is no record of what they ran. You have no way to reconstruct what data was accessed during an incident, who modified a row, or whether a bad query was accidental or malicious. If you have compliance requirements, this is a hard gap to explain.

ZeroIndex replaces the jump server entirely. It runs as a single pod, locks down access by role and by database, logs every query (even the blocked ones), and exposes nothing but a browser UI.

Architecture

ZeroIndex Architecture

This is a single Spring Boot service deployed on Kubernetes. Every request flows through Nginx for TLS termination, then hits Spring Security which validates credentials against LDAP or Active Directory. Once authenticated, each query passes through a two-layer validation pipeline before it ever touches a database connection. Results return to the browser while an audit record is written asynchronously to a dedicated audit database. The production databases being queried and the audit database are entirely separate. Users cannot reach their own audit records.

There are no REST endpoints, no API surface, and no second application to deploy or secure.

Design Decisions

Single deployment, no API surface

ZeroIndex uses Vaadin Flow. This means the UI and business logic run in the same JVM. There is no separate front-end application, no REST API, and no second deployment to worry about.

A conventional React plus Spring Boot approach would mean two pods, a routable API surface, CORS headers to manage, and an /api/execute endpoint sitting on the network even for an internal tool. With Vaadin, there is exactly one security boundary. Query validation, role enforcement, and audit writes all happen in Java before any result reaches the browser. There is nothing to call directly from outside the application.

Authentication via LDAP or Active Directory

Users authenticate with their existing corporate credentials. ZeroIndex does not manage passwords at all. LDAP authentication runs on a dedicated thread pool so logins never block application threads, and the connection goes over LDAPS:

CompletableFuture<Boolean> ldapFuture = CompletableFuture
    .supplyAsync(() -> authenticateUser(username, password), ldapExecutor);
boolean authenticated = ldapFuture.get();

Two-layer query validation

Every query passes through two independent layers before reaching a database.

Layer 1: JSqlParser (AST analysis). The query is parsed into an Abstract Syntax Tree. This catches structural issues that regex cannot handle like CTEs, nested subqueries, multi-statement inputs, and aliased references. If the SQL cannot be parsed or the statement type is not permitted for the user's role, it gets rejected right here.

Layer 2: Compiled regex rules. Pre-compiled Pattern objects enforce business rules at microsecond speed. This checks that a WHERE clause is present on UPDATE and DELETE, ensures SELECT includes a WHERE, LIMIT, JOIN, or aggregate, and blocks DDL keywords for non-admin roles.

The two layers are complementary. JSqlParser handles structural correctness, and regex handles fast rule enforcement. Both must pass.

Role-based access control

Access is controlled across two independent dimensions.

Per-user database access. Each user has a list of named connections they are allowed to see. If a connection is not in that list, it does not appear in the UI. It simply does not exist for them.

Per-user query permissions. There are three roles:

Role Permitted queries
READ SELECT with WHERE, LIMIT, JOIN, DISTINCT, or aggregate only
WRITE READ + UPDATE / DELETE (WHERE required) + INSERT
ADMIN Unrestricted (bounded by database user privileges)

Blocked queries are not silently dropped. They produce a full audit record with status = BLOCKED so refusals are just as visible as successful executions.

Any database via JDBC

ZeroIndex connects via standard JDBC, so any database with a driver will work out of the box (PostgreSQL, MySQL, MariaDB, Oracle, SQL Server, CockroachDB). Each named connection is just a JDBC URL plus credentials. Swap the driver and URL and you are good to go.

All connections run through HikariCP with a hard 30-second query timeout. Connections are configured as read-only at the database user level as a second line of defense independent of the application's validation layer.

HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl(value.getUrl());
dataSource.setMaximumPoolSize(10);

JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.setQueryTimeout(30);

Async audit log

Every interaction (executed, failed, or blocked) is written asynchronously to a dedicated audit database. The write is never on the critical path of the query response.

Each record stores the user identity, full SQL text, target connection, status, execution time in milliseconds, row count, and the error message if applicable.

UI features built for real use

Outcome

Teams that deploy ZeroIndex typically see three immediate changes. Session limits stop being a bottleneck because concurrent access is no longer license-constrained. The production subnet is no longer accessible via a general-purpose desktop environment. And for the first time, there is a complete, queryable record of every query run against production data (including the ones that were blocked before they reached the database).

Once ZeroIndex is running, you can finally decommission those jump servers.

Who Is This For

Stack

Spring Boot · Vaadin Flow · JSqlParser · Spring Security · LDAP / Active Directory · PostgreSQL · HikariCP · JDBC (any database) · Kubernetes · Nginx · Docker