Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
HeliosDB Nano
An embedded database with native PostgreSQL and MySQL wire-protocol compatibility, plus one-shot SQLite file import. Single self-contained binary (~32 MB; ~12 MB compressed download). HNSW vector search, git-like branching, time-travel queries, AES-256-GCM encryption, built-in BaaS layer (Auth, REST API, Realtime).
Use your existing clients (psql, mysql), RESTful HTTP, drivers (psycopg2, mysql-connector, node-postgres, JDBC), and ORMs (SQLAlchemy, Prisma, Drizzle, Hibernate, GORM) — zero migration required. Existing .sqlite files import via a bundled converter.
Ecosystem
Nano is one of four products in the HeliosDB family. SDKs and integrations are cross-edition — the same client code works against Nano, Lite, and Full.
- HeliosDatabase/HeliosDB-SDKs — Official client SDKs (Python, TypeScript, Rust, Go) + integrations (VS Code, n8n, Zapier, Make, Retool, AutoGen) + cross-platform CLI. Apache 2.0.
- HeliosDatabase/Any2HeliosDB — Apache-2.0
a2hmigration toolkit for moving Oracle, MySQL, PostgreSQL, and SQL Server into HeliosDB Nano/Lite/Full or stock PostgreSQL, with wizard setup, resumable loads, validation, CDC, and MCP support. - HeliosDatabase/HeliosDB-Lite — Production self-hosted database with HeliosProxy + HeliosCore baked in. SSPL-1.0.
- HeliosDatabase/HeliosDB-Full — Distributed enterprise database with 14 native wire protocols. SSPL-1.0.
- HeliosDatabase/HeliosDB-Proxy — Programmable Postgres data-plane (PgBouncer drop-in + WASM plugins + zero-downtime PG-12→17 upgrade). Apache 2.0.
- HeliosDatabase/HeliosDB-Proxy-Plugins · Operator · Terraform · Pulumi — Proxy ecosystem.
HeliosDatabase/HeliosDB-CodeKB-MCP provides an MCP server that turns HeliosDB codebases and docs into queryable technical knowledge for Claude Code, Codex, and other MCP clients.
Catalogue: heliosdb.com/sdks.html · Build with AI agents: heliosdb.com/build-with-agents.html · LLM-discoverable index: heliosdb.com/llms.txt
Performance: what's fast, and where we're still improving — honest strengths + transparent limits.
Benchmark: HeliosDB-Nano vs PostgreSQL 18.4 — the pg35 benchmark, its history & evolution — 35 SQL categories head-to-head; Nano wins all 35/35 at 300 iterations (30 by 10×–35,000×, the joins by 1.1×–2×). The one category that ever classified against Nano (Prepared stmts) turned out to expose a real ROLLBACK TO SAVEPOINT bug — fixed in v3.60.3, and now a ~157× Nano win. Read the methodology note before quoting those ratios: pg35 runs Nano embedded and in-memory against a networked, fsyncing PostgreSQL, so the margins include deployment effects, not just engine speed.
For a like-for-like wire-to-wire comparison — Nano also as a TCP server, pgbench driving both — see vs PostgreSQL, 2026-08-28: Nano leads on indexed point-reads across all three query protocols (1.33×–2.22×) and on SELECT 1 (1.84×–2.84×); PostgreSQL leads on COPY above ~50k rows (1.28× at 100k) and on DROP TABLE (2.05×), and PostgreSQL keeps scaling from 32→64 clients (+32–41%) where Nano plateaus.
Install
Supported now: Cargo / crates.io
Install Rust and Cargo first. On Linux, macOS, or WSL, the recommended installer
is rustup:
|
On Windows, install Rust from rustup.rs, then open a new
terminal so cargo is on PATH.
Install the heliosdb-nano CLI binary from crates.io:
Use Nano as an embedded Rust library:
Enable optional crate features when installing or adding the dependency:
Build from source:
Prebuilt Binaries
Grab a self-contained binary from the GitHub Releases
page — Linux (x86_64 / aarch64), macOS (aarch64), and Windows (x86_64),
each with SHA256SUMS. No Rust toolchain required: download the archive for your
platform, verify it against SHA256SUMS, extract, and run the heliosdb-nano
binary.
# Example: Linux x86_64 (see Releases for macOS arm64 / Linux aarch64 / Windows)
VER=<release-tag>
Install Channels
Use Cargo or the GitHub release binaries for a verified install. Package-manager channels such as npm, Homebrew, and container images should be treated as available only when they are listed on the official releases page.
Start the Server
# Persistent, all three protocols
# In-memory (great for dev/test)
# With auth and TLS
# Same-host / embedded mode — Unix sockets (no TCP)
# then: psql -h /tmp or mysql --socket=/tmp/heliosdb-mysql.sock
Three servers start on one process:
| Protocol | Port | Connect |
|---|---|---|
| PostgreSQL wire | 5432 | psql, psycopg2, pgx, JDBC, Npgsql, node-postgres |
| PostgreSQL Unix socket | /tmp/.s.PGSQL.5432 |
psql -h /tmp, libpq-default apps |
| MySQL wire | 3306 | mysql, PyMySQL, SQLAlchemy, JDBC, mysql2 |
| MySQL Unix socket | /tmp/heliosdb-mysql.sock (configurable) |
mysql --socket=…, PHP mysqli, WordPress |
| REST / HTTP | 8080 | curl, fetch, any HTTP client |
Triple Compatibility — Same Data, Any Client
Start the server once, then connect from any of the three interfaces. They all read and write the same tables.
Interactive REPL (zero setup)
));
) ));
;
| |
| |
()
PostgreSQL Client (psql)
)
postgres=# ) );
postgres=# ) ;
MySQL Client (mysql)
;
| | | |
| | | |
| | | |
) );
REST API (curl)
# Query
}}
# Insert
# Interactive API explorer (Swagger UI)
Vector Search
Native HNSW indexes — no extensions, no separate vector database. The default
path is in-process HNSW; --features vector-persist enables RocksDB-backed
persistent HNSW/PQ with persistent = true, quantization = 'product', and
rerank_precision = 'f32' | 'f16' | 'i8'.
-- From any client (psql / mysql / REPL):
(
id SERIAL PRIMARY KEY,
title TEXT,
embedding VECTOR(1536)
);
(embedding vector_cosine_ops);
(embedding)
WITH (persistent = true, quantization = 'product', rerank_precision = 'i8');
INSERT INTO docs (title, embedding)
VALUES ('Intro', '[0.1, 0.2, 0.3, ...]');
-- k-NN search
SELECT title, embedding <-> '[0.15, 0.25, ...]' AS distance
FROM docs
ORDER BY distance
LIMIT 10;
Distance operators: <-> (L2), <=> (cosine), <#> (inner product).
Vector store IDs are namespace-scoped, and metadata/namespace filters are
applied before top-k selection in the REST/embedded vector-store API.
Via REST:
Full-Text Search
PostgreSQL-compatible FTS surface — no extensions, backed by built-in BM25:
-- Native tsvector / tsquery / @@ / ts_rank_cd:
SELECT title, ts_rank_cd(to_tsvector(body), to_tsquery('heliosdb')) AS rank
FROM articles
WHERE to_tsvector(body) @@ to_tsquery('heliosdb')
ORDER BY rank DESC
LIMIT 10;
-- Persistent tsvector column + GIN-style DDL:
(id SERIAL PRIMARY KEY, body TEXT, body_tsv TSVECTOR);
(body_tsv);
-- Hybrid search (FTS + vector) in one query:
SELECT id, text,
0.7 * (1.0 - (embedding <=> $1::vector))
+ 0.3 * ts_rank_cd(to_tsvector(text), plainto_tsquery($2)) AS score
FROM chunks
ORDER BY score DESC LIMIT 10;
Scope and honest limitations: see docs/compatibility/fts.md.
Pagination — Flat at Depth with Keyset
Keyset pagination on an indexed column is flat: ~35 µs whether you are on
page 1 or page 900. LIMIT … OFFSET is not — it is linear in the offset,
because the skipped rows are still stepped over. Use keyset for deep lists.
Measured on this repo (N = 10 000, page = 10, embedded path, p50; full curve and
reproduction in perf/pagination_depth_curve.json,
harness in tests/pagination_depth_curve.rs):
| shape | depth 0 | depth 9 000 | growth |
|---|---|---|---|
keyset, WHERE id > $1 (indexed) |
39 µs | 35 µs | 0.9× — flat |
OFFSET, no ORDER BY |
11 µs | 1 255 µs | 115× |
OFFSET + ORDER BY id |
36 µs | 4 812 µs | 133× |
OFFSET + ORDER BY created_at DESC, id DESC |
5 361 µs | 8 880 µs | 1.7× (flat, but ~5 ms — dominated by sorting every row) |
keyset, row-constructor (created_at, id) |
5 282 µs | 5 867 µs | 1.1× (flat, but ~5 ms — see below) |
Two caveats worth reading before you design around this:
OFFSETis O(offset). Skipping withoutORDER BYis cheap per row (it avoids decode and decrypt), but it is still a step per skipped row. Earlier versions of this README described it as constant-time; that was wrong, and the ~30 µs figure it quoted matches the keyset path, notOFFSET.- Row-constructor keyset does not yet use an index seek.
(a, b) < ($1, $2)is evaluated as a post-scan filter, so it is flat in depth but pays a full scan (~5 ms at 10 000 rows versus ~35 µs for the single-column form). Planner-driven keyset pushdown ontoscan_table_pk_rangeis the fix and is not implemented. Prefer a single indexed sort key until it is.
-- Keyset on an indexed column — flat at any depth, the recommended shape
SELECT id, created_at, subject
FROM leads
WHERE id < $1
ORDER BY id DESC
LIMIT 20;
-- LIMIT / OFFSET — correct, but cost grows with the offset
SELECT id, created_at, subject
FROM leads
ORDER BY created_at DESC, id DESC
LIMIT 20 OFFSET 100000;
-- Keyset (row-constructor tuple) — preferred for high-volume lists
SELECT id, created_at, subject
FROM leads
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 20;
-- JOIN + pagination composes cleanly
SELECT l.id, l.subject, c.name AS company
FROM leads l
LEFT OUTER JOIN companies c ON l.company_id = c.id
WHERE (l.created_at, l.id) < ($1, $2)
ORDER BY l.created_at DESC, l.id DESC
LIMIT 20;
Pitfalls: the sort key must be unique (always tail with id or another unique
column); avoid floating-point sort keys; do not mix ASC/DESC directions
inside the tuple. A NULL in a row-constructor comparison makes the whole
comparison unknown (PostgreSQL semantics), so rows with a NULL sort key are
excluded from every page — use NOT NULL sort keys.
Numbers above are reproduced by
cargo test --release --test pagination_depth_curve -- --ignored --nocapture,
which rewrites perf/pagination_depth_curve.json.
Git-Like Branching — Fork-Test-Discard Sandboxes
Isolated copy-on-write branches: fork the database in an instant, test a
destructive change (a migration, an experiment, an AI agent's writes) against
real data, then discard the branch. main is never touched. This is the
recommended pattern for giving every agent run its own sandbox.
CREATE BRANCH requires an AS OF clause — use AS OF NOW to fork current
state, or AS OF TIMESTAMP '…' to fork from the past. Without it the statement is
rejected with CREATE BRANCH requires AS OF clause (src/sql/parser.rs:3490).
;
USE BRANCH agent_run_42;
-- Changes here are invisible to main
INSERT INTO products (name, price) VALUES ('Test', 0.01);
UPDATE products SET price = price * 1.1; -- rehearse the scary change
SELECT COUNT(*), SUM(price) FROM products; -- validate it
USE BRANCH main;
DROP BRANCH agent_run_42; -- discard; main never changed
Branches can also fork from a past point in time:
CREATE DATABASE BRANCH rewind FROM main AS OF TIMESTAMP '2026-08-24 09:00:00'
(requires time-travel, which the agent profile keeps enabled).
⚠️
MERGE BRANCHmoves rows, but does not detect conflicts. Merging is last-writer-wins: ifmainand the branch both changed the same row, one value silently wins and you get no warning. Merge strategies are not implemented —WITH (conflict_resolution = …)errors with not implemented.WITH (delete_branch_after = true)is honoured and works.Merge is safe when the target has not been written since the fork. When you cannot guarantee that — and an agent never can — use fork-test-discard as designed: drop the branch and re-run the validated SQL against
main.An earlier version of this warning said
MERGE BRANCH"merges nothing and reports success". That was wrong and is retracted — seeCHANGELOG.md[4.16.0]. The evidence came from tests drivingBranchTransaction, an API with no production callers whose on-disk key encoding the real merge never reads. Measured, the SQL path merges correctly and always did; it is pinned bytests/branch_merge_surface_tests.rs.
Time-Travel Queries
-- As of a timestamp
SELECT * FROM products AS OF TIMESTAMP '2026-04-01 12:00:00';
-- As of a transaction
SELECT * FROM products AS OF TRANSACTION 12345;
Built-in Backend-as-a-Service
Self-hosted Supabase/Firebase alternative — Auth, REST, Realtime, RLS in the same binary:
# Sign up
# Google OAuth redirect
# Realtime subscriptions (WebSocket)
RLS is automatic on REST endpoints via JWT claims. See vs-supabase.
ORM & Driver Compatibility
| Language | PostgreSQL driver | MySQL driver | Tested ORMs |
|---|---|---|---|
| Python | psycopg2, asyncpg |
PyMySQL, mysql-connector-python |
SQLAlchemy, Django ORM |
| Node.js | pg, node-postgres |
mysql2 |
Prisma, Drizzle, TypeORM, Sequelize |
| Java | JDBC (postgresql) | JDBC (mysql-connector-j) | Hibernate, JPA |
| Go | lib/pq, pgx |
go-sql-driver/mysql |
GORM, ent |
| Rust | tokio-postgres, sqlx |
mysql_async, sqlx |
SeaORM, Diesel |
| PHP | PDO pgsql | mysqli, PDO mysql |
Laravel Eloquent, WordPress |
WordPress runs natively with standard wpdb — no drop-in required.
Data Types
All PostgreSQL types plus MySQL type aliases (automatically translated):
| Canonical | Aliases |
|---|---|
BOOLEAN |
BOOL, TINYINT(1) |
SMALLINT / INTEGER / BIGINT |
INT2/INT4/INT8, TINYINT, MEDIUMINT |
REAL / DOUBLE PRECISION |
FLOAT4/FLOAT8, FLOAT(N) |
NUMERIC(p,s) |
DECIMAL(p,s) |
TEXT |
VARCHAR(n), LONGTEXT, MEDIUMTEXT, TINYTEXT |
BYTEA |
BLOB, LONGBLOB, MEDIUMBLOB |
TIMESTAMP |
DATETIME |
SERIAL / BIGSERIAL |
INT AUTO_INCREMENT, BIGINT AUTO_INCREMENT |
UUID, JSON, JSONB, VECTOR(n), ARRAY |
— |
TSVECTOR, TSQUERY |
stored as JSON arrays of normalised tokens |
Features at a Glance
- Full SQL: JOINs, CTEs, window functions, subqueries, set operations, aggregates, CASE
- User-defined functions: scalar calls work —
CREATE FUNCTION dbl(x INT) RETURNS INT AS $$ SELECT $1 * 2 $$ LANGUAGE sqlthenSELECT dbl(21),SELECT id, dbl(id) FROM t,SELECT id FROM t WHERE dbl(id) = 2,SELECT public.dbl(21)and bound-parameterSELECT dbl($1)all return42/rows, on the embedded API and both wires, and the definition survives a restart.LANGUAGE plpgsqlbodies work forDECLARE+ SQL statements +SELECT … INTO+RETURN. One rule, identical to stored procedures: parameters and variables must carry the$sigil ($1/$name) — a bare name is a column reference. Recursion is bounded by[session] udf_max_call_depth(default 32). ⚠️ Still absent: set-returning functions (SELECT * FROM f()errors as a missing table, andRETURNS TABLE(...)'s column list is discarded), overloading (the registry keys on the name alone, so two same-name functions collide),CALL f()for a function, non-public.schema qualification, PL/pgSQL control flow inside a function body (IF/CASE/ loops /:=are REFUSED with an explicit error rather than silently taking the wrong branch — the procedural expression parser stores expression text instead of parsing it), catalog visibility (pg_proc,information_schema.routines/.parametersare still empty), and the REST/rest/v1/rpc/<fn>endpoint (still HTTP 501). A function call inside an embedded/REPL explicitBEGINis refused with an error (the body re-enters the executor and would deadlock on the global transaction lock) - Stored procedures:
CREATE PROCEDURE p(a INT, b TEXT) LANGUAGE sql AS $$INSERT INTO t VALUES ($a, $b)$$+CALL p(1, 'x')works, arguments included — in bothLANGUAGE sqlandLANGUAGE plpgsqlbodies (plpgsql binding is new; it substituted nothing through 4.10.2), and now on every client path. One rule: the body must reference parameters andDECLAREd variables with a$sigil, by name ($a) or positionally ($1). A bare parameter name is always a column reference and fails withColumn 'a' not found in schema— PostgreSQL resolves bare PL/pgSQL variable names, Nano deliberately does not, so a variable can never silently shadow a column. Substitution is literal-aware: a$1inside'a string', a-- commentor a$tag$…$tag$block is data, not a placeholder. Through 4.11.0,CALLran the body only on the simple-query / MySQL / REPL / embeddedexecute()path: over the PostgreSQL extended protocol (psycopg with server-side bind, JDBC, sqlx, Drizzle, node-postgres) and through the REST/BaaS layer it was a silent no-op that reported one affected row, andCALL nonexistent_proc()reported success. Both paths now share one implementation;CALLreports 0 rows affected and errors if the procedure does not exist. One limitation remains: on the embedded API and the REPL, aCALLinside an explicitBEGINis refused with an error (the body would re-enter the executor and deadlock on the global transaction lock) — issue it outside the transaction. ABEGINover the PG or MySQL wire is a per-session transaction and is unaffected - Materialized views:
CREATE/REFRESH MATERIALIZED VIEW, staleness tracking (\dmv,pg_mv_staleness()) - MVCC transactions: snapshot isolation;
READ COMMITTED/REPEATABLE READ/SERIALIZABLE; first-committer-wins write-write conflict detection (SQLSTATE40001) - COPY (PostgreSQL wire):
COPY … FROM STDIN/TO STDOUTin text and CSV — works withpsql \copyand high-throughput PG→Nano bulk migration - JSONB:
->,->>,@>,?operators - Full-text search:
tsvector,tsquery,@@,ts_rank_cd,CREATE INDEX ... USING gin(see FTS scope) - Keyset pagination: row-constructor comparison
WHERE (col, id) < ($1, $2); top-K sort. Keyset on a single indexed column is flat at any depth (~35 µs);LIMIT … OFFSETis linear in the offset, and row-constructor keyset is a post-scan filter, not an index seek (see Pagination) - Foreign keys: CASCADE, SET NULL, RESTRICT, deferred/audit/off validation modes,
NOT ENFORCEDconstraints - Triggers: ⚠️ trigger bodies still do not execute —
CREATE TRIGGER … EXECUTE FUNCTION f()parses, registers and now persists, but no trigger body ever runs: no audit rows, no derived-column maintenance, no cascades, on any interface, with no error or warning.AFTERtriggers,FOR EACH STATEMENT,INSTEAD OF,OLD, and deferred/CONSTRAINT triggers do nothing at all, and there is no trigger introspection (pg_triggerdoes not exist,information_schema.triggersis empty, and psql's\dreportsrelhastriggers = false— the column does not exist onpg_classon any other route). SQLite/MySQL-styleBEGIN … ENDbodies do not parse at all. The single exception that has an effect:BEFORE INSERT … FOR EACH ROW EXECUTE FUNCTION f()wheref's body is top-levelNEW.<col> = <expr>and/orRETURN NULLrewrites or skips the row being inserted. As of 4.20.0 that exception behaves IDENTICALLY on every interface — embeddedexecute(), the PostgreSQL simple and extended query protocols, the MySQL wire, REST/BaaS andINSERT … RETURNING(through 4.19.0 it applied only on the text executor family, so a REST insert and apsqlinsert into the same table produced different rows); it honours the trigger'sWHENclause and its enabled flag; and it survives a restart (a trigger created after the last checkpoint and lost to a crash is restored definition-only, i.e. inert).CREATE TRIGGER/DROP TRIGGERalso stopped hard-erroringOperator not yet implemented: CreateTriggerover the extended protocol, andDROP TABLEnow deregisters the table's triggers. Do not use triggers for audit logs or derived-data maintenance — do that work in the application, in an explicit second statement in the same transaction, or in aCREATE PROCEDUREinvoked withCALL. Procedures execute and bind their arguments in either language, provided you use$-sigil parameters, and now do so on every client path (through 4.11.0 this advice was inert over the PostgreSQL extended protocol and the REST layer — see the Stored procedures bullet above, which also covers the one remainingBEGINlimitation). - Row-Level Security: Per-tenant data isolation via policies
- EXPLAIN: Cost-based optimizer, ANALYZE, JSON/XML/YAML output
- Code-graph (opt-in,
--features code-graph): tree-sitter-backed AST index +lsp_definition/lsp_references/lsp_call_hierarchy/lsp_hoveras Rust API & SQL table functions — see code-graph overview - Agentic SQL hooks:
predict,infer,generate, and preview-only self-driving optimizer plans - Backup/Restore: Compressed dumps (zstd/gzip/brotli)
- Import/Export: CSV, JSON, JSONL, Parquet, Arrow, SQL
- Audit logging: Tamper-proof trail (SHA-256 checksums)
- Encryption: AES-256-GCM at rest, FIPS 140-3 mode — see what is and isn't encrypted before relying on it
- Unix domain socket listeners for both PostgreSQL (
--pg-socket-dir /tmp) and MySQL (--mysql-socket /tmp/heliosdb.sock) — PHPmysqli/ WordPress embedded-mode and libpq defaults work out of the box
Architecture
| Layer | Technology |
|---|---|
| Storage engine | RocksDB (LSM-tree) |
| Columnar format | Apache Arrow |
| SQL parser | sqlparser-rs |
| Vector index | HNSW + Product Quantization |
| Wire protocols | PostgreSQL v3, MySQL v10 |
| HTTP server | Axum |
| Encryption | AES-256-GCM, AWS-LC FIPS |
Encryption at rest
Set [encryption] enabled = true (off by default) and supply a key via
[encryption.key_source]. Values are sealed with AES-256-GCM.
Encrypted. Every row image written through any SQL, wire-protocol or library route — the
live row, its MVCC version history, row-id counters, the logical WAL entries that carry those
rows, branch row overlays, materialized-view delta records, the catalog (table schemas, index
definitions, sequences, roles and ACLs) and durable index snapshots. This holds on every write
route: autocommit, batch/COPY, explicit and session transactions, WAL replay, RENAME TABLE
and MERGE BRANCH.
Not encrypted — read this before relying on it:
| Not sealed | Why it matters |
|---|---|
| RocksDB keys | Table names, column names, row ids, timestamps and branch ids are visible on disk. Only values are sealed |
Column sidecars for non-default STORAGE modes (col:, colz:, colp:, dict:, cas:) |
For a column declared STORAGE COLUMNAR, DICTIONARY or CONTENT_ADDRESSED, the row holds only a reference and the payload lives in a sidecar in the clear. With time_travel on (the default) the sealed version copy still holds the value; with time travel off there is no sealed copy anywhere |
HNSW graph snapshots under <data_dir>/hnsw_snapshots/ |
Plain files outside RocksDB containing raw vectors |
Backup dumps ([dump], --dump-on-shutdown, --dump-schedule) |
Written unencrypted; protect the dump directory separately |
| Data written before enabling encryption | Existing values are read as-is and never rewritten. Enabling encryption seals new writes; it does not retroactively seal an existing database |
| The replication link | Rows are shipped to standbys in the clear. Encryption at rest says nothing about the wire — use TLS |
Wrong-key protection. A sentinel is sealed at first open and verified strictly on every subsequent open, so a wrong or rotated key is refused up front rather than silently yielding unreadable rows. Opening an encrypted directory with encryption disabled is refused for the same reason. A pre-existing database without a sentinel gains one only after the configured key is shown to open data that is already there — an ambiguous database is refused rather than stamped with a key that might be wrong.
Key rotation is not implemented. AES-256-GCM here uses a random 96-bit nonce under a single static key; NIST SP 800-38D caps random-nonce GCM at 2^32 invocations per key. High-volume deployments should track write volume against that bound.
High Availability
Warm standby is enabled by default — no feature flag needed. Just pass the replication flags at startup:
# Primary
# Standby
Optional HA features (opt-in at compile time):
| Flag | Description |
|---|---|
ha-tier1 |
Warm standby — enabled by default |
ha-tier2 |
Multi-primary: branch-based active-active |
ha-tier3 |
Sharding: consistent hash ring |
ha-dedup |
Content-addressed deduplication across nodes |
ha-ab-testing |
Branch-based experiment routing |
ha-branch-replication |
Selective branch sync to remote servers |
ha-full |
All optional HA features bundled |
Tip — verify HA changes locally, not in CI: the HA streaming and lock-management integration tests rely on tight TCP-port spin-waits that pass cleanly on a developer workstation (sub-second) but routinely hang on the 2-CPU GitHub Actions runner. The release workflow gates on
cargo test --libonly; if you're modifying anything undersrc/storage/wal/,src/cluster/, orsrc/storage/locks/, run the full integration suite locally first:
Connection Routing & Load Balancing
For production deployments with multiple HeliosDB Nano instances, put HeliosProxy in front — a standalone binary providing:
- Read/write splitting across primary + standbys
- Automatic failover with transaction replay (Oracle TAF-style)
- Connection pooling
- Health checks + circuit breakers
- TLS termination
Recommended Production Setup
┌────────────────┐
psql ─▶│ │──▶ HeliosDB Nano (primary, read+write)
mysql ─▶│ HeliosProxy │──▶ HeliosDB Nano (standby, read-only)
curl ─▶│ │──▶ HeliosDB Nano (standby, read-only)
└────────────────┘
port 5432/3306/8080
- Deploy 1 primary + 2 standbys (Fly.io / Render / Docker Swarm)
- HeliosProxy in front for routing + failover
- Automatic failover on primary death (< 5 s typical)
- Readonly queries load-balanced across standbys
Deploy
| Platform | Template |
|---|---|
| Fly.io | deployment/flyio/ |
| Railway | deployment/railway/ |
| Render | deployment/render/ |
| Docker | deployment/docker/ |
Embedded Library (Rust)
For in-process use (no network, no daemon), add the crate as a dependency:
[]
= "3.39"
See the Rust API guide for embedded usage and the examples/ directory for working code.
Building from Source
The heliosdb-nano binary builds with cargo build --release. Default features are encryption + vector-search + ring-crypto + ha-tier1 — covers most embedded and single-node-server cases. Run cargo info heliosdb-nano (or cargo metadata --no-deps --format-version 1 | jq '.packages[0].features') for the live list. Recipes for the non-obvious combinations:
# Default build — embedded + Postgres/MySQL wire + warm-standby HA.
# Code-graph + MCP server (matches what heliosdb-codekb-mcp links).
# Adds tree-sitter parsers, _hdb_code_* tables, lsp_* APIs, and the
# JSON-RPC dispatcher for stdio / HTTP / WebSocket / SSE clients.
# In-process embedder (no external HTTP service for embeddings).
# Pulls fastembed-rs + ORT — adds ~30 MB to the binary.
# FIPS 140-3 compliant crypto (AWS-LC FIPS Cert #4816, SHA-256, PBKDF2).
# `--no-default-features` is required to swap out ring-crypto.
# Full HA bundle — multi-primary + sharding + dedup + branch replication.
SDKs & Integrations
Official client SDKs (Go, Python, TypeScript, Rust) and platform integrations (VS Code, Zapier, n8n, Retool, Make, AutoGen) live in a shared repository:
heliosdb-sdks — works with all HeliosDB editions.
# JavaScript / TypeScript (Supabase-compatible fluent API)
import from '@heliosdb/client'
const db =
const = await db...
Agentic Operations (Claude Code, Codex CLI, MCP-aware tools)
The canonical way for an AI agent to operate Nano is MCP: build with
--features mcp-endpoint, then claude mcp add heliosdb -- heliosdb-nano mcp serve --data-dir ./mydata (stdio; HTTP and WebSocket transports also
available). The agent gets 16 structured tools — query, schema, insert, branch
create/list/merge, time-travel, hybrid search — instead of building SQL
strings. See the heliosdb-nano-mcp skill for the full catalog and recipes.
For any LLM that ingests a single reference file, docs/llms.txt
condenses install, connect, the SQL dialect, vector operators, branch
sandboxes, time-travel, and the skill catalogue into one agent-readable page.
For agent deployments, set profile = "agent" in the config (see
config.example.toml): group-commit writes plus
time-travel kept ON, so branchable sandboxes, AS OF queries, and vector
search all work out of the box.
HeliosDB-Nano also ships an agentic-operations skill catalogue — 19 SKILL.md files that give an LLM-driven coding agent a full A→Z catalogue of "verbs" for operating the database (install, connect, schema, DML, transactions, branches, time-travel, backup, vector, code-graph, graph-rag, MCP, server, tenant, deploy, observability, migrate).
# After git clone, Claude Code automatically picks up .claude/skills/ in this project.
# To install globally (~/.claude/skills/) so they apply in any project:
Existing ~/.claude/skills/heliosdb-nano-* directories are backed up to *.bak.<unix-ts> before being overwritten in either mode.
Installed via cargo install (no git checkout)? The skill files ship inside the published crate, so they are already on disk in cargo's registry cache — but scripts/install-agent-skills.sh is not packaged. Deploy them straight from the cache. The catalogue is plain Anthropic SKILL.md, so the same files also work in OpenCode and OpenAI Codex, which scan their own global skill dirs:
# Skills bundled inside the crate you installed (newest cached version):
SRC=
# Claude Code + OpenCode read ~/.claude/skills/ ; Codex + OpenCode read ~/.agents/skills/
for; do
done
| Agent | Global skill dir(s) it scans | Notes |
|---|---|---|
| Claude Code | ~/.claude/skills/ |
Auto-discovered at next session start. |
| OpenCode | ~/.claude/skills/, ~/.agents/skills/, ~/.config/opencode/skills/ |
Loaded on demand via the native skill tool; if your permission policy is ask/deny, allow that tool. |
| OpenAI Codex | ~/.agents/skills/ (personal), .agents/skills/ (per-repo, team), /etc/codex/skills/ (admin). The Codex CLI also scans ~/.codex/skills/. |
Auto-selected by description, or invoke /skills / $heliosdb-nano-…. Restart Codex if new skills don't appear. |
The cache path exists once cargo has extracted the crate (cargo install does this). If cargo GC'd it, run cargo fetch heliosdb-nano or reinstall. To pin a specific version instead of newest-cached, replace the SRC= glob with …/heliosdb-nano-<version>/.claude/skills. The _index folder has no SKILL.md, so OpenCode/Codex ignore it as a skill (it stays available as reference docs).
| Skill | What it covers |
|---|---|
heliosdb-nano-overview |
Top-level navigation; routes to the domain skills |
heliosdb-nano-install |
crates.io, source, feature flags (code-graph, mcp-endpoint, fips, ha-full…) |
heliosdb-nano-connect |
Embedded library, REPL, PG wire, MySQL wire, Python sqlite3 drop-in, TLS |
heliosdb-nano-schema |
DDL: tables, indexes (B-tree + HNSW), views; why CREATE TRIGGER registers but trigger bodies never run, and what user-defined functions can and cannot do |
heliosdb-nano-query |
DML, parameter styles (? $1 :name @name), ON CONFLICT, RETURNING |
heliosdb-nano-transactions |
BEGIN/COMMIT/ROLLBACK, savepoints, bulk-load patterns |
heliosdb-nano-branches |
Fork-test-discard sandboxes: CREATE/USE/DROP DATABASE BRANCH, AS OF forks |
heliosdb-nano-time-travel |
SELECT … AS OF TIMESTAMP '…', \snapshots |
heliosdb-nano-backup |
dump/restore, compression, append, partial restore, --dump-schedule |
heliosdb-nano-vector |
HNSW indexes, <-> <#> <=> operators, hybrid search |
heliosdb-nano-code-graph |
AST symbol index, LSP queries, git hook (code-graph feature) |
heliosdb-nano-graph-rag |
Knowledge graph + RAG ingest pipeline (graph-rag feature) |
heliosdb-nano-mcp |
MCP server, 16-tool catalog, stdio/HTTP/WS (mcp-endpoint feature) |
heliosdb-nano-server |
Daemon, TLS, auth, HA tier 1/2/3, user management |
heliosdb-nano-tenant |
Multi-tenant isolation modes, tiered plans, RLS policies |
heliosdb-nano-deploy |
Docker, Fly.io, Railway, Render, systemd template |
heliosdb-nano-observability |
Tracing, slow-query log, /health, \stats, \optimize, \indexes |
heliosdb-nano-migrate |
sqlite3 / Postgres / MySQL drop-in checklists |
heliosdb-nano-merge-validation |
This repo's 8-phase pre-merge validation methodology (contributors) |
Lookups: .claude/skills/_index/verb-map.md (every CLI flag / REPL meta-command / public Rust API method / MCP tool) · .claude/skills/_index/feature-matrix.md (cargo feature ↔ skill).
Documentation
In-repo guides (docs/):
- Upgrade between Nano versions
- Database management —
CREATE/DROP DATABASE - Authentication — SCRAM-SHA-256, trust, password, TLS
information_schemacompatibility- SQLite drop-in tutorial
- REPL demo
- HA cluster tutorial · HA testing
- Audit logging
- Tracing guide
- Full-text search · PL/pgSQL compatibility · SQLite compatibility
- Code-graph overview
Hosted docs:
- Getting Started
- API Explorer (Swagger UI) — when running locally
- vs Supabase
- vs Firebase
- vs PostgreSQL
- vs SQLite
- Migrate from MySQL
License
Apache-2.0 — Apache License, Version 2.0