You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iggy.apache.org by hg...@apache.org on 2025/10/28 10:33:56 UTC

(iggy) 01/01: test

This is an automated email from the ASF dual-hosted git repository.

hgruszecki pushed a commit to branch io_uring_tpc_squashmerge
in repository https://gitbox.apache.org/repos/asf/iggy.git

commit ba179728314534b50d7a6d3ce97f1c36dd8c5926
Author: Hubert Gruszecki <h....@gmail.com>
AuthorDate: Tue Oct 28 11:31:52 2025 +0100

    test
---
 .github/workflows/ci-publish-go.yml                |   67 +
 Cargo.lock                                         | 1131 +++++++--------
 Cargo.toml                                         |   48 +-
 DEPENDENCIES.md                                    |    5 +-
 .../iggy-pvc.yaml => Dockerfile.connectors.ci      |   53 +-
 ...c.yaml => Dockerfile.connectors.ci.dockerignore |   39 +-
 ...iggy-pvc.yaml => Dockerfile.mcp.ci.dockerignore |   39 +-
 bdd/docker-compose.yml                             |   19 +-
 bdd/go/tests/tcp_test/stream_steps.go              |    1 -
 bdd/python/tests/test_basic_messaging.py           |   35 +-
 bdd/rust/tests/steps/streams.rs                    |   16 +-
 bdd/rust/tests/steps/topics.rs                     |   16 +-
 bdd/scenarios/basic_messaging.feature              |   12 +-
 core/ai/mcp/src/service/mod.rs                     |    9 +-
 core/ai/mcp/src/service/requests.rs                |   12 +-
 core/bench/report/src/types/transport.rs           |    3 +
 .../bench/src/actors/consumer/client/high_level.rs |    9 +-
 core/bench/src/actors/consumer/client/interface.rs |    2 +-
 core/bench/src/actors/consumer/client/low_level.rs |   17 +-
 .../actors/consumer/typed_benchmark_consumer.rs    |    2 +-
 core/bench/src/actors/producer/client/interface.rs |    2 +-
 core/bench/src/actors/producer/client/low_level.rs |    8 +-
 .../actors/producer/typed_benchmark_producer.rs    |    2 +-
 .../typed_banchmark_producing_consumer.rs          |    4 +-
 core/bench/src/args/common.rs                      |   12 +-
 core/bench/src/args/defaults.rs                    |    3 +-
 core/bench/src/args/transport.rs                   |   42 +
 core/bench/src/benchmarks/benchmark.rs             |   30 +-
 core/bench/src/benchmarks/common.rs                |   50 +-
 core/bench/src/utils/client_factory.rs             |    4 +
 core/bench/src/utils/mod.rs                        |    1 +
 core/bench/src/utils/server_starter.rs             |   18 +
 .../create_consumer_group.rs                       |   15 +-
 .../src/cli/binary_streams/create_stream.rs        |   11 +-
 .../src/cli/binary_topics/create_topic.rs          |   31 +-
 .../client/binary_clients/consumer_group_client.rs |    1 -
 .../src/client/binary_clients/stream_client.rs     |    6 +-
 .../src/client/binary_clients/topic_client.rs      |    1 -
 .../src/client/binary_consumer_groups/mod.rs       |    2 -
 .../src/client/binary_streams/mod.rs               |    7 +-
 .../src/client/binary_topics/mod.rs                |    2 -
 core/binary_protocol/src/utils/mapper.rs           |    2 +-
 core/cli/src/args/consumer_offset.rs               |    4 +-
 core/cli/src/args/message.rs                       |    4 +-
 core/cli/src/args/permissions/mod.rs               |    2 +-
 core/cli/src/args/permissions/stream.rs            |    4 +-
 core/cli/src/args/permissions/topic.rs             |    4 +-
 core/cli/src/main.rs                               |    6 +-
 core/common/Cargo.toml                             |    1 +
 core/common/src/certificates.rs                    |    1 +
 .../consumer_groups/create_consumer_group.rs       |   47 +-
 .../consumer_offsets/delete_consumer_offset.rs     |    2 +-
 .../consumer_offsets/get_consumer_offset.rs        |   37 +-
 .../consumer_offsets/store_consumer_offset.rs      |   41 +-
 core/common/src/commands/messages/poll_messages.rs |   46 +-
 core/common/src/commands/streams/create_stream.rs  |   41 +-
 core/common/src/commands/topics/create_topic.rs    |   61 +-
 core/common/src/commands/users/defaults.rs         |    2 +-
 core/common/src/error/iggy_error.rs                |   84 +-
 core/common/src/lib.rs                             |    5 +-
 core/common/src/locking/fast_async_lock.rs         |    4 +-
 core/common/src/locking/mod.rs                     |   10 +-
 core/common/src/locking/tokio_lock.rs              |    7 +-
 core/common/src/types/args/mod.rs                  |   89 +-
 .../configuration/auth_config/connection_string.rs |    6 +-
 core/common/src/types/configuration/mod.rs         |    1 +
 core/common/src/types/configuration/transport.rs   |    6 +
 .../types/configuration/websocket_config/mod.rs    |   25 +-
 .../websocket_config/websocket_client_config.rs    |  192 +++
 .../websocket_client_config_builder.rs             |  158 +++
 .../websocket_client_reconnection_config.rs        |   61 +
 .../websocket_connection_string_options.rs         |  209 +++
 core/common/src/types/confirmation/mod.rs          |   54 -
 core/common/src/types/identifier/mod.rs            |    9 +-
 core/common/src/types/mod.rs                       |    1 -
 .../src/types/permissions/permissions_global.rs    |   12 +-
 core/common/src/utils/crypto.rs                    |    8 +-
 core/configs/server.toml                           |   36 +-
 core/integration/Cargo.toml                        |    1 +
 core/integration/src/lib.rs                        |    3 +
 core/integration/src/quic_client.rs                |    2 +
 core/integration/src/test_mcp_server.rs            |   94 +-
 core/integration/src/test_server.rs                |  151 +-
 .../src/{quic_client.rs => websocket_client.rs}    |   22 +-
 core/integration/tests/archiver/disk.rs            |  129 --
 core/integration/tests/archiver/mod.rs             |   59 -
 core/integration/tests/archiver/s3.rs              |   38 -
 .../tests/cli/client/test_client_get_command.rs    |    2 +-
 core/integration/tests/cli/common/mod.rs           |    4 +-
 .../test_consumer_group_create_command.rs          |   17 +-
 .../test_consumer_group_delete_command.rs          |   12 +-
 .../test_consumer_group_get_command.rs             |   14 +-
 .../test_consumer_group_list_command.rs            |  194 +--
 .../test_consumer_offset_get_command.rs            |  163 +--
 .../test_consumer_offset_set_command.rs            |   11 +-
 .../tests/cli/context/test_context_applied.rs      |    9 +-
 .../tests/cli/general/test_help_command.rs         |   17 +-
 .../cli/message/test_message_flush_command.rs      |  128 +-
 .../tests/cli/message/test_message_poll_command.rs |  164 +--
 .../message/test_message_poll_to_file_command.rs   |   87 +-
 .../tests/cli/message/test_message_send_command.rs |  256 ++--
 .../message/test_message_send_from_file_command.rs |  140 +-
 .../cli/partition/test_partition_create_command.rs |   94 +-
 .../cli/partition/test_partition_delete_command.rs |  103 +-
 .../tests/cli/stream/test_stream_create_command.rs |   27 +-
 .../tests/cli/stream/test_stream_delete_command.rs |   16 +-
 .../tests/cli/stream/test_stream_get_command.rs    |   24 +-
 .../tests/cli/stream/test_stream_list_command.rs   |   14 +-
 .../tests/cli/stream/test_stream_purge_command.rs  |   29 +-
 .../tests/cli/stream/test_stream_update_command.rs |   25 +-
 .../tests/cli/system/test_me_command.rs            |    5 +
 .../tests/cli/system/test_snapshot_cmd.rs          |    2 +-
 .../tests/cli/system/test_stats_command.rs         |   12 +-
 .../tests/cli/topic/test_topic_create_command.rs   |   45 +-
 .../tests/cli/topic/test_topic_delete_command.rs   |   35 +-
 .../tests/cli/topic/test_topic_get_command.rs      |   39 +-
 .../tests/cli/topic/test_topic_list_command.rs     |   29 +-
 .../tests/cli/topic/test_topic_purge_command.rs    |   49 +-
 .../tests/cli/topic/test_topic_update_command.rs   |   60 +-
 .../tests/cli/user/test_user_create_command.rs     |    6 +-
 .../tests/cli/user/test_user_delete_command.rs     |    4 +-
 .../tests/cli/user/test_user_get_command.rs        |    8 +-
 .../cli/user/test_user_permissions_command.rs      |    8 +-
 core/integration/tests/config_provider/mod.rs      |   43 +-
 core/integration/tests/connectors/mod.rs           |    3 +-
 .../data_integrity/verify_after_server_restart.rs  |   94 +-
 core/integration/tests/mcp/mod.rs                  |   27 +-
 core/integration/tests/mod.rs                      |    1 -
 core/integration/tests/sdk/producer/background.rs  |   30 +-
 core/integration/tests/sdk/producer/mod.rs         |   14 +-
 core/integration/tests/server/cg.rs                |    3 +-
 .../tests/server/concurrent_addition.rs            |  128 ++
 core/integration/tests/server/general.rs           |   18 +-
 core/integration/tests/server/mod.rs               |   20 +-
 .../tests/server/scenarios/concurrent_scenario.rs  |  759 +++++++++++
 .../scenarios/consumer_group_join_scenario.rs      |   66 +-
 ...h_multiple_clients_polling_messages_scenario.rs |   69 +-
 ...with_single_client_polling_messages_scenario.rs |   48 +-
 .../server/scenarios/create_message_payload.rs     |   20 +-
 .../server/scenarios/delete_segments_scenario.rs   |   46 +-
 .../tests/server/scenarios/encryption_scenario.rs  |   50 +-
 .../server/scenarios/message_headers_scenario.rs   |   19 +-
 .../server/scenarios/message_size_scenario.rs      |   20 +-
 core/integration/tests/server/scenarios/mod.rs     |   28 +-
 .../scenarios/stream_size_validation_scenario.rs   |    3 +-
 .../tests/server/scenarios/system_scenario.rs      |  255 ++--
 .../tests/server/scenarios/tcp_tls_scenario.rs     |   26 +-
 .../tests/server/scenarios/user_scenario.rs        |   12 +-
 ...p_tls_scenario.rs => websocket_tls_scenario.rs} |   37 +-
 core/integration/tests/server/specific.rs          |   49 +-
 core/integration/tests/state/file.rs               |   25 +-
 core/integration/tests/state/mod.rs                |   11 +-
 core/integration/tests/state/system.rs             |   36 +-
 .../tests/streaming/common/test_setup.rs           |   92 +-
 .../integration/tests/streaming/consumer_offset.rs |   83 --
 core/integration/tests/streaming/get_by_offset.rs  |  144 +-
 .../tests/streaming/get_by_timestamp.rs            |   97 +-
 core/integration/tests/streaming/messages.rs       |  164 ---
 core/integration/tests/streaming/mod.rs            |  132 +-
 core/integration/tests/streaming/partition.rs      |  226 ---
 core/integration/tests/streaming/segment.rs        |  704 ----------
 core/integration/tests/streaming/snapshot.rs       |    6 +-
 core/integration/tests/streaming/stream.rs         |  189 ---
 core/integration/tests/streaming/system.rs         |  134 --
 core/integration/tests/streaming/topic.rs          |  270 ----
 core/integration/tests/streaming/topic_messages.rs |  247 ----
 core/sdk/Cargo.toml                                |    2 +
 core/sdk/src/client_provider.rs                    |   49 +-
 core/sdk/src/client_wrappers/binary_client.rs      |    4 +
 .../src/client_wrappers/binary_cluster_client.rs   |    1 +
 .../binary_consumer_group_client.rs                |   40 +-
 .../binary_consumer_offset_client.rs               |   15 +
 .../src/client_wrappers/binary_message_client.rs   |   23 +
 .../src/client_wrappers/binary_partition_client.rs |   10 +
 .../binary_personal_access_token_client.rs         |    8 +
 .../src/client_wrappers/binary_segment_client.rs   |    5 +
 .../src/client_wrappers/binary_stream_client.rs    |   20 +-
 .../src/client_wrappers/binary_system_client.rs    |    7 +
 .../sdk/src/client_wrappers/binary_topic_client.rs |   35 +-
 core/sdk/src/client_wrappers/binary_user_client.rs |   19 +
 core/sdk/src/client_wrappers/client_wrapper.rs     |    2 +
 core/sdk/src/clients/binary_cluster.rs             |    4 +-
 core/sdk/src/clients/binary_consumer_group.rs      |    8 +-
 core/sdk/src/clients/binary_consumer_offset.rs     |    2 +-
 core/sdk/src/clients/binary_message.rs             |    2 +-
 core/sdk/src/clients/binary_partitions.rs          |    2 +-
 .../src/clients/binary_personal_access_tokens.rs   |    2 +-
 core/sdk/src/clients/binary_segments.rs            |    2 +-
 core/sdk/src/clients/binary_streams.rs             |   14 +-
 core/sdk/src/clients/binary_system.rs              |    2 +-
 core/sdk/src/clients/binary_topics.rs              |    4 +-
 core/sdk/src/clients/binary_users.rs               |    2 +-
 core/sdk/src/clients/client.rs                     |   21 +-
 core/sdk/src/clients/client_builder.rs             |   73 +-
 core/sdk/src/clients/consumer.rs                   |   25 +-
 core/sdk/src/clients/consumer_builder.rs           |    6 +-
 core/sdk/src/clients/producer.rs                   |   13 +-
 core/sdk/src/clients/producer_builder.rs           |    6 +-
 core/sdk/src/http/binary_consumer_groups.rs        |    2 -
 core/sdk/src/http/binary_streams.rs                |    7 +-
 core/sdk/src/http/binary_topics.rs                 |    2 -
 core/sdk/src/http/http_client.rs                   |    6 +-
 core/sdk/src/lib.rs                                |    1 +
 core/sdk/src/prelude.rs                            |    4 +-
 .../src/stream_builder/build/build_stream_topic.rs |    7 +-
 core/sdk/src/tcp/tcp_client.rs                     |    5 +-
 core/sdk/src/tcp/tcp_tls_connection_stream.rs      |    2 +-
 core/sdk/src/tcp/tcp_tls_verifier.rs               |   13 +-
 .../channels/commands => sdk/src/websocket}/mod.rs |   11 +-
 core/sdk/src/websocket/websocket_client.rs         |  665 +++++++++
 .../src/websocket/websocket_connection_stream.rs   |  213 +++
 .../websocket_stream.rs}                           |   23 +-
 core/sdk/src/websocket/websocket_stream_kind.rs    |   59 +
 .../websocket/websocket_tls_connection_stream.rs   |  218 +++
 core/server/Cargo.toml                             |   33 +-
 core/server/server.http                            |    2 +-
 core/server/src/archiver/disk.rs                   |   99 --
 core/server/src/archiver/mod.rs                    |  136 --
 core/server/src/archiver/s3.rs                     |  194 ---
 core/server/src/binary/command.rs                  |    5 +-
 .../cluster/get_cluster_metadata_handler.rs        |   10 +-
 .../create_consumer_group_handler.rs               |   57 +-
 .../delete_consumer_group_handler.rs               |   69 +-
 .../consumer_groups/get_consumer_group_handler.rs  |   41 +-
 .../consumer_groups/get_consumer_groups_handler.rs |   44 +-
 .../consumer_groups/join_consumer_group_handler.rs |   11 +-
 .../leave_consumer_group_handler.rs                |   14 +-
 .../delete_consumer_offset_handler.rs              |    8 +-
 .../get_consumer_offset_handler.rs                 |   10 +-
 .../store_consumer_offset_handler.rs               |    9 +-
 .../messages/flush_unsaved_buffer_handler.rs       |   20 +-
 .../handlers/messages/poll_messages_handler.rs     |   72 +-
 .../handlers/messages/send_messages_handler.rs     |   58 +-
 .../partitions/create_partitions_handler.rs        |   48 +-
 .../partitions/delete_partitions_handler.rs        |   41 +-
 .../create_personal_access_token_handler.rs        |   23 +-
 .../delete_personal_access_token_handler.rs        |   19 +-
 .../get_personal_access_tokens_handler.rs          |   11 +-
 .../login_with_personal_access_token_handler.rs    |    9 +-
 .../handlers/segments/delete_segments_handler.rs   |   88 +-
 .../handlers/streams/create_stream_handler.rs      |  121 +-
 .../handlers/streams/delete_stream_handler.rs      |   38 +-
 .../binary/handlers/streams/get_stream_handler.rs  |   39 +-
 .../binary/handlers/streams/get_streams_handler.rs |   27 +-
 .../handlers/streams/purge_stream_handler.rs       |   20 +-
 .../handlers/streams/update_stream_handler.rs      |   28 +-
 .../binary/handlers/system/get_client_handler.rs   |   10 +-
 .../binary/handlers/system/get_clients_handler.rs  |   10 +-
 .../src/binary/handlers/system/get_me_handler.rs   |   11 +-
 .../src/binary/handlers/system/get_snapshot.rs     |    8 +-
 .../binary/handlers/system/get_stats_handler.rs    |   52 +-
 .../src/binary/handlers/system/ping_handler.rs     |   11 +-
 .../binary/handlers/topics/create_topic_handler.rs |  191 ++-
 .../binary/handlers/topics/delete_topic_handler.rs |   49 +-
 .../binary/handlers/topics/get_topic_handler.rs    |   39 +-
 .../binary/handlers/topics/get_topics_handler.rs   |   35 +-
 .../binary/handlers/topics/purge_topic_handler.rs  |   21 +-
 .../binary/handlers/topics/update_topic_handler.rs |   72 +-
 .../handlers/users/change_password_handler.rs      |   25 +-
 .../binary/handlers/users/create_user_handler.rs   |  168 ++-
 .../binary/handlers/users/delete_user_handler.rs   |   23 +-
 .../src/binary/handlers/users/get_user_handler.rs  |   11 +-
 .../src/binary/handlers/users/get_users_handler.rs |   11 +-
 .../binary/handlers/users/login_user_handler.rs    |   26 +-
 .../binary/handlers/users/logout_user_handler.rs   |   13 +-
 .../handlers/users/update_permissions_handler.rs   |   22 +-
 .../binary/handlers/users/update_user_handler.rs   |   26 +-
 core/server/src/binary/handlers/utils.rs           |   15 +-
 core/server/src/binary/mapper.rs                   |  205 +--
 core/server/src/binary/sender.rs                   |   61 +-
 core/server/src/bootstrap.rs                       |  709 ++++++++++
 core/server/src/channels/commands/archive_state.rs |  139 --
 .../commands/clean_personal_access_tokens.rs       |  137 --
 .../src/channels/commands/maintain_messages.rs     |  555 --------
 core/server/src/channels/commands/print_sysinfo.rs |  123 --
 core/server/src/channels/commands/save_messages.rs |  118 --
 .../src/channels/commands/verify_heartbeats.rs     |  150 --
 core/server/src/channels/handler.rs                |   46 -
 core/server/src/channels/server_command.rs         |   40 -
 .../src/compat/index_rebuilding/index_rebuilder.rs |   49 +-
 core/server/src/configs/cluster.rs                 |    3 +-
 core/server/src/configs/config_provider.rs         |  442 ++++++
 core/server/src/configs/defaults.rs                |   78 +-
 core/server/src/configs/displays.rs                |   36 +-
 core/server/src/configs/mod.rs                     |    3 +
 core/server/src/configs/quic.rs                    |    9 +
 core/server/src/configs/server.rs                  |  102 +-
 core/server/src/configs/sharding.rs                |  116 ++
 core/server/src/configs/system.rs                  |  101 +-
 core/server/src/configs/validators.rs              |  104 +-
 core/server/src/configs/websocket.rs               |  107 ++
 core/server/src/http/consumer_groups.rs            |  280 +++-
 core/server/src/http/consumer_offsets.rs           |   30 +-
 core/server/src/http/diagnostics.rs                |    7 +-
 core/server/src/http/error.rs                      |    5 -
 core/server/src/http/http_server.rs                |  150 +-
 core/server/src/http/http_shard_wrapper.rs         |  294 ++++
 core/server/src/http/jwt/cleaner.rs                |   41 -
 core/server/src/http/jwt/jwt_manager.rs            |    8 +-
 core/server/src/http/jwt/mod.rs                    |    1 -
 core/server/src/http/jwt/storage.rs                |   53 +-
 core/server/src/http/mapper.rs                     |  297 ++--
 core/server/src/http/messages.rs                   |   81 +-
 core/server/src/http/metrics.rs                    |    2 +-
 core/server/src/http/mod.rs                        |   10 +-
 core/server/src/http/partitions.rs                 |  110 +-
 core/server/src/http/personal_access_tokens.rs     |   75 +-
 core/server/src/http/shared.rs                     |    4 +-
 core/server/src/http/streams.rs                    |  350 +++--
 core/server/src/http/system.rs                     |   61 +-
 core/server/src/http/topics.rs                     |  466 +++++--
 core/server/src/http/users.rs                      |  367 +++--
 core/server/src/io/fs_locks.rs                     |   48 +
 core/server/src/io/fs_utils.rs                     |  105 ++
 core/server/src/io/mod.rs                          |    3 +
 core/server/src/io/storage.rs                      |  131 ++
 core/server/src/lib.rs                             |   20 +-
 core/server/src/log/logger.rs                      |    8 +-
 core/server/src/log/mod.rs                         |    6 +-
 core/server/src/log/runtime.rs                     |   87 ++
 core/server/src/log/tokio_console.rs               |   53 -
 core/server/src/main.rs                            |  440 ++++--
 core/server/src/quic/listener.rs                   |  170 ++-
 core/server/src/quic/mod.rs                        |    1 +
 core/server/src/quic/quic_sender.rs                |   29 +-
 core/server/src/quic/quic_server.rs                |  144 +-
 core/server/src/quic/quic_socket.rs                |   66 +
 core/server/src/server_error.rs                    |   15 +-
 core/server/src/shard/builder.rs                   |  176 +++
 core/server/src/shard/communication.rs             |  189 +++
 core/server/src/shard/handlers.rs                  |  404 ++++++
 core/server/src/shard/mod.rs                       |  314 +++++
 core/server/src/shard/namespace.rs                 |  121 ++
 .../{streaming/systems => shard/system}/clients.rs |   59 +-
 core/server/src/shard/system/cluster.rs            |   76 ++
 core/server/src/shard/system/consumer_groups.rs    |  306 +++++
 core/server/src/shard/system/consumer_offsets.rs   |  323 +++++
 .../{streaming/systems => shard/system}/info.rs    |   54 -
 core/server/src/shard/system/messages.rs           |  478 +++++++
 .../src/{streaming/systems => shard/system}/mod.rs |    4 +-
 core/server/src/shard/system/partitions.rs         |  328 +++++
 .../system}/personal_access_tokens.rs              |  138 +-
 core/server/src/shard/system/segments.rs           |  115 ++
 .../systems => shard/system}/snapshot/mod.rs       |  138 +-
 .../systems => shard/system}/snapshot/procdump.rs  |   18 +-
 core/server/src/shard/system/stats.rs              |  136 ++
 .../{streaming/systems => shard/system}/storage.rs |   32 +-
 core/server/src/shard/system/streams.rs            |  221 +++
 core/server/src/shard/system/topics.rs             |  348 +++++
 core/server/src/shard/system/users.rs              |  452 ++++++
 core/server/src/shard/system/utils.rs              |  162 +++
 core/server/src/shard/task_registry/builders.rs    |   25 +
 .../src/shard/task_registry/builders/continuous.rs |   92 ++
 .../src/shard/task_registry/builders/oneshot.rs    |  103 ++
 .../src/shard/task_registry/builders/periodic.rs   |  118 ++
 core/server/src/shard/task_registry/mod.rs         |    6 +
 core/server/src/shard/task_registry/registry.rs    |  581 ++++++++
 core/server/src/shard/task_registry/shutdown.rs    |  233 ++++
 .../tasks/continuous/http_server.rs}               |   45 +-
 .../src/shard/tasks/continuous/message_pump.rs     |   75 +
 .../src/{log => shard/tasks/continuous}/mod.rs     |   14 +-
 .../tasks/continuous/quic_server.rs}               |   33 +-
 .../tasks/continuous/tcp_server.rs}                |   24 +-
 .../tasks/continuous/websocket_server.rs}          |   35 +-
 core/server/src/{channels => shard/tasks}/mod.rs   |    6 +-
 .../src/shard/tasks/oneshot/config_writer.rs       |  109 ++
 core/server/src/shard/tasks/oneshot/mod.rs         |    3 +
 .../src/shard/tasks/periodic/heartbeat_verifier.rs |   87 ++
 .../src/shard/tasks/periodic/jwt_token_cleaner.rs  |   61 +
 .../src/shard/tasks/periodic/message_saver.rs      |  124 ++
 .../partitions.rs => shard/tasks/periodic/mod.rs}  |   21 +-
 .../periodic/personal_access_token_cleaner.rs      |   76 ++
 .../src/shard/tasks/periodic/sysinfo_printer.rs    |   94 ++
 core/server/src/shard/transmission/connector.rs    |  107 ++
 core/server/src/shard/transmission/event.rs        |  121 ++
 core/server/src/shard/transmission/frame.rs        |   67 +
 .../segments.rs => shard/transmission/id.rs}       |   31 +-
 core/server/src/shard/transmission/message.rs      |  119 ++
 .../src/{channels => shard/transmission}/mod.rs    |    8 +-
 core/server/src/slab/consumer_groups.rs            |  156 +++
 core/server/src/slab/helpers.rs                    |   53 +
 core/server/src/slab/mod.rs                        |   26 +
 core/server/src/slab/partitions.rs                 |  226 +++
 core/server/src/slab/streams.rs                    | 1439 ++++++++++++++++++++
 core/server/src/slab/topics.rs                     |  259 ++++
 core/server/src/slab/traits_ext.rs                 |  100 ++
 core/server/src/slab/users.rs                      |  190 +++
 core/server/src/state/file.rs                      |   77 +-
 core/server/src/state/mod.rs                       |   53 +-
 core/server/src/state/system.rs                    |  104 +-
 .../server/src/streaming/clients/client_manager.rs |  241 ++--
 .../deduplication/message_deduplicator.rs          |   53 +-
 core/server/src/streaming/diagnostics/metrics.rs   |   79 +-
 core/server/src/streaming/mod.rs                   |    6 +-
 .../src/streaming/partitions/consumer_offset.rs    |   51 +
 .../src/streaming/partitions/consumer_offsets.rs   |  214 ---
 core/server/src/streaming/partitions/helpers.rs    |  525 +++++++
 core/server/src/streaming/partitions/journal.rs    |  118 ++
 core/server/src/streaming/partitions/log.rs        |  159 +++
 core/server/src/streaming/partitions/messages.rs   |  766 -----------
 core/server/src/streaming/partitions/mod.rs        |    7 +-
 core/server/src/streaming/partitions/partition.rs  |  669 +++++----
 .../server/src/streaming/partitions/persistence.rs |  114 --
 core/server/src/streaming/partitions/segments.rs   |   15 +-
 core/server/src/streaming/partitions/storage.rs    |  646 +++------
 core/server/src/streaming/persistence/mod.rs       |    1 -
 core/server/src/streaming/persistence/persister.rs |   80 +-
 core/server/src/streaming/persistence/task.rs      |  133 --
 core/server/src/streaming/polling_consumer.rs      |   16 +-
 .../src/streaming/segments/indexes/index_reader.rs |   60 +-
 .../src/streaming/segments/indexes/index_writer.rs |   45 +-
 .../src/streaming/segments/indexes/indexes_mut.rs  |    9 +-
 .../src/streaming/segments/memory_journal.rs       |    0
 .../streaming/segments/messages/messages_reader.rs |   63 +-
 .../streaming/segments/messages/messages_writer.rs |  132 +-
 core/server/src/streaming/segments/messages/mod.rs |   41 +-
 .../streaming/segments/messages/persister_task.rs  |  215 ---
 .../src/streaming/segments/messages_accumulator.rs |  196 ---
 core/server/src/streaming/segments/mod.rs          |    8 +-
 .../src/streaming/segments/reading_messages.rs     |  380 ------
 core/server/src/streaming/segments/segment.rs      |  550 +-------
 core/server/src/streaming/segments/storage.rs      |   90 ++
 .../streaming/segments/types/messages_batch_mut.rs |    7 +-
 .../streaming/segments/types/messages_batch_set.rs |    8 -
 .../src/streaming/segments/writing_messages.rs     |  182 ---
 core/server/src/streaming/session.rs               |   46 +-
 core/server/src/streaming/stats/mod.rs             |  332 +++++
 core/server/src/streaming/storage.rs               |  178 +--
 core/server/src/streaming/streams/helpers.rs       |   90 ++
 core/server/src/streaming/streams/messages.rs      |   26 -
 core/server/src/streaming/streams/mod.rs           |    6 +-
 core/server/src/streaming/streams/persistence.rs   |   74 -
 core/server/src/streaming/streams/segments.rs      |   26 -
 core/server/src/streaming/streams/storage.rs       |  254 +---
 core/server/src/streaming/streams/stream.rs        |  293 ++--
 core/server/src/streaming/streams/topics.rs        |  338 -----
 .../src/streaming/systems/consumer_groups.rs       |  321 -----
 .../src/streaming/systems/consumer_offsets.rs      |  104 --
 core/server/src/streaming/systems/messages.rs      |  266 ----
 core/server/src/streaming/systems/partitions.rs    |  113 --
 core/server/src/streaming/systems/segments.rs      |  105 --
 core/server/src/streaming/systems/stats.rs         |  113 --
 core/server/src/streaming/systems/streams.rs       |  522 -------
 core/server/src/streaming/systems/system.rs        |  280 ----
 core/server/src/streaming/systems/topics.rs        |  300 ----
 core/server/src/streaming/systems/users.rs         |  530 -------
 core/server/src/streaming/topics/consumer_group.rs |  421 +++---
 .../server/src/streaming/topics/consumer_groups.rs |  430 ------
 .../src/streaming/topics/consumer_offsets.rs       |  136 --
 core/server/src/streaming/topics/helpers.rs        |  284 ++++
 core/server/src/streaming/topics/messages.rs       |  361 -----
 core/server/src/streaming/topics/mod.rs            |    7 +-
 core/server/src/streaming/topics/partitions.rs     |  128 --
 core/server/src/streaming/topics/persistence.rs    |   71 -
 core/server/src/streaming/topics/storage.rs        |  341 +----
 core/server/src/streaming/topics/topic.rs          |  610 +++++----
 core/server/src/streaming/traits.rs                |   28 +
 core/server/src/streaming/users/permissioner.rs    |    6 +-
 .../users/permissioner_rules/consumer_groups.rs    |   24 +-
 .../users/permissioner_rules/consumer_offsets.rs   |   12 +-
 .../streaming/users/permissioner_rules/messages.rs |    8 +-
 .../users/permissioner_rules/partitions.rs         |    8 +-
 .../streaming/users/permissioner_rules/segments.rs |    4 +-
 .../streaming/users/permissioner_rules/streams.rs  |   10 +-
 .../streaming/users/permissioner_rules/topics.rs   |   30 +-
 core/server/src/streaming/users/user.rs            |    4 +-
 core/server/src/streaming/utils/file.rs            |   17 +-
 core/server/src/streaming/utils/head_tail_buf.rs   |   55 -
 core/server/src/streaming/utils/memory_pool.rs     |   47 +-
 core/server/src/streaming/utils/mod.rs             |    2 +-
 core/server/src/streaming/utils/pooled_buffer.rs   |   43 +-
 core/server/src/streaming/utils/ptr.rs             |   53 +
 core/server/src/tcp/connection_handler.rs          |   69 +-
 core/server/src/tcp/mod.rs                         |    2 +-
 core/server/src/tcp/sender.rs                      |   66 +-
 core/server/src/tcp/tcp_listener.rs                |  194 ++-
 core/server/src/tcp/tcp_sender.rs                  |    9 +-
 core/server/src/tcp/tcp_server.rs                  |   42 +-
 core/server/src/tcp/tcp_socket.rs                  |   32 +-
 core/server/src/tcp/tcp_tls_listener.rs            |  273 ++--
 core/server/src/tcp/tcp_tls_sender.rs              |   40 +-
 core/server/src/versioning.rs                      |    2 +-
 core/server/src/websocket/connection_handler.rs    |  161 +++
 core/server/src/websocket/mod.rs                   |    8 +
 core/server/src/websocket/websocket_listener.rs    |  185 +++
 core/server/src/websocket/websocket_sender.rs      |  200 +++
 core/server/src/websocket/websocket_server.rs      |   60 +
 .../server/src/websocket/websocket_tls_listener.rs |  270 ++++
 core/server/src/websocket/websocket_tls_sender.rs  |  186 +++
 core/tools/src/data-seeder/seeder.rs               |   65 +-
 examples/rust/src/getting-started/producer/main.rs |   76 +-
 examples/rust/src/multi-tenant/consumer/main.rs    |    4 +-
 examples/rust/src/multi-tenant/producer/main.rs    |    4 +-
 examples/rust/src/shared/args.rs                   |   52 +
 examples/rust/src/shared/system.rs                 |    3 +-
 helm/README.md                                     |    5 +
 helm/charts/iggy-server/.helmignore                |   23 +
 .../iggy-pvc.yaml => iggy-server/Chart.yaml}       |   39 +-
 helm/charts/iggy-server/README.md                  |   53 +
 helm/charts/iggy-server/templates/NOTES.txt        |   22 +
 helm/charts/iggy-server/templates/_helpers.tpl     |   67 +
 helm/charts/iggy-server/templates/deployment.yaml  |   92 ++
 helm/charts/iggy-server/templates/hpa.yaml         |   61 +
 helm/charts/iggy-server/templates/ingress.yaml     |   78 ++
 .../templates/service.yaml}                        |   41 +-
 .../templates/serviceaccount.yaml}                 |   24 +-
 .../templates/servicemonitor.yaml}                 |   41 +-
 helm/charts/iggy-server/values.yaml                |  126 ++
 helm/charts/iggy-ui/.helmignore                    |   23 +
 .../templates/iggy-pvc.yaml => iggy-ui/Chart.yaml} |   39 +-
 helm/charts/iggy-ui/README.md                      |   47 +
 helm/charts/iggy-ui/templates/NOTES.txt            |   22 +
 helm/charts/iggy-ui/templates/_helpers.tpl         |   67 +
 helm/charts/iggy-ui/templates/deployment.yaml      |   83 ++
 helm/charts/iggy-ui/templates/ingress.yaml         |   78 ++
 .../templates/service.yaml}                        |   33 +-
 .../templates/serviceaccount.yaml}                 |   24 +-
 helm/charts/iggy-ui/values.yaml                    |  105 ++
 justfile                                           |    3 -
 DEPENDENCIES.md => scripts/DEPENDENCIES.md         |  653 ++++-----
 520 files changed, 28096 insertions(+), 20023 deletions(-)

diff --git a/.github/workflows/ci-publish-go.yml b/.github/workflows/ci-publish-go.yml
new file mode 100644
index 000000000..97187d193
--- /dev/null
+++ b/.github/workflows/ci-publish-go.yml
@@ -0,0 +1,67 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+name: ci-publish-go
+on:
+  push:
+    tags:
+      - "foreign/go/v*"
+
+jobs:
+  validate:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Extract tag name
+        id: extract
+        run: echo "TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
+
+      - name: Validate tag format
+        run: |
+          if [[ ! "$TAG" =~ ^foreign/go/v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z]+([.-][0-9A-Za-z]+)*)?$ ]]; then
+            echo "Tag $TAG does not match required format: foreign/go/vX.Y.Z"
+            exit 1
+          fi
+          echo "Valid Go module tag: $TAG"
+
+      - name: Checkout code
+        uses: actions/checkout@v4
+
+      - name: Check go.mod module path in foreign/go
+        run: |
+          MOD_PATH=$(grep '^module ' foreign/go/go.mod | awk '{print $2}')
+          if [[ "$MOD_PATH" != "github.com/${{ github.repository }}/foreign/go" ]]; then
+            echo "go.mod module path ($MOD_PATH) does not match expected path: github.com/${{ github.repository }}/foreign/go"
+            exit 1
+          fi
+          echo "go.mod module path is correct: $MOD_PATH"
+
+      - name: Check if go.mod version matches tag
+        run: |
+          TAG_VERSION="${TAG#foreign/go/}"
+          # Check if go.mod contains this version in replace or require (not strictly necessary, but can be used for extra checks)
+          echo "Tag version is $TAG_VERSION (no strict version field in go.mod to check for single module)"
+          # (No error here, just informative)
+
+      - name: Run go mod tidy
+        working-directory: foreign/go
+        run: |
+          go mod tidy
+          git diff --exit-code || (echo 'Please run "go mod tidy" and commit the changes before tagging.' && exit 1)
+
+      - name: Run Go tests
+        working-directory: foreign/go
+        run: go test -v ./...
diff --git a/Cargo.lock b/Cargo.lock
index 1179da095..9df42cb0b 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -75,7 +75,7 @@ dependencies = [
  "derive_more 2.0.1",
  "encoding_rs",
  "flate2",
- "foldhash 0.1.5",
+ "foldhash",
  "futures-core",
  "h2 0.3.27",
  "http 0.2.12",
@@ -189,7 +189,7 @@ dependencies = [
  "cookie",
  "derive_more 2.0.1",
  "encoding_rs",
- "foldhash 0.1.5",
+ "foldhash",
  "futures-core",
  "futures-util",
  "impl-more",
@@ -405,6 +405,12 @@ version = "1.7.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
 
+[[package]]
+name = "arcshift"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809ce7e2c99d17c161c0ca1673665bab3a3e1aa277b0f793abffc66b57c32d9f"
+
 [[package]]
 name = "argon2"
 version = "0.5.3"
@@ -445,6 +451,22 @@ dependencies = [
  "wait-timeout",
 ]
 
+[[package]]
+name = "astral-tokio-tar"
+version = "0.5.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec179a06c1769b1e42e1e2cbe74c7dcdb3d6383c838454d063eaac5bbb7ebbe5"
+dependencies = [
+ "filetime",
+ "futures-core",
+ "libc",
+ "portable-atomic",
+ "rustc-hash",
+ "tokio",
+ "tokio-stream",
+ "xattr",
+]
+
 [[package]]
 name = "async-broadcast"
 version = "0.7.2"
@@ -457,6 +479,18 @@ dependencies = [
  "pin-project-lite",
 ]
 
+[[package]]
+name = "async-channel"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2"
+dependencies = [
+ "concurrent-queue",
+ "event-listener-strategy",
+ "futures-core",
+ "pin-project-lite",
+]
+
 [[package]]
 name = "async-compression"
 version = "0.4.32"
@@ -553,6 +587,12 @@ dependencies = [
  "syn 2.0.108",
 ]
 
+[[package]]
+name = "async-task"
+version = "4.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de"
+
 [[package]]
 name = "async-trait"
 version = "0.1.89"
@@ -612,45 +652,12 @@ version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
 
-[[package]]
-name = "attohttpc"
-version = "0.30.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16e2cdb6d5ed835199484bb92bb8b3edd526effe995c61732580439c1a67e2e9"
-dependencies = [
- "base64 0.22.1",
- "http 1.3.1",
- "log",
- "rustls",
- "serde",
- "serde_json",
- "url",
- "webpki-roots 1.0.3",
-]
-
 [[package]]
 name = "autocfg"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
 
-[[package]]
-name = "aws-creds"
-version = "0.39.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b13804829a843b3f26e151c97acbb315ee1177a2724690edfcd28f1894146200"
-dependencies = [
- "attohttpc",
- "home",
- "log",
- "quick-xml",
- "rust-ini",
- "serde",
- "thiserror 2.0.17",
- "time",
- "url",
-]
-
 [[package]]
 name = "aws-lc-rs"
 version = "1.14.1"
@@ -674,15 +681,6 @@ dependencies = [
  "fs_extra",
 ]
 
-[[package]]
-name = "aws-region"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5532f65342f789f9c1b7078ea9c9cd9293cd62dcc284fa99adc4a1c9ba43469c"
-dependencies = [
- "thiserror 2.0.17",
-]
-
 [[package]]
 name = "axum"
 version = "0.7.9"
@@ -717,6 +715,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871"
 dependencies = [
  "axum-core 0.5.5",
+ "axum-macros",
  "bytes",
  "form_urlencoded",
  "futures-util",
@@ -782,6 +781,17 @@ dependencies = [
  "tracing",
 ]
 
+[[package]]
+name = "axum-macros"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.108",
+]
+
 [[package]]
 name = "axum-server"
 version = "0.7.2"
@@ -804,12 +814,6 @@ dependencies = [
  "tower-service",
 ]
 
-[[package]]
-name = "base16ct"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
-
 [[package]]
 name = "base64"
 version = "0.21.7"
@@ -1016,9 +1020,9 @@ dependencies = [
 
 [[package]]
 name = "bollard"
-version = "0.19.1"
+version = "0.19.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "899ca34eb6924d6ec2a77c6f7f5c7339e60fd68235eaf91edd5a15f12958bb06"
+checksum = "ec7646ee90964aa59e9f832a67182791396a19a5b1d76eb17599a8310a7e2e09"
 dependencies = [
  "async-stream",
  "base64 0.22.1",
@@ -1055,7 +1059,7 @@ dependencies = [
  "tokio",
  "tokio-stream",
  "tokio-util",
- "tonic 0.13.1",
+ "tonic 0.14.2",
  "tower-service",
  "url",
  "winapi",
@@ -1063,27 +1067,28 @@ dependencies = [
 
 [[package]]
 name = "bollard-buildkit-proto"
-version = "0.6.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40b3e79f8bd0f25f32660e3402afca46fd91bebaf135af017326d905651f8107"
+checksum = "85a885520bf6249ab931a764ffdb87b0ceef48e6e7d807cfdb21b751e086e1ad"
 dependencies = [
- "prost 0.13.5",
- "prost-types 0.13.5",
- "tonic 0.13.1",
+ "prost 0.14.1",
+ "prost-types 0.14.1",
+ "tonic 0.14.2",
+ "tonic-prost",
  "ureq",
 ]
 
 [[package]]
 name = "bollard-stubs"
-version = "1.48.3-rc.28.0.4"
+version = "1.49.1-rc.28.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64ea257e555d16a2c01e5593f40b73865cdf12efbceda33c6d14a2d8d1490368"
+checksum = "5731fe885755e92beff1950774068e0cae67ea6ec7587381536fca84f1779623"
 dependencies = [
  "base64 0.22.1",
  "bollard-buildkit-proto",
  "bytes",
  "chrono",
- "prost 0.13.5",
+ "prost 0.14.1",
  "serde",
  "serde_json",
  "serde_repr",
@@ -1167,9 +1172,9 @@ dependencies = [
 
 [[package]]
 name = "bstr"
-version = "1.12.0"
+version = "1.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4"
+checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab"
 dependencies = [
  "memchr",
  "regex-automata",
@@ -1289,15 +1294,6 @@ dependencies = [
  "thiserror 2.0.17",
 ]
 
-[[package]]
-name = "castaway"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
-dependencies = [
- "rustversion",
-]
-
 [[package]]
 name = "cc"
 version = "1.2.43"
@@ -1505,16 +1501,196 @@ dependencies = [
 ]
 
 [[package]]
-name = "compact_str"
-version = "0.7.1"
+name = "compio"
+version = "0.15.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "compio-buf",
+ "compio-driver",
+ "compio-fs",
+ "compio-io",
+ "compio-log 0.1.0 (git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be)",
+ "compio-macros",
+ "compio-net",
+ "compio-quic",
+ "compio-runtime",
+ "compio-tls",
+]
+
+[[package]]
+name = "compio-buf"
+version = "0.6.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "arrayvec",
+ "bytes",
+ "libc",
+]
+
+[[package]]
+name = "compio-driver"
+version = "0.8.1"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "cfg-if",
+ "cfg_aliases",
+ "compio-buf",
+ "compio-log 0.1.0 (git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be)",
+ "crossbeam-channel",
+ "crossbeam-queue",
+ "futures-util",
+ "io-uring",
+ "io_uring_buf_ring",
+ "libc",
+ "once_cell",
+ "paste",
+ "polling",
+ "slab",
+ "socket2 0.6.1",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "compio-fs"
+version = "0.8.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "cfg-if",
+ "cfg_aliases",
+ "compio-buf",
+ "compio-driver",
+ "compio-io",
+ "compio-runtime",
+ "libc",
+ "os_pipe",
+ "widestring",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "compio-io"
+version = "0.7.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "compio-buf",
+ "futures-util",
+ "paste",
+ "pin-project-lite",
+]
+
+[[package]]
+name = "compio-log"
+version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f"
+checksum = "fc4e560213c1996b618da369b7c9109564b41af9033802ae534465c4ee4e132f"
+dependencies = [
+ "tracing",
+]
+
+[[package]]
+name = "compio-log"
+version = "0.1.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "tracing",
+]
+
+[[package]]
+name = "compio-macros"
+version = "0.1.2"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "proc-macro-crate 3.4.0",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.108",
+]
+
+[[package]]
+name = "compio-net"
+version = "0.8.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
 dependencies = [
- "castaway",
  "cfg-if",
- "itoa",
- "ryu",
- "static_assertions",
+ "compio-buf",
+ "compio-driver",
+ "compio-io",
+ "compio-runtime",
+ "either",
+ "libc",
+ "once_cell",
+ "socket2 0.6.1",
+ "widestring",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "compio-quic"
+version = "0.4.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "cfg_aliases",
+ "compio-buf",
+ "compio-io",
+ "compio-log 0.1.0 (git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be)",
+ "compio-net",
+ "compio-runtime",
+ "flume",
+ "futures-util",
+ "libc",
+ "quinn-proto",
+ "rustc-hash",
+ "rustls",
+ "thiserror 2.0.17",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "compio-runtime"
+version = "0.8.1"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "async-task",
+ "cfg-if",
+ "compio-buf",
+ "compio-driver",
+ "compio-log 0.1.0 (git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be)",
+ "core_affinity",
+ "crossbeam-queue",
+ "futures-util",
+ "libc",
+ "once_cell",
+ "scoped-tls",
+ "slab",
+ "socket2 0.6.1",
+ "windows-sys 0.60.2",
+]
+
+[[package]]
+name = "compio-tls"
+version = "0.6.0"
+source = "git+https://github.com/compio-rs/compio.git?rev=fe4243f0b6811ebc325afd081c9b087b4d9817be#fe4243f0b6811ebc325afd081c9b087b4d9817be"
+dependencies = [
+ "compio-buf",
+ "compio-io",
+ "native-tls",
+ "rustls",
+]
+
+[[package]]
+name = "compio-ws"
+version = "0.1.0"
+source = "git+https://github.com/krishvishal/compio-ws#ca9240b6b0eb2206e8ae050580a053c5e9d0e877"
+dependencies = [
+ "compio",
+ "compio-buf",
+ "compio-io",
+ "compio-net",
+ "compio-runtime",
+ "compio-tls",
+ "rustls",
+ "rustls-pemfile",
+ "tungstenite",
 ]
 
 [[package]]
@@ -1614,26 +1790,6 @@ version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 
-[[package]]
-name = "const-random"
-version = "0.1.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359"
-dependencies = [
- "const-random-macro",
-]
-
-[[package]]
-name = "const-random-macro"
-version = "0.1.16"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e"
-dependencies = [
- "getrandom 0.2.16",
- "once_cell",
- "tiny-keccak",
-]
-
 [[package]]
 name = "constant_time_eq"
 version = "0.3.1"
@@ -1669,6 +1825,16 @@ dependencies = [
  "version_check",
 ]
 
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "libc",
+]
+
 [[package]]
 name = "core-foundation"
 version = "0.10.1"
@@ -1685,6 +1851,17 @@ version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
+[[package]]
+name = "core_affinity"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a034b3a7b624016c6e13f5df875747cc25f884156aad2abd12b6c46797971342"
+dependencies = [
+ "libc",
+ "num_cpus",
+ "winapi",
+]
+
 [[package]]
 name = "cpufeatures"
 version = "0.2.17"
@@ -1803,24 +1980,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "crunchy"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
-
-[[package]]
-name = "crypto-bigint"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
-dependencies = [
- "generic-array",
- "rand_core 0.6.4",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "crypto-common"
 version = "0.1.6"
@@ -1857,6 +2016,17 @@ dependencies = [
  "cipher",
 ]
 
+[[package]]
+name = "ctrlc"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "881c5d0a13b2f1498e2306e82cbada78390e152d4b1378fb28a84f4dcd0dc4f3"
+dependencies = [
+ "dispatch",
+ "nix",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "cucumber"
 version = "0.21.1"
@@ -1917,30 +2087,62 @@ dependencies = [
 ]
 
 [[package]]
-name = "curve25519-dalek"
-version = "4.1.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+name = "cyper"
+version = "0.4.0"
+source = "git+https://github.com/krishvishal/cyper.git?rev=cd75e266df6ab0a9b9474eb7dda1735650d17db6#cd75e266df6ab0a9b9474eb7dda1735650d17db6"
 dependencies = [
- "cfg-if",
- "cpufeatures",
- "curve25519-dalek-derive",
- "digest",
- "fiat-crypto",
- "rustc_version",
- "subtle",
- "zeroize",
+ "async-stream",
+ "base64 0.22.1",
+ "compio",
+ "cyper-core",
+ "encoding_rs",
+ "futures-util",
+ "http 1.3.1",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "mime",
+ "send_wrapper",
+ "serde",
+ "serde_urlencoded",
+ "thiserror 2.0.17",
+ "url",
 ]
 
 [[package]]
-name = "curve25519-dalek-derive"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
+name = "cyper-axum"
+version = "0.4.0"
+source = "git+https://github.com/krishvishal/cyper.git?rev=cd75e266df6ab0a9b9474eb7dda1735650d17db6#cd75e266df6ab0a9b9474eb7dda1735650d17db6"
 dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.108",
+ "axum 0.8.6",
+ "axum-core 0.5.5",
+ "compio",
+ "compio-log 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cyper-core",
+ "futures-channel",
+ "futures-util",
+ "hyper",
+ "hyper-util",
+ "send_wrapper",
+ "socket2 0.6.1",
+ "tokio",
+ "tower 0.5.2",
+ "tower-service",
+]
+
+[[package]]
+name = "cyper-core"
+version = "0.4.0"
+source = "git+https://github.com/krishvishal/cyper.git?rev=cd75e266df6ab0a9b9474eb7dda1735650d17db6#cd75e266df6ab0a9b9474eb7dda1735650d17db6"
+dependencies = [
+ "cfg-if",
+ "compio",
+ "futures-util",
+ "hyper",
+ "hyper-util",
+ "rustls-platform-verifier",
+ "send_wrapper",
+ "tower-service",
 ]
 
 [[package]]
@@ -2027,6 +2229,12 @@ dependencies = [
  "parking_lot_core 0.9.12",
 ]
 
+[[package]]
+name = "data-encoding"
+version = "2.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476"
+
 [[package]]
 name = "dbus"
 version = "0.9.9"
@@ -2212,6 +2420,12 @@ dependencies = [
  "windows-sys 0.61.2",
 ]
 
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
 [[package]]
 name = "displaydoc"
 version = "0.2.5"
@@ -2246,15 +2460,6 @@ dependencies = [
  "syn 2.0.108",
 ]
 
-[[package]]
-name = "dlv-list"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f"
-dependencies = [
- "const-random",
-]
-
 [[package]]
 name = "doc-comment"
 version = "0.3.4"
@@ -2332,44 +2537,6 @@ version = "1.0.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
 
-[[package]]
-name = "ecdsa"
-version = "0.16.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
-dependencies = [
- "der",
- "digest",
- "elliptic-curve",
- "rfc6979",
- "signature",
- "spki",
-]
-
-[[package]]
-name = "ed25519"
-version = "2.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
-dependencies = [
- "pkcs8",
- "signature",
-]
-
-[[package]]
-name = "ed25519-dalek"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
-dependencies = [
- "curve25519-dalek",
- "ed25519",
- "serde",
- "sha2",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "either"
 version = "1.15.0"
@@ -2379,27 +2546,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "elliptic-curve"
-version = "0.13.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
-dependencies = [
- "base16ct",
- "crypto-bigint",
- "digest",
- "ff",
- "generic-array",
- "group",
- "hkdf",
- "pem-rfc7468",
- "pkcs8",
- "rand_core 0.6.4",
- "sec1",
- "subtle",
- "zeroize",
-]
-
 [[package]]
 name = "embedded-io"
 version = "0.4.0"
@@ -2601,22 +2747,6 @@ version = "2.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
 
-[[package]]
-name = "ff"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
-dependencies = [
- "rand_core 0.6.4",
- "subtle",
-]
-
-[[package]]
-name = "fiat-crypto"
-version = "0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
-
 [[package]]
 name = "figlet-rs"
 version = "0.1.5"
@@ -2719,12 +2849,6 @@ version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
 
-[[package]]
-name = "foldhash"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
-
 [[package]]
 name = "foreign-types"
 version = "0.3.2"
@@ -2913,7 +3037,6 @@ checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
 dependencies = [
  "typenum",
  "version_check",
- "zeroize",
 ]
 
 [[package]]
@@ -3445,17 +3568,6 @@ dependencies = [
  "web-time",
 ]
 
-[[package]]
-name = "group"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
-dependencies = [
- "ff",
- "rand_core 0.6.4",
- "subtle",
-]
-
 [[package]]
 name = "h2"
 version = "0.3.27"
@@ -3496,11 +3608,11 @@ dependencies = [
 
 [[package]]
 name = "halfbrown"
-version = "0.4.0"
+version = "0.3.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c7ed2f2edad8a14c8186b847909a41fbb9c3eafa44f88bd891114ed5019da09"
+checksum = "aa2c385c6df70fd180bbb673d93039dbd2cd34e41d782600bdf6e1ca7bce39aa"
 dependencies = [
- "hashbrown 0.16.0",
+ "hashbrown 0.15.5",
  "serde",
 ]
 
@@ -3529,6 +3641,12 @@ dependencies = [
  "byteorder",
 ]
 
+[[package]]
+name = "hash32"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e4e11d13d42fc8d55326b323c63978c75721fbbb695a6e6686765bcb8b33917"
+
 [[package]]
 name = "hashbrown"
 version = "0.12.3"
@@ -3552,7 +3670,7 @@ checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
 dependencies = [
  "allocator-api2",
  "equivalent",
- "foldhash 0.1.5",
+ "foldhash",
 ]
 
 [[package]]
@@ -3560,11 +3678,6 @@ name = "hashbrown"
 version = "0.16.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d"
-dependencies = [
- "allocator-api2",
- "equivalent",
- "foldhash 0.2.0",
-]
 
 [[package]]
 name = "hashlink"
@@ -3595,7 +3708,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
 dependencies = [
  "atomic-polyfill",
- "hash32",
+ "hash32 0.2.1",
  "rustc_version",
  "serde",
  "spin",
@@ -4010,8 +4123,10 @@ dependencies = [
  "serde",
  "tokio",
  "tokio-rustls",
+ "tokio-tungstenite",
  "tracing",
  "trait-variant",
+ "tungstenite",
  "webpki-roots 1.0.3",
 ]
 
@@ -4179,6 +4294,7 @@ dependencies = [
  "chrono",
  "clap",
  "comfy-table",
+ "compio-ws",
  "crc32fast",
  "derive_more 2.0.1",
  "fast-async-mutex",
@@ -4453,6 +4569,7 @@ dependencies = [
  "async-trait",
  "bytes",
  "chrono",
+ "compio",
  "ctor",
  "derive_more 2.0.1",
  "env_logger",
@@ -4490,6 +4607,28 @@ dependencies = [
  "rustversion",
 ]
 
+[[package]]
+name = "io-uring"
+version = "0.7.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b"
+dependencies = [
+ "bitflags 2.10.0",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "io_uring_buf_ring"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a8867874ff5758b47c1dac069e6e86541432f9da8be9111c5e94154134f07d0"
+dependencies = [
+ "bytes",
+ "io-uring",
+ "rustix",
+]
+
 [[package]]
 name = "ipnet"
 version = "2.11.0"
@@ -4594,9 +4733,9 @@ dependencies = [
 
 [[package]]
 name = "js-sys"
-version = "0.3.81"
+version = "0.3.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305"
+checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65"
 dependencies = [
  "once_cell",
  "wasm-bindgen",
@@ -4617,29 +4756,6 @@ dependencies = [
  "simple_asn1",
 ]
 
-[[package]]
-name = "jsonwebtoken"
-version = "10.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d119c6924272d16f0ab9ce41f7aa0bfef9340c00b0bb7ca3dd3b263d4a9150b"
-dependencies = [
- "base64 0.22.1",
- "ed25519-dalek",
- "getrandom 0.2.16",
- "hmac",
- "js-sys",
- "p256",
- "p384",
- "pem",
- "rand 0.8.5",
- "rsa",
- "serde",
- "serde_json",
- "sha2",
- "signature",
- "simple_asn1",
-]
-
 [[package]]
 name = "jwalk"
 version = "0.8.1"
@@ -5010,17 +5126,6 @@ version = "0.8.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
 
-[[package]]
-name = "maybe-async"
-version = "0.2.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.108",
-]
-
 [[package]]
 name = "md-5"
 version = "0.10.6"
@@ -5031,12 +5136,6 @@ dependencies = [
  "digest",
 ]
 
-[[package]]
-name = "md5"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae960838283323069879657ca3de837e9f7bbb4c7bf6ea7f1b290d5e9476d2e0"
-
 [[package]]
 name = "memchr"
 version = "2.7.6"
@@ -5090,15 +5189,6 @@ dependencies = [
  "unicase",
 ]
 
-[[package]]
-name = "minidom"
-version = "0.16.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e394a0e3c7ccc2daea3dffabe82f09857b6b510cb25af87d54bf3e910ac1642d"
-dependencies = [
- "rxml",
-]
-
 [[package]]
 name = "minimal-lexical"
 version = "0.2.1"
@@ -5183,6 +5273,23 @@ dependencies = [
  "getrandom 0.2.16",
 ]
 
+[[package]]
+name = "native-tls"
+version = "0.2.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e"
+dependencies = [
+ "libc",
+ "log",
+ "openssl",
+ "openssl-probe",
+ "openssl-sys",
+ "schannel",
+ "security-framework 2.11.1",
+ "security-framework-sys",
+ "tempfile",
+]
+
 [[package]]
 name = "never-say-never"
 version = "6.6.666"
@@ -5475,7 +5582,7 @@ dependencies = [
  "hyper-rustls",
  "hyper-timeout",
  "hyper-util",
- "jsonwebtoken 9.3.1",
+ "jsonwebtoken",
  "once_cell",
  "percent-encoding",
  "pin-project",
@@ -5657,7 +5764,6 @@ dependencies = [
  "rand 0.9.2",
  "thiserror 2.0.17",
  "tokio",
- "tokio-stream",
 ]
 
 [[package]]
@@ -5667,37 +5773,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
 
 [[package]]
-name = "ordered-multimap"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79"
-dependencies = [
- "dlv-list",
- "hashbrown 0.14.5",
-]
-
-[[package]]
-name = "p256"
-version = "0.13.2"
+name = "os_pipe"
+version = "1.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
 dependencies = [
- "ecdsa",
- "elliptic-curve",
- "primeorder",
- "sha2",
+ "libc",
+ "windows-sys 0.61.2",
 ]
 
 [[package]]
-name = "p384"
-version = "0.13.1"
+name = "papaya"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
+checksum = "f92dd0b07c53a0a0c764db2ace8c541dc47320dad97c2200c2a637ab9dd2328f"
 dependencies = [
- "ecdsa",
- "elliptic-curve",
- "primeorder",
- "sha2",
+ "equivalent",
+ "seize",
 ]
 
 [[package]]
@@ -6004,6 +6096,20 @@ version = "0.3.32"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
 
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "polonius-the-crab"
 version = "0.2.1"
@@ -6120,15 +6226,6 @@ dependencies = [
  "syn 2.0.108",
 ]
 
-[[package]]
-name = "primeorder"
-version = "0.13.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
-dependencies = [
- "elliptic-curve",
-]
-
 [[package]]
 name = "proc-macro-crate"
 version = "1.3.1"
@@ -6374,9 +6471,9 @@ dependencies = [
 
 [[package]]
 name = "quick-xml"
-version = "0.38.3"
+version = "0.37.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89"
+checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb"
 dependencies = [
  "memchr",
  "serde",
@@ -6570,15 +6667,6 @@ dependencies = [
  "bitflags 1.3.2",
 ]
 
-[[package]]
-name = "redox_syscall"
-version = "0.3.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
-dependencies = [
- "bitflags 1.3.2",
-]
-
 [[package]]
 name = "redox_syscall"
 version = "0.5.18"
@@ -6757,16 +6845,6 @@ dependencies = [
  "rand 0.8.5",
 ]
 
-[[package]]
-name = "rfc6979"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
-dependencies = [
- "hmac",
- "subtle",
-]
-
 [[package]]
 name = "ring"
 version = "0.17.14"
@@ -6781,6 +6859,12 @@ dependencies = [
  "windows-sys 0.52.0",
 ]
 
+[[package]]
+name = "ringbuffer"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "57b0b88a509053cbfd535726dcaaceee631313cef981266119527a1d110f6d2b"
+
 [[package]]
 name = "rkyv"
 version = "0.7.45"
@@ -6881,51 +6965,6 @@ dependencies = [
  "zeroize",
 ]
 
-[[package]]
-name = "rust-ini"
-version = "0.21.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
-dependencies = [
- "cfg-if",
- "ordered-multimap",
-]
-
-[[package]]
-name = "rust-s3"
-version = "0.37.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94f9b973bd4097f5bb47e5827dcb9fb5dc17e93879e46badc27d2a4e9a4e5588"
-dependencies = [
- "async-trait",
- "aws-creds",
- "aws-region",
- "base64 0.22.1",
- "bytes",
- "cfg-if",
- "futures-util",
- "hex",
- "hmac",
- "http 1.3.1",
- "log",
- "maybe-async",
- "md5",
- "minidom",
- "percent-encoding",
- "quick-xml",
- "reqwest",
- "serde",
- "serde_derive",
- "serde_json",
- "sha2",
- "sysinfo 0.37.2",
- "thiserror 2.0.17",
- "time",
- "tokio",
- "tokio-stream",
- "url",
-]
-
 [[package]]
 name = "rust_decimal"
 version = "1.39.0"
@@ -6995,7 +7034,7 @@ dependencies = [
  "openssl-probe",
  "rustls-pki-types",
  "schannel",
- "security-framework",
+ "security-framework 3.5.1",
 ]
 
 [[package]]
@@ -7009,9 +7048,9 @@ dependencies = [
 
 [[package]]
 name = "rustls-pki-types"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79"
+checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
 dependencies = [
  "web-time",
  "zeroize",
@@ -7023,7 +7062,7 @@ version = "0.6.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784"
 dependencies = [
- "core-foundation",
+ "core-foundation 0.10.1",
  "core-foundation-sys",
  "jni",
  "log",
@@ -7032,7 +7071,7 @@ dependencies = [
  "rustls-native-certs",
  "rustls-platform-verifier-android",
  "rustls-webpki",
- "security-framework",
+ "security-framework 3.5.1",
  "security-framework-sys",
  "webpki-root-certs",
  "windows-sys 0.61.2",
@@ -7063,22 +7102,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
 
 [[package]]
-name = "rxml"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65bc94b580d0f5a6b7a2d604e597513d3c673154b52ddeccd1d5c32360d945ee"
-dependencies = [
- "bytes",
- "rxml_validation",
-]
-
-[[package]]
-name = "rxml_validation"
-version = "0.11.0"
+name = "rusty-s3"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "826e80413b9a35e9d33217b3dcac04cf95f6559d15944b93887a08be5496c4a4"
+checksum = "8f51a5a6b15f25d3e10c068039ee13befb6110fcb36c2b26317bcbdc23484d96"
 dependencies = [
- "compact_str",
+ "base64 0.22.1",
+ "hmac",
+ "md-5",
+ "percent-encoding",
+ "quick-xml",
+ "serde",
+ "serde_json",
+ "sha2",
+ "time",
+ "url",
+ "zeroize",
 ]
 
 [[package]]
@@ -7152,6 +7191,12 @@ dependencies = [
  "syn 2.0.108",
 ]
 
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
 [[package]]
 name = "scopeguard"
 version = "1.2.0"
@@ -7183,26 +7228,25 @@ dependencies = [
 ]
 
 [[package]]
-name = "sec1"
-version = "0.7.3"
+name = "secrecy"
+version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
 dependencies = [
- "base16ct",
- "der",
- "generic-array",
- "pkcs8",
- "subtle",
  "zeroize",
 ]
 
 [[package]]
-name = "secrecy"
-version = "0.10.3"
+name = "security-framework"
+version = "2.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
+checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02"
 dependencies = [
- "zeroize",
+ "bitflags 2.10.0",
+ "core-foundation 0.9.4",
+ "core-foundation-sys",
+ "libc",
+ "security-framework-sys",
 ]
 
 [[package]]
@@ -7212,7 +7256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef"
 dependencies = [
  "bitflags 2.10.0",
- "core-foundation",
+ "core-foundation 0.10.1",
  "core-foundation-sys",
  "libc",
  "security-framework-sys",
@@ -7228,6 +7272,16 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "seize"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b55fb86dfd3a2f5f76ea78310a88f96c4ea21a3031f8d212443d56123fd0521"
+dependencies = [
+ "libc",
+ "windows-sys 0.61.2",
+]
+
 [[package]]
 name = "semver"
 version = "1.0.27"
@@ -7238,6 +7292,15 @@ dependencies = [
  "serde_core",
 ]
 
+[[package]]
+name = "send_wrapper"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
+dependencies = [
+ "futures-core",
+]
+
 [[package]]
 name = "serde"
 version = "1.0.228"
@@ -7441,7 +7504,9 @@ version = "0.5.0"
 dependencies = [
  "ahash 0.8.12",
  "anyhow",
+ "arcshift",
  "argon2",
+ "async-channel",
  "async_zip",
  "axum 0.8.6",
  "axum-server",
@@ -7450,8 +7515,17 @@ dependencies = [
  "bytes",
  "chrono",
  "clap",
+ "compio",
+ "compio-io",
+ "compio-net",
+ "compio-quic",
+ "compio-tls",
+ "compio-ws",
  "console-subscriber",
  "crossbeam",
+ "ctrlc",
+ "cyper",
+ "cyper-axum",
  "dashmap",
  "derive_more 2.0.1",
  "dotenvy",
@@ -7462,12 +7536,12 @@ dependencies = [
  "figment",
  "flume",
  "futures",
+ "hash32 1.0.0",
  "human-repr",
  "iggy_common",
- "jsonwebtoken 10.1.0",
+ "jsonwebtoken",
  "lending-iterator",
  "mimalloc",
- "mockall",
  "moka",
  "nix",
  "once_cell",
@@ -7476,31 +7550,35 @@ dependencies = [
  "opentelemetry-otlp",
  "opentelemetry-semantic-conventions",
  "opentelemetry_sdk",
+ "papaya",
  "prometheus-client",
  "quinn",
  "rand 0.9.2",
  "reqwest",
  "ring",
- "rust-s3",
+ "ringbuffer",
  "rustls",
  "rustls-pemfile",
+ "rusty-s3",
+ "send_wrapper",
  "serde",
  "serde_with",
  "serial_test",
+ "slab",
+ "socket2 0.6.1",
  "static-toml",
  "strum",
  "sysinfo 0.37.2",
  "tempfile",
+ "test-case",
  "thiserror 2.0.17",
- "tokio",
- "tokio-rustls",
- "tokio-util",
  "toml 0.9.8",
  "tower-http",
  "tracing",
  "tracing-appender",
  "tracing-opentelemetry",
  "tracing-subscriber",
+ "tungstenite",
  "twox-hash",
  "ulid",
  "uuid",
@@ -7571,10 +7649,11 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
 
 [[package]]
 name = "simd-json"
-version = "0.17.0"
+version = "0.15.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4255126f310d2ba20048db6321c81ab376f6a6735608bf11f0785c41f01f64e3"
+checksum = "c962f626b54771990066e5435ec8331d1462576cd2d1e62f24076ae014f92112"
 dependencies = [
+ "getrandom 0.3.4",
  "halfbrown",
  "ref-cast",
  "serde",
@@ -7939,12 +8018,6 @@ dependencies = [
  "toml 0.8.23",
 ]
 
-[[package]]
-name = "static_assertions"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
-
 [[package]]
 name = "stringprep"
 version = "0.1.5"
@@ -8190,13 +8263,13 @@ dependencies = [
 
 [[package]]
 name = "testcontainers"
-version = "0.25.0"
+version = "0.25.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b92bce247dc9260a19808321e11b51ea6a0293d02b48ab1c6578960610cfa2a7"
+checksum = "3f3ac71069f20ecfa60c396316c283fbf35e6833a53dff551a31b5458da05edc"
 dependencies = [
+ "astral-tokio-tar",
  "async-trait",
  "bollard",
- "bollard-stubs",
  "bytes",
  "docker_credential",
  "either",
@@ -8212,7 +8285,6 @@ dependencies = [
  "thiserror 2.0.17",
  "tokio",
  "tokio-stream",
- "tokio-tar",
  "tokio-util",
  "ulid",
  "url",
@@ -8320,15 +8392,6 @@ dependencies = [
  "time-core",
 ]
 
-[[package]]
-name = "tiny-keccak"
-version = "2.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237"
-dependencies = [
- "crunchy",
-]
-
 [[package]]
 name = "tinystr"
 version = "0.8.1"
@@ -8405,18 +8468,19 @@ dependencies = [
 ]
 
 [[package]]
-name = "tokio-tar"
-version = "0.3.1"
+name = "tokio-tungstenite"
+version = "0.27.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d5714c010ca3e5c27114c1cdeb9d14641ace49874aa5626d7149e47aedace75"
+checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1"
 dependencies = [
- "filetime",
- "futures-core",
- "libc",
- "redox_syscall 0.3.5",
+ "futures-util",
+ "log",
+ "rustls",
+ "rustls-pki-types",
  "tokio",
- "tokio-stream",
- "xattr",
+ "tokio-rustls",
+ "tungstenite",
+ "webpki-roots 0.26.11",
 ]
 
 [[package]]
@@ -8566,35 +8630,6 @@ dependencies = [
  "tracing",
 ]
 
-[[package]]
-name = "tonic"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9"
-dependencies = [
- "async-trait",
- "axum 0.8.6",
- "base64 0.22.1",
- "bytes",
- "h2 0.4.12",
- "http 1.3.1",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-timeout",
- "hyper-util",
- "percent-encoding",
- "pin-project",
- "prost 0.13.5",
- "socket2 0.5.10",
- "tokio",
- "tokio-stream",
- "tower 0.5.2",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
 [[package]]
 name = "tonic"
 version = "0.14.2"
@@ -8602,8 +8637,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "eb7613188ce9f7df5bfe185db26c5814347d110db17920415cf2fbcad85e7203"
 dependencies = [
  "async-trait",
+ "axum 0.8.6",
  "base64 0.22.1",
  "bytes",
+ "h2 0.4.12",
  "http 1.3.1",
  "http-body",
  "http-body-util",
@@ -8612,6 +8649,7 @@ dependencies = [
  "hyper-util",
  "percent-encoding",
  "pin-project",
+ "socket2 0.6.1",
  "sync_wrapper",
  "tokio",
  "tokio-stream",
@@ -8823,6 +8861,25 @@ version = "0.2.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
 
+[[package]]
+name = "tungstenite"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eadc29d668c91fcc564941132e17b28a7ceb2f3ebf0b9dae3e03fd7a6748eb0d"
+dependencies = [
+ "bytes",
+ "data-encoding",
+ "http 1.3.1",
+ "httparse",
+ "log",
+ "rand 0.9.2",
+ "rustls",
+ "rustls-pki-types",
+ "sha1",
+ "thiserror 2.0.17",
+ "utf-8",
+]
+
 [[package]]
 name = "twox-hash"
 version = "2.1.2"
@@ -8976,17 +9033,31 @@ checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae"
 
 [[package]]
 name = "ureq"
-version = "2.12.1"
+version = "3.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
+checksum = "99ba1025f18a4a3fc3e9b48c868e9beb4f24f4b4b1a325bada26bd4119f46537"
 dependencies = [
  "base64 0.22.1",
  "log",
- "once_cell",
+ "percent-encoding",
  "rustls",
+ "rustls-pemfile",
  "rustls-pki-types",
- "url",
- "webpki-roots 0.26.11",
+ "ureq-proto",
+ "utf-8",
+ "webpki-roots 1.0.3",
+]
+
+[[package]]
+name = "ureq-proto"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2"
+dependencies = [
+ "base64 0.22.1",
+ "http 1.3.1",
+ "httparse",
+ "log",
 ]
 
 [[package]]
@@ -9007,6 +9078,12 @@ version = "2.1.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
 
+[[package]]
+name = "utf-8"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
+
 [[package]]
 name = "utf8-width"
 version = "0.1.7"
@@ -9053,9 +9130,9 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
 
 [[package]]
 name = "value-trait"
-version = "0.12.1"
+version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e80f0c733af0720a501b3905d22e2f97662d8eacfe082a75ed7ffb5ab08cb59"
+checksum = "0508fce11ad19e0aab49ce20b6bec7f8f82902ded31df1c9fc61b90f0eb396b8"
 dependencies = [
  "float-cmp",
  "halfbrown",
@@ -9175,9 +9252,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.104"
+version = "0.2.105"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d"
+checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -9186,25 +9263,11 @@ dependencies = [
  "wasm-bindgen-shared",
 ]
 
-[[package]]
-name = "wasm-bindgen-backend"
-version = "0.2.104"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19"
-dependencies = [
- "bumpalo",
- "log",
- "proc-macro2",
- "quote",
- "syn 2.0.108",
- "wasm-bindgen-shared",
-]
-
 [[package]]
 name = "wasm-bindgen-futures"
-version = "0.4.54"
+version = "0.4.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c"
+checksum = "551f88106c6d5e7ccc7cd9a16f312dd3b5d36ea8b4954304657d5dfba115d4a0"
 dependencies = [
  "cfg-if",
  "js-sys",
@@ -9215,9 +9278,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.104"
+version = "0.2.105"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119"
+checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -9225,22 +9288,22 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.104"
+version = "0.2.105"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7"
+checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc"
 dependencies = [
+ "bumpalo",
  "proc-macro2",
  "quote",
  "syn 2.0.108",
- "wasm-bindgen-backend",
  "wasm-bindgen-shared",
 ]
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.104"
+version = "0.2.105"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1"
+checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76"
 dependencies = [
  "unicode-ident",
 ]
@@ -9275,9 +9338,9 @@ dependencies = [
 
 [[package]]
 name = "web-sys"
-version = "0.3.81"
+version = "0.3.82"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120"
+checksum = "3a1f95c0d03a47f4ae1f7a64643a6bb97465d9b740f0fa8f90ea33915c99a9a1"
 dependencies = [
  "js-sys",
  "wasm-bindgen",
@@ -9331,6 +9394,12 @@ dependencies = [
  "wasite",
 ]
 
+[[package]]
+name = "widestring"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471"
+
 [[package]]
 name = "winapi"
 version = "0.3.9"
diff --git a/Cargo.toml b/Cargo.toml
index 9214191cb..c21d1c50e 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -60,7 +60,7 @@ async_zip = { version = "0.0.18", features = [
     "deflate",
     "zstd",
 ] }
-axum = "0.8.6"
+axum = { version = "0.8.6", features = ["macros"] }
 axum-server = { version = "0.7.2", features = ["tls-rustls"] }
 base64 = "0.22.1"
 bench-dashboard-frontend = { path = "core/bench/dashboard/frontend" }
@@ -97,6 +97,7 @@ env_logger = "0.11.8"
 figlet-rs = "0.1.5"
 figment = { version = "0.10.19", features = ["toml", "env"] }
 flume = "0.11.1"
+async-channel = "2.3.1"
 futures = "0.3.31"
 futures-util = "0.3.31"
 human-repr = "1.1.0"
@@ -114,17 +115,18 @@ mockall = "0.13.1"
 nonzero_lit = "0.1.2"
 once_cell = "1.21.3"
 passterm = "=2.0.1"
-postcard = { version = "1.1.3", features = ["alloc"] }
 predicates = "3.1.3"
-quinn = "0.11.9"
-rand = "0.9.2"
+quinn = "0.11.8"
+postcard = { version = "1.1.1", features = ["alloc"] }
+rand = "0.9.1"
 regex = "1.12.2"
-reqwest = { version = "0.12.24", default-features = false, features = [
+reqwest = { version = "0.12.20", default-features = false, features = [
     "json",
     "rustls-tls",
 ] }
 reqwest-middleware = { version = "0.4.2", features = ["json"] }
 reqwest-retry = "0.7.0"
+serde_yml = "0.0.12"
 rust-s3 = { version = "0.37.0", default-features = false, features = [
     "tokio-rustls-tls",
     "tags",
@@ -136,17 +138,39 @@ serde_with = { version = "3.15.1", features = ["base64", "macros"] }
 serde_yaml_ng = "0.10.0"
 serial_test = "3.2.0"
 server = { path = "core/server" }
-simd-json = { version = "0.17.0", features = ["serde_impl"] }
+simd-json = { version = "0.15.1", features = ["serde_impl"] }
 strum = { version = "0.27.2", features = ["derive"] }
 strum_macros = "0.27.2"
-sysinfo = "0.37.2"
-tempfile = "3.23.0"
+sysinfo = "0.37.0"
+tempfile = "3.21.0"
 test-case = "3.3.1"
-thiserror = "2.0.17"
-tokio = { version = "1.48.0", features = ["full"] }
-tokio-rustls = "0.26.4"
+thiserror = "2.0.16"
+tokio = { version = "1.47.1", features = ["full"] }
+compio = { git = "https://github.com/compio-rs/compio.git", rev = "fe4243f0b6811ebc325afd081c9b087b4d9817be", features = [
+    "runtime",
+    "macros",
+    "io-uring",
+    "time",
+    "rustls",
+] }
+cyper = { git = "https://github.com/krishvishal/cyper.git", rev = "cd75e266df6ab0a9b9474eb7dda1735650d17db6", features = [
+    "rustls",
+], default-features = false }
+cyper-axum = { git = "https://github.com/krishvishal/cyper", rev = "cd75e266df6ab0a9b9474eb7dda1735650d17db6" }
+compio-net = { git = "https://github.com/compio-rs/compio.git", rev = "fe4243f0b6811ebc325afd081c9b087b4d9817be" }
+compio-quic = { git = "https://github.com/compio-rs/compio.git", rev = "fe4243f0b6811ebc325afd081c9b087b4d9817be" }
+compio-tls = { git = "https://github.com/compio-rs/compio.git", rev = "fe4243f0b6811ebc325afd081c9b087b4d9817be", features = [
+    "rustls",
+] }
+compio-io = { git = "https://github.com/compio-rs/compio.git", rev = "fe4243f0b6811ebc325afd081c9b087b4d9817be" }
+compio-ws = { git = "https://github.com/krishvishal/compio-ws", features = [
+    "rustls",
+] }
+tungstenite = "0.27.0"
+tokio-tungstenite = { version = "0.27", features = ["rustls-tls-webpki-roots"] }
+tokio-rustls = "0.26.2"
 tokio-util = { version = "0.7.16", features = ["compat"] }
-toml = "0.9.8"
+toml = "0.9.5"
 tower-http = { version = "0.6.6", features = [
     "add-extension",
     "cors",
diff --git a/DEPENDENCIES.md b/DEPENDENCIES.md
index 503423dfc..983f95ab9 100644
--- a/DEPENDENCIES.md
+++ b/DEPENDENCIES.md
@@ -276,8 +276,8 @@ getrandom: 0.3.4, "Apache-2.0 OR MIT",
 ghash: 0.5.1, "Apache-2.0 OR MIT",
 gherkin: 0.14.0, "Apache-2.0 OR MIT",
 git2: 0.20.2, "Apache-2.0 OR MIT",
-glob: 0.3.3, "Apache-2.0 OR MIT",
-globset: 0.4.18, "MIT OR Unlicense",
+glob: 0.3.2, "Apache-2.0 OR MIT",
+globset: 0.4.16, "MIT OR Unlicense",
 globwalk: 0.9.1, "MIT",
 gloo: 0.8.1, "Apache-2.0 OR MIT",
 gloo: 0.10.0, "Apache-2.0 OR MIT",
@@ -479,7 +479,6 @@ num-integer: 0.1.46, "Apache-2.0 OR MIT",
 num-iter: 0.1.45, "Apache-2.0 OR MIT",
 num-modular: 0.6.1, "Apache-2.0",
 num-order: 1.2.0, "Apache-2.0",
-num-rational: 0.4.2, "Apache-2.0 OR MIT",
 num-traits: 0.2.19, "Apache-2.0 OR MIT",
 num_cpus: 1.17.0, "Apache-2.0 OR MIT",
 num_threads: 0.1.7, "Apache-2.0 OR MIT",
diff --git a/helm/charts/iggy/templates/iggy-pvc.yaml b/Dockerfile.connectors.ci
similarity index 51%
copy from helm/charts/iggy/templates/iggy-pvc.yaml
copy to Dockerfile.connectors.ci
index c1a5e8cb5..83d9986a5 100644
--- a/helm/charts/iggy/templates/iggy-pvc.yaml
+++ b/Dockerfile.connectors.ci
@@ -15,29 +15,30 @@
 # specific language governing permissions and limitations
 # under the License.
 
-{{- if and .Values.server.persistence.enabled (not .Values.server.persistence.existingClaim) }}
-kind: PersistentVolumeClaim
-apiVersion: v1
-metadata:
-  name: {{ template "iggy.fullname" . }}
-  labels:
-    {{- include "iggy.labels" . | nindent 8 }}
-  annotations:
-    helm.sh/resource-policy: keep
-  {{- with .Values.server.persistence.annotations }}
-    {{- toYaml . | nindent 4 }}
-  {{- end }}
-spec:
-  accessModes:
-    - {{ .Values.server.persistence.accessMode | quote }}
-  resources:
-    requests:
-      storage: {{ .Values.server.persistence.size | quote }}
-  {{- with .Values.server.persistence.storageClass }}
-  {{- if (eq "-" .) }}
-  storageClassName: ""
-  {{- else }}
-  storageClassName: "{{ . }}"
-  {{- end }}
-  {{- end }}
-{{- end }}
+<<<<<<<< HEAD:Dockerfile.connectors.ci
+FROM alpine:latest
+
+ARG IGGY_CONNECTORS_PATH
+RUN test -n "$IGGY_CONNECTORS_PATH" || (echo "IGGY_CONNECTORS_PATH not set" && false)
+
+WORKDIR /iggy
+
+COPY ./core/connectors/runtime/config.toml ./config.toml
+COPY ${IGGY_CONNECTORS_PATH} ./
+
+RUN chmod +x ./iggy-connectors
+
+RUN ln -sf /iggy/iggy-connectors /usr/bin/iggy-connectors
+
+CMD ["/iggy/iggy-connectors"]
+========
+FROM mcr.microsoft.com/dotnet/sdk:8.0
+
+COPY ./foreign/csharp ./foreign/csharp
+COPY ./bdd/scenarios ./bdd/scenarios
+
+WORKDIR /foreign/csharp/Iggy_SDK.Tests.BDD
+RUN dotnet build -c Release
+
+CMD ["dotnet", "test"]
+>>>>>>>> master:bdd/csharp/Dockerfile
diff --git a/helm/charts/iggy/templates/iggy-pvc.yaml b/Dockerfile.connectors.ci.dockerignore
similarity index 51%
copy from helm/charts/iggy/templates/iggy-pvc.yaml
copy to Dockerfile.connectors.ci.dockerignore
index c1a5e8cb5..e2fdd5f0d 100644
--- a/helm/charts/iggy/templates/iggy-pvc.yaml
+++ b/Dockerfile.connectors.ci.dockerignore
@@ -15,29 +15,16 @@
 # specific language governing permissions and limitations
 # under the License.
 
-{{- if and .Values.server.persistence.enabled (not .Values.server.persistence.existingClaim) }}
-kind: PersistentVolumeClaim
-apiVersion: v1
-metadata:
-  name: {{ template "iggy.fullname" . }}
-  labels:
-    {{- include "iggy.labels" . | nindent 8 }}
-  annotations:
-    helm.sh/resource-policy: keep
-  {{- with .Values.server.persistence.annotations }}
-    {{- toYaml . | nindent 4 }}
-  {{- end }}
-spec:
-  accessModes:
-    - {{ .Values.server.persistence.accessMode | quote }}
-  resources:
-    requests:
-      storage: {{ .Values.server.persistence.size | quote }}
-  {{- with .Values.server.persistence.storageClass }}
-  {{- if (eq "-" .) }}
-  storageClassName: ""
-  {{- else }}
-  storageClassName: "{{ . }}"
-  {{- end }}
-  {{- end }}
-{{- end }}
+.config
+.github
+/assets
+/local_data
+/licenses
+/certs
+/helm
+/web
+Dockerfile
+docker-compose.yml
+.dockerignore
+.git
+.gitignore
diff --git a/helm/charts/iggy/templates/iggy-pvc.yaml b/Dockerfile.mcp.ci.dockerignore
similarity index 51%
copy from helm/charts/iggy/templates/iggy-pvc.yaml
copy to Dockerfile.mcp.ci.dockerignore
index c1a5e8cb5..e2fdd5f0d 100644
--- a/helm/charts/iggy/templates/iggy-pvc.yaml
+++ b/Dockerfile.mcp.ci.dockerignore
@@ -15,29 +15,16 @@
 # specific language governing permissions and limitations
 # under the License.
 
-{{- if and .Values.server.persistence.enabled (not .Values.server.persistence.existingClaim) }}
-kind: PersistentVolumeClaim
-apiVersion: v1
-metadata:
-  name: {{ template "iggy.fullname" . }}
-  labels:
-    {{- include "iggy.labels" . | nindent 8 }}
-  annotations:
-    helm.sh/resource-policy: keep
-  {{- with .Values.server.persistence.annotations }}
-    {{- toYaml . | nindent 4 }}
-  {{- end }}
-spec:
-  accessModes:
-    - {{ .Values.server.persistence.accessMode | quote }}
-  resources:
-    requests:
-      storage: {{ .Values.server.persistence.size | quote }}
-  {{- with .Values.server.persistence.storageClass }}
-  {{- if (eq "-" .) }}
-  storageClassName: ""
-  {{- else }}
-  storageClassName: "{{ . }}"
-  {{- end }}
-  {{- end }}
-{{- end }}
+.config
+.github
+/assets
+/local_data
+/licenses
+/certs
+/helm
+/web
+Dockerfile
+docker-compose.yml
+.dockerignore
+.git
+.gitignore
diff --git a/bdd/docker-compose.yml b/bdd/docker-compose.yml
index ab85afff3..86bbc9fa9 100644
--- a/bdd/docker-compose.yml
+++ b/bdd/docker-compose.yml
@@ -27,7 +27,21 @@ services:
         PREBUILT_IGGY_CLI: ${IGGY_CLI_PATH:-target/debug/iggy}
         LIBC: glibc
         PROFILE: debug
-    command: ["--fresh"]
+    command: ["--fresh", "--with-default-root-credentials"]
+    cap_add:
+      - SYS_NICE
+    security_opt:
+      - seccomp:unconfined
+    ulimits:
+      memlock:
+        soft: -1
+        hard: -1
+    healthcheck:
+      test: ["CMD", "/usr/local/bin/iggy", "ping"]
+      interval: 1s
+      timeout: 3s
+      retries: 30
+      start_period: 2s
     environment:
       - IGGY_ROOT_USERNAME=iggy
       - IGGY_ROOT_PASSWORD=iggy
@@ -105,7 +119,8 @@ services:
       context: ..
       dockerfile: bdd/node/Dockerfile
     depends_on:
-      - iggy-server
+      iggy-server:
+        condition: service_healthy
     environment:
       - IGGY_ROOT_USERNAME=iggy
       - IGGY_ROOT_PASSWORD=iggy
diff --git a/bdd/go/tests/tcp_test/stream_steps.go b/bdd/go/tests/tcp_test/stream_steps.go
index 50f0f4dfa..32729b273 100644
--- a/bdd/go/tests/tcp_test/stream_steps.go
+++ b/bdd/go/tests/tcp_test/stream_steps.go
@@ -19,7 +19,6 @@ package tcp_test
 
 import (
 	"fmt"
-
 	iggcon "github.com/apache/iggy/foreign/go/contracts"
 	ierror "github.com/apache/iggy/foreign/go/errors"
 	"github.com/apache/iggy/foreign/go/iggycli"
diff --git a/bdd/python/tests/test_basic_messaging.py b/bdd/python/tests/test_basic_messaging.py
index 9abd177c9..ea5cc8ed5 100644
--- a/bdd/python/tests/test_basic_messaging.py
+++ b/bdd/python/tests/test_basic_messaging.py
@@ -65,12 +65,12 @@ def no_streams_in_system(context):
     pass
 
 
-@when(parsers.parse('I create a stream with ID {stream_id:d} and name {stream_name}'))
-def create_stream(context, stream_id, stream_name):
-    """Create a stream with specified ID and name"""
+@when(parsers.parse('I create a stream with name {stream_name}'))
+def create_stream(context, stream_name):
+    """Create a stream with specified name"""
     async def _create():
-        await context.client.create_stream(name=stream_name, stream_id=stream_id)
-        context.last_stream_id = stream_id
+        stream = await context.client.create_stream(name=stream_name)
+        context.last_stream_id = stream.id
         context.last_stream_name = stream_name
 
     asyncio.run(_create())
@@ -86,29 +86,27 @@ def stream_created_successfully(context):
     asyncio.run(_verify())
 
 
-@then(parsers.parse('the stream should have ID {stream_id:d} and name {stream_name}'))
-def verify_stream_properties(context, stream_id, stream_name):
-    """Verify stream has correct ID and name"""
+@then(parsers.parse('the stream should have name {stream_name}'))
+def verify_stream_properties(context, stream_name):
+    """Verify stream has correct name"""
     async def _verify():
         stream = await context.client.get_stream(stream_name)
         assert stream is not None
-        assert stream.id == stream_id
         assert stream.name == stream_name
 
     asyncio.run(_verify())
 
 
-@when(parsers.parse('I create a topic with ID {topic_id:d} and name {topic_name} in stream {stream_id:d} with {partitions:d} partitions'))
-def create_topic(context, topic_id, topic_name, stream_id, partitions):
+@when(parsers.parse('I create a topic with name {topic_name} in stream {stream_id:d} with {partitions:d} partitions'))
+def create_topic(context, topic_name, stream_id, partitions):
     """Create a topic with specified parameters"""
     async def _create():
-        await context.client.create_topic(
+        topic = await context.client.create_topic(
             stream=stream_id,
             name=topic_name,
-            partitions_count=partitions,
-            topic_id=topic_id
+            partitions_count=partitions
         )
-        context.last_topic_id = topic_id
+        context.last_topic_id = topic.id
         context.last_topic_name = topic_name
         context.last_topic_partitions = partitions
 
@@ -125,13 +123,12 @@ def topic_created_successfully(context):
     asyncio.run(_verify())
 
 
-@then(parsers.parse('the topic should have ID {topic_id:d} and name {topic_name}'))
-def verify_topic_properties(context, topic_id, topic_name):
-    """Verify topic has correct ID and name"""
+@then(parsers.parse('the topic should have name {topic_name}'))
+def verify_topic_properties(context, topic_name):
+    """Verify topic has correct name"""
     async def _verify():
         topic = await context.client.get_topic(context.last_stream_id, topic_name)
         assert topic is not None
-        assert topic.id == topic_id
         assert topic.name == topic_name
 
     asyncio.run(_verify())
diff --git a/bdd/rust/tests/steps/streams.rs b/bdd/rust/tests/steps/streams.rs
index f64f680ec..f24a67919 100644
--- a/bdd/rust/tests/steps/streams.rs
+++ b/bdd/rust/tests/steps/streams.rs
@@ -33,11 +33,11 @@ pub async fn given_no_streams(world: &mut GlobalContext) {
     );
 }
 
-#[when(regex = r"^I create a stream with ID (\d+) and name (.+)$")]
-pub async fn when_create_stream(world: &mut GlobalContext, stream_id: u32, stream_name: String) {
+#[when(regex = r"^I create a stream with name (.+)$")]
+pub async fn when_create_stream(world: &mut GlobalContext, stream_name: String) {
     let client = world.client.as_ref().expect("Client should be available");
     let stream = client
-        .create_stream(&stream_name, Some(stream_id))
+        .create_stream(&stream_name)
         .await
         .expect("Should be able to create stream");
 
@@ -53,18 +53,12 @@ pub async fn then_stream_created_successfully(world: &mut GlobalContext) {
     );
 }
 
-#[then(regex = r"^the stream should have ID (\d+) and name (.+)$")]
-pub async fn then_stream_has_id_and_name(
-    world: &mut GlobalContext,
-    expected_id: u32,
-    expected_name: String,
-) {
-    let stream_id = world.last_stream_id.expect("Stream should exist");
+#[then(regex = r"^the stream should have name (.+)$")]
+pub async fn then_stream_has_name(world: &mut GlobalContext, expected_name: String) {
     let stream_name = world
         .last_stream_name
         .as_ref()
         .expect("Stream should exist");
-    assert_eq!(stream_id, expected_id, "Stream should have expected ID");
     assert_eq!(
         stream_name, &expected_name,
         "Stream should have expected name"
diff --git a/bdd/rust/tests/steps/topics.rs b/bdd/rust/tests/steps/topics.rs
index 93557663b..63150f326 100644
--- a/bdd/rust/tests/steps/topics.rs
+++ b/bdd/rust/tests/steps/topics.rs
@@ -20,12 +20,9 @@ use crate::common::global_context::GlobalContext;
 use cucumber::{then, when};
 use iggy::prelude::{CompressionAlgorithm, Identifier, IggyExpiry, MaxTopicSize, TopicClient};
 
-#[when(
-    regex = r"^I create a topic with ID (\d+) and name (.+) in stream (\d+) with (\d+) partitions$"
-)]
+#[when(regex = r"^I create a topic with name (.+) in stream (\d+) with (\d+) partitions$")]
 pub async fn when_create_topic(
     world: &mut GlobalContext,
-    topic_id: u32,
     topic_name: String,
     stream_id: u32,
     partitions_count: u32,
@@ -38,7 +35,6 @@ pub async fn when_create_topic(
             partitions_count,
             CompressionAlgorithm::default(),
             None,
-            Some(topic_id),
             IggyExpiry::NeverExpire,
             MaxTopicSize::ServerDefault,
         )
@@ -58,15 +54,9 @@ pub async fn then_topic_created_successfully(world: &mut GlobalContext) {
     );
 }
 
-#[then(regex = r"^the topic should have ID (\d+) and name (.+)$")]
-pub async fn then_topic_has_id_and_name(
-    world: &mut GlobalContext,
-    expected_id: u32,
-    expected_name: String,
-) {
-    let topic_id = world.last_topic_id.expect("Topic should exist");
+#[then(regex = r"^the topic should have name (.+)$")]
+pub async fn then_topic_has_name(world: &mut GlobalContext, expected_name: String) {
     let topic_name = world.last_topic_name.as_ref().expect("Topic should exist");
-    assert_eq!(topic_id, expected_id, "Topic should have expected ID");
     assert_eq!(
         topic_name, &expected_name,
         "Topic should have expected name"
diff --git a/bdd/scenarios/basic_messaging.feature b/bdd/scenarios/basic_messaging.feature
index 1709626e1..c13109dc8 100644
--- a/bdd/scenarios/basic_messaging.feature
+++ b/bdd/scenarios/basic_messaging.feature
@@ -26,19 +26,19 @@ Feature: Basic Messaging Operations
 
   Scenario: Create stream and send messages
     Given I have no streams in the system
-    When I create a stream with ID 1 and name "test-stream"
+    When I create a stream with name "test-stream"
     Then the stream should be created successfully
-    And the stream should have ID 1 and name "test-stream"
+    And the stream should have name "test-stream"
 
-    When I create a topic with ID 1 and name "test-topic" in stream 1 with 3 partitions
+    When I create a topic with name "test-topic" in stream 0 with 3 partitions
     Then the topic should be created successfully
-    And the topic should have ID 1 and name "test-topic" 
+    And the topic should have name "test-topic"
     And the topic should have 3 partitions
 
-    When I send 10 messages to stream 1, topic 1, partition 1
+    When I send 10 messages to stream 0, topic 0, partition 0
     Then all messages should be sent successfully
 
-    When I poll messages from stream 1, topic 1, partition 1 starting from offset 0
+    When I poll messages from stream 0, topic 0, partition 0 starting from offset 0
     Then I should receive 10 messages
     And the messages should have sequential offsets from 0 to 9
     And each message should have the expected payload content
diff --git a/core/ai/mcp/src/service/mod.rs b/core/ai/mcp/src/service/mod.rs
index 24d28c0c0..166e839d0 100644
--- a/core/ai/mcp/src/service/mod.rs
+++ b/core/ai/mcp/src/service/mod.rs
@@ -79,10 +79,10 @@ impl IggyService {
     #[tool(description = "Create stream")]
     pub async fn create_stream(
         &self,
-        Parameters(CreateStream { name, stream_id }): Parameters<CreateStream>,
+        Parameters(CreateStream { name }): Parameters<CreateStream>,
     ) -> Result<CallToolResult, ErrorData> {
         self.permissions.ensure_create()?;
-        request(self.client.create_stream(&name, stream_id).await)
+        request(self.client.create_stream(&name).await)
     }
 
     #[tool(description = "Update stream")]
@@ -146,7 +146,6 @@ impl IggyService {
             partitions_count,
             compression_algorithm,
             replication_factor,
-            topic_id,
             message_expiry,
             max_size,
         }): Parameters<CreateTopic>,
@@ -167,7 +166,6 @@ impl IggyService {
                     partitions_count,
                     compression_algorithm,
                     replication_factor,
-                    topic_id,
                     message_expiry,
                     max_size,
                 )
@@ -501,13 +499,12 @@ impl IggyService {
             stream_id,
             topic_id,
             name,
-            group_id,
         }): Parameters<CreateConsumerGroup>,
     ) -> Result<CallToolResult, ErrorData> {
         self.permissions.ensure_create()?;
         request(
             self.client
-                .create_consumer_group(&id(&stream_id)?, &id(&topic_id)?, &name, group_id)
+                .create_consumer_group(&id(&stream_id)?, &id(&topic_id)?, &name)
                 .await,
         )
     }
diff --git a/core/ai/mcp/src/service/requests.rs b/core/ai/mcp/src/service/requests.rs
index 1a8aea5c1..a7cbfb05d 100644
--- a/core/ai/mcp/src/service/requests.rs
+++ b/core/ai/mcp/src/service/requests.rs
@@ -32,8 +32,6 @@ pub struct GetStream {
 pub struct CreateStream {
     #[schemars(description = "stream name (required, must be unique)")]
     pub name: String,
-    #[schemars(description = "stream identifier (numeric, optional)")]
-    pub stream_id: Option<u32>,
 }
 
 #[derive(Debug, Deserialize, JsonSchema)]
@@ -88,9 +86,6 @@ pub struct CreateTopic {
     #[schemars(description = "replication factor (optional, must be greater than 0)")]
     pub replication_factor: Option<u8>,
 
-    #[schemars(description = "topic identifier (numeric, optional)")]
-    pub topic_id: Option<u32>,
-
     #[schemars(description = "message expiry (optional)")]
     pub message_expiry: Option<String>,
 
@@ -282,9 +277,6 @@ pub struct CreateConsumerGroup {
 
     #[schemars(description = "consumer group name (required, must be unique)")]
     pub name: String,
-
-    #[schemars(description = "consumer group identifier (optional, number)")]
-    pub group_id: Option<u32>,
 }
 
 #[derive(Debug, Deserialize, JsonSchema)]
@@ -419,7 +411,7 @@ pub struct Permissions {
     pub global: Option<GlobalPermissions>,
 
     #[schemars(description = "stream permissions (optional)")]
-    pub streams: Option<HashMap<u32, StreamPermissions>>,
+    pub streams: Option<HashMap<usize, StreamPermissions>>,
 }
 
 #[derive(Debug, Deserialize, JsonSchema)]
@@ -476,7 +468,7 @@ pub struct StreamPermissions {
     pub send_messages: Option<bool>,
 
     #[schemars(description = "topics permissions (optional)")]
-    pub topics: Option<HashMap<u32, TopicPermissions>>,
+    pub topics: Option<HashMap<usize, TopicPermissions>>,
 }
 
 #[derive(Debug, Deserialize, JsonSchema)]
diff --git a/core/bench/report/src/types/transport.rs b/core/bench/report/src/types/transport.rs
index 73f036646..36bb5074d 100644
--- a/core/bench/report/src/types/transport.rs
+++ b/core/bench/report/src/types/transport.rs
@@ -31,4 +31,7 @@ pub enum BenchmarkTransport {
     #[display("QUIC")]
     #[serde(rename = "quic")]
     Quic,
+    #[display("WEBSOCKET")]
+    #[serde(rename = "websocket")]
+    WebSocket,
 }
diff --git a/core/bench/src/actors/consumer/client/high_level.rs b/core/bench/src/actors/consumer/client/high_level.rs
index 362fd8085..152b0ffbf 100644
--- a/core/bench/src/actors/consumer/client/high_level.rs
+++ b/core/bench/src/actors/consumer/client/high_level.rs
@@ -101,18 +101,17 @@ impl ConsumerClient for HighLevelConsumerClient {
 
 impl BenchmarkInit for HighLevelConsumerClient {
     async fn setup(&mut self) -> Result<(), IggyError> {
-        let topic_id = 1;
+        let topic_id_str = "topic-1";
         let client = self.client_factory.create_client().await;
         let client = IggyClient::create(client, None, None);
         login_root(&client).await;
 
         let stream_id_str = self.config.stream_id.to_string();
-        let topic_id_str = topic_id.to_string();
 
         let mut consumer = if let Some(cg_id) = self.config.consumer_group_id {
             let consumer_group_name = format!("cg_{cg_id}");
             client
-                .consumer_group(&consumer_group_name, &stream_id_str, &topic_id_str)?
+                .consumer_group(&consumer_group_name, &stream_id_str, topic_id_str)?
                 .batch_length(self.config.messages_per_batch.get())
                 .auto_commit(AutoCommit::When(AutoCommitWhen::PollingMessages))
                 .create_consumer_group_if_not_exists()
@@ -126,8 +125,8 @@ impl BenchmarkInit for HighLevelConsumerClient {
                 .consumer(
                     &format!("hl_consumer_{}", self.config.consumer_id),
                     &stream_id_str,
-                    &topic_id_str,
-                    1,
+                    topic_id_str,
+                    0,
                 )?
                 .polling_strategy(PollingStrategy::offset(0))
                 .batch_length(self.config.messages_per_batch.get())
diff --git a/core/bench/src/actors/consumer/client/interface.rs b/core/bench/src/actors/consumer/client/interface.rs
index d1179a732..2dd004512 100644
--- a/core/bench/src/actors/consumer/client/interface.rs
+++ b/core/bench/src/actors/consumer/client/interface.rs
@@ -25,7 +25,7 @@ use crate::actors::{ApiLabel, BatchMetrics, BenchmarkInit};
 pub struct BenchmarkConsumerConfig {
     pub consumer_id: u32,
     pub consumer_group_id: Option<u32>,
-    pub stream_id: u32,
+    pub stream_id: String,
     pub messages_per_batch: BenchmarkNumericParameter,
     pub warmup_time: IggyDuration,
     pub polling_kind: PollingKind,
diff --git a/core/bench/src/actors/consumer/client/low_level.rs b/core/bench/src/actors/consumer/client/low_level.rs
index 3217a6037..27feb3a4e 100644
--- a/core/bench/src/actors/consumer/client/low_level.rs
+++ b/core/bench/src/actors/consumer/client/low_level.rs
@@ -89,7 +89,7 @@ impl ConsumerClient for LowLevelConsumerClient {
         if polled.messages.is_empty() {
             return Ok(None);
         }
-        let message_count = u32::try_from(polled.messages.len()).unwrap();
+        let messages_count = polled.messages.len() as u64;
         let latency = if self.config.origin_timestamp_latency_calculation {
             let now = IggyTimestamp::now().as_micros();
             Duration::from_micros(now - polled.messages[0].header.origin_timestamp)
@@ -100,10 +100,13 @@ impl ConsumerClient for LowLevelConsumerClient {
         let user_bytes = batch_user_size_bytes(&polled);
         let total_bytes = batch_total_size_bytes(&polled);
 
-        self.offset += polled.messages.len() as u64;
+        self.offset += messages_count;
+        if self.polling_strategy.kind == PollingKind::Offset {
+            self.polling_strategy.value += messages_count;
+        }
 
         Ok(Some(BatchMetrics {
-            messages: message_count,
+            messages: messages_count.try_into().unwrap(),
             user_data_bytes: user_bytes,
             total_bytes,
             latency,
@@ -113,15 +116,15 @@ impl ConsumerClient for LowLevelConsumerClient {
 
 impl BenchmarkInit for LowLevelConsumerClient {
     async fn setup(&mut self) -> Result<(), IggyError> {
-        let topic_id = 1u32;
-        let default_partition_id = 1u32;
+        let topic_id_str = "topic-1";
+        let default_partition_id = 0u32;
 
         let client = self.client_factory.create_client().await;
         let client = IggyClient::create(client, None, None);
         login_root(&client).await;
 
-        let stream_id = self.config.stream_id.try_into().unwrap();
-        let topic_id = topic_id.try_into().unwrap();
+        let stream_id: Identifier = self.config.stream_id.as_str().try_into().unwrap();
+        let topic_id = topic_id_str.try_into().unwrap();
         let partition_id = if self.config.consumer_group_id.is_some() {
             None
         } else {
diff --git a/core/bench/src/actors/consumer/typed_benchmark_consumer.rs b/core/bench/src/actors/consumer/typed_benchmark_consumer.rs
index fca492ea3..723adc35b 100644
--- a/core/bench/src/actors/consumer/typed_benchmark_consumer.rs
+++ b/core/bench/src/actors/consumer/typed_benchmark_consumer.rs
@@ -48,7 +48,7 @@ impl TypedBenchmarkConsumer {
         benchmark_kind: BenchmarkKind,
         consumer_id: u32,
         consumer_group_id: Option<u32>,
-        stream_id: u32,
+        stream_id: String,
         messages_per_batch: BenchmarkNumericParameter,
         finish_condition: Arc<BenchmarkFinishCondition>,
         warmup_time: IggyDuration,
diff --git a/core/bench/src/actors/producer/client/interface.rs b/core/bench/src/actors/producer/client/interface.rs
index f2dd68232..d48f7ec31 100644
--- a/core/bench/src/actors/producer/client/interface.rs
+++ b/core/bench/src/actors/producer/client/interface.rs
@@ -26,7 +26,7 @@ use iggy::prelude::*;
 #[derive(Debug, Clone)]
 pub struct BenchmarkProducerConfig {
     pub producer_id: u32,
-    pub stream_id: u32,
+    pub stream_id: String,
     pub partitions: u32,
     pub messages_per_batch: BenchmarkNumericParameter,
     pub message_size: BenchmarkNumericParameter,
diff --git a/core/bench/src/actors/producer/client/low_level.rs b/core/bench/src/actors/producer/client/low_level.rs
index c657f3545..fc2f483ed 100644
--- a/core/bench/src/actors/producer/client/low_level.rs
+++ b/core/bench/src/actors/producer/client/low_level.rs
@@ -49,7 +49,7 @@ impl LowLevelProducerClient {
             client: None,
             stream_id: Identifier::default(),
             topic_id: Identifier::default(),
-            partitioning: Partitioning::partition_id(1),
+            partitioning: Partitioning::partition_id(0),
         }
     }
 }
@@ -87,7 +87,7 @@ impl ProducerClient for LowLevelProducerClient {
 
 impl BenchmarkInit for LowLevelProducerClient {
     async fn setup(&mut self) -> Result<(), IggyError> {
-        let default_partition_id = 1;
+        let default_partition_id = 0u32;
         let partitions = self.config.partitions;
 
         let client = self.client_factory.create_client().await;
@@ -102,8 +102,8 @@ impl BenchmarkInit for LowLevelProducerClient {
 
         self.client = Some(client);
         self.partitioning = partitioning;
-        self.stream_id = self.config.stream_id.try_into()?;
-        self.topic_id = Identifier::numeric(1)?;
+        self.stream_id = self.config.stream_id.as_str().try_into()?;
+        self.topic_id = Identifier::from_str_value("topic-1")?;
         Ok(())
     }
 }
diff --git a/core/bench/src/actors/producer/typed_benchmark_producer.rs b/core/bench/src/actors/producer/typed_benchmark_producer.rs
index ee2de4833..2a27fd33a 100644
--- a/core/bench/src/actors/producer/typed_benchmark_producer.rs
+++ b/core/bench/src/actors/producer/typed_benchmark_producer.rs
@@ -46,7 +46,7 @@ impl TypedBenchmarkProducer {
         client_factory: Arc<dyn ClientFactory>,
         benchmark_kind: BenchmarkKind,
         producer_id: u32,
-        stream_id: u32,
+        stream_id: String,
         partitions: u32,
         messages_per_batch: BenchmarkNumericParameter,
         message_size: BenchmarkNumericParameter,
diff --git a/core/bench/src/actors/producing_consumer/typed_banchmark_producing_consumer.rs b/core/bench/src/actors/producing_consumer/typed_banchmark_producing_consumer.rs
index 26e3e0fb6..ac7e121b6 100644
--- a/core/bench/src/actors/producing_consumer/typed_banchmark_producing_consumer.rs
+++ b/core/bench/src/actors/producing_consumer/typed_banchmark_producing_consumer.rs
@@ -52,7 +52,7 @@ impl TypedBenchmarkProducingConsumer {
         benchmark_kind: BenchmarkKind,
         actor_id: u32,
         consumer_group_id: Option<u32>,
-        stream_id: u32,
+        stream_id: String,
         partitions_count: u32,
         messages_per_batch: BenchmarkNumericParameter,
         message_size: BenchmarkNumericParameter,
@@ -67,7 +67,7 @@ impl TypedBenchmarkProducingConsumer {
     ) -> Self {
         let producer_config = BenchmarkProducerConfig {
             producer_id: actor_id,
-            stream_id,
+            stream_id: stream_id.clone(),
             partitions: partitions_count,
             messages_per_batch,
             message_size,
diff --git a/core/bench/src/args/common.rs b/core/bench/src/args/common.rs
index f7b1c4465..6dd36b20c 100644
--- a/core/bench/src/args/common.rs
+++ b/core/bench/src/args/common.rs
@@ -23,8 +23,7 @@ use super::{
     defaults::{
         DEFAULT_MESSAGE_BATCHES, DEFAULT_MESSAGE_SIZE, DEFAULT_MESSAGES_PER_BATCH,
         DEFAULT_MOVING_AVERAGE_WINDOW, DEFAULT_PERFORM_CLEANUP, DEFAULT_SAMPLING_TIME,
-        DEFAULT_SERVER_STDOUT_VISIBILITY, DEFAULT_SKIP_SERVER_START, DEFAULT_START_STREAM_ID,
-        DEFAULT_WARMUP_TIME,
+        DEFAULT_SERVER_STDOUT_VISIBILITY, DEFAULT_SKIP_SERVER_START, DEFAULT_WARMUP_TIME,
     },
     transport::BenchmarkTransportCommand,
 };
@@ -64,10 +63,6 @@ pub struct IggyBenchArgs {
     #[arg(long, short = 'T', group = "data_to_process")]
     pub total_data: Option<IggyByteSize>,
 
-    /// Start stream id
-    #[arg(long, short = 'S', default_value_t = DEFAULT_START_STREAM_ID)]
-    pub start_stream_id: NonZeroU32,
-
     /// Optional total rate limit (aggregate, for all actors)
     /// Accepts human-readable formats like "50KB", "10MB", or "1GB"
     #[arg(long, short = 'r', verbatim_doc_comment)]
@@ -137,10 +132,6 @@ impl IggyBenchArgs {
             .server_address()
     }
 
-    pub const fn start_stream_id(&self) -> u32 {
-        self.start_stream_id.get()
-    }
-
     pub fn validate(&mut self) {
         let server_address = self.server_address().parse::<SocketAddr>().unwrap();
         if (self.cleanup || self.verbose) && !server_address.ip().is_loopback() {
@@ -369,6 +360,7 @@ impl IggyBenchArgs {
             BenchmarkTransportCommand::Tcp(_) => "tcp",
             BenchmarkTransportCommand::Quic(_) => "quic",
             BenchmarkTransportCommand::Http(_) => "http",
+            BenchmarkTransportCommand::WebSocket(_) => "ws",
         };
 
         let actors = match &self.benchmark_kind {
diff --git a/core/bench/src/args/defaults.rs b/core/bench/src/args/defaults.rs
index 2e3f2b1e1..7695782fb 100644
--- a/core/bench/src/args/defaults.rs
+++ b/core/bench/src/args/defaults.rs
@@ -29,11 +29,12 @@ pub const DEFAULT_QUIC_SERVER_ADDRESS: &str = "127.0.0.1:8080";
 pub const DEFAULT_QUIC_SERVER_NAME: &str = "localhost";
 pub const DEFAULT_QUIC_VALIDATE_CERTIFICATE: bool = false;
 
+pub const DEFAULT_WEBSOCKET_SERVER_ADDRESS: &str = "127.0.0.1:8092";
+
 pub const DEFAULT_MESSAGES_PER_BATCH: NonZeroU32 = u32!(1000);
 pub const DEFAULT_MESSAGE_BATCHES: NonZeroU32 = u32!(1000);
 pub const DEFAULT_MESSAGE_SIZE: NonZeroU32 = u32!(1000);
 pub const DEFAULT_TOTAL_MESSAGES_SIZE: IggyByteSize = IggyByteSize::new(8_000_000);
-pub const DEFAULT_START_STREAM_ID: NonZeroU32 = u32!(3_000_000);
 
 pub const DEFAULT_PINNED_NUMBER_OF_STREAMS: NonZeroU32 = u32!(8);
 pub const DEFAULT_BALANCED_NUMBER_OF_STREAMS: NonZeroU32 = u32!(1);
diff --git a/core/bench/src/args/transport.rs b/core/bench/src/args/transport.rs
index 7fa8ed42a..ab23ac356 100644
--- a/core/bench/src/args/transport.rs
+++ b/core/bench/src/args/transport.rs
@@ -19,6 +19,7 @@
 use super::defaults::{
     DEFAULT_HTTP_SERVER_ADDRESS, DEFAULT_QUIC_CLIENT_ADDRESS, DEFAULT_QUIC_SERVER_ADDRESS,
     DEFAULT_QUIC_SERVER_NAME, DEFAULT_QUIC_VALIDATE_CERTIFICATE, DEFAULT_TCP_SERVER_ADDRESS,
+    DEFAULT_WEBSOCKET_SERVER_ADDRESS,
 };
 use super::{output::BenchmarkOutputCommand, props::BenchmarkTransportProps};
 use clap::{Parser, Subcommand};
@@ -30,6 +31,8 @@ pub enum BenchmarkTransportCommand {
     Http(HttpArgs),
     Tcp(TcpArgs),
     Quic(QuicArgs),
+    #[command(alias = "ws")]
+    WebSocket(WebSocketArgs),
 }
 
 impl Serialize for BenchmarkTransportCommand {
@@ -41,6 +44,7 @@ impl Serialize for BenchmarkTransportCommand {
             Self::Http(_) => "http",
             Self::Tcp(_) => "tcp",
             Self::Quic(_) => "quic",
+            Self::WebSocket(_) => "websocket",
         };
         serializer.serialize_str(variant_str)
     }
@@ -72,6 +76,7 @@ impl BenchmarkTransportProps for BenchmarkTransportCommand {
             Self::Http(args) => args,
             Self::Tcp(args) => args,
             Self::Quic(args) => args,
+            Self::WebSocket(args) => args,
         }
     }
 
@@ -222,3 +227,40 @@ impl BenchmarkTransportProps for QuicArgs {
         self.output.as_ref()
     }
 }
+
+#[derive(Parser, Debug, Clone)]
+pub struct WebSocketArgs {
+    /// Address of the WebSocket iggy-server
+    #[arg(long, default_value_t = DEFAULT_WEBSOCKET_SERVER_ADDRESS.to_owned())]
+    pub server_address: String,
+
+    /// Optional output command, used to output results (charts, raw json data) to a directory
+    #[command(subcommand)]
+    pub output: Option<BenchmarkOutputCommand>,
+}
+
+impl BenchmarkTransportProps for WebSocketArgs {
+    fn transport(&self) -> &TransportProtocol {
+        &TransportProtocol::WebSocket
+    }
+
+    fn server_address(&self) -> &str {
+        &self.server_address
+    }
+
+    fn validate_certificate(&self) -> bool {
+        panic!("Cannot validate certificate for WebSocket transport!")
+    }
+
+    fn client_address(&self) -> &str {
+        panic!("Setting client address for WebSocket transport is not supported!")
+    }
+
+    fn nodelay(&self) -> bool {
+        panic!("Setting nodelay for WebSocket transport is not supported!")
+    }
+
+    fn output_command(&self) -> Option<&BenchmarkOutputCommand> {
+        self.output.as_ref()
+    }
+}
diff --git a/core/bench/src/benchmarks/benchmark.rs b/core/bench/src/benchmarks/benchmark.rs
index 9b96a1b8c..31f1828c3 100644
--- a/core/bench/src/benchmarks/benchmark.rs
+++ b/core/bench/src/benchmarks/benchmark.rs
@@ -95,39 +95,36 @@ pub trait Benchmarkable: Send {
     /// Initializes the streams and topics for the benchmark.
     /// This method is called before the benchmark is executed.
     async fn init_streams(&self) -> Result<(), IggyError> {
-        let start_stream_id = self.args().start_stream_id();
         let number_of_streams = self.args().streams();
-        let topic_id: u32 = 1;
         let partitions_count: u32 = self.args().number_of_partitions();
         let client = self.client_factory().create_client().await;
         let client = IggyClient::create(client, None, None);
         login_root(&client).await;
         let streams = client.get_streams().await?;
         for i in 1..=number_of_streams {
-            let stream_id = start_stream_id + i;
-            if streams.iter().all(|s| s.id != stream_id) {
-                info!("Creating the test stream {}", stream_id);
-                let name = format!("stream {stream_id}");
-                client.create_stream(&name, Some(stream_id)).await?;
-                let name = format!("topic {topic_id}");
+            let stream_name = format!("bench-stream-{i}");
+            let stream_id: Identifier = stream_name.as_str().try_into()?;
+            if streams.iter().all(|s| s.name != stream_name) {
+                info!("Creating the test stream '{}'", stream_name);
+                client.create_stream(&stream_name).await?;
+                let topic_name = "topic-1".to_string();
                 let max_topic_size = self
                     .args()
                     .max_topic_size()
                     .map_or(MaxTopicSize::Unlimited, MaxTopicSize::Custom);
 
                 info!(
-                    "Creating the test topic {} for stream {} with max topic size: {:?}",
-                    topic_id, stream_id, max_topic_size
+                    "Creating the test topic '{}' for stream '{}' with max topic size: {:?}",
+                    topic_name, stream_name, max_topic_size
                 );
 
                 client
                     .create_topic(
-                        &stream_id.try_into()?,
-                        &name,
+                        &stream_id,
+                        &topic_name,
                         partitions_count,
                         CompressionAlgorithm::default(),
                         None,
-                        None,
                         IggyExpiry::NeverExpire,
                         max_topic_size,
                     )
@@ -138,17 +135,16 @@ pub trait Benchmarkable: Send {
     }
 
     async fn check_streams(&self) -> Result<(), IggyError> {
-        let start_stream_id = self.args().start_stream_id();
         let number_of_streams = self.args().streams();
         let client = self.client_factory().create_client().await;
         let client = IggyClient::create(client, None, None);
         login_root(&client).await;
         let streams = client.get_streams().await?;
         for i in 1..=number_of_streams {
-            let stream_id = start_stream_id + i;
-            if streams.iter().all(|s| s.id != stream_id) {
+            let stream_name = format!("bench-stream-{i}");
+            if streams.iter().all(|s| s.name != stream_name) {
                 return Err(IggyError::ResourceNotFound(format!(
-                    "Streams for testing are not properly initialized. Stream with id: {stream_id} is missing."
+                    "Streams for testing are not properly initialized. Stream '{stream_name}' is missing."
                 )));
             }
         }
diff --git a/core/bench/src/benchmarks/common.rs b/core/bench/src/benchmarks/common.rs
index d5436b562..da6878c0d 100644
--- a/core/bench/src/benchmarks/common.rs
+++ b/core/bench/src/benchmarks/common.rs
@@ -72,8 +72,6 @@ pub async fn init_consumer_groups(
     client_factory: &Arc<dyn ClientFactory>,
     args: &IggyBenchArgs,
 ) -> Result<(), IggyError> {
-    let start_stream_id = args.start_stream_id();
-    let topic_id: u32 = 1;
     let client = client_factory.create_client().await;
     let client = IggyClient::create(client, None, None);
     let cg_count = args.number_of_consumer_groups();
@@ -81,31 +79,19 @@ pub async fn init_consumer_groups(
     login_root(&client).await;
     for i in 1..=cg_count {
         let consumer_group_id = CONSUMER_GROUP_BASE_ID + i;
-        let stream_id = start_stream_id + i;
+        let stream_name = format!("bench-stream-{i}");
+        let stream_id: Identifier = stream_name.as_str().try_into()?;
+        let topic_id: Identifier = "topic-1".try_into()?;
         let consumer_group_name = format!("{CONSUMER_GROUP_NAME_PREFIX}-{consumer_group_id}");
         info!(
-            "Creating test consumer group: name={}, id={}, stream={}, topic={}",
-            consumer_group_name, consumer_group_id, stream_id, topic_id
+            "Creating test consumer group: name={},  stream={}, topic={}",
+            consumer_group_name, stream_name, topic_id
         );
-        match client
-            .create_consumer_group(
-                &stream_id.try_into().unwrap(),
-                &topic_id.try_into().unwrap(),
-                &consumer_group_name,
-                Some(consumer_group_id),
-            )
+        if let Err(err) = client
+            .create_consumer_group(&stream_id, &topic_id, &consumer_group_name)
             .await
         {
-            Err(IggyError::ConsumerGroupIdAlreadyExists(_, _)) => {
-                info!(
-                    "Consumer group with id {} already exists",
-                    consumer_group_id
-                );
-            }
-            Err(err) => {
-                error!("Error when creating consumer group {consumer_group_id}: {err}");
-            }
-            Ok(_) => {}
+            error!("Error when creating consumer group {consumer_group_id}: {err}");
         }
     }
     Ok(())
@@ -117,7 +103,6 @@ pub fn build_producer_futures(
 ) -> Vec<impl Future<Output = Result<BenchmarkIndividualMetrics, IggyError>> + Send + use<>> {
     let streams = args.streams();
     let partitions = args.number_of_partitions();
-    let start_stream_id = args.start_stream_id();
     let producers = args.producers();
     let actors = args.producers() + args.consumers();
     let warmup_time = args.warmup_time();
@@ -141,7 +126,8 @@ pub fn build_producer_futures(
                 BenchmarkFinishCondition::new(args, BenchmarkFinishConditionMode::PerProducer)
             };
 
-            let stream_id = start_stream_id + 1 + (producer_id % streams);
+            let stream_idx = 1 + ((producer_id - 1) % streams);
+            let stream_id = format!("bench-stream-{stream_idx}");
 
             async move {
                 let producer = TypedBenchmarkProducer::new(
@@ -169,7 +155,6 @@ pub fn build_consumer_futures(
     client_factory: &Arc<dyn ClientFactory>,
     args: &IggyBenchArgs,
 ) -> Vec<impl Future<Output = Result<BenchmarkIndividualMetrics, IggyError>> + Send + use<>> {
-    let start_stream_id = args.start_stream_id();
     let cg_count = args.number_of_consumer_groups();
     let consumers = args.consumers();
     let actors = args.producers() + args.consumers();
@@ -204,11 +189,12 @@ pub fn build_consumer_futures(
             } else {
                 BenchmarkFinishCondition::new(args, BenchmarkFinishConditionMode::PerConsumer)
             };
-            let stream_id = if cg_count > 0 {
-                start_stream_id + 1 + (consumer_id % cg_count)
+            let stream_idx = if cg_count > 0 {
+                1 + ((consumer_id - 1) % cg_count)
             } else {
-                start_stream_id + consumer_id
+                consumer_id
             };
+            let stream_id = format!("bench-stream-{stream_idx}");
             let consumer_group_id = if cg_count > 0 {
                 Some(CONSUMER_GROUP_BASE_ID + 1 + (consumer_id % cg_count))
             } else {
@@ -249,14 +235,14 @@ pub fn build_producing_consumers_futures(
     let warmup_time = args.warmup_time();
     let messages_per_batch = args.messages_per_batch();
     let message_size = args.message_size();
-    let start_stream_id = args.start_stream_id();
     let polling_kind = PollingKind::Offset;
 
     (1..=producing_consumers)
         .map(|actor_id| {
             let client_factory_clone = client_factory.clone();
             let args_clone = args.clone();
-            let stream_id = start_stream_id + 1 + (actor_id % streams);
+            let stream_idx = 1 + ((actor_id - 1) % streams);
+            let stream_id = format!("bench-stream-{stream_idx}");
 
             let send_finish_condition = BenchmarkFinishCondition::new(
                 &args,
@@ -320,7 +306,6 @@ pub fn build_producing_consumer_groups_futures(
     let warmup_time = args.warmup_time();
     let messages_per_batch = args.messages_per_batch();
     let message_size = args.message_size();
-    let start_stream_id = args.start_stream_id();
     let start_consumer_group_id = CONSUMER_GROUP_BASE_ID;
     let polling_kind = PollingKind::Next;
     let use_high_level_api = args.high_level_api();
@@ -334,7 +319,8 @@ pub fn build_producing_consumer_groups_futures(
         .map(|actor_id| {
             let client_factory_clone = client_factory.clone();
             let args_clone = args.clone();
-            let stream_id = start_stream_id + 1 + (actor_id % cg_count);
+            let stream_idx = 1 + ((actor_id - 1) % cg_count);
+            let stream_id = format!("bench-stream-{stream_idx}");
 
             let should_produce = actor_id <= producers;
             let should_consume = actor_id <= consumers;
diff --git a/core/bench/src/utils/client_factory.rs b/core/bench/src/utils/client_factory.rs
index 010d276a5..513d22300 100644
--- a/core/bench/src/utils/client_factory.rs
+++ b/core/bench/src/utils/client_factory.rs
@@ -23,6 +23,7 @@ use integration::http_client::HttpClientFactory;
 use integration::quic_client::QuicClientFactory;
 use integration::tcp_client::TcpClientFactory;
 use integration::test_server::ClientFactory;
+use integration::websocket_client::WebSocketClientFactory;
 use std::sync::Arc;
 
 pub fn create_client_factory(args: &IggyBenchArgs) -> Arc<dyn ClientFactory> {
@@ -48,5 +49,8 @@ pub fn create_client_factory(args: &IggyBenchArgs) -> Arc<dyn ClientFactory> {
         TransportProtocol::Quic => Arc::new(QuicClientFactory {
             server_addr: args.server_address().to_owned(),
         }),
+        TransportProtocol::WebSocket => Arc::new(WebSocketClientFactory {
+            server_addr: args.server_address().to_owned(),
+        }),
     }
 }
diff --git a/core/bench/src/utils/mod.rs b/core/bench/src/utils/mod.rs
index 8c07a9f1b..2559a6a3a 100644
--- a/core/bench/src/utils/mod.rs
+++ b/core/bench/src/utils/mod.rs
@@ -117,6 +117,7 @@ pub fn params_from_args_and_metrics(
         TransportProtocol::Tcp => BenchmarkTransport::Tcp,
         TransportProtocol::Quic => BenchmarkTransport::Quic,
         TransportProtocol::Http => BenchmarkTransport::Http,
+        TransportProtocol::WebSocket => BenchmarkTransport::WebSocket,
     };
     let server_address = args.server_address().to_string();
     let remark = args.remark();
diff --git a/core/bench/src/utils/server_starter.rs b/core/bench/src/utils/server_starter.rs
index 049e84924..1ee66d9b6 100644
--- a/core/bench/src/utils/server_starter.rs
+++ b/core/bench/src/utils/server_starter.rs
@@ -30,6 +30,7 @@ struct ServerConfig {
     http: ConfigAddress,
     tcp: ConfigAddress,
     quic: ConfigAddress,
+    websocket: ConfigAddress,
 }
 
 #[derive(Debug, Deserialize)]
@@ -150,6 +151,23 @@ async fn evaluate_server_start_condition(args: &IggyBenchArgs) -> (bool, HashMap
                 envs,
             )
         }
+        TransportProtocol::WebSocket => {
+            let args_websocket_address = args.server_address().parse::<SocketAddr>().unwrap();
+            let config_websocket_address = default_config
+                .websocket
+                .address
+                .parse::<SocketAddr>()
+                .unwrap();
+            let envs = HashMap::from([(
+                "IGGY_WEBSOCKET_ADDRESS".to_owned(),
+                default_config.websocket.address.clone(),
+            )]);
+            (
+                addresses_are_equivalent(&args_websocket_address, &config_websocket_address)
+                    && !is_tcp_addr_in_use(&args_websocket_address).await,
+                envs,
+            )
+        }
     }
 }
 
diff --git a/core/binary_protocol/src/cli/binary_consumer_groups/create_consumer_group.rs b/core/binary_protocol/src/cli/binary_consumer_groups/create_consumer_group.rs
index e206b070e..e8fb0d876 100644
--- a/core/binary_protocol/src/cli/binary_consumer_groups/create_consumer_group.rs
+++ b/core/binary_protocol/src/cli/binary_consumer_groups/create_consumer_group.rs
@@ -29,27 +29,18 @@ pub struct CreateConsumerGroupCmd {
 }
 
 impl CreateConsumerGroupCmd {
-    pub fn new(
-        stream_id: Identifier,
-        topic_id: Identifier,
-        name: String,
-        group_id: Option<u32>,
-    ) -> Self {
+    pub fn new(stream_id: Identifier, topic_id: Identifier, name: String) -> Self {
         Self {
             create_consumer_group: CreateConsumerGroup {
                 stream_id,
                 topic_id,
                 name,
-                group_id,
             },
         }
     }
 
     fn get_group_id_info(&self) -> String {
-        match self.create_consumer_group.group_id {
-            Some(group_id) => format!("ID: {group_id}"),
-            None => "ID auto incremented".to_string(),
-        }
+        "ID auto incremented".to_string()
     }
 }
 
@@ -67,7 +58,7 @@ impl CliCommand for CreateConsumerGroupCmd {
 
     async fn execute_cmd(&mut self, client: &dyn Client) -> anyhow::Result<(), anyhow::Error> {
         client
-            .create_consumer_group(&self.create_consumer_group.stream_id, &self.create_consumer_group.topic_id, &self.create_consumer_group.name, self.create_consumer_group.group_id)
+            .create_consumer_group(&self.create_consumer_group.stream_id, &self.create_consumer_group.topic_id, &self.create_consumer_group.name)
             .await
             .with_context(|| {
                 format!(
diff --git a/core/binary_protocol/src/cli/binary_streams/create_stream.rs b/core/binary_protocol/src/cli/binary_streams/create_stream.rs
index 46ed1d74d..9fe750f87 100644
--- a/core/binary_protocol/src/cli/binary_streams/create_stream.rs
+++ b/core/binary_protocol/src/cli/binary_streams/create_stream.rs
@@ -28,17 +28,14 @@ pub struct CreateStreamCmd {
 }
 
 impl CreateStreamCmd {
-    pub fn new(stream_id: Option<u32>, name: String) -> Self {
+    pub fn new(name: String) -> Self {
         Self {
-            create_stream: CreateStream { stream_id, name },
+            create_stream: CreateStream { name },
         }
     }
 
     fn get_stream_id_info(&self) -> String {
-        match self.create_stream.stream_id {
-            Some(stream_id) => format!("ID: {stream_id}"),
-            None => "ID auto incremented".to_string(),
-        }
+        "ID auto incremented".to_string()
     }
 }
 
@@ -54,7 +51,7 @@ impl CliCommand for CreateStreamCmd {
 
     async fn execute_cmd(&mut self, client: &dyn Client) -> anyhow::Result<(), anyhow::Error> {
         client
-            .create_stream(&self.create_stream.name, self.create_stream.stream_id)
+            .create_stream(&self.create_stream.name)
             .await
             .with_context(|| {
                 format!(
diff --git a/core/binary_protocol/src/cli/binary_topics/create_topic.rs b/core/binary_protocol/src/cli/binary_topics/create_topic.rs
index 56a823877..ff4ef9fd6 100644
--- a/core/binary_protocol/src/cli/binary_topics/create_topic.rs
+++ b/core/binary_protocol/src/cli/binary_topics/create_topic.rs
@@ -36,7 +36,6 @@ impl CreateTopicCmd {
     #[allow(clippy::too_many_arguments)]
     pub fn new(
         stream_id: Identifier,
-        topic_id: Option<u32>,
         partitions_count: u32,
         compression_algorithm: CompressionAlgorithm,
         name: String,
@@ -47,7 +46,6 @@ impl CreateTopicCmd {
         Self {
             create_topic: CreateTopic {
                 stream_id,
-                topic_id,
                 partitions_count,
                 compression_algorithm,
                 name,
@@ -60,13 +58,6 @@ impl CreateTopicCmd {
             replication_factor,
         }
     }
-
-    fn get_topic_id_info(&self) -> String {
-        match self.create_topic.topic_id {
-            Some(topic_id) => format!("ID: {topic_id}"),
-            None => "ID auto incremented".to_string(),
-        }
-    }
 }
 
 #[async_trait]
@@ -77,19 +68,28 @@ impl CliCommand for CreateTopicCmd {
 
     async fn execute_cmd(&mut self, client: &dyn Client) -> anyhow::Result<(), anyhow::Error> {
         client
-            .create_topic(&self.create_topic.stream_id, &self.create_topic.name, self.create_topic.partitions_count, self.create_topic.compression_algorithm, self.create_topic.replication_factor, self.create_topic.topic_id, self.create_topic.message_expiry, self.create_topic.max_topic_size)
+            .create_topic(
+                &self.create_topic.stream_id,
+                &self.create_topic.name,
+                self.create_topic.partitions_count,
+                self.create_topic.compression_algorithm,
+                self.create_topic.replication_factor,
+                self.create_topic.message_expiry,
+                self.create_topic.max_topic_size,
+            )
             .await
             .with_context(|| {
                 format!(
-                    "Problem creating topic (ID: {}, name: {}, partitions count: {}) in stream with ID: {}",
-                    self.create_topic.topic_id.unwrap_or(0), self.create_topic.name, self.create_topic.partitions_count, self.create_topic.stream_id
+                    "Problem creating topic (name: {}, partitions count: {}) in stream with ID: {}",
+                    self.create_topic.name,
+                    self.create_topic.partitions_count,
+                    self.create_topic.stream_id
                 )
             })?;
 
         event!(target: PRINT_TARGET, Level::INFO,
-            "Topic with name: {}, {}, partitions count: {}, compression algorithm: {}, message expiry: {}, max topic size: {}, replication factor: {} created in stream with ID: {}",
+            "Topic with name: {}, partitions count: {}, compression algorithm: {}, message expiry: {}, max topic size: {}, replication factor: {} created in stream with ID: {}",
             self.create_topic.name,
-            self.get_topic_id_info(),
             self.create_topic.partitions_count,
             self.create_topic.compression_algorithm,
             self.message_expiry,
@@ -104,7 +104,6 @@ impl CliCommand for CreateTopicCmd {
 
 impl fmt::Display for CreateTopicCmd {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let topic_id = self.get_topic_id_info();
         let topic_name = &self.create_topic.name;
         let compression_algorithm = &self.create_topic.compression_algorithm;
         let message_expiry = &self.message_expiry;
@@ -114,7 +113,7 @@ impl fmt::Display for CreateTopicCmd {
 
         write!(
             f,
-            "create topic with name: {topic_name}, {topic_id}, message expiry: {message_expiry}, compression algorithm: {compression_algorithm}, \
+            "create topic with name: {topic_name}, message expiry: {message_expiry}, compression algorithm: {compression_algorithm}, \
             max topic size: {max_topic_size}, replication factor: {replication_factor} in stream with ID: {stream_id}",
         )
     }
diff --git a/core/binary_protocol/src/client/binary_clients/consumer_group_client.rs b/core/binary_protocol/src/client/binary_clients/consumer_group_client.rs
index d379ca566..49f3b373f 100644
--- a/core/binary_protocol/src/client/binary_clients/consumer_group_client.rs
+++ b/core/binary_protocol/src/client/binary_clients/consumer_group_client.rs
@@ -47,7 +47,6 @@ pub trait ConsumerGroupClient {
         stream_id: &Identifier,
         topic_id: &Identifier,
         name: &str,
-        group_id: Option<u32>,
     ) -> Result<ConsumerGroupDetails, IggyError>;
     /// Delete a consumer group by unique ID or name for the given stream and topic by unique IDs or names.
     ///
diff --git a/core/binary_protocol/src/client/binary_clients/stream_client.rs b/core/binary_protocol/src/client/binary_clients/stream_client.rs
index bf4a7db42..b56c44b3d 100644
--- a/core/binary_protocol/src/client/binary_clients/stream_client.rs
+++ b/core/binary_protocol/src/client/binary_clients/stream_client.rs
@@ -33,11 +33,7 @@ pub trait StreamClient {
     /// Create a new stream.
     ///
     /// Authentication is required, and the permission to manage the streams.
-    async fn create_stream(
-        &self,
-        name: &str,
-        stream_id: Option<u32>,
-    ) -> Result<StreamDetails, IggyError>;
+    async fn create_stream(&self, name: &str) -> Result<StreamDetails, IggyError>;
     /// Update a stream by unique ID or name.
     ///
     /// Authentication is required, and the permission to manage the streams.
diff --git a/core/binary_protocol/src/client/binary_clients/topic_client.rs b/core/binary_protocol/src/client/binary_clients/topic_client.rs
index 3e89de038..bc2c0b201 100644
--- a/core/binary_protocol/src/client/binary_clients/topic_client.rs
+++ b/core/binary_protocol/src/client/binary_clients/topic_client.rs
@@ -48,7 +48,6 @@ pub trait TopicClient {
         partitions_count: u32,
         compression_algorithm: CompressionAlgorithm,
         replication_factor: Option<u8>,
-        topic_id: Option<u32>,
         message_expiry: IggyExpiry,
         max_topic_size: MaxTopicSize,
     ) -> Result<TopicDetails, IggyError>;
diff --git a/core/binary_protocol/src/client/binary_consumer_groups/mod.rs b/core/binary_protocol/src/client/binary_consumer_groups/mod.rs
index bad644d37..6495665fc 100644
--- a/core/binary_protocol/src/client/binary_consumer_groups/mod.rs
+++ b/core/binary_protocol/src/client/binary_consumer_groups/mod.rs
@@ -71,7 +71,6 @@ impl<B: BinaryClient> ConsumerGroupClient for B {
         stream_id: &Identifier,
         topic_id: &Identifier,
         name: &str,
-        group_id: Option<u32>,
     ) -> Result<ConsumerGroupDetails, IggyError> {
         fail_if_not_authenticated(self).await?;
         let response = self
@@ -79,7 +78,6 @@ impl<B: BinaryClient> ConsumerGroupClient for B {
                 stream_id: stream_id.clone(),
                 topic_id: topic_id.clone(),
                 name: name.to_string(),
-                group_id,
             })
             .await?;
         mapper::map_consumer_group(response)
diff --git a/core/binary_protocol/src/client/binary_streams/mod.rs b/core/binary_protocol/src/client/binary_streams/mod.rs
index b48a017c9..ebb9ab6cd 100644
--- a/core/binary_protocol/src/client/binary_streams/mod.rs
+++ b/core/binary_protocol/src/client/binary_streams/mod.rs
@@ -49,16 +49,11 @@ impl<B: BinaryClient> StreamClient for B {
         mapper::map_streams(response)
     }
 
-    async fn create_stream(
-        &self,
-        name: &str,
-        stream_id: Option<u32>,
-    ) -> Result<StreamDetails, IggyError> {
+    async fn create_stream(&self, name: &str) -> Result<StreamDetails, IggyError> {
         fail_if_not_authenticated(self).await?;
         let response = self
             .send_with_response(&CreateStream {
                 name: name.to_string(),
-                stream_id,
             })
             .await?;
         mapper::map_stream(response)
diff --git a/core/binary_protocol/src/client/binary_topics/mod.rs b/core/binary_protocol/src/client/binary_topics/mod.rs
index 5d1575b7e..7ddb4205a 100644
--- a/core/binary_protocol/src/client/binary_topics/mod.rs
+++ b/core/binary_protocol/src/client/binary_topics/mod.rs
@@ -67,7 +67,6 @@ impl<B: BinaryClient> TopicClient for B {
         partitions_count: u32,
         compression_algorithm: CompressionAlgorithm,
         replication_factor: Option<u8>,
-        topic_id: Option<u32>,
         message_expiry: IggyExpiry,
         max_topic_size: MaxTopicSize,
     ) -> Result<TopicDetails, IggyError> {
@@ -79,7 +78,6 @@ impl<B: BinaryClient> TopicClient for B {
                 partitions_count,
                 compression_algorithm,
                 replication_factor,
-                topic_id,
                 message_expiry,
                 max_topic_size,
             })
diff --git a/core/binary_protocol/src/utils/mapper.rs b/core/binary_protocol/src/utils/mapper.rs
index 725932c5c..356d65931 100644
--- a/core/binary_protocol/src/utils/mapper.rs
+++ b/core/binary_protocol/src/utils/mapper.rs
@@ -898,7 +898,7 @@ fn map_to_client_info(
             .map_err(|_| IggyError::InvalidNumberEncoding)?,
     );
     let user_id = match user_id {
-        0 => None,
+        u32::MAX => None,
         _ => Some(user_id),
     };
 
diff --git a/core/cli/src/args/consumer_offset.rs b/core/cli/src/args/consumer_offset.rs
index bbb031589..26781b9f5 100644
--- a/core/cli/src/args/consumer_offset.rs
+++ b/core/cli/src/args/consumer_offset.rs
@@ -77,7 +77,7 @@ pub(crate) struct ConsumerOffsetGetArgs {
     #[arg(value_parser = clap::value_parser!(Identifier))]
     pub(crate) topic_id: Identifier,
     /// Partitions ID for which consumer offset is retrieved
-    #[arg(value_parser = clap::value_parser!(u32).range(1..))]
+    #[arg(value_parser = clap::value_parser!(u32).range(0..))]
     pub(crate) partition_id: u32,
     /// Consumer kind: "consumer" for regular consumer, "consumer_group" for consumer group
     #[arg(short = 'k', long = "kind", default_value = "consumer", value_enum)]
@@ -103,7 +103,7 @@ pub(crate) struct ConsumerOffsetSetArgs {
     #[arg(value_parser = clap::value_parser!(Identifier))]
     pub(crate) topic_id: Identifier,
     /// Partitions ID for which consumer offset is set
-    #[arg(value_parser = clap::value_parser!(u32).range(1..))]
+    #[arg(value_parser = clap::value_parser!(u32).range(0..))]
     pub(crate) partition_id: u32,
     /// Offset to set
     pub(crate) offset: u64,
diff --git a/core/cli/src/args/message.rs b/core/cli/src/args/message.rs
index 08d69348a..da5e742e6 100644
--- a/core/cli/src/args/message.rs
+++ b/core/cli/src/args/message.rs
@@ -146,7 +146,7 @@ pub(crate) struct PollMessagesArgs {
     #[arg(value_parser = clap::value_parser!(Identifier))]
     pub(crate) topic_id: Identifier,
     /// Partition ID from which message will be polled
-    #[arg(value_parser = clap::value_parser!(u32).range(1..))]
+    #[arg(value_parser = clap::value_parser!(u32).range(0..))]
     pub(crate) partition_id: u32,
     /// Number of messages to poll
     #[clap(verbatim_doc_comment)]
@@ -219,7 +219,7 @@ pub(crate) struct FlushMessagesArgs {
     #[arg(value_parser = clap::value_parser!(Identifier))]
     pub(crate) topic_id: Identifier,
     /// Partition ID for which messages will be flushed
-    #[arg(value_parser = clap::value_parser!(u32).range(1..))]
+    #[arg(value_parser = clap::value_parser!(u32).range(0..))]
     pub(crate) partition_id: u32,
     /// fsync flushed data to disk
     ///
diff --git a/core/cli/src/args/permissions/mod.rs b/core/cli/src/args/permissions/mod.rs
index 732a4f526..6655181f4 100644
--- a/core/cli/src/args/permissions/mod.rs
+++ b/core/cli/src/args/permissions/mod.rs
@@ -49,7 +49,7 @@ impl From<PermissionsArgs> for Option<Permissions> {
             .stream
             .into_iter()
             .map(|s| (s.stream_id, s.into()))
-            .collect::<AHashMap<u32, StreamPermissions>>();
+            .collect::<AHashMap<usize, StreamPermissions>>();
 
         match (value.global, stream_permissions.is_empty()) {
             (Some(global), true) => Some(Permissions {
diff --git a/core/cli/src/args/permissions/stream.rs b/core/cli/src/args/permissions/stream.rs
index fac5c2381..b371d8720 100644
--- a/core/cli/src/args/permissions/stream.rs
+++ b/core/cli/src/args/permissions/stream.rs
@@ -58,7 +58,7 @@ impl FromStr for StreamPermission {
 
 #[derive(Clone, Debug, PartialEq)]
 pub(crate) struct StreamPermissionsArg {
-    pub(crate) stream_id: u32,
+    pub(crate) stream_id: usize,
     pub(crate) permissions: StreamPermissions,
 }
 
@@ -70,7 +70,7 @@ impl From<StreamPermissionsArg> for StreamPermissions {
 
 impl StreamPermissionsArg {
     pub(super) fn new(
-        stream_id: u32,
+        stream_id: usize,
         stream_permissions: Vec<StreamPermission>,
         topic_permissions: Vec<TopicPermissionsArg>,
     ) -> Self {
diff --git a/core/cli/src/args/permissions/topic.rs b/core/cli/src/args/permissions/topic.rs
index c1b31594d..cdc83c2ec 100644
--- a/core/cli/src/args/permissions/topic.rs
+++ b/core/cli/src/args/permissions/topic.rs
@@ -52,7 +52,7 @@ impl FromStr for TopicPermission {
 
 #[derive(Clone, Debug, PartialEq)]
 pub(crate) struct TopicPermissionsArg {
-    pub(crate) topic_id: u32,
+    pub(crate) topic_id: usize,
     pub(crate) permissions: TopicPermissions,
 }
 
@@ -63,7 +63,7 @@ impl From<TopicPermissionsArg> for TopicPermissions {
 }
 
 impl TopicPermissionsArg {
-    fn new(topic_id: u32, topic_permissions: Vec<TopicPermission>) -> Self {
+    fn new(topic_id: usize, topic_permissions: Vec<TopicPermission>) -> Self {
         let mut result = Self {
             topic_id,
             permissions: TopicPermissions::default(),
diff --git a/core/cli/src/main.rs b/core/cli/src/main.rs
index d21536bba..21b60aaa1 100644
--- a/core/cli/src/main.rs
+++ b/core/cli/src/main.rs
@@ -106,9 +106,7 @@ fn get_command(
     #[warn(clippy::let_and_return)]
     match command {
         Command::Stream(command) => match command {
-            StreamAction::Create(args) => {
-                Box::new(CreateStreamCmd::new(args.stream_id, args.name.clone()))
-            }
+            StreamAction::Create(args) => Box::new(CreateStreamCmd::new(args.name.clone())),
             StreamAction::Delete(args) => Box::new(DeleteStreamCmd::new(args.stream_id.clone())),
             StreamAction::Update(args) => Box::new(UpdateStreamCmd::new(
                 args.stream_id.clone(),
@@ -121,7 +119,6 @@ fn get_command(
         Command::Topic(command) => match command {
             TopicAction::Create(args) => Box::new(CreateTopicCmd::new(
                 args.stream_id.clone(),
-                args.topic_id,
                 args.partitions_count,
                 args.compression_algorithm,
                 args.name.clone(),
@@ -252,7 +249,6 @@ fn get_command(
                 create_args.stream_id.clone(),
                 create_args.topic_id.clone(),
                 create_args.name.clone(),
-                create_args.group_id,
             )),
             ConsumerGroupAction::Delete(delete_args) => Box::new(DeleteConsumerGroupCmd::new(
                 delete_args.stream_id.clone(),
diff --git a/core/common/Cargo.toml b/core/common/Cargo.toml
index 2aa4ade37..39bfde4bf 100644
--- a/core/common/Cargo.toml
+++ b/core/common/Cargo.toml
@@ -40,6 +40,7 @@ bon = { workspace = true }
 byte-unit = { workspace = true }
 bytes = { workspace = true }
 chrono = { workspace = true }
+compio-ws = { workspace = true }
 clap = { workspace = true }
 comfy-table = { workspace = true }
 crc32fast = { workspace = true }
diff --git a/core/common/src/certificates.rs b/core/common/src/certificates.rs
index f697b776d..cba7dc1e9 100644
--- a/core/common/src/certificates.rs
+++ b/core/common/src/certificates.rs
@@ -27,5 +27,6 @@ pub fn generate_self_signed_certificate(
     let cert_der = cert.cert.der();
     let key_der = cert.signing_key.serialize_der();
     let key = PrivateKeyDer::try_from(key_der)?;
+
     Ok((vec![cert_der.clone()], key))
 }
diff --git a/core/common/src/commands/consumer_groups/create_consumer_group.rs b/core/common/src/commands/consumer_groups/create_consumer_group.rs
index 265b06592..c5bd3e912 100644
--- a/core/common/src/commands/consumer_groups/create_consumer_group.rs
+++ b/core/common/src/commands/consumer_groups/create_consumer_group.rs
@@ -32,7 +32,6 @@ use std::str::from_utf8;
 /// It has additional payload:
 /// - `stream_id` - unique stream ID (numeric or name).
 /// - `topic_id` - unique topic ID (numeric or name).
-/// - `group_id` - unique consumer group ID.
 /// - `name` - unique consumer group name, max length is 255 characters.
 #[derive(Debug, Serialize, Deserialize, PartialEq)]
 pub struct CreateConsumerGroup {
@@ -42,8 +41,6 @@ pub struct CreateConsumerGroup {
     /// Unique topic ID (numeric or name).
     #[serde(skip)]
     pub topic_id: Identifier,
-    /// Unique consumer group ID (numeric), if None is provided then the server will automatically assign it.
-    pub group_id: Option<u32>,
     /// Unique consumer group name, max length is 255 characters.
     pub name: String,
 }
@@ -59,7 +56,6 @@ impl Default for CreateConsumerGroup {
         CreateConsumerGroup {
             stream_id: Identifier::default(),
             topic_id: Identifier::default(),
-            group_id: None,
             name: "consumer_group_1".to_string(),
         }
     }
@@ -67,12 +63,6 @@ impl Default for CreateConsumerGroup {
 
 impl Validatable<IggyError> for CreateConsumerGroup {
     fn validate(&self) -> Result<(), IggyError> {
-        if let Some(group_id) = self.group_id
-            && group_id == 0
-        {
-            return Err(IggyError::InvalidConsumerGroupId);
-        }
-
         if self.name.is_empty() || self.name.len() > MAX_NAME_LENGTH {
             return Err(IggyError::InvalidConsumerGroupName);
         }
@@ -86,11 +76,10 @@ impl BytesSerializable for CreateConsumerGroup {
         let stream_id_bytes = self.stream_id.to_bytes();
         let topic_id_bytes = self.topic_id.to_bytes();
         let mut bytes = BytesMut::with_capacity(
-            4 + stream_id_bytes.len() + topic_id_bytes.len() + self.name.len(),
+            1 + stream_id_bytes.len() + topic_id_bytes.len() + self.name.len(),
         );
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
-        bytes.put_u32_le(self.group_id.unwrap_or(0));
         #[allow(clippy::cast_possible_truncation)]
         bytes.put_u8(self.name.len() as u8);
         bytes.put_slice(self.name.as_bytes());
@@ -98,7 +87,7 @@ impl BytesSerializable for CreateConsumerGroup {
     }
 
     fn from_bytes(bytes: Bytes) -> Result<CreateConsumerGroup, IggyError> {
-        if bytes.len() < 10 {
+        if bytes.len() < 6 {
             return Err(IggyError::InvalidCommand);
         }
 
@@ -107,20 +96,13 @@ impl BytesSerializable for CreateConsumerGroup {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..))?;
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let group_id = u32::from_le_bytes(
-            bytes[position..position + 4]
-                .try_into()
-                .map_err(|_| IggyError::InvalidNumberEncoding)?,
-        );
-        let group_id = if group_id == 0 { None } else { Some(group_id) };
-        let name_length = bytes[position + 4];
-        let name = from_utf8(&bytes[position + 5..position + 5 + name_length as usize])
+        let name_length = bytes[position];
+        let name = from_utf8(&bytes[position + 1..position + 1 + name_length as usize])
             .map_err(|_| IggyError::InvalidUtf8)?
             .to_string();
         let command = CreateConsumerGroup {
             stream_id,
             topic_id,
-            group_id,
             name,
         };
         Ok(command)
@@ -129,14 +111,7 @@ impl BytesSerializable for CreateConsumerGroup {
 
 impl Display for CreateConsumerGroup {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(
-            f,
-            "{}|{}|{}|{}",
-            self.stream_id,
-            self.topic_id,
-            self.group_id.unwrap_or(0),
-            self.name
-        )
+        write!(f, "{}|{}|{}", self.stream_id, self.topic_id, self.name)
     }
 }
 
@@ -149,7 +124,6 @@ mod tests {
         let command = CreateConsumerGroup {
             stream_id: Identifier::numeric(1).unwrap(),
             topic_id: Identifier::numeric(2).unwrap(),
-            group_id: Some(3),
             name: "test".to_string(),
         };
 
@@ -159,14 +133,12 @@ mod tests {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..)).unwrap();
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let group_id = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap());
 
-        let name_length = bytes[position + 4];
-        let name = from_utf8(&bytes[position + 5..position + 5 + name_length as usize]).unwrap();
+        let name_length = bytes[position];
+        let name = from_utf8(&bytes[position + 1..position + 1 + name_length as usize]).unwrap();
         assert!(!bytes.is_empty());
         assert_eq!(stream_id, command.stream_id);
         assert_eq!(topic_id, command.topic_id);
-        assert_eq!(group_id, command.group_id.unwrap());
         assert_eq!(name, command.name);
     }
 
@@ -174,15 +146,13 @@ mod tests {
     fn should_be_deserialized_from_bytes() {
         let stream_id = Identifier::numeric(1).unwrap();
         let topic_id = Identifier::numeric(2).unwrap();
-        let group_id = 3u32;
         let name = "test".to_string();
         let stream_id_bytes = stream_id.to_bytes();
         let topic_id_bytes = topic_id.to_bytes();
         let mut bytes =
-            BytesMut::with_capacity(4 + stream_id_bytes.len() + topic_id_bytes.len() + name.len());
+            BytesMut::with_capacity(1 + stream_id_bytes.len() + topic_id_bytes.len() + name.len());
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
-        bytes.put_u32_le(group_id);
         #[allow(clippy::cast_possible_truncation)]
         bytes.put_u8(name.len() as u8);
         bytes.put_slice(name.as_bytes());
@@ -192,7 +162,6 @@ mod tests {
         let command = command.unwrap();
         assert_eq!(command.stream_id, stream_id);
         assert_eq!(command.topic_id, topic_id);
-        assert_eq!(command.group_id.unwrap(), group_id);
         assert_eq!(command.name, name);
     }
 }
diff --git a/core/common/src/commands/consumer_offsets/delete_consumer_offset.rs b/core/common/src/commands/consumer_offsets/delete_consumer_offset.rs
index aab6c096d..8cfeac24d 100644
--- a/core/common/src/commands/consumer_offsets/delete_consumer_offset.rs
+++ b/core/common/src/commands/consumer_offsets/delete_consumer_offset.rs
@@ -54,7 +54,7 @@ impl Default for DeleteConsumerOffset {
             consumer: Consumer::default(),
             stream_id: Identifier::default(),
             topic_id: Identifier::default(),
-            partition_id: Some(1),
+            partition_id: Some(0),
         }
     }
 }
diff --git a/core/common/src/commands/consumer_offsets/get_consumer_offset.rs b/core/common/src/commands/consumer_offsets/get_consumer_offset.rs
index c2a3dda8e..0f542e09f 100644
--- a/core/common/src/commands/consumer_offsets/get_consumer_offset.rs
+++ b/core/common/src/commands/consumer_offsets/get_consumer_offset.rs
@@ -67,7 +67,7 @@ impl Command for GetConsumerOffset {
 }
 
 fn default_partition_id() -> Option<u32> {
-    Some(1)
+    Some(0)
 }
 
 impl Validatable<IggyError> for GetConsumerOffset {
@@ -82,21 +82,24 @@ impl BytesSerializable for GetConsumerOffset {
         let stream_id_bytes = self.stream_id.to_bytes();
         let topic_id_bytes = self.topic_id.to_bytes();
         let mut bytes = BytesMut::with_capacity(
-            4 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
+            5 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
         );
         bytes.put_slice(&consumer_bytes);
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
+        // Encode partition_id with a flag byte: 1 = Some, 0 = None
         if let Some(partition_id) = self.partition_id {
+            bytes.put_u8(1);
             bytes.put_u32_le(partition_id);
         } else {
-            bytes.put_u32_le(0);
+            bytes.put_u8(0);
+            bytes.put_u32_le(0); // Padding to keep structure consistent
         }
         bytes.freeze()
     }
 
     fn from_bytes(bytes: Bytes) -> Result<GetConsumerOffset, IggyError> {
-        if bytes.len() < 15 {
+        if bytes.len() < 16 {
             return Err(IggyError::InvalidCommand);
         }
 
@@ -112,15 +115,17 @@ impl BytesSerializable for GetConsumerOffset {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..))?;
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let partition_id = u32::from_le_bytes(
-            bytes[position..position + 4]
+        // Decode partition_id with flag byte: 1 = Some, 0 = None
+        let has_partition_id = bytes[position];
+        let partition_id_value = u32::from_le_bytes(
+            bytes[position + 1..position + 5]
                 .try_into()
                 .map_err(|_| IggyError::InvalidNumberEncoding)?,
         );
-        let partition_id = if partition_id == 0 {
-            None
+        let partition_id = if has_partition_id == 1 {
+            Some(partition_id_value)
         } else {
-            Some(partition_id)
+            None
         };
         let command = GetConsumerOffset {
             consumer,
@@ -171,13 +176,20 @@ mod tests {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..)).unwrap();
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let partition_id = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap());
+        let has_partition_id = bytes[position];
+        let partition_id =
+            u32::from_le_bytes(bytes[position + 1..position + 5].try_into().unwrap());
+        let partition_id = if has_partition_id == 1 {
+            Some(partition_id)
+        } else {
+            None
+        };
 
         assert!(!bytes.is_empty());
         assert_eq!(consumer, command.consumer);
         assert_eq!(stream_id, command.stream_id);
         assert_eq!(topic_id, command.topic_id);
-        assert_eq!(Some(partition_id), command.partition_id);
+        assert_eq!(partition_id, command.partition_id);
     }
 
     #[test]
@@ -191,11 +203,12 @@ mod tests {
         let stream_id_bytes = stream_id.to_bytes();
         let topic_id_bytes = topic_id.to_bytes();
         let mut bytes = BytesMut::with_capacity(
-            4 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
+            5 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
         );
         bytes.put_slice(&consumer_bytes);
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
+        bytes.put_u8(1); // Flag: partition_id is Some
         bytes.put_u32_le(partition_id);
 
         let command = GetConsumerOffset::from_bytes(bytes.freeze());
diff --git a/core/common/src/commands/consumer_offsets/store_consumer_offset.rs b/core/common/src/commands/consumer_offsets/store_consumer_offset.rs
index ad3c4eae6..c7705fbfb 100644
--- a/core/common/src/commands/consumer_offsets/store_consumer_offset.rs
+++ b/core/common/src/commands/consumer_offsets/store_consumer_offset.rs
@@ -57,7 +57,7 @@ impl Default for StoreConsumerOffset {
             consumer: Consumer::default(),
             stream_id: Identifier::default(),
             topic_id: Identifier::default(),
-            partition_id: Some(1),
+            partition_id: Some(0),
             offset: 0,
         }
     }
@@ -81,22 +81,25 @@ impl BytesSerializable for StoreConsumerOffset {
         let stream_id_bytes = self.stream_id.to_bytes();
         let topic_id_bytes = self.topic_id.to_bytes();
         let mut bytes = BytesMut::with_capacity(
-            12 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
+            13 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
         );
         bytes.put_slice(&consumer_bytes);
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
+        // Encode partition_id with a flag byte: 1 = Some, 0 = None
         if let Some(partition_id) = self.partition_id {
+            bytes.put_u8(1);
             bytes.put_u32_le(partition_id);
         } else {
-            bytes.put_u32_le(0);
+            bytes.put_u8(0);
+            bytes.put_u32_le(0); // Padding to keep structure consistent
         }
         bytes.put_u64_le(self.offset);
         bytes.freeze()
     }
 
     fn from_bytes(bytes: Bytes) -> Result<StoreConsumerOffset, IggyError> {
-        if bytes.len() < 23 {
+        if bytes.len() < 24 {
             return Err(IggyError::InvalidCommand);
         }
 
@@ -112,18 +115,20 @@ impl BytesSerializable for StoreConsumerOffset {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..))?;
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let partition_id = u32::from_le_bytes(
-            bytes[position..position + 4]
+        // Decode partition_id with flag byte: 1 = Some, 0 = None
+        let has_partition_id = bytes[position];
+        let partition_id_value = u32::from_le_bytes(
+            bytes[position + 1..position + 5]
                 .try_into()
                 .map_err(|_| IggyError::InvalidNumberEncoding)?,
         );
-        let partition_id = if partition_id == 0 {
-            None
+        let partition_id = if has_partition_id == 1 {
+            Some(partition_id_value)
         } else {
-            Some(partition_id)
+            None
         };
         let offset = u64::from_le_bytes(
-            bytes[position + 4..position + 12]
+            bytes[position + 5..position + 13]
                 .try_into()
                 .map_err(|_| IggyError::InvalidNumberEncoding)?,
         );
@@ -179,14 +184,21 @@ mod tests {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..)).unwrap();
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let partition_id = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap());
-        let offset = u64::from_le_bytes(bytes[position + 4..position + 12].try_into().unwrap());
+        let has_partition_id = bytes[position];
+        let partition_id =
+            u32::from_le_bytes(bytes[position + 1..position + 5].try_into().unwrap());
+        let partition_id = if has_partition_id == 1 {
+            Some(partition_id)
+        } else {
+            None
+        };
+        let offset = u64::from_le_bytes(bytes[position + 5..position + 13].try_into().unwrap());
 
         assert!(!bytes.is_empty());
         assert_eq!(consumer, command.consumer);
         assert_eq!(stream_id, command.stream_id);
         assert_eq!(topic_id, command.topic_id);
-        assert_eq!(Some(partition_id), command.partition_id);
+        assert_eq!(partition_id, command.partition_id);
         assert_eq!(offset, command.offset);
     }
 
@@ -202,11 +214,12 @@ mod tests {
         let stream_id_bytes = stream_id.to_bytes();
         let topic_id_bytes = topic_id.to_bytes();
         let mut bytes = BytesMut::with_capacity(
-            12 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
+            13 + consumer_bytes.len() + stream_id_bytes.len() + topic_id_bytes.len(),
         );
         bytes.put_slice(&consumer_bytes);
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
+        bytes.put_u8(1); // Flag: partition_id is Some
         bytes.put_u32_le(partition_id);
         bytes.put_u64_le(offset);
 
diff --git a/core/common/src/commands/messages/poll_messages.rs b/core/common/src/commands/messages/poll_messages.rs
index 3f4045bbd..c52b335c8 100644
--- a/core/common/src/commands/messages/poll_messages.rs
+++ b/core/common/src/commands/messages/poll_messages.rs
@@ -24,7 +24,7 @@ use bytes::{BufMut, Bytes, BytesMut};
 use serde::{Deserialize, Serialize};
 use std::fmt::Display;
 
-pub const DEFAULT_PARTITION_ID: u32 = 1;
+pub const DEFAULT_PARTITION_ID: u32 = 0;
 pub const DEFAULT_NUMBER_OF_MESSAGES_TO_POLL: u32 = 10;
 
 /// `PollMessages` command is used to poll messages from a topic in a stream.
@@ -76,7 +76,7 @@ impl PollMessages {
         let topic_id_bytes = topic_id.to_bytes();
         let strategy_bytes = strategy.to_bytes();
         let mut bytes = BytesMut::with_capacity(
-            9 + consumer_bytes.len()
+            10 + consumer_bytes.len()
                 + stream_id_bytes.len()
                 + topic_id_bytes.len()
                 + strategy_bytes.len(),
@@ -84,10 +84,13 @@ impl PollMessages {
         bytes.put_slice(&consumer_bytes);
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
+        // Encode partition_id with a flag byte: 1 = Some, 0 = None
         if let Some(partition_id) = partition_id {
+            bytes.put_u8(1);
             bytes.put_u32_le(partition_id);
         } else {
-            bytes.put_u32_le(0);
+            bytes.put_u8(0);
+            bytes.put_u32_le(0); // Padding to keep structure consistent
         }
         bytes.put_slice(&strategy_bytes);
         bytes.put_u32_le(count);
@@ -149,7 +152,7 @@ impl BytesSerializable for PollMessages {
     }
 
     fn from_bytes(bytes: Bytes) -> Result<Self, IggyError> {
-        if bytes.len() < 29 {
+        if bytes.len() < 30 {
             return Err(IggyError::InvalidCommand);
         }
 
@@ -165,17 +168,20 @@ impl BytesSerializable for PollMessages {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..))?;
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let partition_id = u32::from_le_bytes(
-            bytes[position..position + 4]
+        // Decode partition_id with flag byte: 1 = Some, 0 = None
+        let has_partition_id = bytes[position];
+        let partition_id_value = u32::from_le_bytes(
+            bytes[position + 1..position + 5]
                 .try_into()
                 .map_err(|_| IggyError::InvalidNumberEncoding)?,
         );
-        let partition_id = match partition_id {
-            0 => None,
-            partition_id => Some(partition_id),
+        let partition_id = if has_partition_id == 1 {
+            Some(partition_id_value)
+        } else {
+            None
         };
-        let polling_kind = PollingKind::from_code(bytes[position + 4])?;
-        position += 5;
+        let polling_kind = PollingKind::from_code(bytes[position + 5])?;
+        position += 6;
         let value = u64::from_le_bytes(
             bytes[position..position + 8]
                 .try_into()
@@ -254,9 +260,16 @@ mod tests {
         position += stream_id.get_size_bytes().as_bytes_usize();
         let topic_id = Identifier::from_bytes(bytes.slice(position..)).unwrap();
         position += topic_id.get_size_bytes().as_bytes_usize();
-        let partition_id = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap());
-        let polling_kind = PollingKind::from_code(bytes[position + 4]).unwrap();
-        position += 5;
+        let has_partition_id = bytes[position];
+        let partition_id =
+            u32::from_le_bytes(bytes[position + 1..position + 5].try_into().unwrap());
+        let partition_id = if has_partition_id == 1 {
+            Some(partition_id)
+        } else {
+            None
+        };
+        let polling_kind = PollingKind::from_code(bytes[position + 5]).unwrap();
+        position += 6;
         let value = u64::from_le_bytes(bytes[position..position + 8].try_into().unwrap());
         let strategy = PollingStrategy {
             kind: polling_kind,
@@ -270,7 +283,7 @@ mod tests {
         assert_eq!(consumer, command.consumer);
         assert_eq!(stream_id, command.stream_id);
         assert_eq!(topic_id, command.topic_id);
-        assert_eq!(Some(partition_id), command.partition_id);
+        assert_eq!(partition_id, command.partition_id);
         assert_eq!(strategy, command.strategy);
         assert_eq!(count, command.count);
         assert_eq!(auto_commit, command.auto_commit);
@@ -291,7 +304,7 @@ mod tests {
         let topic_id_bytes = topic_id.to_bytes();
         let strategy_bytes = strategy.to_bytes();
         let mut bytes = BytesMut::with_capacity(
-            9 + consumer_bytes.len()
+            10 + consumer_bytes.len()
                 + stream_id_bytes.len()
                 + topic_id_bytes.len()
                 + strategy_bytes.len(),
@@ -299,6 +312,7 @@ mod tests {
         bytes.put_slice(&consumer_bytes);
         bytes.put_slice(&stream_id_bytes);
         bytes.put_slice(&topic_id_bytes);
+        bytes.put_u8(1); // Flag: partition_id is Some
         bytes.put_u32_le(partition_id);
         bytes.put_slice(&strategy_bytes);
         bytes.put_u32_le(count);
diff --git a/core/common/src/commands/streams/create_stream.rs b/core/common/src/commands/streams/create_stream.rs
index c163e7723..292cfcfc2 100644
--- a/core/common/src/commands/streams/create_stream.rs
+++ b/core/common/src/commands/streams/create_stream.rs
@@ -28,12 +28,9 @@ use std::str::from_utf8;
 
 /// `CreateStream` command is used to create a new stream.
 /// It has additional payload:
-/// - `stream_id` - unique stream ID (numeric)
 /// - `name` - unique stream name (string), max length is 255 characters.
 #[derive(Debug, Serialize, Deserialize, PartialEq)]
 pub struct CreateStream {
-    /// Unique stream ID (numeric), if None is provided then the server will automatically assign it.
-    pub stream_id: Option<u32>,
     /// Unique stream name (string), max length is 255 characters.
     pub name: String,
 }
@@ -47,7 +44,6 @@ impl Command for CreateStream {
 impl Default for CreateStream {
     fn default() -> Self {
         CreateStream {
-            stream_id: Some(1),
             name: "stream".to_string(),
         }
     }
@@ -55,12 +51,6 @@ impl Default for CreateStream {
 
 impl Validatable<IggyError> for CreateStream {
     fn validate(&self) -> Result<(), IggyError> {
-        if let Some(stream_id) = self.stream_id
-            && stream_id == 0
-        {
-            return Err(IggyError::InvalidStreamId);
-        }
-
         if self.name.is_empty() || self.name.len() > MAX_NAME_LENGTH {
             return Err(IggyError::InvalidStreamName);
         }
@@ -72,7 +62,6 @@ impl Validatable<IggyError> for CreateStream {
 impl BytesSerializable for CreateStream {
     fn to_bytes(&self) -> Bytes {
         let mut bytes = BytesMut::with_capacity(5 + self.name.len());
-        bytes.put_u32_le(self.stream_id.unwrap_or(0));
         #[allow(clippy::cast_possible_truncation)]
         bytes.put_u8(self.name.len() as u8);
         bytes.put_slice(self.name.as_bytes());
@@ -80,36 +69,26 @@ impl BytesSerializable for CreateStream {
     }
 
     fn from_bytes(bytes: Bytes) -> Result<CreateStream, IggyError> {
-        if bytes.len() < 6 {
+        if bytes.len() < 2 {
             return Err(IggyError::InvalidCommand);
         }
 
-        let stream_id = u32::from_le_bytes(
-            bytes[..4]
-                .try_into()
-                .map_err(|_| IggyError::InvalidNumberEncoding)?,
-        );
-        let stream_id = if stream_id == 0 {
-            None
-        } else {
-            Some(stream_id)
-        };
-        let name_length = bytes[4];
-        let name = from_utf8(&bytes[5..5 + name_length as usize])
+        let name_length = bytes[0];
+        let name = from_utf8(&bytes[1..1 + name_length as usize])
             .map_err(|_| IggyError::InvalidUtf8)?
             .to_string();
         if name.len() != name_length as usize {
             return Err(IggyError::InvalidCommand);
         }
 
-        let command = CreateStream { stream_id, name };
+        let command = CreateStream { name };
         Ok(command)
     }
 }
 
 impl Display for CreateStream {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        write!(f, "{}|{}", self.stream_id.unwrap_or(0), self.name)
+        write!(f, "{}", self.name)
     }
 }
 
@@ -120,26 +99,21 @@ mod tests {
     #[test]
     fn should_be_serialized_as_bytes() {
         let command = CreateStream {
-            stream_id: Some(1),
             name: "test".to_string(),
         };
 
         let bytes = command.to_bytes();
-        let stream_id = u32::from_le_bytes(bytes[..4].try_into().unwrap());
-        let name_length = bytes[4];
-        let name = from_utf8(&bytes[5..5 + name_length as usize]).unwrap();
+        let name_length = bytes[0];
+        let name = from_utf8(&bytes[1..1 + name_length as usize]).unwrap();
 
         assert!(!bytes.is_empty());
-        assert_eq!(stream_id, command.stream_id.unwrap());
         assert_eq!(name, command.name);
     }
 
     #[test]
     fn should_be_deserialized_from_bytes() {
-        let stream_id = 1u32;
         let name = "test".to_string();
         let mut bytes = BytesMut::new();
-        bytes.put_u32_le(stream_id);
         #[allow(clippy::cast_possible_truncation)]
         bytes.put_u8(name.len() as u8);
         bytes.put_slice(name.as_bytes());
@@ -147,7 +121,6 @@ mod tests {
         assert!(command.is_ok());
 
         let command = command.unwrap();
-        assert_eq!(command.stream_id.unwrap(), stream_id);
         assert_eq!(command.name, name);
     }
 }
diff --git a/core/common/src/commands/topics/create_topic.rs b/core/common/src/commands/topics/create_topic.rs
index f8ac2da0b..ba90084e7 100644
--- a/core/common/src/commands/topics/create_topic.rs
+++ b/core/common/src/commands/topics/create_topic.rs
@@ -34,7 +34,6 @@ use std::str::from_utf8;
 /// `CreateTopic` command is used to create a new topic in a stream.
 /// It has additional payload:
 /// - `stream_id` - unique stream ID (numeric or name).
-/// - `topic_id` - unique topic ID (numeric).
 /// - `partitions_count` - number of partitions in the topic, max value is 1000.
 /// - `message_expiry` - message expiry, if `NeverExpire` then messages will never expire.
 /// - `max_topic_size` - maximum size of the topic, if `Unlimited` then topic size is unlimited.
@@ -46,8 +45,6 @@ pub struct CreateTopic {
     /// Unique stream ID (numeric or name).
     #[serde(skip)]
     pub stream_id: Identifier,
-    /// Unique topic ID (numeric), if None is provided then the server will automatically assign it.
-    pub topic_id: Option<u32>,
     /// Number of partitions in the topic, max value is 1000.
     pub partitions_count: u32,
     /// Compression algorithm for the topic.
@@ -73,7 +70,6 @@ impl Default for CreateTopic {
     fn default() -> Self {
         CreateTopic {
             stream_id: Identifier::default(),
-            topic_id: Some(1),
             partitions_count: 1,
             compression_algorithm: CompressionAlgorithm::None,
             message_expiry: IggyExpiry::NeverExpire,
@@ -86,12 +82,6 @@ impl Default for CreateTopic {
 
 impl Validatable<IggyError> for CreateTopic {
     fn validate(&self) -> Result<(), IggyError> {
-        if let Some(topic_id) = self.topic_id
-            && topic_id == 0
-        {
-            return Err(IggyError::InvalidTopicId);
-        }
-
         if self.name.is_empty() || self.name.len() > MAX_NAME_LENGTH {
             return Err(IggyError::InvalidTopicName);
         }
@@ -113,9 +103,8 @@ impl Validatable<IggyError> for CreateTopic {
 impl BytesSerializable for CreateTopic {
     fn to_bytes(&self) -> Bytes {
         let stream_id_bytes = self.stream_id.to_bytes();
-        let mut bytes = BytesMut::with_capacity(23 + stream_id_bytes.len() + self.name.len());
+        let mut bytes = BytesMut::with_capacity(19 + stream_id_bytes.len() + self.name.len());
         bytes.put_slice(&stream_id_bytes);
-        bytes.put_u32_le(self.topic_id.unwrap_or(0));
         bytes.put_u32_le(self.partitions_count);
         bytes.put_u8(self.compression_algorithm.as_code());
         bytes.put_u64_le(self.message_expiry.into());
@@ -131,42 +120,36 @@ impl BytesSerializable for CreateTopic {
     }
 
     fn from_bytes(bytes: Bytes) -> std::result::Result<CreateTopic, IggyError> {
-        if bytes.len() < 18 {
+        if bytes.len() < 14 {
             return Err(IggyError::InvalidCommand);
         }
         let mut position = 0;
         let stream_id = Identifier::from_bytes(bytes.clone())?;
         position += stream_id.get_size_bytes().as_bytes_usize();
-        let topic_id = u32::from_le_bytes(
-            bytes[position..position + 4]
-                .try_into()
-                .map_err(|_| IggyError::InvalidNumberEncoding)?,
-        );
-        let topic_id = if topic_id == 0 { None } else { Some(topic_id) };
         let partitions_count = u32::from_le_bytes(
-            bytes[position + 4..position + 8]
+            bytes[position..position + 4]
                 .try_into()
                 .map_err(|_| IggyError::InvalidNumberEncoding)?,
         );
-        let compression_algorithm = CompressionAlgorithm::from_code(bytes[position + 8])?;
+        let compression_algorithm = CompressionAlgorithm::from_code(bytes[position + 4])?;
         let message_expiry = u64::from_le_bytes(
-            bytes[position + 9..position + 17]
+            bytes[position + 5..position + 13]
                 .try_into()
                 .map_err(|_| IggyError::InvalidNumberEncoding)?,
         );
         let message_expiry: IggyExpiry = message_expiry.into();
         let max_topic_size = u64::from_le_bytes(
-            bytes[position + 17..position + 25]
+            bytes[position + 13..position + 21]
                 .try_into()
                 .map_err(|_| IggyError::InvalidNumberEncoding)?,
         );
         let max_topic_size: MaxTopicSize = max_topic_size.into();
-        let replication_factor = match bytes[position + 25] {
+        let replication_factor = match bytes[position + 21] {
             0 => None,
             factor => Some(factor),
         };
-        let name_length = bytes[position + 26];
-        let name = from_utf8(&bytes[position + 27..(position + 27 + name_length as usize)])
+        let name_length = bytes[position + 22];
+        let name = from_utf8(&bytes[position + 23..(position + 23 + name_length as usize)])
             .map_err(|_| IggyError::InvalidUtf8)?
             .to_string();
         if name.len() != name_length as usize {
@@ -174,7 +157,6 @@ impl BytesSerializable for CreateTopic {
         }
         let command = CreateTopic {
             stream_id,
-            topic_id,
             partitions_count,
             compression_algorithm,
             message_expiry,
@@ -190,9 +172,8 @@ impl Display for CreateTopic {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         write!(
             f,
-            "{}|{}|{}|{}|{}|{}|{}",
+            "{}|{}|{}|{}|{}|{}",
             self.stream_id,
-            self.topic_id.unwrap_or(0),
             self.partitions_count,
             self.message_expiry,
             self.max_topic_size,
@@ -211,7 +192,6 @@ mod tests {
     fn should_be_serialized_as_bytes() {
         let command = CreateTopic {
             stream_id: Identifier::numeric(1).unwrap(),
-            topic_id: Some(2),
             partitions_count: 3,
             message_expiry: IggyExpiry::NeverExpire,
             compression_algorithm: CompressionAlgorithm::None,
@@ -223,25 +203,23 @@ mod tests {
         let mut position = 0;
         let stream_id = Identifier::from_bytes(bytes.clone()).unwrap();
         position += stream_id.get_size_bytes().as_bytes_usize();
-        let topic_id = u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap());
         let partitions_count =
-            u32::from_le_bytes(bytes[position + 4..position + 8].try_into().unwrap());
-        let compression_algorithm = CompressionAlgorithm::from_code(bytes[position + 8]).unwrap();
+            u32::from_le_bytes(bytes[position..position + 4].try_into().unwrap());
+        let compression_algorithm = CompressionAlgorithm::from_code(bytes[position + 4]).unwrap();
         let message_expiry =
-            u64::from_le_bytes(bytes[position + 9..position + 17].try_into().unwrap());
+            u64::from_le_bytes(bytes[position + 5..position + 13].try_into().unwrap());
         let message_expiry: IggyExpiry = message_expiry.into();
         let max_topic_size =
-            u64::from_le_bytes(bytes[position + 17..position + 25].try_into().unwrap());
+            u64::from_le_bytes(bytes[position + 13..position + 21].try_into().unwrap());
         let max_topic_size: MaxTopicSize = max_topic_size.into();
-        let replication_factor = bytes[position + 25];
-        let name_length = bytes[position + 26];
-        let name = from_utf8(&bytes[position + 27..(position + 27 + name_length as usize)])
+        let replication_factor = bytes[position + 21];
+        let name_length = bytes[position + 22];
+        let name = from_utf8(&bytes[position + 23..(position + 23 + name_length as usize)])
             .unwrap()
             .to_string();
 
         assert!(!bytes.is_empty());
         assert_eq!(stream_id, command.stream_id);
-        assert_eq!(topic_id, command.topic_id.unwrap());
         assert_eq!(partitions_count, command.partitions_count);
         assert_eq!(compression_algorithm, command.compression_algorithm);
         assert_eq!(message_expiry, command.message_expiry);
@@ -254,7 +232,6 @@ mod tests {
     #[test]
     fn should_be_deserialized_from_bytes() {
         let stream_id = Identifier::numeric(1).unwrap();
-        let topic_id = 2u32;
         let partitions_count = 3u32;
         let compression_algorithm = CompressionAlgorithm::None;
         let name = "test".to_string();
@@ -262,9 +239,8 @@ mod tests {
         let max_topic_size = MaxTopicSize::ServerDefault;
         let replication_factor = 1;
         let stream_id_bytes = stream_id.to_bytes();
-        let mut bytes = BytesMut::with_capacity(14 + stream_id_bytes.len() + name.len());
+        let mut bytes = BytesMut::with_capacity(10 + stream_id_bytes.len() + name.len());
         bytes.put_slice(&stream_id_bytes);
-        bytes.put_u32_le(topic_id);
         bytes.put_u32_le(partitions_count);
         bytes.put_u8(compression_algorithm.as_code());
         bytes.put_u64_le(message_expiry.into());
@@ -279,7 +255,6 @@ mod tests {
 
         let command = command.unwrap();
         assert_eq!(command.stream_id, stream_id);
-        assert_eq!(command.topic_id.unwrap(), topic_id);
         assert_eq!(command.name, name);
         assert_eq!(command.partitions_count, partitions_count);
         assert_eq!(command.compression_algorithm, compression_algorithm);
diff --git a/core/common/src/commands/users/defaults.rs b/core/common/src/commands/users/defaults.rs
index f18c8e0da..3d60f18d9 100644
--- a/core/common/src/commands/users/defaults.rs
+++ b/core/common/src/commands/users/defaults.rs
@@ -23,6 +23,6 @@ pub const MIN_PASSWORD_LENGTH: usize = 3;
 pub const MAX_PAT_LENGTH: usize = 100;
 pub const MAX_PERSONAL_ACCESS_TOKEN_NAME_LENGTH: usize = 30;
 pub const MIN_PERSONAL_ACCESS_TOKEN_NAME_LENGTH: usize = 3;
-pub const DEFAULT_ROOT_USER_ID: u32 = 1;
+pub const DEFAULT_ROOT_USER_ID: u32 = 0;
 pub const DEFAULT_ROOT_USERNAME: &str = "iggy";
 pub const DEFAULT_ROOT_PASSWORD: &str = "iggy";
diff --git a/core/common/src/error/iggy_error.rs b/core/common/src/error/iggy_error.rs
index cc8a284b1..6939c38ba 100644
--- a/core/common/src/error/iggy_error.rs
+++ b/core/common/src/error/iggy_error.rs
@@ -16,6 +16,7 @@
  * under the License.
  */
 
+use crate::Identifier;
 use crate::utils::topic_size::MaxTopicSize;
 use crate::{IggyMessage, utils::byte_size::IggyByteSize};
 use std::sync::Arc;
@@ -68,6 +69,8 @@ pub enum IggyError {
     CannotOpenDatabase(String) = 19,
     #[error("Resource with key: {0} was not found.")]
     ResourceNotFound(String) = 20,
+    #[error("Cannot close WebSocket connection")]
+    CannotCloseWebSocketConnection(String) = 21,
     #[error("Stale client")]
     StaleClient = 30,
     #[error("TCP error")]
@@ -174,6 +177,16 @@ pub enum IggyError {
     CannotCreateEndpoint = 305,
     #[error("Cannot parse URL")]
     CannotParseUrl = 306,
+    #[error("WebSocket error")]
+    WebSocketError = 400,
+    #[error("WebSocket connection error")]
+    WebSocketConnectionError = 401,
+    #[error("WebSocket close error")]
+    WebSocketCloseError = 402,
+    #[error("WebSocket receive error")]
+    WebSocketReceiveError = 403,
+    #[error("WebSocket send error")]
+    WebSocketSendError = 404,
     #[error("Cannot create streams directory, Path: {0}")]
     CannotCreateStreamsDirectory(String) = 1000,
     #[error("Cannot create stream with ID: {0} directory, Path: {1}")]
@@ -193,11 +206,11 @@ pub enum IggyError {
     #[error("Failed to delete stream directory with ID: {0}")]
     CannotDeleteStreamDirectory(u32) = 1008,
     #[error("Stream with ID: {0} was not found.")]
-    StreamIdNotFound(u32) = 1009,
+    StreamIdNotFound(Identifier) = 1009,
     #[error("Stream with name: {0} was not found.")]
     StreamNameNotFound(String) = 1010,
-    #[error("Stream with ID: {0} already exists.")]
-    StreamIdAlreadyExists(u32) = 1011,
+    #[error("Stream with directory {0} was not found.")]
+    StreamDirectoryNotFound(String) = 1011,
     #[error("Stream with name: {0} already exists.")]
     StreamNameAlreadyExists(String) = 1012,
     #[error("Invalid stream name")]
@@ -211,9 +224,9 @@ pub enum IggyError {
     )]
     InvalidTopicSize(MaxTopicSize, IggyByteSize) = 1019,
     #[error("Cannot create topics directory for stream with ID: {0}, Path: {1}")]
-    CannotCreateTopicsDirectory(u32, String) = 2000,
+    CannotCreateTopicsDirectory(Identifier, String) = 2000,
     #[error("Failed to create directory for topic with ID: {0} for stream with ID: {1}, Path: {2}")]
-    CannotCreateTopicDirectory(u32, u32, String) = 2001,
+    CannotCreateTopicDirectory(usize, usize, String) = 2001,
     #[error("Failed to create topic info file for topic with ID: {0} for stream with ID: {1}.")]
     CannotCreateTopicInfo(u32, u32) = 2002,
     #[error("Failed to update topic info for topic with ID: {0} for stream with ID: {1}.")]
@@ -231,13 +244,11 @@ pub enum IggyError {
     #[error("Cannot poll topic")]
     CannotPollTopic = 2009,
     #[error("Topic with ID: {0} for stream with ID: {1} was not found.")]
-    TopicIdNotFound(u32, u32) = 2010,
+    TopicIdNotFound(Identifier, Identifier) = 2010,
     #[error("Topic with name: {0} for stream with ID: {1} was not found.")]
     TopicNameNotFound(String, String) = 2011,
-    #[error("Topic with ID: {0} for stream with ID: {1} already exists.")]
-    TopicIdAlreadyExists(u32, u32) = 2012,
     #[error("Topic with name: {0} for stream with ID: {1} already exists.")]
-    TopicNameAlreadyExists(String, u32) = 2013,
+    TopicNameAlreadyExists(String, Identifier) = 2013,
     #[error("Invalid topic name")]
     InvalidTopicName = 2014,
     #[error("Too many partitions")]
@@ -248,16 +259,20 @@ pub enum IggyError {
     CannotReadTopics(u32) = 2017,
     #[error("Invalid replication factor")]
     InvalidReplicationFactor = 2018,
+    #[error("Invalid partitions count")]
+    InvalidPartitionsCount = 2019,
+    #[error("Topic directory: {0} not found")]
+    TopicDirectoryNotFound(String) = 2020,
     #[error("Cannot create partition with ID: {0} for stream with ID: {1} and topic with ID: {2}")]
-    CannotCreatePartition(u32, u32, u32) = 3000,
+    CannotCreatePartition(usize, usize, usize) = 3000,
     #[error(
         "Failed to create directory for partitions for stream with ID: {0} and topic with ID: {1}"
     )]
-    CannotCreatePartitionsDirectory(u32, u32) = 3001,
+    CannotCreatePartitionsDirectory(usize, usize) = 3001,
     #[error(
         "Failed to create directory for partition with ID: {0} for stream with ID: {1} and topic with ID: {2}"
     )]
-    CannotCreatePartitionDirectory(u32, u32, u32) = 3002,
+    CannotCreatePartitionDirectory(usize, usize, usize) = 3002,
     #[error("Cannot open partition log file")]
     CannotOpenPartitionLogFile = 3003,
     #[error("Cannot read partitions directories.")]
@@ -265,17 +280,17 @@ pub enum IggyError {
     #[error(
         "Failed to delete partition with ID: {0} for stream with ID: {1} and topic with ID: {2}"
     )]
-    CannotDeletePartition(u32, u32, u32) = 3005,
+    CannotDeletePartition(usize, Identifier, Identifier) = 3005,
     #[error(
         "Failed to delete partition directory with ID: {0} for stream with ID: {1} and topic with ID: {2}"
     )]
-    CannotDeletePartitionDirectory(u32, u32, u32) = 3006,
+    CannotDeletePartitionDirectory(usize, usize, usize) = 3006,
     #[error("Partition with ID: {0} for topic with ID: {1} for stream with ID: {2} was not found.")]
-    PartitionNotFound(u32, u32, u32) = 3007,
+    PartitionNotFound(usize, Identifier, Identifier) = 3007,
     #[error("Topic with ID: {0} for stream with ID: {1} has no partitions.")]
-    NoPartitions(u32, u32) = 3008,
+    NoPartitions(Identifier, Identifier) = 3008,
     #[error("Cannot read partitions for topic with ID: {0} for stream with ID: {1}")]
-    TopicFull(u32, u32) = 3009,
+    TopicFull(Identifier, Identifier) = 3009,
     #[error("Failed to delete consumer offsets directory for path: {0}")]
     CannotDeleteConsumerOffsetsDirectory(String) = 3010,
     #[error("Failed to delete consumer offset file for path: {0}")]
@@ -285,11 +300,15 @@ pub enum IggyError {
     #[error("Failed to read consumers offsets from path: {0}")]
     CannotReadConsumerOffsets(String) = 3020,
     #[error("Consumer offset for consumer with ID: {0} was not found.")]
-    ConsumerOffsetNotFound(u32) = 3021,
+    ConsumerOffsetNotFound(usize) = 3021,
+    #[error("Failed to resolve consumer with ID: {0}")]
+    NotResolvedConsumer(Identifier) = 3022,
+    #[error("Cannot open consumer offsets file for path: {0}")]
+    CannotOpenConsumerOffsetsFile(String) = 3023,
     #[error("Segment not found")]
     SegmentNotFound = 4000,
     #[error("Segment with start offset: {0} and partition with ID: {1} is closed")]
-    SegmentClosed(u64, u32) = 4001,
+    SegmentClosed(u64, usize) = 4001,
     #[error("Segment size is invalid")]
     InvalidSegmentSize(u64) = 4002,
     #[error("Failed to create segment log file for Path: {0}.")]
@@ -386,29 +405,27 @@ pub enum IggyError {
     #[error("Invalid offset: {0}")]
     InvalidOffset(u64) = 4100,
     #[error("Consumer group with ID: {0} for topic with ID: {1} was not found.")]
-    ConsumerGroupIdNotFound(u32, u32) = 5000,
-    #[error("Consumer group with ID: {0} for topic with ID: {1} already exists.")]
-    ConsumerGroupIdAlreadyExists(u32, u32) = 5001,
+    ConsumerGroupIdNotFound(Identifier, Identifier) = 5000,
     #[error("Invalid consumer group ID")]
     InvalidConsumerGroupId = 5002,
     #[error("Consumer group with name: {0} for topic with ID: {1} was not found.")]
-    ConsumerGroupNameNotFound(String, String) = 5003,
+    ConsumerGroupNameNotFound(String, Identifier) = 5003,
     #[error("Consumer group with name: {0} for topic with ID: {1} already exists.")]
-    ConsumerGroupNameAlreadyExists(String, u32) = 5004,
+    ConsumerGroupNameAlreadyExists(String, Identifier) = 5004,
     #[error("Invalid consumer group name")]
     InvalidConsumerGroupName = 5005,
     #[error(
         "Consumer group member with ID: {0} for group with ID: {1} for topic with ID: {2} was not found."
     )]
-    ConsumerGroupMemberNotFound(u32, u32, u32) = 5006,
+    ConsumerGroupMemberNotFound(usize, Identifier, Identifier) = 5006,
     #[error(
         "Failed to create consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}."
     )]
-    CannotCreateConsumerGroupInfo(u32, u32, u32) = 5007,
+    CannotCreateConsumerGroupInfo(usize, Identifier, Identifier) = 5007,
     #[error(
         "Failed to delete consumer group info file for ID: {0} for topic with ID: {1} for stream with ID: {2}."
     )]
-    CannotDeleteConsumerGroupInfo(u32, u32, u32) = 5008,
+    CannotDeleteConsumerGroupInfo(usize, Identifier, Identifier) = 5008,
     #[error("Base offset is missing")]
     MissingBaseOffsetRetainedMessageBatch = 6000,
     #[error("Last offset delta is missing")]
@@ -459,6 +476,19 @@ pub enum IggyError {
     CannotReadIndexPosition = 10011,
     #[error("Cannot read index timestamp")]
     CannotReadIndexTimestamp = 10012,
+
+    #[error("Timestamp out of range: {0}")]
+    TimestampOutOfRange(u64) = 10013,
+
+    #[error("Shard not found for stream ID: {0}, topic ID: {1}, partition ID: {2}")]
+    ShardNotFound(usize, usize, usize) = 11000,
+    #[error("Shard communication error")]
+    ShardCommunicationError = 11001,
+
+    #[error("Cannot bind to socket with addr: {0}")]
+    CannotBindToSocket(String) = 12000,
+    #[error("Task execution timeout")]
+    TaskTimeout = 12001,
 }
 
 impl IggyError {
diff --git a/core/common/src/lib.rs b/core/common/src/lib.rs
index dbf26cefe..ababdfbb1 100644
--- a/core/common/src/lib.rs
+++ b/core/common/src/lib.rs
@@ -66,7 +66,10 @@ pub use types::configuration::tcp_config::tcp_client_config_builder::*;
 pub use types::configuration::tcp_config::tcp_client_reconnection_config::*;
 pub use types::configuration::tcp_config::tcp_connection_string_options::*;
 pub use types::configuration::transport::TransportProtocol;
-pub use types::confirmation::*;
+pub use types::configuration::websocket_config::websocket_client_config::*;
+pub use types::configuration::websocket_config::websocket_client_config_builder::*;
+pub use types::configuration::websocket_config::websocket_client_reconnection_config::*;
+pub use types::configuration::websocket_config::websocket_connection_string_options::*;
 pub use types::consumer::consumer_group::*;
 pub use types::consumer::consumer_kind::*;
 pub use types::consumer::consumer_offset_info::*;
diff --git a/core/common/src/locking/fast_async_lock.rs b/core/common/src/locking/fast_async_lock.rs
index 4e2fb00a2..3d81c8c15 100644
--- a/core/common/src/locking/fast_async_lock.rs
+++ b/core/common/src/locking/fast_async_lock.rs
@@ -16,14 +16,14 @@
  * under the License.
  */
 
-use crate::locking::IggySharedMutFn;
+use crate::locking::IggyRwLockFn;
 use fast_async_mutex::rwlock::{RwLock as FastAsyncRwLock, RwLockReadGuard, RwLockWriteGuard};
 use std::sync::Arc;
 
 #[derive(Debug)]
 pub struct IggyFastAsyncRwLock<T>(Arc<FastAsyncRwLock<T>>);
 
-impl<T> IggySharedMutFn<T> for IggyFastAsyncRwLock<T>
+impl<T> IggyRwLockFn<T> for IggyFastAsyncRwLock<T>
 where
     T: Send + Sync,
 {
diff --git a/core/common/src/locking/mod.rs b/core/common/src/locking/mod.rs
index 42d72ebb3..630de5adc 100644
--- a/core/common/src/locking/mod.rs
+++ b/core/common/src/locking/mod.rs
@@ -29,19 +29,19 @@ mod fast_async_lock;
 
 #[cfg(feature = "tokio_lock")]
 #[cfg(not(any(feature = "fast_async_lock")))]
-pub type IggySharedMut<T> = tokio_lock::IggyTokioRwLock<T>;
+pub type IggyRwLock<T> = tokio_lock::IggyTokioRwLock<T>;
 
 //this can be used in the future to provide different locking mechanisms
 #[cfg(feature = "fast_async_lock")]
-pub type IggySharedMut<T> = fast_async_lock::IggyFastAsyncRwLock<T>;
+pub type IggyRwLock<T> = fast_async_lock::IggyFastAsyncRwLock<T>;
 
 #[allow(async_fn_in_trait)]
-pub trait IggySharedMutFn<T>: Send + Sync {
-    type ReadGuard<'a>: Deref<Target = T> + Send
+pub trait IggyRwLockFn<T> {
+    type ReadGuard<'a>: Deref<Target = T>
     where
         T: 'a,
         Self: 'a;
-    type WriteGuard<'a>: DerefMut<Target = T> + Send
+    type WriteGuard<'a>: DerefMut<Target = T>
     where
         T: 'a,
         Self: 'a;
diff --git a/core/common/src/locking/tokio_lock.rs b/core/common/src/locking/tokio_lock.rs
index 2e26b367e..d2e46ef1d 100644
--- a/core/common/src/locking/tokio_lock.rs
+++ b/core/common/src/locking/tokio_lock.rs
@@ -16,7 +16,7 @@
  * under the License.
  */
 
-use crate::locking::IggySharedMutFn;
+use crate::locking::IggyRwLockFn;
 use std::sync::Arc;
 use tokio::sync::{RwLock as TokioRwLock, RwLockReadGuard, RwLockWriteGuard};
 
@@ -24,10 +24,7 @@ use tokio::sync::{RwLock as TokioRwLock, RwLockReadGuard, RwLockWriteGuard};
 #[derive(Debug)]
 pub struct IggyTokioRwLock<T>(Arc<TokioRwLock<T>>);
 
-impl<T> IggySharedMutFn<T> for IggyTokioRwLock<T>
-where
-    T: Send + Sync,
-{
+impl<T> IggyRwLockFn<T> for IggyTokioRwLock<T> {
     type ReadGuard<'a>
         = RwLockReadGuard<'a, T>
     where
diff --git a/core/common/src/types/args/mod.rs b/core/common/src/types/args/mod.rs
index 3010faa50..cc22f9439 100644
--- a/core/common/src/types/args/mod.rs
+++ b/core/common/src/types/args/mod.rs
@@ -28,7 +28,7 @@ use serde::{Deserialize, Serialize};
 #[derive(Parser, Debug, Clone, Deserialize, Serialize, Default)]
 #[command(author, version, about, long_about = None)]
 pub struct ArgsOptional {
-    /// The transport to use. Valid values are `quic`, `http` and `tcp`
+    /// The transport to use. Valid values are `quic`, `http`, `tcp` and `ws`
     ///
     /// [default: tcp]
     #[arg(long)]
@@ -198,12 +198,33 @@ pub struct ArgsOptional {
     #[arg(long, default_missing_value(Some("true")), num_args(0..1))]
     #[serde(skip_serializing_if = "Option::is_none")]
     pub quic_validate_certificate: Option<bool>,
+
+    /// The optional server address for the WebSocket transport
+    ///
+    /// [default: 127.0.0.1:8092]
+    #[arg(long)]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub websocket_server_address: Option<String>,
+
+    /// The optional number of max reconnect retries for the WebSocket transport
+    ///
+    /// [default: 3]
+    #[arg(long)]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub websocket_reconnection_max_retries: Option<u32>,
+
+    /// The optional reconnect interval for the WebSocket transport
+    ///
+    /// [default: "1s"]
+    #[arg(long)]
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub websocket_reconnection_interval: Option<String>,
 }
 
 /// The arguments used by the `ClientProviderConfig` to create a client.
 #[derive(Debug, Clone)]
 pub struct Args {
-    /// The transport to use. Valid values are `quic`, `http` and `tcp`
+    /// The transport to use. Valid values are `quic`, `http`, `tcp` and `ws`
     pub transport: String,
 
     /// Optional encryption key for the message payload used by the client
@@ -301,11 +322,42 @@ pub struct Args {
 
     /// The optional heartbeat interval for the QUIC transport
     pub quic_heartbeat_interval: String,
+
+    /// The optional server address for the WebSocket transport
+    pub websocket_server_address: String,
+
+    /// The optional number of maximum reconnect retries for the WebSocket transport
+    pub websocket_reconnection_enabled: bool,
+
+    /// The optional number of maximum reconnect retries for the WebSocket transport
+    pub websocket_reconnection_max_retries: Option<u32>,
+
+    /// The optional reconnect interval for the WebSocket transport
+    pub websocket_reconnection_interval: String,
+
+    /// The optional re-establish after last connection interval for WebSocket
+    pub websocket_reconnection_reestablish_after: String,
+
+    /// The optional heartbeat interval for the WebSocket transport
+    pub websocket_heartbeat_interval: String,
+
+    /// The optional TLS enabled for the WebSocket transport
+    pub websocket_tls_enabled: bool,
+
+    /// The optional TLS domain for the WebSocket transport
+    pub websocket_tls_domain: String,
+
+    /// The optional TLS CA file for the WebSocket transport
+    pub websocket_tls_ca_file: Option<String>,
+
+    /// The optional TLS validate certificate for the WebSocket transport
+    pub websocket_tls_validate_certificate: bool,
 }
 
 const QUIC_TRANSPORT: &str = "quic";
 const HTTP_TRANSPORT: &str = "http";
 const TCP_TRANSPORT: &str = "tcp";
+const WEBSOCKET_TRANSPORT: &str = "websocket";
 
 impl Args {
     pub fn get_server_address(&self) -> Option<String> {
@@ -318,6 +370,10 @@ impl Args {
                     .replace("localhost", "127.0.0.1"),
             ),
             TCP_TRANSPORT => Some(self.tcp_server_address.replace("localhost", "127.0.0.1")),
+            WEBSOCKET_TRANSPORT => Some(
+                self.websocket_server_address
+                    .replace("localhost", "127.0.0.1"),
+            ),
             _ => None,
         }
     }
@@ -352,13 +408,23 @@ impl Default for Args {
             quic_max_concurrent_bidi_streams: 10000,
             quic_datagram_send_buffer_size: 100000,
             quic_initial_mtu: 1200,
-            quic_send_window: 100000,
-            quic_receive_window: 100000,
+            quic_send_window: 1000000,
+            quic_receive_window: 1000000,
             quic_response_buffer_size: 1048576,
             quic_keep_alive_interval: 5000,
-            quic_max_idle_timeout: 10000,
+            quic_max_idle_timeout: 100000,
             quic_validate_certificate: false,
             quic_heartbeat_interval: "5s".to_string(),
+            websocket_server_address: "127.0.0.1:8092".to_string(),
+            websocket_reconnection_enabled: true,
+            websocket_reconnection_max_retries: None,
+            websocket_reconnection_interval: "1s".to_string(),
+            websocket_reconnection_reestablish_after: "5s".to_string(),
+            websocket_heartbeat_interval: "5s".to_string(),
+            websocket_tls_enabled: false,
+            websocket_tls_domain: "localhost".to_string(),
+            websocket_tls_ca_file: None,
+            websocket_tls_validate_certificate: false,
         }
     }
 }
@@ -447,6 +513,19 @@ impl From<Vec<ArgsOptional>> for Args {
             if let Some(quic_validate_certificate) = optional_args.quic_validate_certificate {
                 args.quic_validate_certificate = quic_validate_certificate;
             }
+            if let Some(websocket_server_address) = optional_args.websocket_server_address {
+                args.websocket_server_address = websocket_server_address;
+            }
+            if let Some(websocket_reconnection_retries) =
+                optional_args.websocket_reconnection_max_retries
+            {
+                args.websocket_reconnection_max_retries = Some(websocket_reconnection_retries);
+            }
+            if let Some(websocket_reconnection_interval) =
+                optional_args.websocket_reconnection_interval
+            {
+                args.websocket_reconnection_interval = websocket_reconnection_interval;
+            }
         }
 
         args
diff --git a/core/common/src/types/configuration/auth_config/connection_string.rs b/core/common/src/types/configuration/auth_config/connection_string.rs
index cb44c4684..391633d53 100644
--- a/core/common/src/types/configuration/auth_config/connection_string.rs
+++ b/core/common/src/types/configuration/auth_config/connection_string.rs
@@ -43,7 +43,11 @@ impl<T: ConnectionStringOptions + Default> ConnectionString<T> {
     }
 
     pub fn new(connection_string: &str) -> Result<Self, IggyError> {
-        let connection_string = connection_string.split("://").collect::<Vec<&str>>()[1];
+        let protocol_parts = connection_string.split("://").collect::<Vec<&str>>();
+        if protocol_parts.len() != 2 {
+            return Err(IggyError::InvalidConnectionString);
+        }
+        let connection_string = protocol_parts[1];
         let parts = connection_string.split('@').collect::<Vec<&str>>();
         let mut username = "";
         let mut password = "";
diff --git a/core/common/src/types/configuration/mod.rs b/core/common/src/types/configuration/mod.rs
index 75f03ed22..667e58884 100644
--- a/core/common/src/types/configuration/mod.rs
+++ b/core/common/src/types/configuration/mod.rs
@@ -20,3 +20,4 @@ pub(crate) mod http_config;
 pub(crate) mod quic_config;
 pub(crate) mod tcp_config;
 pub(crate) mod transport;
+pub(crate) mod websocket_config;
diff --git a/core/common/src/types/configuration/transport.rs b/core/common/src/types/configuration/transport.rs
index 356295f13..45c78eb63 100644
--- a/core/common/src/types/configuration/transport.rs
+++ b/core/common/src/types/configuration/transport.rs
@@ -32,6 +32,8 @@ pub enum TransportProtocol {
     Quic = 2,
     #[strum(to_string = "http")]
     Http = 3,
+    #[strum(to_string = "ws")]
+    WebSocket = 4,
 }
 
 impl TransportProtocol {
@@ -48,6 +50,7 @@ impl TryFrom<u8> for TransportProtocol {
             v if v == TransportProtocol::Tcp as u8 => Ok(TransportProtocol::Tcp),
             v if v == TransportProtocol::Quic as u8 => Ok(TransportProtocol::Quic),
             v if v == TransportProtocol::Http as u8 => Ok(TransportProtocol::Http),
+            v if v == TransportProtocol::WebSocket as u8 => Ok(TransportProtocol::WebSocket),
             _ => Err(IggyError::InvalidCommand),
         }
     }
@@ -98,6 +101,9 @@ impl<'de> Deserialize<'de> for TransportProtocol {
                     v if v == TransportProtocol::Tcp as u8 => Ok(TransportProtocol::Tcp),
                     v if v == TransportProtocol::Quic as u8 => Ok(TransportProtocol::Quic),
                     v if v == TransportProtocol::Http as u8 => Ok(TransportProtocol::Http),
+                    v if v == TransportProtocol::WebSocket as u8 => {
+                        Ok(TransportProtocol::WebSocket)
+                    }
                     _ => Err(serde::de::Error::custom(format!(
                         "invalid transport protocol number: {}",
                         value
diff --git a/examples/csharp/src/GettingStarted/Iggy_SDK.Examples.GettingStarted.Consumer/Program.cs b/core/common/src/types/configuration/websocket_config/mod.rs
similarity index 53%
rename from examples/csharp/src/GettingStarted/Iggy_SDK.Examples.GettingStarted.Consumer/Program.cs
rename to core/common/src/types/configuration/websocket_config/mod.rs
index deb1ae1df..46de921e5 100644
--- a/examples/csharp/src/GettingStarted/Iggy_SDK.Examples.GettingStarted.Consumer/Program.cs
+++ b/core/common/src/types/configuration/websocket_config/mod.rs
@@ -1,4 +1,4 @@
-// Licensed to the Apache Software Foundation (ASF) under one
+// Licensed to the Apache Software Foundation (ASF) under one
 // or more contributor license agreements.  See the NOTICE file
 // distributed with this work for additional information
 // regarding copyright ownership.  The ASF licenses this file
@@ -15,22 +15,7 @@
 // specific language governing permissions and limitations
 // under the License.
 
-using Apache.Iggy.Configuration;
-using Apache.Iggy.Enums;
-using Apache.Iggy.Factory;
-using Iggy_SDK.Examples.GettingStarted.Consumer;
-using Microsoft.Extensions.Logging;
-
-var loggerFactory = LoggerFactory.Create(b => { b.AddConsole(); });
-var logger = loggerFactory.CreateLogger<Program>();
-
-var client = IggyClientFactory.CreateClient(new IggyClientConfigurator()
-{
-    BaseAddress = Utils.GetTcpServerAddr(args, logger),
-    Protocol = Protocol.Tcp,
-    LoggerFactory = loggerFactory
-});
-
-await client.LoginUser("iggy", "iggy");
-
-await Utils.ConsumeMessages(client, logger);
+pub mod websocket_client_config;
+pub mod websocket_client_config_builder;
+pub mod websocket_client_reconnection_config;
+pub mod websocket_connection_string_options;
diff --git a/core/common/src/types/configuration/websocket_config/websocket_client_config.rs b/core/common/src/types/configuration/websocket_config/websocket_client_config.rs
new file mode 100644
index 000000000..58bc5d9e3
--- /dev/null
+++ b/core/common/src/types/configuration/websocket_config/websocket_client_config.rs
@@ -0,0 +1,192 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use crate::types::configuration::auth_config::connection_string::ConnectionString;
+use crate::types::configuration::websocket_config::websocket_connection_string_options::WebSocketConnectionStringOptions;
+use crate::{AutoLogin, IggyDuration, WebSocketClientReconnectionConfig};
+use compio_ws::WebSocketConfig as TungsteniteConfig;
+use std::fmt::{Display, Formatter};
+use std::str::FromStr;
+
+/// Configuration for the WebSocket client.
+#[derive(Debug, Clone)]
+pub struct WebSocketClientConfig {
+    /// The address of the Iggy server.
+    pub server_address: String,
+    /// Whether to automatically login user after establishing connection.
+    pub auto_login: AutoLogin,
+    /// Whether to automatically reconnect when disconnected.
+    pub reconnection: WebSocketClientReconnectionConfig,
+    /// Interval of heartbeats sent by the client
+    pub heartbeat_interval: IggyDuration,
+    /// WebSocket-specific configuration.
+    pub ws_config: WebSocketConfig,
+    /// Whether tls is enabled
+    pub tls_enabled: bool,
+    /// The domain to use for TLS
+    pub tls_domain: String,
+    /// The path to the CA file for TLS
+    pub tls_ca_file: Option<String>,
+    /// Whether to validate the TLS certificate
+    pub tls_validate_certificate: bool,
+}
+
+/// WebSocket-specific configuration that maps to tungstenite options.
+/// Uses same structure as server-side WebSocketConfig but for client.
+#[derive(Debug, Clone)]
+pub struct WebSocketConfig {
+    /// Read buffer size.
+    pub read_buffer_size: Option<usize>,
+    /// Write buffer size.
+    pub write_buffer_size: Option<usize>,
+    /// Maximum write buffer size.
+    pub max_write_buffer_size: Option<usize>,
+    /// Maximum message size.
+    pub max_message_size: Option<usize>,
+    /// Maximum frame size.
+    pub max_frame_size: Option<usize>,
+    /// Accept unmasked frames (client should typically keep as false).
+    pub accept_unmasked_frames: bool,
+}
+
+impl Default for WebSocketClientConfig {
+    fn default() -> Self {
+        WebSocketClientConfig {
+            server_address: "127.0.0.1:8092".to_string(),
+            auto_login: AutoLogin::Disabled,
+            reconnection: WebSocketClientReconnectionConfig::default(),
+            heartbeat_interval: IggyDuration::from_str("5s").unwrap(),
+            ws_config: WebSocketConfig::default(),
+            tls_enabled: false,
+            tls_domain: "localhost".to_string(),
+            tls_ca_file: None,
+            tls_validate_certificate: false,
+        }
+    }
+}
+
+impl Default for WebSocketConfig {
+    fn default() -> Self {
+        // Use tungstenite defaults
+        let tungstenite_config = TungsteniteConfig::default();
+        WebSocketConfig {
+            read_buffer_size: Some(tungstenite_config.read_buffer_size),
+            write_buffer_size: Some(tungstenite_config.write_buffer_size),
+            max_write_buffer_size: Some(tungstenite_config.max_write_buffer_size),
+            max_message_size: tungstenite_config.max_message_size,
+            max_frame_size: tungstenite_config.max_frame_size,
+            accept_unmasked_frames: false,
+        }
+    }
+}
+
+impl WebSocketConfig {
+    /// Convert to tungstenite WebSocketConfig so we can use tungstenite defaults
+    pub fn to_tungstenite_config(&self) -> TungsteniteConfig {
+        let mut config = TungsteniteConfig::default();
+
+        if let Some(read_buf_size) = self.read_buffer_size {
+            config = config.read_buffer_size(read_buf_size);
+        }
+
+        if let Some(write_buf_size) = self.write_buffer_size {
+            config = config.write_buffer_size(write_buf_size);
+        }
+
+        if let Some(max_write_buf_size) = self.max_write_buffer_size {
+            config = config.max_write_buffer_size(max_write_buf_size);
+        }
+
+        if let Some(max_msg_size) = self.max_message_size {
+            config = config.max_message_size(Some(max_msg_size));
+        }
+
+        if let Some(max_frame_size) = self.max_frame_size {
+            config = config.max_frame_size(Some(max_frame_size));
+        }
+
+        config = config.accept_unmasked_frames(self.accept_unmasked_frames);
+
+        config
+    }
+}
+
+impl From<ConnectionString<WebSocketConnectionStringOptions>> for WebSocketClientConfig {
+    fn from(connection_string: ConnectionString<WebSocketConnectionStringOptions>) -> Self {
+        let options = connection_string.options();
+
+        let mut ws_config = WebSocketConfig::default();
+
+        // Apply connection string options to WebSocket config
+        if let Some(read_buf_size) = options.read_buffer_size() {
+            ws_config.read_buffer_size = Some(read_buf_size);
+        }
+        if let Some(write_buf_size) = options.write_buffer_size() {
+            ws_config.write_buffer_size = Some(write_buf_size);
+        }
+        if let Some(max_write_buf_size) = options.max_write_buffer_size() {
+            ws_config.max_write_buffer_size = Some(max_write_buf_size);
+        }
+        if let Some(max_msg_size) = options.max_message_size() {
+            ws_config.max_message_size = Some(max_msg_size);
+        }
+        if let Some(max_frame_size) = options.max_frame_size() {
+            ws_config.max_frame_size = Some(max_frame_size);
+        }
+        if let Some(accept_unmasked) = options.accept_unmasked_frames() {
+            ws_config.accept_unmasked_frames = accept_unmasked;
+        }
+
+        WebSocketClientConfig {
+            server_address: connection_string.server_address().into(),
+            auto_login: connection_string.auto_login().to_owned(),
+            reconnection: connection_string.options().reconnection().to_owned(),
+            heartbeat_interval: connection_string.options().heartbeat_interval(),
+            ws_config,
+            tls_enabled: options.tls_enabled(),
+            tls_domain: options.tls_domain().into(),
+            tls_ca_file: options.tls_ca_file().map(|s| s.to_string()),
+            tls_validate_certificate: options.tls_validate_certificate(),
+        }
+    }
+}
+
+impl Display for WebSocketClientConfig {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "{{ server_address: {}, reconnection: {}, heartbeat_interval: {}, ws_config: {} }}",
+            self.server_address, self.reconnection, self.heartbeat_interval, self.ws_config
+        )
+    }
+}
+
+impl Display for WebSocketConfig {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "{{ read_buffer_size: {:?}, write_buffer_size: {:?}, max_write_buffer_size: {:?}, max_message_size: {:?}, max_frame_size: {:?}, accept_unmasked_frames: {} }}",
+            self.read_buffer_size,
+            self.write_buffer_size,
+            self.max_write_buffer_size,
+            self.max_message_size,
+            self.max_frame_size,
+            self.accept_unmasked_frames
+        )
+    }
+}
diff --git a/core/common/src/types/configuration/websocket_config/websocket_client_config_builder.rs b/core/common/src/types/configuration/websocket_config/websocket_client_config_builder.rs
new file mode 100644
index 000000000..27e296f6c
--- /dev/null
+++ b/core/common/src/types/configuration/websocket_config/websocket_client_config_builder.rs
@@ -0,0 +1,158 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use crate::{AutoLogin, IggyDuration, IggyError, WebSocketClientConfig};
+use std::net::SocketAddr;
+
+/// Builder for the WebSocket client configuration.
+/// Allows configuring the WebSocket client with custom settings or using defaults:
+/// - `server_address`: Default is "127.0.0.1:8092"
+/// - `auto_login`: Default is AutoLogin::Disabled.
+/// - `reconnection`: Default is enabled unlimited retries and 1 second interval.
+/// - `heartbeat_interval`: Default is 5 seconds.
+/// - WebSocket-specific options with sensible defaults from tungstenite.
+#[derive(Debug, Default)]
+pub struct WebSocketClientConfigBuilder {
+    config: WebSocketClientConfig,
+}
+
+impl WebSocketClientConfigBuilder {
+    pub fn new() -> Self {
+        WebSocketClientConfigBuilder::default()
+    }
+
+    /// Sets the server address for the WebSocket client.
+    pub fn with_server_address(mut self, server_address: String) -> Self {
+        self.config.server_address = server_address;
+        self
+    }
+
+    /// Sets the auto sign in during connection.
+    pub fn with_auto_sign_in(mut self, auto_sign_in: AutoLogin) -> Self {
+        self.config.auto_login = auto_sign_in;
+        self
+    }
+
+    pub fn with_enabled_reconnection(mut self) -> Self {
+        self.config.reconnection.enabled = true;
+        self
+    }
+
+    /// Sets the number of retries when connecting to the server.
+    pub fn with_reconnection_max_retries(mut self, max_retries: Option<u32>) -> Self {
+        self.config.reconnection.max_retries = max_retries;
+        self
+    }
+
+    /// Sets the interval between retries when connecting to the server.
+    pub fn with_reconnection_interval(mut self, interval: IggyDuration) -> Self {
+        self.config.reconnection.interval = interval;
+        self
+    }
+
+    /// Sets the time to wait before attempting to reestablish connection.
+    pub fn with_reestablish_after(mut self, reestablish_after: IggyDuration) -> Self {
+        self.config.reconnection.reestablish_after = reestablish_after;
+        self
+    }
+
+    /// Sets the heartbeat interval.
+    pub fn with_heartbeat_interval(mut self, heartbeat_interval: IggyDuration) -> Self {
+        self.config.heartbeat_interval = heartbeat_interval;
+        self
+    }
+
+    /// Sets the read buffer size.
+    pub fn with_read_buffer_size(mut self, size: usize) -> Self {
+        self.config.ws_config.read_buffer_size = Some(size);
+        self
+    }
+
+    /// Sets the write buffer size.
+    pub fn with_write_buffer_size(mut self, size: usize) -> Self {
+        self.config.ws_config.write_buffer_size = Some(size);
+        self
+    }
+
+    /// Sets the maximum write buffer size.
+    pub fn with_max_write_buffer_size(mut self, size: usize) -> Self {
+        self.config.ws_config.max_write_buffer_size = Some(size);
+        self
+    }
+
+    /// Sets the maximum message size.
+    pub fn with_max_message_size(mut self, size: usize) -> Self {
+        self.config.ws_config.max_message_size = Some(size);
+        self
+    }
+
+    /// Sets the maximum frame size.
+    pub fn with_max_frame_size(mut self, size: usize) -> Self {
+        self.config.ws_config.max_frame_size = Some(size);
+        self
+    }
+
+    /// Sets whether to accept unmasked frames.
+    /// Note: Clients should typically keep this as false for RFC compliance.
+    pub fn with_accept_unmasked_frames(mut self, accept: bool) -> Self {
+        self.config.ws_config.accept_unmasked_frames = accept;
+        self
+    }
+
+    /// Sets whether to use TLS when connecting to the server.
+    pub fn with_tls_enabled(mut self, tls_enabled: bool) -> Self {
+        self.config.tls_enabled = tls_enabled;
+        self
+    }
+
+    /// Sets the domain to use for TLS when connecting to the server.
+    pub fn with_tls_domain(mut self, tls_domain: String) -> Self {
+        self.config.tls_domain = tls_domain;
+        self
+    }
+
+    /// Sets the path to the CA file for TLS.
+    pub fn with_tls_ca_file(mut self, tls_ca_file: String) -> Self {
+        self.config.tls_ca_file = Some(tls_ca_file);
+        self
+    }
+
+    /// Sets whether to validate the TLS certificate.
+    pub fn with_tls_validate_certificate(mut self, tls_validate_certificate: bool) -> Self {
+        self.config.tls_validate_certificate = tls_validate_certificate;
+        self
+    }
+
+    /// Builds the WebSocket client configuration.
+    pub fn build(self) -> Result<WebSocketClientConfig, IggyError> {
+        let addr = self.config.server_address.trim();
+
+        // Check if it's a valid socket address or host:port format
+        if addr.parse::<SocketAddr>().is_err() {
+            let (host, port) = addr.rsplit_once(':').unwrap_or((addr, ""));
+            if port.is_empty() || port.parse::<u16>().is_err() {
+                return Err(IggyError::InvalidIpAddress(
+                    host.to_owned(),
+                    port.to_owned(),
+                ));
+            }
+        }
+
+        Ok(self.config)
+    }
+}
diff --git a/core/common/src/types/configuration/websocket_config/websocket_client_reconnection_config.rs b/core/common/src/types/configuration/websocket_config/websocket_client_reconnection_config.rs
new file mode 100644
index 000000000..c7a86aa90
--- /dev/null
+++ b/core/common/src/types/configuration/websocket_config/websocket_client_reconnection_config.rs
@@ -0,0 +1,61 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+use crate::IggyDuration;
+use serde::{Deserialize, Serialize};
+use std::fmt::{Display, Formatter};
+use std::str::FromStr;
+
+/// WebSocket client reconnection configuration.
+/// It consists of the following fields:
+/// - `enabled`: whether to enable reconnection.
+/// - `max_retries`: the maximum number of retries. If None, will retry infinitely.
+/// - `interval`: the interval between retries.
+/// - `reestablish_after`: the time to wait before attempting to reestablish connection.
+#[derive(Debug, Clone, Deserialize, Serialize)]
+pub struct WebSocketClientReconnectionConfig {
+    /// Whether to enable reconnection.
+    pub enabled: bool,
+    /// The maximum number of retries. If None, will retry infinitely.
+    pub max_retries: Option<u32>,
+    /// The interval between retries.
+    pub interval: IggyDuration,
+    /// The time to wait before attempting to reestablish connection.
+    pub reestablish_after: IggyDuration,
+}
+
+impl Default for WebSocketClientReconnectionConfig {
+    fn default() -> Self {
+        WebSocketClientReconnectionConfig {
+            enabled: true,
+            max_retries: None,
+            interval: IggyDuration::from_str("1s").unwrap(),
+            reestablish_after: IggyDuration::from_str("5s").unwrap(),
+        }
+    }
+}
+
+impl Display for WebSocketClientReconnectionConfig {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(
+            f,
+            "{{ enabled: {}, max_retries: {:?}, interval: {}, reestablish_after: {} }}",
+            self.enabled, self.max_retries, self.interval, self.reestablish_after
+        )
+    }
+}
diff --git a/core/common/src/types/configuration/websocket_config/websocket_connection_string_options.rs b/core/common/src/types/configuration/websocket_config/websocket_connection_string_options.rs
new file mode 100644
index 000000000..85c5fb205
--- /dev/null
+++ b/core/common/src/types/configuration/websocket_config/websocket_connection_string_options.rs
@@ -0,0 +1,209 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use crate::{ConnectionStringOptions, IggyDuration, IggyError, WebSocketClientReconnectionConfig};
+use std::str::FromStr;
+
+#[derive(Debug, Clone)]
+pub struct WebSocketConnectionStringOptions {
+    heartbeat_interval: IggyDuration,
+    reconnection: WebSocketClientReconnectionConfig,
+
+    read_buffer_size: Option<usize>,
+    write_buffer_size: Option<usize>,
+    max_write_buffer_size: Option<usize>,
+    max_message_size: Option<usize>,
+    max_frame_size: Option<usize>,
+
+    accept_unmasked_frames: Option<bool>,
+
+    tls_enabled: bool,
+    tls_domain: String,
+    tls_ca_file: Option<String>,
+    tls_validate_certificate: bool,
+}
+
+impl WebSocketConnectionStringOptions {
+    pub fn heartbeat_interval(&self) -> IggyDuration {
+        self.heartbeat_interval
+    }
+
+    pub fn reconnection(&self) -> &WebSocketClientReconnectionConfig {
+        &self.reconnection
+    }
+
+    pub fn read_buffer_size(&self) -> Option<usize> {
+        self.read_buffer_size
+    }
+
+    pub fn write_buffer_size(&self) -> Option<usize> {
+        self.write_buffer_size
+    }
+
+    pub fn max_write_buffer_size(&self) -> Option<usize> {
+        self.max_write_buffer_size
+    }
+
+    pub fn max_message_size(&self) -> Option<usize> {
+        self.max_message_size
+    }
+
+    pub fn max_frame_size(&self) -> Option<usize> {
+        self.max_frame_size
+    }
+
+    pub fn accept_unmasked_frames(&self) -> Option<bool> {
+        self.accept_unmasked_frames
+    }
+
+    pub fn tls_enabled(&self) -> bool {
+        self.tls_enabled
+    }
+
+    pub fn tls_domain(&self) -> &str {
+        &self.tls_domain
+    }
+
+    pub fn tls_ca_file(&self) -> Option<&str> {
+        self.tls_ca_file.as_deref()
+    }
+
+    pub fn tls_validate_certificate(&self) -> bool {
+        self.tls_validate_certificate
+    }
+}
+
+impl ConnectionStringOptions for WebSocketConnectionStringOptions {
+    fn retries(&self) -> Option<u32> {
+        self.reconnection.max_retries
+    }
+
+    fn heartbeat_interval(&self) -> IggyDuration {
+        self.heartbeat_interval
+    }
+
+    fn parse_options(options_str: &str) -> Result<Self, IggyError> {
+        let mut parsed_options = WebSocketConnectionStringOptions::default();
+
+        if options_str.is_empty() {
+            return Ok(parsed_options);
+        }
+
+        for option in options_str.split('&') {
+            let parts: Vec<&str> = option.split('=').collect();
+            if parts.len() != 2 {
+                return Err(IggyError::InvalidConnectionString);
+            }
+
+            match parts[0] {
+                "heartbeat_interval" => {
+                    parsed_options.heartbeat_interval = IggyDuration::from_str(parts[1])
+                        .map_err(|_| IggyError::InvalidConnectionString)?;
+                }
+                "reconnection_retries" => {
+                    let retries = match parts[1] {
+                        "unlimited" => None,
+                        val => Some(
+                            val.parse::<u32>()
+                                .map_err(|_| IggyError::InvalidConnectionString)?,
+                        ),
+                    };
+                    parsed_options.reconnection.max_retries = retries;
+                }
+                "reconnection_interval" => {
+                    parsed_options.reconnection.interval = IggyDuration::from_str(parts[1])
+                        .map_err(|_| IggyError::InvalidConnectionString)?;
+                }
+                "reestablish_after" => {
+                    parsed_options.reconnection.reestablish_after =
+                        IggyDuration::from_str(parts[1])
+                            .map_err(|_| IggyError::InvalidConnectionString)?;
+                }
+
+                "read_buffer_size" => {
+                    let size = parts[1]
+                        .parse::<usize>()
+                        .map_err(|_| IggyError::InvalidConnectionString)?;
+                    parsed_options.read_buffer_size = Some(size);
+                }
+                "write_buffer_size" => {
+                    let size = parts[1]
+                        .parse::<usize>()
+                        .map_err(|_| IggyError::InvalidConnectionString)?;
+                    parsed_options.write_buffer_size = Some(size);
+                }
+                "max_write_buffer_size" => {
+                    let size = parts[1]
+                        .parse::<usize>()
+                        .map_err(|_| IggyError::InvalidConnectionString)?;
+                    parsed_options.max_write_buffer_size = Some(size);
+                }
+                "max_message_size" => {
+                    let size = parts[1]
+                        .parse::<usize>()
+                        .map_err(|_| IggyError::InvalidConnectionString)?;
+                    parsed_options.max_message_size = Some(size);
+                }
+                "max_frame_size" => {
+                    let size = parts[1]
+                        .parse::<usize>()
+                        .map_err(|_| IggyError::InvalidConnectionString)?;
+                    parsed_options.max_frame_size = Some(size);
+                }
+
+                "accept_unmasked_frames" => {
+                    parsed_options.accept_unmasked_frames = Some(parts[1] == "true");
+                }
+
+                "tls" => {
+                    parsed_options.tls_enabled = parts[1] == "true";
+                }
+                "tls_domain" => {
+                    parsed_options.tls_domain = parts[1].to_string();
+                }
+                "tls_ca_file" => {
+                    parsed_options.tls_ca_file = Some(parts[1].to_string());
+                }
+                "tls_validate_certificate" => {
+                    parsed_options.tls_validate_certificate = parts[1] == "true";
+                }
+
+                _ => return Err(IggyError::InvalidConnectionString),
+            }
+        }
+        Ok(parsed_options)
+    }
+}
+
+impl Default for WebSocketConnectionStringOptions {
+    fn default() -> Self {
+        WebSocketConnectionStringOptions {
+            heartbeat_interval: IggyDuration::from_str("5s").unwrap(),
+            reconnection: WebSocketClientReconnectionConfig::default(),
+            read_buffer_size: None,
+            write_buffer_size: None,
+            max_write_buffer_size: None,
+            max_message_size: None,
+            max_frame_size: None,
+            accept_unmasked_frames: None,
+            tls_enabled: false,
+            tls_domain: "".to_string(),
+            tls_ca_file: None,
+            tls_validate_certificate: false,
+        }
+    }
+}
diff --git a/core/common/src/types/confirmation/mod.rs b/core/common/src/types/confirmation/mod.rs
deleted file mode 100644
index 5289a6274..000000000
--- a/core/common/src/types/confirmation/mod.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-use serde::{Deserialize, Serialize};
-use strum::{Display, EnumString};
-
-#[derive(Clone, Copy, Debug, Default, Display, Serialize, Deserialize, EnumString, PartialEq)]
-#[strum(serialize_all = "snake_case")]
-pub enum Confirmation {
-    #[default]
-    Wait,
-    NoWait,
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use std::str::FromStr;
-
-    #[test]
-    fn test_to_string() {
-        assert_eq!(Confirmation::Wait.to_string(), "wait");
-        assert_eq!(Confirmation::NoWait.to_string(), "no_wait");
-    }
-
-    #[test]
-    fn test_from_str() {
-        assert_eq!(Confirmation::from_str("wait").unwrap(), Confirmation::Wait);
-        assert_eq!(
-            Confirmation::from_str("no_wait").unwrap(),
-            Confirmation::NoWait
-        );
-    }
-
-    #[test]
-    fn test_default() {
-        assert_eq!(Confirmation::default(), Confirmation::Wait);
-    }
-}
diff --git a/core/common/src/types/identifier/mod.rs b/core/common/src/types/identifier/mod.rs
index a12ab4d8a..bd4a5ccc0 100644
--- a/core/common/src/types/identifier/mod.rs
+++ b/core/common/src/types/identifier/mod.rs
@@ -64,7 +64,7 @@ impl Default for Identifier {
         Self {
             kind: IdKind::default(),
             length: 4,
-            value: 1u32.to_le_bytes().to_vec(),
+            value: 0u32.to_le_bytes().to_vec(),
         }
     }
 }
@@ -157,9 +157,11 @@ impl Identifier {
 
     /// Creates a new identifier from the given numeric value.
     pub fn numeric(value: u32) -> Result<Self, IggyError> {
+        /*
         if value == 0 {
             return Err(IggyError::InvalidIdentifier);
         }
+        */
 
         Ok(Self {
             kind: IdKind::Numeric,
@@ -365,11 +367,6 @@ mod tests {
         assert!(Identifier::numeric(1).is_ok());
     }
 
-    #[test]
-    fn identifier_with_a_value_of_zero_should_be_invalid() {
-        assert!(Identifier::numeric(0).is_err());
-    }
-
     #[test]
     fn identifier_with_a_value_of_non_empty_string_should_be_valid() {
         assert!(Identifier::named("test").is_ok());
diff --git a/core/common/src/types/mod.rs b/core/common/src/types/mod.rs
index dd8a2dea9..96b41233d 100644
--- a/core/common/src/types/mod.rs
+++ b/core/common/src/types/mod.rs
@@ -22,7 +22,6 @@ pub(crate) mod cluster;
 pub(crate) mod command;
 pub(crate) mod compression;
 pub(crate) mod configuration;
-pub(crate) mod confirmation;
 pub(crate) mod consumer;
 pub(crate) mod diagnostic;
 pub(crate) mod identifier;
diff --git a/core/common/src/types/permissions/permissions_global.rs b/core/common/src/types/permissions/permissions_global.rs
index e2038009e..8291d48f9 100644
--- a/core/common/src/types/permissions/permissions_global.rs
+++ b/core/common/src/types/permissions/permissions_global.rs
@@ -35,7 +35,7 @@ pub struct Permissions {
     pub global: GlobalPermissions,
 
     /// Stream permissions are applied to a specific stream.
-    pub streams: Option<AHashMap<u32, StreamPermissions>>,
+    pub streams: Option<AHashMap<usize, StreamPermissions>>,
 }
 
 /// `GlobalPermissions` are applied to all streams without a need to specify them one by one in the `streams` field.
@@ -142,7 +142,7 @@ pub struct StreamPermissions {
     pub send_messages: bool,
 
     /// The `topics` field allows to define the granular permissions for each topic of a stream.
-    pub topics: Option<AHashMap<u32, TopicPermissions>>,
+    pub topics: Option<AHashMap<usize, TopicPermissions>>,
 }
 
 /// `TopicPermissions` are applied to a specific topic of a stream. This is the lowest level of permissions.
@@ -237,7 +237,7 @@ impl BytesSerializable for Permissions {
             let streams_count = streams.len();
             let mut current_stream = 1;
             for (stream_id, stream) in streams {
-                bytes.put_u32_le(*stream_id);
+                bytes.put_u32_le(*stream_id as u32);
                 bytes.put_u8(if stream.manage_stream { 1 } else { 0 });
                 bytes.put_u8(if stream.read_stream { 1 } else { 0 });
                 bytes.put_u8(if stream.manage_topics { 1 } else { 0 });
@@ -249,7 +249,7 @@ impl BytesSerializable for Permissions {
                     let topics_count = topics.len();
                     let mut current_topic = 1;
                     for (topic_id, topic) in topics {
-                        bytes.put_u32_le(*topic_id);
+                        bytes.put_u32_le(*topic_id as u32);
                         bytes.put_u8(if topic.manage_topic { 1 } else { 0 });
                         bytes.put_u8(if topic.read_topic { 1 } else { 0 });
                         bytes.put_u8(if topic.poll_messages { 1 } else { 0 });
@@ -296,7 +296,7 @@ impl BytesSerializable for Permissions {
         if bytes.get_u8() == 1 {
             let mut streams_map = AHashMap::new();
             loop {
-                let stream_id = bytes.get_u32_le();
+                let stream_id = bytes.get_u32_le() as usize;
                 let manage_stream = bytes.get_u8() == 1;
                 let read_stream = bytes.get_u8() == 1;
                 let manage_topics = bytes.get_u8() == 1;
@@ -307,7 +307,7 @@ impl BytesSerializable for Permissions {
                 if bytes.get_u8() == 1 {
                     let mut topics_map = AHashMap::new();
                     loop {
-                        let topic_id = bytes.get_u32_le();
+                        let topic_id = bytes.get_u32_le() as usize;
                         let manage_topic = bytes.get_u8() == 1;
                         let read_topic = bytes.get_u8() == 1;
                         let poll_messages = bytes.get_u8() == 1;
diff --git a/core/common/src/utils/crypto.rs b/core/common/src/utils/crypto.rs
index 34cd72c7b..fca62dde1 100644
--- a/core/common/src/utils/crypto.rs
+++ b/core/common/src/utils/crypto.rs
@@ -18,12 +18,11 @@
 
 use crate::IggyError;
 use crate::text;
-use aes_gcm::aead::generic_array::GenericArray;
 use aes_gcm::aead::{Aead, OsRng};
 use aes_gcm::{AeadCore, Aes256Gcm, KeyInit};
 use std::fmt::Debug;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum EncryptorKind {
     Aes256Gcm(Aes256GcmEncryptor),
 }
@@ -46,6 +45,7 @@ pub trait Encryptor {
     fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, IggyError>;
 }
 
+#[derive(Clone)]
 pub struct Aes256GcmEncryptor {
     cipher: Aes256Gcm,
 }
@@ -62,7 +62,7 @@ impl Aes256GcmEncryptor {
             return Err(IggyError::InvalidEncryptionKey);
         }
         Ok(Self {
-            cipher: Aes256Gcm::new(GenericArray::from_slice(key)),
+            cipher: Aes256Gcm::new(key.into()),
         })
     }
 
@@ -83,7 +83,7 @@ impl Encryptor for Aes256GcmEncryptor {
     }
 
     fn decrypt(&self, data: &[u8]) -> Result<Vec<u8>, IggyError> {
-        let nonce = GenericArray::from_slice(&data[0..12]);
+        let nonce = (&data[0..12]).into();
         let payload = self.cipher.decrypt(nonce, &data[12..]);
         if payload.is_err() {
             return Err(IggyError::CannotDecryptData);
diff --git a/core/configs/server.toml b/core/configs/server.toml
index 5b9d0ad4e..ea1be6c66 100644
--- a/core/configs/server.toml
+++ b/core/configs/server.toml
@@ -53,7 +53,7 @@ archiver_enabled = false
 cleaner_enabled = false
 
 # Interval for running the message archiver and cleaner.
-interval = "1 m"
+interval = "35s"
 
 [data_maintenance.state]
 # Enables or disables the archiver process for state log.
@@ -271,6 +271,20 @@ cert_file = "core/certs/iggy_cert.pem"
 # Path to the QUIC TLS key file.
 key_file = "core/certs/iggy_key.pem"
 
+# Configuration for the QUIC socket
+[quic.socket]
+# Whether to override the OS-default socket parameters
+override_defaults = false
+
+# SO_RCVBUF: maximum size of the receive buffer, can be clamped by the OS
+recv_buffer_size = "64 KB"
+
+# SO_SNDBUF: maximum size of the send buffer, can be clamped by the OS
+send_buffer_size = "64 KB"
+
+# SO_KEEPALIVE: whether to regularly send a keepalive packet maintaining the connection
+keepalive = false
+
 # Message cleaner configuration.
 [message_cleaner]
 # Enables or disables the background process for deleting expired messages.
@@ -577,3 +591,23 @@ address = "127.0.0.1:8090"
 id = 2
 name = "iggy-node-2"
 address = "127.0.0.1:8091"
+
+# Sharding configuration
+[system.sharding]
+# CPU allocation - controls the number of shards and their CPU affinity.
+# Possible values:
+# - "all": Use all available CPU cores (default)
+# - numeric value (e.g. 4): Use 4 shards (4 threads pinned to cores 0, 1, 2, 3)
+# - range (e.g. "5..8"): Use 3 shards with affinity to cores 5, 6, 7
+cpu_allocation = "all"
+
+[websocket]
+
+enabled = true
+address = "0.0.0.0:8092"
+
+[websocket.tls]
+enabled = false
+self_signed = true
+cert_file = "core/certs/iggy_cert.pem"
+key_file = "core/certs/iggy_key.pem"
diff --git a/core/integration/Cargo.toml b/core/integration/Cargo.toml
index f3e681c84..bb3d00e7f 100644
--- a/core/integration/Cargo.toml
+++ b/core/integration/Cargo.toml
@@ -32,6 +32,7 @@ assert_cmd = "2.0.17"
 async-trait = { workspace = true }
 bytes = { workspace = true }
 chrono = { workspace = true }
+compio = { workspace = true }
 ctor = "0.6.0"
 derive_more = { workspace = true }
 env_logger = { workspace = true }
diff --git a/core/integration/src/lib.rs b/core/integration/src/lib.rs
index 996961e6d..269698b2b 100644
--- a/core/integration/src/lib.rs
+++ b/core/integration/src/lib.rs
@@ -24,6 +24,9 @@ pub mod http_client;
 pub mod quic_client;
 #[allow(deprecated)]
 pub mod tcp_client;
+#[allow(deprecated)]
+pub mod websocket_client;
+
 #[allow(deprecated)]
 pub mod test_connectors_runtime;
 #[allow(deprecated)]
diff --git a/core/integration/src/quic_client.rs b/core/integration/src/quic_client.rs
index 667a8965c..c9fa64990 100644
--- a/core/integration/src/quic_client.rs
+++ b/core/integration/src/quic_client.rs
@@ -33,6 +33,8 @@ impl ClientFactory for QuicClientFactory {
     async fn create_client(&self) -> ClientWrapper {
         let config = QuicClientConfig {
             server_address: self.server_addr.clone(),
+            // TODO: Need to increase this in order to not timeout during tests
+            max_idle_timeout: 2_000_000,
             ..QuicClientConfig::default()
         };
         let client = QuicClient::create(Arc::new(config)).unwrap();
diff --git a/core/integration/src/test_mcp_server.rs b/core/integration/src/test_mcp_server.rs
index 5fcddbf8f..9d729eaa8 100644
--- a/core/integration/src/test_mcp_server.rs
+++ b/core/integration/src/test_mcp_server.rs
@@ -24,17 +24,18 @@ use rmcp::{
     service::RunningService,
     transport::StreamableHttpClientTransport,
 };
-use std::fs::OpenOptions;
-use std::io::Write;
+use std::fs::{self, File};
 use std::net::{Ipv4Addr, SocketAddr};
 use std::path::PathBuf;
-use std::process::{Child, Command};
+use std::process::{Child, Command, Stdio};
+use std::thread::panicking;
 use std::time::Duration;
 use std::{collections::HashMap, net::TcpListener};
 use tokio::time::sleep;
 
 pub const CONSUMER_NAME: &str = "mcp";
 const MCP_PATH: &str = "/mcp";
+const TEST_VERBOSITY_ENV_VAR: &str = "IGGY_TEST_VERBOSE";
 
 pub type McpClient = RunningService<RoleClient, InitializeRequestParam>;
 
@@ -101,7 +102,23 @@ impl TestMcpServer {
             Command::cargo_bin("iggy-mcp").unwrap()
         };
         command.envs(self.envs.clone());
-        let child = command.spawn().expect("Failed to start MCP server process");
+
+        // By default, MCP server logs are redirected to files,
+        // and dumped to stderr when test fails. With IGGY_TEST_VERBOSE=1
+        // logs are dumped to stdout during test execution.
+        if std::env::var(TEST_VERBOSITY_ENV_VAR).is_ok()
+            || self.envs.contains_key(TEST_VERBOSITY_ENV_VAR)
+        {
+            command.stdout(Stdio::inherit());
+            command.stderr(Stdio::inherit());
+        } else {
+            command.stdout(self.get_stdout_file());
+            self.stdout_file_path = Some(fs::canonicalize(self.get_stdout_file_path()).unwrap());
+            command.stderr(self.get_stderr_file());
+            self.stderr_file_path = Some(fs::canonicalize(self.get_stderr_file_path()).unwrap());
+        }
+
+        let child = command.spawn().unwrap();
         self.child_handle = Some(child);
     }
 
@@ -118,29 +135,7 @@ impl TestMcpServer {
             #[cfg(not(unix))]
             child_handle.kill().unwrap();
 
-            if let Ok(output) = child_handle.wait_with_output() {
-                let stderr = String::from_utf8_lossy(&output.stderr);
-                let stdout = String::from_utf8_lossy(&output.stdout);
-                if let Some(stderr_file_path) = &self.stderr_file_path {
-                    OpenOptions::new()
-                        .append(true)
-                        .create(true)
-                        .open(stderr_file_path)
-                        .unwrap()
-                        .write_all(stderr.as_bytes())
-                        .unwrap();
-                }
-
-                if let Some(stdout_file_path) = &self.stdout_file_path {
-                    OpenOptions::new()
-                        .append(true)
-                        .create(true)
-                        .open(stdout_file_path)
-                        .unwrap()
-                        .write_all(stdout.as_bytes())
-                        .unwrap();
-                }
-            }
+            let _ = child_handle.wait();
         }
     }
 
@@ -224,10 +219,55 @@ impl TestMcpServer {
 
         panic!("Failed to find a free port after {max_retries} retries");
     }
+
+    fn get_stdout_file_path(&self) -> String {
+        format!("/tmp/iggy-mcp-{}.stdout", self.server_address.port())
+    }
+
+    fn get_stderr_file_path(&self) -> String {
+        format!("/tmp/iggy-mcp-{}.stderr", self.server_address.port())
+    }
+
+    fn get_stdout_file(&self) -> Stdio {
+        Stdio::from(File::create(self.get_stdout_file_path()).unwrap())
+    }
+
+    fn get_stderr_file(&self) -> Stdio {
+        Stdio::from(File::create(self.get_stderr_file_path()).unwrap())
+    }
+
+    fn read_file_to_string(path: &str) -> String {
+        fs::read_to_string(path).unwrap_or_else(|_| format!("Failed to read file: {}", path))
+    }
 }
 
 impl Drop for TestMcpServer {
     fn drop(&mut self) {
         self.stop();
+
+        if panicking() {
+            if let Some(stdout_file_path) = &self.stdout_file_path {
+                eprintln!(
+                    "Iggy MCP server stdout:\n{}",
+                    Self::read_file_to_string(stdout_file_path.to_str().unwrap())
+                );
+            }
+
+            if let Some(stderr_file_path) = &self.stderr_file_path {
+                eprintln!(
+                    "Iggy MCP server stderr:\n{}",
+                    Self::read_file_to_string(stderr_file_path.to_str().unwrap())
+                );
+            }
+        }
+
+        // Clean up log files
+        if let Some(stdout_file_path) = &self.stdout_file_path {
+            fs::remove_file(stdout_file_path).unwrap();
+        }
+
+        if let Some(stderr_file_path) = &self.stderr_file_path {
+            fs::remove_file(stderr_file_path).unwrap();
+        }
     }
 }
diff --git a/core/integration/src/test_server.rs b/core/integration/src/test_server.rs
index 372a44139..160094d26 100644
--- a/core/integration/src/test_server.rs
+++ b/core/integration/src/test_server.rs
@@ -16,6 +16,15 @@
  * under the License.
  */
 
+use assert_cmd::prelude::CommandCargoExt;
+use async_trait::async_trait;
+use derive_more::Display;
+use futures::executor::block_on;
+use iggy::prelude::UserStatus::Active;
+use iggy::prelude::*;
+use iggy_common::TransportProtocol;
+use rand::Rng;
+use server::configs::config_provider::{ConfigProvider, FileConfigProvider};
 use std::collections::HashMap;
 use std::fs;
 use std::fs::{File, OpenOptions};
@@ -23,17 +32,8 @@ use std::io::Write;
 use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
 use std::path::{Path, PathBuf};
 use std::process::{Child, Command, Stdio};
-use std::thread::{panicking, sleep};
+use std::thread::{available_parallelism, panicking, sleep};
 use std::time::Duration;
-
-use assert_cmd::prelude::CommandCargoExt;
-use async_trait::async_trait;
-use derive_more::Display;
-use futures::executor::block_on;
-use iggy::prelude::UserStatus::Active;
-use iggy::prelude::*;
-use iggy_common::{ConfigProvider, TransportProtocol};
-use server::configs::server::ServerConfig;
 use uuid::Uuid;
 
 pub const SYSTEM_PATH_ENV_VAR: &str = "IGGY_SYSTEM_PATH";
@@ -59,7 +59,6 @@ pub trait ClientFactory: Sync + Send {
     fn transport(&self) -> TransportProtocol;
     fn server_addr(&self) -> String;
 }
-
 #[derive(Display, Debug)]
 enum ServerProtocolAddr {
     #[display("RAW_TCP:{_0}")]
@@ -70,6 +69,9 @@ enum ServerProtocolAddr {
 
     #[display("QUIC_UDP:{_0}")]
     QuicUdp(SocketAddr),
+
+    #[display("WEBSOCKET:{_0}")]
+    WebSocket(SocketAddr),
 }
 
 #[derive(Debug)]
@@ -98,6 +100,28 @@ impl TestServer {
             }
         }
 
+        // Randomly select 4 CPU cores to reduce interference between parallel tests
+        let cpu_allocation = match available_parallelism() {
+            Ok(parallelism) => {
+                let available_cpus = parallelism.get();
+                if available_cpus >= 4 {
+                    let mut rng = rand::thread_rng();
+                    let max_start = available_cpus - 4;
+                    let start = rng.gen_range(0..=max_start);
+                    let end = start + 4;
+                    format!("{}..{}", start, end)
+                } else {
+                    "all".to_string()
+                }
+            }
+            Err(_) => "0..4".to_string(),
+        };
+
+        envs.insert(
+            "IGGY_SYSTEM_SHARDING_CPU_ALLOCATION".to_string(),
+            cpu_allocation,
+        );
+
         if ip_kind == IpAddrKind::V6 {
             envs.insert(IPV6_ENV_VAR.to_string(), "true".to_string());
         }
@@ -277,6 +301,7 @@ impl TestServer {
             ServerProtocolAddr::QuicUdp(addr),
             ServerProtocolAddr::RawTcp(addr),
             ServerProtocolAddr::HttpTcp(addr),
+            ServerProtocolAddr::WebSocket(addr),
         ]
     }
 
@@ -286,6 +311,7 @@ impl TestServer {
             ServerProtocolAddr::QuicUdp(addr),
             ServerProtocolAddr::RawTcp(addr),
             ServerProtocolAddr::HttpTcp(addr),
+            ServerProtocolAddr::WebSocket(addr),
         ]
     }
 
@@ -301,6 +327,9 @@ impl TestServer {
                 ServerProtocolAddr::QuicUdp(addr) => {
                     ("IGGY_QUIC_ADDRESS".to_string(), addr.to_string())
                 }
+                ServerProtocolAddr::WebSocket(addr) => {
+                    ("IGGY_WEBSOCKET_ADDRESS".to_string(), addr.to_string())
+                }
             };
 
             self.envs.entry(key.0).or_insert(key.1);
@@ -309,11 +338,13 @@ impl TestServer {
 
     fn wait_until_server_has_bound(&mut self) {
         let config_path = format!("{}/runtime/current_config.toml", self.local_data_path);
+        let file_config_provider = FileConfigProvider::new(config_path.clone());
+
         let max_attempts = (MAX_PORT_WAIT_DURATION_S * 1000) / SLEEP_INTERVAL_MS;
         self.server_addrs.clear();
 
         let config = block_on(async {
-            let mut loaded_config: Option<ServerConfig> = None;
+            let mut loaded_config = None;
 
             for _ in 0..max_attempts {
                 if !Path::new(&config_path).exists() {
@@ -325,11 +356,48 @@ impl TestServer {
                     sleep(Duration::from_millis(SLEEP_INTERVAL_MS));
                     continue;
                 }
-                match ServerConfig::file_config_provider(config_path.clone())
-                    .load_config()
-                    .await
-                {
+                match file_config_provider.load_config().await {
                     Ok(config) => {
+                        // Verify config contains fresh addresses, not stale defaults
+                        // Default ports: TCP=8090, HTTP=3000, QUIC=8080, WebSocket=8092
+                        let tcp_port: u16 = config
+                            .tcp
+                            .address
+                            .split(':')
+                            .nth(1)
+                            .and_then(|s| s.parse().ok())
+                            .unwrap_or(0);
+                        let http_port: u16 = config
+                            .http
+                            .address
+                            .split(':')
+                            .nth(1)
+                            .and_then(|s| s.parse().ok())
+                            .unwrap_or(0);
+                        let quic_port: u16 = config
+                            .quic
+                            .address
+                            .split(':')
+                            .nth(1)
+                            .and_then(|s| s.parse().ok())
+                            .unwrap_or(0);
+                        let websocket_port: u16 = config
+                            .websocket
+                            .address
+                            .split(':')
+                            .nth(1)
+                            .and_then(|s| s.parse().ok())
+                            .unwrap_or(0);
+
+                        if tcp_port == 8090
+                            || http_port == 3000
+                            || quic_port == 8080
+                            || websocket_port == 8092
+                        {
+                            sleep(Duration::from_millis(SLEEP_INTERVAL_MS));
+                            continue;
+                        }
+
                         loaded_config = Some(config);
                         break;
                     }
@@ -340,17 +408,41 @@ impl TestServer {
         });
 
         if let Some(config) = config {
-            self.server_addrs.push(ServerProtocolAddr::QuicUdp(
-                config.quic.address.parse().unwrap(),
-            ));
+            // Only validate and add enabled protocols
+            if config.quic.enabled {
+                let quic_addr: SocketAddr = config.quic.address.parse().unwrap();
+                if quic_addr.port() == 0 {
+                    panic!("Quic address port is 0!");
+                }
+                self.server_addrs
+                    .push(ServerProtocolAddr::QuicUdp(quic_addr));
+            }
 
-            self.server_addrs.push(ServerProtocolAddr::RawTcp(
-                config.tcp.address.parse().unwrap(),
-            ));
+            if config.tcp.enabled {
+                let tcp_addr: SocketAddr = config.tcp.address.parse().unwrap();
+                if tcp_addr.port() == 0 {
+                    panic!("Tcp address port is 0!");
+                }
+                self.server_addrs.push(ServerProtocolAddr::RawTcp(tcp_addr));
+            }
+
+            if config.http.enabled {
+                let http_addr: SocketAddr = config.http.address.parse().unwrap();
+                if http_addr.port() == 0 {
+                    panic!("Http address port is 0!");
+                }
+                self.server_addrs
+                    .push(ServerProtocolAddr::HttpTcp(http_addr));
+            }
 
-            self.server_addrs.push(ServerProtocolAddr::HttpTcp(
-                config.http.address.parse().unwrap(),
-            ));
+            if config.websocket.enabled {
+                let websocket_addr: SocketAddr = config.websocket.address.parse().unwrap();
+                if websocket_addr.port() == 0 {
+                    panic!("WebSocket address port is 0!");
+                }
+                self.server_addrs
+                    .push(ServerProtocolAddr::WebSocket(websocket_addr));
+            }
         } else {
             panic!(
                 "Failed to load config from file {config_path} in {MAX_PORT_WAIT_DURATION_S} s!"
@@ -429,6 +521,15 @@ impl TestServer {
             None
         }
     }
+
+    pub fn get_websocket_addr(&self) -> Option<String> {
+        for server_protocol_addr in &self.server_addrs {
+            if let ServerProtocolAddr::WebSocket(a) = server_protocol_addr {
+                return Some(a.to_string());
+            }
+        }
+        None
+    }
 }
 
 impl Drop for TestServer {
diff --git a/core/integration/src/quic_client.rs b/core/integration/src/websocket_client.rs
similarity index 70%
copy from core/integration/src/quic_client.rs
copy to core/integration/src/websocket_client.rs
index 667a8965c..cc0cc0826 100644
--- a/core/integration/src/quic_client.rs
+++ b/core/integration/src/websocket_client.rs
@@ -18,30 +18,30 @@
 
 use crate::test_server::ClientFactory;
 use async_trait::async_trait;
-use iggy::prelude::{Client, ClientWrapper, QuicClientConfig};
-use iggy::quic::quic_client::QuicClient;
+use iggy::prelude::{Client, ClientWrapper, WebSocketClientConfig};
+use iggy::websocket::websocket_client::WebSocketClient;
 use iggy_common::TransportProtocol;
 use std::sync::Arc;
 
 #[derive(Debug, Clone)]
-pub struct QuicClientFactory {
+pub struct WebSocketClientFactory {
     pub server_addr: String,
 }
 
 #[async_trait]
-impl ClientFactory for QuicClientFactory {
+impl ClientFactory for WebSocketClientFactory {
     async fn create_client(&self) -> ClientWrapper {
-        let config = QuicClientConfig {
+        let config = WebSocketClientConfig {
             server_address: self.server_addr.clone(),
-            ..QuicClientConfig::default()
+            ..WebSocketClientConfig::default()
         };
-        let client = QuicClient::create(Arc::new(config)).unwrap();
+        let client = WebSocketClient::create(Arc::new(config)).unwrap();
         Client::connect(&client).await.unwrap();
-        ClientWrapper::Quic(client)
+        ClientWrapper::WebSocket(client)
     }
 
     fn transport(&self) -> TransportProtocol {
-        TransportProtocol::Quic
+        TransportProtocol::WebSocket
     }
 
     fn server_addr(&self) -> String {
@@ -49,5 +49,5 @@ impl ClientFactory for QuicClientFactory {
     }
 }
 
-unsafe impl Send for QuicClientFactory {}
-unsafe impl Sync for QuicClientFactory {}
+unsafe impl Send for WebSocketClientFactory {}
+unsafe impl Sync for WebSocketClientFactory {}
diff --git a/core/integration/tests/archiver/disk.rs b/core/integration/tests/archiver/disk.rs
deleted file mode 100644
index f676d23f7..000000000
--- a/core/integration/tests/archiver/disk.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-use crate::archiver::DiskArchiverSetup;
-use server::streaming::utils::file;
-use server::{archiver::Archiver, server_error::ArchiverError};
-use std::path::Path;
-use tokio::io::{AsyncReadExt, AsyncWriteExt};
-
-#[tokio::test]
-async fn should_init_base_archiver_directory() {
-    let setup = DiskArchiverSetup::init().await;
-    let archiver = setup.archiver();
-    let result = archiver.init().await;
-    assert!(result.is_ok());
-    let path = Path::new(&setup.archive_path);
-    assert!(path.exists());
-}
-
-#[tokio::test]
-async fn should_archive_file_on_disk_by_making_a_copy_of_original_file() {
-    let setup = DiskArchiverSetup::init().await;
-    let archiver = setup.archiver();
-    let content = "hello world";
-    let file_to_archive_path = format!("{}/file_to_archive", setup.base_path);
-    create_file(&file_to_archive_path, content).await;
-    let files_to_archive = vec![file_to_archive_path.as_ref()];
-
-    let result = archiver.archive(&files_to_archive, None).await;
-    assert!(result.is_ok());
-    let archived_file_path = format!("{}/{}", setup.archive_path, file_to_archive_path);
-    assert_archived_file(&file_to_archive_path, &archived_file_path, content).await;
-}
-
-#[tokio::test]
-async fn should_archive_file_on_disk_within_additional_base_directory() {
-    let setup = DiskArchiverSetup::init().await;
-    let archiver = setup.archiver();
-    let base_directory = "base";
-    let content = "hello world";
-    let file_to_archive_path = format!("{}/file_to_archive", setup.base_path);
-    create_file(&file_to_archive_path, content).await;
-    let files_to_archive = vec![file_to_archive_path.as_ref()];
-
-    let result = archiver
-        .archive(&files_to_archive, Some(base_directory.to_string()))
-        .await;
-    assert!(result.is_ok());
-    let archived_file_path = format!(
-        "{}/{base_directory}/{}",
-        setup.archive_path, file_to_archive_path
-    );
-    assert_archived_file(&file_to_archive_path, &archived_file_path, content).await;
-}
-
-#[tokio::test]
-async fn should_return_true_when_file_is_archived() {
-    let setup = DiskArchiverSetup::init().await;
-    let archiver = setup.archiver();
-    let content = "hello world";
-    let file_to_archive_path = format!("{}/file_to_archive", setup.base_path);
-    create_file(&file_to_archive_path, content).await;
-    let files_to_archive = vec![file_to_archive_path.as_ref()];
-    archiver.archive(&files_to_archive, None).await.unwrap();
-
-    let is_archived = archiver.is_archived(&file_to_archive_path, None).await;
-    assert!(is_archived.is_ok());
-    assert!(is_archived.unwrap());
-}
-
-#[tokio::test]
-async fn should_return_false_when_file_is_not_archived() {
-    let setup = DiskArchiverSetup::init().await;
-    let archiver = setup.archiver();
-    let content = "hello world";
-    let file_to_archive_path = format!("{}/file_to_archive", setup.base_path);
-    create_file(&file_to_archive_path, content).await;
-
-    let is_archived = archiver.is_archived(&file_to_archive_path, None).await;
-    assert!(is_archived.is_ok());
-    assert!(!is_archived.unwrap());
-}
-
-#[tokio::test]
-async fn should_fail_when_file_to_archive_does_not_exist() {
-    let setup = DiskArchiverSetup::init().await;
-    let archiver = setup.archiver();
-    let file_to_archive_path = "invalid_file_to_archive";
-    let files_to_archive = vec![file_to_archive_path];
-    let result = archiver.archive(&files_to_archive, None).await;
-
-    assert!(result.is_err());
-    let error = result.err().unwrap();
-    assert!(matches!(error, ArchiverError::FileToArchiveNotFound { .. }));
-}
-
-async fn create_file(path: &str, content: &str) {
-    let mut file = file::overwrite(path).await.unwrap();
-    file.write_all(content.as_bytes()).await.unwrap();
-}
-
-async fn assert_archived_file(file_to_archive_path: &str, archived_file_path: &str, content: &str) {
-    assert!(Path::new(&file_to_archive_path).exists());
-    assert!(Path::new(&archived_file_path).exists());
-    let archived_file = file::open(archived_file_path).await;
-    assert!(archived_file.is_ok());
-    let mut archived_file = archived_file.unwrap();
-    let mut archived_file_content = String::new();
-    archived_file
-        .read_to_string(&mut archived_file_content)
-        .await
-        .unwrap();
-    assert_eq!(content, archived_file_content);
-}
diff --git a/core/integration/tests/archiver/mod.rs b/core/integration/tests/archiver/mod.rs
deleted file mode 100644
index 2e3ed94cc..000000000
--- a/core/integration/tests/archiver/mod.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-use server::archiver::disk::DiskArchiver;
-use server::configs::server::DiskArchiverConfig;
-use tokio::fs::create_dir;
-use uuid::Uuid;
-
-mod disk;
-mod s3;
-
-pub struct DiskArchiverSetup {
-    base_path: String,
-    archive_path: String,
-    archiver: DiskArchiver,
-}
-
-impl DiskArchiverSetup {
-    pub async fn init() -> DiskArchiverSetup {
-        let base_path = format!("test_local_data_{}", Uuid::now_v7().to_u128_le());
-        let archive_path = format!("{base_path}/archive");
-        let config = DiskArchiverConfig {
-            path: archive_path.clone(),
-        };
-        let archiver = DiskArchiver::new(config);
-        create_dir(&base_path).await.unwrap();
-
-        Self {
-            base_path,
-            archive_path,
-            archiver,
-        }
-    }
-
-    pub fn archiver(&self) -> &DiskArchiver {
-        &self.archiver
-    }
-}
-
-impl Drop for DiskArchiverSetup {
-    fn drop(&mut self) {
-        std::fs::remove_dir_all(&self.base_path).unwrap();
-    }
-}
diff --git a/core/integration/tests/archiver/s3.rs b/core/integration/tests/archiver/s3.rs
deleted file mode 100644
index df2970f1f..000000000
--- a/core/integration/tests/archiver/s3.rs
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-use server::archiver::Archiver;
-use server::archiver::s3::S3Archiver;
-use server::configs::server::S3ArchiverConfig;
-
-#[tokio::test]
-async fn should_not_be_initialized_given_invalid_configuration() {
-    let config = S3ArchiverConfig {
-        key_id: "test".to_owned(),
-        key_secret: "secret".to_owned(),
-        bucket: "iggy".to_owned(),
-        endpoint: Some("https://iggy.s3.com".to_owned()),
-        region: None,
-        tmp_upload_dir: "tmp".to_owned(),
-    };
-    let archiver = S3Archiver::new(config);
-    assert!(archiver.is_ok());
-    let archiver = archiver.unwrap();
-    let init = archiver.init().await;
-    assert!(init.is_err());
-}
diff --git a/core/integration/tests/cli/client/test_client_get_command.rs b/core/integration/tests/cli/client/test_client_get_command.rs
index d76c09e47..734341758 100644
--- a/core/integration/tests/cli/client/test_client_get_command.rs
+++ b/core/integration/tests/cli/client/test_client_get_command.rs
@@ -67,7 +67,7 @@ impl IggyCmdTestCase for TestClientGetCmd {
                 "Client ID             | {}",
                 self.get_client_id()
             )))
-            .stdout(contains("User ID               | 1"));
+            .stdout(contains("User ID               | 0"));
     }
 
     async fn verify_server_state(&self, _client: &dyn Client) {}
diff --git a/core/integration/tests/cli/common/mod.rs b/core/integration/tests/cli/common/mod.rs
index 402ba5803..bebf46f21 100644
--- a/core/integration/tests/cli/common/mod.rs
+++ b/core/integration/tests/cli/common/mod.rs
@@ -128,7 +128,7 @@ impl IggyCmdTest {
             .await
             .unwrap();
 
-        assert_eq!(identity_info.user_id, 1);
+        assert_eq!(identity_info.user_id, 0);
     }
 
     pub(crate) async fn execute_test(&mut self, mut test_case: impl IggyCmdTestCase) {
@@ -145,6 +145,8 @@ impl IggyCmdTest {
         let command_args = test_case.get_command();
         // Set environment variables for the command
         command.envs(command_args.get_env());
+        // Set a fixed terminal width for consistent help output formatting
+        command.env("COLUMNS", "500");
         // Set server address for the command - it's randomized for each test
         command.args(test_case.protocol(&self.server));
         // Set COLUMNS environment variable to 200 to avoid truncation of output
diff --git a/core/integration/tests/cli/consumer_group/test_consumer_group_create_command.rs b/core/integration/tests/cli/consumer_group/test_consumer_group_create_command.rs
index 213831f08..4e02d6143 100644
--- a/core/integration/tests/cli/consumer_group/test_consumer_group_create_command.rs
+++ b/core/integration/tests/cli/consumer_group/test_consumer_group_create_command.rs
@@ -86,10 +86,10 @@ impl TestConsumerGroupCreateCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestConsumerGroupCreateCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client
-            .create_stream(&self.stream_name, self.stream_id.into())
-            .await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
+        let stream_details = stream.unwrap();
+        self.stream_id = stream_details.id;
 
         let topic = client
             .create_topic(
@@ -98,12 +98,13 @@ impl IggyCmdTestCase for TestConsumerGroupCreateCmd {
                 1,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
+        let topic_details = topic.unwrap();
+        self.topic_id = topic_details.id;
     }
 
     fn get_command(&self) -> IggyCmdCommand {
@@ -189,7 +190,7 @@ pub async fn should_be_successful() {
             String::from("main"),
             1,
             String::from("sync"),
-            Some(1),
+            None,
             String::from("group1"),
             TestStreamId::Numeric,
             TestTopicId::Numeric,
@@ -201,7 +202,7 @@ pub async fn should_be_successful() {
             String::from("stream"),
             3,
             String::from("topic"),
-            Some(3),
+            None,
             String::from("group3"),
             TestStreamId::Named,
             TestTopicId::Numeric,
@@ -213,7 +214,7 @@ pub async fn should_be_successful() {
             String::from("development"),
             1,
             String::from("probe"),
-            Some(7),
+            None,
             String::from("group7"),
             TestStreamId::Numeric,
             TestTopicId::Named,
@@ -225,7 +226,7 @@ pub async fn should_be_successful() {
             String::from("production"),
             5,
             String::from("test"),
-            Some(4),
+            None,
             String::from("group4"),
             TestStreamId::Named,
             TestTopicId::Named,
diff --git a/core/integration/tests/cli/consumer_group/test_consumer_group_delete_command.rs b/core/integration/tests/cli/consumer_group/test_consumer_group_delete_command.rs
index a6f950440..9c58038e0 100644
--- a/core/integration/tests/cli/consumer_group/test_consumer_group_delete_command.rs
+++ b/core/integration/tests/cli/consumer_group/test_consumer_group_delete_command.rs
@@ -89,10 +89,10 @@ impl TestConsumerGroupDeleteCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestConsumerGroupDeleteCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client
-            .create_stream(&self.stream_name, Some(self.stream_id))
-            .await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
+        let stream_details = stream.unwrap();
+        self.stream_id = stream_details.id;
 
         let topic = client
             .create_topic(
@@ -101,22 +101,24 @@ impl IggyCmdTestCase for TestConsumerGroupDeleteCmd {
                 1,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
+        let topic_details = topic.unwrap();
+        self.topic_id = topic_details.id;
 
         let consumer_group = client
             .create_consumer_group(
                 &self.stream_id.try_into().unwrap(),
                 &self.topic_id.try_into().unwrap(),
                 &self.group_name,
-                Some(self.group_id),
             )
             .await;
         assert!(consumer_group.is_ok());
+        let consumer_group_details = consumer_group.unwrap();
+        self.group_id = consumer_group_details.id;
     }
 
     fn get_command(&self) -> IggyCmdCommand {
diff --git a/core/integration/tests/cli/consumer_group/test_consumer_group_get_command.rs b/core/integration/tests/cli/consumer_group/test_consumer_group_get_command.rs
index 85a58c12f..8db78e700 100644
--- a/core/integration/tests/cli/consumer_group/test_consumer_group_get_command.rs
+++ b/core/integration/tests/cli/consumer_group/test_consumer_group_get_command.rs
@@ -89,34 +89,36 @@ impl TestConsumerGroupGetCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestConsumerGroupGetCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client
-            .create_stream(&self.stream_name, Some(self.stream_id))
-            .await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
+        let stream_details = stream.unwrap();
+        self.stream_id = stream_details.id;
 
         let topic = client
             .create_topic(
                 &self.stream_id.try_into().unwrap(),
                 &self.topic_name,
-                0,
+                1,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
+        let topic_details = topic.unwrap();
+        self.topic_id = topic_details.id;
 
         let consumer_group = client
             .create_consumer_group(
                 &self.stream_id.try_into().unwrap(),
                 &self.topic_id.try_into().unwrap(),
                 &self.group_name,
-                self.group_id.into(),
             )
             .await;
         assert!(consumer_group.is_ok());
+        let consumer_group_details = consumer_group.unwrap();
+        self.group_id = consumer_group_details.id;
     }
 
     fn get_command(&self) -> IggyCmdCommand {
diff --git a/core/integration/tests/cli/consumer_group/test_consumer_group_list_command.rs b/core/integration/tests/cli/consumer_group/test_consumer_group_list_command.rs
index 88712d147..0267b3789 100644
--- a/core/integration/tests/cli/consumer_group/test_consumer_group_list_command.rs
+++ b/core/integration/tests/cli/consumer_group/test_consumer_group_list_command.rs
@@ -18,7 +18,7 @@
 
 use crate::cli::common::{
     CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, OutputFormat, TestHelpCmd,
-    TestStreamId, TestTopicId, USAGE_PREFIX,
+    USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -29,53 +29,50 @@ use predicates::str::{contains, starts_with};
 use serial_test::parallel;
 
 struct TestConsumerGroupListCmd {
-    stream_id: u32,
     stream_name: String,
-    topic_id: u32,
     topic_name: String,
-    consumer_group_id: u32,
     consumer_group_name: String,
-    using_stream_id: TestStreamId,
-    using_topic_id: TestTopicId,
     output: OutputFormat,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
+    actual_consumer_group_id: Option<u32>,
 }
 
 impl TestConsumerGroupListCmd {
-    #[allow(clippy::too_many_arguments)]
     fn new(
-        stream_id: u32,
         stream_name: String,
-        topic_id: u32,
         topic_name: String,
-        consumer_group_id: u32,
         consumer_group_name: String,
-        using_stream_id: TestStreamId,
-        using_topic_id: TestTopicId,
         output: OutputFormat,
     ) -> Self {
         Self {
-            stream_id,
             stream_name,
-            topic_id,
             topic_name,
-            consumer_group_id,
             consumer_group_name,
-            using_stream_id,
-            using_topic_id,
             output,
+            actual_stream_id: None,
+            actual_topic_id: None,
+            actual_consumer_group_id: None,
         }
     }
 
     fn to_args(&self) -> Vec<String> {
-        let mut command = match self.using_stream_id {
-            TestStreamId::Numeric => vec![format!("{}", self.stream_id)],
-            TestStreamId::Named => vec![self.stream_name.clone()],
-        };
+        let mut command = vec![];
 
-        command.push(match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        });
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
+
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
 
         command.extend(self.output.to_args().into_iter().map(String::from));
 
@@ -86,34 +83,38 @@ impl TestConsumerGroupListCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestConsumerGroupListCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
+        // Create stream and capture its actual ID
         let stream = client
-            .create_stream(&self.stream_name, self.stream_id.into())
-            .await;
-        assert!(stream.is_ok());
+            .create_stream(&self.stream_name)
+            .await
+            .expect("Failed to create stream");
+        self.actual_stream_id = Some(stream.id);
 
+        // Create topic and capture its actual ID
         let topic = client
             .create_topic(
-                &self.stream_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
                 &self.topic_name,
                 1,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
-            .await;
-        assert!(topic.is_ok());
+            .await
+            .expect("Failed to create topic");
+        self.actual_topic_id = Some(topic.id);
 
+        // Create consumer group and capture its actual ID
         let consumer_group = client
             .create_consumer_group(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
+                &topic.id.try_into().unwrap(),
                 &self.consumer_group_name,
-                self.consumer_group_id.into(),
             )
-            .await;
-        assert!(consumer_group.is_ok());
+            .await
+            .expect("Failed to create consumer group");
+        self.actual_consumer_group_id = Some(consumer_group.id);
     }
 
     fn get_command(&self) -> IggyCmdCommand {
@@ -125,14 +126,16 @@ impl IggyCmdTestCase for TestConsumerGroupListCmd {
     }
 
     fn verify_command(&self, command_state: Assert) {
-        let stream_id = match self.using_stream_id {
-            TestStreamId::Numeric => format!("{}", self.stream_id),
-            TestStreamId::Named => self.stream_name.clone(),
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
         };
 
-        let topic_id = match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
         };
 
         let start_message = format!(
@@ -147,27 +150,32 @@ impl IggyCmdTestCase for TestConsumerGroupListCmd {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let consumer_group = client
-            .delete_consumer_group(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
-                &self.consumer_group_id.try_into().unwrap(),
-            )
-            .await;
-        assert!(consumer_group.is_ok());
+        // Use the actual IDs that were generated during resource creation
+        if let Some(consumer_group_id) = self.actual_consumer_group_id {
+            let consumer_group = client
+                .delete_consumer_group(
+                    &self.actual_stream_id.unwrap().try_into().unwrap(),
+                    &self.actual_topic_id.unwrap().try_into().unwrap(),
+                    &consumer_group_id.try_into().unwrap(),
+                )
+                .await;
+            assert!(consumer_group.is_ok());
+        }
 
-        let topic = client
-            .delete_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
-            )
-            .await;
-        assert!(topic.is_ok());
+        if let Some(topic_id) = self.actual_topic_id {
+            let topic = client
+                .delete_topic(
+                    &self.actual_stream_id.unwrap().try_into().unwrap(),
+                    &topic_id.try_into().unwrap(),
+                )
+                .await;
+            assert!(topic.is_ok());
+        }
 
-        let stream = client
-            .delete_stream(&self.stream_id.try_into().unwrap())
-            .await;
-        assert!(stream.is_ok());
+        if let Some(stream_id) = self.actual_stream_id {
+            let stream = client.delete_stream(&stream_id.try_into().unwrap()).await;
+            assert!(stream.is_ok());
+        }
     }
 }
 
@@ -177,72 +185,18 @@ pub async fn should_be_successful() {
     let mut iggy_cmd_test = IggyCmdTest::default();
 
     let test_parameters = vec![
-        (
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-            OutputFormat::Default,
-        ),
-        (
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-            OutputFormat::Default,
-        ),
-        (
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            OutputFormat::Default,
-        ),
-        (
-            TestStreamId::Named,
-            TestTopicId::Named,
-            OutputFormat::Default,
-        ),
-        (
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-            OutputFormat::List,
-        ),
-        (
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-            OutputFormat::List,
-        ),
-        (
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            OutputFormat::List,
-        ),
-        (TestStreamId::Named, TestTopicId::Named, OutputFormat::List),
-        (
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-            OutputFormat::Table,
-        ),
-        (
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-            OutputFormat::Table,
-        ),
-        (
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            OutputFormat::Table,
-        ),
-        (TestStreamId::Named, TestTopicId::Named, OutputFormat::Table),
+        OutputFormat::Default,
+        OutputFormat::List,
+        OutputFormat::Table,
     ];
 
     iggy_cmd_test.setup().await;
-    for (using_stream_id, using_topic_id, output_format) in test_parameters {
+    for output_format in test_parameters {
         iggy_cmd_test
             .execute_test(TestConsumerGroupListCmd::new(
-                1,
                 String::from("stream"),
-                2,
                 String::from("topic"),
-                3,
                 String::from("consumer-group"),
-                using_stream_id,
-                using_topic_id,
                 output_format,
             ))
             .await;
diff --git a/core/integration/tests/cli/consumer_offset/test_consumer_offset_get_command.rs b/core/integration/tests/cli/consumer_offset/test_consumer_offset_get_command.rs
index af0861cf4..537787a69 100644
--- a/core/integration/tests/cli/consumer_offset/test_consumer_offset_get_command.rs
+++ b/core/integration/tests/cli/consumer_offset/test_consumer_offset_get_command.rs
@@ -18,7 +18,7 @@
 
 use crate::cli::common::{
     CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestConsumerId, TestHelpCmd,
-    TestStreamId, TestTopicId, USAGE_PREFIX,
+    USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -30,63 +30,59 @@ use std::str::FromStr;
 struct TestConsumerOffsetGetCmd {
     consumer_id: u32,
     consumer_name: String,
-    stream_id: u32,
     stream_name: String,
-    topic_id: u32,
     topic_name: String,
     partition_id: u32,
     using_consumer_id: TestConsumerId,
-    using_stream_id: TestStreamId,
-    using_topic_id: TestTopicId,
     messages_count: u32,
     stored_offset: u64,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl TestConsumerOffsetGetCmd {
-    #[allow(clippy::too_many_arguments)]
     fn new(
         consumer_id: u32,
         consumer_name: String,
-        stream_id: u32,
         stream_name: String,
-        topic_id: u32,
         topic_name: String,
         partition_id: u32,
         using_consumer_id: TestConsumerId,
-        using_stream_id: TestStreamId,
-        using_topic_id: TestTopicId,
     ) -> Self {
         Self {
             consumer_id,
             consumer_name,
-            stream_id,
             stream_name,
-            topic_id,
             topic_name,
             partition_id,
             using_consumer_id,
-            using_stream_id,
-            using_topic_id,
             messages_count: 100,
             stored_offset: 66,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
     fn to_args(&self) -> Vec<String> {
         let mut command = match self.using_consumer_id {
-            TestStreamId::Numeric => vec![format!("{}", self.consumer_id)],
-            TestStreamId::Named => vec![self.consumer_name.clone()],
+            TestConsumerId::Numeric => vec![format!("{}", self.consumer_id)],
+            TestConsumerId::Named => vec![self.consumer_name.clone()],
         };
 
-        command.push(match self.using_stream_id {
-            TestTopicId::Numeric => format!("{}", self.stream_id),
-            TestTopicId::Named => self.stream_name.clone(),
-        });
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
 
-        command.push(match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        });
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
 
         command.push(format!("{}", self.partition_id));
 
@@ -97,24 +93,27 @@ impl TestConsumerOffsetGetCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestConsumerOffsetGetCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
+        // Create stream and capture its actual ID
         let stream = client
-            .create_stream(&self.stream_name, self.stream_id.into())
-            .await;
-        assert!(stream.is_ok());
+            .create_stream(&self.stream_name)
+            .await
+            .expect("Failed to create stream");
+        self.actual_stream_id = Some(stream.id);
 
+        // Create topic and capture its actual ID
         let topic = client
             .create_topic(
-                &self.stream_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
                 &self.topic_name,
                 1,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
-            .await;
-        assert!(topic.is_ok());
+            .await
+            .expect("Failed to create topic");
+        self.actual_topic_id = Some(topic.id);
 
         let mut messages = (1..=self.messages_count)
             .filter_map(|id| IggyMessage::from_str(format!("Test message {id}").as_str()).ok())
@@ -122,8 +121,8 @@ impl IggyCmdTestCase for TestConsumerOffsetGetCmd {
 
         let send_status = client
             .send_messages(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
+                &topic.id.try_into().unwrap(),
                 &Partitioning::partition_id(self.partition_id),
                 &mut messages,
             )
@@ -140,8 +139,8 @@ impl IggyCmdTestCase for TestConsumerOffsetGetCmd {
                     }
                     .unwrap(),
                 },
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
+                &topic.id.try_into().unwrap(),
                 Some(self.partition_id),
                 self.stored_offset,
             )
@@ -163,14 +162,16 @@ impl IggyCmdTestCase for TestConsumerOffsetGetCmd {
             TestConsumerId::Named => self.consumer_name.clone(),
         };
 
-        let stream_id = match self.using_stream_id {
-            TestStreamId::Numeric => format!("{}", self.stream_id),
-            TestStreamId::Named => self.stream_name.clone(),
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
         };
 
-        let topic_id = match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
         };
 
         let message = format!(
@@ -189,18 +190,20 @@ impl IggyCmdTestCase for TestConsumerOffsetGetCmd {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let topic = client
-            .delete_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
-            )
-            .await;
-        assert!(topic.is_ok());
+        if let Some(topic_id) = self.actual_topic_id {
+            let topic = client
+                .delete_topic(
+                    &self.actual_stream_id.unwrap().try_into().unwrap(),
+                    &topic_id.try_into().unwrap(),
+                )
+                .await;
+            assert!(topic.is_ok());
+        }
 
-        let stream = client
-            .delete_stream(&self.stream_id.try_into().unwrap())
-            .await;
-        assert!(stream.is_ok());
+        if let Some(stream_id) = self.actual_stream_id {
+            let stream = client.delete_stream(&stream_id.try_into().unwrap()).await;
+            assert!(stream.is_ok());
+        }
     }
 }
 
@@ -209,68 +212,18 @@ impl IggyCmdTestCase for TestConsumerOffsetGetCmd {
 pub async fn should_be_successful() {
     let mut iggy_cmd_test = IggyCmdTest::default();
 
-    let test_parameters = vec![
-        (
-            TestConsumerId::Numeric,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-        ),
-        (
-            TestConsumerId::Named,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-        ),
-        (
-            TestConsumerId::Numeric,
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-        ),
-        (
-            TestConsumerId::Numeric,
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-        ),
-        (
-            TestConsumerId::Named,
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-        ),
-        (
-            TestConsumerId::Named,
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-        ),
-        (
-            TestConsumerId::Numeric,
-            TestStreamId::Named,
-            TestTopicId::Named,
-        ),
-        (
-            TestConsumerId::Named,
-            TestStreamId::Named,
-            TestTopicId::Named,
-        ),
-        (
-            TestConsumerId::Numeric,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-        ),
-    ];
+    let test_parameters = vec![TestConsumerId::Numeric, TestConsumerId::Named];
 
     iggy_cmd_test.setup().await;
-    for (using_stream_id, using_topic_id, using_consumer_id) in test_parameters {
+    for using_consumer_id in test_parameters {
         iggy_cmd_test
             .execute_test(TestConsumerOffsetGetCmd::new(
                 1,
                 String::from("consumer"),
-                2,
                 String::from("stream"),
-                3,
                 String::from("topic"),
-                1,
+                0,
                 using_consumer_id,
-                using_stream_id,
-                using_topic_id,
             ))
             .await;
     }
diff --git a/core/integration/tests/cli/consumer_offset/test_consumer_offset_set_command.rs b/core/integration/tests/cli/consumer_offset/test_consumer_offset_set_command.rs
index f1a107a26..4f28bae43 100644
--- a/core/integration/tests/cli/consumer_offset/test_consumer_offset_set_command.rs
+++ b/core/integration/tests/cli/consumer_offset/test_consumer_offset_set_command.rs
@@ -97,10 +97,10 @@ impl TestConsumerOffsetSetCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestConsumerOffsetSetCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client
-            .create_stream(&self.stream_name, Some(self.stream_id))
-            .await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
+        let stream_details = stream.unwrap();
+        self.stream_id = stream_details.id;
 
         let topic = client
             .create_topic(
@@ -109,12 +109,13 @@ impl IggyCmdTestCase for TestConsumerOffsetSetCmd {
                 1,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
+        let topic_details = topic.unwrap();
+        self.topic_id = topic_details.id;
 
         let mut messages = (1..=self.stored_offset + 1)
             .filter_map(|id| IggyMessage::from_str(format!("Test message {id}").as_str()).ok())
@@ -274,7 +275,7 @@ pub async fn should_be_successful() {
                 String::from("stream"),
                 3,
                 String::from("topic"),
-                1,
+                0,
                 using_consumer_id,
                 using_stream_id,
                 using_topic_id,
diff --git a/core/integration/tests/cli/context/test_context_applied.rs b/core/integration/tests/cli/context/test_context_applied.rs
index 4e96a69c5..0bbbc1dd5 100644
--- a/core/integration/tests/cli/context/test_context_applied.rs
+++ b/core/integration/tests/cli/context/test_context_applied.rs
@@ -16,8 +16,8 @@
  * under the License.
  */
 
-use std::collections::HashMap;
-
+use super::common::TestIggyContext;
+use crate::cli::common::{IggyCmdCommand, IggyCmdTest, IggyCmdTestCase};
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
 use iggy::prelude::ArgsOptional;
@@ -26,10 +26,7 @@ use iggy_binary_protocol::cli::binary_context::common::ContextConfig;
 use integration::test_server::TestServer;
 use predicates::str::{contains, starts_with};
 use serial_test::parallel;
-
-use crate::cli::common::{IggyCmdCommand, IggyCmdTest, IggyCmdTestCase};
-
-use super::common::TestIggyContext;
+use std::collections::HashMap;
 
 struct TestContextApplied {
     set_transport_context: Option<String>,
diff --git a/core/integration/tests/cli/general/test_help_command.rs b/core/integration/tests/cli/general/test_help_command.rs
index 8481bc456..9ae5fcb95 100644
--- a/core/integration/tests/cli/general/test_help_command.rs
+++ b/core/integration/tests/cli/general/test_help_command.rs
@@ -54,7 +54,7 @@ Commands:
 
 Options:
       --transport <TRANSPORT>
-          The transport to use. Valid values are `quic`, `http` and `tcp`
+          The transport to use. Valid values are `quic`, `http`, `tcp` and `ws`
 {CLAP_INDENT}
           [default: tcp]
 
@@ -174,6 +174,21 @@ Options:
       --quic-validate-certificate
           Flag to enable certificate validation for QUIC
 
+      --websocket-server-address <WEBSOCKET_SERVER_ADDRESS>
+          The optional server address for the WebSocket transport
+{CLAP_INDENT}
+          [default: 127.0.0.1:8092]
+
+      --websocket-reconnection-max-retries <WEBSOCKET_RECONNECTION_MAX_RETRIES>
+          The optional number of max reconnect retries for the WebSocket transport
+{CLAP_INDENT}
+          [default: 3]
+
+      --websocket-reconnection-interval <WEBSOCKET_RECONNECTION_INTERVAL>
+          The optional reconnect interval for the WebSocket transport
+{CLAP_INDENT}
+          [default: "1s"]
+
   -q, --quiet
           Quiet mode (disabled stdout printing)
 
diff --git a/core/integration/tests/cli/message/test_message_flush_command.rs b/core/integration/tests/cli/message/test_message_flush_command.rs
index ce0c888cf..83541b8cd 100644
--- a/core/integration/tests/cli/message/test_message_flush_command.rs
+++ b/core/integration/tests/cli/message/test_message_flush_command.rs
@@ -17,8 +17,7 @@
  */
 
 use crate::cli::common::{
-    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, TestStreamId,
-    TestTopicId, USAGE_PREFIX,
+    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -31,55 +30,51 @@ use serial_test::parallel;
 use std::str::FromStr;
 
 struct TestMessageFetchCmd {
-    stream_id: u32,
     stream_name: String,
-    topic_id: u32,
     topic_name: String,
     partitions_count: u32,
-    using_stream_id: TestStreamId,
-    using_topic_id: TestTopicId,
     partition_id: u32,
     fsync: bool,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl TestMessageFetchCmd {
-    #[allow(clippy::too_many_arguments)]
     fn new(
-        stream_id: u32,
         stream_name: &str,
-        topic_id: u32,
         topic_name: &str,
         partitions_count: u32,
-        using_stream_id: TestStreamId,
-        using_topic_id: TestTopicId,
         partition_id: u32,
         fsync: bool,
     ) -> Self {
         Self {
-            stream_id,
             stream_name: stream_name.to_string(),
-            topic_id,
             topic_name: topic_name.to_string(),
             partitions_count,
-            using_stream_id,
-            using_topic_id,
             partition_id,
             fsync,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
     fn to_args(&self) -> Vec<String> {
         let mut command = Vec::new();
 
-        command.extend(match self.using_stream_id {
-            TestStreamId::Numeric => vec![format!("{}", self.stream_id)],
-            TestStreamId::Named => vec![self.stream_name.clone()],
-        });
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
 
-        command.push(match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        });
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
 
         command.push(format!("{}", self.partition_id));
 
@@ -94,26 +89,27 @@ impl TestMessageFetchCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestMessageFetchCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client.create_stream(&self.stream_name, None).await;
-        assert!(stream.is_ok());
-
-        let stream_id = Identifier::from_str(&self.stream_name);
-        assert!(stream_id.is_ok());
-        let stream_id = stream_id.unwrap();
-
+        // Create stream and capture its actual ID
+        let stream = client
+            .create_stream(&self.stream_name)
+            .await
+            .expect("Failed to create stream");
+        self.actual_stream_id = Some(stream.id);
+
+        // Create topic and capture its actual ID
         let topic = client
             .create_topic(
-                &stream_id,
+                &stream.id.try_into().unwrap(),
                 &self.topic_name,
                 self.partitions_count,
                 Default::default(),
                 None,
-                None,
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
-            .await;
-        assert!(topic.is_ok());
+            .await
+            .expect("Failed to create topic");
+        self.actual_topic_id = Some(topic.id);
     }
 
     fn get_command(&self) -> IggyCmdCommand {
@@ -125,14 +121,16 @@ impl IggyCmdTestCase for TestMessageFetchCmd {
     }
 
     fn verify_command(&self, command_state: Assert) {
-        let stream_id = match self.using_stream_id {
-            TestStreamId::Numeric => format!("{}", self.stream_id),
-            TestStreamId::Named => self.stream_name.clone(),
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
         };
 
-        let topic_id = match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
         };
 
         let identification_part = format!(
@@ -153,18 +151,19 @@ impl IggyCmdTestCase for TestMessageFetchCmd {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let topic_id = Identifier::from_str(&self.topic_name);
-        assert!(topic_id.is_ok());
-        let topic_id = topic_id.unwrap();
-        let stream_id = Identifier::from_str(&self.stream_name);
-        assert!(stream_id.is_ok());
-        let stream_id = stream_id.unwrap();
-
-        let topic = client.delete_topic(&stream_id, &topic_id).await;
-        assert!(topic.is_ok());
-
-        let stream = client.delete_stream(&stream_id).await;
-        assert!(stream.is_ok());
+        if let Some(topic_id) = self.actual_topic_id {
+            let stream_id = Identifier::from_str(&self.stream_name);
+            assert!(stream_id.is_ok());
+            let stream_id = stream_id.unwrap();
+
+            let topic = client
+                .delete_topic(&stream_id, &topic_id.try_into().unwrap())
+                .await;
+            assert!(topic.is_ok());
+
+            let stream = client.delete_stream(&stream_id).await;
+            assert!(stream.is_ok());
+        }
     }
 }
 
@@ -173,31 +172,12 @@ impl IggyCmdTestCase for TestMessageFetchCmd {
 pub async fn should_be_successful() {
     let mut iggy_cmd_test = IggyCmdTest::default();
 
-    let test_parameters: Vec<(TestStreamId, TestTopicId, bool)> = vec![
-        (TestStreamId::Numeric, TestTopicId::Numeric, false),
-        (TestStreamId::Numeric, TestTopicId::Named, false),
-        (TestStreamId::Named, TestTopicId::Numeric, false),
-        (TestStreamId::Named, TestTopicId::Named, false),
-        (TestStreamId::Numeric, TestTopicId::Numeric, true),
-        (TestStreamId::Numeric, TestTopicId::Named, true),
-        (TestStreamId::Named, TestTopicId::Numeric, true),
-        (TestStreamId::Named, TestTopicId::Named, true),
-    ];
+    let test_parameters = vec![false, true]; // Test with and without fsync
 
     iggy_cmd_test.setup().await;
-    for (using_stream_id, using_topic_id, fsync) in test_parameters {
+    for fsync in test_parameters {
         iggy_cmd_test
-            .execute_test(TestMessageFetchCmd::new(
-                1,
-                "stream",
-                1,
-                "topic",
-                1,
-                using_stream_id,
-                using_topic_id,
-                1,
-                fsync,
-            ))
+            .execute_test(TestMessageFetchCmd::new("stream", "topic", 1, 0, fsync))
             .await;
     }
 }
diff --git a/core/integration/tests/cli/message/test_message_poll_command.rs b/core/integration/tests/cli/message/test_message_poll_command.rs
index aea549642..2a4c2b4f3 100644
--- a/core/integration/tests/cli/message/test_message_poll_command.rs
+++ b/core/integration/tests/cli/message/test_message_poll_command.rs
@@ -17,8 +17,7 @@
  */
 
 use crate::cli::common::{
-    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, TestStreamId,
-    TestTopicId, USAGE_PREFIX,
+    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -30,55 +29,47 @@ use std::collections::HashMap;
 use std::str::FromStr;
 
 struct TestMessagePollCmd {
-    stream_id: u32,
     stream_name: String,
-    topic_id: u32,
     topic_name: String,
     partitions_count: u32,
     messages: Vec<String>,
     partition_id: u32,
     message_count: usize,
     strategy: PollingStrategy,
-    using_stream_id: TestStreamId,
-    using_topic_id: TestTopicId,
     show_headers: bool,
     headers: (HeaderKey, HeaderValue),
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl TestMessagePollCmd {
     #[allow(clippy::too_many_arguments)]
     fn new(
-        stream_id: u32,
         stream_name: String,
-        topic_id: u32,
         topic_name: String,
         partitions_count: u32,
         messages: &[String],
         partition_id: u32,
         message_count: usize,
         strategy: PollingStrategy,
-        using_stream_id: TestStreamId,
-        using_topic_id: TestTopicId,
         show_headers: bool,
         headers: (HeaderKey, HeaderValue),
     ) -> Self {
-        assert!(partition_id <= partitions_count);
-        assert!(partition_id > 0);
+        assert!(partition_id < partitions_count);
         assert!(message_count < messages.len());
         Self {
-            stream_id,
             stream_name,
-            topic_id,
             topic_name,
             partitions_count,
             messages: messages.to_owned(),
             partition_id,
             message_count,
             strategy,
-            using_stream_id,
-            using_topic_id,
             show_headers,
             headers,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
@@ -102,15 +93,19 @@ impl TestMessagePollCmd {
             command.extend(vec!["--show-headers".into()]);
         }
 
-        command.extend(match self.using_stream_id {
-            TestStreamId::Numeric => vec![format!("{}", self.stream_id)],
-            TestStreamId::Named => vec![self.stream_name.clone()],
-        });
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
 
-        command.push(match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        });
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
 
         command.push(format!("{}", self.partition_id));
 
@@ -121,24 +116,25 @@ impl TestMessagePollCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestMessagePollCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client
-            .create_stream(&self.stream_name, self.stream_id.into())
-            .await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
+        let stream = stream.unwrap();
+        self.actual_stream_id = Some(stream.id);
 
         let topic = client
             .create_topic(
-                &self.stream_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
                 &self.topic_name,
                 self.partitions_count,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
+        let topic = topic.unwrap();
+        self.actual_topic_id = Some(topic.id);
 
         let mut messages = self
             .messages
@@ -155,8 +151,8 @@ impl IggyCmdTestCase for TestMessagePollCmd {
 
         let send_status = client
             .send_messages(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
+                &topic.id.try_into().unwrap(),
                 &Partitioning::partition_id(self.partition_id),
                 &mut messages,
             )
@@ -173,14 +169,16 @@ impl IggyCmdTestCase for TestMessagePollCmd {
     }
 
     fn verify_command(&self, command_state: Assert) {
-        let stream_id = match self.using_stream_id {
-            TestStreamId::Numeric => format!("{}", self.stream_id),
-            TestStreamId::Named => self.stream_name.clone(),
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
         };
 
-        let topic_id = match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
         };
 
         let polled_status = match self.message_count {
@@ -235,18 +233,18 @@ impl IggyCmdTestCase for TestMessagePollCmd {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let topic = client
-            .delete_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
-            )
-            .await;
-        assert!(topic.is_ok());
-
-        let stream = client
-            .delete_stream(&self.stream_id.try_into().unwrap())
-            .await;
-        assert!(stream.is_ok());
+        if let (Some(stream_id), Some(topic_id)) = (self.actual_stream_id, self.actual_topic_id) {
+            let topic = client
+                .delete_topic(
+                    &stream_id.try_into().unwrap(),
+                    &topic_id.try_into().unwrap(),
+                )
+                .await;
+            assert!(topic.is_ok());
+
+            let stream = client.delete_stream(&stream_id.try_into().unwrap()).await;
+            assert!(stream.is_ok());
+        }
     }
 }
 
@@ -273,74 +271,26 @@ pub async fn should_be_successful() {
         HeaderValue::from_str("value1").unwrap(),
     );
 
-    let test_parameters: Vec<(u32, usize, PollingStrategy, TestStreamId, TestTopicId, bool)> = vec![
-        (
-            1,
-            1,
-            PollingStrategy::offset(0),
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-            true,
-        ),
-        (
-            2,
-            5,
-            PollingStrategy::offset(0),
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            true,
-        ),
-        (
-            3,
-            3,
-            PollingStrategy::offset(3),
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-            true,
-        ),
-        (
-            4,
-            5,
-            PollingStrategy::first(),
-            TestStreamId::Named,
-            TestTopicId::Named,
-            true,
-        ),
-        (
-            1,
-            4,
-            PollingStrategy::last(),
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-            true,
-        ),
-        (
-            2,
-            3,
-            PollingStrategy::next(),
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            false,
-        ),
+    let test_parameters: Vec<(u32, usize, PollingStrategy, bool)> = vec![
+        (0, 1, PollingStrategy::offset(0), true),
+        (1, 5, PollingStrategy::offset(0), true),
+        (2, 3, PollingStrategy::offset(3), true),
+        (3, 5, PollingStrategy::first(), true),
+        (0, 4, PollingStrategy::last(), true),
+        (1, 3, PollingStrategy::next(), false),
     ];
 
     iggy_cmd_test.setup().await;
-    for (partition_id, message_count, strategy, using_stream_id, using_topic_id, show_headers) in
-        test_parameters
-    {
+    for (partition_id, message_count, strategy, show_headers) in test_parameters {
         iggy_cmd_test
             .execute_test(TestMessagePollCmd::new(
-                1,
                 String::from("stream"),
-                2,
                 String::from("topic"),
                 4,
                 &test_messages,
                 partition_id,
                 message_count,
                 strategy,
-                using_stream_id,
-                using_topic_id,
                 show_headers,
                 test_headers.clone(),
             ))
@@ -418,7 +368,7 @@ Options:
 {CLAP_INDENT}
           Consumer ID can be specified as a consumer name or ID
 {CLAP_INDENT}
-          [default: 1]
+          [default: 0]
 
   -s, --show-headers
           Include the message headers in the output
@@ -469,7 +419,7 @@ Options:
   -f, --first                          Polling strategy - start polling from the first message in the partition
   -l, --last                           Polling strategy - start polling from the last message in the partition
   -n, --next                           Polling strategy - start polling from the next message
-  -c, --consumer <CONSUMER>            Regular consumer which will poll messages [default: 1]
+  -c, --consumer <CONSUMER>            Regular consumer which will poll messages [default: 0]
   -s, --show-headers                   Include the message headers in the output
       --output-file <OUTPUT_FILE>      Store polled message into file in binary format
   -h, --help                           Print help (see more with '--help')
diff --git a/core/integration/tests/cli/message/test_message_poll_to_file_command.rs b/core/integration/tests/cli/message/test_message_poll_to_file_command.rs
index 733871c4d..3f9d029e5 100644
--- a/core/integration/tests/cli/message/test_message_poll_to_file_command.rs
+++ b/core/integration/tests/cli/message/test_message_poll_to_file_command.rs
@@ -36,6 +36,9 @@ pub(super) struct TestMessagePollToFileCmd<'a> {
     headers: HashMap<HeaderKey, HeaderValue>,
     output_file: String,
     cleanup: bool,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl<'a> TestMessagePollToFileCmd<'a> {
@@ -60,6 +63,8 @@ impl<'a> TestMessagePollToFileCmd<'a> {
             headers,
             output_file: output_file.into(),
             cleanup,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
@@ -81,11 +86,21 @@ impl<'a> TestMessagePollToFileCmd<'a> {
 
         command.extend(vec!["--output-file".into(), self.output_file.clone()]);
 
-        command.extend(vec![
-            self.stream_name.clone(),
-            self.topic_name.clone(),
-            "1".into(),
-        ]);
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
+
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
+
+        command.push("0".into());
 
         command
     }
@@ -94,30 +109,25 @@ impl<'a> TestMessagePollToFileCmd<'a> {
 #[async_trait]
 impl IggyCmdTestCase for TestMessagePollToFileCmd<'_> {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client.create_stream(&self.stream_name, None).await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
-
-        let stream_id = Identifier::from_str(self.stream_name.as_str());
-        assert!(stream_id.is_ok());
-        let stream_id = stream_id.unwrap();
+        let stream = stream.unwrap();
+        self.actual_stream_id = Some(stream.id);
 
         let topic = client
             .create_topic(
-                &stream_id,
+                &stream.id.try_into().unwrap(),
                 &self.topic_name,
                 1,
                 Default::default(),
                 None,
-                None,
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
-
-        let topic_id = Identifier::from_str(self.topic_name.as_str());
-        assert!(topic_id.is_ok());
-        let topic_id = topic_id.unwrap();
+        let topic = topic.unwrap();
+        self.actual_topic_id = Some(topic.id);
 
         let mut messages = self
             .messages
@@ -134,9 +144,9 @@ impl IggyCmdTestCase for TestMessagePollToFileCmd<'_> {
 
         let send_status = client
             .send_messages(
-                &stream_id,
-                &topic_id,
-                &Partitioning::partition_id(1),
+                &stream.id.try_into().unwrap(),
+                &topic.id.try_into().unwrap(),
+                &Partitioning::partition_id(0),
                 &mut messages,
             )
             .await;
@@ -157,9 +167,21 @@ impl IggyCmdTestCase for TestMessagePollToFileCmd<'_> {
             _ => format!("Polled {} messages", self.message_count),
         };
 
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
+        };
+
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
+        };
+
         let message_prefix = format!(
-            "Executing poll messages from topic ID: {} and stream with ID: {}\nPolled messages from topic with ID: {} and stream with ID: {} (from partition with ID: 1)\n{polled_status}",
-            self.topic_name, self.stream_name, self.topic_name, self.stream_name
+            "Executing poll messages from topic ID: {} and stream with ID: {}\nPolled messages from topic with ID: {} and stream with ID: {} (from partition with ID: 0)\n{polled_status}",
+            topic_id, stream_id, topic_id, stream_id
         );
         let message_file = format!("Storing messages to {} binary file", self.output_file);
         let message_count = format!(
@@ -179,19 +201,18 @@ impl IggyCmdTestCase for TestMessagePollToFileCmd<'_> {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let stream_id = Identifier::from_str(self.stream_name.as_str());
-        assert!(stream_id.is_ok());
-        let stream_id = stream_id.unwrap();
-
-        let topic_id = Identifier::from_str(self.topic_name.as_str());
-        assert!(topic_id.is_ok());
-        let topic_id = topic_id.unwrap();
+        if let (Some(stream_id), Some(topic_id)) = (self.actual_stream_id, self.actual_topic_id) {
+            let topic = client
+                .delete_topic(
+                    &stream_id.try_into().unwrap(),
+                    &topic_id.try_into().unwrap(),
+                )
+                .await;
+            assert!(topic.is_ok());
 
-        let topic = client.delete_topic(&stream_id, &topic_id).await;
-        assert!(topic.is_ok());
-
-        let stream = client.delete_stream(&stream_id).await;
-        assert!(stream.is_ok());
+            let stream = client.delete_stream(&stream_id.try_into().unwrap()).await;
+            assert!(stream.is_ok());
+        }
 
         assert!(Path::new(&self.output_file).is_file());
         if self.cleanup {
diff --git a/core/integration/tests/cli/message/test_message_send_command.rs b/core/integration/tests/cli/message/test_message_send_command.rs
index 2a9cf5a5a..27227cd94 100644
--- a/core/integration/tests/cli/message/test_message_send_command.rs
+++ b/core/integration/tests/cli/message/test_message_send_command.rs
@@ -17,8 +17,7 @@
  */
 
 use crate::cli::common::{
-    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, TestStreamId,
-    TestTopicId, USAGE_PREFIX,
+    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -53,61 +52,57 @@ enum ProvideMessages {
 }
 
 struct TestMessageSendCmd {
-    stream_id: u32,
     stream_name: String,
-    topic_id: u32,
     topic_name: String,
     partitions_count: u32,
     messages: Vec<String>,
     message_input: ProvideMessages,
-    using_stream_id: TestStreamId,
-    using_topic_id: TestTopicId,
     partitioning: PartitionSelection,
     header: Option<HashMap<HeaderKey, HeaderValue>>,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl TestMessageSendCmd {
-    #[allow(clippy::too_many_arguments)]
     fn new(
-        stream_id: u32,
         stream_name: String,
-        topic_id: u32,
         topic_name: String,
         partitions_count: u32,
         messages: Vec<String>,
         message_input: ProvideMessages,
-        using_stream_id: TestStreamId,
-        using_topic_id: TestTopicId,
         partitioning: PartitionSelection,
         header: Option<HashMap<HeaderKey, HeaderValue>>,
     ) -> Self {
         Self {
-            stream_id,
             stream_name,
-            topic_id,
             topic_name,
             partitions_count,
             messages,
             message_input,
-            using_stream_id,
-            using_topic_id,
             partitioning,
             header,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
     fn to_args(&self) -> Vec<String> {
         let mut command = self.partitioning.to_args();
 
-        command.extend(match self.using_stream_id {
-            TestStreamId::Numeric => vec![format!("{}", self.stream_id)],
-            TestStreamId::Named => vec![self.stream_name.clone()],
-        });
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
 
-        command.push(match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        });
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
 
         if let Some(header) = &self.header {
             command.push("--headers".to_string());
@@ -132,12 +127,7 @@ impl TestMessageSendCmd {
 
     fn calculate_partition_id_from_messages_key(&self, messages_key: &[u8]) -> u32 {
         let messages_key_hash = XxHash32::oneshot(0, messages_key);
-        let mut partition_id = messages_key_hash % self.partitions_count;
-        if partition_id == 0 {
-            partition_id = self.partitions_count;
-        }
-
-        partition_id
+        messages_key_hash % self.partitions_count
     }
 
     fn get_partition_id(&self) -> u32 {
@@ -154,24 +144,27 @@ impl TestMessageSendCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestMessageSendCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
+        // Create stream and capture its actual ID
         let stream = client
-            .create_stream(&self.stream_name, self.stream_id.into())
-            .await;
-        assert!(stream.is_ok());
+            .create_stream(&self.stream_name)
+            .await
+            .expect("Failed to create stream");
+        self.actual_stream_id = Some(stream.id);
 
+        // Create topic and capture its actual ID
         let topic = client
             .create_topic(
-                &self.stream_id.try_into().unwrap(),
+                &stream.id.try_into().unwrap(),
                 &self.topic_name,
                 self.partitions_count,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
-            .await;
-        assert!(topic.is_ok());
+            .await
+            .expect("Failed to create topic");
+        self.actual_topic_id = Some(topic.id);
     }
 
     fn get_command(&self) -> IggyCmdCommand {
@@ -190,14 +183,16 @@ impl IggyCmdTestCase for TestMessageSendCmd {
     }
 
     fn verify_command(&self, command_state: Assert) {
-        let stream_id = match self.using_stream_id {
-            TestStreamId::Numeric => format!("{}", self.stream_id),
-            TestStreamId::Named => self.stream_name.clone(),
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
         };
 
-        let topic_id = match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
         };
 
         let message = format!(
@@ -210,40 +205,85 @@ impl IggyCmdTestCase for TestMessageSendCmd {
     async fn verify_server_state(&self, client: &dyn Client) {
         let topic = client
             .get_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
+                &self.actual_topic_id.unwrap().try_into().unwrap(),
             )
             .await;
         assert!(topic.is_ok());
         let topic_details = topic.unwrap().expect("Failed to get topic");
         assert_eq!(topic_details.messages_count, self.messages.len() as u64);
 
-        let polled_messages = client
-            .poll_messages(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
-                Some(self.get_partition_id()),
-                &Consumer::default(),
-                &PollingStrategy::offset(0),
-                self.messages.len() as u32,
-                false,
-            )
-            .await;
+        // For Balanced partitioning, messages are distributed across all partitions
+        // so we need to poll from all partitions and collect messages
+        let all_messages = match &self.partitioning {
+            PartitionSelection::Balanced => {
+                let mut collected_messages = Vec::new();
+                for partition_id in 0..self.partitions_count {
+                    let polled = client
+                        .poll_messages(
+                            &self.actual_stream_id.unwrap().try_into().unwrap(),
+                            &self.actual_topic_id.unwrap().try_into().unwrap(),
+                            Some(partition_id),
+                            &Consumer::default(),
+                            &PollingStrategy::offset(0),
+                            self.messages.len() as u32,
+                            false,
+                        )
+                        .await;
+                    if let Ok(polled) = polled {
+                        collected_messages.extend(polled.messages);
+                    }
+                }
+                collected_messages
+            }
+            _ => {
+                // For specific partition or key-based partitioning
+                let polled_messages = client
+                    .poll_messages(
+                        &self.actual_stream_id.unwrap().try_into().unwrap(),
+                        &self.actual_topic_id.unwrap().try_into().unwrap(),
+                        Some(self.get_partition_id()),
+                        &Consumer::default(),
+                        &PollingStrategy::offset(0),
+                        self.messages.len() as u32,
+                        false,
+                    )
+                    .await;
+
+                assert!(polled_messages.is_ok());
+                polled_messages.unwrap().messages
+            }
+        };
 
-        assert!(polled_messages.is_ok());
-        let polled_messages = polled_messages.unwrap();
-        assert_eq!(polled_messages.messages.len(), self.messages.len());
-        assert_eq!(
-            polled_messages
-                .messages
-                .iter()
-                .map(|m| from_utf8(&m.payload.clone()).unwrap().to_string())
-                .collect::<Vec<_>>(),
-            self.messages
-        );
+        assert_eq!(all_messages.len(), self.messages.len());
+
+        // For Balanced partitioning, messages may arrive in different order
+        // so we just check that all expected messages are present
+        let expected_messages: Vec<String> = self.messages.clone();
+        let received_messages: Vec<String> = all_messages
+            .iter()
+            .map(|m| from_utf8(&m.payload.clone()).unwrap().to_string())
+            .collect();
+
+        match &self.partitioning {
+            PartitionSelection::Balanced => {
+                // For balanced, just check all messages are present (order may vary)
+                for expected in &expected_messages {
+                    assert!(
+                        received_messages.contains(expected),
+                        "Expected message '{}' not found in received messages",
+                        expected
+                    );
+                }
+            }
+            _ => {
+                // For specific partition, order should be preserved
+                assert_eq!(received_messages, expected_messages);
+            }
+        }
 
         if let Some(expected_header) = &self.header {
-            polled_messages.messages.iter().for_each(|m| {
+            all_messages.iter().for_each(|m| {
                 assert!(m.user_headers.is_some());
                 assert_eq!(expected_header, &m.user_headers_map().unwrap().unwrap());
             })
@@ -251,14 +291,14 @@ impl IggyCmdTestCase for TestMessageSendCmd {
 
         let topic = client
             .delete_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
+                &self.actual_topic_id.unwrap().try_into().unwrap(),
             )
             .await;
         assert!(topic.is_ok());
 
         let stream = client
-            .delete_stream(&self.stream_id.try_into().unwrap())
+            .delete_stream(&self.actual_stream_id.unwrap().try_into().unwrap())
             .await;
         assert!(stream.is_ok());
     }
@@ -270,87 +310,25 @@ pub async fn should_be_successful() {
     let mut iggy_cmd_test = IggyCmdTest::default();
 
     let test_parameters = vec![
+        (ProvideMessages::AsArgs, PartitionSelection::Balanced),
+        (ProvideMessages::ViaStdin, PartitionSelection::Balanced),
+        (ProvideMessages::ViaStdin, PartitionSelection::Id(0)),
+        (ProvideMessages::AsArgs, PartitionSelection::Id(1)),
         (
             ProvideMessages::AsArgs,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-            PartitionSelection::Balanced,
-        ),
-        (
-            ProvideMessages::ViaStdin,
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            PartitionSelection::Balanced,
-        ),
-        (
-            ProvideMessages::AsArgs,
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-            PartitionSelection::Balanced,
-        ),
-        (
-            ProvideMessages::ViaStdin,
-            TestStreamId::Named,
-            TestTopicId::Named,
-            PartitionSelection::Balanced,
-        ),
-        (
-            ProvideMessages::ViaStdin,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
-            PartitionSelection::Id(1),
-        ),
-        (
-            ProvideMessages::AsArgs,
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-            PartitionSelection::Id(2),
-        ),
-        (
-            ProvideMessages::AsArgs,
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            PartitionSelection::Id(3),
-        ),
-        (
-            ProvideMessages::ViaStdin,
-            TestStreamId::Named,
-            TestTopicId::Named,
-            PartitionSelection::Id(4),
-        ),
-        (
-            ProvideMessages::AsArgs,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
             PartitionSelection::Key(String::from("some-complex-key")),
         ),
         (
             ProvideMessages::ViaStdin,
-            TestStreamId::Named,
-            TestTopicId::Numeric,
-            PartitionSelection::Key(String::from("another random value")),
-        ),
-        (
-            ProvideMessages::ViaStdin,
-            TestStreamId::Numeric,
-            TestTopicId::Named,
-            PartitionSelection::Key(String::from("some-random-key")),
-        ),
-        (
-            ProvideMessages::AsArgs,
-            TestStreamId::Named,
-            TestTopicId::Named,
-            PartitionSelection::Key(String::from("just a key")),
+            PartitionSelection::Key(String::from("another-key")),
         ),
     ];
 
     iggy_cmd_test.setup().await;
-    for (message_input, using_stream_id, using_topic_id, using_partitioning) in test_parameters {
+    for (message_input, using_partitioning) in test_parameters {
         iggy_cmd_test
             .execute_test(TestMessageSendCmd::new(
-                1,
                 String::from("stream"),
-                2,
                 String::from("topic"),
                 4,
                 vec![
@@ -359,8 +337,6 @@ pub async fn should_be_successful() {
                     String::from("test message3"),
                 ],
                 message_input,
-                using_stream_id,
-                using_topic_id,
                 using_partitioning,
                 Some(HashMap::from([
                     (
diff --git a/core/integration/tests/cli/message/test_message_send_from_file_command.rs b/core/integration/tests/cli/message/test_message_send_from_file_command.rs
index 19cf4c556..1089bd7ff 100644
--- a/core/integration/tests/cli/message/test_message_send_from_file_command.rs
+++ b/core/integration/tests/cli/message/test_message_send_from_file_command.rs
@@ -35,6 +35,9 @@ pub(super) struct TestMessageSendFromFileCmd<'a> {
     messages: Vec<&'a str>,
     message_count: usize,
     headers: HashMap<HeaderKey, HeaderValue>,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl<'a> TestMessageSendFromFileCmd<'a> {
@@ -57,19 +60,33 @@ impl<'a> TestMessageSendFromFileCmd<'a> {
             messages: messages.to_owned(),
             message_count,
             headers,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
     fn to_args(&self) -> Vec<String> {
-        let command = vec![
+        let mut command = vec![
             "--input-file".into(),
             self.input_file.clone(),
             "--partition-id".into(),
-            "1".into(),
-            self.stream_name.clone(),
-            self.topic_name.clone(),
+            "0".into(),
         ];
 
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
+
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
+
         command
     }
 }
@@ -77,29 +94,27 @@ impl<'a> TestMessageSendFromFileCmd<'a> {
 #[async_trait]
 impl IggyCmdTestCase for TestMessageSendFromFileCmd<'_> {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client.create_stream(&self.stream_name, None).await;
-        assert!(stream.is_ok());
-
-        let stream_id = Identifier::from_str(self.stream_name.as_str());
-        assert!(stream_id.is_ok());
-        let stream_id = stream_id.unwrap();
+        // Create stream and capture its actual ID
+        let stream = client
+            .create_stream(&self.stream_name)
+            .await
+            .expect("Failed to create stream");
+        self.actual_stream_id = Some(stream.id);
 
+        // Create topic and capture its actual ID
         let topic = client
             .create_topic(
-                &stream_id,
+                &stream.id.try_into().unwrap(),
                 &self.topic_name,
                 1,
                 Default::default(),
                 None,
-                None,
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
-            .await;
-        assert!(topic.is_ok());
-
-        let topic_id = Identifier::from_str(self.topic_name.as_str());
-        assert!(topic_id.is_ok());
+            .await
+            .expect("Failed to create topic");
+        self.actual_topic_id = Some(topic.id);
 
         if self.initialize {
             let file = tokio::fs::OpenOptions::new()
@@ -154,9 +169,21 @@ impl IggyCmdTestCase for TestMessageSendFromFileCmd<'_> {
     }
 
     fn verify_command(&self, command_state: Assert) {
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
+        };
+
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
+        };
+
         let message_prefix = format!(
             "Executing send messages to topic with ID: {} and stream with ID: {}\n",
-            self.topic_name, self.stream_name
+            topic_id, stream_id
         );
         let message_read = format!("Read [0-9]+ bytes from {} file", self.input_file);
         let message_created = format!(
@@ -168,7 +195,7 @@ impl IggyCmdTestCase for TestMessageSendFromFileCmd<'_> {
         );
         let message_sent = format!(
             "Sent messages to topic with ID: {} and stream with ID: {}\n",
-            self.topic_name, self.stream_name
+            topic_id, stream_id
         );
 
         command_state
@@ -180,50 +207,49 @@ impl IggyCmdTestCase for TestMessageSendFromFileCmd<'_> {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let stream_id = Identifier::from_str(self.stream_name.as_str());
-        assert!(stream_id.is_ok());
-        let stream_id = stream_id.unwrap();
+        if let (Some(stream_id), Some(topic_id)) = (self.actual_stream_id, self.actual_topic_id) {
+            let messages = client
+                .poll_messages(
+                    &stream_id.try_into().unwrap(),
+                    &topic_id.try_into().unwrap(),
+                    Some(0),
+                    &Consumer::new(Identifier::default()),
+                    &PollingStrategy::offset(0),
+                    self.message_count as u32 * 2,
+                    true,
+                )
+                .await;
+            assert!(messages.is_ok());
+            let messages = messages.unwrap();
 
-        let topic_id = Identifier::from_str(self.topic_name.as_str());
-        assert!(topic_id.is_ok());
-        let topic_id = topic_id.unwrap();
+            // Check if there are only the expected number of messages
+            assert_eq!(messages.messages.len(), self.message_count);
 
-        let messages = client
-            .poll_messages(
-                &stream_id,
-                &topic_id,
-                Some(1),
-                &Consumer::new(Identifier::default()),
-                &PollingStrategy::offset(0),
-                self.message_count as u32 * 2,
-                true,
-            )
-            .await;
-        assert!(messages.is_ok());
-        let messages = messages.unwrap();
+            // Check message order and content (payload and headers)
+            for (i, message) in messages.messages.iter().enumerate() {
+                assert_eq!(
+                    message.payload,
+                    Bytes::from(self.messages[i].as_bytes().to_vec())
+                );
+                assert_eq!(
+                    message.user_headers_map().unwrap().is_some(),
+                    !self.headers.is_empty()
+                );
+                assert_eq!(&message.user_headers_map().unwrap().unwrap(), &self.headers);
+            }
 
-        // Check if there are only the expected number of messages
-        assert_eq!(messages.messages.len(), self.message_count);
+            let topic_delete = client
+                .delete_topic(
+                    &stream_id.try_into().unwrap(),
+                    &topic_id.try_into().unwrap(),
+                )
+                .await;
+            assert!(topic_delete.is_ok());
 
-        // Check message order and content (payload and headers)
-        for (i, message) in messages.messages.iter().enumerate() {
-            assert_eq!(
-                message.payload,
-                Bytes::from(self.messages[i].as_bytes().to_vec())
-            );
-            assert_eq!(
-                message.user_headers_map().unwrap().is_some(),
-                !self.headers.is_empty()
-            );
-            assert_eq!(&message.user_headers_map().unwrap().unwrap(), &self.headers);
+            let stream_delete = client.delete_stream(&stream_id.try_into().unwrap()).await;
+            assert!(stream_delete.is_ok());
         }
 
-        let topic_delete = client.delete_topic(&stream_id, &topic_id).await;
-        assert!(topic_delete.is_ok());
-
-        let stream_delete = client.delete_stream(&stream_id).await;
-        assert!(stream_delete.is_ok());
-
         let file_removal = std::fs::remove_file(&self.input_file);
         assert!(file_removal.is_ok());
     }
diff --git a/core/integration/tests/cli/partition/test_partition_create_command.rs b/core/integration/tests/cli/partition/test_partition_create_command.rs
index e1f0b191a..d1ced0b78 100644
--- a/core/integration/tests/cli/partition/test_partition_create_command.rs
+++ b/core/integration/tests/cli/partition/test_partition_create_command.rs
@@ -17,8 +17,7 @@
  */
 
 use crate::cli::common::{
-    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, TestStreamId,
-    TestTopicId, USAGE_PREFIX,
+    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -30,53 +29,51 @@ use predicates::str::diff;
 use serial_test::parallel;
 
 struct TestPartitionCreateCmd {
-    stream_id: u32,
     stream_name: String,
-    topic_id: u32,
     topic_name: String,
     partitions_count: u32,
     compression_algorithm: CompressionAlgorithm,
     new_partitions: u32,
-    using_stream_id: TestStreamId,
-    using_topic_id: TestTopicId,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl TestPartitionCreateCmd {
-    #[allow(clippy::too_many_arguments)]
     fn new(
-        stream_id: u32,
         stream_name: String,
-        topic_id: u32,
         topic_name: String,
         partitions_count: u32,
         compression_algorithm: CompressionAlgorithm,
         new_partitions: u32,
-        using_stream_id: TestStreamId,
-        using_topic_id: TestTopicId,
     ) -> Self {
         Self {
-            stream_id,
             stream_name,
-            topic_id,
             topic_name,
             partitions_count,
             compression_algorithm,
             new_partitions,
-            using_stream_id,
-            using_topic_id,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
     fn to_args(&self) -> Vec<String> {
-        let mut command = match self.using_stream_id {
-            TestStreamId::Numeric => vec![format!("{}", self.stream_id)],
-            TestStreamId::Named => vec![self.stream_name.clone()],
-        };
+        let mut command = vec![];
+
+        // Use actual stream ID if available, otherwise use stream name as fallback
+        if let Some(stream_id) = self.actual_stream_id {
+            command.push(format!("{}", stream_id));
+        } else {
+            command.push(self.stream_name.clone());
+        }
 
-        command.push(match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        });
+        // Use actual topic ID if available, otherwise use topic name as fallback
+        if let Some(topic_id) = self.actual_topic_id {
+            command.push(format!("{}", topic_id));
+        } else {
+            command.push(self.topic_name.clone());
+        }
 
         command.push(format!("{}", self.new_partitions));
 
@@ -87,24 +84,23 @@ impl TestPartitionCreateCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestPartitionCreateCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client
-            .create_stream(&self.stream_name, self.stream_id.into())
-            .await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
+        self.actual_stream_id = Some(stream.unwrap().id);
 
         let topic = client
             .create_topic(
-                &self.stream_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
                 &self.topic_name,
                 self.partitions_count,
                 self.compression_algorithm,
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
+        self.actual_topic_id = Some(topic.unwrap().id);
     }
 
     fn get_command(&self) -> IggyCmdCommand {
@@ -116,14 +112,16 @@ impl IggyCmdTestCase for TestPartitionCreateCmd {
     }
 
     fn verify_command(&self, command_state: Assert) {
-        let stream_id = match self.using_stream_id {
-            TestStreamId::Numeric => format!("{}", self.stream_id),
-            TestStreamId::Named => self.stream_name.clone(),
+        let stream_id = if let Some(stream_id) = self.actual_stream_id {
+            format!("{}", stream_id)
+        } else {
+            self.stream_name.clone()
         };
 
-        let topic_id = match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
+        let topic_id = if let Some(topic_id) = self.actual_topic_id {
+            format!("{}", topic_id)
+        } else {
+            self.topic_name.clone()
         };
 
         let mut partitions = String::from("partition");
@@ -142,14 +140,14 @@ impl IggyCmdTestCase for TestPartitionCreateCmd {
     async fn verify_server_state(&self, client: &dyn Client) {
         let topic = client
             .get_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
+                &self.actual_topic_id.unwrap().try_into().unwrap(),
             )
             .await;
         assert!(topic.is_ok());
         let topic_details = topic.unwrap().expect("Failed to get topic");
         assert_eq!(topic_details.name, self.topic_name);
-        assert_eq!(topic_details.id, self.topic_id);
+        assert_eq!(topic_details.id, self.actual_topic_id.unwrap());
         assert_eq!(
             topic_details.partitions_count,
             self.partitions_count + self.new_partitions
@@ -158,14 +156,14 @@ impl IggyCmdTestCase for TestPartitionCreateCmd {
 
         let topic = client
             .delete_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
+                &self.actual_topic_id.unwrap().try_into().unwrap(),
             )
             .await;
         assert!(topic.is_ok());
 
         let stream = client
-            .delete_stream(&self.stream_id.try_into().unwrap())
+            .delete_stream(&self.actual_stream_id.unwrap().try_into().unwrap())
             .await;
         assert!(stream.is_ok());
     }
@@ -179,54 +177,38 @@ pub async fn should_be_successful() {
     iggy_cmd_test.setup().await;
     iggy_cmd_test
         .execute_test(TestPartitionCreateCmd::new(
-            1,
             String::from("main"),
-            1,
             String::from("sync"),
             1,
             Default::default(),
             1,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestPartitionCreateCmd::new(
-            2,
             String::from("stream"),
-            3,
             String::from("topic"),
             3,
             Default::default(),
             2,
-            TestStreamId::Named,
-            TestTopicId::Numeric,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestPartitionCreateCmd::new(
-            4,
             String::from("development"),
-            1,
             String::from("probe"),
             0,
             Default::default(),
             4,
-            TestStreamId::Numeric,
-            TestTopicId::Named,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestPartitionCreateCmd::new(
-            2,
             String::from("production"),
-            5,
             String::from("test"),
             4,
             Default::default(),
             1,
-            TestStreamId::Named,
-            TestTopicId::Named,
         ))
         .await;
 }
diff --git a/core/integration/tests/cli/partition/test_partition_delete_command.rs b/core/integration/tests/cli/partition/test_partition_delete_command.rs
index 0b789aee0..8e726cd84 100644
--- a/core/integration/tests/cli/partition/test_partition_delete_command.rs
+++ b/core/integration/tests/cli/partition/test_partition_delete_command.rs
@@ -17,8 +17,7 @@
  */
 
 use crate::cli::common::{
-    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, TestStreamId,
-    TestTopicId, USAGE_PREFIX,
+    CLAP_INDENT, IggyCmdCommand, IggyCmdTest, IggyCmdTestCase, TestHelpCmd, USAGE_PREFIX,
 };
 use assert_cmd::assert::Assert;
 use async_trait::async_trait;
@@ -29,50 +28,40 @@ use predicates::str::diff;
 use serial_test::parallel;
 
 struct TestPartitionDeleteCmd {
-    stream_id: u32,
     stream_name: String,
-    topic_id: u32,
     topic_name: String,
     partitions_count: u32,
     new_partitions: u32,
-    using_stream_id: TestStreamId,
-    using_topic_id: TestTopicId,
+    // These will be populated after creating the resources
+    actual_stream_id: Option<u32>,
+    actual_topic_id: Option<u32>,
 }
 
 impl TestPartitionDeleteCmd {
-    #[allow(clippy::too_many_arguments)]
     fn new(
-        stream_id: u32,
         stream_name: String,
-        topic_id: u32,
         topic_name: String,
         partitions_count: u32,
         new_partitions: u32,
-        using_stream_id: TestStreamId,
-        using_topic_id: TestTopicId,
     ) -> Self {
         Self {
-            stream_id,
             stream_name,
-            topic_id,
             topic_name,
             partitions_count,
             new_partitions,
-            using_stream_id,
-            using_topic_id,
+            actual_stream_id: None,
+            actual_topic_id: None,
         }
     }
 
     fn to_args(&self) -> Vec<String> {
-        let mut command = match self.using_stream_id {
-            TestStreamId::Numeric => vec![format!("{}", self.stream_id)],
-            TestStreamId::Named => vec![self.stream_name.clone()],
-        };
+        let mut command = vec![];
+
+        // Always use stream name for consistency with other tests
+        command.push(self.stream_name.clone());
 
-        command.push(match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        });
+        // Always use topic name for consistency with other tests
+        command.push(self.topic_name.clone());
 
         command.push(format!("{}", self.new_partitions));
 
@@ -83,24 +72,23 @@ impl TestPartitionDeleteCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestPartitionDeleteCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client
-            .create_stream(&self.stream_name, self.stream_id.into())
-            .await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
+        self.actual_stream_id = Some(stream.unwrap().id);
 
         let topic = client
             .create_topic(
-                &self.stream_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
                 &self.topic_name,
                 self.partitions_count,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
             .await;
         assert!(topic.is_ok());
+        self.actual_topic_id = Some(topic.unwrap().id);
     }
 
     fn get_command(&self) -> IggyCmdCommand {
@@ -112,16 +100,6 @@ impl IggyCmdTestCase for TestPartitionDeleteCmd {
     }
 
     fn verify_command(&self, command_state: Assert) {
-        let stream_id = match self.using_stream_id {
-            TestStreamId::Numeric => format!("{}", self.stream_id),
-            TestStreamId::Named => self.stream_name.clone(),
-        };
-
-        let topic_id = match self.using_topic_id {
-            TestTopicId::Numeric => format!("{}", self.topic_id),
-            TestTopicId::Named => self.topic_name.clone(),
-        };
-
         let mut partitions = String::from("partition");
         if self.new_partitions > 1 {
             partitions.push('s');
@@ -129,7 +107,12 @@ impl IggyCmdTestCase for TestPartitionDeleteCmd {
 
         let message = format!(
             "Executing delete {} {partitions} for topic with ID: {} and stream with ID: {}\nDeleted {} {partitions} for topic with ID: {} and stream with ID: {}\n",
-            self.new_partitions, topic_id, stream_id, self.new_partitions, topic_id, stream_id
+            self.new_partitions,
+            self.topic_name,
+            self.stream_name,
+            self.new_partitions,
+            self.topic_name,
+            self.stream_name
         );
 
         command_state.success().stdout(diff(message));
@@ -138,34 +121,30 @@ impl IggyCmdTestCase for TestPartitionDeleteCmd {
     async fn verify_server_state(&self, client: &dyn Client) {
         let topic = client
             .get_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
+                &self.actual_topic_id.unwrap().try_into().unwrap(),
             )
             .await;
         assert!(topic.is_ok());
         let topic_details = topic.unwrap().expect("Failed to get topic");
         assert_eq!(topic_details.name, self.topic_name);
-        assert_eq!(topic_details.id, self.topic_id);
-        if self.new_partitions > self.partitions_count {
-            assert_eq!(topic_details.partitions_count, 0);
-        } else {
-            assert_eq!(
-                topic_details.partitions_count,
-                self.partitions_count - self.new_partitions
-            );
-        }
+        assert_eq!(topic_details.id, self.actual_topic_id.unwrap());
+        assert_eq!(
+            topic_details.partitions_count,
+            self.partitions_count - self.new_partitions
+        );
         assert_eq!(topic_details.messages_count, 0);
 
         let topic = client
             .delete_topic(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &self.actual_stream_id.unwrap().try_into().unwrap(),
+                &self.actual_topic_id.unwrap().try_into().unwrap(),
             )
             .await;
         assert!(topic.is_ok());
 
         let stream = client
-            .delete_stream(&self.stream_id.try_into().unwrap())
+            .delete_stream(&self.actual_stream_id.unwrap().try_into().unwrap())
             .await;
         assert!(stream.is_ok());
     }
@@ -179,50 +158,34 @@ pub async fn should_be_successful() {
     iggy_cmd_test.setup().await;
     iggy_cmd_test
         .execute_test(TestPartitionDeleteCmd::new(
-            1,
             String::from("main"),
-            1,
             String::from("sync"),
             3,
             1,
-            TestStreamId::Numeric,
-            TestTopicId::Numeric,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestPartitionDeleteCmd::new(
-            2,
             String::from("stream"),
-            3,
             String::from("topic"),
             3,
             2,
-            TestStreamId::Named,
-            TestTopicId::Numeric,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestPartitionDeleteCmd::new(
-            4,
             String::from("development"),
-            1,
             String::from("probe"),
             3,
             3,
-            TestStreamId::Numeric,
-            TestTopicId::Named,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestPartitionDeleteCmd::new(
-            2,
             String::from("production"),
-            5,
             String::from("test"),
+            5,
             3,
-            7,
-            TestStreamId::Named,
-            TestTopicId::Named,
         ))
         .await;
 }
diff --git a/core/integration/tests/cli/stream/test_stream_create_command.rs b/core/integration/tests/cli/stream/test_stream_create_command.rs
index 70e18f02f..b657863b6 100644
--- a/core/integration/tests/cli/stream/test_stream_create_command.rs
+++ b/core/integration/tests/cli/stream/test_stream_create_command.rs
@@ -25,12 +25,15 @@ use serial_test::parallel;
 
 struct TestStreamCreateCmd {
     stream_id: Option<u32>,
-    name: String,
+    stream_name: String,
 }
 
 impl TestStreamCreateCmd {
     fn new(stream_id: Option<u32>, name: String) -> Self {
-        Self { stream_id, name }
+        Self {
+            stream_id,
+            stream_name: name,
+        }
     }
 
     fn to_args(&self) -> Vec<String> {
@@ -41,7 +44,7 @@ impl TestStreamCreateCmd {
             args.push(format!("{stream_id}"));
         }
 
-        args.push(self.name.clone());
+        args.push(self.stream_name.clone());
 
         args
     }
@@ -60,14 +63,11 @@ impl IggyCmdTestCase for TestStreamCreateCmd {
     }
 
     fn verify_command(&self, command_state: Assert) {
-        let stream_id = match self.stream_id {
-            Some(stream_id) => format!("ID: {stream_id}"),
-            None => "ID auto incremented".to_string(),
-        };
+        let stream_id = "ID auto incremented";
 
         let message = format!(
             "Executing create stream with name: {} and {}\nStream with name: {} and {} created\n",
-            self.name, stream_id, self.name, stream_id
+            self.stream_name, stream_id, self.stream_name, stream_id
         );
 
         command_state.success().stdout(diff(message));
@@ -75,17 +75,14 @@ impl IggyCmdTestCase for TestStreamCreateCmd {
 
     async fn verify_server_state(&self, client: &dyn Client) {
         let stream = client
-            .get_stream(&self.name.clone().try_into().unwrap())
+            .get_stream(&self.stream_name.clone().try_into().unwrap())
             .await;
         assert!(stream.is_ok());
         let stream = stream.unwrap().expect("Stream not found");
-        assert_eq!(stream.name, self.name);
-        if let Some(stream_id) = self.stream_id {
-            assert_eq!(stream.id, stream_id);
-        }
+        assert_eq!(stream.name, self.stream_name);
 
         let delete = client
-            .delete_stream(&self.name.clone().try_into().unwrap())
+            .delete_stream(&self.stream_name.clone().try_into().unwrap())
             .await;
         assert!(delete.is_ok());
     }
@@ -98,7 +95,7 @@ pub async fn should_be_successful() {
 
     iggy_cmd_test.setup().await;
     iggy_cmd_test
-        .execute_test(TestStreamCreateCmd::new(Some(123), String::from("main")))
+        .execute_test(TestStreamCreateCmd::new(None, String::from("main")))
         .await;
 
     iggy_cmd_test.setup().await;
diff --git a/core/integration/tests/cli/stream/test_stream_delete_command.rs b/core/integration/tests/cli/stream/test_stream_delete_command.rs
index fa0fe6a6f..aa548aeb1 100644
--- a/core/integration/tests/cli/stream/test_stream_delete_command.rs
+++ b/core/integration/tests/cli/stream/test_stream_delete_command.rs
@@ -28,7 +28,7 @@ use serial_test::parallel;
 
 struct TestStreamDeleteCmd {
     stream_id: u32,
-    name: String,
+    stream_name: String,
     using_identifier: TestStreamId,
 }
 
@@ -36,14 +36,14 @@ impl TestStreamDeleteCmd {
     fn new(stream_id: u32, name: String, using_identifier: TestStreamId) -> Self {
         Self {
             stream_id,
-            name,
+            stream_name: name,
             using_identifier,
         }
     }
 
     fn to_arg(&self) -> String {
         match self.using_identifier {
-            TestStreamId::Named => self.name.clone(),
+            TestStreamId::Named => self.stream_name.clone(),
             TestStreamId::Numeric => format!("{}", self.stream_id),
         }
     }
@@ -52,7 +52,7 @@ impl TestStreamDeleteCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestStreamDeleteCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client.create_stream(&self.name, Some(self.stream_id)).await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
     }
 
@@ -68,7 +68,7 @@ impl IggyCmdTestCase for TestStreamDeleteCmd {
         let message = match self.using_identifier {
             TestStreamId::Named => format!(
                 "Executing delete stream with ID: {}\nStream with ID: {} deleted\n",
-                self.name, self.name
+                self.stream_name, self.stream_name
             ),
             TestStreamId::Numeric => format!(
                 "Executing delete stream with ID: {}\nStream with ID: {} deleted\n",
@@ -95,14 +95,14 @@ pub async fn should_be_successful() {
     iggy_cmd_test.setup().await;
     iggy_cmd_test
         .execute_test(TestStreamDeleteCmd::new(
-            1,
+            0,
             String::from("testing"),
-            TestStreamId::Numeric,
+            TestStreamId::Named,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestStreamDeleteCmd::new(
-            2,
+            0,
             String::from("production"),
             TestStreamId::Named,
         ))
diff --git a/core/integration/tests/cli/stream/test_stream_get_command.rs b/core/integration/tests/cli/stream/test_stream_get_command.rs
index 9b8c9511a..a46543ef6 100644
--- a/core/integration/tests/cli/stream/test_stream_get_command.rs
+++ b/core/integration/tests/cli/stream/test_stream_get_command.rs
@@ -28,7 +28,7 @@ use serial_test::parallel;
 
 struct TestStreamGetCmd {
     stream_id: u32,
-    name: String,
+    stream_name: String,
     using_identifier: TestStreamId,
 }
 
@@ -36,14 +36,14 @@ impl TestStreamGetCmd {
     fn new(stream_id: u32, name: String, using_identifier: TestStreamId) -> Self {
         Self {
             stream_id,
-            name,
+            stream_name: name,
             using_identifier,
         }
     }
 
     fn to_arg(&self) -> String {
         match self.using_identifier {
-            TestStreamId::Named => self.name.clone(),
+            TestStreamId::Named => self.stream_name.clone(),
             TestStreamId::Numeric => format!("{}", self.stream_id),
         }
     }
@@ -52,7 +52,7 @@ impl TestStreamGetCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestStreamGetCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
-        let stream = client.create_stream(&self.name, Some(self.stream_id)).await;
+        let stream = client.create_stream(&self.stream_name).await;
         assert!(stream.is_ok());
     }
 
@@ -66,7 +66,10 @@ impl IggyCmdTestCase for TestStreamGetCmd {
 
     fn verify_command(&self, command_state: Assert) {
         let start_message = match self.using_identifier {
-            TestStreamId::Named => format!("Executing get stream with ID: {}\n", self.name.clone()),
+            TestStreamId::Named => format!(
+                "Executing get stream with ID: {}\n",
+                self.stream_name.clone()
+            ),
             TestStreamId::Numeric => format!("Executing get stream with ID: {}\n", self.stream_id),
         };
 
@@ -74,10 +77,9 @@ impl IggyCmdTestCase for TestStreamGetCmd {
             .success()
             .stdout(starts_with(start_message))
             .stdout(contains(format!(
-                "Stream ID            | {}",
-                self.stream_id
+                "Stream name          | {}",
+                self.stream_name
             )))
-            .stdout(contains(format!("Stream name          | {}", self.name)))
             .stdout(contains("Stream size          | 0"))
             .stdout(contains("Stream message count | 0"))
             .stdout(contains("Stream topics count  | 0"));
@@ -94,16 +96,16 @@ pub async fn should_be_successful() {
     iggy_cmd_test.setup().await;
     iggy_cmd_test
         .execute_test(TestStreamGetCmd::new(
-            1,
+            0,
             String::from("production"),
             TestStreamId::Named,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestStreamGetCmd::new(
-            2,
+            0,
             String::from("testing"),
-            TestStreamId::Numeric,
+            TestStreamId::Named,
         ))
         .await;
 }
diff --git a/core/integration/tests/cli/stream/test_stream_list_command.rs b/core/integration/tests/cli/stream/test_stream_list_command.rs
index 3c4d4db57..610e03841 100644
--- a/core/integration/tests/cli/stream/test_stream_list_command.rs
+++ b/core/integration/tests/cli/stream/test_stream_list_command.rs
@@ -27,18 +27,13 @@ use predicates::str::{contains, starts_with};
 use serial_test::parallel;
 
 struct TestStreamListCmd {
... 55054 lines suppressed ...