diff --git a/docs/messaging/slim-authentication.md b/docs/messaging/slim-authentication.md index f4dd90a6..297766f9 100644 --- a/docs/messaging/slim-authentication.md +++ b/docs/messaging/slim-authentication.md @@ -12,7 +12,7 @@ SLIM supports JWT (JSON Web Tokens) for identity management. Tokens can come from an external identity provider or can be generated by the SLIM nodes directly if you provide the necessary private key for signing the tokens and public key for verification. Check the [Identity -Test](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/tests/test_identity.py) +Test](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/tests/test_identity.py) for an example of how to use JWT tokens with SLIM if you have your own keys. If you are running your SLIM clients in a Kubernetes environment, using @@ -41,7 +41,7 @@ This section shows how to use SPIRE with SLIM to manage client identities. The f If you already have a Kubernetes cluster or an existing SPIRE deployment, you can adapt only the relevant subsections. -This tutorial is based on the [SLIM examples](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples). +This tutorial is based on the [SLIM examples](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples). ### Prerequisites diff --git a/docs/messaging/slim-controller.md b/docs/messaging/slim-controller.md index 2e51d539..eb733ff9 100644 --- a/docs/messaging/slim-controller.md +++ b/docs/messaging/slim-controller.md @@ -187,8 +187,16 @@ Example config to enable MTLS on Southbound endpoint using [Spire](https://spiff logging: level: DEBUG + reconciler: - threads: 3 + # Max number of times a failed reconcile will be retried + maxRequeues: 15 + # Max number of reconciles that can be run in parallel for different nodes + maxNumOfParallelReconciles: 1000 + + # Specifies the SQLite database file path for storing control plane data + database: + filePath: controlplane.db spire: enabled: false @@ -299,7 +307,7 @@ tls: key_file: "/path/to/client.key" ``` -The `server` endpoint should point to a [SLIM Control](https://github.com/agntcy/slim/tree/slim-v0.6.0/control-plane/control-plane) endpoint which is a central service managing SLIM node configurations. +The `server` endpoint should point to a [SLIM Control](https://github.com/agntcy/slim/tree/slim-v0.7.0/control-plane/control-plane) endpoint which is a central service managing SLIM node configurations. ### Commands @@ -370,7 +378,7 @@ slimctl controller route add org/default/alice/0 via connection_config.json slimctl controller route del org/default/alice/0 via http://localhost:46367 ``` -For full reference of connection_config.json, see the [client-config-schema.json](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/core/config/src/grpc/schema/client-config.schema.json). +For full reference of connection_config.json, see the [client-config-schema.json](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/core/config/src/grpc/schema/client-config.schema.json). ### Managing SLIM Nodes Directly diff --git a/docs/messaging/slim-group-tutorial.md b/docs/messaging/slim-group-tutorial.md index ce35cf4a..4f1bcb7c 100644 --- a/docs/messaging/slim-group-tutorial.md +++ b/docs/messaging/slim-group-tutorial.md @@ -6,7 +6,7 @@ participants. Messages are sent to a shared channel where every member can read and write. All messages are end-to-end encrypted using the [MLS protocol](https://datatracker.ietf.org/doc/html/rfc9420). This tutorial is based on the -[group.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) +[group.py](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) example in the SLIM repo. ## Key Features @@ -26,38 +26,31 @@ Every participant in a group requires a unique identity for authentication and f ### Identity -Each participant must have a unique identity. This is required to set up end-to-end encryption using the MLS protocol. The identity can be a JWT or a shared secret. For simplicity, this example uses a shared secret. For JWT-based identity, see the [tutorial](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/bindings/examples#running-in-kubernetes-spire--jwt) in the SLIM repository. +Each participant must have a unique identity. This is required to set up end-to-end encryption using the MLS protocol. The identity can be a JWT or shared secret. For simplicity, this example uses a shared secret. For JWT-based identity, see the [tutorial](https://github.com/agntcy/slim/tree/slim-v0.7.0/data-plane/python/bindings/examples#running-in-kubernetes-spire--jwt) in the SLIM repository. -The Python objects managing the identity are called `PyIdentityProvider` and `PyIdentityVerifier`. The `PyIdentityProvider` provides the identity, while the `PyIdentityVerifier` verifies it: +The Python objects managing the identity are called `IdentityProvider` and `IdentityVerifier`. The `IdentityProvider` provides the identity, while the `IdentityVerifier` verifies it: ```python def shared_secret_identity(identity: str, secret: str): """ Create a provider & verifier pair for shared-secret (symmetric) authentication. - - Args: - identity: Logical identity string (often same as PyName string form). - secret: Shared secret used to sign / verify tokens (not for production). - - Returns: - (provider, verifier): Tuple of PyIdentityProvider & PyIdentityVerifier. """ - provider = slim_bindings.PyIdentityProvider.SharedSecret( # type: ignore + provider = slim_bindings.IdentityProvider.SharedSecret( identity=identity, shared_secret=secret ) - verifier = slim_bindings.PyIdentityVerifier.SharedSecret( # type: ignore + verifier = slim_bindings.IdentityVerifier.SharedSecret( identity=identity, shared_secret=secret ) return provider, verifier ``` This is a helper function defined in -[common.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/common.py#L85) -that can be used to create a `PyIdentityProvider` and `PyIdentityVerifier` from two input strings. +[common.py](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/common.py#L85) +that can be used to create a `IdentityProvider` and `IdentityVerifier` from two input strings. ### SLIM App -The provider and verifier are used to create a local SLIM application that can exchange messages with other participants via the SLIM network. To create the SLIM app, use the helper function defined in [common.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/common.py#L289): +The provider and verifier are used to create a local SLIM application that can exchange messages with other participants via the SLIM network. To create the SLIM app, use the helper function defined in [common.py](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/common.py#L289): ```python async def create_local_app( @@ -65,10 +58,13 @@ async def create_local_app( slim: dict, remote: str | None = None, enable_opentelemetry: bool = False, - shared_secret: str = "secret", + shared_secret: str = "abcde-12345-fedcb-67890-deadc", jwt: str | None = None, spire_trust_bundle: str | None = None, audience: list[str] | None = None, + spire_socket_path: str | None = None, + spire_target_spiffe_id: str | None = None, + spire_jwt_audience: list[str] | None = None, ): """ Build and connect a Slim application instance given user CLI parameters. @@ -111,21 +107,26 @@ async def create_local_app( spire_trust_bundle, aud=audience, ) - else: - print( - "Warning: Falling back to shared-secret authentication. Don't use this in production!" + elif spire_socket_path or spire_target_spiffe_id or spire_jwt_audience: + print("Using SPIRE dynamic identity authentication.") + provider, verifier = spire_identity( + socket_path=spire_socket_path, + target_spiffe_id=spire_target_spiffe_id, + jwt_audiences=spire_jwt_audience, ) - # Fall back to shared secret (dev-friendly default). + else: + print("Using shared-secret authentication.") + # Fall back to shared secret. provider, verifier = shared_secret_identity( identity=local, secret=shared_secret, ) - # Convert local identifier to a strongly typed PyName. + # Convert local identifier to a strongly typed Name. local_name = split_id(local) - # Instantiate Slim (async constructor prepares underlying PyService). - local_app = await slim_bindings.Slim.new(local_name, provider, verifier) + # Instantiate Slim (async constructor prepares underlying Service). + local_app = slim_bindings.Slim(local_name, provider, verifier) # Provide feedback to user (instance numeric id). format_message_print(f"{local_app.id_str}", "Created app") @@ -145,12 +146,12 @@ This function takes several parameters as input: `org/ns/service`. - `slim` (required, dict): Configuration to connect to the remote SLIM node. For example: - ```python +```python { - "endpoint": "http://127.0.0.1:46357", - "tls": {"insecure": True}, + "endpoint": "http://127.0.0.1:46357", + "tls": {"insecure": True}, } - ``` + ``` - `enable_opentelemetry` (bool, default: `False`): Enable OpenTelemetry tracing. If `True`, traces are sent to `http://localhost:4317` by default. @@ -159,26 +160,42 @@ This function takes several parameters as input: - `jwt` (str | None, default: `None`): JWT token for identity. Used with `spire_trust_bundle` and `audience` for JWT-based authentication. - `spire_trust_bundle` (str | None, default: `None`): JWT trust bundle - (list of JWKs, one for each trust domain). It is expected in JSON format such as: + (list of JWKs, one for each trust domain). Expected in JSON format such as: - ```json + ```json { "trust-domain-1.org": "base-64-encoded-jwks", "trust-domain-2.org": "base-64-encoded-jwks", ... } - ``` + ``` - `audience` (list[str] | None, default: `None`): List of allowed audiences for JWT authentication. +- `spire_socket_path` (str | None, default: `None`): Path to the SPIRE agent + socket for dynamic identity authentication. When provided along with + `spire_target_spiffe_id` and `spire_jwt_audience`, enables SPIRE-based + authentication. +- `spire_target_spiffe_id` (str | None, default: `None`): The SPIFFE ID of the + target service for SPIRE authentication. Used in conjunction with + `spire_socket_path` and `spire_jwt_audience`. +- `spire_jwt_audience` (list[str] | None, default: `None`): List of audiences + for SPIRE JWT authentication. Required when using SPIRE dynamic identity + authentication along with `spire_socket_path` and `spire_target_spiffe_id`. -If `jwt`, `spire_trust_bundle`, and `audience` are not provided, `shared_secret` must be set (only recommended for local testing or examples, not production). In this example, we use the shared secret option, but the same function supports all authentication flows. +The function supports three authentication flows with the following precedence: + +1. JWT/JWKS authentication (if `jwt`, `spire_trust_bundle`, and `audience` are provided) +2. SPIRE dynamic identity (if `spire_socket_path`, `spire_target_spiffe_id`, and `spire_jwt_audience` are provided) +3. Shared secret authentication (fallback, only recommended for local testing or examples, not production) + +In this example, we use the shared secret option. ## Group Communication Using the Python Bindings Now that you know how to set up a SLIM application, we can see how to create a group where multiple participants can exchange messages. We start by showing how to create a group session using the Python bindings. -In this setting, one participant acts as moderator: it creates the group session and invites participants by sending invitation control messages. A detailed description of group sessions and the invitation process is available [here](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/SESSION.md). +In this setting, one participant acts as moderator: it creates the group session and invites participants by sending invitation control messages. A detailed description of group sessions and the invitation process is available [here](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/SESSION.md). ### Creating the Group Session and Inviting Members @@ -196,77 +213,83 @@ end-to-end encrypted, providing confidentiality and integrity for the group communication. ```python - # Create & connect the local Slim instance (auth derived from args). - local_app = await create_local_app( - local, - slim, - enable_opentelemetry=enable_opentelemetry, - shared_secret=shared_secret, - jwt=jwt, - spire_trust_bundle=spire_trust_bundle, - audience=audience, +# Create & connect the local Slim instance (auth derived from args). +local_app = await create_local_app( + local, + slim, + enable_opentelemetry=enable_opentelemetry, + shared_secret=shared_secret, + jwt=jwt, + spire_trust_bundle=spire_trust_bundle, + audience=audience, + spire_socket_path=spire_socket_path, + spire_target_spiffe_id=spire_target_spiffe_id, + spire_jwt_audience=spire_jwt_audience, +) + +# Parse the remote channel/topic if provided; else None triggers passive mode. +chat_channel = split_id(remote) if remote else None + +# Track background tasks (receiver loop + optional keyboard loop). +tasks: list[asyncio.Task] = [] + +# Session sharing between tasks +session_ready = asyncio.Event() +shared_session_container = [None] # Use list to make it mutable across functions + +# Session object only exists immediately if we are moderator. +created_session = None +if chat_channel and invites: + # We are the moderator; create the group session now. + format_message_print( + f"Creating new group session (moderator)... {split_id(local)}" + ) + config = slim_bindings.SessionConfiguration.Group( + max_retries=5, # Max per-message resend attempts upon missing ack before reporting a delivery failure. + timeout=datetime.timedelta( + seconds=5 + ), # Ack / delivery wait window; after this duration a retry is triggered (until max_retries). + mls_enabled=enable_mls, # Enable Messaging Layer Security for end-to-end encrypted & authenticated group communication. ) - # Parse the remote channel/topic if provided; else None triggers passive mode. - chat_channel = split_id(remote) if remote else None - - # Track background tasks (receiver loop + optional keyboard loop). - tasks: list[asyncio.Task] = [] - - # Session sharing between tasks - session_ready = asyncio.Event() - shared_session_container = [None] # Use list to make it mutable across functions - - # Session object only exists immediately if we are moderator. - created_session = None - if chat_channel and invites: - # We are the moderator; create the group session now. - format_message_print( - f"Creating new group session (moderator)... {split_id(local)}" - ) - created_session = await local_app.create_session( - slim_bindings.PySessionConfiguration.Group( # type: ignore # Build group session configuration - channel_name=chat_channel, # Logical group channel (PyName) all participants join; acts as group/topic identifier. - max_retries=5, # Max per-message resend attempts upon missing ack before reporting a delivery failure. - timeout=datetime.timedelta( - seconds=5 - ), # Ack / delivery wait window; after this duration a retry is triggered (until max_retries). - mls_enabled=enable_mls, # Enable Messaging Layer Security for end-to-end encrypted & authenticated group communication. - ) - ) + created_session, handle = await local_app.create_session( + chat_channel, # Logical group channel (Name) all participants join; acts as group/topic identifier. + config, # session configuration + ) - # Small delay so underlying routing / session creation stabilizes. - await asyncio.sleep(1) + await handle - # Invite each provided participant. Route is set before inviting to ensure - # outbound control messages can reach them. For more info see - # https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/SESSION.md#invite-a-new-participant - for invite in invites: - invite_name = split_id(invite) - await local_app.set_route(invite_name) - await created_session.invite(invite_name) - print(f"{local} -> add {invite_name} to the group") + # Invite each provided participant. Route is set before inviting to ensure + # outbound control messages can reach them. For more info, see + # https://github.com/agntcy/slim/blob/main/data-plane/python/bindings/SESSION.md#invite-a-new-participant + for invite in invites: + invite_name = split_id(invite) + await local_app.set_route(invite_name) + handle = await created_session.invite(invite_name) + await handle + print(f"{local} -> add {invite_name} to the group") ``` This code comes from the -[group.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) +[group.py](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) example. The local application is created using the helper function shown earlier. The channel name (the logical group topic) is produced via the -[split_id](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/common.py#L63) -helper by parsing the `remote` parameter. A new group session is then created -using `local_app.create_session(...)` with a -`slim_bindings.PySessionConfiguration.Group` configuration. The key parameters are the following: +[split_id](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/common.py#L63) +helper by parsing the `remote` parameter. + +A new group session is created by calling `local_app.create_session(...)` with two parameters: +the channel name and a `slim_bindings.SessionConfiguration.Group` configuration object. +The `create_session` call returns a tuple containing the session object and a handle that must be awaited. +The key configuration parameters to setup the `SessionConfiguration` are: -- `channel_name`: Logical channel/topic used to exchange messages among participants. - `max_retries`: Maximum number of retransmission attempts (upon missing ack) before notifying the application of delivery failure. -- `timeout`: Duration to wait for an acknowledgment; if the ack is not received in time a retry is triggered. If +- `timeout`: Duration to wait for an acknowledgment; if the ack is not received in time, a retry is triggered. If omitted / None, the session is unreliable (no retry/ack flow). - `mls_enabled`: Set to `True` to enable MLS for end-to-end encryption. -After the session creation, the moderator invites participants via `created_session.invite`. -Before sending each invitation it must call `local_app.set_route(invite_name)` so -SLIM knows how to deliver the control messages. +After the session creation, the moderator invites participants via `created_session.invite(invite_name)`. +Each `invite` call returns a handle that should be awaited to ensure the invitation completes. ### Implement Participants and Receive Messages @@ -300,7 +323,7 @@ async def receive_loop( try: # Await next inbound message from the group session. # The returned parameters are a message context and the raw payload bytes. - # Check session.py for details on PyMessageContext contents. + # Check session.py for details on MessageContext contents. ctx, payload = await session.get_message() print_formatted_text( f"{ctx.source_name} > {payload.decode()}", @@ -316,15 +339,14 @@ async def receive_loop( ``` Each non-moderator participant listens for an incoming session using -`local_app.listen_for_session()`. This returns a -[PySession](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/slim_bindings/session.py) -object containing metadata such as session ID, type, source name, and destination name. +`local_app.listen_for_session()`. This returns a session object containing metadata +such as session ID, type, source name, and destination name. The moderator already holds this information and therefore reuses the existing `created_session` (see `session = created_session`). Participants (including the moderator) then call `ctx, payload = await session.get_message()` to receive messages. `payload` contains the raw message bytes and `ctx` is a -[PyMessageContext](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/slim_bindings/_slim_bindings.pyi#L22) +[MessageContext](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/slim_bindings/session.py) with source, destination, message type, and metadata. ### Publish Messages to the Session @@ -337,7 +359,7 @@ async def keyboard_loop(session_ready, shared_session_container, local_app): Interactive loop allowing participants to publish messages. Typing 'exit' or 'quit' (case-insensitive) terminates the loop. - Each line is published to the group topic as UTF-8 bytes. + Each line is published to the group channel as UTF-8 bytes. """ try: # 1. Initialize an async session @@ -359,34 +381,40 @@ async def keyboard_loop(session_ready, shared_session_container, local_app): if user_input.lower() in ("exit", "quit"): # Also terminate the receive loop. - await local_app.delete_session(shared_session_container[0]) + handle = await local_app.delete_session(shared_session_container[0]) + await handle break - try: - # Send message to the channel_name specified when creating the session. - # As the session is group, all participants will receive it. - # calling publish_with_destination on a group session will raise an error. - await shared_session_container[0].publish(user_input.encode()) - except KeyboardInterrupt: - # Handle Ctrl+C gracefully - break - except Exception as e: - print_formatted_text(f"-> Error sending message: {e}") + # Send message to the channel_name specified when creating the session. + # As the session is group, all participants will receive it. + await shared_session_container[0].publish(user_input.encode()) + except KeyboardInterrupt: + # Handle Ctrl+C gracefully + pass except asyncio.CancelledError: # Handle task cancellation gracefully pass + except Exception as e: + print_formatted_text(f"-> Error sending message: {e}") ``` Messages are sent using `shared_session_container[0].publish(user_input.encode())`. Only the payload is provided and there is no explicit destination, because the -group channel was fixed at session creation and delivery fan-outs to all +group channel was fixed at session creation and delivery fans out to all participants. +When a user types 'exit' or 'quit', the application calls `local_app.delete_session()` +which returns a handle that must be awaited to ensure proper session cleanup before +terminating the loop. When the moderator closes the session, +all other participants are automatically notified, causing their receive loops to terminate +and their sessions to close gracefully. If the session closure is initiated by a participant, +only its local session is closed. + ### Run the Group Communication Example Now we will show how to run a new group session and how to enable group communication on top of SLIM. The full code can be found in -[group.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) +[group.py](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) in the SLIM repo. To run the example, follow the steps listed here: #### Run SLIM @@ -396,7 +424,7 @@ up a SLIM instance representing the SLIM network. We use the pre-built docker image for this purpose. First execute this command to create the SLIM configuration file. Details about -the [configuration](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/config) +the [configuration](https://github.com/agntcy/slim/tree/slim-v0.7.0/data-plane/config) can be found in the SLIM repo. ```bash @@ -436,7 +464,7 @@ You can run the SLIM instance using Docker: ```bash docker run -it \ -v ./config.yaml:/config.yaml -p 46357:46357 \ - ghcr.io/agntcy/slim:0.6.0 /slim --config /config.yaml + ghcr.io/agntcy/slim:0.7.0 /slim --config /config.yaml ``` If everything goes fine, you should see an output like this one: @@ -451,7 +479,7 @@ If everything goes fine, you should see an output like this one: #### Start the Participants -In this example we use two participants: `agntcy/ns/client-1` and `agntcy/ns/client-2`. +In this example, we use two participants: `agntcy/ns/client-1` and `agntcy/ns/client-2`. Authentication uses a shared secret. In the SLIM repository, go to the folder `slim/data-plane/python/bindings/examples` and run these commands in two different terminals: @@ -459,18 +487,18 @@ Authentication uses a shared secret. In the SLIM repository, go to the folder uv run --package slim-bindings-examples group \ --local agntcy/ns/client-1 \ --slim '{"endpoint": "http://localhost:46357", "tls": {"insecure": true}}' \ - --shared-secret "secret" + --shared-secret "very-long-shared-secret-value-0123456789abcdef" ``` ```bash uv run --package slim-bindings-examples group \ --local agntcy/ns/client-2 \ --slim '{"endpoint": "http://localhost:46357", "tls": {"insecure": true}}' \ - --shared-secret "secret" + --shared-secret "very-long-shared-secret-value-0123456789abcdef" ``` -This start two participants authenticated with a shared secret. +This starts two participants authenticated with a shared secret. The output of these commands should look like this: ```bash @@ -489,7 +517,7 @@ participants. In another terminal run: uv run --package slim-bindings-examples group \ --local agntcy/ns/moderator \ --slim '{"endpoint": "http://localhost:46357", "tls": {"insecure": true}}' \ - --shared-secret "secret" \ + --shared-secret "very-long-shared-secret-value-0123456789abcdef" \ --remote agntcy/ns/chat \ --invites agntcy/ns/client-1 \ --invites agntcy/ns/client-2 \ @@ -520,6 +548,8 @@ Send a message to the group, or type 'exit' or 'quit' to quit. At this point, you can write messages from any terminal and they will be received by all other group participants. +Writing 'exit' or 'quit' from the moderator will close all the applications. + ## Group Communication Using the SLIM Controller Previously, we saw how to run group communication using the Python bindings with an in-application moderator. @@ -533,10 +563,10 @@ Identity handling is unchanged between the two approaches; refer back to [SLIM I With the controller, you do not need to set up a moderator in your application. All participants can be run as we did for `client-1` and `client-2` in the previous examples. In code, this means you can avoid creating a new group session (using `local_app.create_session`) and the invitation loop. You only need to implement the `receive_loop` where the application waits for new sessions. This greatly simplifies your code. -### Run the Group Communication example +### Run the Group Communication Example Now we will show how to set up a group using the SLIM Controller. The reference code for the -application is still [group.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py). To run this example, follow the steps listed here. +application is still [group.py](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py). To run this example, follow the steps listed here. #### Run the SLIM Controller @@ -548,18 +578,23 @@ northbound: httpHost: 0.0.0.0 httpPort: 50051 logging: - level: DEBUG + level: INFO southbound: httpHost: 0.0.0.0 httpPort: 50052 + logging: + level: INFO -# number of node reconciler threads reconciler: - threads: 3 + maxRequeues: 15 + maxNumOfParallelReconciles: 1000 logging: level: INFO + +database: + filePath: /db/controlplane.db EOF ``` @@ -572,8 +607,8 @@ Start the controller with Docker: ```bash docker run -it \ - -v ./config-controller.yaml:/config.yaml -p 50051:50051 -p 50052:50052 \ - ghcr.io/agntcy/slim/control-plane:0.6.0 --config /config.yaml + -v ./config-controller.yaml:/config.yaml -v .:/db -p 50051:50051 -p 50052:50052 \ + ghcr.io/agntcy/slim/control-plane:0.7.0 --config /config.yaml ``` If everything goes fine, you should see an output like this: @@ -624,7 +659,8 @@ services: tls: insecure: true token_provider: - shared_secret: "secret" + type: shared_secret + data: "very-long-shared-secret-value-0123456789abcdef" EOF ``` @@ -633,7 +669,7 @@ This starts a SLIM node that connects to the controller: ```bash docker run -it \ -v ./config-slim.yaml:/config.yaml -p 46357:46357 \ - ghcr.io/agntcy/slim:0.6.0 /slim --config /config.yaml + ghcr.io/agntcy/slim:0.7.0 /slim --config /config.yaml ``` If everything goes fine, you should see an output like this one: @@ -667,21 +703,21 @@ Because the controller manages the group lifecycle, no participant needs to be d uv run --package slim-bindings-examples group \ --local agntcy/ns/client-1 \ --slim '{"endpoint": "http://localhost:46357", "tls": {"insecure": true}}' \ - --shared-secret "secret" + --shared-secret "very-long-shared-secret-value-0123456789abcdef" ``` ```bash uv run --package slim-bindings-examples group \ --local agntcy/ns/client-2 \ --slim '{"endpoint": "http://localhost:46357", "tls": {"insecure": true}}' \ - --shared-secret "secret" + --shared-secret "very-long-shared-secret-value-0123456789abcdef" ``` ```bash uv run --package slim-bindings-examples group \ --local agntcy/ns/client-3 \ --slim '{"endpoint": "http://localhost:46357", "tls": {"insecure": true}}' \ - --shared-secret "secret" + --shared-secret "very-long-shared-secret-value-0123456789abcdef" ``` Each terminal should show output similar to: @@ -733,13 +769,16 @@ if [ "$OS" != "linux" ] && [ "$OS" != "darwin" ]; then fi # Construct the download URL -VERSION="v0.6.0" -BINARY_NAME="slimctl-${OS}-${ARCH}" -DOWNLOAD_URL="https://github.com/agntcy/slim/releases/download/slimctl-${VERSION}/${BINARY_NAME}" +VERSION="v0.7.0" +COMMIT_HASH="35c37ab" +ARCHIVE_NAME="slimctl_slimctl-${VERSION}-SNAPSHOT-${COMMIT_HASH}_${OS}_${ARCH}.tar.gz" +DOWNLOAD_URL="https://github.com/agntcy/slim/releases/download/slimctl-${VERSION}/${ARCHIVE_NAME}" -# Download the binary +# Download and extract the binary echo "Downloading slimctl for ${OS}-${ARCH}..." -curl -L "${DOWNLOAD_URL}" -o slimctl +curl -L "${DOWNLOAD_URL}" -o "${ARCHIVE_NAME}" +tar -xzf "${ARCHIVE_NAME}" +rm "${ARCHIVE_NAME}" # Make it executable chmod +x slimctl @@ -842,4 +881,4 @@ The `slimctl` output is this: Channel deleted successfully with ID: agntcy/ns/xyIGhc2igNGmkeBDlZ ``` -All applications connected to the group stop because the receive loops end. +All applications connected to the group stop as their receive loops terminate. diff --git a/docs/messaging/slim-group.md b/docs/messaging/slim-group.md index 9768cd86..d6af1b3f 100644 --- a/docs/messaging/slim-group.md +++ b/docs/messaging/slim-group.md @@ -24,7 +24,7 @@ of the application logic) or serve solely as a channel moderator. This section provides the basic steps to follow, along with Python code snippets, for setting up a group session. -The full code is available in the [group.py](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) example in the SLIM repository. +The full code is available in the [group.py](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) example in the SLIM repository. ### Create the Channel @@ -34,31 +34,34 @@ In a group session, communication between participants can be encrypted end-to-end, enabling MLS. ```python -created_session = await local_app.create_session( - slim_bindings.PySessionConfiguration.Group( # type: ignore # Build group session configuration - channel_name=chat_channel, # Logical group channel (PyName) all participants join; acts as group/topic identifier. - max_retries=5, # Max per-message resend attempts upon missing ack before reporting a delivery failure. - timeout=datetime.timedelta( - seconds=5 - ), # Ack / delivery wait window; after this duration a retry is triggered (until max_retries). - mls_enabled=enable_mls, # Enable Messaging Layer Security for end-to-end encrypted & authenticated group communication. - ) +config = slim_bindings.SessionConfiguration.Group( + max_retries=5, # Max per-message resend attempts upon missing ack before reporting a delivery failure. + timeout=datetime.timedelta( + seconds=5 + ), # Ack / delivery wait window; after this duration a retry is triggered (until max_retries). + mls_enabled=enable_mls, # Enable Messaging Layer Security for end-to-end encrypted & authenticated group communication. ) + +created_session, handle = await local_app.create_session( + chat_channel, # Logical group channel (Name) all participants join; acts as group/topic identifier. + config, # session configuration +) + +await handle # await for the session to be created ``` ### Invite Participants to the Channel Once the group session is created, new participants can be invited -to join. Not all participants need to be added at the beginning; you can add them later, even after communication has started. +to join. Not all participants need to be added at the beginning; +you can add them later, even after communication has started. ```python -# Invite each provided participant. Route is set before inviting to ensure -# outbound control messages can reach them. For more info see -# https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/SESSION.md#invite-a-new-participant for invite in invites: invite_name = split_id(invite) await local_app.set_route(invite_name) - await created_session.invite(invite_name) + handle = await created_session.invite(invite_name) # invite participant + await handle # awit for the invite to be finished print(f"{local} -> add {invite_name} to the group") ``` @@ -81,7 +84,7 @@ while True: try: # Await next inbound message from the group session. # The returned parameters are a message context and the raw payload bytes. - # Check session.py for details on PyMessageContext contents. + # Check session.py for details on MessageContext contents. ctx, payload = await session.get_message() print_formatted_text( f"{ctx.source_name} > {payload.decode()}", @@ -103,7 +106,6 @@ Each participant can also send messages at any time to the new session, and each ```python # Send message to the channel_name specified when creating the session. # As the session is group, all participants will receive it. -# calling publish_with_destination on a group session will raise an error. await shared_session_container[0].publish(user_input.encode()) ``` diff --git a/docs/messaging/slim-howto.md b/docs/messaging/slim-howto.md index a469b3ba..62e172af 100644 --- a/docs/messaging/slim-howto.md +++ b/docs/messaging/slim-howto.md @@ -21,7 +21,7 @@ installed using the provided container image, with #### Using Docker ```bash -docker pull ghcr.io/agntcy/slim:latest +docker pull ghcr.io/agntcy/slim:v0.7.0 cat << EOF > ./config.yaml tracing: @@ -47,7 +47,7 @@ EOF docker run -it \ -v ./config.yaml:/config.yaml -p 46357:46357 \ - ghcr.io/agntcy/slim:latest /slim --config /config.yaml + ghcr.io/agntcy/slim:v0.7.0 /slim --config /config.yaml ``` #### Using Cargo @@ -85,11 +85,11 @@ EOF We also provide a Helm chart for deploying SLIM in Kubernetes environments. ```bash -helm pull oci://ghcr.io/agntcy/slim/helm/slim --version v0.2.0 +helm pull oci://ghcr.io/agntcy/slim/helm/slim --version v0.7.0 ``` For information about how to use the Helm chart, see the -[values.yaml](https://github.com/agntcy/slim/blob/slim-v0.6.0/charts/slim/values.yaml) +[values.yaml](https://github.com/agntcy/slim/blob/slim-v0.7.0/charts/slim/values.yaml) ### SLIM Controller @@ -100,31 +100,43 @@ provided container image or with [Helm](https://helm.sh/). ### Using Docker ```bash -docker pull ghcr.io/agntcy/slim/control-plane:latest +docker pull ghcr.io/agntcy/slim/control-plane:v0.7.0 cat << EOF > ./slim-control-plane.yaml northbound: - httpHost: localhost + httpHost: 0.0.0.0 httpPort: 50051 logging: - level: DEBUG + level: INFO southbound: - httpHost: localhost + httpHost: 0.0.0.0 httpPort: 50052 logging: - level: DEBUG + level: INFO + +reconciler: + maxRequeues: 15 + maxNumOfParallelReconciles: 1000 + +logging: + level: INFO + +database: + filePath: /db/controlplane.db EOF docker run -it \ - -v ./slim-control-plane.yaml:/config.yaml -p 50051:50051 -p 50052:50052 \ - ghcr.io/agntcy/slim/control-plane:latest -config /config.yaml + -v ./slim-control-plane.yaml:/config.yaml -v .:/db \ + -p 50051:50051 -p 50052:50052 \ + ghcr.io/agntcy/slim/control-plane:v0.7.0 \ + -config /config.yaml ``` ### Using Helm ```bash -helm pull oci://ghcr.io/agntcy/slim/helm/slim-control-plane --version v0.1.4 +helm pull oci://ghcr.io/agntcy/slim/helm/slim-control-plane --version v0.7.0 ``` ### SLIM Bindings @@ -142,18 +154,18 @@ pip install slim-bindings ```toml [project] ... -dependencies = ["slim-bindings>=0.6.0"] +dependencies = ["slim-bindings>=0.7.0"] ``` A tutorial on how to use the bindings in an application can be found in the [messaging layer documentation](./slim-data-plane.md). Otherwise examples are available in the -[SLIM Repository](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples). +[SLIM Repository](https://github.com/agntcy/slim/tree/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples). ### Slimctl `slimctl` is a command-line tool for managing SLIM Nodes and Controllers. It can be downloaded from the [releases -page](https://github.com/agntcy/slim/releases/tag/slimctl-v0.6.0) in the SLIM repo. +page](https://github.com/agntcy/slim/releases/tag/slimctl-v0.7.0) in the SLIM repo. #### Installation @@ -162,7 +174,7 @@ Choose the appropriate installation method for your operating system: === "macOS (Apple Silicon)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/slimctl-v0.6.0/slimctl-darwin-arm64 + curl -LO https://github.com/agntcy/slim/releases/download/slimctl-v0.7.0/slimctl_slimctl-v0.7.0-SNAPSHOT-35c37ab_darwin_arm64.tar.gz sudo mv slimctl-darwin-arm64 /usr/local/bin/slimctl sudo chmod +x /usr/local/bin/slimctl ``` @@ -179,7 +191,7 @@ Choose the appropriate installation method for your operating system: === "Linux (AMD64)" ```bash - curl -LO https://github.com/agntcy/slim/releases/download/slimctl-v0.6.0/slimctl-linux-amd64 + curl -LO https://github.com/agntcy/slim/releases/download/slimctl-v0.7.0/slimctl_slimctl-v0.7.0-SNAPSHOT-35c37ab_linux_amd64.tar.gz sudo mv slimctl-linux-amd64 /usr/local/bin/slimctl sudo chmod +x /usr/local/bin/slimctl ``` diff --git a/docs/messaging/slim-mcp.md b/docs/messaging/slim-mcp.md index 1ea3a7dc..0c51d39d 100644 --- a/docs/messaging/slim-mcp.md +++ b/docs/messaging/slim-mcp.md @@ -30,10 +30,10 @@ architecture. In this section of the tutorial, we implement and deploy two sample applications: -- A [LlamaIndex agent](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/integrations/slim-mcp/slim_mcp/examples/llamaindex_time_agent) +- A [LlamaIndex agent](https://github.com/agntcy/slim/tree/slim-v0.7.0/data-plane/python/integrations/slim-mcp/slim_mcp/examples/llamaindex_time_agent) that communicates with an MCP server over SLIM to perform time queries and timezone conversions. - An [MCP time - server](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/integrations/slim-mcp/slim_mcp/examples/mcp_server_time) that implements SLIM as its transport protocol and processes requests from the LlamaIndex agent. + server](https://github.com/agntcy/slim/tree/slim-v0.7.0/data-plane/python/integrations/slim-mcp/slim_mcp/examples/mcp_server_time) that implements SLIM as its transport protocol and processes requests from the LlamaIndex agent. ### Prerequisites diff --git a/docs/messaging/slim-rpc.md b/docs/messaging/slim-rpc.md index 546dd922..ef2b2e69 100644 --- a/docs/messaging/slim-rpc.md +++ b/docs/messaging/slim-rpc.md @@ -360,7 +360,7 @@ session creation is implemented in inside SLIMRPC in ```python # Create a session session = await self.local_app.create_session( - slim_bindings.PySessionConfiguration.FireAndForget( + slim_bindings.SessionConfiguration.FireAndForget( max_retries=10, timeout=datetime.timedelta(seconds=1), sticky=True, diff --git a/docs/messaging/slim-session.md b/docs/messaging/slim-session.md index 49d03be1..e029ffea 100644 --- a/docs/messaging/slim-session.md +++ b/docs/messaging/slim-session.md @@ -3,15 +3,14 @@ This document explains the SLIM session layer and the two supported session types. It helps you understand the two session interfaces, reliability, and security trade‑offs. -The SLIM repository ships with practical, runnable [examples](https://github.com/agntcy/slim/tree/slim-v0.6.0/data-plane/python/bindings/examples) that demonstrate how to create sessions and exchange messages between applications using the Python bindings. +The SLIM repository ships with practical, runnable [examples](https://github.com/agntcy/slim/tree/slim-v0.7.0/data-plane/python/bindings/examples) that demonstrate how to create sessions and exchange messages between applications using the Python bindings. ## Point-to-Point Session The point-to-point session enables point-to-point communication with a specific -instance. This session -performs a discovery phase to bind to one instance and all subsequent traffic in -the session targets that same endpoint. With reliability enabled, each message in -the session must be acked. +instance. This session performs a discovery phase to bind to one instance and all +subsequent traffic in the session targets that same endpoint. With reliability +enabled, each message in the session must be acked. If MLS is enabled, the point-to-point session establishes a two‑member MLS group after discovery. This mirrors the Group flow but with only two participants @@ -70,37 +69,42 @@ sequenceDiagram Using the SLIM Python bindings, you can create a point-to-point session as follows: ```python -# Assume local_app is an initialized application instance -session = await local_app.create_session( - slim_bindings.PySessionConfiguration.PointToPoint( - peer_name=remote_name, - max_retries=5, - timeout=datetime.timedelta(seconds=5), - mls_enabled=True, # Enable MLS for end-to-end security - ) +config = slim_bindings.SessionConfiguration.PointToPoint( + max_retries=5, + timeout=datetime.timedelta(seconds=5), + mls_enabled=enable_mls, ) +session, handle = await local_app.create_session(remote_name, config) +await handle ``` -Parameters: +Config Parameters: -* `peer_name` (required, PyName): Identifier of the remote participant - instance. * `max_retries` (optional, int): Retry attempts per message if Ack missing. * `timeout` (optional, timedelta): Wait per attempt for an Ack before retry. If `timeout` is not set, the session is best‑effort. * `mls_enabled` (optional, bool): Enable end‑to‑end encryption (MLS). +Create Session Parameters: + +* `remote_name` (required, Name): Identifier of the remote participant + instance. +* `config` (required, SessionConfiguration): The configuration object created for this session. + +The `await handle` guarantees that once returned all the underlying message exchange +is done and the remote is correctly connected to the session. + ### Sending and Replying in a Point-to-Point Session As the point-to-point session is bound to a single remote instance after discovery, outbound messages use the implicit destination. Use `publish` for normal sends -and `publish_to` to reply using a previously received message context. +and to reply back to the sender. This example shows how to send and reply in a point-to-point session: ```python -# Send a message using publish it will reach the endpoint -# specified and the session creation +# Send a message using publish; it will reach the endpoint +# specified at session creation await session.publish(b"hello") # Await reply from remote (pattern depends on your control loop) @@ -108,15 +112,15 @@ msg_ctx, payload = await session.get_message() print(payload.decode()) # Send a correlated response back (echo style) -# The message will be sent according to the info in msg_ctx -await session.publish_to(msg_ctx, payload) +# The message will be sent according to the info in the session +await session.publish(payload) ``` ### Point-to-Point Example -This [example](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/point_to_point.py) walks through the creation of a point-to-point session. When running the point-to-point example multiple times, the session binds to different running instances, while the message stream always sticks to the same endpoint. +This [example](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/point_to_point.py) walks through the creation of a point-to-point session. When running the point-to-point example multiple times, the session binds to different running instances, while the message stream always sticks to the same endpoint. -The example demonstrates how to publish messages, enable reliability, and enable MLS for end‑to‑end security. The associated [README](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/README_point_to_point.md) shows more information and how to run the example using the Taskfile provided in the repository. +The example demonstrates how to publish messages, enable reliability, and enable MLS for end‑to‑end security. The associated [README](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/README_point_to_point.md) shows more information and how to run the example using the Taskfile provided in the repository. ## Group Session @@ -138,25 +142,37 @@ name and specify reliability and security settings. Here is an example: ```python -# Assume local_app is an initialized application instance -session = await local_app.create_session( - slim_bindings.PySessionConfiguration.Group( - channel_name=chat_topic, - max_retries=5, - timeout=datetime.timedelta(seconds=5), - mls_enabled=True, - ) +config = slim_bindings.SessionConfiguration.Group( + max_retries=5, # Max per-message resend attempts upon missing ack before reporting a delivery failure. + timeout=datetime.timedelta( + seconds=5 + ), # Ack / delivery wait window; after this duration a retry is triggered (until max_retries). + mls_enabled=enable_mls, # Enable Messaging Layer Security for end-to-end encrypted & authenticated group communication. +) + +created_session, handle = await local_app.create_session( + chat_channel, # Logical group channel (Name) all participants join; acts as group/topic identifier. + config, # session configuration ) + +await handle ``` -Parameters: +Config Parameters: -* `topic` (required, PyName): Channel/Topic name where all the messages are - delivered. -* `max_retries` (optional, int): Retry attempts for missing Acks. -* `timeout` (optional, timedelta): Wait per attempt for Ack before retry. - If `timeout` is not set the session is best‑effort. -* `mls_enabled` (optional, bool): Enable secure group MLS messaging. +* `max_retries` (optional, int): Retry attempts per message if Ack missing. +* `timeout` (optional, timedelta): Wait per attempt for an Ack before retry. + If `timeout` is not set, the session is best‑effort. +* `mls_enabled` (optional, bool): Enable end‑to‑end encryption (MLS). + +Create Session Parameters: + +* `chat_channel` (required, Name): Identifier of the group channel that all + participants join. +* `config` (required, SessionConfiguration): The configuration object created for this session. + +As in the case of point to point session, the `await handle` guarantees that once returned all the +underlying message exchange is completed. ### Sending and Replying in a Group Session @@ -179,22 +195,26 @@ method after creating the session. ```python # After creating the session: -invite_name = slim_bindings.PyName("agntcy", "ns", "participant") -await local_app.set_route(invite_name) -await session.invite(invite_name) +for invite in invites: + invite_name = split_id(invite) + await local_app.set_route(invite_name) + handle = await created_session.invite(invite_name) + await handle + print(f"{local} -> add {invite_name} to the group") ``` Parameters: -* `invite_name` (PyName): Identifier of the participant to add. +* `invite_name` (required, Name): Identifier of the participant to add. Notice the `await local_app.set_route(invite_name)` command before the invite. This instructs SLIM on how to forward a message with the specified name. This has to be done by the application for every invite. -When a moderator wants to add a new participant (e.g., an instance of App-C) to -a group session, the following steps occur. All the steps are visualized in -the diagram below: +The `await handle` returns once the remote participant is correctly added to the +group and all the state on every participant is updated. In fact, +when a moderator wants to add a new participant (e.g., an instance of App-C) to +a group session, all the following steps need to be executed (see diagram below): 1. **Discovery Phase:** The moderator initiates a discovery request to find a running instance of the desired application (App-C). This request is sent to @@ -208,16 +228,20 @@ the diagram below: the channel, and replies with its MLS (Messaging Layer Security) key package. This reply is routed back to the moderator. -3. **MLS State Update:** The moderator initiates an MLS commit to add App-C/1 +3. **Group State Update:** The moderator initiates an MLS commit to add App-C/1 to the secure group. The message is sent using the channel name and so the SLIM Node distributes this commit to all current participants (App-B/2 and - App-A/1), who update their MLS state and acknowledge the commit. The - moderator collects all acknowledgments. Once all acknowledgments are + App-A/1), who update their MLS state and acknowledge the commit. This message + also contains the new list of participants in the group, in particular the + newly added participant. The + moderator collects all acknowledgments for the update message. Once all acknowledgments are received, the moderator sends an MLS Welcome message to App-C/1. App-C/1 - initializes its MLS state and acknowledges receipt. At the end of this + initializes its MLS state, gets the current list of participants in the group + and acknowledges receipt. At the end of this process, all participants (including the new one) share a secure group state and can exchange encrypted messages on the group channel. If MLS is - disabled, the MLS state update and welcome step are skipped. + disabled, the update and welcome messages are used only to exchange the new list of + participants in the group, so that they are all aware of who is connected. ```mermaid sequenceDiagram @@ -245,24 +269,24 @@ sequenceDiagram SLIM Node->>Moderator: Invite Reply (MLS key package) Moderator->>Moderator: Update MLS state - Note over Moderator,App-A/1: MLS State Update - Moderator->>SLIM Node: MLS commit (Add agntcy/ns/App-C/1) to Channel - par Handle MLS commit on App-C/1 - SLIM Node->>App-B/2: MLS commit (Add agntcy/ns/App-C/1) to Channel - App-B/2->>App-B/2: Update MLS state - App-B/2->>SLIM Node: Ack(MLS Commit) - SLIM Node->>Moderator: Ack(MLS Commit) - and Handle MLS commit on App-A/1 - SLIM Node->>App-A/1: MLS commit (Add agntcy/ns/App-C/1) to Channel - App-A/1->>App-A/1: Update MLS state - App-A/1->>SLIM Node: Ack(MLS Commit) - SLIM Node->>Moderator: Ack(MLS Commit) + Note over Moderator,App-A/1: Group State Update + Moderator->>SLIM Node: Group Update (Add agntcy/ns/App-C/1, MLS commit) to Channel + par Group State Update on App-C/1 + SLIM Node->>App-B/2: Group Update (Add agntcy/ns/App-C/1, MLS commit) to Channel + App-B/2->>App-B/2: Update Group and MLS state + App-B/2->>SLIM Node: Ack(Group Update) + SLIM Node->>Moderator: Ack(Group Update) + and Group State Update on App-A/1 + SLIM Node->>App-A/1: Group Update (Add agntcy/ns/App-C/1, MLS commit) to Channel + App-A/1->>App-A/1: Update Group and MLS state + App-A/1->>SLIM Node: Ack(Group Update) + SLIM Node->>Moderator: Ack(Group Update) end - Moderator->>SLIM Node: MLS Welcome agntcy/ns/App-C/1 - SLIM Node->>App-C/1: MLS Welcome agntcy/ns/App-C/1 - App-C/1->>App-C/1: Init MLS state - App-C/1->>SLIM Node: Ack(MLS Welcome) - SLIM Node->>Moderator: Ack(MLS Welcome) + Moderator->>SLIM Node: Welcome agntcy/ns/App-C/1 + SLIM Node->>App-C/1: Welcome agntcy/ns/App-C/1 + App-C/1->>App-C/1: Init Group and MLS state + App-C/1->>SLIM Node: Ack(Welcome) + SLIM Node->>Moderator: Ack(Welcome) ``` ### Remove a Participant @@ -274,24 +298,25 @@ This example shows how to remove a participant from a group session: ```python # To remove a participant from the session: -remove_name = slim_bindings.PyName("agntcy", "ns", "participant") await session.remove(remove_name) ``` Parameter: -* `remove_name` (PyName): Identifier of the participant to remove. +* `remove_name` (required, Name): Identifier of the participant to remove. When a moderator wants to remove a participant (e.g., App-C/1) from a group session, the following steps occur. All the steps are visualized in the diagram below: -1. **MLS State Update:** The moderator creates an MLS commit to remove App-C/1 +1. **Group State Update:** The moderator creates an MLS commit to remove App-C/1 from the secure group. This commit is sent to the group channel and the SLIM Node distributes it to all current participants (App-C/1, App-B/2, and - App-A/1). Each participant updates its MLS state and acknowledges the - commit. The moderator collects all acknowledgments. In case the MLS is - disabled, this step is not executed. + App-A/1). The message also contains the name of the participant to remove so + that all the endpoints can update their list of participants. + Each participant updates its MLS state and acknowledges the + commit. The moderator collects all acknowledgments. If MLS is + disabled, only the name of the participant to remove is sent using this message. 2. **Removal:** After the MLS state is updated, the moderator sends a remove message to App-C/1. Upon receiving the remove message, App-C/1 unsubscribes @@ -310,23 +335,23 @@ sequenceDiagram participant App-B/2 participant App-A/1 - Note over Moderator,App-A/1: MLS State Update - Moderator->>SLIM Node: MLS commit (Remove agntcy/ns/App-C/1) to Channel - par Handle MLS commit on App-C/1 - SLIM Node->>App-C/1: MLS commit (Remove agntcy/ns/App-C/1) to Channel - App-C/1->>App-C/1: Update MLS state - App-C/1->>SLIM Node: Ack(MLS Commit) - SLIM Node->>Moderator: Ack(MLS Commit) - and Handle MLS commit on App-B/2 - SLIM Node->>App-B/2: MLS commit (Remove agntcy/ns/App-C/1) to Channel - App-B/2->>App-B/2: Update MLS state - App-B/2->>SLIM Node: Ack(MLS Commit) - SLIM Node->>Moderator: Ack(MLS Commit) - and Handle MLS commit on App-A/1 - SLIM Node->>App-A/1: MLS commit (Remove agntcy/ns/App-C/1) to Channel - App-A/1->>App-A/1: Update MLS state - App-A/1->>SLIM Node: Ack(MLS Commit) - SLIM Node->>Moderator: Ack(MLS Commit) + Note over Moderator,App-A/1: Group State Update + Moderator->>SLIM Node: Group Update (Remove agntcy/ns/App-C/1, MLS commit) to Channel + par Handle Group State Update on App-C/1 + SLIM Node->>App-C/1: Group Update (Remove agntcy/ns/App-C/1, MLS commit) to Channel + App-C/1->>App-C/1: Update Group and MLS state + App-C/1->>SLIM Node: Ack(Group Update) + SLIM Node->>Moderator: Ack(Group Update) + and Handle Group State Update on App-B/2 + SLIM Node->>App-B/2: Group Update (Remove agntcy/ns/App-C/1, MLS commit) to Channel + App-B/2->>App-B/2: Update Group and MLS state + App-B/2->>SLIM Node: Ack(Group Update) + SLIM Node->>Moderator: Ack(Group Update) + and Handle Group State Update on App-A/1 + SLIM Node->>App-A/1: Group Update (Remove agntcy/ns/App-C/1, MLS commit) to Channel + App-A/1->>App-A/1: Update Group and MLS state + App-A/1->>SLIM Node: Ack(Group Update) + SLIM Node->>Moderator: Ack(Group Update) end Note over Moderator,App-A/1: Remove @@ -338,6 +363,12 @@ sequenceDiagram SLIM Node->>Moderator: Remove Reply ``` +If the moderator is removed from the group, or it simply closes the session, it sends a +close message on the channel that is received by all the group participants. Upon reception, +all the participants acknowledge that the message was received and they close the local session, +similar to the reception of the remove message (see the diagram). In this way, when the moderator +stops, all participants are removed from the group. + ### Group Example -This [example](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) demonstrates how to create a group session, invite participants, and (if enabled) establish an MLS group for end-to-end encryption. It also shows how to broadcast messages to all current members and handle inbound group messages. The associated [README](https://github.com/agntcy/slim/blob/slim-v0.6.0/data-plane/python/bindings/examples/src/slim_bindings_examples/README_group.md) shows more information and how to run the example using the Taskfile. +This [example](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/group.py) demonstrates how to create a group session, invite participants, and (if enabled) establish an MLS group for end-to-end encryption. It also shows how to broadcast messages to all current members and handle inbound group messages. The associated [README](https://github.com/agntcy/slim/blob/slim-v0.7.0/data-plane/python/bindings/examples/src/slim_bindings_examples/README_group.md) shows more information and how to run the example using the Taskfile.