Secure-FL in Rust
A type-safe port of the Secure-FL verifier and aggregation server. It speaks the same proof format and parameter representation as the Python package, so a Python client trained with PyTorch can submit to this server unchanged.
Scope: proof schema, commitments, norm enforcement, FedJSCM aggregation, and Groth16 verification. Training stays in Python — reimplementing PyTorch numerics in Rust would invalidate the existing benchmark results.
What changes vs. the Python server
| Python | Rust |
|---|---|
dict[str, Any] + hand-written schema check |
ClientProof struct, malformed payloads fail to deserialize |
verify_client_proof(...) -> bool, reason only in logs |
Result<(), Reject>, one typed variant per failure |
snarkjs subprocess, 300 s timeout, temp files |
ark-groth16 verification in-process |
json.loads → numpy, dtype inferred |
Tensor { dtype, shape, bytes }, dtype carried explicitly |
per-round if tree returning False |
total functions returning reasons the caller can act on |
Layout
rust/
├── securefl-core/ # no IO, no async
│ ├── tensor.rs # dtype-preserving tensor + numpy-compatible math
│ ├── commit.rs # state_hash (byte-identical to compute_hash), l2_norm
│ ├── proof.rs # typed client proof payload
│ ├── verify.rs # verification pipeline -> Reject
│ ├── fedjscm.rs # momentum aggregation + trimmed mean
│ └── groth16.rs # snarkjs vk/proof JSON -> arkworks verification
├── securefl-prover/ # native Groth16 proving from the existing .zkey
├── securefl-python/ # PyO3 extension (securefl_rust)
├── securefl-server/ # standalone axum round server
├── interop/ # fixture generator, HTTP smoke test, benchmark
└── fixtures/ # generated parity fixtures (checked in)
Wiring into the FL pipeline
There is no backend switch. secure_fl/backend.py's factories always return the
Rust implementations:
| Role | Class |
|---|---|
| Proof verification, generation | RustProofManager |
| FedJSCM aggregation | RustFedJscmAdapter (exported as FedJSCMAggregator) |
| Stability heuristic | StabilityMonitor (Rust) |
make build-rust # builds and installs src/securefl_rust.so
uv run secure-fl-server --config experiments/config.yaml
This covers the live Flower strategy (federation/server.py), the paper
benchmark pipeline (benchmarks/pipeline.py, benchmarks/execution.py) and the
attack benchmarks (benchmarks/attacks.py). Clients and training stay in
PyTorch, so results remain comparable.
The Rust core owns the whole proof path: client delta-bound proofs, server
aggregation proofs, both verifiers, aggregation, and the stability heuristic.
Circom/snarkjs remain build-time tools only — make setup-zkp produces the
circuit artifacts; nothing shells out at runtime.
The Python implementations of all four were removed once parity was proven.
Install as a wheel
make build-rust-wheel # rust/dist/securefl_core-<platform>.whl
uv pip install rust/dist/securefl_core-*.whl # or: pip install ./rust/securefl-python
The wheel installs a top-level securefl_rust extension module, so it shares no
files with the secure-fl wheel. It is built per platform and is not published
to PyPI yet; make build-rust remains the editable-install path.
.github/workflows/wheels.yml (manual dispatch) builds all five targets —
linux x86_64/aarch64, macOS arm64/x86_64, windows x64 — and uploads them as
workflow artifacts.
release.yml publishes securefl-core alongside secure-fl on every tag,
pinned to the same version. It needs a one-time trusted publisher on PyPI:
- Open https://pypi.org/manage/account/publishing/ and add a pending
publisher with:
- PyPI Project Name:
securefl-core - Owner:
krishantt - Repository name:
secure-fl - Workflow name:
release.yml - Environment name:
release
- PyPI Project Name:
- Nothing else. The GitHub environment
releasealready exists (it is used by thesecure-flpublish job).
Until that publisher exists, a release fails at publish-rust-core after
secure-fl has been published; re-run the Release workflow once it is set up.
The wheels.yml workflow still provides downloadable artifacts for testing.
Both packages publish through PyPI trusted publishing (OIDC), so PyPI attaches
a PEP 740 attestation to every file — see the "Provenance" tab of
secure-fl and
securefl-core.
GitHub build attestations (actions/attest-build-provenance) are off: they need
a public repo or a paid plan. Once the repo is public, add the step back to both
publish jobs in release.yml along with attestations: write.
Protocol
GET /v1/health -> {"status":"ok"}
PUT /v1/model {tensors} bootstrap or reset
GET /v1/model -> {round, tensors, accepted_clients}
POST /v1/rounds/{r}/updates {client_id, num_examples, tensors, proof}
POST /v1/rounds/{r}/aggregate -> {round, tensors, server_proof?}
With --circuit-dir set, server_proof carries the aggregation Groth16 proof
({type, version, proof, public}), generated in-process and verifiable against
the circuit's verification_key.json.
A tensor is {"dtype": "float32", "shape": [4,3], "data": "<base64 raw bytes>"}.
Accepting updates returns {"accepted": true}; rejecting returns 422 with a
typed reason in {"error": "..."}. verify order matches the Python server:
proof type/version → replay guards → full state-dict hash → delta hash → norm
bound → Groth16 delta-bound proof.
Run
cargo test # parity + crypto + unit tests
cargo run -p securefl-server -- \
--bind 127.0.0.1:8080 \
--vk ../src/secure_fl/proofs/client/build/delta_bound_vk.json \
--learnable <(echo '[0,1,3,4]') \
--max-norm 10 \
--circuit-dir ../src/secure_fl/proofs/server
--learnable takes a JSON array of state-dict indices that are learnable
parameters (BatchNorm buffers excluded), matching what the Python server gets
from its BatchNorm mask. Use --learnable all when the model has no buffers.
Interop tests
# Regenerate fixtures from the Python implementation, then assert parity
../.venv/bin/python interop/gen_fixtures.py
cargo test
# Real server binary, real HTTP, real Groth16 proof
cargo build -p securefl-server && ../.venv/bin/python interop/smoke_http.py
# Run the Python suite against the Rust backend
make build-rust
uv run pytest tests/unit tests/e2e --no-cov
Historical measurements (the Python implementations have since been removed):
docs/dev/RUST_END_TO_END_QUICK.md and
docs/dev/RUST_BACKEND_BENCHMARK.md.
gen_fixtures.py imports the Python compute_hash, compute_parameter_norm
and FedJSCMAggregator, and produces a real snarkjs Groth16 delta-bound proof
plus a tampered copy. The Rust tests assert byte-identical commitments, matching
aggregation output, native verification of the real proof, and rejection of both
tampered proof points and tampered public signals.
The prover tests re-prove the committed input.json and assert the public
outputs equal the snarkjs public.json, and that a proof built from unsatisfied
inputs does not verify. They need the circuit build directory (make setup-zkp)
and skip without it.
Deployment notes
- No authentication. The server binds
127.0.0.1by default; put it behind a reverse proxy with TLS and auth before exposing it. - Request bodies are capped at 256MB; a ResNet18 state dict is ~45MB.
- Aggregation holds the round lock while proving, so a round blocks other requests for its duration (~30ms on the 400-public circuit).
Not ported (deliberately)
- Training. No
burn/candle/tchrewrite; clients keep using PyTorch and Flower. Numerics would diverge and the paper benchmarks would not transfer. - Flower clientapi transport. The standalone server covers a Python-free deployment; reimplementing Flower's reverse-RPC would move no round cost (verify → aggregate → prove is already Rust).
- Textual TUI demo, CLI, dataset loading, benchmarks.
shortcut: aggregation crosses the PyO3 boundary by copying each tensor, so it
lands within ~5–10% of the numpy implementation it replaced. Remove the copies
with a borrowed-tensor API if that gap ever matters.
Metadata
Release files for securefl-core 26.10.147
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Actions Reset | |||
|---|---|---|---|---|
| securefl_core-26.10.147-cp312-abi3-win_amd64.whl | CPython 3.12 | abi3 | Windows x86-64 | Details for securefl_core-26.10.147-cp312-abi3-win_amd64.whl |
| securefl_core-26.10.147-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl | CPython 3.12 | abi3 | Linux glibc 2.17+ x86-64 | Details for securefl_core-26.10.147-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl |
| securefl_core-26.10.147-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl | CPython 3.12 | abi3 | Linux glibc 2.17+ ARM64 | Details for securefl_core-26.10.147-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl |
| securefl_core-26.10.147-cp312-abi3-macosx_11_0_arm64.whl | CPython 3.12 | abi3 | macOS 11.0+ ARM64 | Details for securefl_core-26.10.147-cp312-abi3-macosx_11_0_arm64.whl |
| securefl_core-26.10.147-cp312-abi3-macosx_10_12_x86_64.whl | CPython 3.12 | abi3 | macOS 10.12+ x86-64 | Details for securefl_core-26.10.147-cp312-abi3-macosx_10_12_x86_64.whl |
Total release size: 19.3 MB
Release files / securefl_core-26.10.147-cp312-abi3-win_amd64.whl
| Download URL | securefl_core-26.10.147-cp312-abi3-win_amd64.whl |
|---|---|
| Size | 3.4 MB |
| Tags | CPython 3.12 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
6d67e7f61e168092d5d6dbe3326f6be565e0607e5f0581eeec047840e8dec751
|
|
BLAKE2b-256 checksum How to use checksums |
203f38c2fad29577df22e330898fa6ed25b85a1b05e4f4c0ea6aa45d3ed88ba0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / securefl_core-26.10.147-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
| Download URL | securefl_core-26.10.147-cp312-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl |
|---|---|
| Size | 4.1 MB |
| Tags | CPython 3.12 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
9bd99dd3a6d00f9c7c079c7e54169e15b4cc20c4a8db9fd456a2033f248f4f78
|
|
BLAKE2b-256 checksum How to use checksums |
75f882eb714e2c9f360977511447f515a52aa8f216d750eab03cefacf8eef8a7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / securefl_core-26.10.147-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
| Download URL | securefl_core-26.10.147-cp312-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl |
|---|---|
| Size | 4.2 MB |
| Tags | CPython 3.12 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
e734e7fb78ba51f89bdd9d0eb447460e0ef5778e2960608404c17cea3dfc6450
|
|
BLAKE2b-256 checksum How to use checksums |
c4e1b75eba896feb23915d0fb7e59bcd59324a6f56ad27ad95511268812b4584
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / securefl_core-26.10.147-cp312-abi3-macosx_11_0_arm64.whl
| Download URL | securefl_core-26.10.147-cp312-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 3.7 MB |
| Tags | CPython 3.12 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
eed0abca1e4dc5178cb90196bd2e2d37599d0fc53f2378b8755366c25cf2c120
|
|
BLAKE2b-256 checksum How to use checksums |
bf148c188c44e0c00e2d91791a1cf6edad19310646fd93c78a93655be380d726
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / securefl_core-26.10.147-cp312-abi3-macosx_10_12_x86_64.whl
| Download URL | securefl_core-26.10.147-cp312-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 3.8 MB |
| Tags | CPython 3.12 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
8333c02a15e6032e02cf4a56369f8bec3cec8ec15321a2fd437d42345df04d2d
|
|
BLAKE2b-256 checksum How to use checksums |
264cb75d361e9594a8fd0eda8511b20d0a5eb6ebcacf038ac86a66f61cd16b7e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for securefl-core 26.10.147
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.