Skip to main content

A client library for accessing reeeductio Spaces API

Project description

reeeductio Python SDK

A clean, modern Python SDK for the reeeductio Spaces API - an end-to-end encrypted messaging system with capability-based authorization.

Features

  • End-to-end encryption: Zero-knowledge server design
  • Blockchain-like message chains: Cryptographic integrity verification
  • Capability-based authorization: Granular, signed permissions
  • Multiple storage types:
    • Messages: Append-only topic streams
    • State: Event-sourced key-value store
    • Data: Simple signed key-value store
    • Blobs: Content-addressed binary storage
  • Real-time streaming: WebSocket support (coming soon)
  • Async support: Built on httpx for both sync and async usage
  • Type-safe: Full type hints and dataclass models

Installation

pip install reeeductio

Or install from source:

cd python-sdk
pip install -e .

Quick Start

from reeeductio import Space, generate_keypair
import os

# Generate a key pair
keypair = generate_keypair()
member_id = keypair.to_user_id()  # 44-char typed user identifier starting with 'U'

# Generate or retrieve the space's symmetric root key (32 bytes)
symmetric_root = os.urandom(32)  # In practice, derive from space key or shared secret

# Connect to a space
# The Space client automatically derives encryption keys on init:
#   - message_key (for encrypting messages)
#   - state_key (for encrypting state)
#   - data_key (for encrypting KV data)
with Space(
    space_id="Sabc123...",       # 44-char space ID
    member_id=member_id,         # typed user/tool identifier
    private_key=keypair.private_key,  # raw 32-byte Ed25519 private key
    symmetric_root=symmetric_root,    # 256-bit root key for encryption
    base_url="http://localhost:8000"
) as space:
    # Keys are available as space.message_key, space.state_key, space.data_key
    # Post a message
    result = space.post_message(
        topic_id="general",
        msg_type="chat",
        data=b"Hello, world!"  # Should be encrypted before passing
    )
    print(f"Posted message: {result.message_hash}")

    # Get messages
    messages = space.get_messages("general", limit=10)
    for msg in messages:
        print(f"{msg.sender}: {msg.data}")

    # Upload an encrypted blob
    blob = space.encrypt_and_upload_blob(b"file contents")
    print(f"Uploaded blob: {blob.blob_id}")

    # Set state
    space.set_state("profiles/alice", b'{"name": "Alice"}')

    # Get state
    state_msg = space.get_state("profiles/alice")
    print(f"State: {state_msg.data}")

Architecture

Components

  • Space: High-level client for interacting with a space
  • Topics: Blockchain-style append-only message streams
  • State: Event-sourced key-value store (stored as messages in the special state topic)
  • Data: Simple signed key-value store
  • Blobs: Content-addressed encrypted file storage
  • Auth: Challenge-response authentication with Ed25519 signatures

Cryptography

All identifiers use typed base64 encoding with a header byte:

  • User IDs start with U (e.g., Uabc123...)
  • Space IDs start with S (e.g., Sabc123...)
  • Message IDs start with M (e.g., Mabc123...)
  • Blob IDs start with B (e.g., Babc123...)
  • Tool IDs start with T (e.g., Tabc123...)

Message Chains

Messages form blockchain-like chains with hash pointers:

Message 1 → Message 2 → Message 3
  (M_abc)    (M_def)     (M_ghi)
              ↑            ↑
         prev_hash    prev_hash

Each message hash is computed over: topic_id|type|prev_hash|data|sender

Key Derivation

The Space client uses HKDF-SHA256 to derive encryption keys from the symmetric_root:

space = Space(space_id, keypair, symmetric_root)

# Automatically derived on initialization (scoped to space_id):
space.message_key  # HKDF(symmetric_root, info="message key | {space_id}") → 32 bytes
space.data_key     # HKDF(symmetric_root, info="data key | {space_id}") → 32 bytes
space.state_key    # HKDF(message_key, info="topic key | state") → 32 bytes

Security Benefits:

  • Single shared secret (symmetric_root) per space
  • Cryptographically independent keys for each data type
  • Domain separation: Keys are scoped to space_id to prevent cross-space key reuse
  • Deterministic: same root + same space always produces same keys
  • Forward-compatible for key rotation

Usage:

from reeeductio import derive_key

# Manual key derivation if needed
custom_key = derive_key(symmetric_root, "custom context", length=32)

API Reference

Space Client

