macula-py
A Python node on the macula 12 mesh, over macula-go's C ABI
Status, 2026-09-26: on the macula 12 wire: ML-DSA-87 identities (as the ML-DSA-87 + RSA-PSS-4096 composite in
pq_hybrid, the fleet's profile), ML-KEM hybrid key exchange, signed requests. Calls and streams by direct dial, serving (under an org or in a node's own namespace), publish/subscribe, the DHT and node-served content are tested against two in-process macula 12 stations on every CI run. 0.1.0 spoke the retired classical wire and cannot reach the current fleet.
What is this?
A Python SDK for the Macula mesh: a node's key, a pool of links to stations it pins by node_id, calls and streams that reach a provider by direct dial, serving procedures, publish/subscribe, the DHT and node-served content, all as asyncio coroutines.
Macula is a federated mesh for sovereign application networks. A station relays and holds the DHT; a node is anything else that joins, and this package is a node.
It is a binding, not a reimplementation. The protocol lives in
macula-go, which exports it as a C
ABI (cabi/macula.h, contract in cabi/CONTRACT.md) shared by every SDK not
written in Go. macula-py loads that library with ctypes, so the wire,
the post-quantum handshake and the signing rules are the ones macula-go
already checks byte for byte against macula itself.
Install
pip install macula-py
Wheels exist for Linux x86-64 and arm64 (glibc 2.28 or later), macOS 13 or later on Apple silicon and Intel, and Windows x86-64. Each carries macula-go's library; nothing compiles at install time and there are no runtime Python dependencies. On any other platform pip finds no wheel.
Quick start
import asyncio
from macula_py import NodeKey, Pool, Seed
STATION = Seed("station-fi-helsinki.macula.io", 4433,
"004d1f470097ccf8826ce291900e882fdb1f20375e53901facaec0f23eb4efd8")
REALM = "abb81b5a614b63551b400b810648c0c8a78efad845442630c94b46cc95d2fcd1" # io.macula
REALM_KEY = "..." # io.macula's public realm key, hex
async def main() -> None:
key = await NodeKey.load_or_create("node.key") # pq_hybrid; the puzzle takes a second
async with await Pool.connect(key, [STATION], realm_trust={REALM: REALM_KEY}) as pool:
print(await pool.call(REALM, "mcl-echo/echo", "hello"))
asyncio.run(main())
A seed is pinned: the station must prove the node_id you give. A realm's key
decides which advertisements in it you trust; procedures in a node's own
namespace (~<node_id>/<name>, see Pool.own_procedure) need none.
The API
NodeKey |
generate, load, load_or_create, save, node_id, public_key, profile, sign, verify, free |
Pool.connect |
seeds, realm_trust, and the pool's tuning; async with closes it |
| calls | call, providers |
| serving | serve(realm, procedure, handler): handler(request) returns the result, directly or as an awaitable; an exception reaches the caller as a ProviderError of code handler_error |
| streams | open_stream, serve_stream; a Stream has send, send_value, close_send, reply, abort, close, recv, and iterates its frames |
| pub/sub | publish, subscribe; a Subscription iterates its events, or next(timeout_ms) |
| content | share_content, unshare_content, get_content |
| DHT | find_record, find_records, find_records_by_type, put_record |
Payloads are what macula's wire carries: str, int within int64, float,
None, bytes, lists and dicts with str keys. There is no boolean on the
wire: send 1 and 0. A Python bool is refused before it leaves, since
Python treats it as an int.
Every networked method is a coroutine, and each native call runs on a
thread of its own, so long waits never starve other calls. The methods that
take timeout_ms, and every wait for an event, a served call or a stream
frame, carry a cancel token: cancelling the task (or asyncio.wait_for
timing out) ends the native call at once. The rest (publish, subscribe,
serve, stop, close, and a stream's sends and ends) are short native
calls without one; cancelling stops the waiting and the call runs to its
end.
Errors are typed: ProviderError, RelayError, StreamError,
NotSharedError, ContentUnavailableError, MaculaTimeoutError (also a
TimeoutError), InvalidArgumentError (also a ValueError),
ClosedError, RefusedError, all MaculaError.
Development
Needs Go 1.27 and a C compiler to build macula-go's library locally.
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
eval "$(scripts/build_native.sh)" # the library and teststation, from abi/MACULA_GO_REF
.venv/bin/pytest
abi/macula.h is macula-go's header at the ref in abi/MACULA_GO_REF;
tests/test_abi_declarations.py holds the ctypes declarations to it
function for function, and build_native.sh refuses a header that differs
from the ref's.
scripts/live_check.sh runs tests/live/ against one fleet station
(helsinki and io.macula by default) with a key made for the run and never
saved. It publishes once and puts nothing in the DHT. CI never runs it.
A v* tag publishes to PyPI through Trusted Publishing, using only
macula-go's released libraries, each checked against the release's
SHA256SUMS and its build provenance attestation.
License
Apache-2.0. See LICENSE.
Built on macula-go, for Python -- sponsor the work if this saved you some time
Release files for macula-py 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| macula_py-0.2.0-py3-none-win_amd64.whl | Python 3 | none | Windows x86-64 | Details |
| macula_py-0.2.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl | Python 3 | none | Linux glibc 2.17+ ARM64, Linux glibc 2.28+ ARM64 | Details |
| macula_py-0.2.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl | Python 3 | none | Linux glibc 2.5+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| macula_py-0.2.0-py3-none-macosx_13_0_x86_64.whl | Python 3 | none | macOS 13.0+ x86-64 | Details |
| macula_py-0.2.0-py3-none-macosx_13_0_arm64.whl | Python 3 | none | macOS 13.0+ ARM64 | Details |
Total release size: 16.5 MB
Release files / macula_py-0.2.0-py3-none-win_amd64.whl
| Download URL | macula_py-0.2.0-py3-none-win_amd64.whl |
|---|---|
| Size | 3.4 MB |
| Tags | Python 3 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
9215b5f9785f1914735956a4206940da5ab270d47920a21f212befcbe737a020
|
|
BLAKE2b-256 checksum How to use checksums |
c78d5baa3b2d6044d897d6057b9e633c610853bf64d43403708e539845361209
|
| 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 Sep 26, 2026.
Transparency logRelease files / macula_py-0.2.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
| Download URL | macula_py-0.2.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl |
|---|---|
| Size | 3.1 MB |
| Tags | Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 Python 3 |
|
SHA-256 checksum How to use checksums |
46d6ebf68b7cbad3fad7830d1f351c2ae841b3d4491dd25f5505f41ca768a78f
|
|
BLAKE2b-256 checksum How to use checksums |
02f7304a9866432558ca0954840fc5809dfd89f224bb06682f5d820571944ed0
|
| 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 Sep 26, 2026.
Transparency logRelease files / macula_py-0.2.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | macula_py-0.2.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 3.5 MB |
| Tags | Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
fb69aaa69cbca52fe8dab8f1a0f3d23d783c7f71aca61c91ae3b0d858a99081e
|
|
BLAKE2b-256 checksum How to use checksums |
05563b0ad87a75be1c273c034dbb294e4cf9361f7e3a769755cf9f787ddd66a8
|
| 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 Sep 26, 2026.
Transparency logRelease files / macula_py-0.2.0-py3-none-macosx_13_0_x86_64.whl
| Download URL | macula_py-0.2.0-py3-none-macosx_13_0_x86_64.whl |
|---|---|
| Size | 3.4 MB |
| Tags | Python 3 macOS 13.0+ x86-64 |
|
SHA-256 checksum How to use checksums |
00906e9d24abd5aeea06a9c71bf526a4a1db98357af9c6bc393d7e9429ec7c4e
|
|
BLAKE2b-256 checksum How to use checksums |
e07ec5546867c7679ccde105ab936def6915bc6568771b8e5e2c9ed8ebeffab7
|
| 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 Sep 26, 2026.
Transparency logRelease files / macula_py-0.2.0-py3-none-macosx_13_0_arm64.whl
| Download URL | macula_py-0.2.0-py3-none-macosx_13_0_arm64.whl |
|---|---|
| Size | 3.1 MB |
| Tags | Python 3 macOS 13.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
d4517bd7db927dfe046d1fcba96859692d9035e07014b78db15fe3ffb423a212
|
|
BLAKE2b-256 checksum How to use checksums |
8cec49530a00d976373ba975ae32c795bc86db8342cd283089cd100d7fe91041
|
| 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 Sep 26, 2026.
Transparency log