Secure one-time secret sharing with zero-knowledge encryption
Project description
zephr
Zero-knowledge one-time secret sharing for AI agents and the humans who orchestrate them. Python SDK for Zephr.
Create a one-time secret link: encrypted on your device and self-destructing after a single retrieval. Pass it between agents, services, and pipelines with no shared infrastructure and no plaintext on the server.
Designed for zero-knowledge secret handoff between independent systems: AI agents, CI/CD pipelines, GitHub Actions, and human operators.
How it works
- A 256-bit key is generated locally. It never reaches Zephr's servers.
- Your secret is encrypted with AES-GCM on your device
- Only the ciphertext is uploaded to Zephr
- The link embeds the key in the URL fragment, which browsers never transmit to servers
- First retrieval atomically consumes the record. A second request returns 410.
Features
- No shared infrastructure: a link is the entire transport mechanism between independent processes
- Zero-knowledge: the server never receives your plaintext or encryption keys
- Local encryption: AES-GCM-256 on your device before any network call
- One-time access: record marked consumed atomically on first retrieval
- Minimal dependencies: only
cryptography(audited, widely trusted) - API key support for higher limits and longer expiry
- Webhook callbacks: HMAC-SHA256 signed events on secret consumption (
callback_url+callback_secret) - Idempotency: auto-generated
Idempotency-Keyon every create for safe retries
Installation
pip install zephr
Usage
Create a secret
import zephr
result = zephr.create_secret("my-api-key-12345")
print(result["full_link"])
# https://zephr.io/secret/abc123#v1.key...
The secret is encrypted exactly as provided. No trimming or normalization. Strings that are empty or contain only whitespace raise ValidationError.
Agent A encrypts and hands off the link. Agent B retrieves it exactly once:
# Agent A: encrypt and dispatch
result = zephr.create_secret("sk-live-abc123", expiry=60, hint="STRIPE_KEY_PROD")
agent_b.dispatch({"credential": result["full_link"]})
# Agent B: consumed atomically on first read
result = zephr.retrieve_secret(result["full_link"])
plaintext = result["plaintext"]
Retrieve a secret
import zephr
# Full URL string
result = zephr.retrieve_secret("https://zephr.io/secret/abc123#v1.key...")
plaintext = result["plaintext"]
# Split mode
result = zephr.retrieve_secret({"url": "https://zephr.io/secret/abc123", "key": "v1.key..."})
plaintext = result["plaintext"]
Retrieval is exactly-once. The server permanently destroys the record on first access.
Options
# Expire in 1 hour
result = zephr.create_secret("secret", expiry=60)
# Expire in 7 days
result = zephr.create_secret("secret", expiry=10080)
# Expire in 30 days (Dev/Pro)
result = zephr.create_secret("secret", expiry=43200, api_key="zeph_...")
# Attach a plaintext label for routing and audit logs
result = zephr.create_secret("secret", hint="STRIPE_KEY_PROD")
# Split URL and key for separate transmission
result = zephr.create_secret("secret", split=True)
print(result["url"]) # https://zephr.io/secret/abc123
print(result["key"]) # v1.key...
Return value
create_secret() returns a dict.
Standard mode:
{
"mode": "standard",
"full_link": "https://zephr.io/secret/abc123#v1.key...",
"expires_at": "2026-03-12T12:00:00.000Z",
"secret_id": "abc123...", # 22-char base64url ID
}
Split mode:
{
"mode": "split",
"url": "https://zephr.io/secret/abc123",
"key": "v1.key...",
"expires_at": "2026-03-12T12:00:00.000Z",
"secret_id": "abc123...", # 22-char base64url ID
}
retrieve_secret() returns a dict with keys plaintext (str), hint (str or None), and purge_at (str or None).
Webhook callback
Get notified when a secret is consumed or expires — no polling needed:
result = zephr.create_secret("db-password",
expiry=60,
hint="DB_PASSWORD_PROD",
callback_url="https://my-orchestrator.example.com/zephr-events",
callback_secret="my-hmac-signing-secret",
api_key=os.environ.get("ZEPHR_API_KEY"),
)
When the secret is retrieved, Zephr POSTs a signed event:
{
"event": "secret.consumed",
"event_id": "550e8400-e29b-41d4-a716-446655440000",
"secret_id": "Ht7kR2mNqP3wXvYz8aB4cD",
"occurred_at": "2026-03-22T14:32:00.000Z",
"hint": "DB_PASSWORD_PROD"
}
Verify the X-Zephr-Signature header (HMAC-SHA256 hex digest of the body, signed with your callback_secret). See examples/webhook-receiver for runnable Node.js and Python receivers.
Fire-and-forget in v1 — no retries. 5-second timeout. Redirects blocked.
Idempotency
The SDK auto-generates an Idempotency-Key header on every create. If a request times out at the infrastructure level and is replayed, the server returns the cached response without creating a duplicate secret. Cache TTL: 24 hours.
Authentication
The SDK works without an account. No setup required. Free, Dev, and Pro tier features require an API key. Pass it via the api_key parameter. Anonymous requests are capped at 3/day per IP with a 1 h max expiry.
| Tier | Create limit | Expiry options | Max size | Authentication |
|---|---|---|---|---|
| Anonymous | 3/day | 1h | 6 KB | None |
| Free | 50/month | 1h, 24h, 7d, 30d | 20 KB | api_key="zeph_..." |
| Dev ($15/mo) | 2,000/month | 5m, 15m, 30m, 1h, 24h, 7d, 30d | 200 KB | api_key="zeph_..." |
| Pro ($39/mo) | 50,000/month | 5m, 15m, 30m, 1h, 24h, 7d, 30d | 1 MB | api_key="zeph_..." |
Getting an API key: Log in at zephr.io/account, open the API Keys tab, and create a key. The raw key is shown exactly once. Copy it immediately.
Passing the key:
import os
import zephr
# Via parameter
result = zephr.create_secret("secret", api_key="zeph_...")
# Via environment variable: preferred for CI and scripts
# export ZEPHR_API_KEY=zeph_...
result = zephr.create_secret("secret", api_key=os.environ.get("ZEPHR_API_KEY"))
GitHub Actions: Add ZEPHR_API_KEY as a repository secret, then pass it to your script:
steps:
- run: python share_secret.py
env:
ZEPHR_API_KEY: ${{ secrets.ZEPHR_API_KEY }}
# share_secret.py
import os, zephr
result = zephr.create_secret(
os.environ["MY_SECRET"],
expiry=60,
api_key=os.environ.get("ZEPHR_API_KEY"),
)
print(result["full_link"])
The key is sent as Authorization: Bearer zeph_... on each request. An invalid or revoked key returns HTTP 401.
Error handling
import zephr
try:
result = zephr.create_secret("my secret")
except zephr.ValidationError:
# Invalid input: empty or whitespace-only string, too long, bad expiry
pass
except zephr.EncryptionError:
# AES-GCM encryption or decryption failed
pass
except zephr.ApiError as e:
# Server returned an error
print(e.status_code) # e.g. 429, 401, 403
print(e.code) # e.g. "MONTHLY_LIMIT_EXCEEDED"
except zephr.NetworkError:
# Connection failed or timed out
pass
Common ApiError codes:
| Code | Status | Meaning |
|---|---|---|
INVALID_API_KEY |
401 | Key not found or revoked |
UPGRADE_REQUIRED |
403 | Feature requires a higher tier (e.g. expiry > 60 min without an account, or sub-hour expiry (5, 15, 30 min) without Dev/Pro) |
ANON_RATE_LIMIT_EXCEEDED |
429 | Anonymous daily limit reached (3/day per IP) |
MONTHLY_LIMIT_EXCEEDED |
429 | Monthly create limit reached for this API key |
PAYLOAD_TOO_LARGE |
413 | Encrypted blob exceeds the tier blob ceiling |
SECRET_NOT_FOUND |
404 | Secret ID does not exist or has expired |
SECRET_ALREADY_CONSUMED |
410 | Secret was already retrieved |
SECRET_EXPIRED |
410 | Secret has passed its expiry time |
Security
- Encrypts on your device before any network call
- AES-GCM-256 with authenticated encryption and built-in tamper detection
- Keys never reach the server. They travel in the URL fragment (RFC 3986 §3.5), which browsers strip before sending requests.
- Sensitive buffers overwritten after use (best-effort in Python)
- No plaintext logging. No analytics in the SDK.
Requirements
- Python 3.10 or higher
cryptography>= 43.0
License
MIT
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
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 zephr-0.4.1.tar.gz.
File metadata
- Download URL: zephr-0.4.1.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5814b066653e7bf3ca0f2ce312160e333be34e30ed47c449707d7436ef678389
|
|
| MD5 |
a321f49aae3e7012df716655a9d4060d
|
|
| BLAKE2b-256 |
ceaf44ead0c3b54ca0f1225835b491adf35620aef0802b140ff06da69f0ad4e8
|
Provenance
The following attestation bundles were made for zephr-0.4.1.tar.gz:
Publisher:
publish-pypi.yml on zephr-io/zephr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zephr-0.4.1.tar.gz -
Subject digest:
5814b066653e7bf3ca0f2ce312160e333be34e30ed47c449707d7436ef678389 - Sigstore transparency entry: 1177834639
- Sigstore integration time:
-
Permalink:
zephr-io/zephr@1e71612a90d80332481ace9b6528145605fba6e1 -
Branch / Tag:
refs/tags/pypi-v0.4.1 - Owner: https://github.com/zephr-io
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@1e71612a90d80332481ace9b6528145605fba6e1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file zephr-0.4.1-py3-none-any.whl.
File metadata
- Download URL: zephr-0.4.1-py3-none-any.whl
- Upload date:
- Size: 18.0 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 |
efa63a8d52f2265e1ea6e966c54294a6b77a7a94a05ea1dc7c84595138bea758
|
|
| MD5 |
3083d9f02162c5c0007d91c91004d776
|
|
| BLAKE2b-256 |
437d1c2596e19d889b68184353b0b160e5c7bd04087ee586a3f6713f24326b66
|
Provenance
The following attestation bundles were made for zephr-0.4.1-py3-none-any.whl:
Publisher:
publish-pypi.yml on zephr-io/zephr
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
zephr-0.4.1-py3-none-any.whl -
Subject digest:
efa63a8d52f2265e1ea6e966c54294a6b77a7a94a05ea1dc7c84595138bea758 - Sigstore transparency entry: 1177834707
- Sigstore integration time:
-
Permalink:
zephr-io/zephr@1e71612a90d80332481ace9b6528145605fba6e1 -
Branch / Tag:
refs/tags/pypi-v0.4.1 - Owner: https://github.com/zephr-io
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@1e71612a90d80332481ace9b6528145605fba6e1 -
Trigger Event:
push
-
Statement type: