Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions federated/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// Federated Learning Client
// Handles local training, secure communication with server

const net = require('net');
const crypto = require('./crypto');
const privacy = require('./privacy');
const model = require('./model');
const compliance = require('./compliance');
const config = require('./config');

class FederatedClient {
constructor(options) {
this.serverHost = options.serverHost || 'localhost';
this.serverPort = options.serverPort || 9000;
this.clientId = options.clientId || crypto.generateClientId();
this.localModel = model.createModel();
this.socket = null;
this.auditLog = [];
}

connect() {
this.socket = net.createConnection({ host: this.serverHost, port: this.serverPort }, () => {
compliance.log('Connected to server', { serverHost: this.serverHost, serverPort: this.serverPort });
crypto.authenticateServer(this.socket, this.clientId);
});
this.socket.on('data', (data) => this.handleServerMessage(data));
this.socket.on('end', () => compliance.log('Disconnected from server', {}));
}

handleServerMessage(data) {
let message;
try {
message = crypto.decryptMessage(data);
} catch (e) {
compliance.log('Decryption failed', { error: e.message });
return;
}
switch (message.type) {
case 'globalModel':
this.updateLocalModel(message.model);
break;
default:
compliance.log('Unknown message type', { type: message.type });
}
}

updateLocalModel(globalModel) {
this.localModel = model.update(this.localModel, globalModel);
compliance.log('Local model updated', {});
}

trainLocalModel(data) {
// Train model on local data
this.localModel = model.train(this.localModel, data);
compliance.log('Local model trained', {});
}

sendModelUpdate() {
// Apply privacy mechanisms
const privateModel = privacy.applyDifferentialPrivacy(this.localModel);
const encrypted = crypto.encryptMessage({
type: 'modelUpdate',
clientId: this.clientId,
model: privateModel
});
this.socket.write(encrypted);
compliance.log('Model update sent', {});
}
}

module.exports = FederatedClient;

// If run directly, start the client
if (require.main === module) {
const client = new FederatedClient({
serverHost: config.serverHost,
serverPort: config.serverPort,
clientId: config.clientId
});
client.connect();
// Example: train and send update
// client.trainLocalModel(localData);
// client.sendModelUpdate();
}
31 changes: 31 additions & 0 deletions federated/compliance.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Compliance Logging and Audit Trail
// Tracks data access, model updates, user actions

const fs = require('fs');
const path = require('path');
const LOG_FILE = path.join(__dirname, 'audit.log');

function log(action, details) {
const entry = {
timestamp: new Date().toISOString(),
action,
details
};
fs.appendFileSync(LOG_FILE, JSON.stringify(entry) + '\n');
}

function getLogs() {
if (!fs.existsSync(LOG_FILE)) return [];
const lines = fs.readFileSync(LOG_FILE, 'utf8').split('\n').filter(Boolean);
return lines.map(line => JSON.parse(line));
}

function queryLogs(filterFn) {
return getLogs().filter(filterFn);
}

module.exports = {
log,
getLogs,
queryLogs
};
11 changes: 11 additions & 0 deletions federated/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Configuration and Setup Scripts
// Server/client config, environment variables

const dotenv = require('dotenv');
dotenv.config();

module.exports = {
serverPort: process.env.SERVER_PORT || 9000,
serverHost: process.env.SERVER_HOST || 'localhost',
clientId: process.env.CLIENT_ID || null
};
71 changes: 71 additions & 0 deletions federated/crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Secure Communication Utilities
// Encryption, authentication, key management

const crypto = require('crypto');

const ALGORITHM = 'aes-256-gcm';
const KEY_LENGTH = 32;
const IV_LENGTH = 12;
const TAG_LENGTH = 16;

let serverKey = crypto.randomBytes(KEY_LENGTH);
let clientKeys = new Map(); // clientId -> key

function generateClientId() {
return crypto.randomBytes(8).toString('hex');
}

function generateKey() {
return crypto.randomBytes(KEY_LENGTH);
}

function encryptMessage(message, key = serverKey) {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
let encrypted = cipher.update(JSON.stringify(message), 'utf8');
encrypted = Buffer.concat([encrypted, cipher.final()]);
const tag = cipher.getAuthTag();
return Buffer.concat([iv, tag, encrypted]);
}

