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 10What 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.
BEGINandCOMMITare accepted and do nothing. There is one writer. - No JOIN and no subqueries. A query reads one collection.
- No SQL.
fenec-pgis compatible at the transport layer, not the language. - No schema migration and no multi-writer replication.
- No decimal type.
floatis binary floating point and unfit for money; the answer is anintin 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
| Target | What you get | Size |
|---|---|---|
| Browser | WebAssembly module plus a dependency-free ES client | 302 KB wasm |
| Command line | The fenec shell, fenec import, fenec types | 636–863 KB |
| Server | fenec-pg: PostgreSQL protocol and an HTTP listener in one process | 1.55 MB image |
| Embedded | fenec-core as a Rust library | zero 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` shellfenec-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.