Private.Me xLink for Haystack (Python)
Identity-based authentication for Haystack RAG pipelines.
Python bindings for @private.me/haystack. Build Haystack RAG pipelines with cryptographic identity instead of API keys. Zero-config setup, enterprise-grade access control, no cascading failures.
Installation
Prerequisites
This package requires both the Node.js module and Python bindings:
# 1. Install Node.js package
npm install @private.me/haystack haystack
# 2. Install Python bindings
pip install private-me-haystack
Requirements:
- Python 3.9+
- Node.js 18+ (for backend)
- npm (for Node.js package installation)
- Haystack 2.x
Quick Start
from private_me import haystack_xlink
# Create RAG pipeline (generates identity automatically)
rag = await haystack_xlink.create_rag(verbose=True)
# Add documents with access control
doc = haystack_xlink.create_document(
content='Sensitive information',
owner_did=rag.get_did(),
access_level='confidential' # public | internal | confidential
)
await rag.add_document(doc)
# Retrieve with identity-based access
result = await rag.retrieve('query', agent_did)
# Returns only documents the requestor has access to
That's it. No API keys, no OAuth, no configuration files.
API Reference
create_rag(config: Dict[str, Any] = None) -> HaystackXLinkRAG
Create RAG pipeline with identity-based authentication.
Parameters:
config(Dict[str, Any], optional): Configuration optionsname(str): Pipeline nameverbose(bool): Enable logging (default: False)enforce_access_control(bool): Enable access control checks (default: True)
Returns:
HaystackXLinkRAG: RAG pipeline instance
Example:
rag = await haystack_xlink.create_rag({
'name': 'my-rag',
'verbose': True,
'enforce_access_control': True
})
create_document(content: str, owner_did: str, access_level: str = 'public', metadata: Dict[str, Any] = None) -> Dict[str, Any]
Create document with access control metadata.
Parameters:
content(str): Document contentowner_did(str): Owner's decentralized identifieraccess_level(str): Access level -public,internal, orconfidential(default:public)metadata(Dict[str, Any], optional): Additional metadatatitle(str): Document titlecategory(str): Document categoryauthorized_dids(List[str]): List of DIDs with access to confidential documents
Returns:
Dict[str, Any]: Document object
Example:
doc = haystack_xlink.create_document(
content='Customer data: 50K users, $2.5M MRR',
owner_did=rag.get_did(),
access_level='confidential',
metadata={
'title': 'Q4 Revenue Report',
'category': 'financial',
'authorized_dids': ['did:key:z6Mkcollaborator...']
}
)
HaystackXLinkRAG.get_did() -> str
Get pipeline's decentralized identifier.
Returns:
str: DID (e.g.,did:key:z6Mk...)
Example:
did = rag.get_did()
print(f"RAG Identity: {did}")
HaystackXLinkRAG.add_document(doc: Dict[str, Any]) -> str
Add document to index.
Parameters:
doc(Dict[str, Any]): Document created viacreate_document()
Returns:
str: Document ID
Raises:
RuntimeError: If Node.js backend failsValueError: If document is invalid
Example:
doc_id = await rag.add_document(doc)
print(f"Document added: {doc_id}")
HaystackXLinkRAG.retrieve(query: str, requestor_did: str, limit: int = 10) -> List[Dict[str, Any]]
Retrieve documents matching query with access control.
Parameters:
query(str): Search queryrequestor_did(str): Requestor's DID for access controllimit(int): Maximum number of results (default: 10)
Returns:
List[Dict[str, Any]]: List of matching documents (filtered by access control)
Raises:
RuntimeError: If Node.js backend fails
Example:
results = await rag.retrieve('budget', engineering_did, limit=5)
for doc in results:
print(f"- {doc['meta']['title']}")
HaystackXLinkRAG.list_documents() -> List[Dict[str, Any]]
List all documents in index (admin/monitoring only, no access control).
Returns:
List[Dict[str, Any]]: All documents
Example:
all_docs = rag.list_documents()
print(f"Total documents: {len(all_docs)}")
HaystackXLinkRAG.clear() -> None
Clear all documents from index.
Example:
await rag.clear()
HaystackXLinkRAG.export_identity() -> bytes
Export identity for persistence.
Returns:
bytes: PKCS8 private key
Example:
identity = await rag.export_identity()
with open('rag-identity.key', 'wb') as f:
f.write(identity)
from_identity(pkcs8: bytes, config: Dict[str, Any] = None) -> HaystackXLinkRAG
Create RAG from existing identity.
Parameters:
pkcs8(bytes): PKCS8 private keyconfig(Dict[str, Any], optional): Configuration options
Returns:
HaystackXLinkRAG: RAG pipeline with restored identity
Example:
with open('rag-identity.key', 'rb') as f:
identity = f.read()
restored = await haystack_xlink.from_identity(identity)
print(restored.get_did()) # Same DID as original
Usage Examples
Basic RAG Pipeline
from private_me import haystack_xlink
# Create pipeline
rag = await haystack_xlink.create_rag({
'name': 'my-rag',
'enforce_access_control': True
})
print(f"RAG Identity: {rag.get_did()}")
# Add document
doc = haystack_xlink.create_document(
content='Customer data: 50K users, $2.5M MRR',
owner_did=rag.get_did(),
access_level='confidential'
)
await rag.add_document(doc)
# Retrieve documents
results = await rag.retrieve('users', rag.get_did())
print(f"Found {len(results)} documents")
for doc in results:
print(f"- {doc['meta']['title']}")
Multi-Agent Document Sharing
from private_me import haystack_xlink
# Create two agents
finance = await haystack_xlink.create_rag({'name': 'finance'})
engineering = await haystack_xlink.create_rag({'name': 'engineering'})
# Finance adds confidential document with authorized access
doc = haystack_xlink.create_document(
content='Budget allocation for Q4',
owner_did=finance.get_did(),
access_level='confidential',
metadata={
'authorized_dids': [engineering.get_did()] # Grant access
}
)
await finance.add_document(doc)
# Engineering can access the shared document
results = await finance.retrieve('budget', engineering.get_did())
print(f"Found {len(results)} shared documents")
Identity Persistence
from private_me import haystack_xlink
import os
# Export identity
rag = await haystack_xlink.create_rag()
identity = await rag.export_identity()
with open('rag-identity.key', 'wb') as f:
f.write(identity)
# Restore RAG with same identity
with open('rag-identity.key', 'rb') as f:
identity_bytes = f.read()
restored = await haystack_xlink.from_identity(identity_bytes)
print(restored.get_did() == rag.get_did()) # True
Access Control Enforcement
from private_me import haystack_xlink
rag = await haystack_xlink.create_rag({'enforce_access_control': True})
owner_did = rag.get_did()
other_user_did = 'did:key:z6Mkother_user'
# Add confidential document
doc = haystack_xlink.create_document('Secret data', owner_did, 'confidential')
await rag.add_document(doc)
# Owner can retrieve
owner_results = await rag.retrieve('secret', owner_did)
print(len(owner_results)) # 1 (found)
# Other user cannot retrieve
other_results = await rag.retrieve('secret', other_user_did)
print(len(other_results)) # 0 (access denied)
Three-Tier Access Control
| Level | Accessible To | Use Case |
|---|---|---|
| public | Everyone | Company handbook, privacy policy |
| internal | Team members | API docs, technical specs |
| confidential | Owner + authorized | Financial reports, customer data |
Architecture
This package uses a wrapper pattern:
Python App → Python Bindings → Node.js Backend → Haystack RAG
- Python layer: Provides Pythonic API (
create_rag(),retrieve()) - Node.js backend: Handles cryptographic operations (Ed25519, DID generation)
- Haystack integration: Document indexing, retrieval, access control enforcement
Troubleshooting
"Node.js module not found"
Error:
RuntimeError: Node.js module @private.me/haystack not found
Solution:
# Install Node.js package first
npm install @private.me/haystack haystack
# Verify installation
ls node_modules/@private.me/haystack
"Node.js backend error"
Error:
RuntimeError: Node.js backend error: <message>
Solution:
- Check Node.js version (requires 18+):
node --version - Verify Node.js package installed:
npm list @private.me/haystack - Check Haystack installed:
npm list haystack - Review Node.js error message for details
Access denied errors
Error:
Empty results despite matching documents
Solution:
- Verify
requestor_didhas access (checkaccess_levelandauthorized_dids) - For confidential documents: add requestor DID to
authorized_didslist - For internal documents: ensure requestor is a team member
- Check
enforce_access_controlis True
Invalid document errors
Error:
ValueError: Invalid document format
Solution:
- Use
create_document()helper function - Verify
owner_didis a valid DID (starts withdid:key:) - Check
access_levelis one of:public,internal,confidential
Development
Running Tests
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest -v
# Run with coverage
pytest -v --cov=private_me --cov-report=html
Building
# Build wheel
python setup.py bdist_wheel
# Validate build
bash validate-build.sh
Support
- Documentation: https://private.me/docs/haystack
- White Paper: https://private.me/docs/haystack.html
- Email: contact@private.me
- GitHub: https://github.com/xail-io/xail
License
Proprietary - See LICENSE.md
Questions? Visit private.me/docs/haystack for complete documentation.
Metadata
Release files for private-me-haystack 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| private_me_haystack-0.2.0.tar.gz | 24.1 kB | Without provenance | Details for private_me_haystack-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| private_me_haystack-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for private_me_haystack-0.2.0-py3-none-any.whl |
Total release size: 41.3 kB
Release files / private_me_haystack-0.2.0.tar.gz
| Download URL | private_me_haystack-0.2.0.tar.gz |
|---|---|
| Size | 24.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
54792e5aa3ed36e0c5819e45b097fb64af0d1ff092807c96c3a7a71f3881a6f4
|
|
BLAKE2b-256 checksum How to use checksums |
48152e36925f70f3057ad2c83cf5f6eb93c2db0bf491dd89bb2837366c83fac8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Release files / private_me_haystack-0.2.0-py3-none-any.whl
| Download URL | private_me_haystack-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
30968282ede993ce377ba7f2e8a051f9f7f10ed71e850f1b776e064e32ea793d
|
|
BLAKE2b-256 checksum How to use checksums |
2191cb3fe0a00de4c52d17c091f06e4a9a3a000455a5e2147d5582a66fa839b6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.3
|
Security for private-me-haystack 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.