# iqdb v1.0.0 — Stable
**The public API and on-disk format are frozen.** v1.0.0 promotes the 0.9.0 release candidate to stable: no surface changes, no new features, one hardening tweak (`IndexKind` marked `#[non_exhaustive]`), and a migration guide for 0.4.x callers.
## What is iqdb?
An embedded vector database for Rust: exact and approximate similarity search, declarative filters, durable storage with tunable fsync and compression, an optional result cache, and an opt-in async surface — the integration layer over the iqdb crate family, all behind one small handle.
## What's new in 1.0.0
### API freeze
The following items are **frozen until 2.0**. No breaking changes will be made without a major version bump:
- `Iqdb` and all its methods (`open_in_memory`, `open_in_memory_with`, `open`, `open_with`, `upsert`, `get`, `delete`, `len`, `is_empty`, `search`, `search_with`, `search_batch`, `search_batch_with`, `optimize`, `cache_stats`, `flush`, `close`)
- `AsyncIqdb` and its async mirror (`async` feature)
- `IqdbConfig` and its fluent setters (`new`, `index`, `cache`, `fsync`, `compression`, `dim`, `metric`, `index_kind`, `is_cached`)
- `IndexKind` variants (`Flat`, `Hnsw`, `Ivf`)
- `Error` variants (`Index`, `Persist`, `Config`) and `Result`
- All re-exported vocabulary (`Vector`, `VectorId`, `Metadata`, `Value`, `Hit`, `Filter`, `DistanceMetric`, `SearchParams`, `HnswConfig`, `IvfConfig`, `CacheConfig`, `CacheStats`, `FsyncPolicy`, `Compression`, `EvictionPolicy`)
- Feature flags (`serde`, `parallel`, `zstd`, `lz4`, `async`)
### On-disk format freeze
The iQDB persistence format is locked at **version 1**. Its structure:
| Outer framing | `iqdb-persist` WAL + snapshot frames (CRC32-checked, magic `IQDB`, version `1`) |
| Inner payload | iqdb codec: magic `IQDC`, version `1`, index-kind tag (u8), dim (u64 LE), metric (u8), row count (u64 LE), row data (id + vector + optional metadata, all LE) |
A database written by any 1.x release reads back correctly by any other 1.x release on any Tier-1 platform (the format is fully little-endian). Format changes require a 2.0 major bump.
### `IndexKind` is now `#[non_exhaustive]`
`IndexKind` gained `#[non_exhaustive]` to allow new variants (`Flat`, `Hnsw`, `Ivf`, …) in 1.x minor releases without a breaking change. If you `match` on `IndexKind` without a wildcard arm, add `_` now:
```rust
// Before (fails to compile on 1.0 if exhaustive)
match kind {
IndexKind::Flat => { /* … */ }
IndexKind::Hnsw(_) => { /* … */ }
IndexKind::Ivf(_) => { /* … */ }
}
// After
match kind {
IndexKind::Flat => { /* … */ }
IndexKind::Hnsw(_) => { /* … */ }
IndexKind::Ivf(_) => { /* … */ }
_ => { /* unknown variant */ }
}
```
## Breaking changes
**None from 0.9.0.** `#[non_exhaustive]` on `IndexKind` is technically a breaking change for external code that matches exhaustively; if you are on 0.5.x–0.9.x, the only change needed is the wildcard arm above.
## Migration from 0.4.x → 1.0.0
The 0.5.0 re-platform was the only large breaking change in this series. The 0.5.0–0.9.0 window was purely additive. If you are coming from 0.4.x:
### Type renames
| `Record` | `(VectorId, Vector, Option<Metadata>)` — pass inline to `upsert` |
| `RecordId` | `VectorId` |
| `Payload` / `PayloadValue` | `Metadata` / `Value` |
| `SearchResult` | `Hit { id, distance, metadata }` |
### Constructor changes
```rust
// 0.4.x: dimensionality inferred from the first vector, no metric
let db = Iqdb::open_in_memory();
// 1.0: dimensionality and metric fixed at construction
let db = Iqdb::open_in_memory(128, DistanceMetric::Cosine)?;
```
### `search` signature
```rust
// 0.4.x: metric as a search argument
let results = db.search(&query, 10, DistanceMetric::Cosine)?;
// 1.0: metric fixed at open, omitted from search
let hits = db.search(&query, 10)?;
```
### `open(path)` path layout
```rust
// 0.4.x: path is a directory; iqdb managed snap + wal inside it
let db = Iqdb::open("./my_db")?;
// 1.0: path is the snapshot file; WAL lives beside it as <path>.wal
let db = Iqdb::open("./my_db/vectors.iqdb", 128, DistanceMetric::Cosine)?;
```
### Distance metric names
| `DistanceMetric::L2` | `DistanceMetric::Euclidean` |
| `DistanceMetric::Dot` | `DistanceMetric::DotProduct` |
| *(new)* | `DistanceMetric::Cosine` |
| *(new)* | `DistanceMetric::Manhattan` |
| *(new)* | `DistanceMetric::Hamming` |
### Error variants
```rust
// 0.4.x: Error::Corrupt { reason }, Error::NotImplemented
// 1.0: Error::Persist(PersistError), Error::Index(IqdbError), Error::Config(&'static str)
```
## Verification
Run on Windows x86_64 and on WSL2 Ubuntu (Rust stable); the same commands run in the configured CI matrix on Linux, macOS, and Windows:
```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test
cargo test --all-features
cargo deny check
cargo audit
cargo bench --bench search
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo +1.87 build --all-features
```
All green. Test counts with `--all-features`: 42 unit + 3 async + 12 persistence + 3 recovery + 2 property + 3 recall + 21 doctests.
## Installation
```toml
[dependencies]
iqdb = "1"
# Optional features
iqdb = { version = "1", features = ["serde", "parallel", "zstd", "async"] }
```
MSRV: Rust 1.87.
## Documentation
- [README](https://github.com/jamesgober/iqdb/blob/main/README.md)
- [API Reference](https://github.com/jamesgober/iqdb/blob/main/docs/API.md)
- [Standards (REPS)](https://github.com/jamesgober/iqdb/blob/main/REPS.md)
- [CHANGELOG](https://github.com/jamesgober/iqdb/blob/main/CHANGELOG.md)
- [docs.rs/iqdb](https://docs.rs/iqdb)
---
**Full diff:** [`v0.9.0...v1.0.0`](https://github.com/jamesgober/iqdb/compare/v0.9.0...v1.0.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/iqdb/blob/main/CHANGELOG.md#100--2026-06-09).