Axon Python Runtime
axon-runtime-sdk is the Python distribution of the canonical Axon runtime
model; its stable import namespace is axon_sdk. It exposes descriptor-bound
invocation, admission, receipt proof, streaming, bidi sessions, provider
binding, and the reference LocalRuntime.
The protocol has one invocation shape:
invoke(caller, callee, ability, subject, nonce, causal_context, args) -> receipt
Every public dispatch binds an exact AbilityDescriptorRef. The receipt closes
the same seven-field invocation with descriptor, implementation, authority,
input, output, and causal proof facts.
Install
pip install axon-runtime-sdk
The runtime facade loads a target-specific Dendrite native bridge. Release artifacts may supply that bridge as package data; source checkouts can select an explicit bridge with:
export AXON_DENDRITE_BRIDGE_LIB=/absolute/path/libaxon_dendrite_bridge.so
Descriptor-Bound Invocation
The native bridge accepts a complete request signed by caller-owned code.
Create the envelope and signature with DescriptorBoundInvocationRequest.signed
or bind an external signature with DescriptorBoundInvocationDraft; the bridge
never receives the signing key.
from axon_sdk import DendriteBridge
from axon_sdk.invocation import DescriptorBoundInvocationRequest
def dispatch(bridge: DendriteBridge, request: DescriptorBoundInvocationRequest,
request_id: str) -> dict:
return bridge.invoke_descriptor_bound(
request,
request_id=request_id,
content_type="application/json",
timeout_ms=30000,
)
The request owns caller, callee, subject, exact descriptor reference, nonce, causal context, payload bytes and signature. Its payload must match the signed digest. The receiving runtime verifies the caller signature and admission policy. LocalRuntime parent handles and supervisor options are rejected rather than silently discarded. Transport responses do not replace independent receipt verification with trusted identity keys.
The historical session-signing shorthand (SidecarTransport with
DendriteSigningConfig, including decorator-based calls) does not match the
current native contract. Supplying signing keys to DendriteBridge now fails
before library loading; use the complete unary request above. The historical shorthand
remains pending migration; complete requests also support server-stream and bidi.
Provider Binding
AbilityDescriptor, AbilityImpl, and ProviderBinding are generic runtime
contracts. A binding is valid only when the descriptor and implementation refer
to the same exact descriptor version.
from axon_sdk import AbilityDescriptor, AbilityImpl, ProviderBinding
from axon_sdk.invocation import sha256
descriptor_ref = (
"easynet:///r/example/ability/example.runtime.documents.summarize"
"@descriptor.documents.v1#aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!invoke"
)
descriptor = AbilityDescriptor(
descriptor_ref=descriptor_ref,
descriptor_version="descriptor.documents.v1",
schema_hash=sha256(b"documents-schema-v1"),
)
implementation = AbilityImpl(
descriptor_ref=descriptor_ref,
impl_hash=sha256(b"python-provider-v1"),
runtime_env="python;provider=documents-v1",
)
async def summarize(context):
return context.payload
binding = ProviderBinding(
descriptor=descriptor,
implementation=implementation,
handler=summarize,
)
LocalRuntime.bind_provider(binding) is the canonical in-process registration
path. LocalRuntime.register_ability(...) constructs the same binding object
for callers that already hold the three components separately.
Streams And Bidi
StreamSource,StreamSink, andLoopbackStreamprovide bounded credit, monotonic cancellation, and one terminal frame.DendriteServerStreamexposes incremental server-stream reads.DendriteSignedBidiStreamcarries typed stream descriptors and opaque content frames without interpreting downstream content.EventStreamresumes invocation observation from a sequence offset.
For a complete externally signed server-stream request, use
bridge.stream_descriptor_bound(request, request_id=..., content_type=...).
Unary and server-stream carriers preserve the same signed envelope and payload.
Use the returned DendriteServerStream as a context manager so that normal
completion, early exit and exceptions release the native handle:
with bridge.stream_descriptor_bound(
request,
request_id="caller-owned-stream-request-id",
content_type="application/json",
chunk_timeout_ms=1000,
chunk_buffer_size=64,
) as stream:
for protocol_chunk in stream:
consume(protocol_chunk)
The iterator returns native transport chunk bytes. Decoding and independent receipt verification remain the caller's responsibility. An older library without stream read/close capability is rejected before stream allocation.
bridge.bidi_descriptor_bound(request, request_id=..., content_type=..., streams=[SignedBidiStreamDescriptor(...)]) uses the same complete signed
opening, with explicit stream descriptors and bounded request/chunk buffers.
DendriteSignedBidiStream.close() sends EOF while allowing receipt reads;
release() disposes the native stream and abandons unread frames. Context exit
always releases resources, including after EOF or an exception. For graceful
completion, send EOF, drain the terminal receipt, then release.
Receipt frames preserve the native admission or terminal receipt in receipt
and expose terminal. Terminal delivery ends iteration and marks the native
handle already released; calling release() afterward is harmless. Receipt
projection does not verify signatures: use independently trusted identity pins.
Missing or ambiguous canonical receipt slots are rejected.
A failed or unacknowledged send leaves the sending direction in a failed state. Later sends and EOF attempts raise the retained error without replaying the operation. Receiving terminal evidence and releasing resources remain available. Only acknowledged EOF is reported as idempotent success; concurrent EOF/close calls are serialized. This does not establish whether an unacknowledged remote operation took effect.
The v1 frame chain is not independent origin authentication. Use authenticated transport and trusted TLS terminators as described in the InvokeBidi security boundary.
Runtime Process
start_server() connects to an existing Axon runtime or starts a local
reference runtime. Its ServerHandle owns an explicit process state and stops
only processes it created.
from axon_sdk import start_server
with start_server() as runtime:
print(runtime.endpoint)
Verification
From this directory:
python -m pytest
python -m black --check axon_sdk tests examples
python -m ruff check axon_sdk tests examples
python -m mypy axon_sdk
python -m compileall -q axon_sdk tests examples
The RF-1 boundary test scans the public exports, package source, examples, and package metadata to prevent product-owned modules or lifecycle APIs from returning to the canonical SDK.
Source release scope
This distribution is a deliberately bounded public SDK, not a source release of every EasyNet control-plane service, research mechanism, or evaluation asset. The release-scope statement explains the staged policy. It does not restrict the Apache-2.0 rights granted for files actually included in this distribution.
Native HTTPS trust
Pass an explicit public PEM CA bundle when opening an HTTPS native session:
from pathlib import Path
from axon_sdk import DendriteBridge
bridge = DendriteBridge(
endpoint="https://runtime.example.org:50051",
tls_ca_pem=Path("runtime-ca.pem").read_text(encoding="utf-8"),
)
The same tls_ca_pem keyword is exposed by SidecarTransport and the
ability_call decorator; their signing shorthand remains subject to the
contract limitation above. SidecarTransport preserves it when reopening the
bridge, including through its reconnect callback. The CA configures transport
trust; it is not an invocation identity, signing key or authority grant.
This requires a native bridge build supporting tls_ca_pem (introduced in
Axon source commit 5f739034). Native code owns certificate parsing, the 64 KiB
limit and endpoint-hostname verification. An older bridge or invalid trust
configuration fails; the SDK does not retry without the CA or downgrade HTTPS.
No field is sent when the option is unspecified. Existing HTTP endpoints remain
unprotected; TLS termination remains a trust boundary for streamed data.
Explicit live native TLS check
After uv sync --extra dev in sdk/python, build the current native library
from the repository root:
cargo build --locked --manifest-path core/runtime-rs/dendrite-bridge/Cargo.toml
Set AXON_PYTHON_SDK_EXECUTABLE to the absolute SDK virtual-environment Python
path and AXON_NATIVE_BRIDGE_TEST_LIB to the absolute library just built
(libaxon_dendrite_bridge.dylib on macOS, .so on Linux). Then run:
cargo test --locked --manifest-path core/runtime-rs/dendrite-bridge/Cargo.toml \
common::session_tls_tests::python_native_tls_rpc_preserves_signed_request \
-- --ignored --exact
This explicit test starts a bounded TLS probe, launches the real Python facade and native library, checks the actual RPC request, and rejects wrong CA and hostname. The receiver uses the canonical Rust signature verifier with an independently pinned test caller public key. Changed signature, payload and caller identity are rejected at authentication. Valid signatures reach a deliberate policy denial; this does not prove permission to execute, replay protection or receipt verification. It runs only when explicitly selected because it requires the Python environment and native build.
Explicit live runtime execution check
With the same Python and freshly built native library environment variables, run from the repository root:
cargo test --locked --manifest-path sdk/rust/Cargo.toml --features grpc \
--test python_native_execution python_executes_and_verifies_runtime_receipts \
-- --ignored --exact
This launches Python against a real canonical Rust LocalRuntime through the native bridge and a test-only tonic service. It checks exact binary echo output, independently verifies admission and terminal signatures against a pinned test callee key, checks invocation/output bindings, and rejects receipt tampering. Unary exposes receipt endpoints rather than every lifecycle receipt, so this check does not verify the complete intervening hash chain. The fixture uses loopback HTTP and an explicit permissive test policy; it does not establish production authorization, external causal ancestry, or released-package support. The explicit prerequisites keep this test out of the default Rust test run.
Server-stream proof boundary
stream_descriptor_bound currently returns raw protobuf chunks. Receiving those
bytes or verifying the terminal receipt does not verify every progress payload.
The server-stream receipt scope
records the current implementation, a reproducible substitution characterization,
and the unfulfilled protocol transcript-binding obligation. Full stream proof
verification remains pending.
Pending event-reader cleanup
EventStream.close() stops observation without cancelling the provider.
When an event read is waiting, the runtime owns both the condition wait and the
close-signal wait. On either completion or cancellation of the reader task, it
cancels and joins those waits before leaving the condition context. This ordering
lets asyncio.Condition.wait() reacquire its lock before the context releases
it, avoiding leaked wait tasks and blocked provider completion.
tests/test_event_reader_close.py exercises explicit close and reader-task
cancellation with a parked provider and checks that the owned wait tasks retire.
These are single-event-loop source checks; they do not certify cross-thread
access, installed-wheel behavior or process-crash recovery.
Complete-request Client forwarding
Client.invoke_descriptor_bound and SidecarTransport.invoke_descriptor_bound
accept the canonical caller-signed request and delegate to the native bridge.
Configure the SidecarTransport without signing; the caller constructs and
signs explicit executor, subject, nonce and causal facts before forwarding.
with SidecarTransport(endpoint=endpoint, library_path=library_path) as transport:
response = Client(transport).invoke_descriptor_bound(
request, request_id="document-review",
content_type="application/octet-stream", timeout_ms=3000,
)
A bound ability(...) must match the signed request; principal(...) cannot
override its subject. Custom transports must provide the same optional method
or Client raises an explicit configuration error. Responses and native failure
receipt evidence are preserved for independent verification. This method does
not retry invocations. Concurrent initialization/reconnect and other lifecycle
surfaces require separate acceptance; this unary evidence does not prove them.
The existing ability/payload call/call_raw paths still require session signing
and infer callee from ability. They are unsupported native signed-call paths
with the current native schema. An ability URI does not identify its executor.
Use the complete request above or the direct bridge entrypoint. See the
native entrypoint matrix.
The explicit python_exchanges_complete_signed_bidi_through_native Rust
integration exercises an installed local Python wheel against the canonical
loopback bidi service and a pinned native library. It checks one binary input
plus EOF, actual progress/output, invalid-signature and replay rejection, and
independently pinned admission and success/failure terminal receipts, including
nonce/output binding and tamper rejection. Unary bridge/Client regression passes
alongside it. A subsequent coordinated candidate run
also passed all eight explicit native selections, including Python bidi.
These local results do not establish registry installation, TLS deployment or
complete transcript proof.
python_streams_complete_signed_requests_through_native additionally exercises
the installed wheel's complete-request server stream. Python returns exact raw
protobuf chunks to Rust's shared verifier, which checks progress bytes, order,
success/failure terminal output and independently pinned endpoint receipt
signatures with identity, nonce and output bindings. Wrong signer/replay are
rejected. The candidate gate now requires this exact test alongside Python
unary/bidi. The rebuilt nine-selection candidate run
also passed, including this exact stream test.
Endpoint receipts do not establish a complete signed stream transcript.
Metadata
Release files for axon-runtime-sdk 0.205.34
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| axon_runtime_sdk-0.205.34.tar.gz | 2.3 MB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| axon_runtime_sdk-0.205.34-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| axon_runtime_sdk-0.205.34-py3-none-manylinux_2_17_x86_64.whl | Python 3 | none | Linux glibc 2.17+ x86-64 | Details |
| axon_runtime_sdk-0.205.34-py3-none-manylinux_2_17_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64 | Details |
| axon_runtime_sdk-0.205.34-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
| axon_runtime_sdk-0.205.34-py3-none-macosx_10_12_x86_64.whl | Python 3 | none | macOS 10.12+ x86-64 | Details |
Total release size: 14.7 MB
Release files / axon_runtime_sdk-0.205.34.tar.gz
| Download URL | axon_runtime_sdk-0.205.34.tar.gz |
|---|---|
| Size | 2.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c2fa5d044cf694f5cdc6f00fe5b42da4fccfea2ad41305f5f98da12c2b366fa3
|
|
BLAKE2b-256 checksum How to use checksums |
de165604ad7464fbb487941919e7363b3f5dcba457a6008ae8a68528fb4004ac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / axon_runtime_sdk-0.205.34-py3-none-win_amd64.whl
| Download URL | axon_runtime_sdk-0.205.34-py3-none-win_amd64.whl |
|---|---|
| Size | 2.2 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
b7b1e9741ee0189958d5d07bc0619f2fd2251e8ef97886669b372a7c23c28b3a
|
|
BLAKE2b-256 checksum How to use checksums |
600c5a2e511a4881cbde81eb6dc0f5d81f6641b481e13fe051c31bfe6fb91518
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / axon_runtime_sdk-0.205.34-py3-none-manylinux_2_17_x86_64.whl
| Download URL | axon_runtime_sdk-0.205.34-py3-none-manylinux_2_17_x86_64.whl |
|---|---|
| Size | 2.8 MB |
| Tags | Linux glibc 2.17+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
19119227c435b5774e8697c743fe50a3b4a2945aa4651fcbdfd1b287d14846fc
|
|
BLAKE2b-256 checksum How to use checksums |
6e5e78d319c69473766a2d4a872e49bc31339381a767e19a7cf971111224f60a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / axon_runtime_sdk-0.205.34-py3-none-manylinux_2_17_aarch64.whl
| Download URL | axon_runtime_sdk-0.205.34-py3-none-manylinux_2_17_aarch64.whl |
|---|---|
| Size | 2.7 MB |
| Tags | Linux glibc 2.17+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
e0199c6e1185d11132a0a47b17a2d85cf93777dc69718c05b9cbf3f9ceb4d963
|
|
BLAKE2b-256 checksum How to use checksums |
da012acc5284b5734bb3480e4b1267263869f2d63ae77eae0570010562fc7505
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / axon_runtime_sdk-0.205.34-py3-none-macosx_11_0_arm64.whl
| Download URL | axon_runtime_sdk-0.205.34-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 2.3 MB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
d83042bcf99486f8b7d2a1cc1d075876dc32a97f3a494a9442ab8092e53b55bc
|
|
BLAKE2b-256 checksum How to use checksums |
5129665189a882da50d24b495a86ce85cab511242d36801cbdabd817ee11cfeb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / axon_runtime_sdk-0.205.34-py3-none-macosx_10_12_x86_64.whl
| Download URL | axon_runtime_sdk-0.205.34-py3-none-macosx_10_12_x86_64.whl |
|---|---|
| Size | 2.4 MB |
| Tags | Python 3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
23cb3c49097f8824b44cf68f08c33c058a44881e262f2be978c5a809c6271172
|
|
BLAKE2b-256 checksum How to use checksums |
0b721a71ff6967b1a17dbe130d4d4ac4c885c26c5307e5849435bbe06b0ad410
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|