Ironflow Python SDK
Python client for Ironflow — the Continuous History platform for backend systems.
Installation
pip install ironflow-py
import ironflow
Install
ironflow-py, notironflow. The distribution name isironflow-py; the import name isironflow. The bare nameironflowon PyPI belongs to an unrelated third-party project (a materials-science tool from the pyiron group), which also installs a top-levelironflowmodule — a virtualenv holding both has two claimants on that name and the install order silently decides which wins. Keep them in separate environments.
Requires Python 3.10+.
Available from v0.33.0. Versions track the Ironflow engine release, so ironflow-py
0.33.0 is the client for engine 0.33.0.
Status
Experimental and client-only. It can emit events and query runs, projections,
KV, and config, but it ships no worker runtime — there is no step.run, no
step.sleep, and no push or pull mode.
Two clients, two protocols, neither a superset of the other. IronflowClient
speaks REST and retries idempotent methods. IronflowRPC / AsyncIronflowRPC
speak ConnectRPC and reach 46 capabilities REST does not serve — webhook
management, agent tools, time travel, pub/sub consumer groups, function
versioning, and environment lookup/key rotation — including four server
streams. IronflowRPC retries only the unary methods the protos annotate
side-effect-free, and reconnects a subscription only when you position it (see
Retries).
Routes the server's manifest annotates with schemas get a typed models.* TypedDict
return and keyword-only query parameters; routes it does not annotate still return Any
and take no query kwargs. TypedDict is a type-checker construct only — there is no
runtime validation. Headers declared by a route, including If-Match,
If-None-Match, and Idempotency-Key, are generated as typed keyword arguments.
BaseClient.request() remains the escape hatch for undeclared headers. WebSocket and
watch endpoints (/ws, config watch, KV watch) have no generated methods at all and
request() cannot reach them either; poll the corresponding read method, use a
ConnectRPC subscription, or use the Go or JavaScript SDK for a live watch.
For durable step execution, use the Go or JavaScript SDK.
Quick Start
from ironflow import IronflowClient
client = IronflowClient(
server_url="http://localhost:9123",
api_key="ifkey_...",
)
# Emit an event
client.events_create(body={
"name": "user.created",
"data": {"user_id": "123", "email": "user@example.com"},
})
# List runs
runs = client.runs_list()
# Get a specific run
run = client.runs_get("run_abc123")
# List projections
projections = client.projections_list()
# Public server inspection
health = client.health()
readiness = client.ready()
capabilities = client.capabilities()
ConnectRPC client
For the capabilities REST does not serve. Request and response types come from
ironflow.rpc.v1; ironflow._gen is private and its layout may change.
from ironflow import IronflowRPC
from ironflow.rpc.v1 import CreateWebhookSourceRequest, SubscribeRequest
with IronflowRPC(server_url="http://localhost:9123", api_key="ifkey_...") as rpc:
source = rpc.webhooks.create_source(
CreateWebhookSourceRequest(name="Stripe", event_prefix="stripe.")
)
# Server streams are ordinary iterators; breaking out cancels.
for event in rpc.pubsub.subscribe(SubscribeRequest(pattern="topic:orders.*")):
print(event.event_id)
break
AsyncIronflowRPC mirrors it method for method — await the unary calls,
async for the streams, and await rpc.aclose() instead of close().
Failures raise IronflowRPCError, which subclasses IronflowError, so
except IronflowError still catches every failure from either client. Full
reference, including timeouts, NO_TIMEOUT, and stream lifetime:
docs.ironflow.run/reference/api/python-sdk.
Retries, and what can still go wrong
A unary call that fails with unavailable — a refused connection, a socket
dropped mid-response, a server shedding load — is sent again, up to 3 attempts
with exponential backoff. Nothing else is retried: every other Connect code is a
decision the server will reach again identically.
Only side-effect-free methods are retried. The protobuf definitions annotate
them idempotency_level = NO_SIDE_EFFECTS, and the client reads that annotation
rather than any list of its own. A method without it — every create, update,
delete, rotate and emit — is sent exactly once and its failure is raised to you
immediately, because a transport error cannot tell you whether the server
committed the write before the connection dropped.
Two things this does not promise:
- A retried read may execute more than once on the server. A response lost on the way back is indistinguishable from a request that never arrived, so the retry re-runs the method. That is harmless for a read by definition, and it is the reason the annotation gates the behaviour.
- A write is never retried for you. If you need one repeated safely, repeat
it yourself with an idempotency key —
EmitRequestandPublishRequestboth carryidempotency_key— and treat a failed write as unknown, not failed.
timeout= still bounds the whole call including every retry and backoff, not
each attempt. Pass max_attempts=1 to the constructor to switch retries off.
subscribe reconnects only if you positioned it. Set
options.start_after_sequence to the last event.sequence you processed, and a
dropped connection is retried from there. That field is both the cursor and the
opt-in: without it there is no honest place to resume from, so the stream raises
and re-subscribing is yours.
from ironflow.rpc.v1 import SubscribeRequest, SubscribeOptions
cursor = 0
for event in rpc.pubsub.subscribe(SubscribeRequest(
pattern="topic:orders.*",
options=SubscribeOptions(start_after_sequence=cursor),
)):
handle(event)
cursor = event.sequence # persist this if you need to resume across restarts
A resumed stream is at-least-once: the frame in flight when the connection
dropped may arrive twice, because the server sending it is not you having
processed it. Make handle idempotent, or dedupe on event.sequence.
The other three streams are not reconnected, and do not need to be.
stream_events, wait_catchup_stream and join_consumer_group are positioned
by the server on a durable consumer, so simply calling them again resumes where
they left off. A client-side cursor there would move a position other readers
share.
API Coverage
This SDK is auto-generated from the Ironflow server's route manifest. Run make sdk-health
for the current method count — it changes on every regeneration, so it is not reproduced
here. Route coverage is not the same as usable coverage; see the Status section above.
See the SDK Comparison Matrix for full coverage details.
Dependencies
Two direct runtime dependencies, connectrpc and pyqwest, which resolve to six
packages — two of them compiled:
connectrpc ─┬─ protobuf-py ── protobuf-py-ext (native, CPython only)
├─ pyqwest (Rust)
│ └─ opentelemetry-api
└─ typing_extensions
They are required, not an extra. The ConnectRPC client is part of the default
contract, so putting it behind a flag would turn a heavier install into a runtime
ImportError — see ADR 0062 (engine repo, internal).
IronflowClient itself still uses only the standard library (urllib, json),
and import ironflow does not load the ConnectRPC stack — that cost is paid on
first use of IronflowRPC.
Where this package is published from
The Ironflow engine is closed source. This SDK's source is mirrored to
sahina/ironflow-py at each release, and PyPI
uploads happen from there over Trusted Publishing.
No PyPI API token exists for this project, so every release carries an attestation
binding the artifact to a public commit you can inspect.
The mirror is read-only. Pull requests against it are closed without review; source changes land in the engine repo and appear at the next release.
Bugs and security
Issues are disabled on the mirror. Everything goes to one tracker:
- Bugs and feature requests → sahina/ironflow-issues, component Python SDK. Include your Python version, platform, and a minimal repro.
- Security issues → private advisory. Do not open a public issue.
- Commercial licensing → the contact in LICENSE.
License
See LICENSE — SPDX LicenseRef-Ironflow-EULA. Not an OSI-approved open source
licence; read it before deploying commercially.
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 ironflow_py-0.33.1.tar.gz.
File metadata
- Download URL: ironflow_py-0.33.1.tar.gz
- Upload date:
- Size: 172.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
afab30ea34a877343f1e0e741963697b24aa418c363b8c80c1b24d25c2202dfe
|
|
| MD5 |
d0460862a8691f0f5ba4797600cd4358
|
|
| BLAKE2b-256 |
ee6cc30f337a229a079f9c90a1e1d76866c129cdb5b35f7e2068805a158b0c8f
|
Provenance
The following attestation bundles were made for ironflow_py-0.33.1.tar.gz:
Publisher:
publish.yml on sahina/ironflow-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ironflow_py-0.33.1.tar.gz -
Subject digest:
afab30ea34a877343f1e0e741963697b24aa418c363b8c80c1b24d25c2202dfe - Sigstore transparency entry: 2632084022
- Sigstore integration time:
-
Permalink:
sahina/ironflow-py@06f1ca70675290e65b91b3ce8fad720ea2c96af1 -
Branch / Tag:
refs/tags/v0.33.1 - Owner: https://github.com/sahina
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@06f1ca70675290e65b91b3ce8fad720ea2c96af1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file ironflow_py-0.33.1-py3-none-any.whl.
File metadata
- Download URL: ironflow_py-0.33.1-py3-none-any.whl
- Upload date:
- Size: 138.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d11852ecc34ca930863d4470ed1608c66fa935c1dd587e37dd035fe8940c85ee
|
|
| MD5 |
566861cd8616c911616d315345734428
|
|
| BLAKE2b-256 |
ed38128f83a4e2813987638e0b7c4e73a4ee74f68024f221fc0a21fe2b62557a
|
Provenance
The following attestation bundles were made for ironflow_py-0.33.1-py3-none-any.whl:
Publisher:
publish.yml on sahina/ironflow-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ironflow_py-0.33.1-py3-none-any.whl -
Subject digest:
d11852ecc34ca930863d4470ed1608c66fa935c1dd587e37dd035fe8940c85ee - Sigstore transparency entry: 2632084060
- Sigstore integration time:
-
Permalink:
sahina/ironflow-py@06f1ca70675290e65b91b3ce8fad720ea2c96af1 -
Branch / Tag:
refs/tags/v0.33.1 - Owner: https://github.com/sahina
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@06f1ca70675290e65b91b3ce8fad720ea2c96af1 -
Trigger Event:
push
-
Statement type: