Zero-dependency-bloat Python library for W3C Decentralized Identifiers (DIDs) using Ed25519 keys
Project description
didlite 🆔
⚠️ BETA STATUS - SECURITY AUDIT PENDING
This library is in active development and has not undergone an independent security audit. While we've conducted comprehensive internal security hardening with 23+ security fixes, we recommend against production use until an external audit is complete.
Use at your own risk. See SECURITY.md for vulnerability reporting.
Verifiable Identity for Agents, IoT, and Edge Devices.
didlite is a zero-dependency-bloat Python library that generates W3C Standard Decentralized Identifiers (DIDs) using Ed25519 keys.
It allows any Python program (Drone, Sensor, AI Agent) to create a cryptographically verifiable identity and sign data without needing a central server, certificate authority, or blockchain.
⚡ Why didlite?
Most Identity libraries (SSI) are massive. They require Rust compilers, system binaries, or heavy async runtimes.
- Zero Bloat: Pure Python wrapper around
pynacl(libsodium). - Standards Compliant: Produces valid
did:keyidentifiers (W3C CCG). - Web-Ready: Signs standard JSON Web Signatures (JWS).
- ARM64 Native: Runs seamlessly on Raspberry Pi, AWS Graviton, and M1/M2/M3 Macs.
The Problem it Solves
Scenario 1: AI Agents - You deploy autonomous agents that need to communicate and transact with each other.
- The Old Way: Central API keys (shared secrets = single point of failure) or OAuth servers (requires infrastructure).
- The
didliteWay: Each agent generates its own identity. Messages are cryptographically signed. Trust is mathematical, not infrastructural.
Scenario 2: IoT at Scale - You have 1,000 temperature sensors deployed in the field.
- The Old Way: Hardcode a shared API key (insecure) or manage 1,000 mTLS certificates (painful).
- The
didliteWay: Each sensor generates its own ID at startup. The server verifies the signature mathematically. No database required.
How It Works: Trust Architecture
sequenceDiagram
participant Agent as AI Agent / IoT Device
participant Verifier as Server / Gateway
Note over Agent: Generate Identity
Agent->>Agent: identity = AgentIdentity()
Agent->>Agent: did = "did:key:z6Mkh..."
Note over Agent: Sign Message
Agent->>Agent: token = create_jws(identity, payload)
Agent->>Verifier: Send signed token
Note over Verifier: Verify Signature
Verifier->>Verifier: header, payload = verify_jws(token)
Verifier->>Verifier: Extract DID from header['kid']
Verifier->>Verifier: Resolve DID → public key
Verifier->>Verifier: Verify signature with public key
alt Signature Valid
Verifier->>Agent: ✅ Trust established
Note over Verifier: No database lookup needed<br/>DID IS the public key
else Signature Invalid
Verifier->>Agent: ❌ Reject (tampering detected)
end
Key Insight: The DID itself encodes the public key (via Multicodec + Multibase). No PKI infrastructure, no certificate authorities, no centralized identity servers.
Performance Benchmarks (v0.2.3) - 2025-12-30
Environment: Raspberry Pi 5 8GB
| Operation | Avg Time | Throughput | Notes |
|---|---|---|---|
| Identity Generation | 0.11ms | ~9,200/sec | No overhead from v0.2.3 changes |
| Token Creation | 0.08ms | ~13,100/sec | Includes iat timestamp validation |
| Token Verification | 0.24ms | ~4,200/sec | Now returns (header, payload) tuple |
| DID Extraction | 0.01ms | ~190,000/sec | NEW - Fast header parsing without signature verification |
| Custom Headers | 0.08ms | ~13,000/sec | NEW - Zero overhead for custom typ, etc. |
Key Takeaways:
- ✅ v0.2.3 header enhancements add negligible overhead (<0.01ms)
- ✅
extract_signer_did()is ~24x faster than full verification (useful for routing/logging) - ✅ All operations remain suitable for high-throughput IoT/edge deployments
- ✅ Ed25519 + PyNaCl's libsodium wrapper delivers excellent ARM64 performance
📦 Installation
pip install didlite
Note: didlite is currently in beta (v0.2.3). Breaking changes may occur before v1.0.0.
🚀 Quick Start
Example 1: AI Agent Communication
Agent 1 (Research Agent) - Collects and signs lead data:
import didlite
# Agent generates its own identity
research_agent = didlite.AgentIdentity()
print(f"Research Agent DID: {research_agent.did}")
# Create a signed lead record
lead_data = {
"action": "lead_discovered",
"company": "Acme Corp",
"industry": "manufacturing",
"interest": "IoT sensors",
"source": "web_research"
}
# Sign the lead data
signed_lead = didlite.create_jws(research_agent, lead_data)
# Now send signed_lead to CRM agent...
Agent 2 (CRM Agent) - Verifies and processes:
import didlite
# Receive signed lead from research agent
# signed_lead = "eyJhbGciOiJFZERTQ..."
try:
# Verify the signature
header, payload = didlite.verify_jws(signed_lead)
# Extract the signer's DID
signer_did = header['kid']
print(f"✅ Verified lead from: {signer_did}")
print(f"Company: {payload['company']}")
print(f"Interest: {payload['interest']}")
# Add to CRM database with attribution to research_agent...
except Exception as e:
print(f"❌ Invalid signature - rejecting lead: {e}")
Example 2: IoT Sensor Data
The Sensor - Generates identity and signs telemetry:
import didlite
# In production, load seed from secure storage
sensor = didlite.AgentIdentity()
# Sign telemetry data
telemetry = {
"temp": 24.5,
"unit": "C",
"timestamp": 1678900000
}
token = didlite.create_jws(sensor, telemetry)
# Send token to gateway...
The Gateway - Verifies without database lookup:
import didlite
# token = "eyJhbGciOiJFZERTQ..." # Received from sensor
try:
header, payload = didlite.verify_jws(token)
print(f"Valid data from: {header['kid']}")
print(f"Temperature: {payload['temp']}°{payload['unit']}")
except Exception as e:
print(f"SECURITY ALERT: Invalid signature! {e}")
🛠 Advanced Usage
Persistent Identity (Using Seeds)
If a device reboots, you want it to have the same DID. Use a secure 32-byte seed (e.g., from an environment variable or HSM).
import os
from didlite import AgentIdentity
# Load secret from secure storage
seed_bytes = os.getenv("DEVICE_SECRET_KEY").encode()[:32]
agent = AgentIdentity(seed=seed_bytes)
Resolving DIDs
If you just want to check a DID string and get the raw public key bytes:
from didlite import resolve_did_to_key
did = "did:key:z6MkhaXgBZDvotDkL5257..."
verify_key = resolve_did_to_key(did)
# Now use verify_key to check raw signatures
🧪 Testing & Quality
Test Coverage (v0.2.4)
Coverage by Module:
| Module | Coverage | Status |
|---|---|---|
didlite/__init__.py |
100% | ✅ Complete coverage |
didlite/core.py |
96% | ✅ All security-critical paths tested |
didlite/jws.py |
99% | ✅ Algorithm confusion attacks prevented |
didlite/keystore.py |
93% | ✅ All storage backends validated |
| Overall | 95.7% | ✅ Production-ready (351 statements, 336 covered) |
Test Suite Breakdown:
| Test Category | Tests | Description |
|---|---|---|
Compliance (test_compliance.py) |
18 | W3C DID & RFC 7515/7519 JWT/JWS standards |
Core (test_core.py) |
37 | Identity, DID resolution, JWK/PEM export |
Fuzzing (test_fuzzing.py) |
32 | Malformed inputs, attack scenarios, DoS prevention |
Integration (test_integration.py) |
5 | Cross-library compatibility (authlib) |
JWS (test_jws.py) |
63 | Token creation/verification, headers, expiration |
Keystore (test_keystore.py) |
49 | Storage backends, encryption, persistence |
Security (test_security.py) |
32 | Error sanitization, input validation |
| Total | 236 | 233 passed, 3 skipped |
What's Tested:
- ✅ W3C DID:key compliance (RFC 8032, Multicodec 0xed01, base58btc encoding)
- ✅ JWS/JWT standards (RFC 7515, RFC 7519, EdDSA signatures)
- ✅ Attack prevention (algorithm confusion, signature tampering, token replay, missing 'kid')
- ✅ Keystore security (PBKDF2 encryption, file permissions 0o600, path traversal)
- ✅ Cross-library compatibility (authlib JWS/JWK interop)
- ✅ Edge cases (malformed tokens, corrupted data, expired tokens, future-dated tokens)
Run tests: pytest --cov=didlite --cov-report=term-missing
See docs/TESTING_GUIDE.md for detailed testing documentation.
🤝 Contributing
We keep this library "lite" on purpose. We only support did:key to ensure maximum portability for Edge AI and IoT.
📄 License
Apache 2.0 - Commercial use allowed.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file didlite-0.2.4.tar.gz.
File metadata
- Download URL: didlite-0.2.4.tar.gz
- Upload date:
- Size: 108.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
407b788b371e9a1c05433dfde5657cfbdd25af58f32c6e90242c4c5a10536f7f
|
|
| MD5 |
f3fb293120d051ba89fa00ab645f4a5f
|
|
| BLAKE2b-256 |
274bf0a99ba6c6e7aa23222176c6755d4924ab9f7047887fe7827cb1f5f32f27
|
Provenance
The following attestation bundles were made for didlite-0.2.4.tar.gz:
Publisher:
publish.yml on jondepalma/didlite-pkg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
didlite-0.2.4.tar.gz -
Subject digest:
407b788b371e9a1c05433dfde5657cfbdd25af58f32c6e90242c4c5a10536f7f - Sigstore transparency entry: 786027770
- Sigstore integration time:
-
Permalink:
jondepalma/didlite-pkg@4899b293de57318680df5002bad47ffcdc42609c -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/jondepalma
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4899b293de57318680df5002bad47ffcdc42609c -
Trigger Event:
release
-
Statement type:
File details
Details for the file didlite-0.2.4-py3-none-any.whl.
File metadata
- Download URL: didlite-0.2.4-py3-none-any.whl
- Upload date:
- Size: 22.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9330eb28846437e9aba1d58eba3a7ee5470763cb08fb3c071e3b342367ce78a
|
|
| MD5 |
4c71c59b5f130c3a2ab8aba27e736346
|
|
| BLAKE2b-256 |
14d7965aa459dba0f3d5925879ccda4b2a6cb4aace61026133d1b563917fbccb
|
Provenance
The following attestation bundles were made for didlite-0.2.4-py3-none-any.whl:
Publisher:
publish.yml on jondepalma/didlite-pkg
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
didlite-0.2.4-py3-none-any.whl -
Subject digest:
f9330eb28846437e9aba1d58eba3a7ee5470763cb08fb3c071e3b342367ce78a - Sigstore transparency entry: 786027785
- Sigstore integration time:
-
Permalink:
jondepalma/didlite-pkg@4899b293de57318680df5002bad47ffcdc42609c -
Branch / Tag:
refs/tags/v0.2.4 - Owner: https://github.com/jondepalma
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@4899b293de57318680df5002bad47ffcdc42609c -
Trigger Event:
release
-
Statement type: