flwr-nightly 1.26.0.dev20260127__py3-none-any.whl → 1.26.0.dev20260128__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.
flwr/cli/config/ls.py CHANGED
@@ -40,7 +40,7 @@ def ls(
40
40
  ),
41
41
  ] = CliOutputFormat.DEFAULT,
42
42
  ) -> None:
43
- """List all SuperLink connections."""
43
+ """List all SuperLink connections (alias: ls)."""
44
44
  suppress_output = output_format == CliOutputFormat.JSON
45
45
  captured_output = io.StringIO()
46
46
  config_path = None
flwr/cli/constant.py CHANGED
@@ -101,7 +101,6 @@ default = "local"
101
101
 
102
102
  [superlink.supergrid]
103
103
  address = "{SUPERGRID_ADDRESS}"
104
- federation = "YOUR-FEDERATION-HERE"
105
104
 
106
105
  [superlink.local]
107
106
  options.num-supernodes = 10
flwr/cli/federation/ls.py CHANGED
@@ -68,7 +68,7 @@ def ls( # pylint: disable=R0914, R0913, R0917, R0912
68
68
  ),
69
69
  ] = None,
70
70
  ) -> None:
71
- """List available federations."""
71
+ """List available federations or details of a specific federation (alias: ls)."""
72
72
  suppress_output = output_format == CliOutputFormat.JSON
73
73
  captured_output = io.StringIO()
74
74
 
flwr/cli/ls.py CHANGED
@@ -79,7 +79,7 @@ def ls( # pylint: disable=too-many-locals, too-many-branches, R0913, R0917
79
79
  ),
80
80
  ] = CliOutputFormat.DEFAULT,
81
81
  ) -> None:
82
- """List the details of one provided run ID or all runs in a Flower federation.
82
+ """List the details of one provided run ID or all runs (alias: ls).
83
83
 
84
84
  The following details are displayed:
85
85
 
flwr/cli/supernode/ls.py CHANGED
@@ -66,7 +66,7 @@ def ls( # pylint: disable=R0914, R0913, R0917
66
66
  ),
67
67
  ] = False,
68
68
  ) -> None:
69
- """List SuperNodes in the federation."""
69
+ """List SuperNodes in the federation (alias: ls)."""
70
70
  suppress_output = output_format == CliOutputFormat.JSON
71
71
  captured_output = io.StringIO()
72
72
 
flwr/cli/typing.py CHANGED
@@ -22,6 +22,7 @@ from flwr.cli.constant import (
22
22
  DEFAULT_SIMULATION_BACKEND_NAME,
23
23
  SuperLinkConnectionTomlKey,
24
24
  )
25
+ from flwr.supercore.utils import check_federation_format
25
26
 
26
27
  _ERROR_MSG_FMT = "SuperLinkConnection.%s is None"
27
28
 
@@ -176,11 +177,15 @@ class SuperLinkConnection:
176
177
  + f"expected bool, but got {type(self._insecure).__name__}."
177
178
  )
178
179
 
179
- if self.federation is not None and not isinstance(self.federation, str):
180
- raise ValueError(
181
- err_prefix % SuperLinkConnectionTomlKey.FEDERATION
182
- + f"expected str, but got {type(self.federation).__name__}."
183
- )
180
+ if self.federation is not None:
181
+ if not isinstance(self.federation, str):
182
+ raise ValueError(
183
+ err_prefix % SuperLinkConnectionTomlKey.FEDERATION
184
+ + f"expected str, but got {type(self.federation).__name__}."
185
+ )
186
+
187
+ # Check if the federation string is valid
188
+ check_federation_format(self.federation)
184
189
 
185
190
  # The connection needs to have either an address or options (or both).
186
191
  if self.address is None and self.options is None:
@@ -33,6 +33,7 @@ class ExitCode:
33
33
  SUPERLINK_LICENSE_MISSING = 102
34
34
  SUPERLINK_LICENSE_URL_INVALID = 103
35
35
  SUPERLINK_INVALID_ARGS = 104
36
+ SUPERLINK_DATABASE_SCHEMA_MISMATCH = 105
36
37
 
37
38
  # ServerApp-specific exit codes (200-299)
38
39
  SERVERAPP_STRATEGY_PRECONDITION_UNMET = 200
@@ -91,6 +92,11 @@ EXIT_CODE_HELP = {
91
92
  "Invalid arguments provided to SuperLink. Use `--help` check for the correct "
92
93
  "usage. Alternatively, check the documentation."
93
94
  ),
95
+ ExitCode.SUPERLINK_DATABASE_SCHEMA_MISMATCH: (
96
+ "The database schema does not match the expected schema for this version of "
97
+ "SuperLink. Please refer to the documentation for guidance on how to resolve "
98
+ "this issue."
99
+ ),
94
100
  # ServerApp-specific exit codes (200-299)
