Metadata-Version: 2.4
Name: erldistpy
Version: 0.1.6
Summary: Native Python client for Erlang distribution protocol — EPMD + v6 handshake + gen_server call(), no asyncio.
Author-email: Russell Ballestrini <russell.ballestrini@gmail.com>
License: Unlicense
Project-URL: Homepage, https://git.unturf.com/python/erldistpy
Project-URL: Repository, https://git.unturf.com/python/erldistpy
Project-URL: Bug Tracker, https://git.unturf.com/python/erldistpy/-/issues
Keywords: erlang,elixir,distribution,dist,gen_server,rpc,etf,epmd,inet_tls_dist,interop
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: Public Domain
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Erlang
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Networking
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest<9,>=8.0; extra == "dev"
Requires-Dist: pytest-cov<7,>=5.0; extra == "dev"
Requires-Dist: ruff<1,>=0.6; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# erldistpy

Native Python client for our Erlang distribution protocol. Talk Erlang/Elixir
nodes from CPython without HTTP shim layers.

Built to swap into Python web apps as a drop-in for HTTP wallet-bridge
clients (see [unfeed](https://git.unturf.com/foxhop/unfeed)'s
`WalletTransport` and `make_post_sell`'s crypto watcher) so they can call
Elixir `gen_server` processes over native Erlang dist instead of JSON-RPC
or HTTPS. Same call semantics, lower latency, fewer moving parts.

## Install

```bash
pip install erldistpy
```

## Quick start

```python
from erldistpy import Node, Atom

with Node(
    our_name="myapp@host",
    peer_name="wallet",                # short EPMD name
    peer_host="wallet.example.com",
    cookie="SHARED_COOKIE",            # read from a file path, never inline
) as node:
    reply = node.call(
        "Elixir.Wallet.Service",
        (Atom("monero"), Atom("get_height"), []),
        timeout=5.0,
    )
    # reply is whatever the gen_server returned — atoms / binaries /
    # tuples / maps / lists / pids / refs decode to Python natives.
```

For TLS dist (Erlang `inet_tls_dist`):

```python
from erldistpy import Node, make_dist_tls_context

ctx = make_dist_tls_context(
    cert="/etc/myapp/client.pem",
    key="/etc/myapp/client.key",
    ca="/etc/myapp/ca.pem",
)
node = Node(our_name=..., peer_name=..., cookie=..., tls_context=ctx)
```

## Scope

- ETF (External Term Format) codec — encode/decode Erlang terms
- EPMD client — node name → port lookup
- v6 distribution handshake — MD5 cookie auth, version negotiation
- `gen_call` to registered processes on a remote node
- TLS dist support (Erlang `inet_tls_dist`)

Out of scope: full Erlang node impersonation, link/monitor lifecycles,
distributed Mnesia. We are a *client*, not a peer node.

## Why not Pyrlang?

Pyrlang implements a full asyncio Erlang node. Heavy, asyncio-first,
complex. erldistpy is a small synchronous client that fits behind the
same Protocol surface as `httpx`. Different shape, different audience.

## Development

```bash
make bootstrap   # create venv, install editable + dev deps
make test        # run pytest (122 tests)
make lint        # ruff check
make build       # build sdist + wheel into dist/
make dist-check  # twine check dist/*
```

See `docs/ROADMAP.md` for the phase-by-phase build log.

## License

Unlicense (public domain).
