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

(iggy) branch io_uring_final updated (f4ef2aaed -> 99a55e97f)

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

gkoszyk pushed a change to branch io_uring_final
in repository https://gitbox.apache.org/repos/asf/iggy.git


    omit f4ef2aaed feat(io_uring): implement thread per core io_uring
     new 99a55e97f feat(io_uring): implement thread per core io_uring

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (f4ef2aaed)
            \
             N -- N -- N   refs/heads/io_uring_final (99a55e97f)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../BasicMessagingOperationsSteps.cs               | 39 +++++-----
 .../PersonalAccessTokenTests.cs                    |  2 +-
 .../Iggy_SDK.Tests.Integration/SystemTests.cs      |  4 +-
 .../Iggy_SDK.Tests.Integration/UsersTests.cs       |  2 +-
 .../csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs  | 89 +++++++++++++++-------
 .../IggyClient/Implementations/TcpMessageStream.cs | 41 ++++++++--
 foreign/node/src/bdd/auth.ts                       |  2 +-
 foreign/node/src/bdd/message.ts                    |  8 +-
 foreign/node/src/bdd/stream.ts                     | 11 ++-
 foreign/node/src/bdd/topic.ts                      | 10 +--
 foreign/node/src/client/client.connection.ts       | 76 +++++++++---------
 foreign/node/src/e2e/tcp.system.e2e.ts             |  2 +-
 .../wire/consumer-group/create-group.command.ts    | 10 +--
 foreign/node/src/wire/offset/offset.utils.ts       | 15 +++-
 .../node/src/wire/stream/create-stream.command.ts  | 10 +--
 .../node/src/wire/topic/create-topic.command.ts    | 19 +++--
 16 files changed, 201 insertions(+), 139 deletions(-)


(iggy) 01/01: feat(io_uring): implement thread per core io_uring

Posted by gk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 99a55e97fab254050a476927384c08e6bc3ede20
Author: numminex <g....@gmail.com>
AuthorDate: Tue Oct 28 12:02:18 2025 +0100

    feat(io_uring): implement thread per core io_uring
---
 Cargo.lock                                         | 1115 +++++++--------
 Cargo.toml                                         |   25 +-
 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              |   14 +-
 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                             |   31 +-
 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                    |   11 +-
 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 +-
 .../BasicMessagingOperationsSteps.cs               |   39 +-
 .../PersonalAccessTokenTests.cs                    |    2 +-
 .../Iggy_SDK.Tests.Integration/SystemTests.cs      |    4 +-
 .../Iggy_SDK.Tests.Integration/UsersTests.cs       |    2 +-
 .../csharp/Iggy_SDK/Contracts/Tcp/TcpContracts.cs  |   89 +-
 .../IggyClient/Implementations/TcpMessageStream.cs |   41 +-
 foreign/node/src/bdd/auth.ts                       |    2 +-
 foreign/node/src/bdd/message.ts                    |    8 +-
 foreign/node/src/bdd/stream.ts                     |   11 +-
 foreign/node/src/bdd/topic.ts                      |   10 +-
 foreign/node/src/client/client.connection.ts       |   76 +-
 foreign/node/src/e2e/tcp.system.e2e.ts             |    2 +-
 .../wire/consumer-group/create-group.command.ts    |   10 +-
 foreign/node/src/wire/offset/offset.utils.ts       |   15 +-
 .../node/src/wire/stream/create-stream.command.ts  |   10 +-
 .../node/src/wire/topic/create-topic.command.ts    |   19 +-
 justfile                                           |    3 -
 507 files changed, 26749 insertions(+), 19618 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 1179da095..558987bb7 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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"