class Space:
    def __init__(
        self,
        space_id: str,
        member_id: str,             # typed user/tool identifier (e.g. keypair.to_user_id())
        private_key: bytes,         # raw 32-byte Ed25519 private key
        symmetric_root: bytes,      # 256-bit (32-byte) root key for HKDF
        base_url: str = "http://localhost:8000",
        auto_authenticate: bool = True,
        local_store: LocalMessageStore | None = None,
        user_symmetric_key: bytes | None = None,  # optional user-private key
    )

    # Authentication
    def authenticate(self) -> str

    # Messages
    def post_message(self, topic_id: str, msg_type: str, data: bytes, prev_hash: str | None = None) -> MessageCreated
    def get_messages(self, topic_id: str, from_timestamp: int | None = None, to_timestamp: int | None = None, limit: int = 100, use_cache: bool = True, validate_chain: bool = True) -> list[Message]
    def get_message(self, topic_id: str, message_hash: str, use_cache: bool = True) -> Message

    # State
    def get_state(self, path: str) -> Message
    def set_state(self, path: str, data: bytes, prev_hash: str | None = None) -> MessageCreated
    def get_state_history(self, from_timestamp: int | None = None, to_timestamp: int | None = None, limit: int = 100) -> list[Message]

    # Data (KV)
    def get_data(self, path: str) -> DataEntry
    def set_data(self, path: str, data: bytes) -> int

    # Blobs
    def upload_plaintext_blob(self, data: bytes) -> BlobCreated
    def encrypt_and_upload_blob(self, data: bytes) -> EncryptedBlobCreated
    def download_plaintext_blob(self, blob_id: str) -> bytes
    def download_and_decrypt_blob(self, blob_id: str, key: bytes) -> bytes
    def delete_blob(self, blob_id: str) -> None

Crypto Utilities

from reeeductio import generate_keypair, sign_data, compute_hash

# Generate keys
keypair = generate_keypair()
user_id = keypair.to_user_id()
space_id = keypair.to_space_id()

# Sign data
signature = sign_data(b"data to sign", keypair.private_key)

# Hash data
hash_bytes = compute_hash(b"data to hash")

Development Status

Implemented

  • ✅ Core models (Message, Capability, etc.)
  • ✅ Cryptography (Ed25519 signing, HKDF key derivation, AES-GCM encryption)
  • ✅ Authentication (challenge-response)
  • ✅ Message posting and retrieval
  • ✅ State management
  • ✅ Data (KV) storage
  • ✅ Blob upload/download (plaintext and encrypted)
  • ✅ Sync API (Space) using httpx
  • ✅ Async API (AsyncSpace) with WebSocket streaming
  • ✅ Local message cache (LocalMessageStore)
  • ✅ Comprehensive test suite (pytest)

Coming Soon

  • ⏳ Additional examples

Testing

Run the test suite:

# Install dev dependencies
uv pip install -e ".[dev]"

# Run all tests (19 tests total)
uv run pytest

# Run with verbose output
uv run pytest -v

# Run specific test file
uv run pytest tests/test_key_derivation.py  # 18 key derivation tests
uv run pytest tests/test_smoke.py           # 1 comprehensive API smoke test

The test suite includes:

  • test_smoke.py: Comprehensive smoke test validating the entire public API surface
  • test_key_derivation.py: 18 tests covering HKDF key derivation, domain separation, and security properties

See tests/README.md for more details.

License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.

Contributing

Contributions welcome! Please open an issue or PR.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

reeeductio-0.1.1.tar.gz (74.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

reeeductio-0.1.1-py3-none-any.whl (67.3 kB view details)

Uploaded Python 3

File details

Details for the file reeeductio-0.1.1.tar.gz.

File metadata

  • Download URL: reeeductio-0.1.1.tar.gz
  • Upload date:
  • Size: 74.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for reeeductio-0.1.1.tar.gz
Algorithm Hash digest
SHA256 21c5bd27f6008c43faae9ba5fc71594fc3520f0cd0c84d7ce7963af8c291e785
MD5 940bf5424831a33ef544206960488bad
BLAKE2b-256 cf9f15f497f3b85abb9d33890ea8ed0ae58ecfcb3d70ce1d58d6bb15f11265f5

See more details on using hashes here.

File details

Details for the file reeeductio-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: reeeductio-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 67.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.5

File hashes

Hashes for reeeductio-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8bea653e83035fd599fe7dcb70e3a228f90a993eebd7d96cf9bf981db634dba7
MD5 65b8c1f51b2639a0e8307ef7b16bb2ed
BLAKE2b-256 9edac50e261b95996123f349cb2b66d2cf28d7ff6ac743be3e913d2b090011f2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page