Federated Learning
The Federated Learning API lets multiple Adjudon organisations
collaborate on shared pattern detection (fraud signatures, AI
incident archetypes, content-moderation drift) without sharing
the underlying decision data. Each org's local trainer pushes a
differentially-private model update; the aggregation service
combines updates with calibrated noise; the resulting global model
is published back to participants. Tested against API version v1.
Mounted at /api/v1/federated. JWT auth; plan-gated by
federatedLearning — Custom plans only.
Federated Learning is on the published roadmap as Phase 3 (Q3-Q4 2027). The endpoints below are wired today on the Custom tier; the cross-org coordination, DP-noise calibration, and participant onboarding go through Adjudon Solutions Engineering as part of the Custom-plan negotiation. The schema ships AS BUILT; the production rollout drains in waves through participating consortia.
How it works
┌───────────────────────────────────────────── ─────────────┐
│ 1. Coordinator creates a PatternFamily (consortium) │
│ ──> sets domain ('financial-fraud', etc.) │
│ ──> sets DP defaults (epsilon, delta) │
├──────────────────────────────────────────────────────────┤
│ 2. Each member org POSTs /families/:id/join │
├──────────────────────────────────────────────────────────┤
│ 3. Coordinator POSTs /families/:id/rounds (kickoff) │
│ ──> Round status: pending → collecting │
├──────────────────────────────────────────────────────────┤
│ 4. Each participant POSTs /rounds/:id/updates │
│ with a DP-clipped local-model vector │
├──────────────────────────────────────────────────────────┤
│ 5. Coordinator POSTs /rounds/:id/aggregate │
│ ──> Status: aggregating → completed │
│ ──> globalModelRef + aggregateHash published │
├──────────────────────────────────────────────────────────┤
│ 6. Coordinator POSTs /rounds/:id/evidence │
│ ──> Sigstore-signed attestation bundle │
└──────────────────────────────────────────────────────────┘
Two MongoDB collections back the API: PatternFamily (the
consortium) and FederatedRound (one row per coordination round).
The Adjudon coordinator process is the trusted aggregator; the
roadmap includes a Trusted-Execution-Environment (TEE) attestation
path so the aggregation can run in a hardware-attested enclave
that even the coordinator cannot inspect (gated by
teeAttestation, also Custom-only).
Endpoints
GET /api/v1/federated/families
GET /api/v1/federated/families/:id
POST /api/v1/federated/families/:id/join
POST /api/v1/federated/families/:id/leave
GET /api/v1/federated/families/:id/rounds
POST /api/v1/federated/families/:id/rounds ── kickoff
GET /api/v1/federated/rounds/:roundId
POST /api/v1/federated/rounds/:roundId/updates ── client update
POST /api/v1/federated/rounds/:roundId/aggregate
POST /api/v1/federated/rounds/:roundId/evidence
The PatternFamily object
| Field | Type | Description |
|---|---|---|
name | string | Consortium name (e.g. eu-fraud-2027-q1) |
domain | string | Free-text classification (financial-fraud, medical-triage, …) |
members[] | array | Each entry { organizationId, joinedAt, status: 'active'/'paused'/'left' } |
coordinatorOrgId | string | null | Adjudon-internal by default; can be a member-org for self-coordinated families |
currentRound | number | Round counter; advances on aggregate completion |
defaultEpsilon | number | DP epsilon default for rounds (1.0) |
defaultDelta | number | DP delta default (1e-5) |
status | enum | active, paused, archived |
The FederatedRound object
| Field | Type | Description |
|---|---|---|
familyId | string | Owning consortium |
roundNumber | number | Unique within familyId; compound index |
participantOrgIds[] | string[] | Orgs that submitted a client update |
minParticipants | number | Default 3; aggregation refuses to run below this |
status | enum | pending, collecting, aggregating, completed, failed |
epsilon, delta | number | DP parameters for the round |
noiseMechanism | enum | laplace, gaussian (default gaussian) |
expectedLength | number | null | Required client-update vector dimension |
clipNorm | number | L2 sensitivity bound; default 1.0 |
globalModelRef | string | null | S3-style reference or content hash post-aggregation |
aggregateVector | number[] | Computed aggregate; omitted until completion |
aggregateHash | string | SHA-256 of canonicalised aggregate |
participantCount | number | Successful contributions |
rejectedCount | number | Rejected contributions (clip-norm violation, malformed payload) |
rejectionReasons | object | orgId → reason; surfaces why each participant was excluded |
contributionWeights | object | orgId → weight; transparency on aggregation weighting |
attestationRef | string | null | TEE attestation hash (when teeAttestation is on) |
sigstoreBundleRef | string | null | Sigstore-signed evidence bundle |
chainHash | string | null | Hash-chain anchor on round completion |
Submit a client update
POST /api/v1/federated/rounds/:roundId/updates
Body: { vector: number[], orgId? }. The vector must match
expectedLength and respect the round's clipNorm (L2
sensitivity bound). Updates that violate the bound are rejected
synchronously with the violation reason landing in
FederatedRound.rejectionReasons[orgId]. The reason vocabulary
is curated — never raw error text, per Cardinal Rule #4
on the federated path.
curl -X POST https://api.adjudon.com/api/v1/federated/rounds/65b1f2c4/updates \
-H "Authorization: Bearer $ADJUDON_JWT" \
-H "Content-Type: application/json" \
-d '{ "vector": [0.124, -0.087, ...], "orgId": "65a8b2c1..." }'
The endpoint never accepts raw training data — only the DP-clipped model-update vector. Adjudon does not see the participant's underlying decisions; that is the entire point of the federated architecture.
Run aggregation
POST /api/v1/federated/rounds/:roundId/aggregate
Coordinator-only. Combines submitted updates with the configured
noise mechanism, computes the L2-clipped weighted average,
serialises the result, hashes it, and writes
globalModelRef + aggregateVector + aggregateHash. The round
status transitions aggregating → completed (or failed if
fewer than minParticipants valid updates were received).
The handler also computes per-participant contributionWeights
and persists them — an auditor or a participant org can
later read who contributed how much without trusting the
coordinator's claim.
Sign aggregation evidence
POST /api/v1/federated/rounds/:roundId/evidence
Generates a Sigstore-signed attestation bundle anchoring the
round's inputs, the noise parameters, the participant set, and
the resulting aggregateHash. The sigstoreBundleRef is
written back to the round so participants can fetch and verify
the bundle independently. Plan-gated by sigstoreEvidence
(Enterprise+).
Differential privacy parameters explained
The four DP-related fields on FederatedRound are the
mathematical contract every participant inherits when they
submit a client update. Understanding them is non-optional for
operators in regulated sectors:
| Parameter | What it bounds | Default |
|---|---|---|
epsilon | Privacy budget per round; lower means stronger privacy, more noise | 1.0 (moderate) |
delta | Probability the privacy guarantee leaks; must be << 1/n for n participants | 1e-5 |
noiseMechanism | gaussian (default; required for ε-δ DP); laplace (pure ε DP, larger noise) | gaussian |
clipNorm | L2 sensitivity bound; client updates exceeding this are scaled down to fit | 1.0 |
The defaults are intentionally conservative. A consortium that
needs tighter privacy reduces epsilon to 0.5 or below at the
cost of noisier global models; a consortium needing higher
utility raises it with explicit member sign-off recorded in the
PatternFamily.members[] consent fields. The coordinator
cannot raise epsilon mid-round — the schema validates
the round's parameters at kickoff and rejects mutations against
the field thereafter.
The privacy-budget accounting itself lives on the Privacy Budget API; each participant tracks cumulative epsilon spent across all rounds they joined.
Architecture — split between Adjudon and the local trainer
The published Phase 3 architecture is deliberately split. Adjudon
operates the coordinator (round kickoff, aggregation,
evidence signing); the participant org operates the local
trainer (loads the org's local trace data, computes a
gradient or pattern statistic, applies clip-and-noise locally,
emits the DP-clipped vector). The split exists because the local
trainer needs read access to raw decisions that must never leave
the org's tenancy. Adjudon ships a reference local-trainer in
the Python SDK under the adjudon-federated
adapter package; customers operating in their own infrastructure
implement the local-trainer side once against the Adjudon
documented round-protocol.
The reference adapter is built against Flower Labs open-source primitives where they fit; the protocol layer between local trainer and coordinator is an Adjudon contract, not Flower's, so customers running their own training stack do not inherit Flower's coupling.
How an auditor reads the federated record
A regulator auditing a federated-learning consortium typically asks for three artefacts per round:
- The aggregation receipt —
GET /rounds/:roundIdreturns participants, parameters, hashes, and Sigstore evidence reference. The receipt is publicly readable inside the consortium. - The privacy-budget tally — per-participant epsilon spend over time, surfaced via the Privacy Budget API. The regulator confirms no participant exceeded their declared budget across the audit window.
- The Sigstore bundle — verifiable proof that the aggregation parameters published to participants match what actually ran. The bundle is the evidence-of-record; without it, the receipt is the coordinator's claim, not a verifiable artefact.
The combination is what closes the regulator's loop on "show me that this consortium runs DP and that the math actually applied."
Common gotchas
- Custom-plan only. This is the most heavily plan-gated
resource on the platform. Sandbox / Scale / Governance /
Enterprise all return
403 UPGRADE_REQUIRED. Custom-plan contracts negotiate participation in specific consortia. - No raw data on the wire. The endpoints accept DP-clipped model updates only. Submitting raw training data or decision payloads is a category error the schema does not represent.
minParticipantsis a floor, not a target. The default3is the minimum for differential-privacy guarantees to hold meaningfully; production consortia run with 10-20 participants.- Curated rejection reasons. Per Cardinal Rule #4, the
reason vocabulary is bounded (
clip-norm-violation,dimension-mismatch,late-submission,malformed-payload). Raw error messages never flow back to participants. - Idempotency.
Idempotency-Keymiddleware is wired only onPOST /traces; client-update submission is bounded by per-round(orgId)uniqueness server-side, so a double-submit with the same vector is no-op idempotent. Aggregation cannot run twice on the same round.
See also
- Privacy Budget API — the DP-budget tracking surface for federated participants
- Provenance API — the C2PA-anchored provenance surface that signs aggregation evidence
- Plans & Features —
the
federatedLearning,teeAttestation, andsigstoreEvidencefeature gates - Hash Chain — the tamper-evident anchor for round completion
- Error Codes — the broader error taxonomy