Initial commit: Elydora Open Source — self-hosted audit platform for AI agents
Full self-hosted port of the Elydora tamper-evident audit protocol: - Hono API server with PostgreSQL, Redis, MinIO adapters - Ed25519 signed operations, chain hashing, Merkle epoch rollups - PBKDF2 password hashing, HMAC-SHA256 JWT, 5-role RBAC - Next.js management console - Node.js, Python, and Go SDKs - Docker Compose for local deployment - Helm chart for Kubernetes - Comprehensive security hardening and audit fixes applied Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
15547a8c35
commit
e390e68594
207 files changed
+32790
-63
No files matched your search
@@ -0,0 +1,307 @@
|
||||
# Elydora Python SDK
|
||||
|
||||
Official Python SDK for the [Elydora](https://elydora.com) tamper-evident audit platform. Build cryptographically verifiable audit trails for AI agent operations.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install elydora
|
||||
```
|
||||
|
||||
Requires Python 3.9+.
|
||||
|
||||
## Quick Start
|
||||
|
||||
```python
|
||||
from elydora import ElydoraClient
|
||||
|
||||
# Authenticate
|
||||
auth = ElydoraClient.login("https://api.elydora.com", "user@example.com", "password")
|
||||
|
||||
# Create client
|
||||
client = ElydoraClient(
|
||||
org_id=auth["user"]["org_id"],
|
||||
agent_id="my-agent-id",
|
||||
private_key="<base64url-encoded-ed25519-seed>",
|
||||
base_url="https://api.elydora.com",
|
||||
token=auth["token"],
|
||||
)
|
||||
|
||||
# Create and submit an operation
|
||||
eor = client.create_operation(
|
||||
operation_type="data.access",
|
||||
subject={"user_id": "u-123", "resource": "patient-record"},
|
||||
action={"type": "read", "scope": "full"},
|
||||
payload={"record_id": "rec-456"},
|
||||
)
|
||||
response = client.submit_operation(eor)
|
||||
print("Receipt:", response["receipt"]["receipt_id"])
|
||||
```
|
||||
|
||||
## Async Support
|
||||
|
||||
```python
|
||||
from elydora import AsyncElydoraClient
|
||||
|
||||
async def main():
|
||||
client = AsyncElydoraClient(
|
||||
org_id="org-123",
|
||||
agent_id="agent-456",
|
||||
private_key="<base64url-encoded-ed25519-seed>",
|
||||
token="<jwt-token>",
|
||||
)
|
||||
|
||||
eor = client.create_operation(
|
||||
operation_type="inference",
|
||||
subject={"model": "gpt-4"},
|
||||
action={"type": "completion"},
|
||||
)
|
||||
response = await client.submit_operation(eor)
|
||||
await client.close()
|
||||
```
|
||||
|
||||
## CLI
|
||||
|
||||
The SDK includes a CLI for installing audit hooks into AI coding agents.
|
||||
|
||||
```bash
|
||||
elydora install \
|
||||
--agent claudecode \
|
||||
--org_id org-123 \
|
||||
--agent_id agent-456 \
|
||||
--private_key <key> \
|
||||
--kid agent-456-key-v1
|
||||
```
|
||||
|
||||
### Commands
|
||||
|
||||
| Command | Description |
|
||||
|---------|-------------|
|
||||
| `elydora install` | Install Elydora audit hook for a coding agent |
|
||||
| `elydora uninstall` | Remove Elydora audit hook for a coding agent |
|
||||
| `elydora status` | Show installation status for all agents |
|
||||
| `elydora agents` | List supported coding agents |
|
||||
|
||||
### Supported Agents
|
||||
|
||||
| Agent | Key |
|
||||
|-------|-----|
|
||||
| Claude Code | `claudecode` |
|
||||
| Copilot CLI | `copilot` |
|
||||
| Cursor | `cursor` |
|
||||
| Gemini CLI | `gemini` |
|
||||
| Kiro CLI | `kirocli` |
|
||||
| Kiro IDE | `kiroide` |
|
||||
| Letta Code | `letta` |
|
||||
| OpenCode | `opencode` |
|
||||
|
||||
## API Reference
|
||||
|
||||
### Configuration
|
||||
|
||||
```python
|
||||
client = ElydoraClient(
|
||||
org_id="org-123", # Organization ID
|
||||
agent_id="agent-456", # Agent ID
|
||||
private_key="<seed>", # Base64url-encoded Ed25519 seed
|
||||
base_url="https://...", # API base URL (default: https://api.elydora.com)
|
||||
ttl_ms=30000, # Operation TTL in ms (default: 30000)
|
||||
max_retries=3, # Max retries on transient failures (default: 3)
|
||||
token="<jwt>", # Optional JWT bearer token
|
||||
)
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
```python
|
||||
# Register a new user and organization
|
||||
reg = ElydoraClient.register(base_url, email, password, display_name=None, org_name=None)
|
||||
|
||||
# Login and receive a JWT
|
||||
auth = ElydoraClient.login(base_url, email, password)
|
||||
|
||||
# Get current authenticated user profile
|
||||
me = client.get_me()
|
||||
|
||||
# Issue a new API token (with optional TTL in seconds)
|
||||
token_resp = client.issue_token(ttl_seconds=3600)
|
||||
```
|
||||
|
||||
### Operations
|
||||
|
||||
```python
|
||||
# Create a signed EOR locally (no network call)
|
||||
eor = client.create_operation(
|
||||
operation_type="inference",
|
||||
subject={"model": "gpt-4"},
|
||||
action={"type": "completion"},
|
||||
payload={"prompt": "Hello"},
|
||||
)
|
||||
|
||||
# Submit to API
|
||||
response = client.submit_operation(eor)
|
||||
|
||||
# Retrieve an operation
|
||||
op = client.get_operation(operation_id)
|
||||
|
||||
# Verify integrity
|
||||
result = client.verify_operation(operation_id)
|
||||
```
|
||||
|
||||
### Agent Management
|
||||
|
||||
```python
|
||||
# Register a new agent
|
||||
agent = client.register_agent({
|
||||
"agent_id": "my-agent",
|
||||
"display_name": "My Agent",
|
||||
"responsible_entity": "team@example.com",
|
||||
"keys": [{"kid": "key-v1", "public_key": "<base64url>", "algorithm": "ed25519"}],
|
||||
})
|
||||
|
||||
# Get agent details
|
||||
details = client.get_agent(agent_id)
|
||||
|
||||
# Freeze an agent
|
||||
client.freeze_agent(agent_id, reason="security review")
|
||||
|
||||
# Revoke a key
|
||||
client.revoke_key(agent_id, kid, reason="key rotation")
|
||||
|
||||
# List all agents in the organization
|
||||
agents_resp = client.list_agents()
|
||||
|
||||
# Unfreeze a previously frozen agent
|
||||
client.unfreeze_agent(agent_id, reason="review complete")
|
||||
|
||||
# Delete an agent permanently
|
||||
deleted_resp = client.delete_agent(agent_id)
|
||||
```
|
||||
|
||||
### Audit
|
||||
|
||||
```python
|
||||
import time
|
||||
|
||||
results = client.query_audit(
|
||||
agent_id="agent-123",
|
||||
operation_type="inference",
|
||||
start_time=int(time.time() * 1000) - 86400000,
|
||||
end_time=int(time.time() * 1000),
|
||||
limit=50,
|
||||
)
|
||||
```
|
||||
|
||||
### Epochs
|
||||
|
||||
```python
|
||||
epochs = client.list_epochs()
|
||||
epoch = client.get_epoch(epoch_id)
|
||||
```
|
||||
|
||||
### Exports
|
||||
|
||||
```python
|
||||
export = client.create_export(
|
||||
start_time=start,
|
||||
end_time=end,
|
||||
format="json",
|
||||
)
|
||||
|
||||
exports = client.list_exports()
|
||||
detail = client.get_export(export_id)
|
||||
|
||||
# Download export file data
|
||||
data = client.download_export(export_id)
|
||||
```
|
||||
|
||||
### JWKS
|
||||
|
||||
```python
|
||||
jwks = client.get_jwks()
|
||||
```
|
||||
|
||||
### Health
|
||||
|
||||
```python
|
||||
# Check API health (no authentication required)
|
||||
health = client.health()
|
||||
# health["status"], health["version"], health["protocol_version"], health["timestamp"]
|
||||
```
|
||||
|
||||
### Crypto Functions
|
||||
|
||||
The SDK exports low-level cryptographic primitives for advanced use:
|
||||
|
||||
```python
|
||||
from elydora import (
|
||||
jcs_canonicalize, # RFC 8785 JSON Canonicalization
|
||||
sha256_base64url, # SHA-256 hash as base64url
|
||||
compute_chain_hash, # Chain hash computation
|
||||
compute_payload_hash, # Payload hash (SHA-256 of JCS-canonicalized payload)
|
||||
sign_ed25519, # Ed25519 signing
|
||||
sign_eor, # Sign an EOR dict
|
||||
get_public_key_base64url, # Derive public key from private seed
|
||||
)
|
||||
```
|
||||
|
||||
### Utility Functions
|
||||
|
||||
```python
|
||||
from elydora import (
|
||||
base64url_encode, # Encode bytes to base64url (no padding)
|
||||
base64url_decode, # Decode base64url string to bytes
|
||||
generate_nonce, # Generate 16-byte random nonce (base64url)
|
||||
generate_uuidv7, # Generate UUIDv7 (time-ordered, RFC 9562)
|
||||
)
|
||||
```
|
||||
|
||||
### Type Definitions
|
||||
|
||||
All types are `TypedDict` classes for structural typing:
|
||||
|
||||
```python
|
||||
from elydora import (
|
||||
# Protocol types
|
||||
EOR, # Elydora Operation Record
|
||||
EAR, # Elydora Acknowledgment Receipt
|
||||
|
||||
# Entity types
|
||||
Agent, AgentKey, Operation, Receipt, Epoch, Export, Organization, User,
|
||||
|
||||
# API response types
|
||||
RegisterAgentResponse, GetAgentResponse, ListAgentsResponse,
|
||||
DeleteAgentResponse, SubmitOperationResponse, GetOperationResponse,
|
||||
VerifyOperationResponse, AuditQueryResponse, GetEpochResponse,
|
||||
ListEpochsResponse, CreateExportResponse, GetExportResponse,
|
||||
ListExportsResponse, JWKSResponse, AuthRegisterResponse,
|
||||
AuthLoginResponse, GetMeResponse, IssueTokenResponse, HealthResponse,
|
||||
|
||||
# Request types
|
||||
RegisterAgentRequest,
|
||||
)
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```python
|
||||
from elydora import ElydoraError
|
||||
|
||||
try:
|
||||
client.submit_operation(eor)
|
||||
except ElydoraError as e:
|
||||
print(e.code) # e.g. "INVALID_SIGNATURE"
|
||||
print(e.message) # Human-readable message
|
||||
print(e.status_code) # HTTP status code
|
||||
print(e.request_id) # Request ID for support
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [requests](https://pypi.org/project/requests/) - Sync HTTP client
|
||||
- [aiohttp](https://pypi.org/project/aiohttp/) - Async HTTP client
|
||||
- [cryptography](https://pypi.org/project/cryptography/) - Ed25519 signing
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
Reference in new issue
Block a user