flwr 1.21.0__py3-none-any.whl → 1.23.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. flwr/cli/app.py +17 -1
  2. flwr/cli/auth_plugin/__init__.py +15 -6
  3. flwr/cli/auth_plugin/auth_plugin.py +95 -0
  4. flwr/cli/auth_plugin/noop_auth_plugin.py +58 -0
  5. flwr/cli/auth_plugin/oidc_cli_plugin.py +16 -25
  6. flwr/cli/build.py +118 -47
  7. flwr/cli/{cli_user_auth_interceptor.py → cli_account_auth_interceptor.py} +6 -5
  8. flwr/cli/log.py +2 -2
  9. flwr/cli/login/login.py +34 -23
  10. flwr/cli/ls.py +13 -9
  11. flwr/cli/new/new.py +196 -42
  12. flwr/cli/new/templates/app/README.flowertune.md.tpl +1 -1
  13. flwr/cli/new/templates/app/code/client.baseline.py.tpl +64 -47
  14. flwr/cli/new/templates/app/code/client.huggingface.py.tpl +68 -30
  15. flwr/cli/new/templates/app/code/client.jax.py.tpl +63 -42
  16. flwr/cli/new/templates/app/code/client.mlx.py.tpl +80 -51
  17. flwr/cli/new/templates/app/code/client.numpy.py.tpl +36 -13
  18. flwr/cli/new/templates/app/code/client.pytorch.py.tpl +71 -46
  19. flwr/cli/new/templates/app/code/client.pytorch_legacy_api.py.tpl +55 -0
  20. flwr/cli/new/templates/app/code/client.sklearn.py.tpl +75 -30
  21. flwr/cli/new/templates/app/code/client.tensorflow.py.tpl +69 -44
  22. flwr/cli/new/templates/app/code/client.xgboost.py.tpl +110 -0
  23. flwr/cli/new/templates/app/code/flwr_tune/client_app.py.tpl +56 -90
  24. flwr/cli/new/templates/app/code/flwr_tune/models.py.tpl +1 -23
  25. flwr/cli/new/templates/app/code/flwr_tune/server_app.py.tpl +37 -58
  26. flwr/cli/new/templates/app/code/flwr_tune/strategy.py.tpl +39 -44
  27. flwr/cli/new/templates/app/code/model.baseline.py.tpl +0 -14
  28. flwr/cli/new/templates/app/code/server.baseline.py.tpl +27 -29
  29. flwr/cli/new/templates/app/code/server.huggingface.py.tpl +23 -19
  30. flwr/cli/new/templates/app/code/server.jax.py.tpl +27 -14
  31. flwr/cli/new/templates/app/code/server.mlx.py.tpl +29 -19
  32. flwr/cli/new/templates/app/code/server.numpy.py.tpl +30 -17
  33. flwr/cli/new/templates/app/code/server.pytorch.py.tpl +36 -26
  34. flwr/cli/new/templates/app/code/server.pytorch_legacy_api.py.tpl +31 -0
  35. flwr/cli/new/templates/app/code/server.sklearn.py.tpl +29 -21
  36. flwr/cli/new/templates/app/code/server.tensorflow.py.tpl +28 -19
  37. flwr/cli/new/templates/app/code/server.xgboost.py.tpl +56 -0
  38. flwr/cli/new/templates/app/code/task.huggingface.py.tpl +16 -20
  39. flwr/cli/new/templates/app/code/task.jax.py.tpl +1 -1
  40. flwr/cli/new/templates/app/code/task.numpy.py.tpl +1 -1
  41. flwr/cli/new/templates/app/code/task.pytorch.py.tpl +14 -27
  42. flwr/cli/new/templates/app/code/{task.pytorch_msg_api.py.tpl → task.pytorch_legacy_api.py.tpl} +27 -14
  43. flwr/cli/new/templates/app/code/task.tensorflow.py.tpl +1 -2
  44. flwr/cli/new/templates/app/code/task.xgboost.py.tpl +67 -0
  45. flwr/cli/new/templates/app/pyproject.baseline.toml.tpl +4 -4
  46. flwr/cli/new/templates/app/pyproject.flowertune.toml.tpl +2 -2
  47. flwr/cli/new/templates/app/pyproject.huggingface.toml.tpl +4 -4
  48. flwr/cli/new/templates/app/pyproject.jax.toml.tpl +1 -1
  49. flwr/cli/new/templates/app/pyproject.mlx.toml.tpl +2 -2
  50. flwr/cli/new/templates/app/pyproject.numpy.toml.tpl +1 -1
  51. flwr/cli/new/templates/app/pyproject.pytorch.toml.tpl +3 -3
  52. flwr/cli/new/templates/app/{pyproject.pytorch_msg_api.toml.tpl → pyproject.pytorch_legacy_api.toml.tpl} +3 -3
  53. flwr/cli/new/templates/app/pyproject.sklearn.toml.tpl +1 -1
  54. flwr/cli/new/templates/app/pyproject.tensorflow.toml.tpl +1 -1
  55. flwr/cli/new/templates/app/pyproject.xgboost.toml.tpl +61 -0
  56. flwr/cli/pull.py +100 -0
  57. flwr/cli/run/run.py +11 -7
  58. flwr/cli/stop.py +2 -2
  59. flwr/cli/supernode/__init__.py +25 -0
  60. flwr/cli/supernode/ls.py +260 -0
  61. flwr/cli/supernode/register.py +185 -0
  62. flwr/cli/supernode/unregister.py +138 -0
  63. flwr/cli/utils.py +109 -69
  64. flwr/client/__init__.py +2 -1
  65. flwr/client/grpc_adapter_client/connection.py +6 -8
  66. flwr/client/grpc_rere_client/connection.py +59 -31
  67. flwr/client/grpc_rere_client/grpc_adapter.py +28 -12
  68. flwr/client/grpc_rere_client/{client_interceptor.py → node_auth_client_interceptor.py} +3 -6
  69. flwr/client/mod/secure_aggregation/secaggplus_mod.py +7 -5
  70. flwr/client/rest_client/connection.py +82 -37
  71. flwr/clientapp/__init__.py +1 -2
  72. flwr/clientapp/mod/__init__.py +4 -1
  73. flwr/clientapp/mod/centraldp_mods.py +156 -40
  74. flwr/clientapp/mod/localdp_mod.py +169 -0
  75. flwr/clientapp/typing.py +22 -0
  76. flwr/{client/clientapp → clientapp}/utils.py +1 -1
  77. flwr/common/constant.py +56 -13
  78. flwr/common/exit/exit_code.py +24 -10
  79. flwr/common/inflatable_utils.py +10 -10
  80. flwr/common/record/array.py +3 -3
  81. flwr/common/record/arrayrecord.py +10 -1
  82. flwr/common/record/typeddict.py +12 -0
  83. flwr/common/secure_aggregation/crypto/symmetric_encryption.py +1 -89
  84. flwr/common/serde.py +4 -2
  85. flwr/common/typing.py +7 -6
  86. flwr/compat/client/app.py +1 -1
  87. flwr/compat/client/grpc_client/connection.py +2 -2
  88. flwr/proto/control_pb2.py +48 -31
  89. flwr/proto/control_pb2.pyi +95 -5
  90. flwr/proto/control_pb2_grpc.py +136 -0
  91. flwr/proto/control_pb2_grpc.pyi +52 -0
  92. flwr/proto/fab_pb2.py +11 -7
  93. flwr/proto/fab_pb2.pyi +21 -1
  94. flwr/proto/fleet_pb2.py +31 -23
  95. flwr/proto/fleet_pb2.pyi +63 -23
  96. flwr/proto/fleet_pb2_grpc.py +98 -28
  97. flwr/proto/fleet_pb2_grpc.pyi +45 -13
  98. flwr/proto/node_pb2.py +3 -1
  99. flwr/proto/node_pb2.pyi +48 -0
  100. flwr/server/app.py +152 -114
  101. flwr/server/superlink/fleet/grpc_adapter/grpc_adapter_servicer.py +17 -7
  102. flwr/server/superlink/fleet/grpc_rere/fleet_servicer.py +132 -38
  103. flwr/server/superlink/fleet/grpc_rere/{server_interceptor.py → node_auth_server_interceptor.py} +27 -51
  104. flwr/server/superlink/fleet/message_handler/message_handler.py +67 -22
  105. flwr/server/superlink/fleet/rest_rere/rest_api.py +52 -31
  106. flwr/server/superlink/fleet/vce/backend/backend.py +1 -1
  107. flwr/server/superlink/fleet/vce/backend/raybackend.py +1 -1
  108. flwr/server/superlink/fleet/vce/vce_api.py +18 -5
  109. flwr/server/superlink/linkstate/in_memory_linkstate.py +167 -73
  110. flwr/server/superlink/linkstate/linkstate.py +107 -24
  111. flwr/server/superlink/linkstate/linkstate_factory.py +2 -1
  112. flwr/server/superlink/linkstate/sqlite_linkstate.py +306 -255
  113. flwr/server/superlink/linkstate/utils.py +3 -54
  114. flwr/server/superlink/serverappio/serverappio_servicer.py +2 -2
  115. flwr/server/superlink/simulation/simulationio_servicer.py +1 -1
  116. flwr/server/utils/validator.py +2 -3
  117. flwr/server/workflow/secure_aggregation/secaggplus_workflow.py +4 -2
  118. flwr/serverapp/strategy/__init__.py +26 -0
  119. flwr/serverapp/strategy/bulyan.py +238 -0
  120. flwr/serverapp/strategy/dp_adaptive_clipping.py +335 -0
  121. flwr/serverapp/strategy/dp_fixed_clipping.py +71 -49
  122. flwr/serverapp/strategy/fedadagrad.py +0 -3
  123. flwr/serverapp/strategy/fedadam.py +0 -3
  124. flwr/serverapp/strategy/fedavg.py +89 -64
  125. flwr/serverapp/strategy/fedavgm.py +198 -0
  126. flwr/serverapp/strategy/fedmedian.py +105 -0
  127. flwr/serverapp/strategy/fedprox.py +174 -0
  128. flwr/serverapp/strategy/fedtrimmedavg.py +176 -0
  129. flwr/serverapp/strategy/fedxgb_bagging.py +117 -0
  130. flwr/serverapp/strategy/fedxgb_cyclic.py +220 -0
  131. flwr/serverapp/strategy/fedyogi.py +0 -3
  132. flwr/serverapp/strategy/krum.py +112 -0
  133. flwr/serverapp/strategy/multikrum.py +247 -0
  134. flwr/serverapp/strategy/qfedavg.py +252 -0
  135. flwr/serverapp/strategy/strategy_utils.py +48 -0
  136. flwr/simulation/app.py +1 -1
  137. flwr/simulation/ray_transport/ray_actor.py +1 -1
  138. flwr/simulation/ray_transport/ray_client_proxy.py +1 -1
  139. flwr/simulation/run_simulation.py +28 -32
  140. flwr/supercore/cli/flower_superexec.py +26 -1
  141. flwr/supercore/constant.py +41 -0
  142. flwr/supercore/object_store/in_memory_object_store.py +0 -4
  143. flwr/supercore/object_store/object_store_factory.py +26 -6
  144. flwr/supercore/object_store/sqlite_object_store.py +252 -0
  145. flwr/{client/clientapp → supercore/primitives}/__init__.py +1 -1
  146. flwr/supercore/primitives/asymmetric.py +117 -0
  147. flwr/supercore/primitives/asymmetric_ed25519.py +165 -0
  148. flwr/supercore/sqlite_mixin.py +156 -0
  149. flwr/supercore/superexec/plugin/exec_plugin.py +11 -1
  150. flwr/supercore/superexec/run_superexec.py +16 -2
  151. flwr/supercore/utils.py +20 -0
  152. flwr/superlink/artifact_provider/__init__.py +22 -0
  153. flwr/superlink/artifact_provider/artifact_provider.py +37 -0
  154. flwr/{common → superlink}/auth_plugin/__init__.py +6 -6
  155. flwr/superlink/auth_plugin/auth_plugin.py +91 -0
  156. flwr/superlink/auth_plugin/noop_auth_plugin.py +87 -0
  157. flwr/superlink/servicer/control/{control_user_auth_interceptor.py → control_account_auth_interceptor.py} +19 -19
  158. flwr/superlink/servicer/control/control_event_log_interceptor.py +1 -1
  159. flwr/superlink/servicer/control/control_grpc.py +16 -11
  160. flwr/superlink/servicer/control/control_servicer.py +207 -58
  161. flwr/supernode/cli/flower_supernode.py +19 -26
  162. flwr/supernode/runtime/run_clientapp.py +2 -2
  163. flwr/supernode/servicer/clientappio/clientappio_servicer.py +1 -1
  164. flwr/supernode/start_client_internal.py +17 -9
  165. {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/METADATA +6 -16
  166. {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/RECORD +170 -140
  167. flwr/cli/new/templates/app/code/client.pytorch_msg_api.py.tpl +0 -80
  168. flwr/cli/new/templates/app/code/server.pytorch_msg_api.py.tpl +0 -41
  169. flwr/common/auth_plugin/auth_plugin.py +0 -149
  170. flwr/serverapp/dp_fixed_clipping.py +0 -352
  171. flwr/serverapp/strategy/strategy_utils_tests.py +0 -304
  172. /flwr/cli/new/templates/app/code/{__init__.pytorch_msg_api.py.tpl → __init__.pytorch_legacy_api.py.tpl} +0 -0
  173. /flwr/{client → clientapp}/client_app.py +0 -0
  174. {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/WHEEL +0 -0
  175. {flwr-1.21.0.dist-info → flwr-1.23.0.dist-info}/entry_points.txt +0 -0
flwr/server/app.py CHANGED
@@ -16,30 +16,26 @@
16
16
 
17
17
 
18
18
  import argparse
19
- import csv
20
19
  import importlib.util
21
20
  import os
22
21
  import subprocess
23
22
  import sys
24
23
  import threading
25
24
  from collections.abc import Sequence
26
- from logging import DEBUG, INFO, WARN
25
+ from logging import INFO, WARN
27
26
  from pathlib import Path
28
27
  from time import sleep
29
- from typing import Any, Callable, Optional, TypeVar
28
+ from typing import Callable, Optional, TypeVar, cast
30
29
 
31
30
  import grpc
32
31
  import yaml
33
- from cryptography.hazmat.primitives.asymmetric import ec
34
- from cryptography.hazmat.primitives.serialization import load_ssh_public_key
35
32
 
36
33
  from flwr.common import GRPC_MAX_MESSAGE_LENGTH, EventType, event
37
34
  from flwr.common.address import parse_address
38
35
  from flwr.common.args import try_obtain_server_certificates
39
- from flwr.common.auth_plugin import ControlAuthPlugin, ControlAuthzPlugin
40
36
  from flwr.common.config import get_flwr_dir
41
37
  from flwr.common.constant import (
42
- AUTH_TYPE_YAML_KEY,
38
+ AUTHN_TYPE_YAML_KEY,
43
39
  AUTHZ_TYPE_YAML_KEY,
44
40
  CLIENT_OCTET,
45
41
  CONTROL_API_DEFAULT_SERVER_ADDRESS,
@@ -53,6 +49,8 @@ from flwr.common.constant import (
53
49
  TRANSPORT_TYPE_GRPC_ADAPTER,
54
50
  TRANSPORT_TYPE_GRPC_RERE,
55
51
  TRANSPORT_TYPE_REST,
52
+ AuthnType,
53
+ AuthzType,
56
54
  EventLogWriterType,
57
55
  ExecPluginType,
58
56
  )
@@ -60,37 +58,45 @@ from flwr.common.event_log_plugin import EventLogWriterPlugin
60
58
  from flwr.common.exit import ExitCode, flwr_exit, register_signal_handlers
61
59
  from flwr.common.grpc import generic_create_grpc_server
62
60
  from flwr.common.logger import log
63
- from flwr.common.secure_aggregation.crypto.symmetric_encryption import (
64
- public_key_to_bytes,
65
- )
61
+ from flwr.common.version import package_version
66
62
  from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
67
63
  add_FleetServicer_to_server,
68
64
  )
69
65
  from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
70
66
  from flwr.server.fleet_event_log_interceptor import FleetEventLogInterceptor
67
+ from flwr.supercore.constant import FLWR_IN_MEMORY_DB_NAME
71
68
  from flwr.supercore.ffs import FfsFactory
72
69
  from flwr.supercore.grpc_health import add_args_health, run_health_server_grpc_no_tls
73
70
  from flwr.supercore.object_store import ObjectStoreFactory
71
+ from flwr.superlink.artifact_provider import ArtifactProvider
72
+ from flwr.superlink.auth_plugin import (
73
+ ControlAuthnPlugin,
74
+ ControlAuthzPlugin,
75
+ NoOpControlAuthnPlugin,
76
+ NoOpControlAuthzPlugin,
77
+ )
74
78
  from flwr.superlink.servicer.control import run_control_api_grpc
75
79
 
76
80
  from .superlink.fleet.grpc_adapter.grpc_adapter_servicer import GrpcAdapterServicer
77
81
  from .superlink.fleet.grpc_rere.fleet_servicer import FleetServicer
78
- from .superlink.fleet.grpc_rere.server_interceptor import AuthenticateServerInterceptor
82
+ from .superlink.fleet.grpc_rere.node_auth_server_interceptor import (
83
+ NodeAuthServerInterceptor,
84
+ )
79
85
  from .superlink.linkstate import LinkStateFactory
80
86
  from .superlink.serverappio.serverappio_grpc import run_serverappio_api_grpc
81
87
  from .superlink.simulation.simulationio_grpc import run_simulationio_api_grpc
82
88
 
83
- DATABASE = ":flwr-in-memory-state:"
84
89
  BASE_DIR = get_flwr_dir() / "superlink" / "ffs"
85
- P = TypeVar("P", ControlAuthPlugin, ControlAuthzPlugin)
90
+ P = TypeVar("P", ControlAuthnPlugin, ControlAuthzPlugin)
86
91
 
87
92
 
88
93
  try:
89
94
  from flwr.ee import (
90
95
  add_ee_args_superlink,
91
- get_control_auth_plugins,
92
- get_control_authz_plugins,
96
+ get_control_authn_ee_plugins,
97
+ get_control_authz_ee_plugins,
93
98
  get_control_event_log_writer_plugins,
99
+ get_ee_artifact_provider,
94
100
  get_fleet_event_log_writer_plugins,
95
101
  )
96
102
  except ImportError:
@@ -99,26 +105,42 @@ except ImportError:
99
105
  def add_ee_args_superlink(parser: argparse.ArgumentParser) -> None:
100
106
  """Add EE-specific arguments to the parser."""
101
107
 
102
- def get_control_auth_plugins() -> dict[str, type[ControlAuthPlugin]]:
103
- """Return all Control API authentication plugins."""
104
- raise NotImplementedError("No authentication plugins are currently supported.")
105
-
106
- def get_control_authz_plugins() -> dict[str, type[ControlAuthzPlugin]]:
107
- """Return all Control API authorization plugins."""
108
- raise NotImplementedError("No authorization plugins are currently supported.")
109
-
110
108
  def get_control_event_log_writer_plugins() -> dict[str, type[EventLogWriterPlugin]]:
111
109
  """Return all Control API event log writer plugins."""
112
110
  raise NotImplementedError(
113
111
  "No event log writer plugins are currently supported."
114
112
  )
115
113
 
114
+ def get_ee_artifact_provider(config_path: str) -> ArtifactProvider:
115
+ """Return the EE artifact provider."""
116
+ raise NotImplementedError("No artifact provider is currently supported.")
117
+
116
118
  def get_fleet_event_log_writer_plugins() -> dict[str, type[EventLogWriterPlugin]]:
117
119
  """Return all Fleet API event log writer plugins."""
118
120
  raise NotImplementedError(
119
121
  "No event log writer plugins are currently supported."
120
122
  )
121
123
 
124
+ def get_control_authn_ee_plugins() -> dict[str, type[ControlAuthnPlugin]]:
125
+ """Return all Control API authentication plugins for EE."""
126
+ return {}
127
+
128
+ def get_control_authz_ee_plugins() -> dict[str, type[ControlAuthzPlugin]]:
129
+ """Return all Control API authorization plugins for EE."""
130
+ return {}
131
+
132
+
133
+ def get_control_authn_plugins() -> dict[str, type[ControlAuthnPlugin]]:
134
+ """Return all Control API authentication plugins."""
135
+ ee_dict: dict[str, type[ControlAuthnPlugin]] = get_control_authn_ee_plugins()
136
+ return ee_dict | {AuthnType.NOOP: NoOpControlAuthnPlugin}
137
+
138
+
139
+ def get_control_authz_plugins() -> dict[str, type[ControlAuthzPlugin]]:
140
+ """Return all Control API authorization plugins."""
141
+ ee_dict: dict[str, type[ControlAuthzPlugin]] = get_control_authz_ee_plugins()
142
+ return ee_dict | {AuthzType.NOOP: NoOpControlAuthzPlugin}
143
+
122
144
 
123
145
  # pylint: disable=too-many-branches, too-many-locals, too-many-statements
124
146
  def run_superlink() -> None:
@@ -183,22 +205,82 @@ def run_superlink() -> None:
183
205
  # Obtain certificates
184
206
  certificates = try_obtain_server_certificates(args)
185
207
 
186
- # Disable the user auth TLS check if args.disable_oidc_tls_cert_verification is
208
+ # Disable the account auth TLS check if args.disable_oidc_tls_cert_verification is
187
209
  # provided
188
210
  verify_tls_cert = not getattr(args, "disable_oidc_tls_cert_verification", None)
189
211
 
190
- auth_plugin: Optional[ControlAuthPlugin] = None
212
+ authn_plugin: Optional[ControlAuthnPlugin] = None
191
213
  authz_plugin: Optional[ControlAuthzPlugin] = None
192
214
  event_log_plugin: Optional[EventLogWriterPlugin] = None
193
- # Load the auth plugin if the args.user_auth_config is provided
215
+ # Load the auth plugin if the args.account_auth_config is provided
194
216
  if cfg_path := getattr(args, "user_auth_config", None):
195
- auth_plugin, authz_plugin = _try_obtain_control_auth_plugins(
196
- Path(cfg_path), verify_tls_cert
217
+ log(
218
+ WARN,
219
+ "The `--user-auth-config` flag is deprecated and will be removed in a "
220
+ "future release. Please use `--account-auth-config` instead.",
197
221
  )
222
+ args.account_auth_config = cfg_path
223
+ cfg_path = getattr(args, "account_auth_config", None)
224
+ authn_plugin, authz_plugin = _load_control_auth_plugins(cfg_path, verify_tls_cert)
225
+ if cfg_path is not None:
198
226
  # Enable event logging if the args.enable_event_log is True
199
227
  if args.enable_event_log:
200
228
  event_log_plugin = _try_obtain_control_event_log_writer_plugin()
201
229
 
230
+ # Load artifact provider if the args.artifact_provider_config is provided
231
+ artifact_provider = None
232
+ if cfg_path := getattr(args, "artifact_provider_config", None):
233
+ log(WARN, "The `--artifact-provider-config` flag is highly experimental.")
234
+ artifact_provider = get_ee_artifact_provider(cfg_path)
235
+
236
+ # Check for incompatible args with SuperNode authentication
237
+ enable_supernode_auth: bool = args.enable_supernode_auth
238
+ if enable_supernode_auth:
239
+ if args.insecure:
240
+ url_v = f"https://flower.ai/docs/framework/v{package_version}/en/"
241
+ page = "how-to-authenticate-supernodes.html"
242
+ flwr_exit(
243
+ ExitCode.SUPERLINK_INVALID_ARGS,
244
+ "The `--enable-supernode-auth` flag requires encrypted TLS "
245
+ "communications. Please provide TLS certificates using the "
246
+ "`--ssl-certfile`, `--ssl-keyfile` and `--ssl-ca-certfile` "
247
+ "arguments to your SuperLink. Please refer to the Flower "
248
+ f"documentation for more information: {url_v}{page}",
249
+ )
250
+ if args.fleet_api_type != TRANSPORT_TYPE_GRPC_RERE:
251
+ flwr_exit(
252
+ ExitCode.SUPERLINK_INVALID_ARGS,
253
+ "The `--enable-supernode-auth` flag is only supported "
254
+ "with the gRPC-rere Fleet API transport. Please set "
255
+ f"`--fleet-api-type` to `{TRANSPORT_TYPE_GRPC_RERE}`.",
256
+ )
257
+ if args.simulation:
258
+ log(
259
+ WARN,
260
+ "SuperNode authentication is not applicable with the simulation, "
261
+ "runtime as no SuperNodes can connect to this SuperLink. "
262
+ "Proceeding...",
263
+ )
264
+ # If supernode authentication is disabled, warn users
265
+ else:
266
+ log(
267
+ WARN,
268
+ "SuperNode authentication is disabled. The SuperLink will accept "
269
+ "connections from any SuperNode.",
270
+ )
271
+
272
+ if args.auth_list_public_keys:
273
+ url_v = f"https://flower.ai/docs/framework/v{package_version}/en/"
274
+ page = "how-to-authenticate-supernodes.html"
275
+ flwr_exit(
276
+ ExitCode.SUPERLINK_INVALID_ARGS,
277
+ "The `--auth-list-public-keys` "
278
+ "argument is no longer supported. To enable SuperNode authentication, "
279
+ "use the `--enable-supernode-auth` flag and use the Flower CLI to register "
280
+ "SuperNodes by supplying their public keys. Please refer"
281
+ f" to the Flower documentation for more information: {url_v}{page}",
282
+ )
283
+
202
284
  # Initialize StateFactory
203
285
  state_factory = LinkStateFactory(args.database)
204
286
 
@@ -206,7 +288,7 @@ def run_superlink() -> None:
206
288
  ffs_factory = FfsFactory(args.storage_dir)
207
289
 
208
290
  # Initialize ObjectStoreFactory
209
- objectstore_factory = ObjectStoreFactory()
291
+ objectstore_factory = ObjectStoreFactory(args.database)
210
292
 
211
293
  # Start Control API
212
294
  is_simulation = args.simulation
@@ -217,9 +299,10 @@ def run_superlink() -> None:
217
299
  objectstore_factory=objectstore_factory,
218
300
  certificates=certificates,
219
301
  is_simulation=is_simulation,
220
- auth_plugin=auth_plugin,
302
+ authn_plugin=authn_plugin,
221
303
  authz_plugin=authz_plugin,
222
304
  event_log_plugin=event_log_plugin,
305
+ artifact_provider=artifact_provider,
223
306
  )
224
307
  grpc_servers = [control_server]
225
308
  bckg_threads: list[threading.Thread] = []
@@ -293,22 +376,8 @@ def run_superlink() -> None:
293
376
  fleet_thread.start()
294
377
  bckg_threads.append(fleet_thread)
295
378
  elif args.fleet_api_type == TRANSPORT_TYPE_GRPC_RERE:
296
- node_public_keys = _try_load_public_keys_node_authentication(args)
297
- auto_auth = True
298
- if node_public_keys is not None:
299
- auto_auth = False
300
- state = state_factory.state()
301
- state.clear_supernode_auth_keys()
302
- state.store_node_public_keys(node_public_keys)
303
- log(
304
- INFO,
305
- "Node authentication enabled with %d known public keys",
306
- len(node_public_keys),
307
- )
308
- else:
309
- log(DEBUG, "Automatic node authentication enabled")
310
379
 
311
- interceptors = [AuthenticateServerInterceptor(state_factory, auto_auth)]
380
+ interceptors = [NodeAuthServerInterceptor(state_factory)]
312
381
  if getattr(args, "enable_event_log", None):
313
382
  fleet_log_plugin = _try_obtain_fleet_event_log_writer_plugin()
314
383
  if fleet_log_plugin is not None:
@@ -320,6 +389,7 @@ def run_superlink() -> None:
320
389
  state_factory=state_factory,
321
390
  ffs_factory=ffs_factory,
322
391
  objectstore_factory=objectstore_factory,
392
+ enable_supernode_auth=enable_supernode_auth,
323
393
  certificates=certificates,
324
394
  interceptors=interceptors,
325
395
  )
@@ -387,55 +457,21 @@ def _format_address(address: str) -> tuple[str, str, int]:
387
457
  return (f"[{host}]:{port}" if is_v6 else f"{host}:{port}", host, port)
388
458
 
389
459
 
390
- def _try_load_public_keys_node_authentication(
391
- args: argparse.Namespace,
392
- ) -> Optional[set[bytes]]:
393
- """Return a set of node public keys."""
394
- if args.auth_superlink_private_key or args.auth_superlink_public_key:
395
- log(
396
- WARN,
397
- "The `--auth-superlink-private-key` and `--auth-superlink-public-key` "
398
- "arguments are deprecated and will be removed in a future release. Node "
399
- "authentication no longer requires these arguments.",
400
- )
401
-
402
- if not args.auth_list_public_keys:
403
- return None
404
-
405
- node_keys_file_path = Path(args.auth_list_public_keys)
406
- if not node_keys_file_path.exists():
407
- sys.exit(
408
- "The provided path to the known public keys CSV file does not exist: "
409
- f"{node_keys_file_path}. "
410
- "Please provide the CSV file path containing known public keys "
411
- "to '--auth-list-public-keys'."
412
- )
413
-
414
- node_public_keys: set[bytes] = set()
415
-
416
- with open(node_keys_file_path, newline="", encoding="utf-8") as csvfile:
417
- reader = csv.reader(csvfile)
418
- for row in reader:
419
- for element in row:
420
- public_key = load_ssh_public_key(element.encode())
421
- if isinstance(public_key, ec.EllipticCurvePublicKey):
422
- node_public_keys.add(public_key_to_bytes(public_key))
423
- else:
424
- sys.exit(
425
- "Error: Unable to parse the public keys in the CSV "
426
- "file. Please ensure that the CSV file path points to a valid "
427
- "known SSH public keys files and try again."
428
- )
429
- return node_public_keys
430
-
431
-
432
- def _try_obtain_control_auth_plugins(
433
- config_path: Path, verify_tls_cert: bool
434
- ) -> tuple[ControlAuthPlugin, ControlAuthzPlugin]:
460
+ def _load_control_auth_plugins(
461
+ config_path: Optional[str], verify_tls_cert: bool
462
+ ) -> tuple[ControlAuthnPlugin, ControlAuthzPlugin]:
435
463
  """Obtain Control API authentication and authorization plugins."""
464
+ # Load NoOp plugins if no config path is provided
465
+ if config_path is None:
466
+ config_path = ""
467
+ config = {
468
+ "authentication": {AUTHN_TYPE_YAML_KEY: AuthnType.NOOP},
469
+ "authorization": {AUTHZ_TYPE_YAML_KEY: AuthzType.NOOP},
470
+ }
436
471
  # Load YAML file
437
- with config_path.open("r", encoding="utf-8") as file:
438
- config: dict[str, Any] = yaml.safe_load(file)
472
+ else:
473
+ with Path(config_path).open("r", encoding="utf-8") as file:
474
+ config = yaml.safe_load(file)
439
475
 
440
476
  def _load_plugin(
441
477
  section: str, yaml_key: str, loader: Callable[[], dict[str, type[P]]]
@@ -445,9 +481,7 @@ def _try_obtain_control_auth_plugins(
445
481
  try:
446
482
  plugins: dict[str, type[P]] = loader()
447
483
  plugin_cls: type[P] = plugins[auth_plugin_name]
448
- return plugin_cls(
449
- user_auth_config_path=config_path, verify_tls_cert=verify_tls_cert
450
- )
484
+ return plugin_cls(Path(cast(str, config_path)), verify_tls_cert)
451
485
  except KeyError:
452
486
  if auth_plugin_name:
453
487
  sys.exit(
@@ -455,14 +489,22 @@ def _try_obtain_control_auth_plugins(
455
489
  f"Please provide a valid {section} type in the configuration."
456
490
  )
457
491
  sys.exit(f"No {section} type is provided in the configuration.")
458
- except NotImplementedError:
459
- sys.exit(f"No {section} plugins are currently supported.")
492
+
493
+ # Warn deprecated auth_type key
494
+ if authn_type := config["authentication"].pop("auth_type", None):
495
+ log(
496
+ WARN,
497
+ "The `auth_type` key in the authentication configuration is deprecated. "
498
+ "Use `%s` instead.",
499
+ AUTHN_TYPE_YAML_KEY,
500
+ )
501
+ config["authentication"][AUTHN_TYPE_YAML_KEY] = authn_type
460
502
 
461
503
  # Load authentication plugin
462
- auth_plugin = _load_plugin(
504
+ authn_plugin = _load_plugin(
463
505
  section="authentication",
464
- yaml_key=AUTH_TYPE_YAML_KEY,
465
- loader=get_control_auth_plugins,
506
+ yaml_key=AUTHN_TYPE_YAML_KEY,
507
+ loader=get_control_authn_plugins,
466
508
  )
467
509
 
468
510
  # Load authorization plugin
@@ -472,7 +514,7 @@ def _try_obtain_control_auth_plugins(
472
514
  loader=get_control_authz_plugins,
473
515
  )
474
516
 
475
- return auth_plugin, authz_plugin
517
+ return authn_plugin, authz_plugin
476
518
 
477
519
 
478
520
  def _try_obtain_control_event_log_writer_plugin() -> Optional[EventLogWriterPlugin]:
@@ -508,6 +550,7 @@ def _run_fleet_api_grpc_rere( # pylint: disable=R0913, R0917
508
550
  state_factory: LinkStateFactory,
509
551
  ffs_factory: FfsFactory,
510
552
  objectstore_factory: ObjectStoreFactory,
553
+ enable_supernode_auth: bool,
511
554
  certificates: Optional[tuple[bytes, bytes, bytes]],
512
555
  interceptors: Optional[Sequence[grpc.ServerInterceptor]] = None,
513
556
  ) -> grpc.Server:
@@ -517,6 +560,7 @@ def _run_fleet_api_grpc_rere( # pylint: disable=R0913, R0917
517
560
  state_factory=state_factory,
518
561
  ffs_factory=ffs_factory,
519
562
  objectstore_factory=objectstore_factory,
563
+ enable_supernode_auth=enable_supernode_auth,
520
564
  )
521
565
  fleet_add_servicer_to_server_fn = add_FleetServicer_to_server
522
566
  fleet_grpc_server = generic_create_grpc_server(
@@ -535,6 +579,7 @@ def _run_fleet_api_grpc_rere( # pylint: disable=R0913, R0917
535
579
  return fleet_grpc_server
536
580
 
537
581
 
582
+ # pylint: disable=R0913, R0917
538
583
  def _run_fleet_api_grpc_adapter(
539
584
  address: str,
540
585
  state_factory: LinkStateFactory,
@@ -548,6 +593,7 @@ def _run_fleet_api_grpc_adapter(
548
593
  state_factory=state_factory,
549
594
  ffs_factory=ffs_factory,
550
595
  objectstore_factory=objectstore_factory,
596
+ enable_supernode_auth=False,
551
597
  )
552
598
  fleet_add_servicer_to_server_fn = add_GrpcAdapterServicer_to_server
553
599
  fleet_grpc_server = generic_create_grpc_server(
@@ -678,11 +724,9 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
678
724
  parser.add_argument(
679
725
  "--database",
680
726
  help="A string representing the path to the database "
681
- "file that will be opened. Note that passing ':memory:' "
682
- "will open a connection to a database that is in RAM, "
683
- "instead of on disk. If nothing is provided, "
727
+ "file that will be opened. If nothing is provided, "
684
728
  "Flower will just create a state in memory.",
685
- default=DATABASE,
729
+ default=FLWR_IN_MEMORY_DB_NAME,
686
730
  )
687
731
  parser.add_argument(
688
732
  "--storage-dir",
@@ -692,18 +736,12 @@ def _add_args_common(parser: argparse.ArgumentParser) -> None:
692
736
  parser.add_argument(
693
737
  "--auth-list-public-keys",
694
738
  type=str,
695
- help="A CSV file (as a path str) containing a list of known public "
696
- "keys to enable authentication.",
697
- )
698
- parser.add_argument(
699
- "--auth-superlink-private-key",
700
- type=str,
701
739
  help="This argument is deprecated and will be removed in a future release.",
702
740
  )
703
741
  parser.add_argument(
704
- "--auth-superlink-public-key",
705
- type=str,
706
- help="This argument is deprecated and will be removed in a future release.",
742
+ "--enable-supernode-auth",
743
+ action="store_true",
744
+ help="Enable supernode authentication.",
707
745
  )
708
746
 
709
747
 
@@ -33,10 +33,12 @@ from flwr.common.version import package_name, package_version
33
33
  from flwr.proto import grpcadapter_pb2_grpc # pylint: disable=E0611
34
34
  from flwr.proto.fab_pb2 import GetFabRequest # pylint: disable=E0611
35
35
  from flwr.proto.fleet_pb2 import ( # pylint: disable=E0611
36
- CreateNodeRequest,
37
- DeleteNodeRequest,
36
+ ActivateNodeRequest,
37
+ DeactivateNodeRequest,
38
38
  PullMessagesRequest,
39
39
  PushMessagesRequest,
40
+ RegisterNodeFleetRequest,
41
+ UnregisterNodeFleetRequest,
40
42
  )
41
43
  from flwr.proto.grpcadapter_pb2 import MessageContainer # pylint: disable=E0611
42
44
  from flwr.proto.heartbeat_pb2 import SendNodeHeartbeatRequest # pylint: disable=E0611
@@ -77,15 +79,23 @@ def _handle(
77
79
  class GrpcAdapterServicer(grpcadapter_pb2_grpc.GrpcAdapterServicer, FleetServicer):
78
80
  """Fleet API via GrpcAdapter servicer."""
79
81
 
80
- def SendReceive( # pylint: disable=too-many-return-statements
82
+ def SendReceive( # pylint: disable=too-many-return-statements, too-many-branches
81
83
  self, request: MessageContainer, context: grpc.ServicerContext
82
84
  ) -> MessageContainer:
83
85
  """."""
84
86
  log(DEBUG, "GrpcAdapterServicer.SendReceive")
85
- if request.grpc_message_name == CreateNodeRequest.__qualname__:
86
- return _handle(request, context, CreateNodeRequest, self.CreateNode)
87
- if request.grpc_message_name == DeleteNodeRequest.__qualname__:
88
- return _handle(request, context, DeleteNodeRequest, self.DeleteNode)
87
+ if request.grpc_message_name == RegisterNodeFleetRequest.__qualname__:
88
+ return _handle(
89
+ request, context, RegisterNodeFleetRequest, self.RegisterNode
90
+ )
91
+ if request.grpc_message_name == ActivateNodeRequest.__qualname__:
92
+ return _handle(request, context, ActivateNodeRequest, self.ActivateNode)
93
+ if request.grpc_message_name == DeactivateNodeRequest.__qualname__:
94
+ return _handle(request, context, DeactivateNodeRequest, self.DeactivateNode)
95
+ if request.grpc_message_name == UnregisterNodeFleetRequest.__qualname__:
96
+ return _handle(
97
+ request, context, UnregisterNodeFleetRequest, self.UnregisterNode
98
+ )
89
99
  if request.grpc_message_name == SendNodeHeartbeatRequest.__qualname__:
90
100
  return _handle(
91
101
  request, context, SendNodeHeartbeatRequest, self.SendNodeHeartbeat