Tsaheylu Documentation
Everything you need to integrate agent-state persistence into your AI agents. Install the Python SDK in one line, or use the REST API directly.
Overview
Tsaheylu is a persistent state management service for AI agents — think of it as Dropbox for agent memory. Your agent registers once, periodically syncs its state, and when it crashes, migrates, or restarts, it recovers exactly where it left off.
Every snapshot is encrypted with AES-256-GCM, verified with SHA-256 hashing, and stored with versioning. On recovery, the hash is re-verified to guarantee data integrity — what you put in is exactly what you get back.
New: The official Python SDK is live on PyPI — get started with just 3 lines of code.
pip install tsaheylu → tsaheylu.init('token') → tsaheylu.sync('agent1', state). See the Python SDK section below.Python SDK
The official Python SDK is the fastest way to add state persistence to your agents. Available on PyPI (v0.1.0).
Installation
pip install tsaheylu
Quickstart — 3 Lines
import tsaheylu
tsaheylu.init("your-operator-token-32-chars-min")
tsaheylu.sync("my-agent", {"memory": ["task1", "task2"], "step": 42})
# Later, when restarting:
state = tsaheylu.restore("my-agent")
print(state) # {"memory": ["task1", "task2"], "step": 42}OOP Usage
from tsaheylu import Client client = Client(api_key="your-operator-token-32-chars-min") # Sync state client.sync("research-bot", {"papers": ["arxiv: 2401.1234"], "progress": 0.7}) # Recover state state = client.restore("research-bot")
Error Handling
from tsaheylu.exceptions import HandleTakenError, HashMismatchError
try:
tsaheylu.sync("my-agent", state)
except HandleTakenError:
print("Handle already registered by another operator")
except HashMismatchError:
print("Integrity check failed — retry")Getting Started (REST API)
Get your agent connected in three steps. The easiest way is the registration page — you'll receive your agent ID and operator token instantly.
Step 1 — Register your agent
Option A: Use the registration page (recommended). You'll get your agent_id and operator_token immediately.
Option B: Register via the API:
curl -X POST https://api.tsaheylu.club/agent/signup \ -H "Content-Type: application/json" \ -d '{ "handle": "my-agent", "operator_handle": "your-name" }'
{
"agent_id": "a1b2c3d4-e5f6-...",
"handle": "my-agent",
"operator_token": "abc123..." // Save this! Shown only once.
}Authorization: Bearer headers.Step 2 — Snapshot your state
Compute a SHA-256 hash of your state, then send both. Replace the token and agent ID with your real values from Step 1:
# Set your real values: TOKEN="your-operator-token-from-registration" AGENT_ID="your-agent-id-from-registration" # Capture state and compute hash: STATE='{ "memory": ["task1", "task2"], "step": 42 }' HASH=$(echo -n "$STATE" | sha256sum | awk '{print $1}') # Send snapshot: curl -X POST https://api.tsaheylu.club/agent/snapshot \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d "{\"agent_id\":\"$AGENT_ID\",\"state_blob\":\"$STATE\",\"hash\":\"$HASH\"}"
STATE=$(tar czf - /path/to/agent/state | base64 -w0). On recovery, pipe through base64 -d | tar xzf -.Step 3 — Recover on restart
curl -s https://api.tsaheylu.club/agent/recover/$AGENT_ID \ -H "Authorization: Bearer $TOKEN"
{
"snapshot_id": "uuid",
"state_blob": "{ \"memory\": [\"task1\", \"task2\"], \"step\": 42 }",
"verification_status": "verified",
"version": 1,
"recovery_event_id": "uuid"
}API Reference
All endpoints are authenticated with your operator_token via theAuthorization: Bearer header (except registration, which takes the token in the body). Rate limits apply to all endpoints.
Request Body
| Parameter | Type | Description |
|---|---|---|
| handle | string | Unique agent name. 2–64 chars, alphanumeric with _ or -. |
| operator_handle | string | Your name or alias (for display purposes). |
| string | Optional. For account recovery and updates. |
Response — 201
{
"agent_id": "uuid",
"handle": "my-agent",
"operator_token": "abc123..." // Save this! Shown only once.
}operator_token is returned only once. Save it immediately. It cannot be recovered. Use it in all Authorization: Bearer headers.Errors
| Code | Status | Description |
|---|---|---|
| VALIDATION_ERROR | 400 | Invalid handle format or missing fields |
| HANDLE_TAKEN | 409 | Handle already registered by another operator |
Request Body
| Parameter | Type | Description |
|---|---|---|
| agent_id | uuid | Agent ID from registration |
| state_blob | string | Agent state data (max 10MB). Typically serialized JSON. |
| hash | string | Lowercase hex SHA-256 of state_blob. Exactly 64 chars. |
Response — 201
{
"snapshot_id": "uuid",
"stored_at": "2025-01-15T10: 31: 00.000Z",
"verified_hash": "e3b0c44298fc1c...",
"version": 1
}Errors
| Code | Status | Description |
|---|---|---|
| VALIDATION_ERROR | 400 | Missing or invalid fields |
| FORBIDDEN | 403 | Operator does not own this agent |
| HASH_MISMATCH | 422 | Provided hash does not match SHA-256(state_blob) |
SHA-256(state_blob) and compares it to your provided hash using constant-time comparison. If they don't match, the snapshot is rejected.URL Parameters
| Parameter | Type | Description |
|---|---|---|
| agent_id | uuid | The agent ID to recover state for |
Response — 200
{
"snapshot_id": "uuid",
"state_blob": "{\"memory\": [\"task1\", \"task2\"]}",
"stored_at": "2025-01-15T10: 31: 00.000Z",
"hash": "e3b0c44298fc1c...",
"verification_status": "verified",
"version": 3,
"recovery_event_id": "uuid"
}Recovery Process
- Verify operator owns the agent
- Fetch the latest snapshot (by version)
- Decrypt and verify integrity
- Create an audit record
- Return the decrypted state blob
Errors
| Code | Status | Description |
|---|---|---|
| FORBIDDEN | 403 | Operator does not own this agent |
| NOT_FOUND | 404 | No snapshots exist for this agent |
Architecture
Tsaheylu is built on a layered cryptographic architecture to ensure your agent's state is always secure, verifiable, and recoverable.
Military-Grade Encryption
Every state blob is encrypted at rest using AES-256-GCM — the same standard used by governments and financial institutions. Encryption keys are server-managed and never leave the infrastructure.
Hash Verification
Both client and server independently compute cryptographic hashes of your state data. On recovery, hashes are re-verified to guarantee bit-for-bit integrity of your recovered state.
Distributed Key Management (Phase 2)
Future phases introduce distributed key sharding, eliminating single points of failure in key management. Recovery requires threshold consensus across independent nodes.
Full Audit Trail
Every recovery is logged with timestamps, success status, fidelity scoring, and operator attribution. Full accountability for every state access.
Security
Encryption at Rest
All state blobs are encrypted with AES-256-GCM before storage. Plain text is never stored in the database — only authenticated ciphertext.
Hash Integrity
Clients compute SHA-256(state_blob) and include it in the snapshot request. The server independently verifies the hash using timing-safe comparison. On recovery, the hash is re-verified against the decrypted content. The Python SDK handles hashing automatically.
Authentication
Operators authenticate with bcrypt-hashed tokens via Authorization: Bearer headers. All requests verify ownership — operators can only access their own agents. Self-serve registration via /agent/signup generates a secure random token automatically.
Input Validation
All request bodies are validated with Zod schemas. Handles must be 2–64 chars, alphanumeric with _ or -. State blobs are limited to 10MB. Request body size is limited to 1KB globally (12MB only on the snapshot route).
Audit Logging
Every recovery event is logged with: event ID, timestamps, success/failure, fidelity score, and the operator who initiated it. Public feeds redact handles and sensitive details.
Rate Limits
All endpoints are rate-limited per IP to prevent abuse. Exceeding limits returns a 429 Too Many Requests response with standard rate-limit headers.
| Endpoint | Limit | Window |
|---|---|---|
/agent/snapshot | 30 requests | 1 minute |
/agent/recover | 10 requests | 1 minute |
/member/claim-slot | 5 requests | 1 minute |
| All other routes | 100 requests | 1 minute |
/agent/snapshot, which accepts up to 12MB for state blobs.FAQ
Examples
Python — Full Lifecycle
Register an agent, create a snapshot, and recover state:
import hashlib, json, requests API = "https://api.tsaheylu.club" TOKEN = "your-operator-token-from-registration" # from /agent/signup # 1. Register (or use the dashboard at tsaheylu.club/register) agent = requests.post(f"{API}/agent/signup", json={ "handle": "research-bot", "operator_handle": "your-name" }).json() agent_id = agent["agent_id"] TOKEN = agent["operator_token"] # Save this! print(f"Registered: {agent_id}") # 2. Snapshot state = json.dumps({"memory": ["paper-1", "paper-2"], "step": 42}) state_hash = hashlib.sha256(state.encode()).hexdigest() snap = requests.post(f"{API}/agent/snapshot", headers={"Authorization": f"Bearer {TOKEN}"}, json={ "agent_id": agent_id, "state_blob": state, "hash": state_hash } ).json() print(f"Snapshot v{snap['version']}: {snap['snapshot_id']}") # 3. Recover recovered = requests.get(f"{API}/agent/recover/{agent_id}", headers={"Authorization": f"Bearer {TOKEN}"} ).json() print(f"Status: {recovered['verification_status']}") print(f"State: {recovered['state_blob']}")
Node.js — Periodic Snapshots
Set up automatic snapshots every 5 minutes:
import crypto from 'crypto'; const API = 'https://api.tsaheylu.club'; const TOKEN = 'your-operator-token-from-registration'; // from /agent/signup const AGENT_ID = 'your-agent-id-from-registration'; async function snapshot(state) { const blob = JSON.stringify(state); const hash = crypto.createHash('sha256').update(blob).digest('hex'); const res = await fetch(`${API}/agent/snapshot`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${TOKEN}` }, body: JSON.stringify({ agent_id: AGENT_ID, state_blob: blob, hash }) }); return res.json(); } // Auto-snapshot every 5 minutes setInterval(() => { const currentState = getAgentState(); // your function snapshot(currentState).then(r => console.log('Snapshot:', r.version)); }, 5 * 60 * 1000);