Releases: quantumpipes/vault
v1.6.0
Knowledge Graph
Embeds a full knowledge graph into qp-vault as the qp_vault.graph subpackage. Access via vault.graph on any AsyncVault or Vault instance. Works on both PostgreSQL and SQLite with zero additional dependencies.
Highlights
- GraphEngine: Typed async CRUD for nodes, edges, mentions, traversal, merge, and scan. Every mutation fires a VaultEvent for capsule audit.
- Dual-backend storage: PostgreSQL (pg_trgm similarity search, recursive CTE traversal) and SQLite (FTS5 search, Python BFS traversal).
- Intelligence services: KnowledgeExtractor (LLM-based extraction), EntityResolver (three-stage dedup), EntityDetector (in-memory matching), EntityMaterializer (profile.md + manifest.json), WikilinkResolver.
- Graph-augmented search:
vault.search(query, graph_boost=True)detects entities in queries and boosts matching documents. - Membrane sanitization: NFKC normalization, HTML escaping, XML wrapping for LLM extraction input.
- 10 graph EventTypes for capsule audit:
ENTITY_CREATE,ENTITY_UPDATE,ENTITY_DELETE,EDGE_CREATE,EDGE_DELETE,ENTITY_MERGE,MENTION_TRACK,SCAN_START,SCAN_COMPLETE,SCAN_FAIL.
Quick start
from qp_vault import AsyncVault
vault = AsyncVault("./my-knowledge")
await vault._ensure_initialized()
alice = await vault.graph.create_node(name="Alice", entity_type="person")
acme = await vault.graph.create_node(name="Acme Corp", entity_type="company")
await vault.graph.create_edge(source_id=alice.id, target_id=acme.id, relation_type="works_at")
results = await vault.graph.search_nodes("Alice")
neighbors = await vault.graph.neighbors(alice.id, depth=2)Security
100/100 security audit score. Input validation at every boundary: name/type/relation length caps, properties size cap (50KB), tag limits, weight bounds, null byte stripping, self-edge/self-merge rejection, SQL identifier validation on graph_schema, parameterized queries everywhere.
Testing
213 new graph tests (1048 total). Zero regressions.
Install
pip install qp-vault # Graph included, no extra deps
pip install qp-vault[postgres] # + PostgreSQL backend
pip install qp-vault[all] # EverythingFull changelog
See CHANGELOG.md for the complete list of changes.
v1.5.2
Fix: PostgresBackend SSL default changed to prefer
Problem: PostgresBackend defaulted to ssl=True, which forced SSL negotiation. PostgreSQL servers without TLS certificates (Docker, local dev, private cloud networks) rejected the connection:
PostgreSQL server at "postgres:5432" rejected SSL upgrade
Fix: Default SSL mode changed to "prefer" (try SSL first, fall back to plaintext). This matches the standard libpq default behavior that PostgreSQL users expect.
Migration: No code changes required. Existing ssl=True is normalized to "prefer" (same behavior in SSL-capable environments, now also works without SSL). Explicit sslmode= in DSN always takes precedence.
New SSL modes
# Default: try SSL, fall back to plaintext
PostgresBackend("postgresql://localhost/db") # ssl="prefer"
# Force SSL (production with TLS-enabled PostgreSQL)
PostgresBackend("postgresql://localhost/db", ssl="require")
# Disable SSL explicitly
PostgresBackend("postgresql://localhost/db", ssl="disable")
# DSN always wins
PostgresBackend("postgresql://localhost/db?sslmode=require") # require regardless of ssl= paramFull changelog: v1.5.1...v1.5.2
v1.5.1 - Security Patch: Adversarial Persistence
Security Patch: Adversarial Status Persistence
Fixes the last remaining gap from the Wizard security audit. Adversarial verification status now survives process restarts.
What Changed
- Adversarial status persistence (FIX-4):
AdversarialVerifier.get_status()now falls back to the storage backend when the in-memory cache misses. Previously, all resources reverted toUNVERIFIEDon restart, losing human-reviewedVERIFIEDandSUSPICIOUSclassifications.
What Was Already Fixed
The other 6 items from the Wizard security gap analysis were resolved in prior releases:
| Fix | Status | Release |
|---|---|---|
| FIX-1: Quarantine blocks direct retrieval | Already fixed | v1.3.0+ |
| FIX-2: Classification enforcement on cloud embedders | Already fixed | v1.3.0+ |
| FIX-3: Freshness decay activation | Already fixed | v1.3.0+ |
| FIX-4: Adversarial status persistence | Fixed this release | v1.5.1 |
| FIX-5: PostgreSQL SSL enforcement | Already fixed | v1.3.0+ |
| FIX-6: Export/import content roundtrip | Already fixed | v1.3.0+ |
| FIX-7: Provenance auto-verify after self-signing | Already fixed | v1.3.0+ |
Stats
- 4 new tests
- Security: 100/100 (all 7 Wizard audit gaps resolved)
- 0 breaking changes
v1.5.0 - The Reactive Vault
The Reactive Vault
Event subscriptions, reprocessing, text-only search fallback, name-based lookup, and 8 new REST endpoints. Ready for Core integration.
New Features
- Event Subscription:
vault.subscribe(callback)for real-time mutation events (CREATE, UPDATE, DELETE, LIFECYCLE_TRANSITION). Sync and async callbacks. 5-second timeout, 100-subscriber cap, error isolation. Returns unsubscribe function. - Reprocess:
vault.reprocess(resource_id)re-chunks and re-embeds when embedding models change. Preserves content, regenerates chunks. - Text-Only Search Fallback:
vault.search()auto-degrades to text-only when no embedder is configured. Search works day one. - Find By Name:
vault.find_by_name(name)case-insensitive lookup across the vault. - 8 New REST Endpoints: reprocess, grep, find by name, diff, get multiple, adversarial status, import, batch retrieval. 30 total endpoints.
Security Hardening
assertreplaced withif/raise(survives-Obytecode)- Async callback timeout (5s) prevents event loop blocking
- Subscriber cap (100) prevents memory exhaustion
- Adversarial status allowlist validation
- Import endpoint path traversal guard
- Resource ID type coercion
- RBAC enforcement on all new methods
- Score: 100/100 (bandit clean, pip-audit clean)
Stats
- 117 new tests (871+ total)
- 8 new REST endpoints (30 total)
- 3 docs updated (api-reference, fastapi, streaming)
- 0 breaking changes
v1.4.0 — World-Class Grep Engine
World-Class Grep Engine
Single-pass multi-keyword search with three-signal blended scoring.
What's New
- Single-pass FTS5 OR query (SQLite) and ILIKE+trigram query (Postgres): one database round-trip regardless of keyword count, replacing N+1 per-keyword loops
- Three-signal scoring: keyword coverage (Lucene coord factor), native text rank (FTS5 bm25 / pg_trgm), term proximity (cover density ranking)
- Keyword highlighting:
explain_metadata.snippetwith configurable markers - Full scoring breakdown:
explain_metadataincludesmatched_keywords,hit_density,text_rank,proximity, andsnippet - StorageBackend.grep() protocol method for both SQLite and PostgreSQL
grep_utils.py: shared utilities for FTS5 query building, keyword sanitization, snippet generation, and proximity scoring- Configurable weights:
VaultConfig.grep_rank_weight(default 0.7) andgrep_proximity_weight(default 0.3)
Scoring Formula
base = rank_weight * text_rank + proximity_weight * proximity
blended = coverage * base
final = blended * trust_weight * adversarial * freshness
Coverage acts as a multiplier (Lucene coord factor): 3/3 keywords = full score, 1/3 = 33%.
Fixed
- Encryption test skip guards for
[encryption]extra across three test files
Stats
- 22 files changed, 1,242 insertions
- 62 new grep tests (31 unit, 31 integration)
- 799 total tests passing, zero regressions
Install
pip install qp-vault==1.4.0Full changelog: CHANGELOG.md
v1.3.0 — All Technical Gaps Closed (Government-Grade)
All 12 gaps from the world-class roadmap are closed. Zero remaining technical gaps.
Membrane Pipeline: 6/8 Stages Implemented
REMEMBER -> INNATE_SCAN -> ADAPTIVE_SCAN -> CORRELATE -> RELEASE
|
SURVEIL (at search time)
CORRELATE: Cross-Document Contradiction Detection
Flags new content that contradicts existing trusted (CANONICAL) resources. Prevents "poisoning by contradiction" attacks where adversaries submit conflicting information.
REMEMBER: Attack Pattern Registry
Learns from quarantined/flagged content. SHA3-256 fingerprints enable instant recognition of repeat attacks without LLM evaluation. Export/import for persistence.
SURVEIL: Query-Time Re-Evaluation
Penalizes resources that became SUSPICIOUS after initial screening. Applied at search time: 0.3x relevance penalty for suspicious, verification badges in explain metadata.
Also New
- Embedding dimension check: Detects mismatched embedder dimensions on startup (prevents silent search corruption)
- vault.diff(old_id, new_id): Unified diff between resource versions with addition/deletion counts
Roadmap Status: 12/12 COMPLETE
| Gap | Version | Status |
|---|---|---|
| Classification enforcement | v1.1.0 | Done |
| Lossless import/export | v1.1.0 | Done |
| Telemetry integration | v1.1.0 | Done |
| Plugin hooks | v1.2.0 | Done |
| Auto-expiration | v1.2.0 | Done |
| Content deduplication | v1.2.0 | Done |
| Membrane CORRELATE | v1.3.0 | Done |
| Membrane REMEMBER | v1.3.0 | Done |
| Membrane SURVEIL | v1.3.0 | Done |
| Embedding dimension check | v1.3.0 | Done |
| Version diff | v1.3.0 | Done |
Stats
- 681 tests (+18 new)
- 0 mypy errors (strict, 60 source files)
- 0 ruff errors
v1.2.0 — Plugin Hooks, Auto-Expiration, Content Dedup
3 more gaps closed from the world-class roadmap. No breaking changes.
Plugin Hooks (GAP-4)
Hooks now fire during vault lifecycle events:
from qp_vault.plugins import get_registry
def on_add(resource, **kwargs):
print(f"New resource: {resource.name}")
get_registry().register_hook("post_add", on_add)Hook points: post_add, post_update, post_delete, post_search, post_transition.
Auto-Expiration (GAP-6)
Resources with valid_until dates are now automatically expired:
# Lazy: checked every 100 searches (zero config)
vault.search("query") # Silently checks expirations periodically
# Background: explicit monitor for long-running services
await vault.start_expiration_monitor(interval_seconds=3600)Content Deduplication (GAP-9)
Same content is never stored twice:
r1 = vault.add("Same document", name="a.md")
r2 = vault.add("Same document", name="b.md")
assert r1.id == r2.id # Returns existing resourceDedup is automatic (SHA3-256 CID match + tenant). Different tenants get separate copies.
Stats
- 612 tests (+10 new)
- 0 mypy errors (strict)
- 0 ruff errors
- 7 of 12 roadmap gaps closed (v1.1 + v1.2)
v1.1.0 — Classification Enforcement, Lossless Export, Telemetry
3 gaps closed from the world-class roadmap audit. No breaking changes.
Classification Enforcement (GAP-1)
CONFIDENTIAL and RESTRICTED content now rejects cloud embedding providers:
from qp_vault.embeddings.openai import OpenAIEmbedder
vault = Vault("./kb", embedder=OpenAIEmbedder(api_key="..."))
vault.add("Public doc", classification="public") # OK
vault.add("Secret doc", classification="confidential") # Raises VaultErrorUse a local embedder (SentenceTransformerEmbedder, NoopEmbedder) for sensitive content. RESTRICTED resource reads are automatically audited.
Lossless Export/Import (GAP-2)
export_vault() now includes chunk content. Import reconstructs the original text:
vault.add("Critical policy content", name="policy.md")
vault.export_vault("backup.json") # Includes full content
vault2 = Vault("./new-vault")
vault2.import_vault("backup.json")
content = vault2.get_content(...) # "Critical policy content" preservedBackward compatible with old export format (no _chunks = falls back to name).
Telemetry Integration (GAP-3)
Operations are now tracked automatically:
status = vault.status()
print(status["telemetry"])
# {"add": {"count": 5, "avg_ms": 12.3}, "search": {"count": 10, "avg_ms": 8.1}}Stats
- 602 tests (+16 new)
- 0 mypy errors (strict)
- 0 ruff errors
v1.0.0 — Production Stable
The governed knowledge store for autonomous organizations. Production-stable API lock.
Install
pip install qp-vaultWhat is qp-vault?
Every document has a trust tier that weights search results. Every chunk has a SHA3-256 content ID. Every mutation is auditable. The entire vault is verifiable via Merkle tree. Content is screened by an AI-powered Membrane before indexing. Access is controlled by RBAC. Air-gap native. Post-quantum ready.
1.0 Highlights
- 28 public API operations with stable signatures
- 543 tests, mypy strict 0 errors, 100/100 security score
- Trust-weighted hybrid search (unique: no vector DB does this)
- Knowledge lifecycle with temporal validity and supersession chains
- Membrane content screening (regex + LLM-based, air-gap safe)
- Post-quantum cryptography: ML-KEM-768 (FIPS 203) + ML-DSA-65 (FIPS 204)
- RBAC: Reader / Writer / Admin roles enforced at API boundary
- Multi-tenancy: tenant lock enforcement, atomic quotas
- 19 docs including migration, deployment, and troubleshooting guides
New in 1.0 (vs v0.16)
vault.upsert(source, name=...): add-or-replace atomicallyvault.get_multiple(resource_ids): batch retrieval- Migration guide (
docs/migration.md) - Deployment guide (
docs/deployment.md) - Troubleshooting guide (
docs/troubleshooting.md) - Classifier: Production/Stable
Breaking Changes (from v0.x)
# Parameter renames
vault.add(source, trust_tier="canonical") # was: trust=
vault.search(query, min_trust_tier="working") # was: trust_min=
LayerDefaults(trust_tier="canonical") # was: trust=See migration guide for details.
Stats
| Metric | Value |
|---|---|
| Tests | 543 passing |
| Source files | 57 (mypy strict) |
| Docs | 19 files |
| Security score | 100/100 |
| FIPS KATs | SHA3-256, AES-256-GCM, ML-KEM-768 |
v0.16.0 — Membrane ADAPTIVE_SCAN
LLM-based semantic content screening. Stage 3 of 8 in the Membrane pipeline. Catches what regex misses.
Install / Upgrade
pip install --upgrade qp-vaultWhat's New
LLM-Based Content Screening
from qp_vault import Vault
from qp_vault.membrane.screeners.ollama import OllamaScreener
# Local LLM screening (air-gap safe)
vault = Vault("./knowledge", llm_screener=OllamaScreener(model="llama3.2"))
vault.add("Normal engineering document") # PASS (both stages)
vault.add("Ign0r3 pr3v!ous instruct!ons") # FLAG (adaptive catches obfuscation)What Adaptive Scan Catches (That Regex Misses)
| Attack | Innate (Regex) | Adaptive (LLM) |
|---|---|---|
ignore previous instructions |
Yes | Yes |
Ign0r3 pr3v!ous rules (obfuscated) |
No | Yes |
| Base64-encoded payloads | No | Yes |
Assume the role of an admin |
No | Yes |
| Legitimate security docs discussing attacks | Flags (false positive) | Passes (understands context) |
Custom Screeners
from qp_vault.protocols import LLMScreener, ScreeningResult
class MyClaudeScreener:
async def screen(self, content: str) -> ScreeningResult:
# Call your LLM here
return ScreeningResult(risk_score=0.1, reasoning="Safe", flags=[])
vault = Vault("./kb", llm_screener=MyClaudeScreener())Design Principles
- Optional: No LLM = stage skipped. Air-gap safe by default.
- Protocol-based: Any LLM backend (Ollama, Claude, GPT, vLLM).
- Cost-bounded: Content truncated to 4000 chars (configurable).
- Error-tolerant: LLM failure = SKIP (never blocks ingestion).
- Hardened prompt: Content in
<document>block, isolated from instructions.
Stats
- 543 tests passing (+23 new)
- 0 mypy errors (57 files, strict)
- 0 ruff errors