Skip to content

Repository files navigation

PrivacyMesh

Federated learning where the aggregator can compute on your gradients but can never see them — enforced by encryption, not policy.

Clients train locally on their own data and send only homomorphically-encrypted gradients. The aggregator performs the averaging math directly on ciphertext — it never has access to the secret key needed to decrypt anything, by design rather than by policy.

Dashboard (screenshot placeholder — add your dashboard screenshot here)

What this actually does

In standard federated learning, clients still send somewhat-sensitive gradient information to a central server in plaintext. PrivacyMesh closes that gap using CKKS homomorphic encryption (via TenSEAL): clients encrypt their gradients before sending them, the aggregator performs the sum/average math directly on ciphertexts, and only the clients — who hold the only copy of the secret key — can decrypt the final result.

The aggregator is not simply trusted not to peek. It is structurally incapable of decrypting anything, because it only ever holds a public-key context with the secret key stripped out. This repo includes a standalone proof of that guarantee (key_separation_poc.py) that deliberately tries to decrypt on the aggregator side and shows TenSEAL refusing.

Architecture

┌──────────────┐        encrypted gradient        ┌──────────────────┐
│   Client 1   │ ───────────────────────────────▶ │                  │
│ (local train,│                                    │    Aggregator    │
│  encrypt)    │◀─────────────────────────────────  │  (sums/averages  │
└──────────────┘     encrypted averaged update      │   ciphertexts,   │
                                                      │  never decrypts) │
┌──────────────┐        encrypted gradient          │                  │
│   Client 2   │ ───────────────────────────────▶  │                  │
└──────────────┘◀───────────────────────────────── └──────────────────┘
                     encrypted averaged update

                    ┌─────────────┐
   loss values ────▶ │  Dashboard  │  (loss only — never gradients/data)
   (plaintext,       │  (FastAPI + │
    non-sensitive)   │   live chart)│
                    └─────────────┘
  • Encryption: CKKS scheme via TenSEAL — supports real-valued arithmetic on ciphertexts (needed since gradients are floats, not integers)
  • Training: PyTorch, CPU-only
  • Networking: gRPC between clients and aggregator
  • Orchestration: Docker + docker-compose (each component is a separate container)
  • Monitoring: FastAPI dashboard, receives only loss values (never gradients or raw data) for live visualization

Proven guarantees

This project isn't just "it runs" — each of these was independently tested and verified before being combined:

  1. Homomorphic addition is correct — encrypted sum matches plaintext sum within floating-point tolerance (poc_homomorphic.py)
  2. The aggregator cannot decrypt, even if it tries — public/secret key separation is enforced by the library itself, not by convention (key_separation_poc.py)
  3. Real multi-layer PyTorch gradients survive the full encrypt → aggregate → decrypt round trip, correctly reconstructed and applied via an optimizer step (real_gradients_poc.py)
  4. Multi-round training genuinely converges — loss decreases monotonically across 8 rounds for every client tested
  5. Works across independently networked environments — verified running with the aggregator on native Windows (Docker Desktop) and clients inside WSL2's separate Linux environment, communicating over a real network boundary

Running it

Prerequisites

  • Docker Desktop
  • ~2GB free disk space (CPU-only PyTorch, much smaller than the GPU build)

Setup

  1. Generate the shared encryption keys (one-time):
cd shared
docker run --rm -v "${PWD}:/app" -w /app python:3.11-slim sh -c "pip install tenseal numpy && python keygen.py"

This produces secret_context.bin and public_context.bin.

  1. Distribute the keys:
cp secret_context.bin ../client/
cp public_context.bin ../aggregator/

Important: secret_context.bin must never end up in the aggregator/ folder — that would defeat the entire security model.

  1. Run everything:
cd ..
docker compose up --build
  1. Open the live dashboard:
http://localhost:8000

You'll see 8 rounds of federated training run automatically across two simulated clients, with a live-updating loss chart.

Project structure

privacymesh/
├── docker-compose.yml
├── proto/federated.proto        # gRPC message definitions
├── shared/keygen.py             # one-time shared key generation
├── aggregator/                  # holds ONLY the public key context
│   └── server.py                # gRPC server, barrier-synchronized multi-client aggregation
├── client/                      # holds the secret key
│   └── client.py                # local training loop, encryption, gRPC calls
├── dashboard/                   # FastAPI + Chart.js, receives loss values only
│   └── server.py
├── poc_homomorphic.py           # standalone proof: encrypted math is correct
├── key_separation_poc.py        # standalone proof: aggregator cannot decrypt
└── real_gradients_poc.py        # standalone proof: real PyTorch gradients work end-to-end

Design notes & honest limitations

  • Key distribution is simulated, not solved. All clients currently share one encryption context generated by a single trusted keygen.py run. Real-world federated learning with genuinely independent, mutually-untrusting clients would need a proper distributed key generation protocol (multi-party computation) — a legitimate direction for future work.
  • Barrier synchronization is simple. The aggregator waits for exactly NUM_CLIENTS submissions per round before aggregating. It doesn't yet handle stragglers, dropped clients, or asynchronous rounds — all real considerations in production FL systems.
  • Cloud deployment is architected but not yet live. The system is designed to deploy identically to any environment (swap AGGREGATOR_HOST for a public IP) — validated across Windows/WSL2 as a stand-in for genuinely separate machines. Deployment to a cloud provider (Oracle Cloud / Fly.io / GCP) is a natural next step once account verification is sorted out.

Why this exists

Built as a self-directed exploration into combining homomorphic encryption with practical machine learning — extending earlier work on privacy-preserving systems (EncryptedHealthLedger, MediProof) from statistics and credential verification into the ML domain.

About

Federated learning with homomorphic-encrypted gradient aggregation — the server can compute on encrypted gradients but can never decrypt them.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages