Skip to content

Qirava DMS docs

Overview

Qirava DMS is an AI-native, zero-dependency data management system. The entire product is one idea — every query, every authentication check, and every HTTP handler is a function in a single registry, and everything is dispatched through one concurrent, resource-governed executor. On top of that primitive sits QQL, a small query language that runs the CRUD core (SELECT / INSERT / UPDATE / DELETE) plus DDL and three search modalities — full-text SEARCH, graph TRAVERSE, and vector NEAREST (k-NN) — over the same record store. There are no third-party dependencies: only the Rust standard library and the sibling Qirava crates. This page explains what the engine is, maps the rest of the documentation, and points you at where to begin.

What Qirava DMS is#

The DMS is a generic record store driven by a query language. You talk to it over HTTP with plain text and JSON — there are no drivers, no client SDK, and no separate schema-migration tool required. The wire protocol is HTTP plus the QQL text, and every response comes back in one uniform JSON envelope.

  • Generic record store: tables are schemaless (accept any fields) or typed (declared columns). Every row gets an immutable, DB-generated primary key named _id.
  • Exact numbers end-to-end: arbitrary-precision BigInt and Decimal values are kept exact internally and serialized as JSON strings on the wire so no precision is lost.
  • AI-native: vector columns with cosine NEAREST (k-NN) search accelerated by a random-hyperplane LSH index, and an inverted full-text SEARCH index — both first-class in QQL, not bolt-ons.
  • Durable when you want it: a write-ahead log (WAL) commits before memory is touched; on restart, committed writes replay into their tables.
  • Self-describing: table and index definitions live as data in _sys_tables / _sys_indexes, and worker, route, function, and job configuration live in _sys_* tables that hot-reload on change.
  • Injection-safe by construction: bound parameters (:name) resolve to typed values at parse time, so a value can never alter a query's structure.

The one primitive: execute over a function registry#

Reads and writes are separate internal functions in the registry. QQL itself is exposed through a single public handler. Every statement is dispatched through the executor — concurrent, multi-threaded, and FIFO-capable for ordered mutations — never run on the caller's thread. A Worker is just an orchestrator: it maps a protocol request to a sequence of function calls and returns one envelope. Everything ultimately calls runtime.execute(...).

  HTTP / WebSocket request
          |
          v
      Worker (route match)
          |
   +------+-------------------+----------+
   | before fns   ->   qql handler  ->  after fns
   | (auth /            (classify &       (read-only,
   |  validation)        dispatch)         errors ignored)
   +----------------------+--------------------+
                          |
              +-----------+-----------+
              |                       |
          db.read                 db.mutate
          (planner picks          (WAL commit, then
           access path)            in-memory apply)
                          |
                          v
            one uniform JSON envelope
The request spine: a worker maps a request to a before -> handle -> after pipeline, the handler dispatches QQL to the planner, and durable writes hit the WAL before memory.

The planner picks the access path per query — a primary-key point lookup, a secondary index (filter / search / vector / composite), or a full scan — and streams the result: folding aggregates, early-stopping at LIMIT, and top-K-ing an ORDER + LIMIT while cloning only the rows actually returned.

The HTTP surface#

The DMS exposes QQL over HTTP. There are three endpoints, all returning the same uniform JSON envelope. The same /api/qql route also serves over WebSocket: a connection upgrades on the route, then each text frame is one query and each frame back is one envelope.

MethodPathPurpose
POST/api/qqlRun one QQL statement; the query is the raw request body.
GET/api/qql?q=...Run one read-only QQL statement passed in the q query parameter.
POST/api/qql/batchRun a JSON array of QQL statements concurrently in one request. Add ?atomic=true for all-or-nothing authorization.

On the qql routes the handler builds the full QQL response envelope itself and the worker passes it through verbatim (the route output kind is Passthrough). A successful query returns its rows in data and the row count in root.count. A failure puts a stable string code plus a human message in error and null in data.

Request

curl -X POST http://localhost:8080/api/qql \
  -H 'Content-Type: text/plain' \
  --data 'SELECT name, age FROM users WHERE age >= 18 LIMIT 2'

Response

{
  "error": null,
  "data": [
    { "name": "alice", "age": 30 },
    { "name": "bob", "age": 42 }
  ],
  "root": { "count": 2 }
}

Output

A parse or validation failure returns the same shape with data null:

{
  "error": { "code": "bad_request", "message": "expected ON in JOIN" },
  "data": null,
  "root": { "count": 0 }
}

Where to begin#

Start the binary, then create a table, write a row, and read it back — all over the one /api/qql endpoint. The DMS binary starts the executor, ensures the _sys_* system tables exist, and binds the hardcoded system worker that serves /api/qql. By default it runs in-memory (ephemeral); point it at a data directory for WAL-backed durability.

bash
cargo run --bin qdms
qql
-- 1. Create a table (schemaless accepts any fields)
CREATE TABLE users SCHEMALESS

-- 2. Insert a row; the DB assigns the immutable _id
INSERT INTO users { email: "ada@example.com", age: 36 }

-- 3. Read it back
SELECT email, age FROM users WHERE age >= 18 SORT age DESC LIMIT 100

The documentation map#

This site documents every Qirava product and is itself a Quill app, server-rendered and prerendered to static HTML. Read the sections in this order; each is grouped in the sidebar.

SectionStart atWhat it covers
Qirava DMSThis page, then Getting Started -> QQL -> ArchitectureThe product overview, an end-to-end getting-started walkthrough, the QQL language, and the worker/executor architecture.
QQL & HTTP API/docs/qql_api/httpThe HTTP endpoints, authentication, and the response envelope in depth; the /api/qql/batch endpoint; the full QQL language reference; and bound parameters, API keys & RBAC.
Clients/docs/clientsA client quickstart plus copy-paste recipes in five languages (no SDK required — just HTTP + JSON).
Quill/docs/quillThe Rust-native, zero-dependency Quill UI framework: View & Style, the Theme/UI/Design/Icons components, and Build/SSG/ISR.
Operations/docs/ops/scheduled-jobsRunning the DMS in production: scheduled jobs (_sys_jobs), RBAC & API keys, and the security & deployment model.

Two extra pages round out the build-and-show story: the Component Showcase (/docs/showcase) renders the real Quill design components, and the Build on Quill guide (/docs/build) walks through authoring a page, styling, deploying, and reusing the crates.

QQL
The DMS query language: CRUD (SELECT/INSERT/UPDATE/DELETE), DDL (CREATE TABLE / CREATE INDEX), full-text SEARCH, graph TRAVERSE, vector NEAREST, and JOIN — all parsed zero-dependency and executed through the registry.
_id
The immutable, DB-generated primary key on every record. It is assigned by the engine on INSERT and cannot be set or changed by the caller.
Envelope
The uniform JSON response shape. On qql routes: { error, data, root: { count } }. On router-level paths: { error, data, root: { took_us, request_id } }. Exactly one of error/data is meaningful.
_sys_* tables
Self-describing system catalogs that store configuration as data — workers, routes, groups, functions, jobs, API keys, the schema/index catalog, and the Quill page/asset/theme catalogs. Writing them via QQL hot-reloads the running system with no restart.
Worker
An orchestrator that maps a protocol request to a before -> handle -> after pipeline of registry functions and returns one envelope. The DMS's own system worker (system.worker) is hardcoded and never overridable as data.