CRP-SPEC-038: Pluggable Storage Backends - Bring Your Own Store¶
Document: CRP-SPEC-038
Title: Context Relay Protocol (CRP) - Pluggable Storage Backends: User-Definable Locations, Backends, and Visibility for Every Storage Primitive
Version: 1.0.0
Status: Foundational - Deployment Flexibility
Author: Constantinos Vidiniotis, AutoCyber AI Pty Ltd
Date: 2026-06-01
License: CC BY 4.0
Amends: CRP-SPEC-035 (storage primitives), CRP-SPEC-037 (config)
Prerequisites: CRP-SPEC-035, CRP-SPEC-037
Abstract¶
SPEC-035 defines five storage primitives (CKF graph, rolling context log, hot cache, inverted index, pointer-based ephemeral store) but treats their physical storage as internal. This is a real limitation for the local-first, data-sovereign deployments CRP targets: a developer cannot say "put my CKF in my own Postgres/pgvector," "store the rolling log on this path," or "let me inspect what's in my knowledge fabric." This document makes every storage primitive pluggable and locatable: each has a defined backend interface, ships with a sensible default backend, and can be pointed at a user-provided store through the unified config. It also defines visibility - how a developer inspects, exports, and views the contents of each primitive. The principle: CRP owns the access logic (the router, the formulas, the lifecycle) but the developer owns the data and its location. This is what makes CRP genuinely local-first and sovereign - your knowledge lives where you choose, in a store you control, inspectable by you, while CRP provides the intelligence layer over it.
1. The Principle: CRP Owns Logic, You Own Data¶
┌─────────────────────────────────────────────────────────┐
│ CRP owns: the ACCESS LOGIC │
│ - the router (which primitive per access, SPEC-035) │
│ - CDR/CDGR formulas, lifecycle, promotion rules │
│ - the protocol semantics │
├─────────────────────────────────────────────────────────┤
│ YOU own: the DATA and its LOCATION │
│ - which backend stores each primitive │
│ - where it physically lives (your infra, your cloud) │
│ - the ability to inspect, export, and delete it │
└─────────────────────────────────────────────────────────┘
This separation is what "bring your own store" means: CRP is the intelligent access layer; the storage is yours, wherever you want it, in whatever supported backend you choose.
2. The Backend Interface Per Primitive¶
Each of the five primitives (SPEC-035) defines a backend interface. CRP ships a default backend for each (works out of the box) and accepts any conforming backend the developer configures.
2.1 CKF Graph Backend¶
CKFBackend interface:
upsert_fact(fact, embedding, metadata)
search(query_embedding, k) -> facts # ANN
neighbours(fact_id) -> fact_ids # graph edges
delete(fact_id) # GDPR erasure
stats() -> {fact_count, communities, ...}
| Backend | When to use | How configured |
|---|---|---|
crp-native (default) | zero-setup, embedded HNSW | nothing - works out of the box |
pgvector | you already run Postgres | backend: pgvector, dsn: ... |
qdrant / weaviate / milvus | dedicated vector DB at scale | backend: qdrant, url: ... |
pinecone | managed vector cloud | backend: pinecone, api_key: ... |
| custom | your own store | implement CKFBackend |
2.2 Rolling Context Log Backend¶
RollingLogBackend interface:
append(entry)
read_recent(n) -> entries # pointer/offset read
roll_off() # evict tail
| Backend | When to use | Config |
|---|---|---|
mmap-file (default) | fast local, durable | path: ./crp/rolling.log |
memory | ephemeral, fastest | backend: memory |
redis | shared across instances | backend: redis, url: ... |
2.3 Hot Cache Backend¶
| Backend | When to use | Config |
|---|---|---|
memory-lru (default) | single instance | size: 1000 |
redis | shared cache, multi-instance | backend: redis, url: ... |
2.4 Inverted Index Backend¶
| Backend | When to use | Config |
|---|---|---|
crp-native (default) | embedded | nothing |
elasticsearch / opensearch | existing search infra | backend: elasticsearch, url: ... |
sqlite-fts | lightweight local | path: ./crp/index.db |
2.5 Pointer-Based Ephemeral Store Backend¶
| Backend | When to use | Config |
|---|---|---|
local-disk (default) | local blobs | path: ./crp/scratch/ |
s3 / gcs / azure-blob | cloud blob storage | backend: s3, bucket: ... |
memory | small, fastest | backend: memory |
3. Configuring Storage in the Unified Config¶
All storage locations and backends live in the context.storage section of the unified config (SPEC-037), extended here:
context:
storage:
# CKF - your knowledge fabric, your location
ckf:
backend: pgvector
dsn: "${YOUR_DATABASE_URL}"
table: "crp_facts"
# view/inspect endpoint auto-exposed (§4)
# Rolling context log - set your own location
rolling_log:
backend: mmap-file
path: "./crp/rolling.log"
size: 50 # ring buffer entries
# Hot cache
hot_cache:
backend: redis
url: "${YOUR_REDIS_URL}"
# Inverted index
inverted_index:
backend: sqlite-fts
path: "./crp/index.db"
# Ephemeral scratch (tool outputs)
scratch:
backend: s3
bucket: "my-crp-scratch"
ttl: "5m"
# default (omit everything): all crp-native/local, zero setup
Omit the whole section and everything uses the zero-setup defaults (embedded native stores, local files). Specify only what you want to relocate - e.g. just point the CKF at your pgvector and leave the rest default.
4. Visibility - Inspect, View, Export Your Data¶
A developer must be able to see what's in each primitive. Every backend exposes inspection through the SDK:
# CKF - what does CRP know?
client.knowledge.stats() # counts, communities, coverage
client.knowledge.search("etcd") # browse facts by query
client.knowledge.facts(source="manual.pdf") # facts from a doc
client.knowledge.location() # WHERE it's stored: "pgvector://...table=crp_facts"
client.knowledge.export("facts.jsonl") # full export
# Rolling log - what's the recent context?
client.context.rolling.view() # the current rolling window
client.context.rolling.location() # "./crp/rolling.log"
# Scratch - what tool data is held?
client.context.scratch.list() # active entries + summaries
client.context.scratch.location() # "s3://my-crp-scratch/"
# Everything - one overview
client.storage.overview()
client.storage.overview() prints a map of every primitive, its backend, its location, and its size:
CRP STORAGE MAP
CKF graph pgvector postgres://…/crp_facts 12,840 facts
Rolling log mmap-file ./crp/rolling.log 50 entries
Hot cache redis redis://… 312 cached
Inverted index sqlite-fts ./crp/index.db 8,201 terms
Scratch store s3 s3://my-crp-scratch/ 7 blobs (2 pinned)
This answers the exact questions: where is my CKF stored, where do I view it, where is my vector store, where is my rolling log - all visible, located, and exportable.
5. The Local-First / Sovereignty Guarantee¶
For AutoCyber AI's local-first positioning, this is essential. A developer can run CRP entirely on their own infrastructure:
context:
storage:
ckf: { backend: pgvector, dsn: "${LOCAL_PG}" }
rolling_log: { backend: mmap-file, path: "/data/crp/log" }
inverted_index: { backend: sqlite-fts, path: "/data/crp/idx" }
scratch: { backend: local-disk, path: "/data/crp/scratch" }
deployment:
mode: local # no hosted gateway; CRP runs in-process
No data leaves the developer's infrastructure. CRP provides the access intelligence; every byte of context lives in the developer's own stores. This is the data-sovereignty promise made concrete: your knowledge, your location, your control - CRP is just the brain over it.
6. Migration and Backend Switching¶
Switching backends (e.g. native → pgvector as you scale) is a config change plus a re-index:
client.storage.migrate(primitive="ckf", to="pgvector", dsn="...")
# re-embeds and re-indexes facts into the new backend; verifies count
Embedding-model consistency (SPEC-027 §2.5) is enforced across migration - the same embedding model must be used or a full re-embed is triggered and flagged.
7. Headers¶
| Header | Meaning |
|---|---|
CRP-Storage-CKF-Backend | The active CKF backend (e.g. pgvector) |
CRP-Storage-Location-Hash | Hash of the storage configuration (provenance) |
8. Honest Status & Limits¶
The backend interfaces are standard adapter patterns; shipping adapters for every store (pgvector, qdrant, pinecone, redis, elasticsearch, s3, etc.) is ongoing engineering - CRP ships the native defaults plus the most common adapters first, and the interface lets the community or the developer add others.
A user-provided backend is only as fast as that backend. The millisecond guarantees (SPEC-035) assume the default local/native stores or a low-latency configured store. Pointing the CKF at a slow remote vector DB trades sovereignty/scale for latency - the developer's choice, surfaced via CRP-Context-Retrieval-Ms.
Custom backends run in the retrieval path; they must meet the interface contract and latency expectations or the developer's own deployment degrades. CRP validates a configured backend on startup (connectivity, schema) and fails fast with a clear error rather than degrading silently.
9. References¶
- CRP-SPEC-035 - Context Lifecycle & Access Tiering (the primitives)
- CRP-SPEC-037 - Unified Config (where storage is configured)
- CRP-SPEC-027 - Retrieval Integrity (embedding-model consistency on migration)
Copyright © 2025–2026 AutoCyber AI Pty Ltd. Licensed under CC BY 4.0. CRP™ is a trademark of AutoCyber AI Pty Ltd.