sirr (Python)
Ephemeral secrets for Python AI agents. Credentials that delete themselves.
sirr is the Python client for Sirr — a self-hosted vault where every secret expires by read count, by time, or both. Built for the Python AI ecosystem: LangChain, CrewAI, AutoGen, LlamaIndex, and any framework that needs to hand credentials to agents without leaving them lying around forever.
The Problem It Solves
Python dominates the AI/ML landscape. That means Python is where credentials get handed to agents, embedded in tool calls, interpolated into prompts, and logged by frameworks. Every time an agent reads a database URL or API key, you have limited visibility into what that framework stored, logged, or will use for fine-tuning.
The standard answer — "rotate your secrets after every session" — doesn't scale. You forget. It's tedious. It requires manual IAM work.
Sirr gives you a better primitive: credentials that enforce their own expiry. An agent reads it once. The server deletes it. You don't have to remember to clean anything up.
# Push a dead drop — returns a one-time URL
result = sirr.push(api_key, reads=1, ttl=600)
print(result.url) # → https://sirr.sirrlock.com/s/abc123
# Or set an org-scoped named secret
sirr.set("OPENAI_KEY", api_key, org="acme", reads=1, ttl=600)
# Agent calls sirr.get("OPENAI_KEY", org="acme") → gets the value → record deleted
Install
pip install sirr
Requires Python 3.10+. Supports sync and async.
Usage
import os
from sirr import SirrClient, SecretExistsError
sirr = SirrClient(
server=os.environ.get("SIRR_SERVER", "https://sirr.sirrlock.com"),
token=os.environ["SIRR_TOKEN"],
)
# Push a public dead drop — returns { id, url }
result = sirr.push("sk-...", reads=1, ttl=3600)
print(result.url) # → https://sirr.sirrlock.com/s/abc123
# Set an org-scoped named secret — raises SecretExistsError on 409
sirr.set("DB_URL", "postgres://...", org="acme", ttl=86400, reads=3)
# Retrieve — routes by org presence
value = sirr.get(result.id) # dead drop by ID
db_url = sirr.get("DB_URL", org="acme") # org-scoped by key
# Handle conflicts
try:
sirr.set("DB_URL", "postgres://new...", org="acme")
except SecretExistsError:
print("Key already exists — delete first or use a different key")
# Inspect metadata without consuming a read
meta = sirr.head("DB_URL")
# → SecretHead(key='DB_URL', read_count=0, reads_remaining=3, ...)
# Pull all secrets into a dict
secrets = sirr.pull_all()
# → {"DB_URL": "postgres://..."}
# Inject as environment variables for the duration of a block
with sirr.env():
# os.environ["DB_URL"] is set here
run_agent_task()
# restored on exit, even on exception
# Delete immediately
sirr.delete("DB_URL")
# List active secrets (metadata only — no values)
entries = sirr.list()
# Prune expired secrets
pruned = sirr.prune()
# Check server health
alive = sirr.health() # True / False
Sealed Secrets
A secret with reads=N, delete=False is sealed once the read limit is exhausted — it stays on
the server but can no longer be read. get() will raise SirrSealed (a subclass
of SirrError) in this case. Use head() to inspect a sealed secret without triggering an error:
from sirr import SirrSealed
sirr.set("AUDIT_KEY", value, org="acme", reads=3, delete=False)
try:
value = sirr.get("AUDIT_KEY", org="acme")
except SirrSealed:
meta = sirr.head("AUDIT_KEY")
print(f"sealed — read {meta.read_count} times, created at {meta.created_at}")
Async
from sirr import AsyncSirrClient, SecretExistsError
async with AsyncSirrClient(server=..., token=...) as sirr:
# Push a dead drop
result = await sirr.push("sk-...", reads=1, ttl=3600)
value = await sirr.get(result.id)
# Set an org-scoped secret
await sirr.set("API_KEY", "sk-...", org="acme", reads=1)
value = await sirr.get("API_KEY", org="acme")
meta = await sirr.head("API_KEY")
AI Workflows
LangChain tool with scoped credential
from langchain.tools import tool
@tool
def query_production_db(sql: str) -> str:
"""Run a SQL query against the production database."""
conn_str = sirr.get("AGENT_DB")
if conn_str is None:
raise ValueError("DB credential expired or already used")
return run_query(conn_str, sql)
CrewAI agent with burn-after-use credential
from crewai import Agent, Task, Crew
# Set before the crew runs — burns after first read
sirr.set("STRIPE_KEY", stripe_key, org="acme", reads=1, ttl=600)
analyst = Agent(
role="Data Analyst",
goal="Fetch and analyze payment data",
tools=[stripe_tool], # tool calls sirr.get("STRIPE_KEY", org="acme") internally
)
crew = Crew(agents=[analyst], tasks=[analysis_task])
crew.kickoff()
# STRIPE_KEY was read once by the tool — already deleted
AutoGen multi-agent with isolated credentials
import autogen
# Each agent gets its own scoped, expiring credential
sirr.set("AGENT_1_DB", db_url_1, org="acme", reads=5, ttl=3600)
sirr.set("AGENT_2_DB", db_url_2, org="acme", reads=5, ttl=3600)
# Agents run — their credential budgets are enforced server-side
# No agent can exceed its read limit even if the framework retries
Inject all secrets into a subprocess
with sirr.env():
# All Sirr secrets set as os.environ
subprocess.run(["python", "agent_script.py"])
# Env restored after block
pytest fixture for CI secrets
import pytest
from sirr import SirrClient
@pytest.fixture(autouse=True)
def inject_test_secrets():
sirr = SirrClient(server=os.environ["SIRR_SERVER"], token=os.environ["SIRR_TOKEN"])
with sirr.env():
yield
# Credentials cleaned from env after each test
Multi-Tenant / Org Mode
When running against a multi-tenant Sirr server, pass the org parameter to scope
all secret, audit, webhook, and prune operations to that organization:
from sirr import SirrClient
sirr = SirrClient(
server="https://sirr.sirrlock.com",
token=os.environ["SIRR_TOKEN"],
)
# Org is now per-call on set() and get()
sirr.set("DB_URL", "postgres://...", org="acme", reads=3)
value = sirr.get("DB_URL", org="acme")
# Audit, list, and webhook calls still support org at the client level
sirr_acme = SirrClient(server="https://sirr.sirrlock.com", token=..., org="acme")
events = sirr_acme.get_audit_log()
/me endpoints
Inspect or update the currently authenticated principal:
info = sirr.me() # GET /me → MeInfo
sirr.update_me(metadata={"env": "prod"}) # PATCH /me → MeInfo
key = sirr.create_key("ci") # POST /me/keys → ApiKeyCreateResult
print(key.key) # sirr_key_... (shown once)
sirr.delete_key(key.id) # DELETE /me/keys/:id
Admin endpoints
Manage orgs, principals, and roles (requires admin privileges):
# Orgs
org = sirr.create_org("Acme", metadata={"env": "prod"})
orgs = sirr.list_orgs()
sirr.delete_org(org.id)
# Principals
p = sirr.create_principal(org.id, "alice", "writer")
principals = sirr.list_principals(org.id)
sirr.delete_principal(org.id, p.id)
# Roles
role = sirr.create_role(org.id, "writer", permissions="rRlL")
roles = sirr.list_roles(org.id)
sirr.delete_role(org.id, role.name)
Permission strings use single letters: r read-own, R read-org, l list-own, L list-org,
c create, C create-on-behalf, p patch-own, P patch-org, a account-read,
A account-read-org, m account-manage, M manage-org, S sirr-admin,
d delete-own, D delete-org.
Async
All multi-tenant, /me, and admin methods are also available on AsyncSirrClient:
from sirr import AsyncSirrClient
async with AsyncSirrClient(server=..., token=...) as sirr:
await sirr.set("KEY", "value", org="acme")
info = await sirr.me()
org = await sirr.create_org("NewOrg")
p = await sirr.create_principal(org.id, "bob", "reader")
Related
| Package | Description |
|---|---|
| sirr | Rust monorepo: sirrd server + sirr CLI |
| @sirrlock/mcp | MCP server for AI assistants |
| @sirrlock/node | Node.js / TypeScript SDK |
| Sirr.Client (NuGet) | .NET SDK |
| sirr.dev | Documentation |
| sirrlock.com | Managed cloud + license keys |
Metadata
Release files for sirr 1.0.20
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| sirr-1.0.20.tar.gz | 11.9 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sirr-1.0.20-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.0 kB
Release files / sirr-1.0.20.tar.gz
| Download URL | sirr-1.0.20.tar.gz |
|---|---|
| Size | 11.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c6aa1034d5b304a368b1bad5038bb22f19dc571687b382ce0e1c7c96126e02e3
|
|
BLAKE2b-256 checksum How to use checksums |
095314b0cb379a488aad93a3faa617a009d9596e191b261406517772f5a7e959
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 12, 2026.
Transparency logRelease files / sirr-1.0.20-py3-none-any.whl
| Download URL | sirr-1.0.20-py3-none-any.whl |
|---|---|
| Size | 11.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
613db47bd6cedde8b45557bc07acce93e8b286bd9b209eb2ba343cdcd959d134
|
|
BLAKE2b-256 checksum How to use checksums |
932073566493daa29097fd216b89b9efe2becc7d871c56355f1ad2a6dde87dcc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 12, 2026.
Transparency logSecurity for sirr 1.0.20
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for sirr-1.0.20.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 12, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.