Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
4181353
feat(graph): tagged JSON forms for nodes, relationships and paths
cevheri Oct 3, 2026
f538dc3
test(graph): build the id-less node as a literal, no unused binding
cevheri Oct 3, 2026
10d6367
chore(neo4j): pinned 5.26 compose service, original seed graph, neo4j…
cevheri Oct 3, 2026
fe38a3c
docs(neo4j): name the RANGE index that backs the service_id constraint
cevheri Oct 3, 2026
2a30441
feat(graph): Cypher lexer shared by the editor and the read policy
cevheri Oct 3, 2026
40b813e
fix(graph): note the lexer's two known gaps and spell the astral test…
cevheri Oct 3, 2026
b88cd3c
feat(graph): Cypher statement splitter and safe quoting
cevheri Oct 3, 2026
1e21d70
docs(graph): note that statement text relies on the lossless lexer
cevheri Oct 3, 2026
81654e9
feat(graph): fail-closed Cypher read policy driven by an engine profile
cevheri Oct 3, 2026
723c4e4
feat(graph): Bolt transport over neo4j-driver-lite with a fixed, audi…
cevheri Oct 3, 2026
1ade567
fix(graph): read a CALL scope, a called dotted backtick name and CALL…
cevheri Oct 3, 2026
03af5ab
feat(graph): graph object kinds and Cypher generators
cevheri Oct 3, 2026
11cbe84
fix(graph): classify a Bolt session open or close failure and keep a …
cevheri Oct 3, 2026
900d5e3
test(neo4j): pin that each generated statement lexes as exactly one s…
cevheri Oct 3, 2026
396a328
feat(graph): GraphBaseProvider runs every statement through policy, e…
cevheri Oct 3, 2026
4bef865
fix(graph): cancel every run under one query id and close the previou…
cevheri Oct 3, 2026
16d79a8
test(neo4j): live evidence harness and captures from Neo4j 5.26
cevheri Oct 3, 2026
8a3866f
feat(neo4j): engine profile, catalog, statement gate, errors and labels
cevheri Oct 3, 2026
0f93e5b
test(neo4j): measure the cancel through the Bolt client and refuse du…
cevheri Oct 3, 2026
7121a7c
fix(neo4j): read owningConstraint one way in the index listing and rows
cevheri Oct 3, 2026
de8895e
feat(neo4j): read-only Neo4j provider on the graph layer, with monito…
cevheri Oct 3, 2026
90fd69d
fix(neo4j): answer a refused overview listing, count labels one at a …
cevheri Oct 3, 2026
93f4212
feat(neo4j): register the neo4j type-id
cevheri Oct 3, 2026
7a5be52
feat(neo4j): Cypher editor language, completion and generators
cevheri Oct 3, 2026
36ec485
fix(neo4j): insert only the rest of a SHOW form, share the multi-line…
cevheri Oct 3, 2026
8d8e993
build(neo4j): package the Bolt driver for both builds
cevheri Oct 3, 2026
938661b
fix(neo4j): keep the port and tested-series constants file-local, so …
cevheri Oct 3, 2026
d2498f4
docs(neo4j): provider doc, graph layer architecture, backlog
cevheri Oct 3, 2026
4f59b2d
test(neo4j): E2E for the connection dialog
cevheri Oct 3, 2026
a5a4503
test(neo4j): cite #424 and spec E7 in the E2E spec, drop the unmeasur…
cevheri Oct 3, 2026
87db29f
test(neo4j): pin the doc's policy lists as sets, and SECURITY note 3.…
cevheri Oct 3, 2026
e52a458
docs(neo4j): outward copy and chart metadata for the twentieth engine
cevheri Oct 3, 2026
93febcb
docs(neo4j): count the DigitalOcean listing's relative numeral
cevheri Oct 3, 2026
c44751b
fix(neo4j): the Read-only toggle says the connection is read-only eit…
cevheri Oct 3, 2026
d68e06b
test(neo4j): move the factory.ts citations past the neo4j case
cevheri Oct 3, 2026
0869d1e
fix(neo4j): a configured database the server does not hold fails the …
cevheri Oct 3, 2026
91848b2
fix(neo4j): end a Cypher line comment at a carriage return, as the se…
cevheri Oct 3, 2026
5979673
test(neo4j): hold the graph policy registry to every Cypher provider
cevheri Oct 3, 2026
cd18936
fix(neo4j): a cypher fence names neo4j, so other engines' plan runs r…
cevheri Oct 3, 2026
3416418
fix(neo4j): deny GQL's INSERT write clause
cevheri Oct 3, 2026
27a9d0b
docs(neo4j): an allowlisted SHOW form skips the server classification
cevheri Oct 3, 2026
c28cfef
fix(neo4j): the read policy refuses a CYPHER prefix the dialect does …
cevheri Oct 3, 2026
e6696b3
fix(neo4j): deny DEALLOCATE, REALLOCATE and DRYRUN
cevheri Oct 3, 2026
b9ed71b
docs(agent): re-derive the engine counts for the twenty-first type-id
cevheri Oct 3, 2026
2487a10
fix(neo4j): report the labels the table stats skip
cevheri Oct 3, 2026
6db6abc
fix(neo4j): accept the driver's text for a whole-day elapsed time
cevheri Oct 3, 2026
de961a2
test(neo4j): pin the cut label listing's floor and its wording
cevheri Oct 3, 2026
0e30563
test(neo4j): drop the stale cast of the neo4j type-id
cevheri Oct 3, 2026
b7baadc
refactor(neo4j): the graph base takes its transport from the engine
cevheri Oct 3, 2026
1552e1f
test(neo4j): hold the browser-shipped profile files to the pure rules
cevheri Oct 3, 2026
a174150
fix(neo4j): report a property or index read the catalog bound cut
cevheri Oct 3, 2026
b9ebd28
test(neo4j): corpus cases for IN CONCURRENT TRANSACTIONS
cevheri Oct 3, 2026
337f468
docs(neo4j): a cypher fence names Neo4j while it is the only Cypher e…
cevheri Oct 3, 2026
a9c52e5
fix(neo4j): the provider hands the Bolt transport to the graph base
cevheri Oct 3, 2026
0c251a0
test(neo4j): connect without an injected factory runs the Bolt transport
cevheri Oct 3, 2026
f922eec
fix(neo4j): deny CONCURRENT TRANSACTIONS, which a count after IN hid …
cevheri Oct 3, 2026
f6da1f2
fix(neo4j): a running transaction counts as an active session
cevheri Oct 3, 2026
c08bce6
fix(neo4j): plan mode counts the inventory as graph objects, not node…
cevheri Oct 3, 2026
b286560
fix(neo4j): refuse a unicode escape, which the server decodes before …
cevheri Oct 3, 2026
26695e6
fix(neo4j): deny ROWS and TRANSACTIONS as single words
cevheri Oct 3, 2026
54868c4
test(neo4j): the active sessions read reports a running transaction a…
cevheri Oct 3, 2026
eb87051
fix(neo4j): say the denied namespaces apply to every qualified name, …
cevheri Oct 3, 2026
9d0c854
fix(neo4j): name CALL and SHOW used as names cleanly, with the backti…
cevheri Oct 3, 2026
fe800da
fix(neo4j): log a tree listing the catalog cut at its bound
cevheri Oct 3, 2026
d9727e0
fix(neo4j): an overlapping connect awaits the attempt in flight
cevheri Oct 3, 2026
f1b25af
fix(neo4j): the gate puts EXPLAIN after the whole CYPHER option block
cevheri Oct 3, 2026
9534cfe
fix(neo4j): a custom CA works on Windows, and the CA file is created …
cevheri Oct 3, 2026
48d67e9
fix(neo4j): sort completion names and the table-stats labels as a per…
cevheri Oct 3, 2026
23b9735
test(neo4j): the E2E spec signs in as the admin, whose rate-limit buc…
cevheri Oct 3, 2026
2d6bcb2
Merge origin/main into feat/neo4j-provider
cevheri Oct 3, 2026
d4dfeef
refactor(editor): give the Cypher tab its DIALECT_EDITORS record
cevheri Oct 3, 2026
3345488
test(dialect-registry): regenerate the golden outputs with the neo4j row
cevheri Oct 3, 2026
e87c03c
Merge remote-tracking branch 'origin/main' into feat/neo4j-provider
cevheri Oct 3, 2026
6b4c766
test(neo4j): list offersSchemaDiagram's Cypher read on the dialect re…
cevheri Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Guidance for Claude Code in this repo — conventions, rules, and gotchas only.

