Metadata-Version: 2.4
Name: fedmesh
Version: 0.1.0
Summary: Federated joint-training mesh built on iroh-tinker
License: Apache-2.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
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.14
Classifier: Programming Language :: Python :: 3.15
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: iroh-tinker>=0.3.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Requires-Dist: types-requests; extra == "dev"
Dynamic: license-file

# fedmesh

Privacy-preserving federated training across organisations, built on
[iroh-tinker](https://github.com/scairesearch/iroh_tinker).

fedmesh coordinates a training run that spans several organisations. Each
party keeps its data local; the parties exchange masked model updates over
iroh-tinker's transports and every party ends up with the same aggregate.

Two properties make it different from a plain multi-node job:

* **No raw gradient leaves a party.** Updates are masked with pairwise
  secrets before they cross the wire, and the masks cancel in the aggregate.
* **No single party holds a global secret.** Every pair of parties derives
  its own secret, so compromising the coordinator does not open the other
  pairs' channels.

The full threat model, including what is **not** guaranteed, is in
[`docs/threat-model.md`](docs/threat-model.md). Read it before making
promises about confidentiality.

## Install

```bash
pip install fedmesh
pip install -e ".[dev]"           # from a checkout
```

fedmesh depends on the public `iroh-tinker` surface only.

## Built on the iroh-tinker public surface

| API | Used for |
|---|---|
| `iroh_tinker.comm.transport.Transport` | the transport interface `MaskedTransport` implements |
| `iroh_tinker.comm.transport.LocalTransport` / `LocalNetwork` | in-process mesh for tests and simulations |
| `iroh_tinker.comm.transport.CompressedTransport` | the bandwidth path (never on a masked path) |
| `iroh_tinker.cloud.job.JobManager` / `LaunchSpec` | launching the federated run |

## CLI

```bash
fedmesh init --party-id alpha --dir ./parties/alpha --ranks 2 --rank-offset 0
fedmesh manifest --dir ./parties/alpha --json
fedmesh mesh ./parties/*/manifest.json --json
fedmesh shards --parties alpha,beta,gamma --rows 100000 --json
fedmesh dp --epsilon 8 --delta 1e-5 --noise 10 --json
fedmesh spike --json
```

`fedmesh spike` runs the P2.1 spike and prints the numbers:

```
exact fp32 wire : relative error 8.310e-08
8-bit wire      : relative error 8.569e-03
lossy is 103066x worse -> the masked path must be lossless
```

## How the aggregate works

Each pair of parties derives a shared secret from a Diffie-Hellman exchange
(RFC 3526 group 14) and HKDF-SHA256. From each pairwise secret both sides
derive the same pseudorandom vector `v_pq`. Party `p`'s mask is

```
M_p = sum over q != p of sign(p, q) * v_pq      (sign is +1 when p < q)
```

so `sum_p M_p = 0` and the aggregate of the masked contributions is the
plain sum of the parties' contributions.

## What is and is not guaranteed

**Guaranteed**

* The aggregate equals the sum of the parties' contributions (to float32
  rounding).
* A single party cannot isolate another party's contribution from the
  traffic it sees.
* No raw gradient appears in any wire payload.
* No party holds a global secret; compromising one party exposes only that
  party's channels.
* Rendezvous tickets are authenticated per party pair.

**Not guaranteed**

* **Colluding parties.** Two parties can reconstruct the third party's mask
  and recover its update. v1 protects against one honest-but-curious party,
  not against a coalition. Closing this needs Bonawitz share-splitting and is
  tracked in the product-suite plan.
* **Lossy compression on a masked path.** It destroys the cancellation, so
  the aggregate is wrong. `MaskedTransport` refuses to wrap a lossy
  transport unless the caller passes `allow_lossy=True`, which downgrades the
  guarantee to "no confidentiality beyond transport encryption".
* **Metadata.** Party membership, rank counts and payload sizes are visible.
* **Intra-party traffic.** Ranks of one party talk in the clear, by design.

## Tests

```bash
pip install -e ".[dev]"
pytest
```

The suite is credential-free and runs entirely in-process over
`LocalNetwork`. The compression spike lives in
`tests/test_spike_compression.py` and can be run on its own:

```bash
pytest tests/test_spike_compression.py -v
```

## Layout

```
src/fedmesh/
  trust.py      Diffie-Hellman key pairs, pairwise secrets, ticket signing
  party.py      one organisation: key pair, ranks, rendezvous namespace
  mesh.py       manifests, rank layout, deterministic coordinator election
  masking.py    the pairwise mask, MaskedTransport, the inter-party exchange
  dp.py         clipping, Gaussian noise, budget accounting
  sampler.py    deterministic shard policy and the read audit
  testing.py    RecordingTransport, used by the wire-leakage tests
  cli.py        fedmesh init | manifest | mesh | shards | dp | spike
docs/threat-model.md
```

## Licence

Apache-2.0. fedmesh is a separate project from iroh-tinker, which stays
MIT OR Apache-2.0.