@@ -457,6 +463,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 +571,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 +636,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,49 +665,14 @@ 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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
-dependencies = [
- "async-trait",
- "axum-core 0.4.5",
- "bytes",
- "futures-util",
- "http 1.3.1",
- "http-body",
- "http-body-util",
- "itoa",
- "matchit 0.7.3",
- "memchr",
- "mime",
- "percent-encoding",
- "pin-project-lite",
- "rustversion",
- "serde",
- "sync_wrapper",
- "tower 0.5.2",
- "tower-layer",
- "tower-service",
-]
-
 [[package]]
 name = "axum"
 version = "0.8.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871"
 dependencies = [
- "axum-core 0.5.5",
+ "axum-core",
+ "axum-macros",
  "bytes",
  "form_urlencoded",
  "futures-util",
@@ -726,7 +682,7 @@ dependencies = [
  "hyper",
  "hyper-util",
  "itoa",
- "matchit 0.8.4",
+ "matchit",
  "memchr",
  "mime",
  "percent-encoding",
@@ -737,7 +693,7 @@ dependencies = [
  "serde_urlencoded",
  "sync_wrapper",
  "tokio",
- "tower 0.5.2",
+ "tower",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -745,41 +701,32 @@ dependencies = [
 
 [[package]]
 name = "axum-core"
-version = "0.4.5"
+version = "0.5.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
+checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
 dependencies = [
- "async-trait",
  "bytes",
- "futures-util",
+ "futures-core",
  "http 1.3.1",
  "http-body",
  "http-body-util",
  "mime",
  "pin-project-lite",
- "rustversion",
  "sync_wrapper",
  "tower-layer",
  "tower-service",
+ "tracing",
 ]
 
 [[package]]
-name = "axum-core"
-version = "0.5.5"
+name = "axum-macros"
+version = "0.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59446ce19cd142f8833f856eb31f3eb097812d1479ab224f54d72428ca21ea22"
+checksum = "604fde5e028fea851ce1d8570bbdc034bec850d157f7569d10f347d06808c05c"
 dependencies = [
- "bytes",
- "futures-core",
- "http 1.3.1",
- "http-body",
- "http-body-util",
- "mime",
- "pin-project-lite",
- "sync_wrapper",
- "tower-layer",
- "tower-service",
- "tracing",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.108",
 ]
 
 [[package]]
@@ -804,12 +751,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"
@@ -1289,15 +1230,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 +1437,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]]
@@ -1559,45 +1671,6 @@ dependencies = [
  "windows-sys 0.59.0",
 ]
 
-[[package]]
-name = "console-api"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8030735ecb0d128428b64cd379809817e620a40e5001c54465b99ec5feec2857"
-dependencies = [
- "futures-core",
- "prost 0.13.5",
- "prost-types 0.13.5",
- "tonic 0.12.3",
- "tracing-core",
-]
-
-[[package]]
-name = "console-subscriber"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6539aa9c6a4cd31f4b1c040f860a1eac9aa80e7df6b05d506a6e7179936d6a01"
-dependencies = [
- "console-api",
- "crossbeam-channel",
- "crossbeam-utils",
- "futures-task",
- "hdrhistogram",
- "humantime",
- "hyper-util",
- "prost 0.13.5",
- "prost-types 0.13.5",
- "serde",
- "serde_json",
- "thread_local",
- "tokio",
- "tokio-stream",
- "tonic 0.12.3",
- "tracing",
- "tracing-core",
- "tracing-subscriber",
-]
-
 [[package]]
 name = "console_error_panic_hook"
 version = "0.1.7"
@@ -1614,26 +1687,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 +1722,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 +1748,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 +1877,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 +1913,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 +1984,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",
+ "axum-core",
+ "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",
+ "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 +2126,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 +2317,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 +2357,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 +2434,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 +2443,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 +2644,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"
@@ -2913,7 +2940,6 @@ checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2"
 dependencies = [
  "typenum",
  "version_check",
- "zeroize",
 ]
 
 [[package]]
@@ -3445,17 +3471,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"
@@ -3529,6 +3544,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"
@@ -3575,19 +3596,6 @@ dependencies = [
  "hashbrown 0.15.5",
 ]
 
-[[package]]
-name = "hdrhistogram"
-version = "7.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "765c9198f173dd59ce26ff9f95ef0aafd0a0fe01fb9d72841bc5066a4c06511d"
-dependencies = [
- "base64 0.21.7",
- "byteorder",
- "flate2",
- "nom",
- "num-traits",
-]
-
 [[package]]
 name = "heapless"
 version = "0.7.17"
@@ -3595,7 +3603,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 +4018,10 @@ dependencies = [
  "serde",
  "tokio",
  "tokio-rustls",
+ "tokio-tungstenite",
  "tracing",
  "trait-variant",
+ "tungstenite",
  "webpki-roots 1.0.3",
 ]
 
@@ -4096,7 +4106,7 @@ dependencies = [
 name = "iggy-connectors"
 version = "0.1.1"
 dependencies = [
- "axum 0.8.6",
+ "axum",
  "axum-server",
  "dashmap",
  "dlopen2",
@@ -4127,7 +4137,7 @@ dependencies = [
 name = "iggy-mcp"
 version = "0.1.1"
 dependencies = [
- "axum 0.8.6",
+ "axum",
  "axum-server",
  "dotenvy",
  "figlet-rs",
@@ -4179,6 +4189,7 @@ dependencies = [
  "chrono",
  "clap",
  "comfy-table",
+ "compio-ws",
  "crc32fast",
  "derive_more 2.0.1",
  "fast-async-mutex",
@@ -4453,6 +4464,7 @@ dependencies = [
  "async-trait",
  "bytes",
  "chrono",
+ "compio",
  "ctor",
  "derive_more 2.0.1",
  "env_logger",
@@ -4490,6 +4502,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"
@@ -4617,29 +4651,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"
@@ -5000,26 +5011,9 @@ dependencies = [
 
 [[package]]
 name = "matchit"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
-
-[[package]]
-name = "matchit"
-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",
-]
+version = "0.8.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3"
 
 [[package]]
 name = "md-5"
@@ -5031,12 +5025,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 +5078,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 +5162,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 +5471,7 @@ dependencies = [
  "hyper-rustls",
  "hyper-timeout",
  "hyper-util",
- "jsonwebtoken 9.3.1",
+ "jsonwebtoken",
  "once_cell",
  "percent-encoding",
  "pin-project",
@@ -5486,7 +5482,7 @@ dependencies = [
  "serde_urlencoded",
  "snafu",
  "tokio",
- "tower 0.5.2",
+ "tower",
  "tower-http",
  "tracing",
  "url",
@@ -5657,7 +5653,6 @@ dependencies = [
  "rand 0.9.2",
  "thiserror 2.0.17",
  "tokio",
- "tokio-stream",
 ]
 
 [[package]]
@@ -5667,37 +5662,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 +5985,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 +6115,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"
@@ -6372,16 +6358,6 @@ dependencies = [
  "winapi",
 ]
 
-[[package]]
-name = "quick-xml"
-version = "0.38.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "42a232e7487fc2ef313d96dde7948e7a3c05101870d8985e4fd8d26aedd27b89"
-dependencies = [
- "memchr",
- "serde",
-]
-
 [[package]]
 name = "quinn"
 version = "0.11.9"
@@ -6700,7 +6676,7 @@ dependencies = [
  "tokio",
  "tokio-rustls",
  "tokio-util",
- "tower 0.5.2",
+ "tower",
  "tower-http",
  "tower-service",
  "url",
@@ -6757,16 +6733,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 +6747,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"
@@ -6816,7 +6788,7 @@ version = "0.8.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1fdad1258f7259fdc0f2dfc266939c82c3b5d1fd72bcde274d600cdc27e60243"
 dependencies = [
- "axum 0.8.6",
+ "axum",
  "base64 0.22.1",
  "bytes",
  "chrono",
@@ -6881,51 +6853,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 +6922,7 @@ dependencies = [
  "openssl-probe",
  "rustls-pki-types",
  "schannel",
- "security-framework",
+ "security-framework 3.5.1",
 ]
 
 [[package]]
@@ -7023,7 +6950,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 +6959,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",
@@ -7062,25 +6989,6 @@ version = "1.0.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"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "826e80413b9a35e9d33217b3dcac04cf95f6559d15944b93887a08be5496c4a4"
-dependencies = [
- "compact_str",
-]
-
 [[package]]
 name = "ryu"
 version = "1.0.20"
@@ -7152,6 +7060,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 +7097,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 +7125,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 +7141,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 +7161,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,17 +7373,26 @@ version = "0.5.0"
 dependencies = [
  "ahash 0.8.12",
  "anyhow",
+ "arcshift",
  "argon2",
+ "async-channel",
  "async_zip",
- "axum 0.8.6",
+ "axum",
  "axum-server",
  "bincode 2.0.1",
  "blake3",
  "bytes",
  "chrono",
  "clap",
- "console-subscriber",
+ "compio",
+ "compio-net",
+ "compio-quic",
+ "compio-tls",
+ "compio-ws",
  "crossbeam",
+ "ctrlc",
+ "cyper",
+ "cyper-axum",
  "dashmap",
  "derive_more 2.0.1",
  "dotenvy",
@@ -7462,12 +7403,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 +7417,32 @@ dependencies = [
  "opentelemetry-otlp",
  "opentelemetry-semantic-conventions",
  "opentelemetry_sdk",
+ "papaya",
  "prometheus-client",
- "quinn",
  "rand 0.9.2",
  "reqwest",
  "ring",
- "rust-s3",
+ "ringbuffer",
  "rustls",
  "rustls-pemfile",
+ "send_wrapper",
  "serde",
  "serde_with",
  "serial_test",
+ "slab",
+ "socket2 0.6.1",
  "static-toml",
  "strum",
  "sysinfo 0.37.2",
  "tempfile",
  "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",
@@ -7939,12 +7881,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"
@@ -8320,15 +8256,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"
@@ -8368,7 +8295,6 @@ dependencies = [
  "signal-hook-registry",
  "socket2 0.6.1",
  "tokio-macros",
- "tracing",
  "windows-sys 0.61.2",
 ]
 
@@ -8419,6 +8345,22 @@ dependencies = [
  "xattr",
 ]
 
+[[package]]
+name = "tokio-tungstenite"
+version = "0.27.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "489a59b6730eda1b0171fcfda8b121f4bee2b35cba8645ca35c5f7ba3eb736c1"
+dependencies = [
+ "futures-util",
+ "log",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tungstenite",
+ "webpki-roots 0.26.11",
+]
+
 [[package]]
 name = "tokio-util"
 version = "0.7.16"
@@ -8536,36 +8478,6 @@ version = "1.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2"
 
-[[package]]
-name = "tonic"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
-dependencies = [
- "async-stream",
- "async-trait",
- "axum 0.7.9",
- "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.4.13",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
 [[package]]
 name = "tonic"
 version = "0.13.1"
@@ -8573,7 +8485,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9"
 dependencies = [
  "async-trait",
- "axum 0.8.6",
+ "axum",
  "base64 0.22.1",
  "bytes",
  "h2 0.4.12",
@@ -8589,7 +8501,7 @@ dependencies = [
  "socket2 0.5.10",
  "tokio",
  "tokio-stream",
- "tower 0.5.2",
+ "tower",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -8615,7 +8527,7 @@ dependencies = [
  "sync_wrapper",
  "tokio",
  "tokio-stream",
- "tower 0.5.2",
+ "tower",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -8645,26 +8557,6 @@ dependencies = [
  "tracing-subscriber",
 ]
 
-[[package]]
-name = "tower"
-version = "0.4.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
-dependencies = [
- "futures-core",
- "futures-util",
- "indexmap 1.9.3",
- "pin-project",
- "pin-project-lite",
- "rand 0.8.5",
- "slab",
- "tokio",
- "tokio-util",
- "tower-layer",
- "tower-service",
- "tracing",
-]
-
 [[package]]
 name = "tower"
 version = "0.5.2"
@@ -8697,7 +8589,7 @@ dependencies = [
  "http-body",
  "iri-string",
  "pin-project-lite",
- "tower 0.5.2",
+ "tower",
  "tower-layer",
  "tower-service",
  "tracing",
@@ -8823,6 +8715,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"
@@ -9007,6 +8918,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"
@@ -9331,6 +9248,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..b1e1f6e27 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -50,6 +50,7 @@ ahash = { version = "0.8.12", features = ["serde"] }
 anyhow = "1.0.100"
 argon2 = "0.5.3"
 async-broadcast = "0.7.2"
+async-channel = "2.3.1"
 async-dropper = { version = "0.3.1", features = ["tokio", "simple"] }
 async-trait = "0.1.89"
 async_zip = { version = "0.0.18", features = [
@@ -60,7 +61,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" }
@@ -82,9 +83,29 @@ chrono = { version = "0.4.42", features = ["serde"] }
 clap = { version = "4.5.50", features = ["derive", "wrap_help"] }
 colored = "3.0.0"
 comfy-table = "7.2.1"
+compio = { git = "https://github.com/compio-rs/compio.git", rev = "fe4243f0b6811ebc325afd081c9b087b4d9817be", features = [
+    "runtime",
+    "macros",
+    "io-uring",
+    "time",
+    "rustls",
+] }
+compio-io = { git = "https://github.com/compio-rs/compio.git", rev = "fe4243f0b6811ebc325afd081c9b087b4d9817be" }
+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-ws = { git = "https://github.com/krishvishal/compio-ws", features = [
+    "rustls",
+] }
 console-subscriber = "0.4.1"
 crc32fast = "1.5.0"
 crossbeam = "0.8.4"
+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" }
 dashmap = "6.1.0"
 derive-new = "0.7.0"
 derive_builder = "0.20.2"
@@ -145,6 +166,7 @@ test-case = "3.3.1"
 thiserror = "2.0.17"
 tokio = { version = "1.48.0", features = ["full"] }
 tokio-rustls = "0.26.4"
+tokio-tungstenite = { version = "0.27", features = ["rustls-tls-webpki-roots"] }
 tokio-util = { version = "0.7.16", features = ["compat"] }
 toml = "0.9.8"
 tower-http = { version = "0.6.6", features = [
@@ -160,6 +182,7 @@ tracing-subscriber = { version = "0.3.20", default-features = false, features =
     "ansi",
 ] }
 trait-variant = "0.1.2"
+tungstenite = "0.27.0"
 twox-hash = { version = "2.1.2", features = ["xxhash32"] }
 uuid = { version = "1.18.1", features = [
     "v4",
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..e31eea599 100644
--- a/bdd/scenarios/basic_messaging.feature
+++ b/bdd/scenarios/basic_messaging.feature
@@ -26,20 +26,20 @@ 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
-    And the last polled message should match the last sent message
\ No newline at end of file
+    And the last polled message should match the last sent message
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..44782d0c8 100644
--- a/core/common/Cargo.toml
+++ b/core/common/Cargo.toml
@@ -42,6 +42,7 @@ bytes = { workspace = true }
 chrono = { workspace = true }
 clap = { workspace = true }
 comfy-table = { workspace = true }
+compio-ws = { workspace = true }
 crc32fast = { workspace = true }
 derive_more = { workspace = true }
 fast-async-mutex = { version = "0.6.7", optional = 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 {
-    stream_id: u32,
     name: String,
     output: OutputFormat,
 }
 
 impl TestStreamListCmd {
-    fn new(stream_id: u32, name: String, output: OutputFormat) -> Self {
-        Self {
-            stream_id,
-            name,
-            output,
-        }
+    fn new(name: String, output: OutputFormat) -> Self {
+        Self { name, output }
     }
 
     fn to_args(&self) -> Vec<&str> {
@@ -49,7 +44,7 @@ impl TestStreamListCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestStreamListCmd {
     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.name).await;
         assert!(stream.is_ok());
     }
 
@@ -82,21 +77,18 @@ pub async fn should_be_successful() {
     iggy_cmd_test.setup().await;
     iggy_cmd_test
         .execute_test(TestStreamListCmd::new(
-            1,
             String::from("prod"),
             OutputFormat::Default,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestStreamListCmd::new(
-            2,
             String::from("testing"),
             OutputFormat::List,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestStreamListCmd::new(
-            3,
             String::from("misc"),
             OutputFormat::Table,
         ))
diff --git a/core/integration/tests/cli/stream/test_stream_purge_command.rs b/core/integration/tests/cli/stream/test_stream_purge_command.rs
index c24edff9e..571f5add7 100644
--- a/core/integration/tests/cli/stream/test_stream_purge_command.rs
+++ b/core/integration/tests/cli/stream/test_stream_purge_command.rs
@@ -31,7 +31,6 @@ struct TestStreamPurgeCmd {
     stream_id: u32,
     stream_name: String,
     using_identifier: TestStreamId,
-    topic_id: u32,
     topic_name: String,
 }
 
@@ -41,7 +40,6 @@ impl TestStreamPurgeCmd {
             stream_id,
             stream_name: name,
             using_identifier,
-            topic_id: 1,
             topic_name: String::from("test_topic"),
         }
     }
@@ -57,19 +55,16 @@ impl TestStreamPurgeCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestStreamPurgeCmd {
     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 topic = client
             .create_topic(
-                &self.stream_id.try_into().unwrap(),
+                &self.stream_name.clone().try_into().unwrap(),
                 &self.topic_name,
                 10,
                 Default::default(),
                 None,
-                Some(self.topic_id),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
@@ -83,15 +78,17 @@ impl IggyCmdTestCase for TestStreamPurgeCmd {
 
         let send_status = client
             .send_messages(
-                &self.stream_id.try_into().unwrap(),
-                &self.topic_id.try_into().unwrap(),
+                &self.stream_name.clone().try_into().unwrap(),
+                &self.topic_name.clone().try_into().unwrap(),
                 &Partitioning::default(),
                 &mut messages,
             )
             .await;
         assert!(send_status.is_ok());
 
-        let stream_state = client.get_stream(&self.stream_id.try_into().unwrap()).await;
+        let stream_state = client
+            .get_stream(&self.stream_name.clone().try_into().unwrap())
+            .await;
         assert!(stream_state.is_ok());
         let stream_state = stream_state.unwrap().expect("Stream not found");
         assert!(stream_state.size > 0);
@@ -119,13 +116,15 @@ impl IggyCmdTestCase for TestStreamPurgeCmd {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let stream_state = client.get_stream(&self.stream_id.try_into().unwrap()).await;
+        let stream_state = client
+            .get_stream(&self.stream_name.clone().try_into().unwrap())
+            .await;
         assert!(stream_state.is_ok());
         let stream_state = stream_state.unwrap().expect("Stream not found");
         assert_eq!(stream_state.size, 0);
 
         let stream_delete = client
-            .delete_stream(&self.stream_id.try_into().unwrap())
+            .delete_stream(&self.stream_name.clone().try_into().unwrap())
             .await;
         assert!(stream_delete.is_ok());
     }
@@ -139,16 +138,16 @@ pub async fn should_be_successful() {
     iggy_cmd_test.setup().await;
     iggy_cmd_test
         .execute_test(TestStreamPurgeCmd::new(
-            1,
+            0,
             String::from("production"),
             TestStreamId::Named,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestStreamPurgeCmd::new(
-            2,
+            1,
             String::from("testing"),
-            TestStreamId::Numeric,
+            TestStreamId::Named,
         ))
         .await;
 }
diff --git a/core/integration/tests/cli/stream/test_stream_update_command.rs b/core/integration/tests/cli/stream/test_stream_update_command.rs
index d7fd95fe7..94eed72e9 100644
--- a/core/integration/tests/cli/stream/test_stream_update_command.rs
+++ b/core/integration/tests/cli/stream/test_stream_update_command.rs
@@ -28,7 +28,7 @@ use serial_test::parallel;
 
 struct TestStreamUpdateCmd {
     stream_id: u32,
-    name: String,
+    stream_name: String,
     new_name: String,
     using_identifier: TestStreamId,
 }
@@ -37,7 +37,7 @@ impl TestStreamUpdateCmd {
     fn new(stream_id: u32, name: String, new_name: String, using_identifier: TestStreamId) -> Self {
         Self {
             stream_id,
-            name,
+            stream_name: name,
             new_name,
             using_identifier,
         }
@@ -45,7 +45,7 @@ impl TestStreamUpdateCmd {
 
     fn to_args(&self) -> Vec<String> {
         match self.using_identifier {
-            TestStreamId::Named => vec![self.name.clone(), self.new_name.clone()],
+            TestStreamId::Named => vec![self.stream_name.clone(), self.new_name.clone()],
             TestStreamId::Numeric => {
                 vec![format!("{}", self.stream_id), self.new_name.clone()]
             }
@@ -56,7 +56,7 @@ impl TestStreamUpdateCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestStreamUpdateCmd {
     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());
     }
 
@@ -72,7 +72,7 @@ impl IggyCmdTestCase for TestStreamUpdateCmd {
         let message = match self.using_identifier {
             TestStreamId::Named => format!(
                 "Executing update stream with ID: {} and name: {}\nStream with ID: {} updated name: {}\n",
-                self.name, self.new_name, self.name, self.new_name
+                self.stream_name, self.new_name, self.stream_name, self.new_name
             ),
             TestStreamId::Numeric => format!(
                 "Executing update stream with ID: {} and name: {}\nStream with ID: {} updated name: {}\n",
@@ -84,10 +84,17 @@ impl IggyCmdTestCase for TestStreamUpdateCmd {
     }
 
     async fn verify_server_state(&self, client: &dyn Client) {
-        let stream = client.get_stream(&self.stream_id.try_into().unwrap()).await;
+        let stream = client
+            .get_stream(&self.new_name.clone().try_into().unwrap())
+            .await;
         assert!(stream.is_ok());
         let stream = stream.unwrap().expect("Stream not found");
         assert_eq!(stream.name, self.new_name);
+
+        let delete = client
+            .delete_stream(&self.new_name.clone().try_into().unwrap())
+            .await;
+        assert!(delete.is_ok());
     }
 }
 
@@ -99,15 +106,15 @@ pub async fn should_be_successful() {
     iggy_cmd_test.setup().await;
     iggy_cmd_test
         .execute_test(TestStreamUpdateCmd::new(
-            1,
+            0,
             String::from("testing"),
             String::from("development"),
-            TestStreamId::Numeric,
+            TestStreamId::Named,
         ))
         .await;
     iggy_cmd_test
         .execute_test(TestStreamUpdateCmd::new(
-            2,
+            0,
             String::from("production"),
             String::from("prototype"),
             TestStreamId::Named,
diff --git a/core/integration/tests/cli/system/test_me_command.rs b/core/integration/tests/cli/system/test_me_command.rs
index 9730217cc..19dfeb628 100644
--- a/core/integration/tests/cli/system/test_me_command.rs
+++ b/core/integration/tests/cli/system/test_me_command.rs
@@ -44,6 +44,7 @@ impl TransportProtocolExt for TransportProtocol {
         match self {
             TransportProtocol::Tcp => vec!["--transport", "tcp"],
             TransportProtocol::Quic => vec!["--transport", "quic"],
+            TransportProtocol::WebSocket => vec!["--transport", "websocket"],
             // Note: HTTP is not supported for the 'me' command
             TransportProtocol::Http => {
                 panic!("HTTP transport is not supported for the 'me' command")
@@ -117,6 +118,10 @@ impl IggyCmdTestCase for TestMeCmd {
                 "--quic-server-address".into(),
                 server.get_quic_udp_addr().unwrap(),
             ],
+            TransportProtocol::WebSocket => vec![
+                "--websocket-server-address".into(),
+                server.get_websocket_addr().unwrap(),
+            ],
             TransportProtocol::Http => {
                 panic!("HTTP transport is not supported for the 'me' command")
             }
diff --git a/core/integration/tests/cli/system/test_snapshot_cmd.rs b/core/integration/tests/cli/system/test_snapshot_cmd.rs
index 14906b110..df90db8a3 100644
--- a/core/integration/tests/cli/system/test_snapshot_cmd.rs
+++ b/core/integration/tests/cli/system/test_snapshot_cmd.rs
@@ -108,7 +108,7 @@ pub async fn should_be_successful() {
         contents
     };
 
-    assert!(contents.trim().contains("INFO ThreadId"));
+    assert!(contents.trim().contains("INFO shard-"));
 }
 
 #[tokio::test]
diff --git a/core/integration/tests/cli/system/test_stats_command.rs b/core/integration/tests/cli/system/test_stats_command.rs
index 6af7e43c6..4f921c7d6 100644
--- a/core/integration/tests/cli/system/test_stats_command.rs
+++ b/core/integration/tests/cli/system/test_stats_command.rs
@@ -64,7 +64,7 @@ impl TestStatsCmd {
 impl IggyCmdTestCase for TestStatsCmd {
     async fn prepare_server_state(&mut self, client: &dyn Client) {
         let stream_id = Identifier::from_str_value("logs").unwrap();
-        let stream = client.create_stream(&stream_id.as_string(), Some(1)).await;
+        let stream = client.create_stream(&stream_id.as_string()).await;
         assert!(stream.is_ok());
 
         let topic = client
@@ -74,7 +74,6 @@ impl IggyCmdTestCase for TestStatsCmd {
                 5,
                 Default::default(),
                 None,
-                Some(1),
                 IggyExpiry::NeverExpire,
                 MaxTopicSize::ServerDefault,
             )
@@ -97,7 +96,7 @@ impl IggyCmdTestCase for TestStatsCmd {
                     .stdout(contains("Partitions Count         | 5"))
                     .stdout(contains("Segments Count           | 5"))
                     .stdout(contains("Message Count            | 0"))
-                    .stdout(contains("Clients Count            | 2")) // 2 clients are connected during test
+                    // Note: Client count can vary due to connection lifecycle; at least 2 expected
                     .stdout(contains("Consumer Groups Count    | 0"));
             }
             TestStatsCmdOutput::Set(GetStatsOutput::List) => {
@@ -108,7 +107,6 @@ impl IggyCmdTestCase for TestStatsCmd {
                     .stdout(contains("Partitions Count|5"))
                     .stdout(contains("Segments Count|5"))
                     .stdout(contains("Message Count|0"))
-                    .stdout(contains("Clients Count|2")) // 2 clients are connected during test
                     .stdout(contains("Consumer Groups Count|0"));
             }
             TestStatsCmdOutput::Set(GetStatsOutput::Json) => {
@@ -119,7 +117,6 @@ impl IggyCmdTestCase for TestStatsCmd {
                     .stdout(contains(r#""partitions_count": 5"#))
                     .stdout(contains(r#""segments_count": 5"#))
                     .stdout(contains(r#""messages_count": 0"#))
-                    .stdout(contains(r#""clients_count": 2"#)) // 2 clients are connected during test
                     .stdout(contains(r#""consumer_groups_count": 0"#));
             }
             TestStatsCmdOutput::Set(GetStatsOutput::Toml) => {
@@ -130,7 +127,6 @@ impl IggyCmdTestCase for TestStatsCmd {
                     .stdout(contains("partitions_count = 5"))
                     .stdout(contains("segments_count = 5"))
                     .stdout(contains("messages_count = 0"))
-                    .stdout(contains("clients_count = 2")) // 2 clients are connected during test
                     .stdout(contains("consumer_groups_count = 0"));
             }
         }
@@ -138,11 +134,11 @@ impl IggyCmdTestCase for TestStatsCmd {
 
     async fn verify_server_state(&self, client: &dyn Client) {
         let topic = client
-            .delete_topic(&1.try_into().unwrap(), &1.try_into().unwrap())
+            .delete_topic(&0.try_into().unwrap(), &0.try_into().unwrap())
             .await;
         assert!(topic.is_ok());
 
-        let stream = client.delete_stream(&1.try_into().unwrap()).await;
+        let stream = client.delete_stream(&0.try_into().unwrap()).await;
         assert!(stream.is_ok());
     }
 }
diff --git a/core/integration/tests/cli/topic/test_topic_create_command.rs b/core/integration/tests/cli/topic/test_topic_create_command.rs
index f4dc9ad56..cb1718f9e 100644
--- a/core/integration/tests/cli/topic/test_topic_create_command.rs
+++ b/core/integration/tests/cli/topic/test_topic_create_command.rs
@@ -36,7 +36,6 @@ use std::time::Duration;
 struct TestTopicCreateCmd {
     stream_id: u32,
     stream_name: String,
-    topic_id: Option<u32>,
     topic_name: String,
     partitions_count: u32,
     compression_algorithm: CompressionAlgorithm,
@@ -51,7 +50,6 @@ impl TestTopicCreateCmd {
     fn new(
         stream_id: u32,
         stream_name: String,
-        topic_id: Option<u32>,
         topic_name: String,
         partitions_count: u32,
         compression_algorithm: CompressionAlgorithm,
@@ -63,7 +61,6 @@ impl TestTopicCreateCmd {
         Self {
             stream_id,
             stream_name,
-            topic_id,
             topic_name,
             partitions_count,
             compression_algorithm,
@@ -77,11 +74,6 @@ impl TestTopicCreateCmd {
     fn to_args(&self) -> Vec<String> {
         let mut args = Vec::new();
 
-        if let Some(topic_id) = self.topic_id {
-            args.push("-t".to_string());
-            args.push(format!("{topic_id}"));
-        };
-
         match self.using_identifier {
             TestStreamId::Numeric => args.extend(vec![format!("{}", self.stream_id)]),
             TestStreamId::Named => args.extend(vec![self.stream_name.clone()]),
@@ -101,9 +93,7 @@ impl TestTopicCreateCmd {
 #[async_trait]
 impl IggyCmdTestCase for TestTopicCreateCmd {
     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());
     }
 
@@ -121,10 +111,6 @@ impl IggyCmdTestCase for TestTopicCreateCmd {
             TestStreamId::Named => self.stream_name.clone(),
         };
         let partitions_count = self.partitions_count;
-        let topic_id = match self.topic_id {
-            Some(topic_id) => format!("ID: {topic_id}"),
-            None => "ID auto incremented".to_string(),
-        };
         let topic_name = &self.topic_name;
         let compression_algorithm = &self.compression_algorithm;
         let message_expiry = (match &self.message_expiry {
@@ -138,9 +124,9 @@ impl IggyCmdTestCase for TestTopicCreateCmd {
         let replication_factor = self.replication_factor;
 
         let message = format!(
-            "Executing create topic with name: {topic_name}, {topic_id}, message expiry: {message_expiry}, compression algorithm: {compression_algorithm}, \
+            "Executing 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}\n\
-            Topic with name: {topic_name}, {topic_id}, partitions count: {partitions_count}, compression algorithm: {compression_algorithm}, message expiry: {message_expiry}, \
+            Topic with name: {topic_name}, partitions count: {partitions_count}, compression algorithm: {compression_algorithm}, message expiry: {message_expiry}, \
             max topic size: {max_topic_size}, replication factor: {replication_factor} created in stream with ID: {stream_id}\n",
         );
 
@@ -150,7 +136,7 @@ impl IggyCmdTestCase for TestTopicCreateCmd {
     async fn verify_server_state(&self, client: &dyn Client) {
         let topic = client
             .get_topic(
-                &self.stream_id.try_into().unwrap(),
+                &self.stream_name.clone().try_into().unwrap(),
                 &self.topic_name.clone().try_into().unwrap(),
             )
             .await;
@@ -159,9 +145,6 @@ impl IggyCmdTestCase for TestTopicCreateCmd {
         assert_eq!(topic_details.name, self.topic_name);
         assert_eq!(topic_details.partitions_count, self.partitions_count);
         assert_eq!(topic_details.messages_count, 0);
-        if let Some(topic_id) = self.topic_id {
-            assert_eq!(topic_details.id, topic_id);
-        }
 
... 52788 lines suppressed ...