exactly-once
Idempotency middleware for AI-agent side-effects. Wrap any tool call that must never fire twice — a payment, an email, an onchain transaction — and it runs at most once per key, replaying its stored result across retries, concurrent workers, crashes, and replays.
This is exactly-once effect (at-most-once execution + replay-on-success) — not exactly-once delivery, which is impossible (Two Generals / FLP). The library is scrupulous about that line; see Guarantees & limits.
The problem
Agents retry. They crash and resume. They get replayed during debugging. Every one of those can fire a side-effect twice — a card charged twice, an email sent twice, a transaction submitted twice. Frameworks give you retries and checkpoints, but not idempotency for the effects those retries cause — so you hand-roll dedupe logic, badly, every time.
exactly-once is the missing primitive: two lines that make an unsafe retry safe.
Install
pip install exactly-once # core — zero required dependencies
pip install "exactly-once[redis]" # + a Redis store
# extras: [redis] · [postgres] · [onchain] · [langgraph] · [crewai]
Python 3.11+ · fully typed (py.typed) · no LLM, no model costs.
Quickstart
from exactly_once import once, Store, current_key
store = Store.sqlite("effects.db") # or .memory() / .redis(url) / .postgres(dsn)
@once(store, key=lambda order, **_: f"charge:{order.id}")
def charge_card(order):
# pass our key through as Stripe's own idempotency key — belt and suspenders
return stripe.charge(order.customer, order.amount, idempotency_key=current_key())
charge_card(order) # runs the charge
charge_card(order) # replays the stored result — Stripe is NOT called again
Inline effects use the context manager. Async is identical — async with and async callables have the same semantics:
with once(store, key="welcome:user-4471") as guard:
if guard.fresh:
guard.result = send_email(...) # skipped on every replay
async with once(store, key=f"notify:{event_id}") as guard:
if guard.fresh:
await post_to_slack(...)
⚠️ Key on business identity (
order_id), never a mutable value like amount — two distinct $50 charges must not collapse into one.
See it stop a double-charge in 15 seconds:
python examples/crash_mid_payment.py
It crashes an agent mid-payment, resumes, and shows one charge with @once versus two without — side by side. More runnable examples in examples/.
What you get
- Two-line API — a
@oncedecorator and awith once(...)context manager. Sync and async, identical semantics. - Pluggable stores — memory · SQLite · Redis · Postgres — each with a documented atomicity and writer model (see the table below).
- Safe by default on crash — a crash mid-effect is quarantined, never silently re-fired. Opt-in policies (
check_then_decide,wait,auto_retry) when you want more. - Concurrency-safe — an ownership/fencing token on every claim; an optional lease + heartbeat makes reconciliation safe even across live workers.
- Onchain adapter — dedupe transactions by
(chain_id, from, nonce, calldata); a resumed agent never double-submits. - Framework helpers — thin
once_node(LangGraph) andonce_tool_run(CrewAI) wrappers. - Honest by policy — leads with its limits, and a CI lint fails the build if the docs ever overclaim.
- Zero required deps, fully typed, zero LLM. It's plumbing — it works offline, forever.
Guarantees & limits
The mechanism: compute a stable key → atomically claim it → if committed, replay the stored result without re-running; if in-flight, block/deny per policy; if new, run the effect and commit. On a crash mid-effect the key is left in-flight and quarantined — a half-completed payment must never silently re-fire.
It guarantees (given a store with an atomic claim): the effect is entered at most once per key across retries, concurrent workers, crashes, and replays; after a commit, every later call replays the stored result; a concurrent second caller never runs in parallel; a crash mid-effect never auto-re-fires.
It does not: promise exactly-once delivery (impossible — it's at-most-once execution + replay-on-success, and end-to-end "the world changed once" holds only when composed with an idempotent provider). It cannot know the outcome of a crash mid-effect — it refuses to guess (quarantine), and lets a prober or a provider idempotency key narrow the window. It is only as strong as the store you pick:
| Store | Guarantee | Use for |
|---|---|---|
| memory | strong within one process | tests, dev |
| SQLite | strong on one host | single-node agents, jobs, CI |
| Redis | strong single-instance · best-effort under failover | distributed workers sharing one Redis |
Postgres SERIALIZABLE |
true multi-writer, linearizable | multi-host production |
The full boundary — every guarantee and every limit — is in docs/ARCHITECTURE.md §9.
Recipes
Crash recovery for money movement — observe the world instead of guessing:
from exactly_once import once, check_then_decide, ProbeResult, Verdict
def prober(key): # observe the world: did the charge actually land?
charge = find_stripe_charge_by_idempotency_key(key) # your lookup against Stripe
return ProbeResult(Verdict.COMMITTED, charge) if charge else ProbeResult(Verdict.NOT_COMMITTED)
@once(store, key=lambda o: f"charge:{o.id}", policy=check_then_decide(prober))
def charge_card(order): ...
Concurrent workers — a lease makes reconciliation safe across live workers (a dead worker's orphan is adopted by exactly one; a live one is never adopted):
@once(store, key=..., policy=check_then_decide(prober), lease_ttl=30.0)
def charge_card(order): ...
Onchain — at-most-once transactions:
from exactly_once.onchain import onchain_once, TxIntent, Web3ChainClient
chain = Web3ChainClient(w3, private_key)
@onchain_once(store, chain) # key = (chain_id, from, nonce, calldata)
def payout(to, amount) -> TxIntent:
return TxIntent(to=to, value=amount)
LangGraph / CrewAI:
from exactly_once.integrations.langgraph import once_node
from exactly_once.integrations.crewai import once_tool_run
@once_node(store) # keys on the run's thread_id + node name
def charge(state, config): ...
class ChargeTool(BaseTool):
@once_tool_run(store, key=lambda self, order_id, **_: f"charge:{order_id}")
def _run(self, order_id): ...
How it compares
exactly-once is a library that guards the effect boundary — not a replacement for a workflow engine. It composes with all of these.
| exactly-once | Temporal / Restate / DBOS | AWS Lambda Powertools | Stripe idempotency keys | |
|---|---|---|---|---|
| What | a two-line library | a durable-execution runtime you adopt | a Lambda-only utility | a single provider's feature |
| Scope | the effect boundary, anywhere | orchestration and the effect boundary | Lambda handlers | one API |
| Crash mid-effect | quarantine — never auto-re-fire | activity re-runs; you make it idempotent | deletes the record and re-runs | replays the cached response |
| Adoption cost | pip install, two lines |
adopt a runtime | be on AWS Lambda | be on Stripe |
Every durable-execution engine, at the effect boundary, reduces to "at-least-once + an idempotency key." exactly-once is that reduction as a drop-in — with a safe default for the crash it can't otherwise resolve.
Documentation
- SPEC.md — the design spec & PRD.
- docs/ARCHITECTURE.md — the state machine, the store contract, and §9 Guarantees & Limits (read this before trusting anything).
- CHANGELOG.md · examples/ · ROADMAP.md
Development
uv venv --python 3.11 && uv pip install -e ".[dev]"
uv run pytest # full suite (Redis/Postgres need Docker; onchain needs Foundry)
uv run mypy src/exactly_once # strict typing
uv run ruff check src tests examples scripts # lint
uv run python scripts/check_docs_honesty.py # the docs-honesty gate
uv run python scripts/benchmark.py # per-call overhead
Overhead per guarded call is one store round-trip plus key/codec work — a few microseconds on the in-memory store; real deployments are dominated by the store's own latency.
Part of the Swarm Proof toolkit
Trust infrastructure for the agent economy — seven projects, one thesis.
| Project | What it does |
|---|---|
| stampede | Point a herd of realistic agents at your system before real ones arrive |
| mockworld | A synthetic internet for agents — fake Stripe, Gmail, exchange, instantly |
| mcp-probe | The CI quality suite for MCP servers — lint, contract-test, benchmark, load |
| costbomb | Denial-of-wallet fuzzing — find the inputs that make your agent spend $500 |
| exactly-once ← you are here | Idempotency middleware so agent side-effects fire once |
| agent-postmortems | A structured incident database + post-mortem standard for agent failures |
| awesome-agent-reliability | The curated map of the field |
License
MIT. Citable via CITATION.cff.
Metadata
Release files for exactly-once 0.2.1
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 |
|---|---|---|---|---|
| exactly_once-0.2.1.tar.gz | 100.5 kB | With provenance | Details for exactly_once-0.2.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| exactly_once-0.2.1-py3-none-any.whl | Python 3 | none | any | Details for exactly_once-0.2.1-py3-none-any.whl |
Total release size: 143.0 kB
Release files / exactly_once-0.2.1.tar.gz
| Download URL | exactly_once-0.2.1.tar.gz |
|---|---|
| Size | 100.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
79677c2f8619f0347b95ea9b8d2e1b305577b95f33e91d60baa498c119ae2397
|
|
BLAKE2b-256 checksum How to use checksums |
d9163a2cbfb364bb35b330dffd8c1c96a8f459b324e02d5628b1dcbe4876aadf
|
| 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 Aug 28, 2026.
Transparency logRelease files / exactly_once-0.2.1-py3-none-any.whl
| Download URL | exactly_once-0.2.1-py3-none-any.whl |
|---|---|
| Size | 42.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
c4123cf4156b88a8dd8bba5de68b708d2c4b4263d0b0e037df14483f02316942
|
|
BLAKE2b-256 checksum How to use checksums |
c8f71c23a7a57af75e6baaa6896bebee1586c83cb3b7c9c81eb2b58ae0c2701c
|
| 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 Aug 28, 2026.
Transparency logSecurity for exactly-once 0.2.1
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 exactly_once-0.2.1.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 Aug 28, 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.