95
101
  ExitCode.SERVERAPP_STRATEGY_PRECONDITION_UNMET: (
96
102
  "The strategy received replies that cannot be aggregated. Please ensure all "
flwr/common/typing.py CHANGED
@@ -24,6 +24,7 @@ import numpy as np
24
24
  import numpy.typing as npt
25
25
 
26
26
  from flwr.app.user_config import UserConfig
27
+ from flwr.proto.federation_pb2 import Account # pylint: disable=E0611
27
28
  from flwr.proto.node_pb2 import NodeInfo # pylint: disable=E0611
28
29
 
29
30
  NDArray = npt.NDArray[Any]
@@ -343,5 +344,6 @@ class Federation:
343
344
 
344
345
  name: str
345
346
  member_aids: list[str]
347
+ accounts: list[Account]
346
348
  nodes: list[NodeInfo]
347
349
  runs: list[Run]
@@ -26,13 +26,15 @@ from flwr.proto import run_pb2 as flwr_dot_proto_dot_run__pb2
26
26
  from flwr.proto import node_pb2 as flwr_dot_proto_dot_node__pb2
27
27
 
28
28
 
29
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x66lwr/proto/federation.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x15\x66lwr/proto/node.proto\"s\n\nFederation\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0bmember_aids\x18\x02 \x03(\t\x12#\n\x05nodes\x18\x03 \x03(\x0b\x32\x14.flwr.proto.NodeInfo\x12\x1d\n\x04runs\x18\x04 \x03(\x0b\x32\x0f.flwr.proto.Runb\x06proto3')
29
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1b\x66lwr/proto/federation.proto\x12\nflwr.proto\x1a\x14\x66lwr/proto/run.proto\x1a\x15\x66lwr/proto/node.proto\"#\n\x07\x41\x63\x63ount\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"\x9a\x01\n\nFederation\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0bmember_aids\x18\x02 \x03(\t\x12%\n\x08\x61\x63\x63ounts\x18\x03 \x03(\x0b\x32\x13.flwr.proto.Account\x12#\n\x05nodes\x18\x04 \x03(\x0b\x32\x14.flwr.proto.NodeInfo\x12\x1d\n\x04runs\x18\x05 \x03(\x0b\x32\x0f.flwr.proto.Runb\x06proto3')
30
30
 
31
31
  _globals = globals()
32
32
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
33
33
  _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'flwr.proto.federation_pb2', _globals)
34
34
  if not _descriptor._USE_C_DESCRIPTORS:
35
35
  DESCRIPTOR._loaded_options = None
36
- _globals['_FEDERATION']._serialized_start=88
37
- _globals['_FEDERATION']._serialized_end=203
36
+ _globals['_ACCOUNT']._serialized_start=88
37
+ _globals['_ACCOUNT']._serialized_end=123
38
+ _globals['_FEDERATION']._serialized_start=126
39
+ _globals['_FEDERATION']._serialized_end=280
38
40
  # @@protoc_insertion_point(module_scope)
@@ -28,18 +28,39 @@ import typing
28
28
 
29
29
  DESCRIPTOR: google.protobuf.descriptor.FileDescriptor
30
30
 
31
+ @typing.final
32
+ class Account(google.protobuf.message.Message):
33
+ DESCRIPTOR: google.protobuf.descriptor.Descriptor
34
+
35
+ ID_FIELD_NUMBER: builtins.int
36
+ NAME_FIELD_NUMBER: builtins.int
37
+ id: builtins.str
38
+ name: builtins.str
39
+ def __init__(
40
+ self,
41
+ *,
42
+ id: builtins.str = ...,
43
+ name: builtins.str = ...,
44
+ ) -> None: ...
45
+ def ClearField(self, field_name: typing.Literal["id", b"id", "name", b"name"]) -> None: ...
46
+
47
+ global___Account = Account
48
+
31
49
  @typing.final
32
50
  class Federation(google.protobuf.message.Message):
33
51
  DESCRIPTOR: google.protobuf.descriptor.Descriptor
34
52
 
35
53
  NAME_FIELD_NUMBER: builtins.int
36
54
  MEMBER_AIDS_FIELD_NUMBER: builtins.int
55
+ ACCOUNTS_FIELD_NUMBER: builtins.int
37
56
  NODES_FIELD_NUMBER: builtins.int
38
57
  RUNS_FIELD_NUMBER: builtins.int
39
58
  name: builtins.str
40
59
  @property
41
60
  def member_aids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ...
42
61
  @property
62
+ def accounts(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___Account]: ...
63
+ @property
43
64
  def nodes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[flwr.proto.node_pb2.NodeInfo]: ...
44
65
  @property
45
66
  def runs(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[flwr.proto.run_pb2.Run]: ...
@@ -48,9 +69,10 @@ class Federation(google.protobuf.message.Message):
48
69
  *,
49
70
  name: builtins.str = ...,
50
71
  member_aids: collections.abc.Iterable[builtins.str] | None = ...,
72
+ accounts: collections.abc.Iterable[global___Account] | None = ...,
51
73
  nodes: collections.abc.Iterable[flwr.proto.node_pb2.NodeInfo] | None = ...,
52
74
  runs: collections.abc.Iterable[flwr.proto.run_pb2.Run] | None = ...,
53
75
  ) -> None: ...
54
- def ClearField(self, field_name: typing.Literal["member_aids", b"member_aids", "name", b"name", "nodes", b"nodes", "runs", b"runs"]) -> None: ...
76
+ def ClearField(self, field_name: typing.Literal["accounts", b"accounts", "member_aids", b"member_aids", "name", b"name", "nodes", b"nodes", "runs", b"runs"]) -> None: ...
55
77
 
56
78
  global___Federation = Federation
flwr/server/app.py CHANGED
@@ -36,13 +36,11 @@ from flwr.common.config import get_flwr_dir
36
36
  from flwr.common.constant import (
37
37
  AUTHN_TYPE_YAML_KEY,
38
38
  AUTHZ_TYPE_YAML_KEY,
39
- CLIENT_OCTET,
40
39
  CONTROL_API_DEFAULT_SERVER_ADDRESS,
41
40
  FLEET_API_GRPC_RERE_DEFAULT_ADDRESS,
42
41
  FLEET_API_REST_DEFAULT_ADDRESS,
43
42
  ISOLATION_MODE_PROCESS,
44
43
  ISOLATION_MODE_SUBPROCESS,
45
- SERVER_OCTET,
46
44
  SERVERAPPIO_API_DEFAULT_SERVER_ADDRESS,
47
45
  SIMULATIONIO_API_DEFAULT_SERVER_ADDRESS,
48
46
  TRANSPORT_TYPE_GRPC_ADAPTER,
@@ -62,7 +60,7 @@ from flwr.proto.fleet_pb2_grpc import ( # pylint: disable=E0611
62
60
  )
63
61
  from flwr.proto.grpcadapter_pb2_grpc import add_GrpcAdapterServicer_to_server
64
62
  from flwr.server.fleet_event_log_interceptor import FleetEventLogInterceptor
65
- from flwr.supercore.address import parse_address
63
+ from flwr.supercore.address import parse_address, resolve_bind_address
66
64
  from flwr.supercore.constant import FLWR_IN_MEMORY_DB_NAME
67
65
  from flwr.supercore.ffs import FfsFactory
68
66
  from flwr.supercore.grpc_health import add_args_health, run_health_server_grpc_no_tls
@@ -429,16 +427,11 @@ def run_superlink() -> None:
429
427
  raise ValueError(f"Unknown fleet_api_type: {args.fleet_api_type}")
430
428
 
431
429
  if args.isolation == ISOLATION_MODE_SUBPROCESS:
432
-
433
- _octet, _colon, _port = serverappio_address.rpartition(":")
434
- io_address = (
435
- f"{CLIENT_OCTET}:{_port}" if _octet == SERVER_OCTET else serverappio_address
430
+ appio_address = resolve_bind_address(
431
+ simulationio_address if is_simulation else serverappio_address
436
432
  )
437
433
  command = ["flower-superexec", "--insecure"]
438
- command += [
439
- "--appio-api-address",
440
- simulationio_address if is_simulation else io_address,
441
- ]
434
+ command += ["--appio-api-address", appio_address]
442
435
  command += [
443
436
  "--plugin-type",
444
437
  ExecPluginType.SIMULATION if is_simulation else ExecPluginType.SERVER_APP,
flwr/supercore/address.py CHANGED
@@ -100,3 +100,12 @@ def is_port_in_use(address: str) -> bool:
100
100
  return True
101
101
 
102
102
  return False
103
+
104
+
105
+ def resolve_bind_address(address: str) -> str:
106
+ """Replace bind-all addresses (0.0.0.0, ::) with localhost (127.0.0.1, ::1)."""
107
+ if address.startswith("[::]"):
108
+ return address.replace("[::]", "[::1]", 1)
109
+ if address.startswith("0.0.0.0"):
110
+ return address.replace("0.0.0.0", "127.0.0.1", 1)
111
+ return address
@@ -25,6 +25,9 @@ EXEC_PLUGIN_SECTION = "exec_plugin"
25
25
  # Flower in-memory Python-based database name
26
26
  FLWR_IN_MEMORY_DB_NAME = ":flwr-in-memory:"
27
27
 
28
+ # Flower in-memory SQLite database URL
29
+ FLWR_IN_MEMORY_SQLITE_DB_URL = "sqlite:///:memory:"
30
+
28
31
  # Constants for Hub
29
32
  APP_ID_PATTERN = r"^@[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
30
33
  APP_VERSION_PATTERN = r"^\d+\.\d+\.\d+$"
@@ -29,7 +29,7 @@ from sqlalchemy.exc import SQLAlchemyError
29
29
  from sqlalchemy.orm import Session, sessionmaker
30
30
 
31
31
  from flwr.common.logger import log
32
- from flwr.supercore.constant import SQLITE_PRAGMAS
32
+ from flwr.supercore.constant import FLWR_IN_MEMORY_SQLITE_DB_URL, SQLITE_PRAGMAS
33
33
 
34
34
  _current_session: ContextVar[Session | None] = ContextVar(
35
35
  "current_sqlalchemy_session",
@@ -95,7 +95,7 @@ class SqlMixin(ABC):
95
95
 
96
96
  # Auto-convert file path to SQLAlchemy SQLite URL if needed
97
97
  if database_path == ":memory:":
98
- self.database_url = "sqlite:///:memory:"
98
+ self.database_url = FLWR_IN_MEMORY_SQLITE_DB_URL
99
99
  elif not database_path.startswith("sqlite://"):
100
100
  # Treat as file path, convert to absolute and create SQLite URL
101
101
  abs_path = Path(database_path).resolve()
@@ -0,0 +1,15 @@
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Alembic helpers and migration environment for SQL state."""
@@ -0,0 +1,81 @@
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Alembic environment configuration for State migrations."""
16
+
17
+
18
+ from logging.config import fileConfig
19
+
20
+ from alembic import context
21
+ from sqlalchemy import engine_from_config, pool
22
+
23
+ from flwr.supercore.state.alembic.utils import get_combined_metadata
24
+
25
+ # Alembic Config object
26
+ config = context.config # pylint: disable=no-member
27
+
28
+ # Interpret the config file for Python logging
29
+ if config.config_file_name is not None:
30
+ fileConfig(config.config_file_name)
31
+
32
+
33
+ # Target metadata for autogenerate
34
+ target_metadata = get_combined_metadata()
35
+
36
+
37
+ def run_migrations_offline() -> None:
38
+ """Run migrations in 'offline' mode.
39
+
40
+ This configures the context with just a URL and not an Engine, though an Engine is
41
+ acceptable here as well. By skipping the Engine creation we don't even need a DBAPI
42
+ to be available.
43
+
44
+ Calls to context.execute() here emit the given string to the script output.
45
+ """
46
+ url = config.get_main_option("sqlalchemy.url")
47
+ context.configure( # pylint: disable=no-member
48
+ url=url,
49
+ target_metadata=target_metadata,
50
+ literal_binds=True,
51
+ dialect_opts={"paramstyle": "named"},
52
+ )
53
+
54
+ with context.begin_transaction(): # pylint: disable=no-member
55
+ context.run_migrations() # pylint: disable=no-member
56
+
57
+
58
+ def run_migrations_online() -> None:
59
+ """Run migrations in 'online' mode.
60
+
61
+ In this scenario we need to create an Engine and associate a connection with the
62
+ context.
63
+ """
64
+ connectable = engine_from_config(
65
+ config.get_section(config.config_ini_section, {}),
66
+ prefix="sqlalchemy.",
67
+ poolclass=pool.NullPool,
68
+ )
69
+
70
+ with connectable.connect() as connection:
71
+ # pylint: disable-next=no-member
72
+ context.configure(connection=connection, target_metadata=target_metadata)
73
+
74
+ with context.begin_transaction(): # pylint: disable=no-member
75
+ context.run_migrations() # pylint: disable=no-member
76
+
77
+
78
+ if context.is_offline_mode(): # pylint: disable=no-member
79
+ run_migrations_offline()
80
+ else:
81
+ run_migrations_online()
@@ -0,0 +1,43 @@
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """${message}.
16
+
17
+ Revision ID: ${up_revision}
18
+ Revises: ${down_revision | comma,n}
19
+ Create Date: ${create_date}
20
+ """
21
+ from typing import Sequence, Union
22
+
23
+ from alembic import op
24
+ import sqlalchemy as sa
25
+ ${imports if imports else ""}
26
+
27
+ # pylint: disable=no-member
28
+
29
+ # revision identifiers, used by Alembic.
30
+ revision: str = ${repr(up_revision)}
31
+ down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)}
32
+ branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
33
+ depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
34
+
35
+
36
+ def upgrade() -> None:
37
+ """Upgrade schema."""
38
+ ${upgrades if upgrades else "pass"}
39
+
40
+
41
+ def downgrade() -> None:
42
+ """Downgrade schema."""
43
+ ${downgrades if downgrades else "pass"}
@@ -0,0 +1,49 @@
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Helpers for running and validating Alembic migrations."""
16
+
17
+
18
+ from sqlalchemy import MetaData
19
+
20
+ from flwr.supercore.state.schema.corestate_tables import create_corestate_metadata
21
+ from flwr.supercore.state.schema.linkstate_tables import create_linkstate_metadata
22
+ from flwr.supercore.state.schema.objectstore_tables import create_objectstore_metadata
23
+
24
+
25
+ def get_combined_metadata() -> MetaData:
26
+ """Combine all Flower state metadata objects into a single MetaData instance.
27
+
28
+ This ensures Alembic can track all tables across CoreState, LinkState, and
29
+ ObjectStore.
30
+
31
+ Returns
32
+ -------
33
+ MetaData
34
+ Combined SQLAlchemy MetaData with all Flower state tables.
35
+ """
36
+ # Start with linkstate tables
37
+ metadata = create_linkstate_metadata()
38
+
39
+ # Add corestate tables
40
+ corestate_metadata = create_corestate_metadata()
41
+ for table in corestate_metadata.tables.values():
42
+ table.to_metadata(metadata)
43
+
44
+ # Add objectstore tables
45
+ objectstore_metadata = create_objectstore_metadata()
46
+ for table in objectstore_metadata.tables.values():
47
+ table.to_metadata(metadata)
48
+
49
+ return metadata
@@ -0,0 +1,15 @@
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Alembic migration versions."""
@@ -0,0 +1,200 @@
1
+ # Copyright 2026 Flower Labs GmbH. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ # ==============================================================================
15
+ """Initialize migration of state tables.
16
+
17
+ Revision ID: 8e65d8ae60b0
18
+ Revises:
19
+ Create Date: 2026-01-28 11:03:18.038794
20
+ """
21
+ from collections.abc import Sequence
22
+
23
+ import sqlalchemy as sa
24
+ from alembic import op
25
+
26
+ # pylint: disable=no-member
27
+
28
+ # revision identifiers, used by Alembic.
29
+ revision: str = "8e65d8ae60b0"
30
+ down_revision: str | Sequence[str] | None = None
31
+ branch_labels: str | Sequence[str] | None = None
32
+ depends_on: str | Sequence[str] | None = None
33
+
34
+
35
+ def upgrade() -> None:
36
+ """Upgrade schema."""
37
+ # ### commands auto generated by Alembic - please adjust! ###
38
+ # LinkState tables
39
+ op.create_table(
40
+ "node",
41
+ sa.Column("node_id", sa.Integer(), nullable=True),
42
+ sa.Column("owner_aid", sa.String(), nullable=True),
43
+ sa.Column("owner_name", sa.String(), nullable=True),
44
+ sa.Column("status", sa.String(), nullable=True),
45
+ sa.Column("registered_at", sa.String(), nullable=True),
46
+ sa.Column("last_activated_at", sa.String(), nullable=True),
47
+ sa.Column("last_deactivated_at", sa.String(), nullable=True),
48
+ sa.Column("unregistered_at", sa.String(), nullable=True),
49
+ sa.Column("online_until", sa.TIMESTAMP(), nullable=True),
50
+ sa.Column("heartbeat_interval", sa.Float(), nullable=True),
51
+ sa.Column("public_key", sa.LargeBinary(), nullable=True),
52
+ sa.UniqueConstraint("node_id"),
53
+ sa.UniqueConstraint("public_key"),
54
+ )
55
+ op.create_index("idx_node_owner_aid", "node", ["owner_aid"], unique=False)
56
+ op.create_index("idx_node_status", "node", ["status"], unique=False)
57
+ op.create_index("idx_online_until", "node", ["online_until"], unique=False)
58
+ op.create_table(
59
+ "run",
60
+ sa.Column("run_id", sa.Integer(), nullable=True),
61
+ sa.Column("fab_id", sa.String(), nullable=True),
62
+ sa.Column("fab_version", sa.String(), nullable=True),
63
+ sa.Column("fab_hash", sa.String(), nullable=True),
64
+ sa.Column("override_config", sa.String(), nullable=True),
65
+ sa.Column("pending_at", sa.String(), nullable=True),
66
+ sa.Column("starting_at", sa.String(), nullable=True),
67
+ sa.Column("running_at", sa.String(), nullable=True),
68
+ sa.Column("finished_at", sa.String(), nullable=True),
69
+ sa.Column("sub_status", sa.String(), nullable=True),
70
+ sa.Column("details", sa.String(), nullable=True),
71
+ sa.Column("federation", sa.String(), nullable=True),
72
+ sa.Column("federation_options", sa.LargeBinary(), nullable=True),
73
+ sa.Column("flwr_aid", sa.String(), nullable=True),
74
+ sa.Column("bytes_sent", sa.Integer(), server_default="0", nullable=True),
75
+ sa.Column("bytes_recv", sa.Integer(), server_default="0", nullable=True),
76
+ sa.Column("clientapp_runtime", sa.Float(), server_default="0.0", nullable=True),
77
+ sa.UniqueConstraint("run_id"),
78
+ )
79
+ op.create_table(
80
+ "logs",
81
+ sa.Column("timestamp", sa.Float(), nullable=True),
82
+ sa.Column("run_id", sa.Integer(), nullable=True),
83
+ sa.Column("node_id", sa.Integer(), nullable=True),
84
+ sa.Column("log", sa.String(), nullable=True),
85
+ sa.ForeignKeyConstraint(
86
+ ["run_id"],
87
+ ["run.run_id"],
88
+ ),
89
+ sa.UniqueConstraint("timestamp", "run_id", "node_id"),
90
+ )
91
+ op.create_table(
92
+ "context",
93
+ sa.Column("run_id", sa.Integer(), nullable=True),
94
+ sa.Column("context", sa.LargeBinary(), nullable=True),
95
+ sa.ForeignKeyConstraint(
96
+ ["run_id"],
97
+ ["run.run_id"],
98
+ ),
99
+ sa.UniqueConstraint("run_id"),
100
+ )
101
+ op.create_table(
102
+ "message_ins",
103
+ sa.Column("message_id", sa.String(), nullable=True),
104
+ sa.Column("group_id", sa.String(), nullable=True),
105
+ sa.Column("run_id", sa.Integer(), nullable=True),
106
+ sa.Column("src_node_id", sa.Integer(), nullable=True),
107
+ sa.Column("dst_node_id", sa.Integer(), nullable=True),
108
+ sa.Column("reply_to_message_id", sa.String(), nullable=True),
109
+ sa.Column("created_at", sa.Float(), nullable=True),
110
+ sa.Column("delivered_at", sa.String(), nullable=True),
111
+ sa.Column("ttl", sa.Float(), nullable=True),
112
+ sa.Column("message_type", sa.String(), nullable=True),
113
+ sa.Column("content", sa.LargeBinary(), nullable=True),
114
+ sa.Column("error", sa.LargeBinary(), nullable=True),
115
+ sa.ForeignKeyConstraint(
116
+ ["run_id"],
117
+ ["run.run_id"],
118
+ ),
119
+ sa.UniqueConstraint("message_id"),
120
+ )
121
+ op.create_table(
122
+ "message_res",
123
+ sa.Column("message_id", sa.String(), nullable=True),
124
+ sa.Column("group_id", sa.String(), nullable=True),
125
+ sa.Column("run_id", sa.Integer(), nullable=True),
126
+ sa.Column("src_node_id", sa.Integer(), nullable=True),
127
+ sa.Column("dst_node_id", sa.Integer(), nullable=True),
128
+ sa.Column("reply_to_message_id", sa.String(), nullable=True),
129
+ sa.Column("created_at", sa.Float(), nullable=True),
130
+ sa.Column("delivered_at", sa.String(), nullable=True),
131
+ sa.Column("ttl", sa.Float(), nullable=True),
132
+ sa.Column("message_type", sa.String(), nullable=True),
133
+ sa.Column("content", sa.LargeBinary(), nullable=True),
134
+ sa.Column("error", sa.LargeBinary(), nullable=True),
135
+ sa.ForeignKeyConstraint(
136
+ ["run_id"],
137
+ ["run.run_id"],
138
+ ),
139
+ sa.UniqueConstraint("message_id"),
140
+ )
141
+ # CoreState tables
142
+ op.create_table(
143
+ "token_store",
144
+ sa.Column("run_id", sa.Integer(), nullable=True),
145
+ sa.Column("token", sa.String(), nullable=False),
146
+ sa.Column("active_until", sa.Float(), nullable=True),
147
+ sa.PrimaryKeyConstraint("run_id"),
148
+ sa.UniqueConstraint("token"),
149
+ )
150
+ # ObjectStore tables
151
+ op.create_table(
152
+ "objects",
153
+ sa.Column("object_id", sa.String(), nullable=True),
154
+ sa.Column("content", sa.LargeBinary(), nullable=True),
155
+ sa.Column("is_available", sa.Integer(), server_default="0", nullable=False),
156
+ sa.Column("ref_count", sa.Integer(), server_default="0", nullable=False),
157
+ sa.CheckConstraint("is_available IN (0, 1)", name="ck_objects_is_available"),
158
+ sa.PrimaryKeyConstraint("object_id"),
159
+ )
160
+ op.create_table(
161
+ "object_children",
162
+ sa.Column("parent_id", sa.String(), nullable=False),
163
+ sa.Column("child_id", sa.String(), nullable=False),
164
+ sa.ForeignKeyConstraint(
165
+ ["child_id"], ["objects.object_id"], ondelete="CASCADE"
166
+ ),
167
+ sa.ForeignKeyConstraint(
168
+ ["parent_id"], ["objects.object_id"], ondelete="CASCADE"
169
+ ),
170
+ sa.PrimaryKeyConstraint("parent_id", "child_id"),
171
+ )
172
+ op.create_table(
173
+ "run_objects",
174
+ sa.Column("run_id", sa.Integer(), nullable=False),
175
+ sa.Column("object_id", sa.String(), nullable=False),
176
+ sa.ForeignKeyConstraint(
177
+ ["object_id"], ["objects.object_id"], ondelete="CASCADE"
178
+ ),
179
+ sa.PrimaryKeyConstraint("run_id", "object_id"),
180
+ )
181
+ # ### end Alembic commands ###
182
+
183
+
184
+ def downgrade() -> None:
185
+ """Downgrade schema."""
186
+ # ### commands auto generated by Alembic - please adjust! ###
187
+ op.drop_table("run_objects")
188
+ op.drop_table("object_children")
189
+ op.drop_table("objects")
190
+ op.drop_table("token_store")
191
+ op.drop_table("message_res")
192
+ op.drop_table("message_ins")
193
+ op.drop_table("context")
194
+ op.drop_table("logs")
195
+ op.drop_table("run")
196
+ op.drop_index("idx_online_until", table_name="node")
197
+ op.drop_index("idx_node_status", table_name="node")
198
+ op.drop_index("idx_node_owner_aid", table_name="node")
199
+ op.drop_table("node")
200
+ # ### end Alembic commands ###
flwr/supercore/utils.py CHANGED
@@ -254,3 +254,24 @@ def humanize_bytes(num_bytes: int) -> str:
254
254
  value /= 1024
255
255
 
256
256
  raise RuntimeError("Unreachable code") # Make mypy happy
257
+
258
+
259
+ def check_federation_format(federation: str) -> None:
260
+ """Check if the federation string is valid.
261
+
262
+ Parameters
263
+ ----------
264
+ federation : str
265
+ The federation string to check.
266
+
267
+ Raises
268
+ ------
269
+ ValueError
270
+ If the federation string is not valid. The expected
271
+ format is '@<account-name>/<federation-name>'.
272
+ """
273
+ if not re.match(r"^@[a-zA-Z0-9\-_]+/[a-zA-Z0-9\-_]+$", federation):
274
+ raise ValueError(
275
+ f"Invalid federation format: {federation}. "
276
+ f"Expected format: '@<account-name>/<federation-name>'."
277
+ )
@@ -15,8 +15,9 @@
15
15
  """NoOp implementation of FederationManager."""
16
16
 
17
17
 
18
- from flwr.common.constant import NOOP_FLWR_AID
18
+ from flwr.common.constant import NOOP_ACCOUNT_NAME, NOOP_FLWR_AID
19
19
  from flwr.common.typing import Federation
20
+ from flwr.proto.federation_pb2 import Account # pylint: disable=E0611
20
21
  from flwr.supercore.constant import NOOP_FEDERATION
21
22
 
22
23
  from .federation_manager import FederationManager
@@ -66,6 +67,7 @@ class NoOpFederationManager(FederationManager):
66
67
  return Federation(
67
68
  name=NOOP_FEDERATION,
68
69
  member_aids=[NOOP_FLWR_AID],
70
+ accounts=[Account(id=NOOP_FLWR_AID, name=NOOP_ACCOUNT_NAME)],
69
71
  nodes=nodes,
70
72
  runs=runs,
71
73
  )
@@ -65,7 +65,7 @@ from flwr.common.telemetry import EventType
65
65
  from flwr.common.typing import Fab, Run, RunNotRunningException
66
66
  from flwr.proto.clientappio_pb2_grpc import add_ClientAppIoServicer_to_server
67
67
  from flwr.proto.message_pb2 import ObjectTree # pylint: disable=E0611
68
- from flwr.supercore.address import parse_address
68
+ from flwr.supercore.address import parse_address, resolve_bind_address
69
69
  from flwr.supercore.ffs import Ffs, FfsFactory
70
70
  from flwr.supercore.grpc_health import run_health_server_grpc_no_tls
71
71
  from flwr.supercore.object_store import ObjectStore, ObjectStoreFactory
@@ -213,7 +213,10 @@ def start_client_internal(
213
213
  # Launch the SuperExec if the isolation mode is `subprocess`
214
214
  if isolation == ISOLATION_MODE_SUBPROCESS:
215
215
  command = ["flower-superexec", "--insecure"]
216
- command += ["--appio-api-address", clientappio_api_address]
216
+ command += [
217
+ "--appio-api-address",
218
+ resolve_bind_address(clientappio_api_address),
219
+ ]
217
220
  command += ["--plugin-type", ExecPluginType.CLIENT_APP]
218
221
  command += ["--parent-pid", str(os.getpid())]
219
222
  # pylint: disable-next=consider-using-with
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: flwr-nightly
3
- Version: 1.26.0.dev20260127
3
+ Version: 1.26.0.dev20260128
4
4
  Summary: Flower: A Friendly Federated AI Framework
5
5
  License: Apache-2.0
6
6
  Keywords: Artificial Intelligence,Federated AI,Federated Analytics,Federated Evaluation,Federated Learning,Flower,Machine Learning
@@ -31,6 +31,7 @@ Classifier: Typing :: Typed
31
31
  Provides-Extra: rest
32
32
  Provides-Extra: simulation
33
33
  Requires-Dist: SQLAlchemy (>=2.0.45,<3.0.0)
34
+ Requires-Dist: alembic (>=1.18.1,<2.0.0)
34
35
  Requires-Dist: click (<8.2.0)
35
36
  Requires-Dist: cryptography (>=44.0.1,<45.0.0)
36
37
  Requires-Dist: grpcio (>=1.70.0,<2.0.0)
@@ -17,19 +17,19 @@ flwr/cli/auth_plugin/oidc_cli_plugin.py,sha256=1gN00FifSrhUA5s5uaE_GpQ3N7U8fQec4
17
17
  flwr/cli/build.py,sha256=wjLrxWHGnwRFp7WxZFgm8J0cwcdQY9-QHxjFnEa2dII,10097
18
18
  flwr/cli/cli_account_auth_interceptor.py,sha256=mXgxThpZjU_2Xlae9xT8ewOw60GeE64comDd57asLIY,3680
19
19
  flwr/cli/config/__init__.py,sha256=46z6whA3hvKkl9APRs-UG7Ym3K9VOqKx_pYcgelRjtE,788
20
- flwr/cli/config/ls.py,sha256=llQiXC0kK7shT96Y3GD2uvVP3MjWWLpYTeExUH1rALo,3537
20
+ flwr/cli/config/ls.py,sha256=i976g1rzpSLEagyjEFdxxYAM-FqaP0Unqlk2EiKj4B4,3549
21
21
  flwr/cli/config_migration.py,sha256=KJIVqpVbEfhY53taELFZuVGY0-sTPGiPOI6_FwEjfDI,10932
22
22
  flwr/cli/config_utils.py,sha256=t9GNUPnUH1nUNZQ3oGjx6Jd5VSO9SlnQXjz3-yro7Rs,7683
23
- flwr/cli/constant.py,sha256=uf4Boxj4A90NWP89cCSa542ijYSbU-9aGIzyBoEw0F4,3537
23
+ flwr/cli/constant.py,sha256=N50fATyqwwFuy8Ty7nl4zvKOQtmdSjfoJH4bHggb6BU,3501
24
24
  flwr/cli/example.py,sha256=SNTorkKPrx1rOryGREUyZu8TcOc1-vFv1zEddaysdY0,2216
25
25
  flwr/cli/federation/__init__.py,sha256=okxswL4fAjApI9gV_alU1lRkTUcQRbwlzvtUTLz61fE,793
26
- flwr/cli/federation/ls.py,sha256=xjS3HHNwOXXsaNN-xrdWvClmV29R6yT8R2AA0SVzJas,10689
26
+ flwr/cli/federation/ls.py,sha256=SZrQb_uUBrK2v4nAo3GETxyVBoNpBUJLjQuE_maX8_E,10737
27
27
  flwr/cli/flower_config.py,sha256=EFsSJByBD-SmIReoBaSzuEriLz4716qh7LMU2_yfGzc,15065
28
28
  flwr/cli/install.py,sha256=gBYg1SczpvBh8MMrXJxi0q4DUdzXCdvapK3jC3CBlPk,9114
29
29
  flwr/cli/log.py,sha256=BPA0dvGlXx5PrtURPua5fJyF7iVrb28K4fEY2Uth0EE,7317
30
30
  flwr/cli/login/__init__.py,sha256=B1SXKU3HCQhWfFDMJhlC7FOl8UsvH4mxysxeBnrfyUE,800
31
31
  flwr/cli/login/login.py,sha256=o-jm5QRMIpivE8kpY7cPTGbr7nQhsyFYaftMtBaaT0s,3649
32
- flwr/cli/ls.py,sha256=ROSDyaQw5y9BjcBUtj57Vc3HgHTW_JbYX5NDIl-Yfhg,12793
32
+ flwr/cli/ls.py,sha256=7CUtSdqnEuAkAgN-7u9c8TBOoM-IYms0nh0h1SkLz7I,12782
33
33
  flwr/cli/new/__init__.py,sha256=QA1E2QtzPvFCjLTUHnFnJbufuFiGyT_0Y53Wpbvg1F0,790
34
34
  flwr/cli/new/new.py,sha256=15phs5dhEpo7ORs7uulGzHWtW9W5ZGn0EKFE_T5WXb8,7829
35
35
  flwr/cli/pull.py,sha256=XzBvLrdYGrSCuky5OoCEU4V9UR9QlyZMT7Tj-W6wSw0,2946
@@ -38,10 +38,10 @@ flwr/cli/run/run.py,sha256=6yrVljd1CqqALq-wqiv0TnZlveiN5ymiUI5iTjuvUJM,8954
38
38
  flwr/cli/run_utils.py,sha256=ZnlBOqFfRxVEPZKR_9zYrynNcohiCFY8QN6OFyOCrQs,5082
39
39
  flwr/cli/stop.py,sha256=w3frNt3TD5AukGmlGq4CbWo-3PC3B-YSLO6PjJeHjH4,4692
40
40
  flwr/cli/supernode/__init__.py,sha256=DBkjoPo2hS2Y-ghJxwLbrAbCQFBgD82_Itl2_892UBo,917
41
- flwr/cli/supernode/ls.py,sha256=x4ghJHpqcpDGuZx91ZehzSdmrUJ8ZuZvpY_UybnP63U,8224
41
+ flwr/cli/supernode/ls.py,sha256=o3YENA1e-W4oJjq2rHwwzZthllpLzEhSY--9-m_c6Qc,8236
42
42
  flwr/cli/supernode/register.py,sha256=YPf28ZDvqPAQAt6c6OG73AJa9zZPONKgpxhnnb3NWQk,5498
43
43
  flwr/cli/supernode/unregister.py,sha256=qWGg8G_KqF300ze4D1dYaSIRJZkl9zwM2ul8VLYjJMY,3719
44
- flwr/cli/typing.py,sha256=N0Q3ylAOLryV5fsojaj2WuVo2xMzA4NpWXtkqdE97Hw,7174
44
+ flwr/cli/typing.py,sha256=COLEK_zVQuGGm4t_nYAW0_8EWu09zFiAv-tEZNg95_s,7367
45
45
  flwr/cli/utils.py,sha256=HCQYvqAjO74awSWQ9dV_OfCZ9QSkMIoKQ5b0Y4EOeoo,15568
46
46
  flwr/client/__init__.py,sha256=xwkPJfdeWxIIfmiPE5vnmnY_JbTlErP0Qs9eBP6qRFg,1252
47
47
  flwr/client/client.py,sha256=3HAchxvknKG9jYbB7swNyDj-e5vUWDuMKoLvbT7jCVM,7895
@@ -86,7 +86,7 @@ flwr/common/event_log_plugin/__init__.py,sha256=ts3VAL3Fk6Grp1EK_1Qg_V-BfOof9F86
86
86
  flwr/common/event_log_plugin/event_log_plugin.py,sha256=uJYR363nVzjfJePay3UHwaifwgBx2rAbKHHAuQC10VU,2014
87
87
  flwr/common/exit/__init__.py,sha256=8W7xaO1iw0vacgmQW7FTFbSh7csNv6XfsgIlnIbNF6U,978
88
88
  flwr/common/exit/exit.py,sha256=EBHZos4EhZLxW1lmZXDVlFUOf0_eTQ1FxzQiZ466hZ0,4018
89
- flwr/common/exit/exit_code.py,sha256=1S_LJNdo2lai3tjAYgZgNjN5jcIyU60Y1a-IDfUZVAM,6702
89
+ flwr/common/exit/exit_code.py,sha256=xpvi_3UOhf6AT8pR0ZGXyBxWfnuFFE50O1cKqOGVxxE,7000
90
90
  flwr/common/exit/exit_handler.py,sha256=zUAXHtp4rYK_sbkgsqS5zrVAzJUiSBrB-a4Ay3vSPE8,2302
91
91
  flwr/common/exit/signal_handler.py,sha256=o_e33v9kP0jz13q-8BZDrKF7vNCzOApvDPZ3-nTXfIc,3697
92
92
  flwr/common/grpc.py,sha256=8XoMTA5rJMze8fG6vOs1g4_rkxMhfpvMEYJPnqYPi2g,9799
@@ -119,7 +119,7 @@ flwr/common/secure_aggregation/secaggplus_utils.py,sha256=E_xU-Zd45daO1em7M6C2wO
119
119
  flwr/common/serde.py,sha256=FqqJqNqCk-DryCOgTGkabENvFeU2Q0LieLe0bt3qBbU,22342
120
120
  flwr/common/serde_utils.py,sha256=Vk49PnZWRgo0NrvBhFqH-Li7sMFVVgyrs3Ck7kHB-ZE,5820
121
121
  flwr/common/telemetry.py,sha256=7HNy5ytioJ3SbFtaKNucwYYC-IXEOXphitZbdHmSlfk,8924
122
- flwr/common/typing.py,sha256=pUhyfCA6vbApelxxDnUti5Yj_oBZv1xuuzyU8vhwqaw,7295
122
+ flwr/common/typing.py,sha256=LLL-4wB8LaijpiVCIJQBv-PI8SXM4RNjbClu3BHWQh0,7394
123
123
  flwr/compat/__init__.py,sha256=gbfDQKKKMZzi3GswyVRgyLdDlHiWj3wU6dg7y6m5O_s,752
124
124
  flwr/compat/client/__init__.py,sha256=qpbo0lcxdNL4qy5KHqiGm8OLxSxkYgI_-dLh5rwhtcI,746
125
125
  flwr/compat/client/app.py,sha256=oBOB-gYm0oqxtfAp58QZtv3dyUsPxjFm__f1_teenHU,27178
@@ -151,8 +151,8 @@ flwr/proto/fab_pb2.py,sha256=QDkVHGRGmugjQQUiN8A_okppZ5e2pJi5KipjnWmF6e4,2320
151
151
  flwr/proto/fab_pb2.pyi,sha256=PUUZ1FHeTwXxjVmTX14Vx5gmgRSFvoW_e5v_Z5qkT54,3948
152
152
  flwr/proto/fab_pb2_grpc.py,sha256=jmhCnnwSqBpZ0329XGUGARbcz5sHrr0WrXwccqzc3Vs,895
153
153
  flwr/proto/fab_pb2_grpc.pyi,sha256=fMwjLr-QCljWBa8uk0fvMCytnrF1-IzFLCbezqEmvdU,1071
154
- flwr/proto/federation_pb2.py,sha256=x8hwIFsw87e7STmvq5qL90RmcImCGw5oOqbke7xaykA,1670
155
- flwr/proto/federation_pb2.pyi,sha256=_At18SzwJjlf3k3FXjVi3Op59rs2i5XcIeBSPlvfXr0,2215
154
+ flwr/proto/federation_pb2.py,sha256=1IAerSPg5O3DDx1Nixc9PL-0ELK4C_yjeCa3IpHLCjE,1927
155
+ flwr/proto/federation_pb2.pyi,sha256=EMPXcT0b6a6rs8oB-rHp-5dIQ_83aBDd7fTmamVzy60,2970
156
156
  flwr/proto/federation_pb2_grpc.py,sha256=HPErt9uXJPbY8jLtTNFr941z82uMm7cOx10hTHgcwZM,902
157
157
  flwr/proto/federation_pb2_grpc.pyi,sha256=fMwjLr-QCljWBa8uk0fvMCytnrF1-IzFLCbezqEmvdU,1071
158
158
  flwr/proto/fleet_pb2.py,sha256=HnxtM2pqVdn2xhl3e1NlLazpSMaGoUUEG9Nd17egxqU,6543
@@ -201,7 +201,7 @@ flwr/proto/transport_pb2_grpc.py,sha256=jYsbV3KYdp4TaNfWxv3ljFEvB-Yjsa8MIPtTH-vV
201
201
  flwr/proto/transport_pb2_grpc.pyi,sha256=qd2cOXq6HBZtwRfPpKuni27NGMJCAxyP5R95Vk8WWDU,2073
202
202
  flwr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
203
203
  flwr/server/__init__.py,sha256=LQQHiuL2jy7TpNaKastRdGsexlxSt5ZWAQNVqitDnrY,1598
204
- flwr/server/app.py,sha256=Z9JRCp8S4YIGr79r1LM2wXsuoNoxt4K7dKnletzxJU0,31827
204
+ flwr/server/app.py,sha256=90rGFbhQw0vDrVDamqBVIm0RtUDfUUdUcXc9WpU7BKI,31678
205
205
  flwr/server/client_manager.py,sha256=3WOqNWh5dlqPRG-DMGwXiyvGNEYS_jKqXKkFIkMqWLQ,6187
206
206
  flwr/server/client_proxy.py,sha256=QS_RGgjSZ8V4StAHH8zc7W9_os2FyO4wNqkbylQMBTY,2341
207
207
  flwr/server/compat/__init__.py,sha256=0IsttWvY15qO98_1GyzVC-vR1e_ZPXOdu2qUlOkYMPE,886
@@ -324,11 +324,11 @@ flwr/simulation/ray_transport/utils.py,sha256=KrexpWYCF-dAF3UHc9yDbPQWO-ahMT-BbD
324
324
  flwr/simulation/run_simulation.py,sha256=hSJAqOfMd8I30Pq9x2TXYXDCXAeUtWqwBncX6JGEMOQ,20798
325
325
  flwr/simulation/simulationio_connection.py,sha256=cS7yTOa5yeK-bQ2YjS9eJCXL8SOItEkiJD5sN5mBb_Y,3475
326
326
  flwr/supercore/__init__.py,sha256=pqkFoow_E6UhbBlhmoD1gmTH-33yJRhBsIZqxRPFZ7U,755
327
- flwr/supercore/address.py,sha256=vVE5y-t77YL16lMDVTeK3g75N8IKbQ72sVcRI-RPPWs,2984
327
+ flwr/supercore/address.py,sha256=SgM_pazKsw1Avo5nms0WWgIZFUGS349O0gOP7N3bFW4,3318
328
328
  flwr/supercore/app_utils.py,sha256=P558DOr5a4UPyOdRCxbDQueQF-L-kSc6qzouPPuqj0U,1773
329
329
  flwr/supercore/cli/__init__.py,sha256=EDl2aO-fuQfxSbL-T1W9RAfA2N0hpWHmqX_GSwblJbQ,845
330
330
  flwr/supercore/cli/flower_superexec.py,sha256=IQIGzxgaeLNMNzGXGemfYK3lp8God5bTkXpVkbeP_ig,6109
331
- flwr/supercore/constant.py,sha256=qMeN0YGu_aZ95XKlLc7n4WBl_AVjA9vv_1666gGhCq8,2792
331
+ flwr/supercore/constant.py,sha256=ZtS-CpGSyth9iKjavNOLE7T-8xmCaEVoXi4ZarYry5s,2884
332
332
  flwr/supercore/corestate/__init__.py,sha256=Vau6-L_JG5QzNqtCTa9xCKGGljc09wY8avZmIjSJemg,774
333
333
  flwr/supercore/corestate/corestate.py,sha256=EZg4gPXqVOXwS7t0tlPfedajoWj5T80oeDBNxpV2y2I,2874
334
334
  flwr/supercore/corestate/in_memory_corestate.py,sha256=9qa6RuRZfCp6vs-ARYdiZjCL31VOAAxw0a_VkBXR5zY,5116
@@ -355,8 +355,14 @@ flwr/supercore/object_store/sql_object_store.py,sha256=ZBmyc3a_GXZFNaefDCxhrri-_
355
355
  flwr/supercore/primitives/__init__.py,sha256=Tx8GOjnmMo8Y74RsDGrMpfr-E0Nl8dcUDF784_ge6F8,745
356
356
  flwr/supercore/primitives/asymmetric.py,sha256=1643niHYj3uEbfPd06VuMHwN3tKVwg0uVyR3RhTdWIU,3778
357
357
  flwr/supercore/primitives/asymmetric_ed25519.py,sha256=eIhOTMibQW0FJX4MXdplHdL3HcfCiKuFu2mQ8GQTUz8,5025
358
- flwr/supercore/sql_mixin.py,sha256=eT4JHaLG4gShZrC10pkUVIGcTvZszlJI1ARhOJEA_WY,11727
358
+ flwr/supercore/sql_mixin.py,sha256=dAK_NCnXa0Ili0N5vYgQ9dlVOE-KeiQCRc2Z0RcS0sU,11765
359
359
  flwr/supercore/state/__init__.py,sha256=FkKhsNVM4LjlRlOgXTz6twINmw5ohIUKS_OER0BNo_w,724
360
+ flwr/supercore/state/alembic/__init__.py,sha256=pC-y-xsfgi9YwCrqnt814yOJ9ytlDRUFGqOY6Qzt-_U,746
361
+ flwr/supercore/state/alembic/env.py,sha256=fmRDLxsmJ77pC4LLDlpx3iBeBZXN2DFZ7LKhNTmGTIg,2739
362
+ flwr/supercore/state/alembic/script.py.mako,sha256=oNrcIBPuSq0jfg3ycPAg3vo18QhA-_OaGMTkKCix74I,1416
363
+ flwr/supercore/state/alembic/utils.py,sha256=6KpFEgR4_oNjq8KmNXuq-3ilOfNbzqlCw80NbBgjn9I,1813
364
+ flwr/supercore/state/alembic/versions/__init__.py,sha256=ri3OIpl8xD_Csv25uAAT7l-MtHhixqmAGt9Aa1OpM1s,717
365
+ flwr/supercore/state/alembic/versions/rev_2026_01_28_initialize_migration_of_state_tables.py,sha256=U4j4PdRUtIAZuWUGuLO7N8W_5Ixit1rfEqfcIp2SAmE,8407
360
366
  flwr/supercore/state/schema/README.md,sha256=qVCGuyU-gObFrL5MRvH9LraNi0Z_LbXzolXykbOrY9w,2884
361
367
  flwr/supercore/state/schema/__init__.py,sha256=Egnde6OY01wrpT4PuhL4NGn_NY4jdAH7kf7ktagN4Ws,724
362
368
  flwr/supercore/state/schema/corestate_tables.py,sha256=TwonogWgEUtKeE-LY59wousWzhF0w3wMKAotRqvRxao,1392
@@ -370,7 +376,7 @@ flwr/supercore/superexec/plugin/exec_plugin.py,sha256=iRRZcnTcM3wMwkkUElmfFka31a
370
376
  flwr/supercore/superexec/plugin/serverapp_exec_plugin.py,sha256=IwRzdPV-cSKwrP2krGh0De4IkAuxsmgK0WU6J-2GXqM,1035
371
377
  flwr/supercore/superexec/plugin/simulation_exec_plugin.py,sha256=upn5zE-YKkl_jTw8RzmeyQ58PU_UAlQ7CqnBXXdng8I,1060
372
378
  flwr/supercore/superexec/run_superexec.py,sha256=yeV6_eI0XFfPyNwnlgqchR9jI80FzSI80oyekrIlO2M,7144
373
- flwr/supercore/utils.py,sha256=WGHnvyZ9sC5fvFDR8rXnGyz3CQ1O-qSBMT0Alh1a090,7819
379
+ flwr/supercore/utils.py,sha256=2ywouz7y4M4NSdvZ_DMPYZX5xCpt5zIR5z5rEgl7znI,8410
374
380
  flwr/supercore/version.py,sha256=7GAGzPn73Mkh09qhrjbmjZQtcqVhBuzhFBaK4Mk4VRk,1325
375
381
  flwr/superlink/__init__.py,sha256=GNSuJ4-N6Z8wun2iZNlXqENt5beUyzC0Gi_tN396bbM,707
376
382
  flwr/superlink/artifact_provider/__init__.py,sha256=pgZEcVPKRE874LSu3cgy0HbwSJBIpVy_HxQOmne4PAs,810
@@ -380,7 +386,7 @@ flwr/superlink/auth_plugin/auth_plugin.py,sha256=xLqAYFG7sjAhYgyeTvvEY6fSMjPRq0o
380
386
  flwr/superlink/auth_plugin/noop_auth_plugin.py,sha256=0MWjNXZdz6zGRfitlnZfVKu4sKVn0HhJvnp09c3xVUA,2962
381
387
  flwr/superlink/federation/__init__.py,sha256=A1pFFvkEQrHzvRAOstr2o7nphx3JbuJgkNRFKJ9qD3M,890
382
388
  flwr/superlink/federation/federation_manager.py,sha256=-Vu1v_WWhWK7wgKEQua24lw9a6iPxG89VI-jqLO5E4g,2322
383
- flwr/superlink/federation/noop_federation_manager.py,sha256=Hc6_YVlMZZkdElS8bawi85eCTHOzVPUZVe9Dmrl2DW8,2881
389
+ flwr/superlink/federation/noop_federation_manager.py,sha256=YqAfmbxUPzFrivZNAheJtDEpUjTLaxPi-k7tuZTAvoU,3045
384
390
  flwr/superlink/servicer/__init__.py,sha256=ZC-kILcUGeh6IxJsfu24cTzUqIGXmQfEKsGfhsnhBpM,717
385
391
  flwr/superlink/servicer/control/__init__.py,sha256=qhUTMt_Mg4lxslCJYn5hDSrA-lXf5ya3617BT8kR-2Y,803
386
392
  flwr/superlink/servicer/control/control_account_auth_interceptor.py,sha256=AJs7GE-fyUBLcUEUzB058TpFzC7gyDOL17THvYkJTn8,6529
@@ -401,8 +407,8 @@ flwr/supernode/runtime/run_clientapp.py,sha256=-JsN3h7jQC12vzDBmImzYmyccI3LveIu-
401
407
  flwr/supernode/servicer/__init__.py,sha256=lucTzre5WPK7G1YLCfaqg3rbFWdNSb7ZTt-ca8gxdEo,717
402
408
  flwr/supernode/servicer/clientappio/__init__.py,sha256=7Oy62Y_oijqF7Dxi6tpcUQyOpLc_QpIRZ83NvwmB0Yg,813
403
409
  flwr/supernode/servicer/clientappio/clientappio_servicer.py,sha256=rRL4CQ0L78jF_p0ct4-JMGREt6wWRy__wy4czF4f54Y,11639
404
- flwr/supernode/start_client_internal.py,sha256=BYk69UBQ2gQJaDQxXhccUgfOWrb7ShAstrbcMOCZIIs,26173
405
- flwr_nightly-1.26.0.dev20260127.dist-info/METADATA,sha256=RhGNTW1fpVATXZ-ajG9LFAL6O3lXMfWkJvvye6jVGN8,14398
406
- flwr_nightly-1.26.0.dev20260127.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
407
- flwr_nightly-1.26.0.dev20260127.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
408
- flwr_nightly-1.26.0.dev20260127.dist-info/RECORD,,
410
+ flwr/supernode/start_client_internal.py,sha256=AUzEUFJbKRvyJbOjE6b6kShJrd4aJkqqOZgCokY67_A,26252
411
+ flwr_nightly-1.26.0.dev20260128.dist-info/METADATA,sha256=vwyjsIU9OBUX7zRbFGbUOqciYyerGlRDba7iSXcEfOM,14439
412
+ flwr_nightly-1.26.0.dev20260128.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
413
+ flwr_nightly-1.26.0.dev20260128.dist-info/entry_points.txt,sha256=hxHD2ixb_vJFDOlZV-zB4Ao32_BQlL34ftsDh1GXv14,420
414
+ flwr_nightly-1.26.0.dev20260128.dist-info/RECORD,,