FL Client over Secure RPC¶
In this notebook, we will present how to launch a gRPC client as an FL client with an authenticator. To pair with the server notebook, we consider only one client.
[1]:
num_clients = 1
Load client configurations¶
We load the configuration for the client from examples/resources/configs/mnist/client_1.yaml
[2]:
from omegaconf import OmegaConf
client_config_file = "../../examples/resources/configs/mnist/client_1.yaml"
client_config = OmegaConf.load(client_config_file)
print(OmegaConf.to_yaml(client_config))
client_id: Client1
train_configs:
device: cpu
logging_output_dirname: ./output
logging_output_filename: result
data_configs:
dataset_path: ./resources/dataset/mnist_dataset.py
dataset_name: get_mnist
dataset_kwargs:
num_clients: 2
client_id: 0
partition_strategy: class_noniid
visualization: true
output_dirname: ./output
output_filename: visualization.pdf
comm_configs:
grpc_configs:
server_uri: localhost:50051
max_message_size: 1048576
use_ssl: false
💡 We need to change the relative path in data_configs.dataset_path to point to the right file relative to this notebook.
💡 We also need to change data_configs.dataset_kwargs.num_clients to 1 to make sure we only partition the MNIST dataset to one client split. We change data_configs.dataset_kwargs.visualization to False as well.
[3]:
client_config.data_configs.dataset_path = (
"../../examples/resources/dataset/mnist_dataset.py"
)
client_config.data_configs.dataset_kwargs.num_clients = num_clients
client_config.data_configs.dataset_kwargs.visualization = False
Create secure SSL channel and authenticator¶
The client requires the federation’s CA certificate (ca.crt) to verify the server’s certificate. Get it from the server operator after they have run appfl-setup-ssl. By convention the CA cert lives at ~/.appfl/ssl/ca.crt.
💡 Please check this tutorial for more details on generating SSL certificates for production deployments.
Then point the client config at the CA cert path:
[4]:
import os
ssl_dir = os.path.expanduser("~/.appfl/ssl")
client_config.comm_configs.grpc_configs.use_ssl = True
client_config.comm_configs.grpc_configs.root_certificate = f"{ssl_dir}/ca.crt"
We also need to set configurations to use the naive authenticator and provide the auth_token agreed with the server for authentication.
[5]:
client_config.comm_configs.grpc_configs.use_authenticator = True
client_config.comm_configs.grpc_configs.authenticator = "NaiveAuthenticator"
client_config.comm_configs.grpc_configs.authenticator_args = {
"auth_token": "A_SECRET_DEMO_TOKEN"
}
Create the client agent and communicator¶
Now we are ready to create the client agent using the client_agent defined and modified above, as well as a GRPCClientCommunicator to send request to the server.
⚠️ Please make sure that you have started the server from the other notebook!
[6]:
from appfl.agent import ClientAgent
from appfl.comm.grpc import GRPCClientCommunicator
client_agent = ClientAgent(client_agent_config=client_config)
client_communicator = GRPCClientCommunicator(
client_id=client_agent.get_id(),
**client_config.comm_configs.grpc_configs,
)
appfl: ✅[2026-05-31 21:49:19,715 Client1]: Logging to ./output/result_Client1_2026-05-31-21-49-19.txt
/Users/zilinghan/Documents/projects/appfl/appfl-release/src/appfl/misc/utils.py:126: UserWarning: NaiveAuthenticator is a shared-secret scheme suitable only for demos and trusted-network testing. Use GlobusAuthenticator (or another identity-bound authenticator) in production.
authenticator = AuthenticatorClass(**authenticator_args)
Start the FL experiment¶
Client start the FL experiment by doing the following things:
Obtain general client-side configurations from the server and load them
Obtain the initial global model from the server
[Optional] Send the number of local data to the server
Iteratively train the model and update the global model until receiving a
DONEstatus flag from the server.
💡 The server is also logging several information regarding the recipe of client requests.
[7]:
# Obtain general client-side configurations from the server and load them
client_config = client_communicator.get_configuration()
client_agent.load_config(client_config)
# Obtain the initial global model from the server
init_global_model = client_communicator.get_global_model(init_model=True)
client_agent.load_parameters(init_global_model)
# Send the number of local data to the server
sample_size = client_agent.get_sample_size()
client_communicator.invoke_custom_action(
action="set_sample_size", sample_size=sample_size
)
while True:
client_agent.train()
local_model = client_agent.get_parameters()
if isinstance(local_model, tuple):
local_model, meta_data_local = local_model[0], local_model[1]
else:
meta_data_local = {}
new_global_model, metadata = client_communicator.update_global_model(
local_model, **meta_data_local
)
if metadata["status"] == "DONE":
break
client_agent.load_parameters(new_global_model)
client_communicator.invoke_custom_action(action="close_connection")
appfl: ✅[2026-05-31 21:49:27,337 Client1]: Round Pre Val? Time Train Loss Train Accuracy Val Loss Val Accuracy
appfl: ✅[2026-05-31 21:49:28,336 Client1]: 0 Y 2.3006 15.9300
appfl: ✅[2026-05-31 21:49:31,240 Client1]: 0 N 2.9033 0.0662 81.5625 0.1981 94.2200
appfl: ✅[2026-05-31 21:49:34,932 Client1]: 1 Y 0.1981 94.2200
appfl: ✅[2026-05-31 21:49:37,824 Client1]: 1 N 2.8909 0.0178 94.8438 0.1068 96.7000
appfl: ✅[2026-05-31 21:49:41,419 Client1]: 2 Y 0.1068 96.7000
appfl: ✅[2026-05-31 21:49:44,365 Client1]: 2 N 2.9457 0.0129 96.4219 0.0915 97.2900
appfl: ✅[2026-05-31 21:49:47,988 Client1]: 3 Y 0.0915 97.2900
appfl: ✅[2026-05-31 21:49:50,872 Client1]: 3 N 2.8832 0.0092 97.5000 0.0579 98.2300
appfl: ✅[2026-05-31 21:49:54,440 Client1]: 4 Y 0.0579 98.2300
appfl: ✅[2026-05-31 21:49:57,335 Client1]: 4 N 2.8942 0.0083 97.6719 0.0527 98.4100
appfl: ✅[2026-05-31 21:50:00,934 Client1]: 5 Y 0.0527 98.4100
appfl: ✅[2026-05-31 21:50:03,866 Client1]: 5 N 2.9315 0.0069 98.0781 0.0468 98.4900
appfl: ✅[2026-05-31 21:50:07,496 Client1]: 6 Y 0.0468 98.4900
appfl: ✅[2026-05-31 21:50:10,396 Client1]: 6 N 2.8989 0.0061 98.2812 0.0452 98.5600
appfl: ✅[2026-05-31 21:50:13,983 Client1]: 7 Y 0.0452 98.5600
appfl: ✅[2026-05-31 21:50:16,895 Client1]: 7 N 2.9114 0.0054 98.4219 0.0395 98.7300
appfl: ✅[2026-05-31 21:50:20,492 Client1]: 8 Y 0.0395 98.7300
appfl: ✅[2026-05-31 21:50:23,389 Client1]: 8 N 2.8971 0.0048 98.7031 0.0418 98.6500
appfl: ✅[2026-05-31 21:50:27,019 Client1]: 9 Y 0.0418 98.6500
appfl: ✅[2026-05-31 21:50:29,948 Client1]: 9 N 2.9285 0.0051 98.4531 0.0395 98.5600
[7]:
{}