🧵 CredWeave
Provider-agnostic credential pooling, intelligent scheduling, rate-limit cooldown, and automatic failover for Python.
Key Features • Why CredWeave? • Design Philosophy • Quickstart • Architecture • Security • Roadmap • Contributing
📌 Why CredWeave?
Production applications communicating with external services—such as LLM APIs, cloud providers, and third-party SaaS—often start with a single API key in an .env file.
As workload scale increases, systems quickly outgrow single-credential architectures:
- 🏢 Multiple Accounts & Tenants: Distributing load across organization tiers, departments, or multiple provider accounts.
- ⏱️ Rate-Limit Throttling: Encountering
429 Too Many Requestsspikes requiring automated cooldown windows and upstreamRetry-Afteradherence. - 🔀 Failover & High Availability: Automatically routing around revoked tokens, depleted credit quotas, or regional outages without downtime.
- 🔄 Dynamic Scheduling: Balancing requests fairly using Round-Robin, Weighted distribution, or Least-Recently-Used heuristics.
- 🔒 Security Risks: Secret values accidentally leaking into APM traces, exception tracebacks, or console logs.
Most teams end up hand-crafting fragile, ad-hoc rotation loops tightly coupled to a specific HTTP client (httpx, requests) or provider SDK (openai, anthropic).
CredWeave solves this once and for all: a clean, reusable, protocol-agnostic credential lifecycle engine for Python with zero external dependencies.
✨ Key Features
| Feature | Description |
|---|---|
| 🛡️ Secret-Safe by Design | Secrets are automatically masked (***) in repr(), str(), and exception messages. Zero accidental leaks in logs. |
| 🔌 Protocol & Client Agnostic | Works seamlessly with any HTTP client, gRPC client, or provider SDK (httpx, aiohttp, requests, OpenAI, etc.). |
| ⏳ Intelligent Cooldowns | Automatically isolates rate-limited keys with fixed durations or exponential backoff, respecting upstream retry_after. |
| 🔀 Resilient Failover | Seamlessly pivots to secondary/backup credential tiers when primary quotas are exhausted or credentials fail. |
| 🔄 Pluggable Scheduling | Round-robin, weighted allocation, least-recently-used, and custom selection strategies. |
| 📊 Outcome-Driven Lifecycle | Simple lease model where clients report outcomes (success, rate_limited, auth_failed, transient_error). |
| 🪶 Zero Dependencies | Built strictly on the Python standard library. Hyper-lightweight and blisteringly fast. |
| 🏗️ Clean Architecture | Strict inward dependency rule: pure Domain models, well-defined Application ports, and pluggable Infrastructure. |
💡 Core Design Philosophy
🔑 CredWeave manages credentials and their lifecycle. It does not perform network requests itself.
┌─────────────────────────────────────────────────────────────────┐
│ Your Application / Task │
│ │
│ 1. Acquire Lease 2. Make Request 3. Report Result │
└───────────┬───────────────────────┬─────────────────────▲───────┘
│ │ │
▼ ▼ │
┌─────────────────┐ ┌─────────────────┐ │
│ 🧵 CredWeave │ │ 🌐 Upstream API │ │
│ Credential Pool │ │ (OpenAI, Cloud) │ │
└─────────────────┘ └────────┬────────┘ │
│ │
└─────────────────────┘
By decoupling credential management from networking, your codebase retains complete control over HTTP clients, connection pools, custom retry policies, and telemetry.
🚀 Quickstart
1. Installation
pip install credweave
(Currently in active development: install from source or editable mode via pip install -e .)
2. Basic Usage
Define credentials, initialize a pool, acquire a lease, and report the execution outcome:
import asyncio
from credweave import Credential, CredentialPool, Outcome
# 1. Define credentials with secret material and arbitrary metadata
credentials = [
Credential(
id="openai-prod-team-a",
secrets={"api_key": "sk-proj-live-mock-key-alpha"},
metadata={"tier": "primary", "rate_limit_rpm": 5000},
),
Credential(
id="openai-prod-team-b",
secrets={"api_key": "sk-proj-live-mock-key-beta"},
metadata={"tier": "fallback", "rate_limit_rpm": 2000},
),
]
# 2. Instantiate a pool
pool = CredentialPool(credentials=credentials)
# 3. Acquire a lease, execute your request, and report the outcome
async def dispatch_request() -> None:
# Acquire the optimal eligible credential:
lease = await pool.acquire()
try:
# Retrieve secret for network execution
api_key = lease.credential.get_secret("api_key")
# >>> Execute with your preferred client (e.g. httpx, openai, etc.) <<<
# response = await openai_client.chat.completions.create(...)
# Report successful completion:
await pool.report(lease, Outcome.success())
except RateLimitException as exc:
# Credential is automatically put in cooldown; upstream retry_after respected:
await pool.report(
lease,
Outcome.rate_limited(retry_after=exc.retry_after, reason=str(exc)),
)
except AuthenticationException as exc:
# Permanently isolate compromised or revoked credentials:
await pool.report(
lease,
Outcome.auth_failed(reason="Invalid API Key or Revoked Token"),
)
🔄 The Lease Lifecycle
CredWeave uses a formal Lease pattern to ensure atomic handling, state tracking, and concurrency safety:
stateDiagram-v2
[*] --> AVAILABLE: Registered in Pool
AVAILABLE --> LEASED: pool.acquire()
LEASED --> AVAILABLE: Outcome.success()
LEASED --> RATE_LIMITED: Outcome.rate_limited()
LEASED --> COOLDOWN: Outcome.transient_error()
LEASED --> REVOKED: Outcome.auth_failed()
LEASED --> UNHEALTHY: Outcome.consecutive_failures_exceeded()
RATE_LIMITED --> AVAILABLE: retry_after elapsed
COOLDOWN --> AVAILABLE: cooldown elapsed
UNHEALTHY --> AVAILABLE: authorize_secret() / reset_credential()
REVOKED --> AVAILABLE: authorize_secret() after repairing the secret
- Acquire: The pool evaluates eligible credentials using the configured
SelectionStrategyand yields an activeLease. - Execute: The client performs the desired operation using the credential secrets.
- Report: The client returns the
Leasealong with a standardizedOutcome(success,rate_limited,auth_failed,transient_error). - Transition: The state store updates credential state, adjusts backoff timers, or escalates health flags.
Concurrency caps and lease timeouts
pool = CredentialPool(
[
Credential("primary", secrets={"api_key": "..."}, metadata={"max_concurrency": 2}),
Credential("backup", secrets={"api_key": "..."}),
],
max_concurrency_per_credential=5, # default cap; None (the default) means unlimited
lease_timeout=60.0, # unreported leases are reclaimed after 60 s
)
A credential at its cap is skipped (its health is untouched) until a slot frees up, and the cap is enforced atomically in the state store, so it holds across threads, asyncio tasks and pools sharing one store. Leases that are never reported are reclaimed automatically on the next acquire/report, or on demand with pool.reclaim_expired_leases(); a late report of a reclaimed lease raises LeaseExpiredError.
🔌 Credential Sources
Besides in-memory credentials (StaticSource), a pool can load credentials from the environment or a JSON file and pick up rotations while running, with no pool recreation and no background thread. Sources are re-read on every acquire; state (usage, cooldowns, health) stays attached to the stable credential id. A secret that was rotated away from is never re-adopted: a pool still holding an older snapshot gets no leases from it, so it cannot undo a rotation. A never-seen secret is adopted as a rotation only by a pool that is advancing from the secret it last saw. CredWeave has no trustworthy source revisions, so it cannot tell a newer unseen secret from an older one. A pool built over a store that already holds a different secret, or whose last observation was superseded, therefore gets no leases from its unseen secret until you call pool.authorize_secret(credential_id); until then its acquires raise NoCredentialsAvailableError. A rotation never changes a credential's health: a REVOKED or UNHEALTHY credential stays out of rotation until you call pool.authorize_secret(credential_id) (after repairing the secret in the source), or pool.reset_credential(credential_id) to clear its state without a secret check. authorize_secret is also how you roll back: restore the earlier secret in the source, then authorize it.
EnvSource: credentials from environment variables
Configure variable names, never secret values:
from credweave import CredentialPool, EnvCredential, EnvSource
source = EnvSource(
[
EnvCredential(
id="openai-primary",
secrets={"api_key": "OPENAI_API_KEY_PRIMARY"}, # secret field -> env var name
optional_secrets={"org_id": "OPENAI_ORG_PRIMARY"}, # omitted when unset
metadata={"tier": "primary", "max_concurrency": 4},
),
EnvCredential(id="openai-backup", secrets={"api_key": "OPENAI_API_KEY_BACKUP"}),
]
)
pool = CredentialPool(source=source)
A required variable that is unset or empty raises CredentialSourceError (naming the variable, never a value). Pass environ={...} to read from an injected mapping instead of os.environ, e.g. in tests.
JsonSource: credentials from a JSON file
{
"credentials": [
{
"id": "primary",
"secrets": {"api_key": "<your-api-key>"},
"metadata": {"tier": "primary", "max_concurrency": 2}
},
{"id": "backup", "secrets": {"api_key": "<your-backup-api-key>"}}
]
}
from credweave import CredentialPool, JsonSource
source = JsonSource("credentials.json") # raises CredentialSourceError if invalid
pool = CredentialPool(source=source)
lease = pool.acquire_sync() # always sees the newest valid file contents
...
print(source.reload_status) # generation, last_error, consecutive_failures
- Strict schema: top-level
credentialslist; each entry has a unique non-emptyid, a non-emptysecretsobject of strings, and an optionalmetadataobject. Unknown fields, duplicate keys, wrong types andNaNare rejected, with secret-safe messages that never echo file content. - Rotation: replace the file (ideally atomically: write a temp file, then
os.replace). A changed secret under the sameidis used by future leases, while the credential's history and cooldown are preserved. Added credentials are eligible immediately; removed ones receive no new leases, and their active leases can still be reported. The pool reads the source once when it is constructed, so a source that cannot be read then raisesCredentialSourceErrorinstead of starting with an unknown baseline. - Last known good: if the file turns malformed, the previous credentials keep being served and
source.reload_status/source.refresh()report the error. Only the initial load raises. - Cost: one
statper read (tune withJsonSource(path, min_check_interval=1.0)). The async API runs file work off the event loop.
YamlSourceis not available yet: it needs a YAML parser, which the zero-dependency standard library does not provide.
🛡️ Security & Defense-in-Depth Guarantees
Handling API keys, bearer tokens, and secrets requires strict security invariants and defense-in-depth:
1. Automatic Secret Masking
Secret dictionary keys are visible for debugging, but secret values are masked as '***' in all representations (__repr__, __str__):
from credweave import Credential
cred = Credential(id="ai-key", secrets={"api_key": "sk-secret-12345"})
print(cred)
# Output: Credential(id='ai-key', secrets={'api_key': '***'}, metadata={})
repr(cred)
# Output: "Credential(id='ai-key', secrets={'api_key': '***'}, metadata={})"
2. Safe Secret Access
Secret values are accessed explicitly via cred.get_secret("key"). Attempting to retrieve a missing key raises a clean SecretAccessError without exposing environment state.
3. Exception & Log Sanitization
Exception classes across CredWeave never format or interpolate raw secret values into error messages. Known secret values and identifiable token patterns are redacted from representations and diagnostics.
4. Deep Immutability
All credential dictionaries, views, and nested metadata structures are deeply frozen and immutable, preventing accidental runtime mutation or tampering.
🏗️ Clean Architecture
CredWeave is engineered strictly following Clean Architecture principles:
src/credweave/
├── domain/ # Pure enterprise entities & domain rules
│ ├── models.py # Credential, Lease (Immutable data structures)
│ ├── outcomes.py # Outcome & OutcomeType
│ ├── enums.py # CredentialState, OutcomeType
│ └── errors.py # CredWeave domain exception hierarchy
├── application/ # Use cases & port protocols
│ ├── ports/ # Clock, CredentialSource, SelectionStrategy, StateStore
│ └── services/ # CredentialPool orchestration
├── infrastructure/ # Adapters implementing application ports
│ ├── clocks/ # SystemClock (real-time execution)
│ ├── sources/ # StaticSource, EnvSource, JsonSource, FileReloader
│ └── stores/ # MemoryStateStore
└── __init__.py # Curated, strictly-typed public API exports
For complete architectural specifications, thread-safety invariants, and design trade-offs, explore our Architecture Guide.
🗺️ Roadmap
CredWeave is evolving through distinct, carefully planned phases—from the initial architectural foundation and secret-safe domain models to intelligent scheduling, distributed state stores, and cloud secret managers.
For a detailed breakdown of release phases and planned capabilities, see the full Roadmap.
🤝 Contributing
We welcome contributions from the community! Whether you are fixing a bug, adding new strategies, improving documentation, or proposing features, please check out our Contributing Guide for development setup, testing workflows, and architectural guidelines.
📄 License
This project is licensed under the terms of the MIT License.
Metadata
Release files for credweave 0.1.0
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 | Actions |
|---|---|---|---|---|
| credweave-0.1.0.tar.gz | 151.3 kB | With provenance | Details for credweave-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| credweave-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for credweave-0.1.0-py3-none-any.whl |
Total release size: 228.2 kB
Release files / credweave-0.1.0.tar.gz
| Download URL | credweave-0.1.0.tar.gz |
|---|---|
| Size | 151.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fb1e24b71e3a3d6d5f4963c50f8b0f1d02863d1ded4b59079aa6aba5ef538c0c
|
|
BLAKE2b-256 checksum How to use checksums |
06db4cbce167d0cb950d892fa19974e8ad8fb1c0aa746333183cbe548eba1a91
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 8, 2026.
Transparency logRelease files / credweave-0.1.0-py3-none-any.whl
| Download URL | credweave-0.1.0-py3-none-any.whl |
|---|---|
| Size | 76.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ced80bc88c3ed7b2787a98aceb6e4b64aa527e1e757c9d1b3ebe509d7db9dcf2
|
|
BLAKE2b-256 checksum How to use checksums |
d21d8103538473d9bbf949ce83144efd3772fac6ca5ead4a7392210b4e0a356f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Oct 8, 2026.
Transparency logSecurity for credweave 0.1.0
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 credweave-0.1.0.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 Oct 8, 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.
To learn more about protecting yourself from malicious actors, visit our package security documentation.