fenecdb

Documentation

fenecdb stores rows and embeddings in one file, indexes them with HNSW, and answers queries from inside a browser tab — or from a process that speaks the PostgreSQL wire protocol.

Everything below describes the engine as it is built today. Where something costs you a capability, that cost is stated next to the feature rather than at the bottom of the page.

  • Quickstart

    Build it, open a file, write a vector query. Five minutes from clone.

  • How it works

    Segments, the offset index, HNSW, and why there is no page cache.

  • FenecQL

    The language: statements, types, operators, indexes, parameters.

  • JavaScript

    The browser client, the query builder and generated TypeScript types.

  • HTTP endpoint

    A REST surface derived from the schema, plus a subscription stream.

  • Sync

    A local replica that reads without the network and writes optimistically.

  • PostgreSQL server

    Wire-protocol compatibility, authentication, durability, cancellation.

  • Benchmarks

    Numbers against SQLite and pgvector, with the method that produced them.

What it is

One file. Records are appended to immutable 8 MiB segments and the byte sequence on disk is the byte sequence in memory, so a read decodes directly over the arena. A HashMap<DocId, Loc> offset index is the only helper structure in the whole store.

Vectors are not bolted on. vector<N> is a type, @hnsw is an index, near is a query clause, and the planner knows about all three — so a scalar filter and a vector search are planned together rather than stacked on each other.

create collection articles (
  title     text,
  tags      [text],
  year      int   @hash,
  embed     vector<768> @hnsw(cosine, m=16, ef_construction=200)
)

get articles select title
  where year >= 2024 and tags has "rust"
  near embed $1
  limit 10

What it gave up

The list is short and none of it is coming back. These are consequences of the design, not a roadmap.

  • No transactions. BEGIN and COMMIT are accepted and do nothing. There is one writer.
  • No JOIN and no subqueries. A query reads one collection.
  • No SQL. fenec-pg is compatible at the transport layer, not the language.
  • No schema migration and no multi-writer replication.
  • No decimal type. float is binary floating point and unfit for money; the answer is an int in cents.
  • No nested objects. A list is homogeneous and an object value is refused. A field you want to filter on should be a field.

The full accounting, including every hard-coded ceiling, is in Limits.

Where it runs

TargetWhat you getSize
BrowserWebAssembly module plus a dependency-free ES client302 KB wasm
Command lineThe fenec shell, fenec import, fenec types636–863 KB
Serverfenec-pg: PostgreSQL protocol and an HTTP listener in one process1.55 MB image
Embeddedfenec-core as a Rust libraryzero dependencies

How the crates fit together

Dependencies point one way. Nothing points back up.

fenec-core   storage, HNSW, plan executor, plugins, JSON, calendar, changes
     |       std only, zero dependencies
fenec-ql     FenecQL lexer and parser              fenec-wasm   browser ABI
     |                                                          (no wasm-bindgen)
fenec-http   REST/JSON surface and SSE subscriptions
     |
fenec-pg     PostgreSQL v3 wire protocol — server and client
     |
fenec-import SQLite file reader, PostgreSQL COPY source
     |
fenec-cli    the `fenec` shell

fenec-bench is a measurement harness and the only crate allowed external dependencies — that is where rusqlite and postgres live, so the comparisons in Benchmarks can run against the real engines.