function decryptMessage(buffer, key = serverKey) {
const iv = buffer.slice(0, IV_LENGTH);
const tag = buffer.slice(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
const encrypted = buffer.slice(IV_LENGTH + TAG_LENGTH);
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
decipher.setAuthTag(tag);
let decrypted = decipher.update(encrypted);
decrypted = Buffer.concat([decrypted, decipher.final()]);
return JSON.parse(decrypted.toString('utf8'));
}

function authenticateClient(socket, callback) {
// Simple handshake: client sends ID, server stores key
socket.once('data', (data) => {
const clientId = data.toString('utf8');
const key = generateKey();
clientKeys.set(clientId, key);
socket.write(key);
callback(clientId);
});
}

function authenticateServer(socket, clientId) {
// Client sends ID, receives key
socket.write(clientId);
socket.once('data', (data) => {
// Store received key for future communication
clientKeys.set(clientId, data);
});
}

module.exports = {
generateClientId,
generateKey,
encryptMessage,
decryptMessage,
authenticateClient,
authenticateServer,
clientKeys,
serverKey
};
45 changes: 45 additions & 0 deletions federated/model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Model Definition and Training Logic
// Financial analytics model, local training, serialization

function createModel() {
// Simple linear regression model: weights and bias
return {
weight: Math.random(),
bias: Math.random()
};
}

function train(model, data) {
// Dummy training: update weights based on data
// data: [{x, y}, ...]
let lr = 0.01;
for (const point of data) {
let pred = model.weight * point.x + model.bias;
let error = point.y - pred;
model.weight += lr * error * point.x;
model.bias += lr * error;
}
return model;
}

function aggregate(globalModel, clientModel) {
// Simple averaging
return {
weight: (globalModel.weight + clientModel.weight) / 2,
bias: (globalModel.bias + clientModel.bias) / 2
};
}

function update(localModel, globalModel) {
// Update local model with global model
localModel.weight = globalModel.weight;
localModel.bias = globalModel.bias;
return localModel;
}

module.exports = {
createModel,
train,
aggregate,
update
};
40 changes: 40 additions & 0 deletions federated/privacy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Privacy-Preserving Mechanisms
// Differential privacy, secure aggregation

function applyDifferentialPrivacy(model) {
// Add Gaussian noise to model weights for privacy
const noiseLevel = 0.01;
let noisyModel = {};
for (const key in model) {
if (typeof model[key] === 'number') {
noisyModel[key] = model[key] + gaussianNoise(0, noiseLevel);
} else {
noisyModel[key] = model[key];
}
}
return noisyModel;
}

function gaussianNoise(mean, std) {
// Box-Muller transform
let u = 0, v = 0;
while(u === 0) u = Math.random();
while(v === 0) v = Math.random();
return std * Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v) + mean;
}

function secureAggregate(models) {
// Simple averaging for demonstration
if (models.length === 0) return {};
let agg = {};
let keys = Object.keys(models[0]);
for (const key of keys) {
agg[key] = models.reduce((sum, m) => sum + (m[key] || 0), 0) / models.length;
}
return agg;
}

module.exports = {
applyDifferentialPrivacy,
secureAggregate
};
104 changes: 104 additions & 0 deletions federated/server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// Federated Learning Server
// Orchestrates rounds, aggregates models, manages clients

const net = require('net');
const crypto = require('./crypto');
const privacy = require('./privacy');
const model = require('./model');
const compliance = require('./compliance');
const config = require('./config');

class FederatedServer {
constructor(options) {
this.port = options.port || 9000;
this.clients = new Map(); // clientId -> socket
this.globalModel = model.createModel();
this.round = 0;
this.auditLog = [];
this.server = net.createServer(this.handleConnection.bind(this));
}

start() {
this.server.listen(this.port, () => {
console.log(`Federated server listening on port ${this.port}`);
compliance.log('Server started', { port: this.port });
});
}

handleConnection(socket) {
socket.on('data', (data) => this.handleClientMessage(socket, data));
socket.on('end', () => this.handleClientDisconnect(socket));
// Authentication handshake
crypto.authenticateClient(socket, (clientId) => {
this.clients.set(clientId, socket);
compliance.log('Client connected', { clientId });
});
}

handleClientMessage(socket, data) {
// Decrypt and parse message
let message;
try {
message = crypto.decryptMessage(data);
} catch (e) {
compliance.log('Decryption failed', { error: e.message });
return;
}
// Handle message types: model update, request global model, etc.
switch (message.type) {
case 'modelUpdate':
this.handleModelUpdate(socket, message);
break;
case 'requestGlobalModel':
this.sendGlobalModel(socket);
break;
default:
compliance.log('Unknown message type', { type: message.type });
}
}

handleModelUpdate(socket, message) {
// Apply privacy-preserving aggregation
const clientModel = privacy.applyDifferentialPrivacy(message.model);
this.aggregateModel(clientModel);
compliance.log('Model update received', { clientId: message.clientId });
// Optionally send updated global model back
this.sendGlobalModel(socket);
}

aggregateModel(clientModel) {
// Aggregate client model into global model
this.globalModel = model.aggregate(this.globalModel, clientModel);
this.round++;
compliance.log('Model aggregated', { round: this.round });
}

sendGlobalModel(socket) {
const encrypted = crypto.encryptMessage({
type: 'globalModel',
model: this.globalModel,
round: this.round
});
socket.write(encrypted);
compliance.log('Global model sent', {});
}

handleClientDisconnect(socket) {
// Remove client from map
for (const [clientId, s] of this.clients.entries()) {
if (s === socket) {
this.clients.delete(clientId);
compliance.log('Client disconnected', { clientId });
break;
}
}
}
}

module.exports = FederatedServer;

// If run directly, start the server
if (require.main === module) {
const server = new FederatedServer({ port: config.serverPort });
server.start();
}