The VectorPin CLI (vectorpin) provides commands for key generation, single-pin demos, and bulk auditing of vector-store collections. It ships with the Python package.
pip install vectorpin
# Or, if you'll audit a specific backend, pull its driver too:
pip install 'vectorpin[default]' # LanceDB
pip install 'vectorpin[chroma]'
pip install 'vectorpin[qdrant]'
pip install 'vectorpin[pinecone]'Confirm the install:
vectorpin --help| Command | Purpose |
|---|---|
vectorpin keygen |
Generate an Ed25519 signing key pair. |
vectorpin pin |
Sign a single (text, vector) pair (debug / demo). |
vectorpin verify-pin |
Verify a single Pin against ground-truth source / vector. |
vectorpin audit-lancedb |
Walk a LanceDB table and verify every pinned record. |
vectorpin audit-chroma |
Walk a Chroma collection and verify every pinned record. |
vectorpin audit-qdrant |
Walk a Qdrant collection and verify every pinned record. |
Generate an Ed25519 key pair for signing pins.
vectorpin keygen --key-id prod-2026-05 --output ./keysOptions:
| Flag | Required | Default | Description |
|---|---|---|---|
--key-id |
Yes | — | Identifier for the new key (travels in the pin's kid field). |
--output |
No | keys |
Output directory. Created if missing. |
Output files:
keys/
├── prod-2026-05.priv # Ed25519 private key, 32 bytes — KEEP SECRET
└── prod-2026-05.pub # Ed25519 public key, 32 bytes
Set restrictive permissions on the private key immediately:
chmod 600 ./keys/prod-2026-05.privSign one (source, vector) pair and print the pin JSON to stdout. Intended for demos and one-off integrations; production ingestion should use a vector store adapter so the pin is written next to the embedding atomically.
vectorpin pin \
--private-key ./keys/prod-2026-05.priv \
--key-id prod-2026-05 \
--model text-embedding-3-large \
--source ./doc.txt \
--vector ./embedding.npyOptions:
| Flag | Required | Description |
|---|---|---|
--private-key |
Yes | Path to .priv key file. |
--key-id |
Yes | Key identifier (written into the pin's kid field). |
--model |
Yes | Embedding model identifier (e.g., text-embedding-3-large). |
--source |
Yes | Path to UTF-8 source-text file. |
--vector |
Yes | Path to vector file: .npy (NumPy) or .json (array of floats). |
Output: Compact pin JSON to stdout; pipe to a file or directly into your DB write path.
Verify one pin against ground-truth source and/or vector. Both ground-truth inputs are optional — omit them to check only the signature (i.e., that the pin was produced by the registered key).
vectorpin verify-pin \
--public-key ./keys/prod-2026-05.pub \
--key-id prod-2026-05 \
--pin ./pin.json \
--source ./doc.txt \
--vector ./embedding.npyOptions:
| Flag | Required | Description |
|---|---|---|
--public-key |
Yes | Path to .pub key file. |
--key-id |
Yes | Key identifier (must match the pin's kid). |
--pin |
Yes | Path to pin JSON. |
--source |
No | Path to source text. If set, source is verified against source_hash. |
--vector |
No | Path to vector. If set, vector is verified against vec_hash. |
Exit codes:
| Code | Meaning |
|---|---|
0 |
OK — pin verified. |
2 |
Failure. Reason printed to stderr in the form FAIL [<error>] <detail>. |
Audit commands walk an entire collection, verify every pinned record, and print a JSON summary to stdout. They exit non-zero if any pinned record fails verification — so they compose cleanly into CI or cron.
Common audit summary shape:
{
"table": "rag-corpus",
"total": 12453,
"pinned": 12450,
"verified_ok": 12450,
"verification_failed": 0,
"unpinned": 3
}unpinned records fail the run unless --allow-unpinned is passed: an attacker who can rewrite a vector can delete its pin just as easily. Each one is named on stderr as FAIL <id> [unpinned]. An empty or whitespace-only pin field is always a parse_error, never "unpinned". A malformed pin does not stop the walk — every later record is still examined.
Flags shared by every audit-* command:
| Flag | Effect |
|---|---|
--allow-unpinned |
Tolerate records without a pin (partial backfills). |
--no-record-id-check |
Do not require a pin's vectorpin.record_id to equal the id of the record it is stored on (enforced by default — this is what defeats pin-shuffle replay). |
--collection-id ID / --tenant-id ID |
Require every pin to carry exactly this vectorpin.collection_id / vectorpin.tenant_id. |
--expected-model NAME / --expected-model-hash sha256:... |
Require the model identifier / weights hash. |
--manifest PATH |
Compare the walked record set against a signed collection manifest (count + Merkle root); --manifest-public-key / --manifest-key-id select a different key than the audit key. |
--write-manifest PATH --manifest-key PRIV --manifest-key-id KID |
Write a signed manifest of the walked record set (run right after ingestion). |
--sign-summary-key PRIV --sign-summary-key-id KID |
Sign the JSON summary; check it with vectorpin verify-summary. |
The summary gains "manifest": "ok" (or the mismatch reason) when --manifest is used, "manifest_written" when a manifest is written, "aborted" if the store walk failed part-way, and summary_kid / summary_ts / summary_sig when signed.
Audit a LanceDB table. The recommended default backend.
vectorpin audit-lancedb \
--uri ./data/vector_db \
--table rag-corpus \
--public-key ./keys/prod-2026-05.pub \
--key-id prod-2026-05 \
--source-column textOptions:
| Flag | Required | Default | Description |
|---|---|---|---|
--uri |
Yes | — | LanceDB URI: a directory, s3://, gs://, or LanceDB Cloud connection string. |
--table |
Yes | — | Table name. |
--public-key |
Yes | — | Path to .pub key. |
--key-id |
Yes | — | Key identifier. |
--id-column |
No | id |
Column holding the record id. |
--vector-column |
No | vector |
Column holding the embedding. |
--source-column |
No | — | Optional column holding the source text; if set, source is verified too. For Symbiont's default schema, use --source-column content. |
--batch-size |
No | 256 |
Records per batch. |
Audit a Chroma collection (persistent or HTTP).
# Persistent (file-based)
vectorpin audit-chroma \
--path ./chroma_db \
--collection my-rag \
--public-key ./keys/prod-2026-05.pub \
--key-id prod-2026-05 \
--source-metadata-key text
# HTTP server
vectorpin audit-chroma \
--host chroma.internal \
--port 8000 \
--collection my-rag \
--public-key ./keys/prod-2026-05.pub \
--key-id prod-2026-05Options:
| Flag | Required | Default | Description |
|---|---|---|---|
--collection |
Yes | — | Collection name. |
--public-key |
Yes | — | Path to .pub key. |
--key-id |
Yes | — | Key identifier. |
--path |
One of | — | Path for a PersistentClient. |
--host |
One of | — | Host for an HttpClient. (Either --path or --host is required.) |
--port |
No | 8000 |
HTTP port. |
--ssl |
No | off | Enable TLS for HTTP client. |
--source-metadata-key |
No | — | Optional metadata key holding the source text. |
--batch-size |
No | 256 |
Records per batch. |
Audit a Qdrant collection.
vectorpin audit-qdrant \
--url http://localhost:6333 \
--collection my-rag \
--public-key ./keys/prod-2026-05.pub \
--key-id prod-2026-05Options:
| Flag | Required | Default | Description |
|---|---|---|---|
--url |
Yes | — | Qdrant URL, e.g. http://localhost:6333. |
--collection |
Yes | — | Collection name. |
--public-key |
Yes | — | Path to .pub key. |
--key-id |
Yes | — | Key identifier. |
--api-key |
No | — | API key for Qdrant Cloud. |
--source-payload-key |
No | — | Optional payload key holding source text; if set, source is verified too. |
--batch-size |
No | 256 |
Records per batch. |
#!/usr/bin/env bash
set -euo pipefail
vectorpin audit-lancedb \
--uri "$VECTOR_DB_URI" \
--table "$VECTOR_TABLE" \
--public-key "$VECTORPIN_PUB" \
--key-id "$VECTORPIN_KID" \
--source-column text \
> /var/log/vectorpin/$(date +%Y-%m-%dT%H:%M).jsonNon-zero exit triggers your alerting; the JSON summary is the audit log.
- name: VectorPin audit
run: |
pip install 'vectorpin[default]'
vectorpin audit-lancedb \
--uri "${{ secrets.VECTOR_DB_URI }}" \
--table rag-corpus \
--public-key ./keys/prod-2026-05.pub \
--key-id prod-2026-05 \
--source-column textThe audit commands fail closed on unpinned records. While a corpus is being pinned for the first time, opt in explicitly and drop the flag once the backfill is complete:
vectorpin audit-lancedb --uri ./db --table t --public-key ./k.pub --key-id k --allow-unpinnedVerify a summary produced with --sign-summary-key:
vectorpin verify-summary --public-key ./keys/audit.pub --key-id audit --summary report.jsonExit code 0 on OK, 2 with FAIL [<error>] <detail> on stderr otherwise.
- Getting Started — Library usage in Python, Rust, and TypeScript
- Vector Store Adapters — Programmatic equivalents to the
audit-*commands - Deployment — Where to keep keys, how to rotate them