{"id":51634165,"url":"https://github.com/ata-sesli/zova","last_synced_at":"2026-07-13T12:01:12.847Z","repository":{"id":365894370,"uuid":"1274219119","full_name":"ata-sesli/zova","owner":"ata-sesli","description":"SQLite-backed local storage for records, objects, vectors, and graphs.","archived":false,"fork":false,"pushed_at":"2026-07-04T22:02:16.000Z","size":3843,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-05T00:05:20.279Z","etag":null,"topics":["c-abi","cli","content-addressed-storage","database","embedded-database","fastcdc","ffi","object","record","sqlite","vector","zig"],"latest_commit_sha":null,"homepage":"","language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ata-sesli.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-19T09:43:17.000Z","updated_at":"2026-07-04T22:02:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ata-sesli/zova","commit_stats":null,"previous_names":["ata-sesli/zova"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/ata-sesli/zova","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ata-sesli%2Fzova","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ata-sesli%2Fzova/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ata-sesli%2Fzova/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ata-sesli%2Fzova/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ata-sesli","download_url":"https://codeload.github.com/ata-sesli/zova/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ata-sesli%2Fzova/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35421719,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-13T02:00:06.543Z","response_time":119,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-abi","cli","content-addressed-storage","database","embedded-database","fastcdc","ffi","object","record","sqlite","vector","zig"],"created_at":"2026-07-13T12:01:12.124Z","updated_at":"2026-07-13T12:01:12.821Z","avatar_url":"https://github.com/ata-sesli.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zova\n\nSQLite-backed embedded database for records, objects, vectors, and graph-aware\nrelationships in local `.zova` files.\n\nZova keeps SQLite as the relational core and adds native storage for\ncontent-addressed objects, chunk manifests, streaming writes, exact vector\nsearch, SQL-native vector queries, graph relationships, SQL-native graph\ntraversal, transaction-aware app events, bound object/vector/graph stores,\ndiagnostics, salvage, backup, compact copy, restore, and a trusted extension\nhost foundation.\n\nCurrent package version: `0.23.0`.\n\nZova is pre-1.0. The current `.zova` file `format_version` is `7`. Format-7\nfiles require Zova 0.23.0 or newer. Older builds reject them; the format 6 to 7\ntransition is an intentional pre-1.0 compatibility boundary.\n\nZova `0.23.0` adds one optional bound graph store alongside the existing object\nand vector stores. Graph reads and writes route transparently after open, while\nthe CLI provides explicit create, bind, split, inspect, and unbind workflows.\nThe release also retains the raw `f16`/`i8` vector, `trgm` salvage, app-defined\nSQL callback, and trusted local extension-bundle capabilities from v0.22.\n\n## Contents\n\n1. [Install](#install)\n2. [Dependency Matrix](#dependency-matrix)\n3. [Quick Start](#quick-start)\n4. [What Zova Stores](#what-zova-stores)\n5. [Architecture](#architecture)\n6. [Records](#records)\n7. [Convert SQLite To Zova](#convert-sqlite-to-zova)\n8. [Objects](#objects)\n9. [Vectors](#vectors)\n10. [SQL-Native Vector Search](#sql-native-vector-search)\n11. [Graphs](#graphs)\n12. [SQL-Native Graph Traversal](#sql-native-graph-traversal)\n13. [Operational Safety](#operational-safety)\n14. [App Events](#app-events)\n15. [Extensions](#extensions)\n16. [Diagnostics And Salvage](#diagnostics-and-salvage)\n17. [CLI](#cli)\n18. [Bindings](#bindings)\n19. [Build From Source](#build-from-source)\n20. [SQLite Policy](#sqlite-policy)\n21. [Current Boundaries](#current-boundaries)\n22. [Testing](#testing)\n23. [Release Package Policy](#release-package-policy)\n24. [License](#license)\n\n## Install\n\nRust:\n\n```sh\ncargo add zova\n```\n\nor:\n\n```toml\n[dependencies]\nzova = \"0.23.0\"\n```\n\nPython:\n\n```sh\nuv add zova\n```\n\nor:\n\n```sh\npython -m pip install zova\n```\n\nGo:\n\n```sh\ngo get github.com/atasesli/zova/bindings/go@v0.23.0\n```\n\nThe Go binding uses cgo over Zova's C ABI. Build or provide the C ABI library\nbefore using it from another project.\n\nC ABI:\n\n```sh\n# Download a matching zova-v0.23.0-\u003cplatform\u003e-c-abi archive\n# from the GitHub Release, or build it locally:\nzig build c-abi\n```\n\nCLI:\n\n```sh\n# Download a matching zova-v0.23.0-\u003cplatform\u003e-cli archive\n# from the GitHub Release, or build it locally:\nzig build\nzig-out/bin/zova --help\n```\n\n## Dependency Matrix\n\nZova vendors SQLite. You do not need a system SQLite installation.\n\n| Path | Main Command | Needs Zig | Needs Rust | Needs C Compiler | Notes |\n|---|---|---:|---:|---:|---|\n| Rust | `cargo add zova` | no | yes | yes | `zova-sys` builds Zova's native C ABI from bundled generated C |\n| Python | `uv add zova` / `pip install zova` | no | only for sdist builds | only for sdist builds | wheels are published for Linux/macOS x86_64/arm64 on CPython 3.10/3.12; sdist fallback builds through Rust |\n| Go | `go get github.com/atasesli/zova/bindings/go@v0.23.0` | no, if using a release C ABI archive | no | yes, cgo | caller provides `zova.h` and `libzova_c.a` |\n| C ABI | release archive or `zig build c-abi` | no, if using a release archive | no | no, if using a release archive | static C ABI library and `zova.h` |\n| Zig | package source | yes | no | yes | native API |\n| CLI | release archive or `zig build` | no, if using a release archive | no | no, if using a release archive | source-built or prebuilt command line tool |\n\nMinimum tool versions used by the project:\n\n| Tool | Minimum / Current |\n|---|---|\n| Zig | `0.16.0` or newer |\n| Rust | `1.79` or newer |\n| Go | `1.22` or newer |\n| Python | `3.10` or newer |\n| SQLite | vendored `3.53.2` |\n\n## Quick Start\n\n### Rust\n\n```rust\nuse zova::{Database, Step};\n\nfn main() -\u003e Result\u003c(), zova::Error\u003e {\n    let mut db = Database::create(\"app.zova\")?;\n    db.exec(\"create table notes(id integer primary key, body text not null)\")?;\n\n    let mut insert = db.prepare(\"insert into notes(body) values (?1)\")?;\n    insert.bind_text(1, \"hello from Rust\")?;\n    assert_eq!(insert.step()?, Step::Done);\n\n    let object_id = db.put_object(b\"large bytes live here\")?;\n\n    db.create_vector_collection(\n        \"chunks\",\n        zova::VectorCollectionOptions {\n            dimensions: 2,\n            metric: zova::VectorMetric::L2,\n        },\n    )?;\n    db.put_vector(\"chunks\", \"chunk:1\", \u0026[0.0, 1.0])?;\n\n    println!(\"stored object: {object_id:?}\");\n    Ok(())\n}\n```\n\n### Python\n\n```python\nimport zova\n\nwith zova.Database.create(\"app.zova\") as db:\n    db.exec(\"create table notes(id integer primary key, body text not null)\")\n\n    with db.prepare(\"insert into notes(body) values (?1)\") as stmt:\n        stmt.bind_text(1, \"hello from Python\")\n        assert stmt.step() == zova.Step.DONE\n\n    object_id = db.put_object(b\"large bytes live here\")\n\n    db.create_vector_collection(\n        \"chunks\",\n        zova.VectorCollectionOptions(2, zova.VectorMetric.L2),\n    )\n    db.put_vector(\"chunks\", \"chunk:1\", [0.0, 1.0])\n```\n\n### Go\n\n```go\npackage main\n\nimport zova \"github.com/atasesli/zova/bindings/go\"\n\nfunc main() {\n    db, err := zova.Create(\"app.zova\")\n    if err != nil {\n        panic(err)\n    }\n    defer db.Close()\n\n    if err := db.Exec(\"create table notes(id integer primary key, body text not null)\"); err != nil {\n        panic(err)\n    }\n}\n```\n\n## What Zova Stores\n\nZova has four first-class storage shapes:\n\n- **Records:** normal SQLite tables, indexes, views, triggers, and SQL.\n- **Objects:** content-addressed bytes, chunked with FastCDC-v1 and addressed by\n  `SHA-256(full bytes)`.\n- **Vectors:** named vector collections with exact flat search and SQL-native\n  query helpers.\n- **Graphs:** named relationship graphs with application-provided stable node\n  IDs and explicit directed edges.\n\nApplications own their metadata in normal SQL tables. Zova-owned private tables\nstore object bytes, manifests, chunk rows, vector collections, and vector rows.\nUser tables should reference Zova object ids or vector ids.\n\n```text\nSQL row\n  title       = \"receipt.pdf\"\n  object_id   = \u003c32-byte ObjectId\u003e\n  vector_id   = \"receipt:chunk:42\"\n```\n\n## Architecture\n\n```mermaid\nflowchart TD\n    App[\"Application\"]\n    API[\"Zova API\u003cbr/\u003eRust, Python, Go, Zig, or C ABI\"]\n    CLI[\"zova CLI\u003cbr/\u003einspect, check, doctor, salvage, backup\"]\n    File[\"local .zova file\u003cbr/\u003eSQLite database\"]\n    UserSQL[\"User SQL tables\u003cbr/\u003erecords and metadata\"]\n    Meta[\"_zova_meta\u003cbr/\u003eidentity and format\"]\n    Objects[\"_zova_objects\u003cbr/\u003eobject ids and sizes\"]\n    Chunks[\"_zova_chunks\u003cbr/\u003everified chunk BLOBs\"]\n    Manifest[\"_zova_object_chunks\u003cbr/\u003eobject manifests\"]\n    VecCols[\"_zova_vector_collections\u003cbr/\u003edimensions, metric, and element type\"]\n    Vecs[\"_zova_vectors\u003cbr/\u003etyped vector BLOBs\"]\n    Graphs[\"_zova_graphs\u003cbr/\u003enamed relationship graphs\"]\n    Nodes[\"_zova_graph_nodes\u003cbr/\u003estable app node ids\"]\n    Edges[\"_zova_graph_edges\u003cbr/\u003edirected relationships\"]\n    Ext[\"_zova_extensions\u003cbr/\u003einstalled extension registry\"]\n    ExtStore[\"_zova_ext_name_*\u003cbr/\u003eextension-owned storage\"]\n\n    App --\u003e API\n    App --\u003e UserSQL\n    API --\u003e File\n    CLI --\u003e File\n    File --\u003e UserSQL\n    File --\u003e Meta\n    File --\u003e Objects\n    File --\u003e Chunks\n    File --\u003e Manifest\n    File --\u003e VecCols\n    File --\u003e Vecs\n    File --\u003e Graphs\n    File --\u003e Ext\n    Ext --\u003e ExtStore\n    Graphs --\u003e Nodes\n    Nodes --\u003e Edges\n    Objects --\u003e Manifest\n    Manifest --\u003e Chunks\n    VecCols --\u003e Vecs\n```\n\nThe file boundary is explicit:\n\n```text\n*.zova  -\u003e Zova database\nother   -\u003e normal SQLite database\n```\n\nRenaming `app.db` to `app.zova` is not enough. A valid Zova database has Zova\nmetadata and private schema.\n\n## Records\n\nRecords are just SQLite.\n\nUse normal SQL for application tables:\n\n```sql\ncreate table attachments(\n  id integer primary key,\n  filename text not null,\n  object_id blob not null,\n  vector_id text\n);\n```\n\nThe C ABI and all bindings expose prepared statements, bind/step/column access,\ntransactions, savepoints, `last_insert_rowid`, `changes`, `total_changes`, and\ncolumn names. Serious application metadata belongs here.\n\n## Convert SQLite To Zova\n\nExisting SQLite databases can be copied into a new `.zova` file without\nmutating the source database.\n\nUse this when an application already has normal SQLite tables and wants to add\nZova objects, vectors, diagnostics, backup, compact copy, and salvage around\nthe same local file model.\n\nConversion is exposed through the native APIs:\n\n```zig\ntry zova.convertSqliteToZova(\"app.sqlite\", \"app.zova\");\n```\n\n```rust\nzova::Database::convert_sqlite_to_zova(\"app.sqlite\", \"app.zova\")?;\n```\n\n```go\nerr := zova.ConvertSqliteToZova(\"app.sqlite\", \"app.zova\")\n```\n\n```python\nzova.convert_sqlite_to_zova(\"app.sqlite\", \"app.zova\")\n```\n\nThe destination must be a new `.zova` path. If the SQLite source uses table\nnames reserved by Zova, conversion fails instead of silently rewriting the\napplication schema.\n\nFor a full application migration path, see\n[SQLite App To Zova App Migration Guide](docs/sqlite-to-zova.md).\n\n## Objects\n\nObjects are raw bytes stored by content identity:\n\n```text\nObjectId = SHA-256(full object bytes)\n```\n\nZova splits objects into FastCDC-v1 chunks and deduplicates chunks inside the\nsame `.zova` file. You can put/get whole objects, range-read object bytes,\ninspect manifests, fetch verified chunks, store loose chunks, and assemble a\ncomplete object from chunks.\n\n### Optional Bound Object, Vector, And Graph Stores\n\nSingle-file `.zova` remains the default. In v0.23.0, applications can opt into\none bound object store, one bound vector store, and one bound graph store when\nlarge object bytes, vector rows, or graph topology should live beside the main\nrecords database:\n\n```sh\nzova object-store create objects.zova\nzova object-store bind main.zova objects.zova\nzova object-store info main.zova\n\nzova vector-store create vectors.zova\nzova vector-store bind main.zova vectors.zova\nzova vector-store info main.zova\n\nzova graph-store create graphs.zova\nzova graph-store bind main.zova graphs.zova\nzova graph-store info main.zova\n```\n\nUse `bind` for new or empty Zova-owned storage. If the main file already has\nobject, vector, or graph rows that you want to move out, use `split` instead:\n\n```sh\nzova split --objects main.zova objects.zova\nzova split --vectors main.zova vectors.zova\nzova split --graphs main.zova graphs.zova\n```\n\n`split` is an in-place local migration. It creates a new store file, copies the\nselected Zova-owned private rows into that store, clears those private rows from\nthe main file, binds the new store, and verifies the result. User SQL tables and\nrows stay in `main.zova`. The destination store must be a new `.zova` path; Zova\ndoes not overwrite existing files. Take a backup before splitting; after a\nsuccessful split, rollback means restoring that backup or running another\nexplicit local migration.\n\nAfter binding, Zova attaches the object store to the main SQLite connection and\nroutes `_zova_objects`, `_zova_chunks`, and manifests through the internal\n`object_store` schema. A bound vector store similarly routes vector collections\nand vector rows through the internal `vector_store` schema. A bound graph store\nroutes graphs, nodes, and edges through the internal `graph_store` schema.\nUser SQL records stay in the main database. If a store file is moved, run\n`bind` again with the new path:\n\n```sh\nzova object-store bind main.zova new/path/objects.zova\nzova vector-store bind main.zova new/path/vectors.zova\nzova graph-store bind main.zova new/path/graphs.zova\n```\n\nUse the matching `object-store unbind`, `vector-store unbind`, or\n`graph-store unbind` command to remove binding metadata without deleting the\nstore file.\n\n`bind` is a safe set-or-replace operation for already-empty or already-bound\nstorage: Zova validates the new store file before updating the main database's\nbinding metadata. It rejects a first-time bind when the main file already\ncontains object/vector/graph rows, because that would hide existing data. Use\n`split` for that case. The main database records store identity, a bound-set id,\nand object/vector/graph epochs. Normal open rejects missing stores, wrong stores,\nmarker mismatches, and split bound sets instead of silently continuing. `doctor` and\n`check --deep` report those as `bound_store` diagnostics so the problem is\nvisible without mutating any file. For a moved store path, run `bind` again with\nthe new location; marker mismatches are treated as consistency problems, not\npath-repair prompts.\n\n`backup`, `compact`, and `restore` are bound-store-aware: they copy readable\nbound object/vector/graph data back into the new destination so the produced\nfile is self-contained.\n\nObject writes, deletes, chunk writes, assembly, and `ObjectWriter.finish` can\nparticipate in the same Zova transaction/savepoint as main-file SQL when an\nobject store is bound. Vector collection and vector row mutations follow the\nsame transaction/savepoint stack when a vector store is bound. Graph mutations\nlikewise route transparently and advance the graph epoch once\nper successful mutation or batch when a graph store is bound.\nStore management is still explicit: `bind`, `unbind`, and replacement binds are\nrejected while the main database has an active transaction or savepoint.\n\nSQLite's `ATTACH` rules still matter. Multi-file transactions are crash-atomic\nonly under SQLite's documented journal-mode conditions. Zova does not claim a\nstronger guarantee; the bound-set id and epoch exist so Zova can detect and\nexplain split-file states during open, `doctor`, and `check --deep`.\n\nThis is local, manual storage placement. It is not distributed storage, cloud\nsync, automatic path repair, or a multi-file transaction guarantee. Zova\nsupports at most three optional stores total: one object store, one vector\nstore, and one graph store. Multiple named stores are deferred.\n\nUse `ObjectWriter` when bytes arrive over time:\n\n```rust\nlet mut writer = db.object_writer()?;\nwriter.write(b\"chunk one\")?;\nwriter.write(b\"chunk two\")?;\nlet object_id = writer.finish()?;\n```\n\nDeleting an object removes Zova-owned object rows and unreferenced chunks. It\ndoes not scan or mutate user SQL rows. SQLite may reuse freed pages without\nshrinking the file; use explicit vacuum or compact copy when you want file-size\nreclamation.\n\n## Vectors\n\nVectors live in named collections:\n\n```text\ncollection: \"chunks\"\ndimensions: 384\nmetric: cosine | l2 | dot\nelement type: f32 | f16 | i8\nvector id: application-provided text\n```\n\n`f32` is the default and keeps the existing APIs/file behavior. Raw `f16`\ncollections store IEEE 754 binary16 bits as little-endian `uint16` values, and\nraw `i8` collections store signed bytes. These are storage element types, not\nautomatic quantization; Zova does not add scales, zero-points, reranking, or ANN\nindexes for them.\n\nSupported metrics:\n\n- cosine distance: `1 - cosine_similarity`\n- L2 distance: Euclidean distance\n- dot distance: `-dot_product`\n\nZova supports collection create/info/list/delete, vector CRUD, batch upsert,\nexact search, candidate-filtered search, search-by-id, and inclusive distance\nthresholds.\n\nSearch is exact and flat-scan in `0.23.0`. It is good for local datasets,\noffline ranking, deterministic tests, and SQL-filter-first workflows. It is not\nyet an ANN engine for million-scale low-latency search.\n\n## SQL-Native Vector Search\n\nZova registers SQL vector helpers on `zova.Database` connections:\n\n```sql\nzova_vector_distance(collection, vector_id, query_vector_blob)\nzova_vector_distance_by_id(collection, vector_id, source_vector_id)\n```\n\nIt also exposes a read-only virtual table:\n\n```sql\nselect\n  c.id,\n  c.text,\n  s.distance\nfrom zova_vector_search as s\njoin chunks as c on c.vector_id = s.vector_id\nwhere s.collection = 'chunks'\n  and s.query_vector = ?1\n  and s.top_k = 10\norder by s.rank;\n```\n\nFor `f32` collections, `query_vector_blob` is little-endian `f32` data. Typed\ncollections use query blobs matching their collection element type; `f16` blobs\nare little-endian `uint16` bit patterns and `i8` blobs are raw signed bytes.\nThis lets applications combine SQL metadata filters with vector ranking without\npulling the whole metadata set into application code.\n\n## Graphs\n\nGraphs let applications store relationships between records, objects, chunks,\nvectors, entities, facts, concepts, and external references.\n\nZova does not invent row IDs for your app. Nodes use stable IDs that the\napplication provides:\n\n```text\nmessage:123 --has_attachment--\u003e object:8f...\nmessage:123 --embedded_as--\u003e vector:chunks:message-123\nentity:person:alice --mentioned_in--\u003e message:123\nfact:991 --supported_by--\u003e chunk:doc7:12\n```\n\nGraph rows store topology and small routing fields only. Application metadata\nstays in normal SQL tables. Zova validates graph names, node IDs, edge types,\nedge endpoint existence, and Zova-owned targets such as object IDs, chunk IDs,\nand vector IDs. It does not validate arbitrary user SQL row existence; apps own\nthat contract.\n\nCLI inspection is bounded and privacy-aware:\n\n```sh\nzova graphs app.zova\nzova graph app.zova app\nzova graph-node app.zova app message:123\nzova graph-neighbors --limit 20 app.zova app message:123\nzova graph-walk --max-depth 2 --limit 50 app.zova app message:123\n```\n\nThis is a local graph-aware relationship layer, not Neo4j, Cypher, GQL,\nGremlin, SPARQL, or automatic LLM extraction.\n\n## SQL-Native Graph Traversal\n\nZova registers read-only graph virtual tables on Zova SQLite connections:\n\n```sql\nselect m.body, g.edge_type\nfrom zova_graph_neighbors as g\njoin messages as m on m.graph_node_id = g.node_id\nwhere g.graph_name = 'default'\n  and g.source_node_id = 'message:123'\n  and g.direction = 'outgoing'\n  and g.\"limit\" = 20\norder by g.rank;\n```\n\nFor bounded directed walks:\n\n```sql\nselect node_id, depth, predecessor_node_id, edge_type\nfrom zova_graph_walk\nwhere graph_name = 'default'\n  and start_node_id = 'message:123'\n  and edge_type_filter = 'mentions'\n  and max_depth = 2\n  and \"limit\" = 50\norder by rank;\n```\n\n`zova_graph_neighbors` returns one-hop neighboring nodes. `zova_graph_walk`\nreturns the start node plus bounded reachable nodes. In both helpers, visible\n`node_id` is the returned node ID; input nodes use `source_node_id` or\n`start_node_id`. Apps join those node IDs back to their own SQL tables.\n\n## Operational Safety\n\nZova includes file-level safety operations:\n\n```sh\nzova backup app.zova app.backup.zova\nzova compact app.zova app.compact.zova\nzova restore app.backup.zova app.restored.zova\n```\n\n- `backup` uses SQLite's online backup API.\n- `compact` uses SQLite `VACUUM INTO` to create a space-reclaiming copy.\n- `restore` copies a backup into a new destination file.\n\nDestinations must be new `.zova` paths. Zova does not overwrite destination\nfiles in these operations.\n\nSavepoints are available for connection-local partial rollback:\n\n```text\nSAVEPOINT name\nROLLBACK TO name\nRELEASE name\n```\n\nBindings also expose scoped savepoint helpers for cleanup ergonomics.\n\n## App Events\n\nZova has same-process `listen` / `notify` app events for storage workflows:\n\n```rust\nlet mut listener = db.listen(\"message:1:attachments\")?;\nlet object_id = db.put_object(b\"attachment bytes\")?;\n\ndb.begin_immediate()?;\n// Store object_id in your SQL metadata row here.\ndb.notify(\"message:1:attachments\", \"changed\")?;\nassert!(listener.try_receive()?.is_none());\ndb.commit()?;\n\nlet event = listener.try_receive()?.unwrap();\nassert_eq!(event.payload, \"changed\");\n```\n\nNotifications are explicit, local to one open database handle, in-memory, and\nnon-persistent. They are delivered to subscription queues after commit. Rollback\ndiscards pending notifications. Savepoint rollback discards inner pending\nnotifications; savepoint release preserves them for the outer scope.\nSQL `zova_notify(...)` participates in this model when transactions/savepoints\nare opened through Zova helpers; raw SQL transaction scopes that Zova cannot\ntrack are rejected instead of guessed.\n\nThis is useful when one process wants a clean storage-runtime boundary: a write\nworkflow stores records, objects, vectors, or graph relationships, then notifies\nanother part of the same process to reload by id. Graph mutations do not emit\nautomatic events; call `notify(\"graph:changed\", \"...\")` explicitly inside the\nsame transaction when your app wants listeners to refresh graph-derived views.\nIt is not cross-process delivery, replay, replication, audit logging, or\nautomatic mutation tracking.\n\nQueue details:\n\n- channel names are ASCII, 1-128 bytes, using letters, digits, `_`, `.`, `:`,\n  and `-`\n- payloads are UTF-8 text, up to 64 KiB\n- each subscription queue holds 1024 notifications\n- when a queue overflows, Zova drops the oldest notification and reports the\n  drop count on the next received notification\n- the current event API has polling only: use `try_receive` / drain loops, not\n  callbacks\n\n## Extensions\n\nThe v0.23 release includes the extension host, controlled app-defined SQL\ncallbacks, trusted local extension bundles, and the first bundled extension,\n`trgm`.\n\nAn extension is trusted process code plus private Zova metadata:\n\n- the database records installed extension metadata in `_zova_extensions`\n- an extension owns only tables with its `_zova_ext_\u003cname\u003e_` prefix\n- extension code is provided by the process, not loaded from the `.zova` file\n- SQL functions or virtual tables are registered on each opened Zova connection\n- install, check, and drop hooks run inside Zova-managed transactions\n- extension registry and private storage live in the main database in the\n  current v0 model\n\nThis keeps `.zova` files non-executable. A file may say it requires an\nextension, but the application or CLI process decides which extension code is\navailable and trusted. Opening a database with an installed required extension\nwhose code is unavailable fails clearly instead of silently ignoring the\nextension.\n\nThe host foundation supports app-registered extensions in native Zig and CLI\ninspection/management:\n\n```sh\nzova extension list app.zova\nzova extension info app.zova \u003cname\u003e\nzova extension check app.zova [name]\nzova extension drop app.zova \u003cname\u003e\nzova extension install app.zova \u003cname\u003e\n```\n\nIt also supports explicitly trusted local `.zovaext` bundles for one process at\na time:\n\n```sh\nzova extension trust ./my_ext.zovaext\nzova --extension ./my_ext.zovaext extension install app.zova my_ext\nzova --extension ./my_ext.zovaext check --deep app.zova\nzova extension trusted\nzova extension untrust my_ext\n```\n\nTrusted bundles are native code. Zova records hashes of the bundle manifest and\nlibrary; if either changes, the bundle must be trusted again. Zova never loads\nextension code just because a `.zova` file contains extension metadata.\nIf a command needs a dynamic extension that is missing or untrusted, diagnostics\ntell you to provide `--extension \u003cbundle.zovaext\u003e` or trust the bundle first.\n\nDynamic `.zovaext` loading is a native Zig/CLI/C ABI capability. The generated-C\nsnapshot used by package builds intentionally disables dynamic loading because\nZig `0.16` does not portably emit the `std.DynLib` loader path through its C\nbackend. Those builds keep the C ABI bundle symbols for source compatibility,\nbut calls that need to load an external bundle fail with an extension load or\nunavailable status. Use the native CLI or a Zig-built C ABI archive when an\napplication needs external `.zovaext` loading.\n\nThe v0.23 release also includes an experimental bundle producer CLI:\n\n```sh\nzova extension scaffold ./sample_ext --name sample_ext --version 0.1.0\nzova extension build ./sample_ext\nzova extension pack ./sample_ext --out ./sample_ext.zovaext\nzova extension verify --smoke ./sample_ext.zovaext\n```\n\nAt the low-level C ABI, apps can register scalar SQL functions on Zova-owned\nconnections with `zova_database_register_function`. Callback arguments are\nborrowed for the call only, result bytes are copied by Zova, and callbacks must\nnot re-enter the same `zova_database` handle. Safe high-level Rust, Go, and\nPython callback APIs are not part of the v0.23 release. See\n`examples/c_callbacks/` for C callback snippets and `examples/zig_bridge/` for\na minimal native Zig registry bridge.\n\n`install` succeeds only for extensions registered in the current process or\nbundled with Zova. The default Zova process registry includes `trgm`, so this\nworks in the normal CLI build:\n\n```sh\nzova extension install app.zova trgm\n```\n\nAfter installation, Zova registers the `zova_trgm_*` SQL surface on each open\nconnection. `trgm` is for fuzzy target lookup: typo-tolerant matching over app\ndocument IDs that point back to records, objects, chunks, vectors, graph nodes,\nentities, facts, concepts, or external refs.\n\nThose target refs may point at objects or vectors stored in optional bound\nstores. The extension index itself still stays in the main database.\n\n```sql\nselect zova_trgm_create_index('messages');\nselect zova_trgm_put(\n  'messages',\n  'message:123',\n  'record',\n  'messages',\n  '123',\n  'attachment upload failed'\n);\n\nselect document_id, score\nfrom zova_trgm_search\nwhere index_name = 'messages'\n  and query = 'attachement failed'\n  and threshold = 0.20\n  and \"limit\" = 10\norder by rank;\n```\n\n`trgm` is not SQLite FTS and not vector search. FTS is best for tokenized\nfull-text search such as matching words and phrases. Vectors are best for\nsemantic similarity. Trigram lookup is useful when the query or target has\nsmall spelling differences, filename variations, IDs, short labels, or\noperator-entered text where typo tolerance matters.\n\nExtension operations do not emit automatic app events. If an application wants\nsame-process listeners to react to indexing, it should call `notify` explicitly\ninside the same transaction, for example `notify(\"search:indexed\", \"messages\")`.\n\nFor the host contract, authoring shape, storage rules, diagnostics behavior,\nand current non-goals, see [docs/extensions.md](docs/extensions.md).\n\nWhen moving a database that requires extensions, move or document the required\nextension code too. Bundled extensions such as `trgm` are available in the\nnormal Zova process. Dynamic local extensions must be trusted and supplied again\nby the receiving CLI command or application process; `.zova` files never\nauto-load them.\n\n## Diagnostics And Salvage\n\nZova keeps diagnostics non-mutating by default:\n\n```sh\nzova check app.zova\nzova check --deep app.zova\nzova doctor app.zova\nzova salvage --dry-run app.zova\n```\n\n`doctor` explains file health and suggests next actions. `salvage --dry-run`\nreports what appears recoverable. Real salvage writes readable, validated data\ninto a new file:\n\n```sh\nzova salvage damaged.zova recovered.zova\n```\n\nSalvage never mutates the source file and never overwrites the destination. A\ngood backup is still preferred when one exists. In v0.23.0, salvage is\ngraph-aware and extension-aware: it copies valid graph topology, skips invalid\ngraph nodes or edges, and lets trusted extension hooks recover their own private\nstorage.\n\nDiagnostics also include extension health. Unknown `_zova_ext_*` storage and\ncorrupt `trgm` private tables are\nreported as extension issues without printing indexed text or private schema\nSQL.\n\nExtension-aware salvage is hook-based. Core Zova never copies `_zova_ext_*`\ntables by guessing their meaning. If trusted extension code provides a salvage\nhook, Zova lets that extension copy, rebuild, or skip its own storage. If the\nextension code is unavailable or the extension has no salvage hook, extension\nstorage is skipped and reported. In v0.23.0, bundled `trgm` salvage recovers a\nvalid subset of trgm private storage, rebuilds derived term rows from copied\npostings, and still never prints indexed text or private schema SQL.\n\n## CLI\n\nThe CLI is for inspection, diagnostics, and operational workflows:\n\n```sh\nzova info app.zova\nzova stats --json app.zova\nzova objects app.zova\nzova object app.zova \u003cobject-id-hex\u003e\nzova chunks app.zova\nzova chunk app.zova \u003cchunk-id-hex\u003e\nzova vectors app.zova\nzova vector-collection app.zova chunks\nzova graphs app.zova\nzova graph app.zova app\nzova graph-node app.zova app message:123\nzova graph-neighbors --limit 20 app.zova app message:123\nzova graph-walk --max-depth 2 --limit 50 app.zova app message:123\nzova tables app.zova\nzova check --deep app.zova\nzova doctor --json app.zova\nzova object-store info app.zova\nzova vector-store info app.zova\nzova graph-store info app.zova\nzova split --graphs app.zova graphs.zova\nzova extension list app.zova\nzova extension check app.zova\n```\n\nJSON output includes `cli_json_version = 1`. CLI output is bounded and avoids\nprinting object bytes, chunk bytes, vector values, private schema SQL, and user\nrow values.\n\n## Bindings\n\n### Rust\n\nRust users normally use the safe crate:\n\n```toml\n[dependencies]\nzova = \"0.23.0\"\n```\n\nThe lower-level raw FFI crate is available as:\n\n```toml\n[dependencies]\nzova-sys = \"0.23.0\"\n```\n\n`zova` exposes `Database` for single-owner code and `SharedDatabase` for an\nopt-in cloneable `Send + Sync` handle. One shared handle is safe and internally\nserialized; open multiple handles for true SQLite concurrency.\n\nExisting Rust object, vector, and graph APIs transparently use a bound store after the\ndatabase is opened. Store create/bind/unbind/split management remains\nnative-Zig/CLI-only in v0.23.\n\nFrom crates.io, the Rust crates build through a bundled generated C snapshot, so\nnormal Rust users need Rust and a C compiler, not Zig. Zig is only needed when\ndeveloping Zova itself or regenerating the native snapshot.\n\n### Python\n\nInstall from PyPI:\n\n```sh\nuv add zova\n```\n\nor:\n\n```sh\npython -m pip install zova\n```\n\nThe Python package is a PyO3/maturin extension backed by the Rust `zova` crate.\nIt exposes records, prepared statements, transactions, savepoints, app events,\nbackup, compact, restore, objects, `ObjectWriter`, vectors, graphs,\nSQL-native vector and graph helpers, and bundled extension lifecycle APIs.\n\nPyPI releases include wheels for Linux/macOS x86_64/arm64 on CPython 3.10/3.12\nplus an sdist fallback. Wheel installs do not require Zig, Rust, or a local C\ncompiler. If pip falls back to the sdist for an unsupported platform/Python\ncombination, the build uses Rust/Cargo and a C compiler. Zig is only needed when\ndeveloping Zova itself or regenerating the bundled native snapshot.\n\nExisting Python object, vector, and graph APIs transparently use a bound store after the\ndatabase is opened. Store create/bind/unbind/split management remains\nnative-Zig/CLI-only in v0.23.\n\n### Go\n\nInstall:\n\n```sh\ngo get github.com/atasesli/zova/bindings/go@v0.23.0\n```\n\nImport:\n\n```go\nimport zova \"github.com/atasesli/zova/bindings/go\"\n```\n\nThe Go package uses cgo over `include/zova.h` and links `libzova_c.a`. The\nGitHub Release includes prebuilt C ABI archives for Go/manual embedding. Point\ncgo at an unpacked archive:\n\n```sh\nCGO_CFLAGS=\"-I/path/to/zova-c-abi/include\" \\\nCGO_LDFLAGS=\"-L/path/to/zova-c-abi/lib -lzova_c\" \\\ngo test ./...\n```\n\nOr build the C ABI first in this repository:\n\n```sh\nzig build c-abi\n```\n\nExisting Go object, vector, and graph APIs transparently use a bound store after the\ndatabase is opened. Store create/bind/unbind/split management remains\nnative-Zig/CLI-only in v0.23.\n\nExternal Go projects should point cgo at an installed Zova C ABI:\n\n```sh\nCGO_CFLAGS=\"-I/path/to/zova/include\" \\\nCGO_LDFLAGS=\"-L/path/to/zova/lib -lzova_c\" \\\ngo test ./...\n```\n\n### C ABI\n\nThe C ABI is the language-neutral integration layer:\n\n```c\n#include \"zova.h\"\n```\n\nIt uses opaque handles, request structs, fixed-width ids, explicit free\nfunctions, and `zova_status` return codes. Returned buffers, messages,\nmanifests, vectors, collection lists, and search results are owned by Zova and\nmust be freed with the matching `zova_*_free` function.\n\nOne `zova_database *` handle is internally serialized. Calls on the same handle\nrun one at a time. Multiple handles are the path for true concurrency and follow\nnormal SQLite locking behavior.\n\n### Zig\n\nZig users can import the package and use the native facade:\n\n```zig\nconst zova = @import(\"zova\");\n\nvar db = try zova.Database.create(\"app.zova\");\ndefer db.deinit();\n```\n\nThe thin SQLite wrapper is also public as `zova.sqlite`.\n\n## Build From Source\n\nBuild the CLI:\n\n```sh\nzig build\n```\n\nRun it:\n\n```sh\nzig build run\n```\n\nBuild the C ABI:\n\n```sh\nzig build c-abi\n```\n\nRun the C ABI smoke tests:\n\n```sh\nzig build c-abi-test\n```\n\nRun Rust checks:\n\n```sh\ncargo test --workspace --manifest-path bindings/rust/Cargo.toml\n```\n\nRun Go checks after building the C ABI:\n\n```sh\nzig build c-abi\ncd bindings/go\ngo test ./...\n```\n\nRun Python checks:\n\n```sh\nuv run --isolated --with maturin --with pytest --directory bindings/python maturin develop\nuv run --isolated --with pytest --directory bindings/python python -m pytest\n```\n\n## SQLite Policy\n\nZova does not hide SQLite. SQL remains SQLite SQL, locking remains SQLite\nlocking, and PRAGMAs remain application policy.\n\nZova does not silently enable `PRAGMA foreign_keys = ON`, does not run `VACUUM`\nautomatically, does not enable `auto_vacuum`, and does not change journal or\nsynchronous settings automatically.\n\n## Current Boundaries\n\nZova `0.23.0` does not include:\n\n- binding-level app-registered extension authoring APIs\n- binding-level dynamic `.zovaext` loading APIs\n- dynamic `.zovaext` loading from generated-C package artifacts\n- safe high-level Rust, Go, or Python SQL callback APIs\n- ANN indexes such as HNSW or IVFFlat\n- Zova-owned BM25 abstraction\n- vector SQL operators\n- object or chunk virtual tables\n- Cypher, GQL, Gremlin, SPARQL, or Neo4j compatibility\n- graph reconciliation/import engine\n- automatic graph extraction from SQL, documents, or LLM output\n- embedding generation\n- TypeScript or Swift bindings\n- background worker threads hidden inside Zova\n- cross-process notifications, durable notification replay, or automatic\n  mutation logging\n- in-place repair\n- overwrite mode for backup/compact/restore/salvage\n- bundle backup or multi-file restore packages\n- multiple named object/vector/graph stores or routing rules\n- automatic bound-store path repair\n- C ABI, Rust, Go, or Python store-management APIs\n- remote sync, S3 compatibility, NATS integration, or Redis-like behavior\n- Python wheels outside the current Linux/macOS CPython 3.10/3.12 matrix\n\nDiagnostics and salvage are CLI-first in this release. Bindings should not parse\nhuman text output as a stable library contract.\n\n## Testing\n\nRun the core tests:\n\n```sh\nzig build test\nzig build e2e\nzig build cli-test\nzig build c-abi-test\n```\n\nRun the full release smoke:\n\n```sh\nscripts/check-release.sh\n```\n\n## Release Package Policy\n\nZova publishes several release artifact types:\n\n- GitHub Release CLI archives for Linux x86_64, Linux arm64, macOS x86_64,\n  macOS arm64, and Windows x86_64.\n- GitHub Release C ABI archives for the same platform set.\n- GitHub Release generated-C source archives, used to prove the no-Zig native\n  build path.\n- A GitHub Release source archive.\n- Rust crates on crates.io: `zova-sys` and `zova`.\n- Python wheels and sdist on PyPI.\n- A Go module tag: `bindings/go/v0.23.0`.\n\nThe source archive includes:\n\n- `README.md`\n- `LICENSE`\n- `build.zig`\n- `build.zig.zon`\n- `docs`\n- `scripts`\n- `bindings/rust`\n- `bindings/go`\n- `bindings/python`\n- `include`\n- `src`\n- `tests`\n- `vendor`\n\nThe source archive does not include compiled CLI binaries, compiled C ABI\nlibraries, Rust `target` directories, Go build outputs, Python wheels, Python\nnative extensions, or cache directories.\n\nThe `zova-sys` crate does not include the full Zig source snapshot. It packages\nthe generated C bundle, `zig.h`, `zova.h`, vendored SQLite C sources, and the\nlicense needed to build the C ABI with a normal C compiler.\n\nMaintainer source-package command:\n\n```sh\nscripts/package-release.sh 0.23.0\n```\n\nMaintainer local distribution command for crates.io and PyPI, in that order:\n\n```sh\nscripts/distribute-release.sh 0.23.0\n```\n\nGitHub Actions provides the preferred release flow:\n\n1. Let CI pass on the exact commit.\n2. Run **Release Artifacts** with the release version to build source, CLI,\n   C ABI, generated-C, Python wheel, and Python sdist artifacts.\n3. Inspect the uploaded artifacts.\n4. Run **Publish Release** with the same version and the Release Artifacts run\n   ID. The publish workflow verifies that the artifacts came from the checked\n   out commit, uses the protected `release` environment, creates the GitHub and\n   Go module tags, creates or updates the GitHub Release, then publishes\n   crates.io and PyPI packages.\n\nThe publish workflow is intentionally tied to a specific Release Artifacts run:\nit refuses to publish artifacts built from a different commit.\n\nThe local scripts remain useful for maintainer smoke tests. Do not run release\nor distribution commands until the exact commit is ready to tag and publish.\n\nThe Go module tag is created by the protected publish workflow.\n\n## License\n\nZova is MIT licensed. See `LICENSE`.\n\nSQLite is vendored in `vendor/sqlite3.53.2` and is public domain.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fata-sesli%2Fzova","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fata-sesli%2Fzova","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fata-sesli%2Fzova/lists"}