## Project Overview

Web-based SQL IDE for cloud-native teams: nineteen external engines, plus the embedded LibreDB store (`EXTERNAL_DATABASE_TYPES` in [`src/lib/db/compatibility.ts`](src/lib/db/compatibility.ts) is the external-engine list, never a prose enumeration), plus AI query assistance. It runs **two ways**, as a standalone Next.js app and as a published npm package, and the two render different chrome, so a UI change verified in one is not verified in the other.
Web-based SQL IDE for cloud-native teams: twenty external engines, plus the embedded LibreDB store (`EXTERNAL_DATABASE_TYPES` in [`src/lib/db/compatibility.ts`](src/lib/db/compatibility.ts) is the external-engine list, never a prose enumeration), plus AI query assistance. It runs **two ways**, as a standalone Next.js app and as a published npm package, and the two render different chrome, so a UI change verified in one is not verified in the other.

## Branching & PRs

Expand Down Expand Up @@ -81,7 +81,7 @@ A clean local pass is still not a guarantee: the same job also runs `build:lib`
> - **One directory may serve two type-ids** — `sql/search/` is both `elasticsearch` and `opensearch` (#424). Docs and tests stay 1:1 anyway: the invariant is per type-id.
> - Any change to one side MUST sync the others **in the same PR**. The doc mirrors the code and the code mirrors the doc — never let them drift.

- **DB abstraction:** Strategy Pattern. SQL-dialect providers extend `SQLBaseProvider`; the non-SQL ones (`mongodb`, `redis`, `couchbase`, `prometheus`, `kafka`, `etcd`, `libredb`) extend `BaseDatabaseProvider` directly, and `SQLBaseProvider` itself extends it. Inside `src/lib/db`, never branch on the type id; drive behaviour through capabilities/labels. Three `=== "mongodb"` branches survive in the UI layer as known debt (`src/hooks/use-connection-form.ts`, `src/lib/editor/tab-language.ts`, `src/components/ConnectionModal.tsx`); do not add a fourth.
- **DB abstraction:** Strategy Pattern. SQL-dialect providers extend `SQLBaseProvider`; the non-SQL ones (`mongodb`, `redis`, `couchbase`, `prometheus`, `kafka`, `etcd`, `libredb`) extend `BaseDatabaseProvider` directly, `neo4j` extends `GraphBaseProvider` (`src/lib/db/graph/`), and `SQLBaseProvider` and `GraphBaseProvider` themselves extend it. Inside `src/lib/db`, never branch on the type id; drive behaviour through capabilities/labels. Three `=== "mongodb"` branches survive in the UI layer as known debt (`src/hooks/use-connection-form.ts`, `src/lib/editor/tab-language.ts`, `src/components/ConnectionModal.tsx`); do not add a fourth.
- **Auth:** `NEXT_PUBLIC_AUTH_PROVIDER` = `local` (email/password) or `oidc` (PKCE → the same JWT cookie); `src/proxy.ts` enforces RBAC (admin vs user). [`docs/OIDC.md`](docs/OIDC.md).
- **Storage:** write-through cache — localStorage serves reads, `useStorageSync` pushes mutations to the server (debounced). `STORAGE_PROVIDER` (server-side only) = `local` | `sqlite` | `postgres`. [`docs/STORAGE.md`](docs/STORAGE.md).
- **API routes:** all backend in `src/app/api/`, except `/health`, which is a route at the app root so the plainest liveness path exists; JWT-protected except the public set in [`src/proxy.ts`](src/proxy.ts) — `/login`, `/api/auth/*`, `/health`, `/api/health`, `/api/db/health`, `/api/storage/config`, `/_next`, `/favicon.ico` and static assets — plus two machine paths gated by bearer credentials of their own instead of the JWT: the agent drive callback and /api/mcp. `src/proxy.ts` is the authority; do not restate the list elsewhere.
Expand Down
9 changes: 5 additions & 4 deletions DOCKERHUB.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

> 📖 **Full documentation, source, and issues:** <https://github.com/libredb/libredb-studio>

Query **PostgreSQL, MySQL, SQLite, libSQL, DuckDB, Oracle, SQL Server, MongoDB, Redis, Couchbase, ClickHouse, Apache Druid, Elasticsearch, OpenSearch, Trino, Apache Cassandra, Prometheus, Apache Kafka and etcd** from your browser, with AI-powered query assistance, interactive ER diagrams, schema diff, a virtualized data grid, RBAC, OIDC SSO, and a live monitoring dashboard. A lightweight, secure bridge between heavy desktop tools (DataGrip/DBeaver) and minimal CLIs.
Query **PostgreSQL, MySQL, SQLite, libSQL, DuckDB, Oracle, SQL Server, MongoDB, Redis, Couchbase, ClickHouse, Apache Druid, Elasticsearch, OpenSearch, Trino, Apache Cassandra, Prometheus, Apache Kafka, etcd and Neo4j** from your browser, with AI-powered query assistance, interactive ER diagrams, schema diff, a virtualized data grid, RBAC, OIDC SSO, and a live monitoring dashboard.

---

Expand Down Expand Up @@ -109,8 +109,8 @@ Every one of those tags is published on three bases, and the suffix is appended

## Supported databases

Nineteen external engines share one interface.
The twentieth row is the embedded LibreDB store, which ships inside the image rather than being a server you connect out to.
Twenty external engines share one interface.
The twenty-first row is the embedded LibreDB store, which ships inside the image rather than being a server you connect out to.

| Database | Driver | Highlights |
| :--- | :--- | :--- |
Expand All @@ -133,14 +133,15 @@ The twentieth row is the embedded LibreDB store, which ships inside the image ra
| **Prometheus** | none, HTTP | PromQL editor, metric, rule and target browser |
| **Apache Kafka** | `@platformatic/kafka` | Topic, group and broker browser, reads by offset or time |
| **etcd** | `@grpc/grpc-js` | etcdctl command editor, key-prefix browser, guarded value edits |
| **Neo4j** | `neo4j-driver-lite` | Read-only Cypher editor, label and relationship-type browser |
| **LibreDB** | `@libredb/libredb` | The embedded key-value store, for a database with nothing to install |

**Read-only where the engine is.** Druid, Elasticsearch and OpenSearch have no `UPDATE` and no `CREATE TABLE` anywhere in their grammar, so inline editing and DDL are reported as unsupported instead of failing when used.
Prometheus and Apache Kafka are read-only too: Studio calls only their read APIs.

### Engines with no provider of their own

Twenty-eight further engines speak the wire protocol of one of the nineteen drivers above, so they connect through it unchanged: pick that driver in the connection dialog. Every one of them was measured against a real instance rather than assumed, and how much of the product worked is recorded per engine.
Twenty-eight further engines speak the wire protocol of one of the twenty drivers above, so they connect through it unchanged: pick that driver in the connection dialog. Every one of them was measured against a real instance rather than assumed, and how much of the product worked is recorded per engine.

| Engine | Connect as | Support |
| :--- | :--- | :--- |
Expand Down
23 changes: 13 additions & 10 deletions README.md

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions README_es.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,9 @@ Tomada en serio, esa frase deja de ser una preferencia y se vuelve una especific

## Capacidades principales

### Diecinueve motores, una sola interfaz
### Veinte motores, una sola interfaz

PostgreSQL · MySQL · Oracle · SQL Server · SQLite · libSQL · DuckDB · MongoDB · Redis · Couchbase · ClickHouse · Apache Druid · Elasticsearch · OpenSearch · Trino · Apache Cassandra · Prometheus · Apache Kafka · etcd
PostgreSQL · MySQL · Oracle · SQL Server · SQLite · libSQL · DuckDB · MongoDB · Redis · Couchbase · ClickHouse · Apache Druid · Elasticsearch · OpenSearch · Trino · Apache Cassandra · Prometheus · Apache Kafka · etcd · Neo4j

Todos los motores SQL comparten el mismo explorador de esquemas, los diagramas ER, la comparación de esquemas y los paneles de monitoreo. MongoDB y Redis no son motores SQL: no tienen diagrama ER ni comparación de esquemas. Druid, Elasticsearch, OpenSearch y Trino son doblemente excepcionales: sus interfaces SQL sobre HTTP no tienen una forma de URI que este build sepa interpretar, así que se configuran por host y puerto, y las migraciones que se generan explican la limitación en lugar de inventar DDL para un motor cuyo SQL no tiene sentencias de cambio de columna. Lo mismo pasa con las colecciones sin esquema de Couchbase. El diagrama ER de los clústeres de búsqueda tiene cajas pero no líneas: los índices no declaran claves foráneas, y en el modelo del motor no hay ninguna que declarar.

Expand All @@ -128,6 +128,7 @@ Todos los motores SQL comparten el mismo explorador de esquemas, los diagramas E
| **Prometheus** | Sin driver, HTTP puro (la API HTTP de Prometheus, puerto 9090) | Editor de PromQL que envía el texto al servidor sin cambios, resultados en la grilla y en la pestaña de gráficos (una subconsulta con paso, como `rate(x[5m])[1h:1m]`, se grafica como líneas sobre sus marcas de tiempo: la pestaña abre con la primera serie, se agregan más desde el menú Y-Axis, una línea cada una, y se dibujan como máximo ocho a la vez, más allá de lo cual dice "Showing first 8 of N series"; pero el gráfico dibuja en 0 una muestra que falta y un `NaN` o `Inf` entre números, así que un rango crudo sobre targets con desfases de scrape distintos muestra ceros falsos), explorador de métricas con los nombres de etiqueta como columnas y los metadatos como fuente, grupos de reglas con reglas de registro y de alerta (una alerta disparada se marca en el árbol), pools y targets de scrape (un target caído se marca en el árbol), y salud, versión, tiempo de actividad y estadísticas del TSDB. Solo lectura por diseño: no llama a la API de administración, no hace remote write, no tiene EXPLAIN (el endpoint de parseo es experimental) ni operaciones de mantenimiento. Una credencial sobre HTTP en texto plano se envía en vez de rechazarse, así que activá TLS si la conexión cruza una red que no controlás |
| **Apache Kafka** | `@platformatic/kafka` (TypeScript puro, puerto 9092) | Una solicitud de lectura en JSON que lee un topic por partición, offset o marca de tiempo, desde su offset más antiguo o sus mensajes más recientes, con claves, valores y cabeceras decodificados como JSON, texto o base64 y un valor con el formato de Confluent etiquetado con su id de esquema; explorador de topics con sus particiones y configuraciones no predeterminadas (un topic offline o con réplicas insuficientes se marca), grupos de consumidores de ambos protocolos con su retraso por partición, brokers con su configuración, y salud, cantidad de topics y tamaño en disco. Solo lectura por construcción: no produce, no confirma offsets, no se une a grupos y no crea topics. TLS con CA propia y certificados de cliente, y SASL PLAIN o SCRAM solo sobre TLS; sin túnel SSH, porque a los brokers se llega por las direcciones que anuncian |
| **etcd** | `@grpc/grpc-js` (JavaScript puro, gRPC, puerto 2379) | Un subconjunto de etcdctl en el editor (`get`, `put`, `del`, `txn`, leases, un `watch` acotado, miembros, alarmas, usuarios y roles); grupos por prefijo de clave en el árbol y cada clave en el panel de claves; el valor de una clave editado en una transacción condicionada a su revisión; compactación, desfragmentación y desactivación de alarmas para administradores, cada una confirmada escribiendo el nombre de la conexión. Se rechaza toda escritura bajo un prefijo de Kubernetes o en `compact_rev_key`, y nunca se muestran los Secrets de Kubernetes ni los valores protobuf o cifrados. TLS con CA propia, certificados de cliente (su Common Name como usuario de etcd) e inicio de sesión con contraseña solo sobre TLS; túnel SSH; un modo de solo lectura declarado en la semilla |
| **Neo4j** | `neo4j-driver-lite` (JavaScript puro, Bolt, puerto 7687) | Cypher de solo lectura en el editor, una sentencia por ejecución, contra Neo4j 5.26 LTS; etiquetas de nodo y tipos de relación con sus propiedades como columnas, índices y restricciones en el árbol; nodos, relaciones y caminos como celdas JSON etiquetadas en la grilla, con los enteros de 64 bits y los valores temporales exactos; los paneles de monitoreo que ofrece Community. Cada sentencia pasa una política de lectura sobre sus tokens, luego la clasificación del propio servidor mediante `EXPLAIN` (que una forma SHOW permitida omite), y corre en una sesión READ, así que una escritura tendría que vencer las tres. Sin vista de EXPLAIN ni PROFILE, sin mantenimiento, sin ejecución del agente y sin `run_read_query` de MCP. TLS con CA propia y túnel SSH; sin cadena de conexión |
| **Redis** | `ioredis` | Editor de comandos, explorador de claves, monitoreo basado en INFO |

> **La seguridad del transporte es transversal, no depende del motor.** El túnel SSH se establece antes de que el proveedor abra la conexión, y la conexión se reescribe hacia el extremo local: por eso no depende del motor, y aplica a cualquier conexión configurada con host y puerto, salvo una conexión de Kafka, que lo rechaza porque un cliente de Kafka llega a cada broker por la dirección que ese broker anuncia, y un túnel que reenvía una sola dirección no la transporta. Las conexiones que se llenan con una cadena de conexión (MongoDB, Couchbase y ClickHouse lo permiten) no tienen host ni puerto, así que no pasan por el túnel; SQLite y DuckDB tampoco tienen ninguno de los dos. El panel de SSL/TLS hoy tiene efecto en PostgreSQL, MySQL, SQL Server, Couchbase, ClickHouse, Druid, Elasticsearch, OpenSearch, Trino, Prometheus y Apache Kafka; en Trino no es opcional, porque el coordinador rechaza contraseñas sobre HTTP en texto plano. Oracle, MongoDB y Redis ignoran esta opción, así que si el tráfico de esos tres va cifrado depende de cómo esté escrita la cadena de conexión, no de lo que se elija en el diálogo.
Expand Down
Loading
Loading